Remove Editor & specter

This commit is contained in:
Luke Street 2021-05-24 01:06:51 -04:00
parent 8f493f17f1
commit 48d22ddab4
134 changed files with 713 additions and 5088 deletions

4
.gitignore vendored
View File

@ -5,8 +5,8 @@ version.h
*.autosave
docs/*
.idea/
Editor/platforms/win/metaforce.rc
Runtime/platforms/win/metaforce.rc
.vs/
out/
cmake-build-*/
build/
build/

View File

@ -395,7 +395,6 @@ add_subdirectory(extern/athena)
add_subdirectory(extern/boo)
add_subdirectory(hecl/shaderc)
include(hecl/ApplicationTools.cmake)
add_subdirectory(specter/shaders)
add_subdirectory(Shaders)
add_subdirectory(extern/libSquish)
add_subdirectory(extern/libpng)
@ -441,23 +440,14 @@ add_subdirectory(extern/freetype2)
if (NOT MSVC)
target_compile_options(freetype PRIVATE -Wno-implicit-fallthrough)
endif()
add_subdirectory(specter)
target_include_directories(specter PRIVATE ${CMAKE_SOURCE_DIR})
target_link_libraries(specter PRIVATE nod)
add_subdirectory(assetnameparser)
add_compile_definitions(URDE_ZIP_INPUT_STREAM=1) # Enable CZipInputStream now that zlib header is known
add_subdirectory(DataSpec)
add_subdirectory(extern/kabufuda)
add_subdirectory(Editor)
add_subdirectory(extern/jbus)
set(JBUS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/jbus/include)
set(CLIENT_SOURCES
${CMAKE_SOURCE_DIR}/Editor/ProjectResourceFactoryBase.hpp
${CMAKE_SOURCE_DIR}/Editor/ProjectResourceFactoryBase.cpp
${CMAKE_SOURCE_DIR}/Editor/ProjectResourceFactoryMP1.hpp
${CMAKE_SOURCE_DIR}/Editor/ProjectResourceFactoryMP1.cpp)
add_subdirectory(NESEmulator)
add_subdirectory(Runtime)
add_subdirectory(mpcksum)

View File

@ -75,7 +75,7 @@ add_library(AssetNameMapNull
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
target_include_directories(RetroDataSpec PUBLIC ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
${HECL_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod specter squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor)
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor)
if(COMMAND add_sanitizers)
add_sanitizers(RetroDataSpec)
endif()

View File

@ -6,12 +6,12 @@
namespace DataSpec::DNAMP1 {
struct Actor : IScriptObject {
AT_DECL_DNA_YAMLV
String<-1> name SO_NAME_SPECPROP();
Value<atVec3f> location SO_LOCATION_SPECPROP();
Value<atVec3f> orientation SO_ORIENTATION_SPECPROP();
Value<atVec3f> scale SO_SCALE_SPECPROP();
Value<atVec3f> collisionExtent SO_COLLISION_EXTENT_SPECPROP();
Value<atVec3f> collisionOffset SO_COLLISION_OFFSET_SPECPROP();
String<-1> name;
Value<atVec3f> location;
Value<atVec3f> orientation;
Value<atVec3f> scale;
Value<atVec3f> collisionExtent;
Value<atVec3f> collisionOffset;
Value<float> mass;
Value<float> zMomentum;
HealthInfo healthInfo;

View File

@ -3,18 +3,9 @@
#include "../DNAMP1.hpp"
#include "../SAVW.hpp"
#include "zeus/CAABox.hpp"
#include "specter/genie.hpp"
#include <cstdio>
#define SO_NAME_SPECPROP() SPECTER_PROPERTY("Name", "Instance name; Used to debug scripting events")
#define SO_LOCATION_SPECPROP() SPECTER_PROPERTY("Location", "World relative location of the Actor instance")
#define SO_ORIENTATION_SPECPROP() SPECTER_PROPERTY("Orientation", "Object local axis-angle")
#define SO_SCALE_SPECPROP() SPECTER_PROPERTY("Scale", "Object local scale")
#define SO_COLLISION_EXTENT_SPECPROP() SPECTER_PROPERTY("Collision Extent", "")
#define SO_COLLISION_OFFSET_SPECPROP() SPECTER_PROPERTY("Collision Offset", "")
#define SO_ACTIVE_SPECPROP() SPECTER_PROPERTY("Active", "If enabled, object instance is drawn and updated")
namespace DataSpec::DNAMP1 {
zeus::CTransform ConvertEditorEulerToTransform4f(const zeus::CVector3f& scale, const zeus::CVector3f& orientation,

View File

@ -3,7 +3,6 @@
#include "../../DNACommon/DNACommon.hpp"
#include "../DNAMP1.hpp"
#include "../SAVW.hpp"
#include "specter/genie.hpp"
namespace DataSpec::DNAMP1 {
@ -49,7 +48,7 @@ enum class EPickupType : atUint32 {
World = 38,
Spirit = 39,
Newborn = 40
} SPECTER_ENUM("Pickup Type", "", EPickupType);
};
enum class ESpecialFunctionType : atUint32 {
What,
@ -85,7 +84,7 @@ enum class ESpecialFunctionType : atUint32 {
Ending,
FusionRelay,
WeaponSwitch // PAL Only
} SPECTER_ENUM("Special Function", "", ESpecialFunctionType);
};
struct AnimationParameters : BigDNA {
AT_DECL_DNA_YAML
@ -211,9 +210,9 @@ struct GrappleParameters : BigDNA {
struct HealthInfo : BigDNA {
AT_DECL_DNA_YAML
Value<atUint32> propertyCount;
Value<float> health SPECTER_PROPERTY("Health", "Base health for object");
Value<float> knockbackResistance SPECTER_PROPERTY("Knockback Resistance", "");
} SPECTER_PROPERTY("Health Info", "");
Value<float> health;
Value<float> knockbackResistance;
};
struct LightParameters : BigDNA {
AT_DECL_DNA_YAML

View File

@ -1,112 +0,0 @@
add_subdirectory(locale)
add_subdirectory(icons)
add_subdirectory(badging)
unset(URDE_PLAT_LIBS)
if(WIN32)
configure_file(platforms/win/metaforce.rc.in "${CMAKE_CURRENT_SOURCE_DIR}/platforms/win/metaforce.rc" @ONLY)
set(PLAT_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/platforms/win/metaforce.rc" platforms/win/metaforce.manifest)
if(WINDOWS_STORE)
set(UWP_ASSETS
platforms/win/Assets/LargeTile.scale-100.png
platforms/win/Assets/LargeTile.scale-125.png
platforms/win/Assets/LargeTile.scale-150.png
platforms/win/Assets/LargeTile.scale-200.png
platforms/win/Assets/LargeTile.scale-400.png
platforms/win/Assets/SmallTile.scale-100.png
platforms/win/Assets/SmallTile.scale-125.png
platforms/win/Assets/SmallTile.scale-150.png
platforms/win/Assets/SmallTile.scale-200.png
platforms/win/Assets/SmallTile.scale-400.png
platforms/win/Assets/SplashScreen.scale-100.png
platforms/win/Assets/SplashScreen.scale-125.png
platforms/win/Assets/SplashScreen.scale-150.png
platforms/win/Assets/SplashScreen.scale-200.png
platforms/win/Assets/SplashScreen.scale-400.png
platforms/win/Assets/Square44x44Logo.scale-100.png
platforms/win/Assets/Square44x44Logo.scale-125.png
platforms/win/Assets/Square44x44Logo.scale-150.png
platforms/win/Assets/Square44x44Logo.scale-200.png
platforms/win/Assets/Square44x44Logo.scale-400.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-16.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-24.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-32.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-48.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-256.png
platforms/win/Assets/Square150x150Logo.scale-100.png
platforms/win/Assets/Square150x150Logo.scale-125.png
platforms/win/Assets/Square150x150Logo.scale-150.png
platforms/win/Assets/Square150x150Logo.scale-200.png
platforms/win/Assets/Square150x150Logo.scale-400.png
platforms/win/Assets/metaforce.scale-100.png
platforms/win/Assets/metaforce.scale-125.png
platforms/win/Assets/metaforce.scale-150.png
platforms/win/Assets/metaforce.scale-200.png
platforms/win/Assets/metaforce.scale-400.png
platforms/win/Assets/WideTile.scale-100.png
platforms/win/Assets/WideTile.scale-125.png
platforms/win/Assets/WideTile.scale-150.png
platforms/win/Assets/WideTile.scale-200.png
platforms/win/Assets/WideTile.scale-400.png)
set_property(SOURCE platforms/win/Package.appxmanifest PROPERTY VS_DEPLOYMENT_CONTENT 1)
set_property(SOURCE ${UWP_ASSETS} PROPERTY VS_DEPLOYMENT_CONTENT 1)
set_property(SOURCE ${UWP_ASSETS} PROPERTY VS_DEPLOYMENT_LOCATION "Assets")
list(APPEND PLAT_SRCS ${UWP_ASSETS} platforms/win/Package.appxmanifest)
endif()
elseif(APPLE)
# nothing
elseif(UNIX)
add_subdirectory(platforms/freedesktop)
declare_wmicon_target()
set(PLAT_SRCS mainicon_netwm.cpp)
set(URDE_PLAT_LIBS rt)
endif()
add_executable(metaforce WIN32
main.cpp ${PLAT_SRCS}
Space.hpp Space.cpp
SplashScreen.hpp SplashScreen.cpp
ResourceBrowser.hpp ResourceBrowser.cpp
ModelViewer.hpp ModelViewer.cpp
ParticleEditor.hpp ParticleEditor.cpp
InformationCenter.hpp InformationCenter.hpp
ProjectManager.hpp ProjectManager.cpp
ViewManager.hpp ViewManager.cpp
Resource.hpp Resource.cpp
Camera.hpp Camera.cpp
GameMode.hpp GameMode.cpp)
target_include_directories(metaforce PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/Runtime # FIXME atdna cmake issue
${CMAKE_BINARY_DIR})
target_link_libraries(metaforce
MetaforceIcons
MetaforceBadging
RuntimeCommon
amuse
RetroDataSpec
kabufuda
${URDE_PLAT_LIBS})
target_atdna(metaforce atdna_Space.cpp Space.hpp)
target_atdna(metaforce atdna_ResourceBrowser.cpp ResourceBrowser.hpp)
target_atdna(metaforce atdna_ModelViewer.cpp ModelViewer.hpp)
target_atdna(metaforce atdna_ParticleEditor.cpp ParticleEditor.hpp)
target_atdna(metaforce atdna_InformationCenter.cpp InformationCenter.hpp)
target_atdna(metaforce atdna_GameMode.cpp GameMode.hpp)
if(COMMAND add_sanitizers)
add_sanitizers(metaforce)
endif()
if (NOT WINDOWS_STORE)
add_dependencies(metaforce visigen hecl)
else()
set_property(TARGET metaforce PROPERTY VS_WINRT_COMPONENT TRUE)
# This should match the Package.appxmanifest
set_property(TARGET metaforce PROPERTY VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION "10.0.14393.0")
endif()

View File

View File

@ -1,25 +0,0 @@
#pragma once
#include "zeus/CProjection.hpp"
#include "zeus/CFrustum.hpp"
#include "zeus/CQuaternion.hpp"
#include "zeus/CVector3f.hpp"
#include "zeus/Math.hpp"
namespace metaforce {
class Camera {
zeus::CFrustum m_frustum;
zeus::CProjection m_projection;
zeus::CVector3f m_position;
zeus::CQuaternion m_orientation;
public:
void setPosition(const zeus::CVector3f& position) { m_position = position; }
void setOrientation(const zeus::CQuaternion& orientation) { m_orientation = orientation; }
const zeus::CMatrix4f& projectionMatrix() const { return m_projection.getCachedMatrix(); }
const zeus::CProjection& projection() const { return m_projection; }
virtual void think() {}
};
} // namespace metaforce

View File

@ -1,11 +0,0 @@
#include "GameMode.hpp"
namespace metaforce {
void GameMode::think() { ViewerSpace::think(); }
void GameMode::View::draw(boo::IGraphicsCommandQueue* gfxQ) {
if (m_gMode.m_main)
m_gMode.m_main->Draw();
}
} // namespace metaforce

View File

@ -1,54 +0,0 @@
#pragma once
#include "Space.hpp"
#include "ViewManager.hpp"
#include "Runtime/IMain.hpp"
namespace metaforce {
class GameMode : public ViewerSpace {
std::shared_ptr<IMain> m_main;
struct State : Space::State {
AT_DECL_DNA_YAMLV
Value<bool> showToolbar = true;
} m_state;
const Space::State& spaceState() const override { return m_state; }
struct View : specter::View {
GameMode& m_gMode;
View(GameMode& gMode, specter::ViewResources& res) : specter::View(res, gMode.m_vm.rootView()), m_gMode(gMode) {}
void draw(boo::IGraphicsCommandQueue* gfxQ) override;
};
std::unique_ptr<View> m_view;
public:
GameMode(ViewManager& vm, Space* parent) : ViewerSpace(vm, Class::GameMode, parent) { reloadState(); }
GameMode(ViewManager& vm, Space* parent, const GameMode& other) : GameMode(vm, parent) {
m_state = other.m_state;
reloadState();
}
GameMode(ViewManager& vm, Space* parent, ConfigReader& r) : GameMode(vm, parent) {
m_state.read(r);
reloadState();
}
void reloadState() override {}
specter::View* buildContentView(specter::ViewResources& res) override {
m_view.reset(new View(*this, res));
return m_view.get();
}
void think() override;
Space* copy(Space* parent) const override { return new GameMode(m_vm, parent, *this); }
bool usesToolbar() const override { return m_state.showToolbar; }
};
} // namespace metaforce

View File

@ -1,3 +0,0 @@
#include "InformationCenter.hpp"
namespace metaforce {}

View File

@ -1,50 +0,0 @@
#pragma once
#include "Space.hpp"
#include "ViewManager.hpp"
namespace metaforce {
class InformationCenter : public ViewerSpace {
struct State : Space::State {
AT_DECL_DNA_YAMLV
Value<bool> showLog;
} m_state;
const Space::State& spaceState() const override { return m_state; }
struct View : specter::View {
InformationCenter& m_ic;
std::vector<hecl::SystemString> m_log;
View(InformationCenter& ic, specter::ViewResources& res) : specter::View(res, ic.m_vm.rootView()), m_ic(ic) {}
};
std::unique_ptr<View> m_view;
public:
InformationCenter(ViewManager& vm, Space* parent) : ViewerSpace(vm, Class::InformationCenter, parent) {
reloadState();
}
InformationCenter(ViewManager& vm, Space* parent, const InformationCenter& other) : InformationCenter(vm, parent) {
m_state = other.m_state;
reloadState();
}
InformationCenter(ViewManager& vm, Space* parent, ConfigReader& r) : InformationCenter(vm, parent) {
m_state.read(r);
reloadState();
}
void reloadState() override {}
specter::View* buildContentView(specter::ViewResources& res) override {
m_view.reset(new View(*this, res));
return m_view.get();
}
Space* copy(Space* parent) const override { return new InformationCenter(m_vm, parent, *this); }
bool usesToolbar() const override { return true; }
};
} // namespace metaforce

View File

@ -1,10 +0,0 @@
#include "ModelViewer.hpp"
namespace metaforce {
void ModelViewer::View::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) {
specter::View::resized(root, sub);
m_scissorRect = sub;
}
} // namespace metaforce

View File

@ -1,64 +0,0 @@
#pragma once
#include "Space.hpp"
#include "ViewManager.hpp"
#include "Camera.hpp"
namespace metaforce {
class ModelViewer : public ViewerSpace {
struct State : Space::State {
AT_DECL_DNA_YAMLV
enum class Mode { Solid, Material, Wireframe };
Value<Mode> renderMode = Mode::Material;
Value<zeus::CVector3f> cameraPosition;
Value<zeus::CQuaternion> cameraOrientation;
} m_state;
const Space::State& spaceState() const override { return m_state; }
std::unique_ptr<metaforce::CLineRenderer> m_lineRenderer;
struct View : specter::View {
ModelViewer& m_mv;
boo::SWindowRect m_scissorRect;
View(ModelViewer& mv, specter::ViewResources& res) : specter::View(res, mv.m_vm.rootView()), m_mv(mv) {}
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) override;
};
Camera m_camera;
std::unique_ptr<View> m_view;
public:
ModelViewer(ViewManager& vm, Space* parent) : ViewerSpace(vm, Class::ModelViewer, parent) {
reloadState();
m_lineRenderer.reset(new metaforce::CLineRenderer(metaforce::CLineRenderer::EPrimitiveMode::LineStrip, 4, nullptr, true));
}
ModelViewer(ViewManager& vm, Space* parent, const ModelViewer& other) : ModelViewer(vm, parent) {
m_state = other.m_state;
reloadState();
}
ModelViewer(ViewManager& vm, Space* parent, ConfigReader& r) : ModelViewer(vm, parent) {
m_state.read(r);
reloadState();
}
void reloadState() override {
m_camera.setPosition(m_state.cameraPosition);
m_camera.setOrientation(m_state.cameraOrientation);
}
Space* copy(Space* parent) const override { return new ModelViewer(m_vm, parent, *this); }
specter::View* buildContentView(specter::ViewResources& res) override {
m_view.reset(new View(*this, res));
return m_view.get();
}
bool usesToolbar() const override { return true; }
};
} // namespace metaforce

View File

@ -1,3 +0,0 @@
#include "ParticleEditor.hpp"
namespace metaforce {}

View File

@ -1,32 +0,0 @@
#pragma once
#include "Space.hpp"
namespace metaforce {
class EffectEditor : public EditorSpace {
struct State : Space::State {
AT_DECL_DNA_YAMLV
String<-1> path;
} m_state;
const Space::State& spaceState() const override { return m_state; }
struct View : specter::View {
View(specter::ViewResources& res, specter::View& parent) : specter::View(res, parent) {}
};
specter::View* buildContentView(specter::ViewResources& res) override { return nullptr; }
public:
EffectEditor(ViewManager& vm, Space* parent) : EditorSpace(vm, Class::EffectEditor, parent) {}
EffectEditor(ViewManager& vm, Space* parent, ConfigReader& r) : EffectEditor(vm, parent) { m_state.read(r); }
EffectEditor(ViewManager& vm, Space* parent, const EffectEditor& other) : EffectEditor(vm, parent) {
m_state = other.m_state;
}
Space* copy(Space* parent) const override { return new EffectEditor(m_vm, parent, *this); }
bool usesToolbar() const override { return true; }
};
} // namespace metaforce

View File

@ -1,202 +0,0 @@
#include "ProjectManager.hpp"
#include "ViewManager.hpp"
#include "DataSpecRegistry.hpp"
#include "hecl/Blender/Connection.hpp"
#include "version.h"
namespace metaforce {
static logvisor::Module Log("URDE::ProjectManager");
ProjectManager* ProjectManager::g_SharedManager = nullptr;
CToken ProjectResourcePool::GetObj(std::string_view name) {
CToken ret = CSimplePool::GetObj(name);
if (ret)
return ret;
hecl::ProjectPath path(*m_parent.project(), name);
SObjectTag tag =
static_cast<ProjectResourceFactoryBase&>(x18_factory).TagFromPath(path);
if (tag)
return CSimplePool::GetObj(tag);
return {};
}
CToken ProjectResourcePool::GetObj(std::string_view name, const CVParamTransfer& pvxfer) {
CToken ret = CSimplePool::GetObj(name, pvxfer);
if (ret)
return ret;
hecl::ProjectPath path(*m_parent.project(), name);
SObjectTag tag =
static_cast<ProjectResourceFactoryBase&>(x18_factory).TagFromPath(path);
if (tag)
return CSimplePool::GetObj(tag, pvxfer);
return {};
}
bool ProjectManager::m_registeredSpecs = false;
ProjectManager::ProjectManager(ViewManager& vm)
: m_vm(vm), m_clientProc(nullptr), m_factoryMP1(m_clientProc), m_objStore(m_factoryMP1, *this) {
if (!m_registeredSpecs) {
HECLRegisterDataSpecs();
m_registeredSpecs = true;
}
g_SharedManager = this;
}
bool ProjectManager::newProject(hecl::SystemStringView path) {
hecl::ProjectRootPath projPath = hecl::SearchForProject(path);
if (projPath) {
Log.report(logvisor::Warning, FMT_STRING(_SYS_STR("project already exists at '{}'")), path);
return false;
}
hecl::MakeDir(path.data());
m_proj = std::make_unique<hecl::Database::Project>(path);
if (!*m_proj) {
m_proj.reset();
return false;
}
m_vm.ProjectChanged(*m_proj);
m_vm.SetupEditorView();
saveProject();
m_vm.m_mainWindow->setTitle(fmt::format(FMT_STRING(_SYS_STR("{} - Metaforce {} [{}]")),
m_proj->getProjectRootPath().getLastComponent(), METAFORCE_WC_DESCRIBE_SYS, m_vm.platformName()));
m_vm.DismissSplash();
m_vm.FadeInEditors();
return true;
}
bool ProjectManager::openProject(hecl::SystemStringView path) {
OPTICK_EVENT();
hecl::SystemString subPath;
hecl::ProjectRootPath projPath = hecl::SearchForProject(path, subPath);
if (!projPath) {
Log.report(logvisor::Warning, FMT_STRING(_SYS_STR("project doesn't exist at '{}'")), path);
return false;
}
m_proj = std::make_unique<hecl::Database::Project>(projPath);
if (!*m_proj) {
m_proj.reset();
return false;
}
athena::io::YAMLDocReader r;
const auto makeProj = [this, &r, &subPath](bool needsSave) {
m_vm.ProjectChanged(*m_proj);
if (needsSave)
m_vm.SetupEditorView();
else
m_vm.SetupEditorView(r);
const bool doRun = hecl::StringUtils::BeginsWith(subPath, _SYS_STR("out"));
if (doRun) {
m_mainMP1.emplace(nullptr, nullptr, m_vm.m_mainBooFactory, m_vm.m_mainCommandQueue, m_vm.m_renderTex);
m_vm.InitMP1(*m_mainMP1);
}
if (needsSave)
saveProject();
m_vm.m_mainWindow->setTitle(fmt::format(FMT_STRING(_SYS_STR("{} - Metaforce {} [{}]")),
m_proj->getProjectRootPath().getLastComponent(), METAFORCE_WC_DESCRIBE_SYS,
m_vm.platformName()));
m_vm.DismissSplash();
m_vm.FadeInEditors();
m_vm.pushRecentProject(m_proj->getProjectRootPath().getAbsolutePath());
return true;
};
const hecl::ProjectPath metaforceSpacesPath(*m_proj, _SYS_STR(".hecl/metaforce_spaces.yaml"));
athena::io::FileReader reader(metaforceSpacesPath.getAbsolutePath());
if (!reader.isOpen()) {
return makeProj(true);
}
const auto readHandler = [](void* data, unsigned char* buffer, size_t size, size_t* size_read) {
auto* const reader = static_cast<athena::io::IStreamReader*>(data);
return athena::io::YAMLAthenaReader(reader, buffer, size, size_read);
};
yaml_parser_set_input(r.getParser(), readHandler, &reader);
if (!r.ValidateClassType("MetaforceSpacesState")) {
return makeProj(true);
}
r.reset();
reader.seek(0, athena::SeekOrigin::Begin);
if (!r.parse(&reader)) {
return makeProj(true);
}
return makeProj(false);
}
bool ProjectManager::extractGame(hecl::SystemStringView path) { return false; }
bool ProjectManager::saveProject() {
if (!m_proj)
return false;
hecl::ProjectPath oldSpacesPath(*m_proj, _SYS_STR(".hecl/~metaforce_spaces.yaml"));
athena::io::FileWriter writer(oldSpacesPath.getAbsolutePath());
if (!writer.isOpen())
return false;
athena::io::YAMLDocWriter w("UrdeSpacesState");
m_vm.SaveEditorView(w);
if (!w.finish(&writer))
return false;
hecl::ProjectPath newSpacesPath(*m_proj, _SYS_STR(".hecl/metaforce_spaces.yaml"));
hecl::Unlink(newSpacesPath.getAbsolutePath().data());
hecl::Rename(oldSpacesPath.getAbsolutePath().data(), newSpacesPath.getAbsolutePath().data());
m_vm.pushRecentProject(m_proj->getProjectRootPath().getAbsolutePath());
return true;
}
void ProjectManager::mainUpdate() {
OPTICK_EVENT();
if (m_precooking) {
if (!m_factoryMP1.IsBusy())
m_precooking = false;
else
return;
}
if (m_mainMP1) {
if (m_mainMP1->Proc()) {
m_mainMP1->Shutdown();
m_mainMP1 = std::nullopt;
}
}
}
void ProjectManager::mainDraw() {
if (m_precooking)
return;
if (m_mainMP1)
m_mainMP1->Draw();
}
void ProjectManager::shutdown() {
if (m_mainMP1)
m_mainMP1->Shutdown();
m_clientProc.shutdown();
m_factoryMP1.Shutdown();
hecl::blender::Connection::Shutdown();
}
} // namespace metaforce

View File

@ -1,58 +0,0 @@
#pragma once
#include "hecl/Database.hpp"
#include "athena/DNAYaml.hpp"
#include "ProjectResourceFactoryMP1.hpp"
#include "Runtime/CSimplePool.hpp"
#include "hecl/Runtime.hpp"
#include "MP1/MP1.hpp"
namespace metaforce {
class ViewManager;
using ConfigReader = athena::io::YAMLDocReader;
using ConfigWriter = athena::io::YAMLDocWriter;
class ProjectResourcePool : public CSimplePool {
class ProjectManager& m_parent;
public:
ProjectResourcePool(IFactory& factory, ProjectManager& parent) : CSimplePool(factory), m_parent(parent) {}
CToken GetObj(std::string_view) override;
CToken GetObj(std::string_view, const CVParamTransfer&) override;
};
class ProjectManager {
ViewManager& m_vm;
std::unique_ptr<hecl::Database::Project> m_proj;
static bool m_registeredSpecs;
hecl::ClientProcess m_clientProc;
ProjectResourceFactoryMP1 m_factoryMP1;
ProjectResourcePool m_objStore;
std::optional<MP1::CMain> m_mainMP1;
bool m_precooking = false;
public:
static ProjectManager* g_SharedManager;
ProjectManager(ViewManager& vm);
operator bool() const { return m_proj.operator bool(); }
hecl::Database::Project* project() { return m_proj.get(); }
ProjectResourcePool& objectStore() { return m_objStore; }
ProjectResourceFactoryMP1& resourceFactoryMP1() { return m_factoryMP1; }
MP1::CMain* gameMain() { return m_mainMP1 ? &*m_mainMP1 : nullptr; }
SObjectTag TagFromPath(hecl::SystemStringView path) const {
return m_factoryMP1.ProjectResourceFactoryBase::TagFromPath(path);
}
bool newProject(hecl::SystemStringView path);
bool openProject(hecl::SystemStringView path);
bool extractGame(hecl::SystemStringView path);
bool saveProject();
void mainUpdate();
void mainDraw();
void shutdown();
};
} // namespace metaforce

View File

@ -1,467 +0,0 @@
#include "Editor/ProjectResourceFactoryBase.hpp"
#include "Runtime/IObj.hpp"
#include <logvisor/logvisor.hpp>
#undef min
#undef max
namespace metaforce {
static logvisor::Module Log("metaforce::ProjectResourceFactoryBase");
void ProjectResourceFactoryBase::BeginBackgroundIndex(hecl::Database::Project& proj,
const hecl::Database::DataSpecEntry& origSpec,
const hecl::Database::DataSpecEntry& pcSpec) {
CancelBackgroundIndex();
m_proj = &proj;
m_origSpec = &origSpec;
m_pcSpec = &pcSpec;
m_cookSpec = pcSpec.m_factory(proj, hecl::Database::DataSpecTool::Cook);
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).beginBackgroundIndex();
}
bool ProjectResourceFactoryBase::SyncCook(const hecl::ProjectPath& working) {
Log.report(logvisor::Warning, FMT_STRING(_SYS_STR("sync-cooking {}")), working.getRelativePath());
return m_clientProc.syncCook(working, m_cookSpec.get(), hecl::blender::SharedBlenderToken, false, false);
}
CFactoryFnReturn ProjectResourceFactoryBase::BuildSync(const SObjectTag& tag, const hecl::ProjectPath& path,
const CVParamTransfer& paramXfer, CObjectReference* selfRef) {
/* Ensure cooked rep is on the filesystem */
std::optional<athena::io::FileReader> fr;
if (!PrepForReadSync(tag, path, fr))
return {};
/* All good, build resource */
if (m_factoryMgr.CanMakeMemory(tag)) {
u32 length = fr->length();
std::unique_ptr<u8[]> memBuf = fr->readUBytes(length);
CFactoryFnReturn ret = m_factoryMgr.MakeObjectFromMemory(tag, std::move(memBuf), length, false, paramXfer, selfRef);
Log.report(logvisor::Info, FMT_STRING("sync-built {}"), tag);
return ret;
}
CFactoryFnReturn ret = m_factoryMgr.MakeObject(tag, *fr, paramXfer, selfRef);
Log.report(logvisor::Info, FMT_STRING("sync-built {}"), tag);
return ret;
}
void ProjectResourceFactoryBase::AsyncTask::EnsurePath(const metaforce::SObjectTag& tag, const hecl::ProjectPath& path) {
if (!m_workingPath) {
m_workingPath = path;
/* Ensure requested resource is on the filesystem */
if (!path.isFileOrGlob()) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to find resource path '{}'")), path.getRelativePath());
m_failed = true;
return;
}
/* Cached resolve (try PC first, then original) */
m_cookedPath = path.getCookedPath(*m_parent.m_pcSpec);
if (!m_cookedPath.isFile())
m_cookedPath = path.getCookedPath(*m_parent.m_origSpec);
if (!m_cookedPath.isFile() || m_cookedPath.getModtime() < path.getModtime()) {
/* Last chance type validation */
metaforce::SObjectTag verifyTag = m_parent.TagFromPath(path);
if (verifyTag.type != tag.type) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("{}: expected type '{}', found '{}'")), path.getRelativePath(),
tag.type, verifyTag.type);
m_failed = true;
return;
}
/* Get cooked representation path */
m_cookedPath = m_parent.GetCookedPath(path, true);
/* Perform mod-time comparison */
if (!m_cookedPath.isFile() || m_cookedPath.getModtime() < path.getModtime()) {
/* Start a background cook here */
m_cookTransaction = m_parent.m_clientProc.addCookTransaction(path, false, false, m_parent.m_cookSpec.get());
return;
}
}
CookComplete();
}
}
void ProjectResourceFactoryBase::AsyncTask::CookComplete() {
/* Ensure cooked rep is on the filesystem */
athena::io::FileReader fr(m_cookedPath.getAbsolutePath(), 32 * 1024, false);
if (fr.hasError()) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open cooked resource path '{}'")), m_cookedPath.getAbsolutePath());
m_failed = true;
return;
}
/* Ready for buffer transaction at this point */
u32 availSz = std::max(0, s32(fr.length()) - s32(x14_resOffset));
x14_resSize = std::min(x14_resSize, availSz);
if (xc_targetDataRawPtr) {
m_bufTransaction =
m_parent.m_clientProc.addBufferTransaction(m_cookedPath, xc_targetDataRawPtr, x14_resSize, x14_resOffset);
} else if (xc_targetDataPtr || xc_targetObjPtr) {
x10_loadBuffer.reset(new u8[x14_resSize]);
m_bufTransaction =
m_parent.m_clientProc.addBufferTransaction(m_cookedPath, x10_loadBuffer.get(), x14_resSize, x14_resOffset);
} else {
/* Skip buffer transaction if no target pointers set */
m_complete = true;
}
}
bool ProjectResourceFactoryBase::AsyncTask::AsyncPump() {
if (m_failed || m_complete)
return true;
if (m_bufTransaction) {
if (m_bufTransaction->m_complete) {
m_complete = true;
return true;
}
} else if (m_cookTransaction) {
if (m_cookTransaction->m_complete)
CookComplete();
}
return m_failed;
}
void ProjectResourceFactoryBase::AsyncTask::WaitUntilComplete() {
using ItType = std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator;
ItType search = m_parent.m_asyncLoadMap.find(x0_tag);
if (search == m_parent.m_asyncLoadMap.end())
return;
for (ItType tmp = search; !m_parent.AsyncPumpTask(tmp); tmp = search) {
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
}
using AsyncTask = ProjectResourceFactoryBase::AsyncTask;
std::shared_ptr<AsyncTask> ProjectResourceFactoryBase::_AddTask(const std::shared_ptr<AsyncTask>& ptr) {
m_asyncLoadMap.insert({ptr->x0_tag, m_asyncLoadList.insert(m_asyncLoadList.end(), ptr)});
return ptr;
}
std::list<std::shared_ptr<AsyncTask>>::iterator
ProjectResourceFactoryBase::_RemoveTask(std::list<std::shared_ptr<AsyncTask>>::iterator it) {
m_asyncLoadMap.erase((*it)->x0_tag);
return m_asyncLoadList.erase(it);
}
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator
ProjectResourceFactoryBase::_RemoveTask(
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator it) {
if (it != m_asyncLoadMap.end()) {
m_asyncLoadList.erase(it->second);
return m_asyncLoadMap.erase(it);
}
return it;
};
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator
ProjectResourceFactoryBase::_RemoveTask(const SObjectTag& tag) {
return _RemoveTask(m_asyncLoadMap.find(tag));
};
bool ProjectResourceFactoryBase::PrepForReadSync(const SObjectTag& tag, const hecl::ProjectPath& path,
std::optional<athena::io::FileReader>& fr) {
/* Ensure requested resource is on the filesystem */
if (!path.isFileOrGlob()) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to find resource path '{}'")), path.getAbsolutePath());
return false;
}
/* Cached resolve (try PC first, then original) */
hecl::ProjectPath cooked = path.getCookedPath(*m_pcSpec);
if (!cooked.isFile())
cooked = path.getCookedPath(*m_origSpec);
if (!cooked.isFile() || cooked.getModtime() < path.getModtime()) {
/* Last chance type validation */
metaforce::SObjectTag verifyTag = TagFromPath(path);
if (verifyTag.type != tag.type) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("{}: expected type '{}', found '{}'")), path.getRelativePath(),
tag.type, verifyTag.type);
return false;
}
/* Get cooked representation path */
cooked = GetCookedPath(path, true);
/* Perform mod-time comparison */
if (!cooked.isFile() || cooked.getModtime() < path.getModtime()) {
/* Do a blocking cook here */
if (!SyncCook(path)) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to cook resource path '{}'")), path.getAbsolutePath());
return false;
}
}
}
/* Ensure cooked rep is on the filesystem */
fr.emplace(cooked.getAbsolutePath(), 32 * 1024, false);
if (fr->hasError()) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open cooked resource path '{}'")), cooked.getAbsolutePath());
return false;
}
return true;
}
std::unique_ptr<metaforce::IObj> ProjectResourceFactoryBase::Build(const metaforce::SObjectTag& tag,
const metaforce::CVParamTransfer& paramXfer,
CObjectReference* selfRef) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id on type '{}'"), tag.type);
const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath))
return {};
auto asyncSearch = m_asyncLoadMap.find(tag);
if (asyncSearch != m_asyncLoadMap.end()) {
/* Async spinloop */
AsyncTask& task = **asyncSearch->second;
task.EnsurePath(task.x0_tag, *resPath);
/* Pump load pipeline (cooking if needed) */
while (!task.AsyncPump()) {
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
if (task.m_complete && task.x10_loadBuffer) {
/* Load complete, build resource */
std::unique_ptr<IObj> newObj;
if (m_factoryMgr.CanMakeMemory(task.x0_tag)) {
newObj = m_factoryMgr.MakeObjectFromMemory(tag, std::move(task.x10_loadBuffer), task.x14_resSize, false,
task.x18_cvXfer, selfRef);
} else {
athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize);
newObj = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer, selfRef);
}
//*task.xc_targetObjPtr = newObj.get();
Log.report(logvisor::Warning, FMT_STRING("spin-built {}"), task.x0_tag);
_RemoveTask(asyncSearch);
return newObj;
} else if (task.m_complete) {
Log.report(logvisor::Error, FMT_STRING("unable to spin-build {}; Resource requested as cook-only"), task.x0_tag);
} else {
Log.report(logvisor::Error, FMT_STRING("unable to spin-build {}"), task.x0_tag);
}
_RemoveTask(asyncSearch);
return {};
}
/* Fall-back to sync build */
return BuildSync(tag, *resPath, paramXfer, selfRef);
}
std::shared_ptr<AsyncTask> ProjectResourceFactoryBase::BuildAsyncInternal(const metaforce::SObjectTag& tag,
const metaforce::CVParamTransfer& paramXfer,
std::unique_ptr<metaforce::IObj>* objOut,
CObjectReference* selfRef) {
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {};
return _AddTask(std::make_unique<AsyncTask>(*this, tag, objOut, paramXfer, selfRef));
}
void ProjectResourceFactoryBase::BuildAsync(const metaforce::SObjectTag& tag, const metaforce::CVParamTransfer& paramXfer,
std::unique_ptr<metaforce::IObj>* objOut, CObjectReference* selfRef) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id on type '{}'"), tag.type);
BuildAsyncInternal(tag, paramXfer, objOut, selfRef);
}
u32 ProjectResourceFactoryBase::ResourceSize(const SObjectTag& tag) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id on type '{}'"), tag.type);
/* Ensure resource at requested path is indexed and not cooking */
const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath))
return {};
/* Ensure cooked rep is on the filesystem */
std::optional<athena::io::FileReader> fr;
if (!PrepForReadSync(tag, *resPath, fr))
return {};
return fr->length();
}
std::shared_ptr<metaforce::IDvdRequest> ProjectResourceFactoryBase::LoadResourceAsync(const metaforce::SObjectTag& tag,
void* target) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {};
return std::static_pointer_cast<metaforce::IDvdRequest>(
_AddTask(std::make_shared<AsyncTask>(*this, tag, reinterpret_cast<u8*>(target))));
}
std::shared_ptr<metaforce::IDvdRequest> ProjectResourceFactoryBase::LoadResourcePartAsync(const metaforce::SObjectTag& tag,
u32 off, u32 size, void* target) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {};
return std::static_pointer_cast<metaforce::IDvdRequest>(
_AddTask(std::make_shared<AsyncTask>(*this, tag, reinterpret_cast<u8*>(target), size, off)));
}
std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadResourceSync(const metaforce::SObjectTag& tag) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
/* Ensure resource at requested path is indexed and not cooking */
const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath))
return {};
/* Ensure cooked rep is on the filesystem */
std::optional<athena::io::FileReader> fr;
if (!PrepForReadSync(tag, *resPath, fr))
return {};
return fr->readUBytes(fr->length());
}
std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadNewResourcePartSync(const metaforce::SObjectTag& tag, u32 off,
u32 size) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
/* Ensure resource at requested path is indexed and not cooking */
const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath))
return {};
/* Ensure cooked rep is on the filesystem */
std::optional<athena::io::FileReader> fr;
if (!PrepForReadSync(tag, *resPath, fr))
return {};
s32 sz = std::min(s32(size), std::max(0, s32(fr->length()) - s32(off)));
fr->seek(off, athena::SeekOrigin::Begin);
return fr->readUBytes(sz);
}
std::shared_ptr<AsyncTask> ProjectResourceFactoryBase::CookResourceAsync(const metaforce::SObjectTag& tag) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {};
return _AddTask(std::make_shared<AsyncTask>(*this, tag));
}
void ProjectResourceFactoryBase::CancelBuild(const metaforce::SObjectTag& tag) { _RemoveTask(tag); }
bool ProjectResourceFactoryBase::CanBuild(const metaforce::SObjectTag& tag) {
if (!tag.id.IsValid())
Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath))
return false;
if (resPath->isFile())
return true;
return false;
}
const metaforce::SObjectTag* ProjectResourceFactoryBase::GetResourceIdByName(std::string_view name) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).getResourceIdByName(name);
}
FourCC ProjectResourceFactoryBase::GetResourceTypeById(CAssetId id) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).getResourceTypeById(id);
}
void ProjectResourceFactoryBase::EnumerateResources(const std::function<bool(const SObjectTag&)>& lambda) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).enumerateResources(lambda);
}
void ProjectResourceFactoryBase::EnumerateNamedResources(
const std::function<bool(std::string_view, const SObjectTag&)>& lambda) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).enumerateNamedResources(lambda);
}
template <typename ItType>
bool ProjectResourceFactoryBase::AsyncPumpTask(ItType& it) {
/* Ensure requested resource is in the index */
AsyncTask& task = _GetAsyncTask(it);
hecl::ProjectPath path = static_cast<DataSpec::SpecBase&>(*m_cookSpec).pathFromTag(task.x0_tag);
if (!path) {
if (!static_cast<DataSpec::SpecBase&>(*m_cookSpec).backgroundIndexRunning()) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to find async load resource ({})")), task.x0_tag);
it = _RemoveTask(it);
}
return true;
}
task.EnsurePath(task.x0_tag, path);
/* Pump load pipeline (cooking if needed) */
if (task.AsyncPump()) {
if (task.m_complete) {
/* Load complete, build resource */
if (task.xc_targetObjPtr) {
/* Factory build */
std::unique_ptr<IObj> newObj;
if (m_factoryMgr.CanMakeMemory(task.x0_tag)) {
newObj = m_factoryMgr.MakeObjectFromMemory(task.x0_tag, std::move(task.x10_loadBuffer), task.x14_resSize,
false, task.x18_cvXfer, task.m_selfRef);
} else {
athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize);
newObj = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer, task.m_selfRef);
}
*task.xc_targetObjPtr = std::move(newObj);
Log.report(logvisor::Info, FMT_STRING("async-built {}"), task.x0_tag);
} else if (task.xc_targetDataPtr) {
/* Buffer only */
*task.xc_targetDataPtr = std::move(task.x10_loadBuffer);
Log.report(logvisor::Info, FMT_STRING("async-loaded {}"), task.x0_tag);
} else if (task.xc_targetDataRawPtr) {
/* Buffer only raw */
Log.report(logvisor::Info, FMT_STRING("async-loaded {}"), task.x0_tag);
}
}
it = _RemoveTask(it);
return true;
}
++it;
return false;
}
template bool ProjectResourceFactoryBase::AsyncPumpTask<std::list<std::shared_ptr<AsyncTask>>::iterator>(
std::list<std::shared_ptr<AsyncTask>>::iterator& it);
template bool ProjectResourceFactoryBase::AsyncPumpTask<
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator>(
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator& it);
void ProjectResourceFactoryBase::AsyncIdle() {
/* Consume completed transactions, they will be processed this cycle at the latest */
std::list<std::shared_ptr<hecl::ClientProcess::Transaction>> completed;
m_clientProc.swapCompletedQueue(completed);
/* Begin self-profiling loop */
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
for (auto it = m_asyncLoadList.begin(); it != m_asyncLoadList.end();) {
/* Allow 8 milliseconds (roughly 1/2 frame-time) for each async build cycle */
std::chrono::steady_clock::time_point resStart = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(resStart - start).count() > 8)
break;
AsyncPumpTask(it);
}
}
} // namespace metaforce

View File

@ -1,163 +0,0 @@
#pragma once
#include <list>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <unordered_map>
#include "DataSpec/SpecBase.hpp"
#include "Runtime/CFactoryMgr.hpp"
#include "Runtime/CResFactory.hpp"
#include "Runtime/IFactory.hpp"
#include <hecl/ClientProcess.hpp>
#include <hecl/Database.hpp>
namespace metaforce {
class ProjectResourceFactoryBase : public IFactory {
friend class ProjectResourcePool;
hecl::ClientProcess& m_clientProc;
public:
struct AsyncTask : metaforce::IDvdRequest {
ProjectResourceFactoryBase& m_parent;
SObjectTag x0_tag;
// IDvdRequest* x8_dvdReq = nullptr;
std::unique_ptr<u8[]>* xc_targetDataPtr = nullptr;
u8* xc_targetDataRawPtr = nullptr;
std::unique_ptr<IObj>* xc_targetObjPtr = nullptr;
std::unique_ptr<u8[]> x10_loadBuffer;
u32 x14_resSize = UINT32_MAX;
u32 x14_resOffset = 0;
CVParamTransfer x18_cvXfer;
CObjectReference* m_selfRef = nullptr;
hecl::ProjectPath m_workingPath;
hecl::ProjectPath m_cookedPath;
std::shared_ptr<const hecl::ClientProcess::CookTransaction> m_cookTransaction;
std::shared_ptr<const hecl::ClientProcess::BufferTransaction> m_bufTransaction;
bool m_failed = false;
bool m_complete = false;
AsyncTask(ProjectResourceFactoryBase& parent, const SObjectTag& tag, std::unique_ptr<u8[]>& ptr)
: m_parent(parent), x0_tag(tag), xc_targetDataPtr(&ptr) {}
AsyncTask(ProjectResourceFactoryBase& parent, const SObjectTag& tag, std::unique_ptr<u8[]>& ptr, u32 size, u32 off)
: m_parent(parent), x0_tag(tag), xc_targetDataPtr(&ptr), x14_resSize(size), x14_resOffset(off) {}
AsyncTask(ProjectResourceFactoryBase& parent, const SObjectTag& tag, u8* ptr)
: m_parent(parent), x0_tag(tag), xc_targetDataRawPtr(ptr) {}
AsyncTask(ProjectResourceFactoryBase& parent, const SObjectTag& tag, u8* ptr, u32 size, u32 off)
: m_parent(parent), x0_tag(tag), xc_targetDataRawPtr(ptr), x14_resSize(size), x14_resOffset(off) {}
AsyncTask(ProjectResourceFactoryBase& parent, const SObjectTag& tag, std::unique_ptr<IObj>* ptr,
const CVParamTransfer& xfer, CObjectReference* selfRef)
: m_parent(parent), x0_tag(tag), xc_targetObjPtr(ptr), x18_cvXfer(xfer), m_selfRef(selfRef) {}
/* Cook only */
AsyncTask(ProjectResourceFactoryBase& parent, const SObjectTag& tag) : m_parent(parent), x0_tag(tag) {}
void EnsurePath(const metaforce::SObjectTag& tag, const hecl::ProjectPath& path);
void CookComplete();
bool AsyncPump();
void WaitUntilComplete() override;
bool IsComplete() override { return m_complete; }
void PostCancelRequest() override {}
EMediaType GetMediaType() const override { return EMediaType::Real; }
};
protected:
hecl::Database::Project* m_proj = nullptr;
const hecl::Database::DataSpecEntry* m_origSpec = nullptr;
const hecl::Database::DataSpecEntry* m_pcSpec = nullptr;
/* Used to resolve cooked paths */
std::unique_ptr<hecl::Database::IDataSpec> m_cookSpec;
metaforce::CFactoryMgr m_factoryMgr;
std::list<std::shared_ptr<AsyncTask>> m_asyncLoadList;
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator> m_asyncLoadMap;
std::shared_ptr<AsyncTask> _AddTask(const std::shared_ptr<AsyncTask>& ptr);
std::list<std::shared_ptr<AsyncTask>>::iterator _RemoveTask(std::list<std::shared_ptr<AsyncTask>>::iterator it);
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator
_RemoveTask(std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator it);
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator
_RemoveTask(const SObjectTag& tag);
static AsyncTask& _GetAsyncTask(std::list<std::shared_ptr<AsyncTask>>::iterator it) { return **it; }
static AsyncTask&
_GetAsyncTask(std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator it) {
return **it->second;
}
bool PrepForReadSync(const SObjectTag& tag, const hecl::ProjectPath& path,
std::optional<athena::io::FileReader>& fr);
bool WaitForTagReady(const metaforce::SObjectTag& tag, const hecl::ProjectPath*& pathOut) {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).waitForTagReady(tag, pathOut);
}
SObjectTag TagFromPath(const hecl::ProjectPath& path) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).tagFromPath(path);
}
SObjectTag BuildTagFromPath(const hecl::ProjectPath& path) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).buildTagFromPath(path);
}
void GetTagListForFile(const char* pakName, std::vector<SObjectTag>& out) const override {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).getTagListForFile(pakName, out);
}
void CancelBackgroundIndex() {
if (m_cookSpec)
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).cancelBackgroundIndex();
}
void BeginBackgroundIndex(hecl::Database::Project& proj, const hecl::Database::DataSpecEntry& origSpec,
const hecl::Database::DataSpecEntry& pcSpec);
bool SyncCook(const hecl::ProjectPath& working);
CFactoryFnReturn BuildSync(const SObjectTag& tag, const hecl::ProjectPath& path, const CVParamTransfer& paramXfer,
CObjectReference* selfRef);
std::shared_ptr<AsyncTask> BuildAsyncInternal(const metaforce::SObjectTag&, const metaforce::CVParamTransfer&,
std::unique_ptr<metaforce::IObj>*, CObjectReference* selfRef);
public:
ProjectResourceFactoryBase(hecl::ClientProcess& clientProc) : m_clientProc(clientProc) {}
std::unique_ptr<metaforce::IObj> Build(const metaforce::SObjectTag&, const metaforce::CVParamTransfer&,
CObjectReference* selfRef) override;
void BuildAsync(const metaforce::SObjectTag&, const metaforce::CVParamTransfer&, std::unique_ptr<metaforce::IObj>*,
CObjectReference* selfRef) override;
void CancelBuild(const metaforce::SObjectTag&) override;
bool CanBuild(const metaforce::SObjectTag&) override;
const metaforce::SObjectTag* GetResourceIdByName(std::string_view) const override;
FourCC GetResourceTypeById(CAssetId id) const override;
hecl::ProjectPath GetCookedPath(const hecl::ProjectPath& working, bool pcTarget) const {
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).getCookedPath(working, pcTarget);
}
void EnumerateResources(const std::function<bool(const SObjectTag&)>& lambda) const override;
void EnumerateNamedResources(const std::function<bool(std::string_view, const SObjectTag&)>& lambda) const override;
u32 ResourceSize(const SObjectTag& tag) override;
std::shared_ptr<metaforce::IDvdRequest> LoadResourceAsync(const metaforce::SObjectTag& tag, void* target) override;
std::shared_ptr<metaforce::IDvdRequest> LoadResourcePartAsync(const metaforce::SObjectTag& tag, u32 off, u32 size,
void* target) override;
std::unique_ptr<u8[]> LoadResourceSync(const metaforce::SObjectTag& tag) override;
std::unique_ptr<u8[]> LoadNewResourcePartSync(const metaforce::SObjectTag& tag, u32 off, u32 size) override;
std::shared_ptr<AsyncTask> CookResourceAsync(const metaforce::SObjectTag& tag);
template <typename ItType>
bool AsyncPumpTask(ItType& it);
void AsyncIdle() override;
void Shutdown() { CancelBackgroundIndex(); }
bool IsBusy() const { return m_asyncLoadMap.size() != 0; }
SObjectTag TagFromPath(hecl::SystemStringView path) const { return TagFromPath(hecl::ProjectPath(*m_proj, path)); }
~ProjectResourceFactoryBase() override { Shutdown(); }
};
} // namespace metaforce

View File

@ -1,84 +0,0 @@
#include "Editor/ProjectResourceFactoryMP1.hpp"
#include "Runtime/CDependencyGroup.hpp"
#include "Runtime/CGameHintInfo.hpp"
#include "Runtime/CSaveWorld.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/IOStreams.hpp"
#include "Runtime/Audio/CAudioGroupSet.hpp"
#include "Runtime/Audio/CMidiManager.hpp"
#include "Runtime/Audio/CSfxManager.hpp"
#include "Runtime/AutoMapper/CMapArea.hpp"
#include "Runtime/AutoMapper/CMapUniverse.hpp"
#include "Runtime/AutoMapper/CMapWorld.hpp"
#include "Runtime/CScannableObjectInfo.hpp"
#include "Runtime/Character/CAllFormatsAnimSource.hpp"
#include "Runtime/Character/CAnimCharacterSet.hpp"
#include "Runtime/Character/CAnimPOIData.hpp"
#include "Runtime/Character/CCharLayoutInfo.hpp"
#include "Runtime/Character/CSkinRules.hpp"
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"
#include "Runtime/Collision/CCollisionResponseData.hpp"
#include "Runtime/Graphics/CModel.hpp"
#include "Runtime/Graphics/CTexture.hpp"
#include "Runtime/GuiSys/CGuiFrame.hpp"
#include "Runtime/GuiSys/CRasterFont.hpp"
#include "Runtime/GuiSys/CStringTable.hpp"
#include "Runtime/Particle/CDecalDataFactory.hpp"
#include "Runtime/Particle/CElectricDescription.hpp"
#include "Runtime/Particle/CGenDescription.hpp"
#include "Runtime/Particle/CParticleDataFactory.hpp"
#include "Runtime/Particle/CParticleElectricDataFactory.hpp"
#include "Runtime/Particle/CParticleSwooshDataFactory.hpp"
#include "Runtime/Particle/CProjectileWeaponDataFactory.hpp"
#include "Runtime/Particle/CSwooshDescription.hpp"
#include "Runtime/Particle/CWeaponDescription.hpp"
#include "Runtime/World/CPathFindArea.hpp"
#include "DataSpec/DNACommon/TXTR.hpp"
namespace DataSpec {
extern hecl::Database::DataSpecEntry SpecEntMP1;
extern hecl::Database::DataSpecEntry SpecEntMP1PC;
} // namespace DataSpec
namespace metaforce {
ProjectResourceFactoryMP1::ProjectResourceFactoryMP1(hecl::ClientProcess& clientProc)
: ProjectResourceFactoryBase(clientProc) {
m_factoryMgr.AddFactory(FOURCC('TXTR'), FMemFactoryFunc(FTextureFactory));
m_factoryMgr.AddFactory(FOURCC('PART'), FFactoryFunc(FParticleFactory));
m_factoryMgr.AddFactory(FOURCC('FRME'), FFactoryFunc(RGuiFrameFactoryInGame));
m_factoryMgr.AddFactory(FOURCC('FONT'), FFactoryFunc(FRasterFontFactory));
m_factoryMgr.AddFactory(FOURCC('CMDL'), FMemFactoryFunc(FModelFactory));
m_factoryMgr.AddFactory(FOURCC('CINF'), FFactoryFunc(FCharLayoutInfo));
m_factoryMgr.AddFactory(FOURCC('CSKR'), FFactoryFunc(FSkinRulesFactory));
m_factoryMgr.AddFactory(FOURCC('ANCS'), FFactoryFunc(FAnimCharacterSet));
m_factoryMgr.AddFactory(FOURCC('ANIM'), FFactoryFunc(AnimSourceFactory));
m_factoryMgr.AddFactory(FOURCC('EVNT'), FFactoryFunc(AnimPOIDataFactory));
m_factoryMgr.AddFactory(FOURCC('DCLN'), FFactoryFunc(FCollidableOBBTreeGroupFactory));
m_factoryMgr.AddFactory(FOURCC('DGRP'), FFactoryFunc(FDependencyGroupFactory));
m_factoryMgr.AddFactory(FOURCC('AGSC'), FMemFactoryFunc(FAudioGroupSetDataFactory));
m_factoryMgr.AddFactory(FOURCC('CSNG'), FFactoryFunc(FMidiDataFactory));
m_factoryMgr.AddFactory(FOURCC('ATBL'), FFactoryFunc(FAudioTranslationTableFactory));
m_factoryMgr.AddFactory(FOURCC('STRG'), FFactoryFunc(FStringTableFactory));
m_factoryMgr.AddFactory(FOURCC('HINT'), FFactoryFunc(FHintFactory));
m_factoryMgr.AddFactory(FOURCC('SAVW'), FFactoryFunc(FSaveWorldFactory));
m_factoryMgr.AddFactory(FOURCC('MAPW'), FFactoryFunc(FMapWorldFactory));
m_factoryMgr.AddFactory(FOURCC('SCAN'), FFactoryFunc(FScannableObjectInfoFactory));
m_factoryMgr.AddFactory(FOURCC('CRSC'), FFactoryFunc(FCollisionResponseDataFactory));
m_factoryMgr.AddFactory(FOURCC('SWHC'), FFactoryFunc(FParticleSwooshDataFactory));
m_factoryMgr.AddFactory(FOURCC('ELSC'), FFactoryFunc(FParticleElectricDataFactory));
m_factoryMgr.AddFactory(FOURCC('WPSC'), FFactoryFunc(FProjectileWeaponDataFactory));
m_factoryMgr.AddFactory(FOURCC('DPSC'), FFactoryFunc(FDecalDataFactory));
m_factoryMgr.AddFactory(FOURCC('MAPA'), FFactoryFunc(FMapAreaFactory));
m_factoryMgr.AddFactory(FOURCC('MAPU'), FFactoryFunc(FMapUniverseFactory));
m_factoryMgr.AddFactory(FOURCC('PATH'), FMemFactoryFunc(FPathFindAreaFactory));
}
void ProjectResourceFactoryMP1::IndexMP1Resources(hecl::Database::Project& proj, CSimplePool& sp) {
BeginBackgroundIndex(proj, DataSpec::SpecEntMP1, DataSpec::SpecEntMP1PC);
}
} // namespace metaforce

View File

@ -1,16 +0,0 @@
#pragma once
#include "Editor/ProjectResourceFactoryBase.hpp"
#include "Runtime/CToken.hpp"
namespace metaforce {
class MP1OriginalIDs;
class CSimplePool;
class ProjectResourceFactoryMP1 : public ProjectResourceFactoryBase {
public:
ProjectResourceFactoryMP1(hecl::ClientProcess& clientProc);
void IndexMP1Resources(hecl::Database::Project& proj, CSimplePool& sp);
};
} // namespace metaforce

View File

@ -1,12 +0,0 @@
#include "Resource.hpp"
namespace metaforce {
Space::Class Resource::DeduceDefaultSpaceClass(const hecl::ProjectPath& path) {
athena::io::FileReader r(path.getAbsolutePath(), 32 * 1024, false);
if (r.hasError())
return Space::Class::None;
return Space::Class::None;
}
} // namespace metaforce

View File

@ -1,46 +0,0 @@
#pragma once
#include "hecl/Database.hpp"
#include "Space.hpp"
namespace metaforce {
/** Combines a ProjectPath with actively used Space references
*
* This class is intended to be heap-allocated in a hierarchical mapping, so the entire tree
* of resources is available in-memory to systems that need it. Refreshes of the index will
* continue to use existing allocations that haven't been deleted.
*
* The key purpose of this class is to centrally register observer-nodes for resources that
* are updated via editing, or external file changes.
*/
class Resource {
public:
using ProjectDataSpec = hecl::Database::Project::ProjectDataSpec;
private:
hecl::ProjectPath m_path;
Space::Class m_defaultClass = Space::Class::None;
// EditorSpace* m_editingSpace = nullptr;
std::vector<ViewerSpace*> m_viewingSpaces;
public:
static Space::Class DeduceDefaultSpaceClass(const hecl::ProjectPath& path);
explicit Resource(hecl::ProjectPath&& path)
: m_path(std::move(path)), m_defaultClass(DeduceDefaultSpaceClass(m_path)) {}
const hecl::ProjectPath& path() const { return m_path; }
};
/** Provides centralized hierarchical lookup and ownership of Resource nodes */
class ResourceTree {
public:
struct Node {
std::map<hecl::ProjectPath, std::unique_ptr<Node>> m_subnodes;
std::map<hecl::ProjectPath, std::unique_ptr<Resource>> m_resources;
};
private:
std::unique_ptr<Node> m_rootNode;
};
} // namespace metaforce

View File

@ -1,70 +0,0 @@
#include "ResourceBrowser.hpp"
namespace metaforce {
#define BROWSER_MARGIN 8
bool ResourceBrowser::navigateToPath(const hecl::ProjectPath& pathIn) {
if (pathIn.isFile())
m_path = pathIn.getParentPath();
else
m_path = pathIn;
m_comps = m_path.getPathComponents();
hecl::DirectoryEnumerator dEnum(
m_path.getAbsolutePath(), hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
m_state.sortColumn == State::SortColumn::Size,
m_state.sortDir == specter::SortDirection::Descending &&
(m_state.sortColumn == State::SortColumn::Name || m_state.sortColumn == State::SortColumn::Size),
true);
m_resListingBind.updateListing(dEnum);
if (m_pathButtons)
m_pathButtons->setButtons(m_comps);
if (m_view) {
m_view->m_resListing.m_view->selectRow(-1);
m_view->m_resListing.m_view->updateData();
m_view->updateSize();
}
return true;
}
void ResourceBrowser::pathButtonActivated(size_t idx) {
if (idx >= m_comps.size())
return;
hecl::SystemString dir;
bool needSlash = false;
size_t i = 0;
for (const hecl::SystemString& d : m_comps) {
if (needSlash)
dir += _SYS_STR('/');
if (d != _SYS_STR("/"))
needSlash = true;
dir += d;
if (++i > idx)
break;
}
navigateToPath(hecl::ProjectPath(*m_vm.project(), dir));
}
void ResourceBrowser::View::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) {
m_resListing.mouseDown(coord, button, mod);
}
void ResourceBrowser::View::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) {
m_resListing.mouseUp(coord, button, mod);
}
void ResourceBrowser::View::mouseMove(const boo::SWindowCoord& coord) { m_resListing.mouseMove(coord); }
void ResourceBrowser::View::mouseLeave(const boo::SWindowCoord& coord) { m_resListing.mouseLeave(coord); }
void ResourceBrowser::View::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) {
specter::View::resized(root, sub);
m_resListing.m_view->resized(root, sub);
}
void ResourceBrowser::View::draw(boo::IGraphicsCommandQueue* gfxQ) { m_resListing.m_view->draw(gfxQ); }
} // namespace metaforce

View File

@ -1,205 +0,0 @@
#pragma once
#include "Space.hpp"
#include "ViewManager.hpp"
#include "specter/PathButtons.hpp"
#include "specter/Table.hpp"
namespace metaforce {
class ResourceBrowser : public Space, public specter::IPathButtonsBinding {
struct State : Space::State {
AT_DECL_DNA_YAMLV
String<-1> path;
Value<float> columnSplits[3] = {0.0f, 0.7f, 0.9f};
enum class SortColumn { Name, Type, Size };
Value<SortColumn> sortColumn = SortColumn::Name;
Value<specter::SortDirection> sortDir = specter::SortDirection::Ascending;
} m_state;
const Space::State& spaceState() const override { return m_state; }
hecl::ProjectPath m_path;
std::vector<hecl::SystemString> m_comps;
void pathButtonActivated(size_t idx) override;
struct ResListingDataBind : specter::ITableDataBinding, specter::ITableStateBinding {
ResourceBrowser& m_rb;
struct Entry {
hecl::SystemString m_path;
std::string m_name;
std::string m_type;
std::string m_size;
};
std::vector<Entry> m_entries;
std::string m_nameCol;
std::string m_typeCol;
std::string m_sizeCol;
std::string m_dirStr;
std::string m_projStr;
std::string m_fileStr;
size_t columnCount() const override { return 3; }
size_t rowCount() const override { return m_entries.size(); }
std::string_view header(size_t cIdx) const override {
switch (cIdx) {
case 0:
return m_nameCol;
case 1:
return m_typeCol;
case 2:
return m_sizeCol;
default:
break;
}
return {};
}
std::string_view cell(size_t cIdx, size_t rIdx) const override {
switch (cIdx) {
case 0:
return m_entries.at(rIdx).m_name;
case 1:
return m_entries.at(rIdx).m_type;
case 2:
return m_entries.at(rIdx).m_size;
default:
break;
}
return {};
}
bool columnSplitResizeAllowed() const override { return true; }
float getColumnSplit(size_t cIdx) const override { return m_rb.m_state.columnSplits[cIdx]; }
void setColumnSplit(size_t cIdx, float split) override { m_rb.m_state.columnSplits[cIdx] = split; }
void updateListing(const hecl::DirectoryEnumerator& dEnum) {
m_entries.clear();
m_entries.reserve(dEnum.size());
for (const hecl::DirectoryEnumerator::Entry& d : dEnum) {
m_entries.emplace_back();
Entry& ent = m_entries.back();
ent.m_path = d.m_path;
hecl::SystemUTF8Conv nameUtf8(d.m_name);
ent.m_name = nameUtf8.str();
if (d.m_isDir) {
if (hecl::SearchForProject(d.m_path))
ent.m_type = m_projStr;
else
ent.m_type = m_dirStr;
} else {
ent.m_type = m_fileStr;
ent.m_size = hecl::HumanizeNumber(d.m_fileSz, 7, nullptr, int(hecl::HNScale::AutoScale),
hecl::HNFlags::B | hecl::HNFlags::Decimal);
}
}
m_needsUpdate = false;
}
bool m_needsUpdate = false;
specter::SortDirection getSort(size_t& cIdx) const override {
cIdx = size_t(m_rb.m_state.sortColumn);
if (cIdx > 2)
cIdx = 0;
return m_rb.m_state.sortDir;
}
void setSort(size_t cIdx, specter::SortDirection dir) override {
m_rb.m_state.sortDir = dir;
m_rb.m_state.sortColumn = State::SortColumn(cIdx);
m_needsUpdate = true;
}
void setSelectedRow(size_t rIdx) override {}
void rowActivated(size_t rIdx) override {}
ResListingDataBind(ResourceBrowser& rb, const specter::IViewManager& vm) : m_rb(rb) {
m_nameCol = vm.translate<locale::name>();
m_typeCol = vm.translate<locale::type>();
m_sizeCol = vm.translate<locale::size>();
m_dirStr = vm.translate<locale::directory>();
m_projStr = vm.translate<locale::hecl_project>();
m_fileStr = vm.translate<locale::file>();
}
} m_resListingBind;
struct View : specter::View {
ResourceBrowser& m_ro;
specter::ViewChild<std::unique_ptr<specter::Table>> m_resListing;
View(ResourceBrowser& ro, specter::ViewResources& res) : specter::View(res, ro.m_vm.rootView()), m_ro(ro) {
m_resListing.m_view.reset(new specter::Table(res, *this, &ro.m_resListingBind, &ro.m_resListingBind, 3));
}
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) override;
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) override;
void mouseMove(const boo::SWindowCoord&) override;
void mouseLeave(const boo::SWindowCoord&) override;
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) override;
void draw(boo::IGraphicsCommandQueue* gfxQ) override;
};
std::unique_ptr<View> m_view;
std::unique_ptr<specter::PathButtons> m_pathButtons;
public:
ResourceBrowser(ViewManager& vm, Space* parent)
: Space(vm, Class::ResourceBrowser, parent), m_resListingBind(*this, vm) {
reloadState();
}
ResourceBrowser(ViewManager& vm, Space* parent, const ResourceBrowser& other) : ResourceBrowser(vm, parent) {
m_state = other.m_state;
reloadState();
}
ResourceBrowser(ViewManager& vm, Space* parent, ConfigReader& r) : ResourceBrowser(vm, parent) {
m_state.read(r);
reloadState();
}
void reloadState() override {
hecl::ProjectPath pp(*m_vm.project(), m_state.path);
if (m_state.path.empty() || pp.isNone()) {
m_state.path = m_vm.project()->getProjectWorkingPath().getRelativePathUTF8();
navigateToPath(hecl::ProjectPath(*m_vm.project(), m_state.path));
} else
navigateToPath(pp);
}
void think() override {
if (m_resListingBind.m_needsUpdate)
reloadState();
}
bool navigateToPath(const hecl::ProjectPath& path);
Space* copy(Space* parent) const override { return new ResourceBrowser(m_vm, parent, *this); }
void buildToolbarView(specter::ViewResources& res, specter::Toolbar& tb) override {
m_pathButtons.reset(new specter::PathButtons(res, tb, *this, true));
tb.push_back(m_pathButtons.get(), 1);
reloadState();
}
specter::View* buildContentView(specter::ViewResources& res) override {
m_view.reset(new View(*this, res));
return m_view.get();
}
bool usesToolbar() const override { return true; }
unsigned toolbarUnits() const override { return 2; }
};
} // namespace metaforce

View File

@ -1,262 +0,0 @@
#include "Space.hpp"
#include "ViewManager.hpp"
#include "ResourceBrowser.hpp"
#include "ParticleEditor.hpp"
#include "ModelViewer.hpp"
#include "InformationCenter.hpp"
#include "GameMode.hpp"
#include "icons/icons.hpp"
#include "specter/Menu.hpp"
namespace metaforce {
static logvisor::Module Log("URDE::Space");
Space::Space(ViewManager& vm, Class cls, Space* parent)
: m_spaceMenuNode(*this), m_spaceSelectBind(*this), m_vm(vm), m_class(cls), m_parent(parent) {}
specter::View* Space::buildSpaceView(specter::ViewResources& res) {
if (usesToolbar()) {
m_spaceView.reset(
new specter::Space(res, *m_parent->basisView(), *this, specter::Toolbar::Position::Bottom, toolbarUnits()));
specter::View* sview = buildContentView(res);
m_spaceView->setContentView(sview);
specter::Toolbar& tb = *m_spaceView->toolbar();
specter::Icon* classIcon = SpaceMenuNode::LookupClassIcon(m_class);
const zeus::CColor* classColor = SpaceMenuNode::LookupClassColor(m_class);
m_spaceSelectButton.reset(new specter::Button(res, tb, &m_spaceSelectBind, "", classIcon,
specter::Button::Style::Block,
classColor ? *classColor : zeus::skWhite));
tb.push_back(m_spaceSelectButton.get(), 0);
buildToolbarView(res, tb);
return m_spaceView.get();
} else {
m_spaceView.reset(new specter::Space(res, *m_parent->basisView(), *this, specter::Toolbar::Position::None, 0));
specter::View* sview = buildContentView(res);
m_spaceView->setContentView(sview);
return m_spaceView.get();
}
}
std::vector<Space::SpaceMenuNode::SubNodeData> Space::SpaceMenuNode::s_subNodeDats = {
{Class::ResourceBrowser,
"Resource Browser",
GetIcon(SpaceIcon::ResourceBrowser),
{0.0f, 1.0f, 0.0f, 1.0f}},
{Class::EffectEditor,
"Effect Editor",
GetIcon(SpaceIcon::ParticleEditor),
{1.0f, 0.5f, 0.0f, 1.0f}},
{Class::ModelViewer, "Model Viewer", GetIcon(SpaceIcon::ModelViewer), {0.95f, 0.95f, 0.95f, 1.0f}},
{Class::InformationCenter,
"Information Center",
GetIcon(SpaceIcon::InformationCenter),
{0.0f, 1.0f, 1.0f, 1.0f}},
{Class::GameMode, "Game Mode", GetIcon(SpaceIcon::GameMode), {}}};
std::string Space::SpaceMenuNode::s_text = "Space Types";
template <typename Key>
static void RecurseTranslations(ViewManager& vm, std::vector<Space::SpaceMenuNode::SubNodeData>::iterator it) {
it->m_text = vm.translate<Key>();
}
template <typename Key, typename Key2, typename... RemKeys>
static void RecurseTranslations(ViewManager& vm, std::vector<Space::SpaceMenuNode::SubNodeData>::iterator it) {
RecurseTranslations<Key>(vm, it);
RecurseTranslations<Key2, RemKeys...>(vm, ++it);
}
void Space::SpaceMenuNode::InitializeStrings(ViewManager& vm) {
s_text = vm.translate<locale::space_types>();
RecurseTranslations<locale::resource_browser,
locale::effect_editor,
locale::model_viewer,
locale::information_center,
locale::game_mode>(vm, s_subNodeDats.begin());
}
std::unique_ptr<specter::View> Space::SpaceSelectBind::buildMenu(const specter::Button* button) {
return std::unique_ptr<specter::View>(
new specter::Menu(m_space.m_vm.rootView().viewRes(), *m_space.m_spaceView, &m_space.m_spaceMenuNode));
}
specter::View* RootSpace::buildSpaceView(specter::ViewResources& res) {
specter::View* newRoot = buildContentView(res);
m_vm.RootSpaceViewBuilt(newRoot);
return newRoot;
}
specter::View* RootSpace::basisView() { return &m_vm.rootView(); }
specter::View* SplitSpace::buildContentView(specter::ViewResources& res) {
int clearance = res.pixelFactor() * SPECTER_TOOLBAR_GAUGE;
m_splitView.reset(
new specter::SplitView(res, *m_parent->basisView(), this, m_state.axis, m_state.split, clearance, clearance));
if (m_slots[0])
m_splitView->setContentView(0, m_slots[0]->buildSpaceView(res));
if (m_slots[1])
m_splitView->setContentView(1, m_slots[1]->buildSpaceView(res));
return m_splitView.get();
}
void SplitSpace::setChildSlot(unsigned slot, std::unique_ptr<Space>&& space) {
if (slot > 1)
Log.report(logvisor::Fatal, FMT_STRING("invalid slot {} for SplitView"), slot);
m_slots[slot] = std::move(space);
m_slots[slot]->m_parent = this;
}
void SplitSpace::joinViews(specter::SplitView* thisSplit, int thisSlot, specter::SplitView* otherSplit, int otherSlot) {
if (thisSplit == otherSplit) {
SplitSpace* thisSS = m_slots[thisSlot]->castToSplitSpace();
if (thisSS) {
int ax = thisSS->m_state.axis == specter::SplitView::Axis::Horizontal ? 1 : 0;
const boo::SWindowRect& thisRect = m_splitView->subRect();
const boo::SWindowRect& subRect = thisSS->m_splitView->subRect();
int splitPx = subRect.location[ax] + subRect.size[ax] * thisSS->m_state.split - thisRect.location[ax];
thisSS->m_state.split = splitPx / float(thisRect.size[ax]);
}
m_parent->exchangeSpaceSplitJoin(this, std::move(m_slots[thisSlot]));
m_vm.BuildSpaceViews();
} else {
for (int i = 0; i < 2; ++i) {
SplitSpace* otherSS = m_slots[i]->castToSplitSpace();
if (otherSS && otherSS->m_splitView.get() == otherSplit) {
int ax = m_state.axis == specter::SplitView::Axis::Horizontal ? 1 : 0;
const boo::SWindowRect& thisRect = m_splitView->subRect();
const boo::SWindowRect& subRect = otherSS->m_splitView->subRect();
int splitPx = subRect.location[ax] + subRect.size[ax] * otherSS->m_state.split - thisRect.location[ax];
m_state.split = splitPx / float(thisRect.size[ax]);
exchangeSpaceSplitJoin(otherSS, std::move(otherSS->m_slots[otherSlot ^ 1]));
m_vm.BuildSpaceViews();
break;
}
}
}
}
specter::ISplitSpaceController* Space::spaceSplit(specter::SplitView::Axis axis, int thisSlot) {
if (m_parent) {
/* Reject split operations with insufficient clearance */
int clearance = m_vm.m_viewResources.pixelFactor() * SPECTER_TOOLBAR_GAUGE;
if (axis == specter::SplitView::Axis::Horizontal) {
if (m_spaceView->subRect().size[1] <= clearance)
return nullptr;
} else {
if (m_spaceView->subRect().size[0] <= clearance)
return nullptr;
}
SplitSpace* ss = new SplitSpace(m_vm, m_parent, axis);
ss->setChildSlot(thisSlot, m_parent->exchangeSpaceSplitJoin(this, std::unique_ptr<Space>(ss)));
ss->setChildSlot(thisSlot ^ 1, std::unique_ptr<Space>(copy(ss)));
m_vm.BuildSpaceViews();
return ss;
}
return nullptr;
}
std::unique_ptr<Space> RootSpace::exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) {
std::unique_ptr<Space> ret = std::move(keepSpace);
if (removeSpace == m_spaceTree.get()) {
m_spaceTree.swap(ret);
m_spaceTree->m_parent = this;
} else
Log.report(logvisor::Fatal, FMT_STRING("RootSpace::exchangeSpaceSplitJoin() failure"));
return ret;
}
std::unique_ptr<Space> SplitSpace::exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) {
std::unique_ptr<Space> ret = std::move(keepSpace);
if (removeSpace == m_slots[0].get()) {
m_slots[0].swap(ret);
m_slots[0]->m_parent = this;
} else if (removeSpace == m_slots[1].get()) {
m_slots[1].swap(ret);
m_slots[1]->m_parent = this;
} else
Log.report(logvisor::Fatal, FMT_STRING("SplitSpace::exchangeSpaceSplitJoin() failure"));
return ret;
}
template <class Reader>
static Space* BuildNewSpace(ViewManager& vm, Space::Class cls, Space* parent, Reader& r) {
using Class = Space::Class;
switch (cls) {
case Class::SplitSpace:
return new SplitSpace(vm, parent, r);
case Class::ResourceBrowser:
return new ResourceBrowser(vm, parent, r);
case Class::EffectEditor:
return new EffectEditor(vm, parent, r);
case Class::ModelViewer:
return new ModelViewer(vm, parent, r);
case Class::InformationCenter:
return new InformationCenter(vm, parent, r);
case Class::GameMode:
return new GameMode(vm, parent, r);
default:
break;
}
return nullptr;
}
void Space::saveState(athena::io::IStreamWriter& w) const {
w.writeUint32Big(atUint32(m_class));
spaceState().write(w);
}
void Space::saveState(athena::io::YAMLDocWriter& w) const {
w.writeUint32("class", atUint32(m_class));
spaceState().write(w);
}
Space* Space::NewSpaceFromConfigStream(ViewManager& vm, Space* parent, ConfigReader& r) {
Class cls = Class(r.readUint32("class"));
return BuildNewSpace(vm, cls, parent, r);
}
RootSpace* Space::NewRootSpaceFromConfigStream(ViewManager& vm, ConfigReader& r) {
Class cls = Class(r.readUint32("class"));
if (cls != Class::RootSpace)
return nullptr;
return new RootSpace(vm, r);
}
void Space::SpaceMenuNode::SubNode::activated(const boo::SWindowCoord& coord) {
std::unique_ptr<Space> newSpace;
switch (m_data.m_cls) {
case Class::InformationCenter:
if (m_space.cls() == Class::InformationCenter)
newSpace.reset(new InformationCenter(m_space.m_parent->m_vm, m_space.m_parent));
break;
case Class::EffectEditor:
if (m_space.cls() == Class::EffectEditor)
newSpace.reset(new EffectEditor(m_space.m_parent->m_vm, m_space.m_parent));
break;
case Class::ResourceBrowser:
if (m_space.cls() == Class::ResourceBrowser)
newSpace.reset(new ResourceBrowser(m_space.m_parent->m_vm, m_space.m_parent));
break;
case Class::ModelViewer:
if (m_space.cls() == Class::ModelViewer)
newSpace.reset(new ModelViewer(m_space.m_parent->m_vm, m_space.m_parent));
break;
case Class::GameMode:
if (m_space.cls() == Class::GameMode)
newSpace.reset(new GameMode(m_space.m_parent->m_vm, m_space.m_parent));
break;
default:
break;
}
if (newSpace) {
Space* parent = m_space.m_parent;
m_space.m_parent->exchangeSpaceSplitJoin(&m_space, std::move(newSpace));
parent->m_vm.BuildSpaceViews();
}
}
} // namespace metaforce

View File

@ -1,378 +0,0 @@
#pragma once
#include "athena/DNAYaml.hpp"
#include "specter/Space.hpp"
#include "specter/IMenuNode.hpp"
#include "specter/Button.hpp"
#include "specter/MultiLineTextView.hpp"
#include "specter/ViewResources.hpp"
#include "specter/RootView.hpp"
#include "ProjectManager.hpp"
namespace specter {
class View;
class SplitView;
class ViewResources;
class Toolbar;
struct Icon;
} // namespace specter
namespace metaforce {
class ViewManager;
class RootSpace;
class SplitSpace;
class Space : public specter::ISpaceController {
friend class SplitSpace;
public:
virtual ~Space() = default;
Space(const Space& other) = delete;
Space& operator=(const Space& other) = delete;
/** Common encoded-enumeration for all space classes */
enum class Class {
None,
RootSpace,
SplitSpace,
TestSpace,
ResourceBrowser,
ModelViewer,
EffectEditor,
InformationCenter,
GameMode
};
struct State : athena::io::DNAVYaml<athena::Endian::Big> {
Delete _d;
};
static Space* NewSpaceFromConfigStream(ViewManager& vm, Space* parent, ConfigReader& r);
static RootSpace* NewRootSpaceFromConfigStream(ViewManager& vm, ConfigReader& r);
struct SpaceMenuNode final : specter::IMenuNode {
struct SubNodeData final : specter::IMenuNode {
Class m_cls;
std::string m_text;
specter::Icon& m_icon;
zeus::CColor m_color;
const std::string* text() const override { return &m_text; }
void activated(const boo::SWindowCoord& coord) override {}
SubNodeData(Class cls, const char* text, specter::Icon& icon, const zeus::CColor& color)
: m_cls(cls), m_text(text), m_icon(icon), m_color(color) {}
};
static std::vector<SubNodeData> s_subNodeDats;
struct SubNode final : specter::IMenuNode {
Space& m_space;
const SubNodeData& m_data;
const std::string* text() const override { return &m_data.m_text; }
void activated(const boo::SWindowCoord& coord) override;
SubNode(Space& space, const SubNodeData& data) : m_space(space), m_data(data) {}
};
std::vector<SubNode> m_subNodes;
SpaceMenuNode(Space& space) {
m_subNodes.reserve(s_subNodeDats.size());
for (const SubNodeData& sn : s_subNodeDats)
m_subNodes.emplace_back(space, sn);
}
static std::string s_text;
const std::string* text() const override { return &s_text; }
size_t subNodeCount() const override { return m_subNodes.size(); }
IMenuNode* subNode(size_t idx) override { return &m_subNodes[idx]; }
static void InitializeStrings(ViewManager& vm);
static const std::string* LookupClassString(Class cls) {
for (const SubNodeData& sn : s_subNodeDats)
if (sn.m_cls == cls)
return &sn.m_text;
return nullptr;
}
static specter::Icon* LookupClassIcon(Class cls) {
for (SubNodeData& sn : s_subNodeDats)
if (sn.m_cls == cls)
return &sn.m_icon;
return nullptr;
}
static const zeus::CColor* LookupClassColor(Class cls) {
for (SubNodeData& sn : s_subNodeDats)
if (sn.m_cls == cls)
return &sn.m_color;
return nullptr;
}
} m_spaceMenuNode;
struct SpaceSelectBind : specter::IButtonBinding {
Space& m_space;
std::string_view name(const specter::Control* control) const override { return SpaceMenuNode::s_text; }
MenuStyle menuStyle(const specter::Button* button) const override { return MenuStyle::Primary; }
std::unique_ptr<specter::View> buildMenu(const specter::Button* button) override;
SpaceSelectBind(Space& space) : m_space(space) {}
} m_spaceSelectBind;
std::unique_ptr<specter::Button> m_spaceSelectButton;
protected:
friend class ViewManager;
friend class RootSpace;
ViewManager& m_vm;
Class m_class = Class::None;
Space* m_parent;
std::unique_ptr<specter::Space> m_spaceView;
Space(ViewManager& vm, Class cls, Space* parent);
/* Allows common Space code to access DNA-encoded state */
virtual const Space::State& spaceState() const = 0;
/* Structural control */
virtual bool usesToolbar() const { return false; }
virtual unsigned toolbarUnits() const { return 1; }
virtual void buildToolbarView(specter::ViewResources& res, specter::Toolbar& tb) {}
virtual specter::View* buildContentView(specter::ViewResources& res) = 0;
virtual specter::View* buildSpaceView(specter::ViewResources& res);
public:
virtual void saveState(athena::io::IStreamWriter& w) const;
virtual void saveState(athena::io::YAMLDocWriter& w) const;
virtual void reloadState() {}
virtual void think() {}
virtual Space* copy(Space* parent) const = 0;
bool spaceSplitAllowed() const override { return true; }
specter::ISplitSpaceController* spaceSplit(specter::SplitView::Axis axis, int thisSlot) override;
virtual std::unique_ptr<Space> exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) {
return std::unique_ptr<Space>();
}
virtual specter::View* basisView() { return m_spaceView.get(); }
Class cls() const { return m_class; }
SplitSpace* castToSplitSpace();
};
class RootSpace : public Space {
friend class ViewManager;
std::unique_ptr<specter::RootView> m_rootView;
std::unique_ptr<Space> m_spaceTree;
struct State : Space::State{AT_DECL_DNA_YAMLV } m_state;
const Space::State& spaceState() const override { return m_state; }
public:
RootSpace(ViewManager& vm) : Space(vm, Class::RootSpace, nullptr) {}
RootSpace(ViewManager& vm, ConfigReader& r) : RootSpace(vm) {
m_state.read(r);
if (auto rec = r.enterSubRecord("spaceTree"))
m_spaceTree.reset(NewSpaceFromConfigStream(vm, this, r));
}
void think() override {
OPTICK_EVENT();
if (m_spaceTree)
m_spaceTree->think();
}
void saveState(athena::io::IStreamWriter& w) const override {
w.writeUint32Big(atUint32(m_class));
m_state.write(w);
if (m_spaceTree)
m_spaceTree->saveState(w);
else
w.writeUint32Big(0);
}
void saveState(athena::io::YAMLDocWriter& w) const override {
w.writeUint32("class", atUint32(m_class));
m_state.write(w);
if (auto rec = w.enterSubRecord("spaceTree")) {
if (m_spaceTree)
m_spaceTree->saveState(w);
else
w.writeUint32("class", 0);
}
}
void setChild(std::unique_ptr<Space>&& space) {
m_spaceTree = std::move(space);
m_spaceTree->m_parent = this;
}
Space* copy(Space* parent) const override { return nullptr; }
bool spaceSplitAllowed() const override { return false; }
specter::View* buildSpaceView(specter::ViewResources& res) override;
specter::View* buildContentView(specter::ViewResources& res) override { return m_spaceTree->buildSpaceView(res); }
std::unique_ptr<Space> exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) override;
specter::View* basisView() override;
};
class SplitSpace : public Space, public specter::ISplitSpaceController {
friend class ViewManager;
std::unique_ptr<Space> m_slots[2];
std::unique_ptr<specter::SplitView> m_splitView;
struct State : Space::State {
AT_DECL_DNA_YAMLV
Value<specter::SplitView::Axis> axis = specter::SplitView::Axis::Horizontal;
Value<float> split = 0.5;
} m_state;
const Space::State& spaceState() const override { return m_state; }
public:
SplitSpace(ViewManager& vm, Space* parent, specter::SplitView::Axis axis) : Space(vm, Class::SplitSpace, parent) {
m_state.axis = axis;
reloadState();
}
SplitSpace(ViewManager& vm, Space* parent, ConfigReader& r)
: SplitSpace(vm, parent, specter::SplitView::Axis::Horizontal) {
m_state.read(r);
if (auto rec = r.enterSubRecord("slot0"))
m_slots[0].reset(NewSpaceFromConfigStream(vm, this, r));
if (auto rec = r.enterSubRecord("slot1"))
m_slots[1].reset(NewSpaceFromConfigStream(vm, this, r));
reloadState();
}
void reloadState() override {
m_state.split = std::min(1.f, std::max(0.f, m_state.split));
if (m_state.axis != specter::SplitView::Axis::Horizontal && m_state.axis != specter::SplitView::Axis::Vertical)
m_state.axis = specter::SplitView::Axis::Horizontal;
if (m_splitView) {
m_splitView->setSplit(m_state.split);
m_splitView->setAxis(m_state.axis);
}
}
void think() override {
if (m_slots[0])
m_slots[0]->think();
if (m_slots[1])
m_slots[1]->think();
}
void saveState(athena::io::IStreamWriter& w) const override {
w.writeUint32Big(atUint32(m_class));
m_state.write(w);
if (m_slots[0])
m_slots[0]->saveState(w);
else
w.writeUint32Big(0);
if (m_slots[1])
m_slots[1]->saveState(w);
else
w.writeUint32Big(0);
}
void saveState(athena::io::YAMLDocWriter& w) const override {
w.writeUint32("class", atUint32(m_class));
m_state.write(w);
if (auto rec = w.enterSubRecord("slot0")) {
if (m_slots[0])
m_slots[0]->saveState(w);
else
w.writeUint32("class", 0);
}
if (auto rec = w.enterSubRecord("slot1")) {
if (m_slots[1])
m_slots[1]->saveState(w);
else
w.writeUint32("class", 0);
}
}
void setChildSlot(unsigned slot, std::unique_ptr<Space>&& space);
specter::View* buildSpaceView(specter::ViewResources& res) override { return buildContentView(res); }
specter::View* buildContentView(specter::ViewResources& res) override;
Space* copy(Space* parent) const override { return nullptr; }
bool spaceSplitAllowed() const override { return false; }
ISpaceController* spaceJoin(int keepSlot) {
if (m_parent) {
ISpaceController* ret = m_slots[keepSlot].get();
m_parent->exchangeSpaceSplitJoin(this, std::move(m_slots[keepSlot]));
return ret;
}
return nullptr;
}
std::unique_ptr<Space> exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) override;
specter::SplitView* splitView() override { return m_splitView.get(); }
void updateSplit(float split) override { m_state.split = split; }
void joinViews(specter::SplitView* thisSplit, int thisSlot, specter::SplitView* otherSplit, int otherSlot) override;
void setAxis(specter::SplitView::Axis axis) {
m_state.axis = axis;
reloadState();
}
specter::SplitView::Axis axis() const { return m_state.axis; }
float split() const { return m_state.split; }
specter::View* basisView() override { return m_splitView.get(); }
};
inline SplitSpace* Space::castToSplitSpace() {
return cls() == Class::SplitSpace ? static_cast<SplitSpace*>(this) : nullptr;
}
class ViewerSpace : public Space {
public:
ViewerSpace(ViewManager& vm, Class cls, Space* parent) : Space(vm, cls, parent) {}
};
class EditorSpace : public Space {
public:
EditorSpace(ViewManager& vm, Class cls, Space* parent) : Space(vm, cls, parent) {}
};
class GameSpace : public Space {
public:
GameSpace(ViewManager& vm, Class cls, Space* parent) : Space(vm, cls, parent) {}
};
class TestSpace : public Space {
std::unique_ptr<specter::Button> m_button;
std::unique_ptr<specter::MultiLineTextView> m_textView;
std::string m_contentStr;
std::string m_buttonStr;
specter::IButtonBinding* m_binding;
public:
TestSpace(ViewManager& vm, Space* parent, std::string_view content, std::string_view button,
specter::IButtonBinding* binding)
: Space(vm, Class::TestSpace, parent), m_contentStr(content), m_buttonStr(button), m_binding(binding) {}
struct State : Space::State{AT_DECL_DNA_YAMLV} m_state;
const Space::State& spaceState() const override { return m_state; }
bool usesToolbar() const override { return true; }
void buildToolbarView(specter::ViewResources& res, specter::Toolbar& tb) override {
m_button.reset(new specter::Button(res, tb, m_binding, m_buttonStr));
tb.push_back(m_button.get(), 0);
}
specter::View* buildContentView(specter::ViewResources& res) override {
m_textView.reset(new specter::MultiLineTextView(res, *m_spaceView, res.m_heading14));
m_textView->setBackground(res.themeData().viewportBackground());
m_textView->typesetGlyphs(m_contentStr, res.themeData().uiText());
return m_textView.get();
}
};
} // namespace metaforce

View File

@ -1,115 +0,0 @@
#include "SplashScreen.hpp"
#include "version.h"
#include "badging/Badging.hpp"
namespace metaforce {
#define SPLASH_WIDTH 555
#define SPLASH_HEIGHT 100
#define WIRE_START 0
#define WIRE_FRAMES 60
#define SOLID_START 40
#define SOLID_FRAMES 40
#define TEXT_START 80
#define TEXT_FRAMES 40
#define LINE_WIDTH 2
#define TEXT_MARGIN 10
SplashScreen::SplashScreen(ViewManager& vm, specter::ViewResources& res)
: ModalWindow(res, vm.rootView(),
specter::RectangleConstraint(SPLASH_WIDTH * res.pixelFactor(), SPLASH_HEIGHT * res.pixelFactor()))
, m_vm(vm)
, m_textColor(res.themeData().uiText()) {
if (METAFORCE_WC_DATE[0] != '\0' && METAFORCE_WC_REVISION[0] != '\0' && METAFORCE_WC_BRANCH[0] != '\0') {
m_buildInfoStr = fmt::format(FMT_STRING("{}: {}\n{}: {}\n{}: {}"),
vm.translate<locale::version>(), METAFORCE_WC_DESCRIBE,
vm.translate<locale::branch>(), METAFORCE_WC_BRANCH,
vm.translate<locale::commit>(), METAFORCE_WC_REVISION/*,
vm.translate<locale::date>(), METAFORCE_WC_DATE*/);
}
}
void SplashScreen::think() {
if (phase() == Phase::Done) {
return;
}
OPTICK_EVENT();
ModalWindow::think();
}
void SplashScreen::updateContentOpacity(float opacity) {
specter::ViewResources& res = rootView().viewRes();
if (!m_title && res.fontCacheReady()) {
m_title.reset(new specter::TextView(res, *this, res.m_titleFont));
zeus::CColor clearColor = res.themeData().uiText();
clearColor[3] = 0.0;
m_title->typesetGlyphs("Metaforce", clearColor);
m_buildInfo.reset(new specter::MultiLineTextView(res, *this, res.m_mainFont, specter::TextView::Alignment::Right));
m_buildInfo->typesetGlyphs(m_buildInfoStr, clearColor);
m_badgeText.reset(new specter::TextView(res, *this, res.m_heading18));
m_badgeText->typesetGlyphs(BADGE_PHRASE, clearColor);
m_infoStr = std::make_unique<specter::TextView>(res, *this, res.m_mainFont);
m_infoStr->typesetGlyphs("No game detected. Use metaforce-gui or check README for command-line options."sv, clearColor);
updateSize();
}
zeus::CColor clearColor = res.themeData().uiText();
clearColor[3] = 0.0;
zeus::CColor color = zeus::CColor::lerp(clearColor, res.themeData().uiText(), opacity);
m_title->colorGlyphs(color);
m_buildInfo->colorGlyphs(color);
m_badgeText->colorGlyphs(color);
m_infoStr->colorGlyphs(color);
}
void SplashScreen::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) {
ModalWindow::resized(root, sub);
float pf = rootView().viewRes().pixelFactor();
boo::SWindowRect centerRect = subRect();
centerRect.location[0] = root.size[0] / 2 - (SPLASH_WIDTH * pf / 2.0);
centerRect.location[1] = root.size[1] / 2 - (SPLASH_HEIGHT * pf / 2.0);
boo::SWindowRect textRect = centerRect;
textRect.location[0] += TEXT_MARGIN * pf;
textRect.location[1] += (SPLASH_HEIGHT - 28) * pf;
if (m_title) {
m_title->resized(root, textRect);
textRect.location[1] -= m_title->nominalHeight() * 0.75;
m_badgeText->resized(root, textRect);
textRect.location[0] = centerRect.location[0] + (SPLASH_WIDTH - TEXT_MARGIN) * pf;
textRect.location[1] = centerRect.location[1] + (SPLASH_HEIGHT - 36) * pf - 5 * pf;
m_buildInfo->resized(root, textRect);
textRect.size[0] = m_infoStr->nominalWidth();
textRect.size[1] = m_infoStr->nominalHeight();
textRect.location[1] = centerRect.location[1] + 20 * pf;
textRect.location[0] = centerRect.location[0] + SPLASH_WIDTH * 2 / 4 * pf - m_infoStr->nominalWidth() / 2;
m_infoStr->resized(root, textRect);
}
}
void SplashScreen::draw(boo::IGraphicsCommandQueue* gfxQ) {
if (phase() == Phase::Done)
return;
ModalWindow::draw(gfxQ);
if (m_title) {
m_title->draw(gfxQ);
m_buildInfo->draw(gfxQ);
m_badgeText->draw(gfxQ);
m_infoStr->draw(gfxQ);
}
}
} // namespace metaforce

View File

@ -1,36 +0,0 @@
#pragma once
#include "specter/View.hpp"
#include "specter/ModalWindow.hpp"
#include "specter/MultiLineTextView.hpp"
#include "specter/FileBrowser.hpp"
#include "specter/Menu.hpp"
#include "specter/Icon.hpp"
#include "ViewManager.hpp"
namespace metaforce {
static logvisor::Module Log("specter::SplashScreen");
class SplashScreen : public specter::ModalWindow {
ViewManager& m_vm;
zeus::CColor m_textColor;
std::unique_ptr<specter::TextView> m_title;
std::string m_buildInfoStr;
std::unique_ptr<specter::MultiLineTextView> m_buildInfo;
std::unique_ptr<specter::TextView> m_badgeText;
std::unique_ptr<specter::TextView> m_infoStr;
public:
SplashScreen(ViewManager& vm, specter::ViewResources& res);
void think() override;
void updateContentOpacity(float opacity) override;
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) override;
void draw(boo::IGraphicsCommandQueue* gfxQ) override;
};
} // namespace metaforce

View File

@ -1,437 +0,0 @@
#include "ViewManager.hpp"
#include "specter/Space.hpp"
#include "specter/Menu.hpp"
#include "SplashScreen.hpp"
#include "locale/locale.hpp"
#include "ResourceBrowser.hpp"
#include "icons/icons.hpp"
#include "badging/Badging.hpp"
#include "Runtime/Graphics/CModel.hpp"
#include "Runtime/Graphics/CGraphics.hpp"
#include "Runtime/Character/CSkinRules.hpp"
#include "Graphics/CMetroidModelInstance.hpp"
#include "World/CWorldTransManager.hpp"
#include "Graphics/Shaders/CColoredQuadFilter.hpp"
#include "Audio/CStreamAudioManager.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/World/CPlayer.hpp"
#include "hecl/Pipeline.hpp"
#include "version.h"
#include <cstdio>
#include "optick.h"
using YAMLNode = athena::io::YAMLNode;
extern hecl::SystemString ExeDir;
namespace metaforce {
void ViewManager::InitMP1(MP1::CMain& main) {
main.Init(m_fileStoreManager, &m_cvarManager, m_mainWindow.get(), m_voiceEngine.get(), *m_amuseAllocWrapper);
if (!m_noShaderWarmup)
main.WarmupShaders();
m_testGameView.reset(new TestGameView(*this, m_viewResources, *m_rootView, m_cvarManager));
m_rootView->accessContentViews().clear();
m_rootView->accessContentViews().push_back(m_testGameView.get());
m_rootView->updateSize();
}
void ViewManager::TestGameView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) {
specter::View::resized(root, sub);
metaforce::CGraphics::SetViewportResolution({sub.size[0], sub.size[1]});
if (m_debugText) {
boo::SWindowRect newSub = sub;
newSub.location[1] = 5 * m_vm.m_viewResources.pixelFactor();
m_debugText->resized(root, newSub);
}
}
void ViewManager::TestGameView::draw(boo::IGraphicsCommandQueue* gfxQ) {
m_vm.m_projManager.mainDraw();
if (m_debugText)
m_debugText->draw(gfxQ);
}
void ViewManager::TestGameView::think() {
OPTICK_EVENT();
if (!m_debugText) {
m_debugText.reset(new specter::MultiLineTextView(m_vm.m_viewResources, *this, m_vm.m_viewResources.m_monoFont18));
boo::SWindowRect sub = subRect();
sub.location[1] = 5 * m_vm.m_viewResources.pixelFactor();
m_debugText->resized(rootView().subRect(), sub);
}
if (m_debugText) {
std::string overlayText;
if (g_StateManager) {
if (m_vm.m_cvarCommons.m_debugOverlayShowFrameCounter->toBoolean())
overlayText += fmt::format(FMT_STRING("Frame: {}\n"), g_StateManager->GetUpdateFrameIndex());
if (m_vm.m_cvarCommons.m_debugOverlayShowFramerate->toBoolean())
overlayText += fmt::format(FMT_STRING("FPS: {}\n"), metaforce::CGraphics::GetFPS());
if (m_vm.m_cvarCommons.m_debugOverlayShowInGameTime->toBoolean()) {
double igt = g_GameState->GetTotalPlayTime();
u32 ms = u64(igt * 1000) % 1000;
auto pt = std::div(igt, 3600);
overlayText +=
fmt::format(FMT_STRING("PlayTime: {:02d}:{:02d}:{:02d}.{:03d}\n"), pt.quot, pt.rem / 60, pt.rem % 60, ms);
}
if (g_StateManager->GetCurrentArea() != nullptr && m_vm.m_cvarCommons.m_debugOverlayShowRoomTimer->toBoolean()) {
double igt = g_GameState->GetTotalPlayTime();
if (m_currentRoom != g_StateManager->GetCurrentArea()) {
m_currentRoom = static_cast<const void*>(g_StateManager->GetCurrentArea());
m_lastRoomTime = igt - m_currentRoomStart;
m_currentRoomStart = igt;
}
double currentRoomTime = igt - m_currentRoomStart;
u32 curFrames = std::round(u32(currentRoomTime * 60));
u32 lastFrames = std::round(u32(m_lastRoomTime * 60));
overlayText += fmt::format(FMT_STRING("Room Time:{:8.3f}/{:6d}| Last Room:{:8.3f}/{:6d}\n"), currentRoomTime,
curFrames, m_lastRoomTime, lastFrames);
}
if (g_StateManager->Player() && m_vm.m_cvarCommons.m_debugOverlayPlayerInfo->toBoolean()) {
const CPlayer& pl = g_StateManager->GetPlayer();
const zeus::CQuaternion plQ = zeus::CQuaternion(pl.GetTransform().getRotation().buildMatrix3f());
const zeus::CTransform camXf = g_StateManager->GetCameraManager()->GetCurrentCameraTransform(*g_StateManager);
const zeus::CQuaternion camQ = zeus::CQuaternion(camXf.getRotation().buildMatrix3f());
overlayText += fmt::format(FMT_STRING("Player Position: x {}, y {}, z {}\n"
" Roll: {}, Pitch: {}, Yaw: {}\n"
" Momentum: x {}, y: {}, z: {}\n"
" Velocity: x {}, y: {}, z: {}\n"
"Camera Position: x {}, y {}, z {}\n"
" Roll: {}, Pitch: {}, Yaw: {}\n"),
pl.GetTranslation().x(), pl.GetTranslation().y(), pl.GetTranslation().z(),
zeus::radToDeg(plQ.roll()), zeus::radToDeg(plQ.pitch()), zeus::radToDeg(plQ.yaw()),
pl.GetMomentum().x(), pl.GetMomentum().y(), pl.GetMomentum().z(),
pl.GetVelocity().x(), pl.GetVelocity().y(), pl.GetVelocity().z(), camXf.origin.x(),
camXf.origin.y(), camXf.origin.z(), zeus::radToDeg(camQ.roll()),
zeus::radToDeg(camQ.pitch()), zeus::radToDeg(camQ.yaw()));
}
if (m_vm.m_cvarCommons.m_debugOverlayWorldInfo->toBoolean()) {
TLockedToken<CStringTable> tbl =
g_SimplePool->GetObj({FOURCC('STRG'), g_StateManager->GetWorld()->IGetStringTableAssetId()});
const metaforce::TAreaId aId = g_GameState->CurrentWorldState().GetCurrentAreaId();
overlayText += fmt::format(FMT_STRING("World: 0x{}{}, Area: {}\n"), g_GameState->CurrentWorldAssetId(),
(tbl.IsLoaded() ? (" " + hecl::Char16ToUTF8(tbl->GetString(0))).c_str() : ""), aId);
}
const metaforce::TAreaId aId = g_GameState->CurrentWorldState().GetCurrentAreaId();
if (m_vm.m_cvarCommons.m_debugOverlayAreaInfo->toBoolean() && g_StateManager->GetWorld() &&
g_StateManager->GetWorld()->DoesAreaExist(aId)) {
const auto& layerStates = g_GameState->CurrentWorldState().GetLayerState();
std::string layerBits;
u32 totalActive = 0;
for (u32 i = 0; i < layerStates->GetAreaLayerCount(aId); ++i) {
if (layerStates->IsLayerActive(aId, i)) {
++totalActive;
layerBits += "1";
} else
layerBits += "0";
}
overlayText += fmt::format(FMT_STRING("Area AssetId: 0x{}, Total Objects: {}\n"
"Active Layer bits: {}\n"),
g_StateManager->GetWorld()->GetArea(aId)->GetAreaAssetId(),
g_StateManager->GetAllObjectList().size(), layerBits);
}
}
if (m_vm.m_cvarCommons.m_debugOverlayShowRandomStats->toBoolean()) {
overlayText += fmt::format(FMT_STRING("CRandom16::Next calls: {}\n"), metaforce::CRandom16::GetNumNextCalls());
}
if (m_vm.m_cvarCommons.m_debugOverlayShowResourceStats->toBoolean())
overlayText += fmt::format(FMT_STRING("Resource Objects: {}\n"), g_SimplePool->GetLiveObjects());
if (!overlayText.empty())
m_debugText->typesetGlyphs(overlayText);
}
}
specter::View* ViewManager::BuildSpaceViews() {
m_rootSpaceView = m_rootSpace->buildSpaceView(m_viewResources);
return m_rootSpaceView;
}
specter::RootView* ViewManager::SetupRootView() {
m_rootView.reset(new specter::RootView(*this, m_viewResources, m_mainWindow.get()));
m_rootView->setBackground(zeus::skBlack);
return m_rootView.get();
}
SplashScreen* ViewManager::SetupSplashView() {
m_splash.reset(new SplashScreen(*this, m_viewResources));
if (!m_showSplash)
m_splash->close(true);
return m_splash.get();
}
void ViewManager::RootSpaceViewBuilt(specter::View* view) {
std::vector<specter::View*>& cViews = m_rootView->accessContentViews();
cViews.clear();
cViews.push_back(view);
cViews.push_back(m_splash.get());
m_rootView->updateSize();
}
void ViewManager::ProjectChanged(hecl::Database::Project& proj) {
CDvdFile::Shutdown();
// FIXME trilogy hack
hecl::ProjectPath projectPath(proj.getProjectWorkingPath(), _SYS_STR("out/files/MP1"));
if (!projectPath.isDirectory()) {
projectPath = hecl::ProjectPath(proj.getProjectWorkingPath(), _SYS_STR("out/files"));
}
CDvdFile::Initialize(projectPath);
}
void ViewManager::SetupEditorView() {
m_rootSpace.reset(new RootSpace(*this));
SplitSpace* split = new SplitSpace(*this, nullptr, specter::SplitView::Axis::Horizontal);
m_rootSpace->setChild(std::unique_ptr<Space>(split));
split->setChildSlot(0, std::make_unique<ResourceBrowser>(*this, split));
split->setChildSlot(1, std::make_unique<ResourceBrowser>(*this, split));
BuildSpaceViews();
}
void ViewManager::SetupEditorView(ConfigReader& r) {
m_rootSpace.reset(Space::NewRootSpaceFromConfigStream(*this, r));
BuildSpaceViews();
}
void ViewManager::SaveEditorView(ConfigWriter& w) {
if (!m_rootSpace)
return;
m_rootSpace->saveState(w);
}
void ViewManager::DismissSplash() {
if (!m_showSplash)
return;
m_showSplash = false;
m_splash->close();
}
ViewManager::ViewManager(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr)
: m_fileStoreManager(fileMgr)
, m_cvarManager(cvarMgr)
, m_cvarCommons(cvarMgr)
, m_projManager(*this)
, m_fontCache(fileMgr)
, m_locale(locale::SystemLocaleOrEnglish())
, m_recentProjectsPath(fmt::format(FMT_STRING(_SYS_STR("{}/recent_projects.txt")), fileMgr.getStoreRoot()))
, m_recentFilesPath(fmt::format(FMT_STRING(_SYS_STR("{}/recent_files.txt")), fileMgr.getStoreRoot())) {
Space::SpaceMenuNode::InitializeStrings(*this);
char path[2048];
hecl::Sstat theStat;
auto fp = hecl::FopenUnique(m_recentProjectsPath.c_str(), _SYS_STR("r"), hecl::FileLockType::Read);
if (fp) {
while (std::fgets(path, std::size(path), fp.get())) {
std::string pathStr(path);
pathStr.pop_back();
hecl::SystemStringConv pathStrView(pathStr);
if (!hecl::Stat(pathStrView.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) {
m_recentProjects.emplace_back(pathStrView.sys_str());
}
}
}
fp = hecl::FopenUnique(m_recentFilesPath.c_str(), _SYS_STR("r"), hecl::FileLockType::Read);
if (fp) {
while (std::fgets(path, std::size(path), fp.get())) {
std::string pathStr(path);
pathStr.pop_back();
hecl::SystemStringConv pathStrView(pathStr);
if (!hecl::Stat(pathStrView.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) {
m_recentFiles.emplace_back(pathStrView.sys_str());
}
}
}
}
ViewManager::~ViewManager() {}
void ViewManager::pushRecentProject(hecl::SystemStringView path) {
for (hecl::SystemString& testPath : m_recentProjects) {
if (path == testPath)
return;
}
m_recentProjects.emplace_back(path);
const auto fp = hecl::FopenUnique(m_recentProjectsPath.c_str(), _SYS_STR("w"), hecl::FileLockType::Write);
if (fp == nullptr) {
return;
}
for (const hecl::SystemString& pPath : m_recentProjects) {
fmt::print(fp.get(), FMT_STRING("{}\n"), hecl::SystemUTF8Conv(pPath));
}
}
void ViewManager::pushRecentFile(hecl::SystemStringView path) {
for (hecl::SystemString& testPath : m_recentFiles) {
if (path == testPath)
return;
}
m_recentFiles.emplace_back(path);
const auto fp = hecl::FopenUnique(m_recentFilesPath.c_str(), _SYS_STR("w"), hecl::FileLockType::Write);
if (fp == nullptr) {
return;
}
for (const hecl::SystemString& pPath : m_recentFiles) {
fmt::print(fp.get(), FMT_STRING("{}\n"), hecl::SystemUTF8Conv(pPath));
}
}
void ViewManager::init(boo::IApplication* app) {
m_mainWindow = app->newWindow(_SYS_STR("Metaforce"));
m_mainWindow->showWindow();
m_mainWindow->setWaitCursor(true);
if (m_cvarCommons.getFullscreen()) {
m_mainWindow->setFullscreen(true);
}
float pixelFactor = m_mainWindow->getVirtualPixelFactor();
m_mainBooFactory = m_mainWindow->getMainContextDataFactory();
m_pipelineConv = hecl::NewPipelineConverter(m_mainBooFactory);
hecl::conv = m_pipelineConv.get();
m_mainPlatformName = m_mainBooFactory->platformName();
m_mainWindow->setTitle(
fmt::format(FMT_STRING(_SYS_STR("Metaforce {} [{}]")), METAFORCE_WC_DESCRIBE_SYS, m_mainPlatformName));
m_mainCommandQueue = m_mainWindow->getCommandQueue();
m_viewResources.init(m_mainBooFactory, &m_fontCache, &m_themeData, pixelFactor);
InitializeIcons(m_viewResources);
InitializeBadging(m_viewResources);
m_viewResources.prepFontCacheAsync(m_mainWindow.get());
specter::RootView* root = SetupRootView();
m_showSplash = true;
root->accessContentViews().push_back(SetupSplashView());
root->updateSize();
m_renderTex = root->renderTex();
m_mainWindow->setWaitCursor(false);
m_voiceEngine = boo::NewAudioVoiceEngine();
m_voiceEngine->setVolume(0.7f);
m_amuseAllocWrapper.emplace(*m_voiceEngine);
for (const auto& arg : app->getArgs()) {
hecl::Sstat theStat;
if (!hecl::Stat((arg + _SYS_STR("/out")).c_str(), &theStat) && S_ISDIR(theStat.st_mode)) {
hecl::ProjectRootPath rootPath(arg);
hecl::Database::Project tmp(rootPath); // Force project creation
}
if (m_deferedProject.empty() && hecl::SearchForProject(arg))
m_deferedProject = arg + _SYS_STR("/out");
if (arg == _SYS_STR("--no-shader-warmup"))
m_noShaderWarmup = true;
else if (arg == _SYS_STR("--no-sound"))
m_voiceEngine->setVolume(0.f);
}
if (m_deferedProject.empty()) {
/* Default behavior - search upwards for packaged project containing the program */
if (hecl::ProjectRootPath projRoot = hecl::SearchForProject(ExeDir)) {
hecl::SystemString rootPath(projRoot.getAbsolutePath());
hecl::Sstat theStat;
if (!hecl::Stat((rootPath + _SYS_STR("/out/files/Metroid1.upak")).c_str(), &theStat) && S_ISREG(theStat.st_mode))
m_deferedProject = rootPath + _SYS_STR("/out");
}
}
}
bool ViewManager::proc() {
if (!m_deferedProject.empty() && m_viewResources.fontCacheReady()) {
m_projManager.openProject(m_deferedProject);
m_deferedProject.clear();
}
boo::IGraphicsCommandQueue* gfxQ = m_mainWindow->getCommandQueue();
if (m_rootView->isDestroyed())
return false;
if (m_updatePf) {
OPTICK_EVENT("m_updatePf");
m_viewResources.resetPixelFactor(m_reqPf);
specter::RootView* root = SetupRootView();
if (m_rootSpace)
BuildSpaceViews();
else {
std::vector<specter::View*>& cViews = m_rootView->accessContentViews();
cViews.push_back(SetupSplashView());
}
root->updateSize();
m_updatePf = false;
}
{
OPTICK_EVENT("m_rootView->DispatchEvents");
m_rootView->dispatchEvents();
}
{
OPTICK_EVENT("m_rootView->internalThink");
m_rootView->internalThink();
}
if (m_rootSpace)
m_rootSpace->think();
if (m_splash)
m_splash->think();
if (m_deferSplit) {
SplitSpace* ss = static_cast<SplitSpace*>(m_deferSplit->spaceSplit(m_deferSplitAxis, m_deferSplitThisSlot));
m_rootView->startSplitDrag(ss->splitView(), m_deferSplitCoord);
m_deferSplit = nullptr;
}
++m_editorFrames;
if (m_rootSpaceView && m_editorFrames <= 30)
m_rootSpaceView->setMultiplyColor(zeus::CColor::lerp({1, 1, 1, 0}, {1, 1, 1, 1}, m_editorFrames / 30.0));
m_cvarManager.proc();
m_projManager.mainUpdate();
if (m_testGameView)
m_testGameView->think();
{
OPTICK_EVENT("Draw");
if (g_Renderer)
g_Renderer->BeginScene();
m_rootView->draw(gfxQ);
if (g_Renderer)
g_Renderer->EndScene();
gfxQ->execute();
}
if (g_ResFactory)
g_ResFactory->AsyncIdle();
#ifndef URDE_MSAN
m_voiceEngine->pumpAndMixVoices();
#endif
if (!m_skipWait || !hecl::com_developer->toBoolean()) {
OPTICK_EVENT("waitForRetrace");
m_mainWindow->waitForRetrace();
}
CBooModel::ClearModelUniformCounters();
CGraphics::TickRenderTimings();
++logvisor::FrameIndex;
return true;
}
void ViewManager::stop() {
m_mainWindow->getCommandQueue()->stopRenderer();
m_projManager.shutdown();
CDvdFile::Shutdown();
DestroyIcons();
DestroyBadging();
m_viewResources.destroyResData();
m_fontCache.destroyAtlases();
}
} // namespace metaforce

View File

@ -1,199 +0,0 @@
#pragma once
#include "hecl/CVarManager.hpp"
#include "hecl/CVarCommons.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "amuse/BooBackend.hpp"
#include "ProjectManager.hpp"
#include "Space.hpp"
#include "specter/IViewManager.hpp"
#include "specter/FontCache.hpp"
#include "specter/ViewResources.hpp"
#include "Runtime/CGameHintInfo.hpp"
#include "Runtime/Particle/CElementGen.hpp"
#include "Runtime/Graphics/CLineRenderer.hpp"
#include "Runtime/Graphics/CMoviePlayer.hpp"
#include "Runtime/Graphics/CModel.hpp"
#include "Runtime/Particle/CGenDescription.hpp"
#include "Runtime/Character/CAssetFactory.hpp"
#include "Runtime/Graphics/Shaders/CColoredQuadFilter.hpp"
#include "Runtime/Graphics/Shaders/CXRayBlurFilter.hpp"
#include "Runtime/Graphics/Shaders/CCameraBlurFilter.hpp"
#include "Runtime/Audio/CStaticAudioPlayer.hpp"
namespace hecl {
class PipelineConverterBase;
}
namespace metaforce {
class SplashScreen;
class ViewManager final : public specter::IViewManager {
friend class ProjectManager;
friend class Space;
friend class RootSpace;
friend class SplitSpace;
std::shared_ptr<boo::IWindow> m_mainWindow;
hecl::Runtime::FileStoreManager& m_fileStoreManager;
hecl::CVarManager& m_cvarManager;
hecl::CVarCommons m_cvarCommons;
ProjectManager m_projManager;
specter::FontCache m_fontCache;
specter::DefaultThemeData m_themeData;
specter::ViewResources m_viewResources;
locale::ELocale m_locale = locale::ELocale::en_US;
boo::IGraphicsDataFactory* m_mainBooFactory = nullptr;
boo::IGraphicsCommandQueue* m_mainCommandQueue = nullptr;
std::unique_ptr<hecl::PipelineConverterBase> m_pipelineConv;
boo::ObjToken<boo::ITextureR> m_renderTex;
const boo::SystemChar* m_mainPlatformName;
std::unique_ptr<specter::RootView> m_rootView;
std::unique_ptr<SplashScreen> m_splash;
std::unique_ptr<RootSpace> m_rootSpace;
specter::View* m_rootSpaceView = nullptr;
bool m_skipWait = false;
class TestGameView : public specter::View {
ViewManager& m_vm;
std::unique_ptr<specter::MultiLineTextView> m_debugText;
const void* m_currentRoom = nullptr;
double m_lastRoomTime = 0.f;
double m_currentRoomStart = 0.f;
public:
TestGameView(ViewManager& vm, specter::ViewResources& res, specter::View& parent, hecl::CVarManager& cvarMgr)
: View(res, parent), m_vm(vm) {}
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) override;
void draw(boo::IGraphicsCommandQueue* gfxQ) override;
void think() override;
void mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mkey) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->mouseDown(coord, button, mkey);
}
void mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mkey) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->mouseUp(coord, button, mkey);
}
void mouseMove(const boo::SWindowCoord& coord) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->mouseMove(coord);
}
void scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& sd) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->scroll(coord, sd);
}
void charKeyDown(unsigned long cc, boo::EModifierKey mkey, bool repeat) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->charKeyDown(cc, mkey, repeat);
}
void charKeyUp(unsigned long cc, boo::EModifierKey mkey) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->charKeyUp(cc, mkey);
}
void specialKeyDown(boo::ESpecialKey skey, boo::EModifierKey mkey, bool repeat) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->specialKeyDown(skey, mkey, repeat);
if (skey == boo::ESpecialKey::F1)
m_vm.m_skipWait = true;
}
void specialKeyUp(boo::ESpecialKey skey, boo::EModifierKey mkey) override {
if (MP1::CMain* m = m_vm.m_projManager.gameMain())
if (MP1::CGameArchitectureSupport* as = m->GetArchSupport())
as->specialKeyUp(skey, mkey);
if (skey == boo::ESpecialKey::F1)
m_vm.m_skipWait = false;
}
};
std::unique_ptr<TestGameView> m_testGameView;
std::unique_ptr<boo::IAudioVoiceEngine> m_voiceEngine;
std::optional<amuse::BooBackendVoiceAllocator> m_amuseAllocWrapper;
hecl::SystemString m_recentProjectsPath;
std::vector<hecl::SystemString> m_recentProjects;
hecl::SystemString m_recentFilesPath;
std::vector<hecl::SystemString> m_recentFiles;
bool m_updatePf = false;
float m_reqPf;
specter::View* BuildSpaceViews();
specter::RootView* SetupRootView();
SplashScreen* SetupSplashView();
void RootSpaceViewBuilt(specter::View* view);
void ProjectChanged(hecl::Database::Project& proj);
void SetupEditorView();
void SetupEditorView(ConfigReader& r);
void SaveEditorView(ConfigWriter& w);
bool m_showSplash = false;
void DismissSplash();
unsigned m_editorFrames = 120;
void FadeInEditors() { m_editorFrames = 0; }
void InitMP1(MP1::CMain& main);
Space* m_deferSplit = nullptr;
specter::SplitView::Axis m_deferSplitAxis;
int m_deferSplitThisSlot;
boo::SWindowCoord m_deferSplitCoord;
hecl::SystemString m_deferedProject;
bool m_noShaderWarmup = false;
public:
ViewManager(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr);
~ViewManager();
specter::RootView& rootView() const { return *m_rootView; }
void requestPixelFactor(float pf) {
m_reqPf = pf;
m_updatePf = true;
}
ProjectManager& projectManager() { return m_projManager; }
hecl::Database::Project* project() { return m_projManager.project(); }
locale::ELocale getTranslatorLocale() const override { return m_locale; }
void deferSpaceSplit(specter::ISpaceController* split, specter::SplitView::Axis axis, int thisSlot,
const boo::SWindowCoord& coord) override {
m_deferSplit = static_cast<Space*>(split);
m_deferSplitAxis = axis;
m_deferSplitThisSlot = thisSlot;
m_deferSplitCoord = coord;
}
const std::vector<hecl::SystemString>* recentProjects() const override { return &m_recentProjects; }
void pushRecentProject(hecl::SystemStringView path) override;
const std::vector<hecl::SystemString>* recentFiles() const override { return &m_recentFiles; }
void pushRecentFile(hecl::SystemStringView path) override;
void init(boo::IApplication* app);
const boo::SystemChar* platformName() { return m_mainPlatformName; }
bool proc();
void stop();
void deferOpenProject(const hecl::SystemString& path) { m_deferedProject = path; }
};
} // namespace metaforce

View File

@ -1,45 +0,0 @@
#include "Badging.hpp"
#include "athena/MemoryReader.hpp"
#include <specter/Icon.hpp>
#include <zlib.h>
extern "C" uint8_t URDE_BADGE[];
extern "C" size_t URDE_BADGE_SZ;
namespace metaforce {
static logvisor::Module Log("URDE::badging");
static specter::Icon g_BadgeIcon;
void InitializeBadging(specter::ViewResources& viewRes) {
athena::io::MemoryReader r(URDE_BADGE, URDE_BADGE_SZ);
size_t fmt = r.readUint32Big();
if (fmt != 16)
Log.report(logvisor::Fatal, FMT_STRING("incorrect icon texture format"));
size_t width = r.readUint16Big();
size_t height = r.readUint16Big();
size_t mips = r.readUint32Big();
size_t decompSz = r.readUint32Big();
std::unique_ptr<uint8_t[]> texels(new uint8_t[decompSz]);
uLongf destSz = decompSz;
size_t pos = r.position();
if (uncompress(texels.get(), &destSz, URDE_BADGE + pos, URDE_BADGE_SZ - pos) != Z_OK)
Log.report(logvisor::Fatal, FMT_STRING("unable to decompress badge"));
viewRes.m_factory->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) {
specter::IconAtlas<1, 1> atlas;
atlas.initializeAtlas(ctx.newStaticTexture(width, height, mips, boo::TextureFormat::RGBA8,
boo::TextureClampMode::Repeat, texels.get(), destSz));
g_BadgeIcon = atlas.getIcon(0, 0);
return true;
} BooTrace);
}
void DestroyBadging() { g_BadgeIcon.m_tex.reset(); }
specter::Icon& GetBadge() { return g_BadgeIcon; }
} // namespace metaforce

View File

@ -1,14 +0,0 @@
#pragma once
#include "specter/ViewResources.hpp"
namespace metaforce {
void InitializeBadging(specter::ViewResources& viewRes);
void DestroyBadging();
specter::Icon& GetBadge();
} // namespace metaforce
#ifndef BADGE_PHRASE
#define BADGE_PHRASE "Alpha"
#endif

View File

@ -1,43 +0,0 @@
if(NOT CMAKE_CROSSCOMPILING)
add_executable(packbadge packbadge.c)
find_library(M_LIB m)
if(NOT M_LIB)
unset(M_LIB CACHE)
endif()
target_link_libraries(packbadge ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${M_LIB})
target_include_directories(packbadge PRIVATE ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
##################
# Package Export #
##################
# Add all targets to the build-tree export set
export(TARGETS packbadge FILE "${CMAKE_CURRENT_BINARY_DIR}/metaforce-packbadgeTargets.cmake")
# Export the package for use from the build-tree
# (this registers the build-tree with a global CMake-registry)
export(PACKAGE metaforce-packbadge)
# Create the atdnaConfig.cmake
# ... for the build tree
configure_file(metaforce-packbadgeConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/metaforce-packbadgeConfig.cmake" @ONLY)
# ... for the install tree
configure_file(metaforce-packbadgeConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/metaforce-packbadgeConfig.cmake" @ONLY)
# ... for both
configure_file(metaforce-packbadgeConfigVersion.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/metaforce-packbadgeConfigVersion.cmake" @ONLY)
else()
find_package(metaforce-packbadge REQUIRED)
endif()
add_custom_command(OUTPUT badge.bin COMMAND $<TARGET_FILE:packbadge>
ARGS ${CMAKE_CURRENT_SOURCE_DIR}/badge.png
${CMAKE_CURRENT_BINARY_DIR}/badge.bin
MAIN_DEPENDENCY badge.png COMMENT "Generating badge.bin")
bintoc(badge.cpp ${CMAKE_CURRENT_BINARY_DIR}/badge.bin URDE_BADGE)
add_library(MetaforceBadging
badge.cpp badge.bin
Badging.hpp Badging.cpp)
target_link_libraries(MetaforceBadging PUBLIC specter)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,9 +0,0 @@
# - Config file for the packbadge package
# Compute paths
get_filename_component(PACKBADGE_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# Our library dependencies (contains definitions for IMPORTED targets)
if(NOT TARGET packbadge AND NOT packbadge_BINARY_DIR)
include("${PACKBADGE_CMAKE_DIR}/metaforce-packbadgeTargets.cmake")
endif()

View File

@ -1,12 +0,0 @@
set(PACKAGE_VERSION "@PACKBADGE_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@ -1,174 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <zlib.h>
#include <png.h>
#if __FreeBSD__
#include <sys/wait.h>
#endif
#if _WIN32
#define _bswap32(v) _byteswap_ulong(v)
#define _bswap16(v) _byteswap_ushort(v)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#define _bswap32(v) __builtin_bswap32(v)
#define _bswap16(v) __builtin_bswap16(v)
#endif
int main(int argc, char* argv[])
{
if (argc < 3)
{
fprintf(stderr, "Usage: packbadge <in.png> <out.bin>\n");
return 1;
}
/* Validate input */
FILE* fp = fopen(argv[1], "rb");
if (!fp)
{
fprintf(stderr, "'%s' is not able to be opened for reading as a regular file\n", argv[1]);
return 1;
}
FILE* ofp = fopen(argv[2], "wb");
if (!ofp)
{
fprintf(stderr, "'%s' is not able to be opened for writing as a regular file\n", argv[2]);
return 1;
}
size_t decompSz = 0;
int numMips = 1;
z_stream z = {0};
size_t rowSz = 0;
uLong rowSzC = 0;
png_bytep row;
png_bytep rowC;
/* Get PNG data */
char header[8];
fread(header, 1, 8, fp);
if (png_sig_cmp((png_const_bytep)header, 0, 8))
{
fprintf(stderr, "invalid PNG signature in '%s'\n", argv[1]);
fclose(fp);
fclose(ofp);
return 1;
}
png_structp pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngRead)
{
fprintf(stderr, "unable to initialize libpng\n");
fclose(fp);
fclose(ofp);
return 1;
}
png_infop info = png_create_info_struct(pngRead);
if (!info)
{
fprintf(stderr, "unable to initialize libpng info\n");
fclose(fp);
fclose(ofp);
return 1;
}
if (setjmp(png_jmpbuf(pngRead)))
{
fprintf(stderr, "unable to initialize libpng I/O for '%s'\n", argv[1]);
fclose(fp);
fclose(ofp);
return 1;
}
png_init_io(pngRead, fp);
png_set_sig_bytes(pngRead, 8);
png_read_info(pngRead, info);
png_uint_32 width = png_get_image_width(pngRead, info);
png_uint_32 height = png_get_image_height(pngRead, info);
decompSz = width * height * 4;
png_byte colorType = png_get_color_type(pngRead, info);
png_byte bitDepth = png_get_bit_depth(pngRead, info);
if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
{
fprintf(stderr, "'%s' is not in RGBA color mode\n", argv[1]);
fclose(fp);
fclose(ofp);
return 1;
}
if (bitDepth != 8)
{
fprintf(stderr, "'%s' is not 8 bits-per-channel\n", argv[1]);
fclose(fp);
fclose(ofp);
return 1;
}
if (setjmp(png_jmpbuf(pngRead)))
{
fprintf(stderr, "unable to read image in '%s'\n", argv[1]);
fclose(fp);
fclose(ofp);
return 1;
}
uint32_t fmt = _bswap32(16);
uint16_t w = _bswap16(width);
uint16_t h = _bswap16(height);
uint32_t mips = _bswap32(numMips);
uint32_t dsz = _bswap32(decompSz);
fwrite(&fmt, 1, 4, ofp);
fwrite(&w, 1, 2, ofp);
fwrite(&h, 1, 2, ofp);
fwrite(&mips, 1, 4, ofp);
fwrite(&dsz, 1, 4, ofp);
rowSz = width*4;
rowSzC = compressBound(rowSz);
deflateInit(&z, Z_DEFAULT_COMPRESSION);
row = malloc(rowSz);
rowC = malloc(rowSzC);
for (png_uint_32 r=0 ; r<height ; ++r)
{
png_read_row(pngRead, row, NULL);
z.next_in = row;
z.avail_in = rowSz;
while (z.avail_in)
{
z.next_out = rowC;
z.avail_out = rowSzC;
z.total_out = 0;
deflate(&z, Z_NO_FLUSH);
fwrite(rowC, 1, z.total_out, ofp);
}
}
png_destroy_read_struct(&pngRead, &info, NULL);
int finishCycle = Z_OK;
while (finishCycle != Z_STREAM_END)
{
z.next_out = rowC;
z.avail_out = rowSzC;
z.total_out = 0;
finishCycle = deflate(&z, Z_FINISH);
fwrite(rowC, 1, z.total_out, ofp);
}
deflateEnd(&z);
free(row);
free(rowC);
fclose(fp);
return 0;
}

View File

@ -1,63 +0,0 @@
if(NOT CMAKE_CROSSCOMPILING)
add_executable(packicons packicons.c)
find_library(M_LIB m)
if(NOT M_LIB)
unset(M_LIB CACHE)
endif()
target_link_libraries(packicons ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${M_LIB})
target_include_directories(packicons PRIVATE ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
##################
# Package Export #
##################
# Add all targets to the build-tree export set
export(TARGETS packicons FILE "${CMAKE_CURRENT_BINARY_DIR}/metaforce-packiconsTargets.cmake")
# Export the package for use from the build-tree
# (this registers the build-tree with a global CMake-registry)
export(PACKAGE metaforce-packicons)
# Create the atdnaConfig.cmake
# ... for the build tree
configure_file(metaforce-packiconsConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/metaforce-packiconsConfig.cmake" @ONLY)
# ... for the install tree
configure_file(metaforce-packiconsConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/metaforce-packiconsConfig.cmake" @ONLY)
# ... for both
configure_file(metaforce-packiconsConfigVersion.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/metaforce-packiconsConfigVersion.cmake" @ONLY)
else()
find_package(metaforce-packicons REQUIRED)
endif()
unset(INKSCAPE_BIN CACHE)
set(CMAKE_FIND_APPBUNDLE "NEVER")
if(WIN32)
find_program(INKSCAPE_BIN inkscape.exe PATHS
"$ENV{PROGRAMFILES}/Inkscape"
"$ENV{ProgramW6432}/Inkscape"
"$ENV{PROGRAMFILES\(X86\)}/Inkscape")
else()
set(CMAKE_FIND_APPBUNDLE FIRST)
find_program(INKSCAPE_BIN inkscape)
endif()
if(NOT INKSCAPE_BIN)
add_custom_command(OUTPUT icons.bin COMMAND ${CMAKE_COMMAND} ARGS -P
${CMAKE_CURRENT_SOURCE_DIR}/IconDownload.cmake)
else()
if(APPLE)
get_filename_component(INKSCAPE_DIR ${INKSCAPE_BIN} DIRECTORY)
if(EXISTS "${INKSCAPE_DIR}/../Resources/bin/inkscape")
set(INKSCAPE_BIN "${INKSCAPE_DIR}/../Resources/bin/inkscape")
endif()
endif()
message(STATUS "Inkscape found; will render icons locally")
add_custom_command(OUTPUT icons.bin COMMAND $<TARGET_FILE:packicons>
ARGS ${INKSCAPE_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/icons.svg
${CMAKE_CURRENT_BINARY_DIR}/icons.bin
MAIN_DEPENDENCY icons.svg COMMENT "Generating icons.bin")
endif()
bintoc(icons_dat.cpp ${CMAKE_CURRENT_BINARY_DIR}/icons.bin URDE_ICONS)
add_library(MetaforceIcons icons.cpp icons.hpp icons.bin icons_dat.cpp)
target_link_libraries(MetaforceIcons PUBLIC specter)

View File

@ -1,3 +0,0 @@
message(STATUS "Inkscape not found; downloading icons to '${CMAKE_CURRENT_BINARY_DIR}/icons.bin'")
file(DOWNLOAD "https://axiodl.com/files/icons.bin"
${CMAKE_CURRENT_BINARY_DIR}/icons.bin SHOW_PROGRESS EXPECTED_HASH SHA1=412baa3b12163a89b306ebfef2a395762105a8d4)

View File

@ -1,94 +0,0 @@
#include "icons.hpp"
#include "athena/MemoryReader.hpp"
#include <zlib.h>
extern "C" uint8_t URDE_ICONS[];
extern "C" size_t URDE_ICONS_SZ;
namespace metaforce {
static logvisor::Module Log("URDE::icons");
specter::IconAtlas<8, 8> g_IconAtlas;
void InitializeIcons(specter::ViewResources& viewRes) {
athena::io::MemoryReader r(URDE_ICONS, URDE_ICONS_SZ);
size_t fmt = r.readUint32Big();
if (fmt != 16)
Log.report(logvisor::Fatal, FMT_STRING("incorrect icon texture format"));
size_t width = r.readUint16Big();
size_t height = r.readUint16Big();
size_t mips = r.readUint32Big();
size_t decompSz = r.readUint32Big();
std::unique_ptr<uint8_t[]> texels(new uint8_t[decompSz]);
uLongf destSz = decompSz;
size_t pos = r.position();
if (uncompress(texels.get(), &destSz, URDE_ICONS + pos, URDE_ICONS_SZ - pos) != Z_OK)
Log.report(logvisor::Fatal, FMT_STRING("unable to decompress icons"));
viewRes.m_factory->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) {
g_IconAtlas.initializeAtlas(ctx.newStaticTexture(width, height, mips, boo::TextureFormat::RGBA8,
boo::TextureClampMode::Repeat, texels.get(), destSz));
return true;
} BooTrace);
}
void DestroyIcons() { g_IconAtlas.destroyAtlas(); }
specter::Icon& GetIcon(SpaceIcon icon) {
switch (icon) {
case SpaceIcon::ResourceBrowser:
return g_IconAtlas.getIcon(0, 0);
case SpaceIcon::ParticleEditor:
return g_IconAtlas.getIcon(0, 1);
case SpaceIcon::WorldEditor:
return g_IconAtlas.getIcon(0, 2);
case SpaceIcon::InformationCenter:
return g_IconAtlas.getIcon(0, 3);
case SpaceIcon::ModelViewer:
return g_IconAtlas.getIcon(0, 4);
case SpaceIcon::GameMode:
return g_IconAtlas.getIcon(0, 5);
default:
return g_IconAtlas.getIcon(6, 0);
}
}
specter::Icon& GetIcon(MonoIcon icon) {
switch (icon) {
case MonoIcon::Sync:
return g_IconAtlas.getIcon(7, 0);
case MonoIcon::Edit:
return g_IconAtlas.getIcon(7, 1);
case MonoIcon::Caution:
return g_IconAtlas.getIcon(7, 2);
case MonoIcon::Save:
return g_IconAtlas.getIcon(7, 3);
case MonoIcon::Filter:
return g_IconAtlas.getIcon(7, 4);
case MonoIcon::Document:
return g_IconAtlas.getIcon(7, 5);
case MonoIcon::ZoomOut:
return g_IconAtlas.getIcon(7, 6);
case MonoIcon::ZoomIn:
return g_IconAtlas.getIcon(7, 7);
case MonoIcon::Exclaim:
return g_IconAtlas.getIcon(6, 0);
case MonoIcon::Clock:
return g_IconAtlas.getIcon(6, 1);
case MonoIcon::Gamepad:
return g_IconAtlas.getIcon(6, 2);
case MonoIcon::Unlink:
return g_IconAtlas.getIcon(6, 3);
case MonoIcon::Link:
return g_IconAtlas.getIcon(6, 4);
case MonoIcon::Folder:
return g_IconAtlas.getIcon(6, 5);
case MonoIcon::Info:
return g_IconAtlas.getIcon(6, 6);
default:
return g_IconAtlas.getIcon(6, 0);
}
}
} // namespace metaforce

View File

@ -1,33 +0,0 @@
#pragma once
#include "specter/Icon.hpp"
#include "specter/ViewResources.hpp"
namespace metaforce {
void InitializeIcons(specter::ViewResources& viewRes);
void DestroyIcons();
enum class SpaceIcon { ResourceBrowser, ParticleEditor, WorldEditor, ModelViewer, InformationCenter, GameMode };
specter::Icon& GetIcon(SpaceIcon icon);
enum class MonoIcon {
Sync,
Edit,
Caution,
Save,
Filter,
Document,
ZoomOut,
ZoomIn,
Exclaim,
Clock,
Gamepad,
Unlink,
Link,
Folder,
Info
};
specter::Icon& GetIcon(MonoIcon icon);
} // namespace metaforce

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@ -1,9 +0,0 @@
# - Config file for the packicons package
# Compute paths
get_filename_component(PACKICONS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# Our library dependencies (contains definitions for IMPORTED targets)
if(NOT TARGET packicons AND NOT packicons_BINARY_DIR)
include("${PACKICONS_CMAKE_DIR}/metaforce-packiconsTargets.cmake")
endif()

View File

@ -1,12 +0,0 @@
set(PACKAGE_VERSION "@PACKICONS_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@ -1,345 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <zlib.h>
#include <png.h>
#if __FreeBSD__
#include <sys/wait.h>
#endif
#if _WIN32
#define _bswap32(v) _byteswap_ulong(v)
#define _bswap16(v) _byteswap_ushort(v)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#define _bswap32(v) __builtin_bswap32(v)
#define _bswap16(v) __builtin_bswap16(v)
#endif
static int CountBits(uint32_t n)
{
int ret = 0;
for (int i=0 ; i<32 ; ++i)
if (((n >> i) & 1) != 0)
++ret;
return ret;
}
int main(int argc, char* argv[])
{
if (argc < 4)
{
fprintf(stderr, "Usage: packicons <inkscape-bin> <in.svg> <out.bin>\n");
return 1;
}
/* Validate inkscape */
char command[2048];
FILE* fp;
#if _WIN32
STARTUPINFOA sinfo = {sizeof(STARTUPINFOA)};
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0))
{
fprintf(stderr, "unable to CreatePipe\n");
return 1;
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
fprintf(stderr, "unable to SetHandleInformation\n");
return 1;
}
sinfo.hStdError = hChildStd_OUT_Wr;
sinfo.hStdOutput = hChildStd_OUT_Wr;
sinfo.dwFlags |= STARTF_USESTDHANDLES;
PROCESS_INFORMATION pinfo;
if (!CreateProcessA(argv[1], " --version", NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
{
LPSTR messageBuffer = NULL;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
fprintf(stderr, "unable to launch inkscape from %s: %s\n", argv[1], messageBuffer);
return 1;
}
CloseHandle(hChildStd_OUT_Wr);
CloseHandle(pinfo.hThread);
char readback[8];
DWORD bytesRead = 0;
if (!ReadFile(hChildStd_OUT_Rd, readback, 8, &bytesRead, NULL) || bytesRead != 8 ||
strncmp(readback, "Inkscape", 8))
{
fprintf(stderr, "'%s' did not return expected \"Inkscape\"\n", command);
CloseHandle(hChildStd_OUT_Rd);
CloseHandle(pinfo.hProcess);
return 1;
}
CloseHandle(hChildStd_OUT_Rd);
WaitForSingleObject(pinfo.hProcess, INFINITE);
CloseHandle(pinfo.hProcess);
#else
snprintf(command, 2048, "%s --version", argv[1]);
fp = popen(command, "r");
if (!fp)
{
fprintf(stderr, "'%s' is not executable on this system\n", command);
return 1;
}
char readback[8];
if (fread(readback, 1, 8, fp) != 8 || strncmp(readback, "Inkscape", 8))
{
fprintf(stderr, "'%s' did not return expected \"Inkscape\"\n", command);
pclose(fp);
return 1;
}
pclose(fp);
#endif
/* Validate input */
fp = fopen(argv[2], "rb");
if (!fp)
{
fprintf(stderr, "'%s' is not able to be opened for reading as a regular file\n", argv[2]);
return 1;
}
fclose(fp);
#ifdef _WIN32
char* TMPDIR = getenv("TEMP");
if (!TMPDIR)
TMPDIR = (char*)"\\Temp";
#else
char* TMPDIR = getenv("TMPDIR");
if (!TMPDIR)
TMPDIR = (char*)"/tmp";
#endif
FILE* ofp = fopen(argv[3], "wb");
if (!ofp)
{
fprintf(stderr, "'%s' is not able to be opened for writing as a regular file\n", argv[3]);
return 1;
}
size_t decompSz = 0;
int numMips = 0;
for (int i=512 ; i>=1 ; i/=2)
{
decompSz += i*i*4;
++numMips;
}
z_stream z = {0};
size_t rowSz = 0;
uLong rowSzC = 0;
png_bytep row;
png_bytep rowC;
for (int i=512 ; i>=1 ; i/=2)
{
printf("Rendering icons @%dx%d\n", i, i);
fflush(stdout);
#if _WIN32
snprintf(command, 2048, " --export-filename=\"%s/icon_pack.png\" --export-width=%d --export-height=%d \"%s\"",
TMPDIR, i, i, argv[2]);
STARTUPINFOA sinfo = {sizeof(STARTUPINFOA)};
PROCESS_INFORMATION pinfo;
if (!CreateProcessA(argv[1], command, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
{
LPSTR messageBuffer = NULL;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
fprintf(stderr, "unable to launch inkscape from %s: %s\n", argv[1], messageBuffer);
return 1;
}
CloseHandle(pinfo.hThread);
WaitForSingleObject(pinfo.hProcess, INFINITE);
CloseHandle(pinfo.hProcess);
#else
snprintf(command, 2048, "%s --export-filename=\"%s/icon_pack.png\" --export-width=%d --export-height=%d \"%s\"",
argv[1], TMPDIR, i, i, argv[2]);
fp = popen(command, "r");
if (!fp)
{
fprintf(stderr, "'%s' is not executable on this system\n", command);
fclose(ofp);
return 1;
}
int status = pclose(fp);
if (WEXITSTATUS(status))
{
fprintf(stderr, "'%s' failed\n", command);
fclose(ofp);
return 1;
}
#endif
/* Get PNG data */
snprintf(command, 2048, "%s/icon_pack.png", TMPDIR);
fp = fopen(command, "rb");
if (!fp)
{
fprintf(stderr, "unable to open '%s' for reading\n", command);
fclose(ofp);
return 1;
}
char header[8];
fread(header, 1, 8, fp);
if (png_sig_cmp((png_const_bytep)header, 0, 8))
{
fprintf(stderr, "invalid PNG signature in '%s'\n", command);
fclose(fp);
fclose(ofp);
return 1;
}
png_structp pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngRead)
{
fprintf(stderr, "unable to initialize libpng\n");
fclose(fp);
fclose(ofp);
return 1;
}
png_infop info = png_create_info_struct(pngRead);
if (!info)
{
fprintf(stderr, "unable to initialize libpng info\n");
fclose(fp);
fclose(ofp);
return 1;
}
if (setjmp(png_jmpbuf(pngRead)))
{
fprintf(stderr, "unable to initialize libpng I/O for '%s'\n", command);
fclose(fp);
fclose(ofp);
return 1;
}
png_init_io(pngRead, fp);
png_set_sig_bytes(pngRead, 8);
png_read_info(pngRead, info);
png_uint_32 width = png_get_image_width(pngRead, info);
png_uint_32 height = png_get_image_height(pngRead, info);
png_byte colorType = png_get_color_type(pngRead, info);
png_byte bitDepth = png_get_bit_depth(pngRead, info);
if (CountBits(width) != 1 || CountBits(height) != 1)
{
fprintf(stderr, "'%s' is not power-of-2 in one or both dimensions\n", command);
fclose(fp);
fclose(ofp);
return 1;
}
if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
{
fprintf(stderr, "'%s' is not in RGBA color mode\n", command);
fclose(fp);
fclose(ofp);
return 1;
}
if (bitDepth != 8)
{
fprintf(stderr, "'%s' is not 8 bits-per-channel\n", command);
fclose(fp);
fclose(ofp);
return 1;
}
if (setjmp(png_jmpbuf(pngRead)))
{
fprintf(stderr, "unable to read image in '%s'\n", command);
fclose(fp);
fclose(ofp);
return 1;
}
if (i == 512)
{
uint32_t fmt = _bswap32(16);
uint16_t w = _bswap16(width);
uint16_t h = _bswap16(height);
uint32_t mips = _bswap32(numMips);
uint32_t dsz = _bswap32(decompSz);
fwrite(&fmt, 1, 4, ofp);
fwrite(&w, 1, 2, ofp);
fwrite(&h, 1, 2, ofp);
fwrite(&mips, 1, 4, ofp);
fwrite(&dsz, 1, 4, ofp);
rowSz = width*4;
rowSzC = compressBound(rowSz);
deflateInit(&z, Z_DEFAULT_COMPRESSION);
row = malloc(rowSz);
rowC = malloc(rowSzC);
}
for (png_uint_32 r=0 ; r<height ; ++r)
{
png_read_row(pngRead, row, NULL);
z.next_in = row;
z.avail_in = rowSz;
while (z.avail_in)
{
z.next_out = rowC;
z.avail_out = rowSzC;
z.total_out = 0;
deflate(&z, Z_NO_FLUSH);
fwrite(rowC, 1, z.total_out, ofp);
}
}
rowSz /= 2;
png_destroy_read_struct(&pngRead, &info, NULL);
fclose(fp);
}
if (rowSzC)
{
int finishCycle = Z_OK;
while (finishCycle != Z_STREAM_END)
{
z.next_out = rowC;
z.avail_out = rowSzC;
z.total_out = 0;
finishCycle = deflate(&z, Z_FINISH);
fwrite(rowC, 1, z.total_out, ofp);
}
deflateEnd(&z);
free(row);
free(rowC);
}
fclose(ofp);
return 0;
}

View File

@ -1,15 +0,0 @@
add_executable(genlocales genlocales.cpp)
target_link_libraries(genlocales fmt athena-core)
set(LOCALES_IN en_US.yaml en_GB.yaml ja_JP.yaml)
set(LOCALES_OUT ${CMAKE_CURRENT_BINARY_DIR}/locales-inl.hpp)
add_custom_command(OUTPUT ${LOCALES_OUT} COMMAND $<TARGET_FILE:genlocales>
ARGS ${LOCALES_OUT} ${LOCALES_IN}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS genlocales ${LOCALES_IN})
add_library(UrdeLocales ${LOCALES_OUT} locale.hpp locale.cpp)
target_link_libraries(UrdeLocales fmt)
target_include_directories(UrdeLocales PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(specter PUBLIC UrdeLocales)

View File

@ -1,3 +0,0 @@
name: "British English"
en_GB:
color: "Colour"

View File

@ -1,42 +0,0 @@
name: "US English"
en_US:
color: "Color"
branch: "Branch"
commit: "Commit"
release: "Release"
date: "Date"
new_project: "New Project"
open_project: "Open Project"
extract_game: "Extract Game"
name: "Name"
type: "Type"
size: "Size"
directory: "Directory"
file: "File"
file_name: "File Name"
cancel: "Cancel"
system_locations: "System Locations"
recent_projects: "Recent Projects"
recent_files: "Recent Files"
scroll_left: "Scroll Left"
scroll_right: "Scroll Right"
ok: "OK"
cancel: "Cancel"
boundary_action: "Boundary Action"
split: "Split"
join: "Join"
hecl_project: "HECL Project"
no_access_as_dir: "Unable to access '{}' as directory"
file_field_empty: "Unable to save empty file"
overwrite_confirm: "Overwrite '{}'?"
directory_field_empty: "Unable to make empty-named directory"
no_overwrite_file: "Unable to make directory over file"
no_overwrite_project: "Unable to make project within existing project"
no_access_as_file: "Unable to access '{}' as file"
space_types: "Space Types"
resource_browser: "Resource Browser"
effect_editor: "Effect Editor"
model_viewer: "Model Viewer"
information_center: "Information Center"
game_mode: "Game Mode"
version: "Version"

View File

@ -1,113 +0,0 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_set>
#include "athena/FileReader.hpp"
#include "athena/YAMLDocReader.hpp"
#include <fmt/format.h>
#include <fmt/ostream.h>
int main(int argc, char** argv) {
if (argc < 3) {
fmt::print(FMT_STRING("{} <out-header> <in-yamls>...\n"), argv[0]);
return 1;
}
std::ofstream out(argv[1]);
if (!out.is_open()) {
fmt::print(FMT_STRING("Unable to open {} for writing\n"), argv[1]);
return 1;
}
std::unordered_set<std::string> seenLocales;
std::stringstream enumLocales;
std::stringstream declLocales;
std::unordered_set<std::string> seenKeys;
std::stringstream keys;
std::stringstream lookups;
std::stringstream dos;
for (int i = 2; i < argc; ++i) {
athena::io::FileReader fr(argv[i]);
if (!fr.isOpen()) {
fmt::print(FMT_STRING("Unable to open {} for reading\n"), argv[i]);
return 1;
}
athena::io::YAMLDocReader r;
if (!r.parse(&fr)) {
fmt::print(FMT_STRING("Unable to parse {}\n"), argv[i]);
return 1;
}
std::string name;
std::string fullName;
athena::io::YAMLNode* listNode = nullptr;
for (const auto& c : r.getRootNode()->m_mapChildren) {
if (c.first == "name") {
fullName = c.second->m_scalarString;
} else {
name = c.first;
listNode = c.second.get();
}
}
if (fullName.empty()) {
fmt::print(FMT_STRING("Unable to find 'name' node in {}\n"), argv[i]);
return 1;
}
if (!listNode) {
fmt::print(FMT_STRING("Unable to find list node in {}\n"), argv[i]);
return 1;
}
if (seenLocales.find(name) == seenLocales.end()) {
seenLocales.insert(name);
fmt::print(enumLocales, FMT_STRING(" {},\n"), name);
fmt::print(declLocales,
FMT_STRING("struct {0} {{ static constexpr auto Name = \"{0}\"sv; static constexpr auto FullName = \"{1}\"sv; }};\n"),
name, fullName);
fmt::print(dos,
FMT_STRING(" case ELocale::{0}:\n"
" return act.template Do<{0}>(std::forward<Args>(args)...);\n"), name);
fmt::print(lookups, FMT_STRING("/* {} */\n"), name);
for (const auto& k : listNode->m_mapChildren) {
if (seenKeys.find(k.first) == seenKeys.end()) {
seenKeys.insert(k.first);
fmt::print(keys, FMT_STRING("struct {} {{}};\n"), k.first);
}
fmt::print(lookups,
FMT_STRING("template<> struct Lookup<{}, {}> {{ static constexpr auto Value() {{ return FMT_STRING(\"{}\"); }} }};\n"),
name, k.first, k.second->m_scalarString);
}
}
lookups << '\n';
}
out << "/* Locales */\n"
"enum class ELocale {\n"
" Invalid = -1,\n";
out << enumLocales.str();
out << " MAXLocale\n"
"};\n";
out << declLocales.str();
out << "\n"
"using DefaultLocale = en_US;\n"
"template<typename L, typename K> struct Lookup {\n"
" static_assert(!std::is_same_v<L, DefaultLocale>, \"The default locale must translate all keys\");\n"
" static constexpr auto Value() { return Lookup<DefaultLocale, K>::Value(); }\n"
"};\n"
"\n"
"/* Keys */\n";
out << keys.str();
out << "\n";
out << lookups.str();
out << "template <typename Action, typename... Args>\n"
"constexpr auto Do(ELocale l, Action act, Args&&... args) {\n"
" switch (l) {\n"
" default:\n";
out << dos.str();
out << " }\n"
"}\n";
return 0;
}

View File

@ -1,19 +0,0 @@
name: "日本語"
ja_JP:
color: "色"
branch: "分派"
commit: "預ける"
date: "年月日"
new_project: "新しいプロジェクト"
open_project: "プロジェクトを開きます"
extract_game: "ビデオゲームを抽出"
name: "名"
type: "タイプ"
size: "サイズ"
directory: "ディレクトリ"
file: "ファイル"
file_name: "ファイル名"
cancel: "キャンセル"
system_locations: "システムの場所"
recent_projects: "最近使ったプロジェクト"
recent_files: "最近使用したファイル"

View File

@ -1,37 +0,0 @@
#include "locale.hpp"
#include <cstring>
#include <clocale>
#include <algorithm>
#undef min
#undef max
namespace locale {
std::vector<std::pair<std::string_view, std::string_view>> ListLocales() {
std::vector<std::pair<std::string_view, std::string_view>> ret;
ret.reserve(std::size_t(ELocale::MAXLocale));
for (ELocale l = ELocale(0); l < ELocale::MAXLocale; l = ELocale(int(l) + 1))
ret.emplace_back(GetName(l), GetFullName(l));
return ret;
}
ELocale LookupLocale(std::string_view name) {
for (ELocale l = ELocale(0); l < ELocale::MAXLocale; l = ELocale(int(l) + 1))
if (name == GetName(l))
return l;
return ELocale::Invalid;
}
ELocale SystemLocaleOrEnglish() {
const char* sysLocale = std::setlocale(LC_ALL, nullptr);
size_t sysLocaleLen = std::strlen(sysLocale);
for (ELocale l = ELocale(0); l < ELocale::MAXLocale; l = ELocale(int(l) + 1)) {
auto name = GetName(l);
if (!name.compare(0, std::min(name.size(), sysLocaleLen), sysLocale))
return l;
}
return ELocale::en_US;
}
} // namespace locale

View File

@ -1,42 +0,0 @@
#pragma once
#include <type_traits>
#include <string_view>
#include <vector>
#include <fmt/format.h>
namespace locale {
using namespace std::literals;
#include <locales-inl.hpp>
struct DoGetName {
template <typename L>
constexpr auto Do() { return L::Name; }
};
constexpr auto GetName(ELocale l) {
return Do(l, DoGetName());
}
struct DoGetFullName {
template <typename L>
constexpr auto Do() { return L::FullName; }
};
constexpr auto GetFullName(ELocale l) {
return Do(l, DoGetFullName());
}
template <typename Key>
struct DoTranslate {
template <typename L, typename... Args>
constexpr auto Do(Args&&... args) { return fmt::format(Lookup<L, Key>::Value(), std::forward<Args>(args)...); }
};
template <typename Key, typename... Args>
constexpr auto Translate(ELocale l, Args&&... args) {
return Do(l, DoTranslate<Key>(), std::forward<Args>(args)...);
}
std::vector<std::pair<std::string_view, std::string_view>> ListLocales();
ELocale LookupLocale(std::string_view name);
ELocale SystemLocaleOrEnglish();
} // namespace locale

View File

@ -1,247 +0,0 @@
#include "logvisor/logvisor.hpp"
#include "boo/boo.hpp"
#include "specter/specter.hpp"
#include "hecl/CVarManager.hpp"
#include "Runtime/CBasics.hpp"
#include "ViewManager.hpp"
#include "hecl/hecl.hpp"
#include "hecl/CVarCommons.hpp"
#include "hecl/Console.hpp"
#include "fmt/chrono.h"
#include "version.h"
#include "optick.h"
static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt,
fmt::format_args args) {
AthenaLog.vreport(logvisor::Level(level), fmt, args);
}
namespace metaforce {
static logvisor::Module Log{"Metaforce"};
static hecl::SystemString CPUFeatureString(const zeus::CPUInfo& cpuInf) {
hecl::SystemString features;
#if defined(__x86_64__) || defined(_M_X64)
auto AddFeature = [&features](const hecl::SystemChar* str) {
if (!features.empty())
features += _SYS_STR(", ");
features += str;
};
if (cpuInf.AESNI)
AddFeature(_SYS_STR("AES-NI"));
if (cpuInf.SSE1)
AddFeature(_SYS_STR("SSE"));
if (cpuInf.SSE2)
AddFeature(_SYS_STR("SSE2"));
if (cpuInf.SSE3)
AddFeature(_SYS_STR("SSE3"));
if (cpuInf.SSSE3)
AddFeature(_SYS_STR("SSSE3"));
if (cpuInf.SSE4a)
AddFeature(_SYS_STR("SSE4a"));
if (cpuInf.SSE41)
AddFeature(_SYS_STR("SSE4.1"));
if (cpuInf.SSE42)
AddFeature(_SYS_STR("SSE4.2"));
if (cpuInf.AVX)
AddFeature(_SYS_STR("AVX"));
if (cpuInf.AVX2)
AddFeature(_SYS_STR("AVX2"));
#endif
return features;
}
struct Application : boo::IApplicationCallback {
hecl::Runtime::FileStoreManager& m_fileMgr;
hecl::CVarManager& m_cvarManager;
hecl::CVarCommons& m_cvarCommons;
std::unique_ptr<ViewManager> m_viewManager;
std::atomic_bool m_running = {true};
Application(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr, hecl::CVarCommons& cvarCmns)
: m_fileMgr(fileMgr)
, m_cvarManager(cvarMgr)
, m_cvarCommons(cvarCmns)
, m_viewManager(std::make_unique<ViewManager>(m_fileMgr, m_cvarManager)) {}
virtual ~Application() = default;
int appMain(boo::IApplication* app) override {
initialize(app);
m_viewManager->init(app);
while (m_running.load()) {
OPTICK_FRAME("MainThread");
if (!m_viewManager->proc())
break;
}
m_viewManager->stop();
m_viewManager->projectManager().saveProject();
m_cvarManager.serialize();
m_viewManager.reset();
return 0;
}
void appQuitting(boo::IApplication*) override { m_running.store(false); }
void appFilesOpen(boo::IApplication*, const std::vector<boo::SystemString>& paths) override {
for (const auto& path : paths) {
hecl::ProjectRootPath projPath = hecl::SearchForProject(path);
if (projPath) {
m_viewManager->deferOpenProject(path);
break;
}
}
}
void initialize(boo::IApplication* app) {
zeus::detectCPU();
for (const boo::SystemString& arg : app->getArgs()) {
if (arg.find(_SYS_STR("--verbosity=")) == 0 || arg.find(_SYS_STR("-v=")) == 0) {
hecl::SystemUTF8Conv utf8Arg(arg.substr(arg.find_last_of('=') + 1));
hecl::VerbosityLevel = atoi(utf8Arg.c_str());
hecl::LogModule.report(logvisor::Info, FMT_STRING("Set verbosity level to {}"), hecl::VerbosityLevel);
}
}
const zeus::CPUInfo& cpuInf = zeus::cpuFeatures();
Log.report(logvisor::Info, FMT_STRING("CPU Name: {}"), cpuInf.cpuBrand);
Log.report(logvisor::Info, FMT_STRING("CPU Vendor: {}"), cpuInf.cpuVendor);
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("CPU Features: {}")), CPUFeatureString(cpuInf));
}
std::string getGraphicsApi() const { return m_cvarCommons.getGraphicsApi(); }
uint32_t getSamples() const { return m_cvarCommons.getSamples(); }
uint32_t getAnisotropy() const { return m_cvarCommons.getAnisotropy(); }
bool getDeepColor() const { return m_cvarCommons.getDeepColor(); }
int64_t getTargetFrameTime() { return m_cvarCommons.getVariableFrameTime() ? 0 : 1000000000L / 60; }
};
} // namespace metaforce
static hecl::SystemChar CwdBuf[1024];
hecl::SystemString ExeDir;
static void SetupBasics(bool logging) {
auto result = zeus::validateCPU();
if (!result.first) {
#if _WIN32 && !WINDOWS_STORE
std::wstring msg = fmt::format(FMT_STRING(L"ERROR: This build of Metaforce requires the following CPU features:\n{}\n"),
metaforce::CPUFeatureString(result.second));
MessageBoxW(nullptr, msg.c_str(), L"CPU error", MB_OK | MB_ICONERROR);
#else
fmt::print(stderr, FMT_STRING("ERROR: This build of Metaforce requires the following CPU features:\n{}\n"),
metaforce::CPUFeatureString(result.second));
#endif
exit(1);
}
logvisor::RegisterStandardExceptions();
if (logging)
logvisor::RegisterConsoleLogger();
atSetExceptionHandler(AthenaExc);
#if SENTRY_ENABLED
hecl::Runtime::FileStoreManager fileMgr{_SYS_STR("sentry-native-metaforce")};
hecl::SystemUTF8Conv cacheDir{fileMgr.getStoreRoot()};
logvisor::RegisterSentry("metaforce", METAFORCE_WC_DESCRIBE, cacheDir.c_str());
#endif
}
static bool IsClientLoggingEnabled(int argc, const boo::SystemChar** argv) {
for (int i = 1; i < argc; ++i)
if (!hecl::StrNCmp(argv[i], _SYS_STR("-l"), 2))
return true;
return false;
}
#if !WINDOWS_STORE
#if _WIN32
int wmain(int argc, const boo::SystemChar** argv)
#else
int main(int argc, const boo::SystemChar** argv)
#endif
{
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
fmt::print(FMT_STRING("{}\n"), METAFORCE_DLPACKAGE);
return 100;
}
SetupBasics(IsClientLoggingEnabled(argc, argv));
hecl::Runtime::FileStoreManager fileMgr{_SYS_STR("metaforce")};
hecl::CVarManager cvarMgr{fileMgr};
hecl::CVarCommons cvarCmns{cvarMgr};
std::vector<boo::SystemString> args;
for (int i = 1; i < argc; ++i)
args.push_back(argv[i]);
cvarMgr.parseCommandLine(args);
hecl::SystemString logFile{hecl::SystemStringConv(cvarCmns.getLogFile()).c_str()};
hecl::SystemString logFilePath;
if (!logFile.empty()) {
std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
char buf[100];
std::strftime(buf, 100, "%Y-%m-%d_%H-%M-%S", std::localtime(&time));
hecl::SystemString timeStr = hecl::SystemStringConv(buf).c_str();
logFilePath = fmt::format(FMT_STRING(_SYS_STR("{}/{}-{}")), fileMgr.getStoreRoot(), timeStr, logFile);
logvisor::RegisterFileLogger(logFilePath.c_str());
}
if (hecl::SystemChar* cwd = hecl::Getcwd(CwdBuf, 1024)) {
if (hecl::PathRelative(argv[0]))
ExeDir = hecl::SystemString(cwd) + _SYS_STR('/');
hecl::SystemString Argv0(argv[0]);
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
if (lastIdx != hecl::SystemString::npos)
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
}
/* Handle -j argument */
hecl::SetCpuCountOverride(argc, argv);
metaforce::Application appCb(fileMgr, cvarMgr, cvarCmns);
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto, appCb, _SYS_STR("metaforce"),
_SYS_STR("Metaforce"), argc, argv, appCb.getGraphicsApi(), appCb.getSamples(),
appCb.getAnisotropy(), appCb.getDeepColor(), appCb.getTargetFrameTime(), false);
// printf("IM DYING!!\n");
return ret;
}
#endif
#if WINDOWS_STORE
#include "boo/UWPViewProvider.hpp"
using namespace Windows::ApplicationModel::Core;
[Platform::MTAThread] int WINAPIV main(Platform::Array<Platform::String ^> ^ params) {
SetupBasics(false);
metaforce::Application appCb;
auto viewProvider =
ref new boo::ViewProvider(appCb, _SYS_STR("metaforce"), _SYS_STR("Metaforce"), _SYS_STR("metaforce"), params, false);
CoreApplication::Run(viewProvider);
return 0;
}
#elif _WIN32
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i = 0; i < argc; ++i)
booArgv[i + 1] = argv[i];
const DWORD outType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
if (IsClientLoggingEnabled(argc + 1, booArgv) && outType == FILE_TYPE_UNKNOWN)
logvisor::CreateWin32Console();
return wmain(argc + 1, booArgv);
}
#endif

View File

@ -1,18 +0,0 @@
add_executable(mkwmicon mkwmicon.c)
target_link_libraries(mkwmicon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
target_include_directories(mkwmicon PRIVATE ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
macro(declare_wmicon_target)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_netwm.bin
COMMAND $<TARGET_FILE:mkwmicon>
ARGS ${CMAKE_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_netwm.bin
DEPENDS
${CMAKE_SOURCE_DIR}/Editor/platforms/freedesktop/128x128/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/metaforce.png
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Editor/platforms/freedesktop
COMMENT "Generating mainicon_netwm.bin")
bintoc(mainicon_netwm.cpp ${CMAKE_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_netwm.bin MAINICON_NETWM)
endmacro()

509
Runtime/CMain.cpp Normal file
View File

@ -0,0 +1,509 @@
#include <string>
#include <string_view>
#include <iostream>
#include <hecl/Pipeline.hpp>
#include "boo/boo.hpp"
#include "logvisor/logvisor.hpp"
#include "Runtime/Graphics/CGraphics.hpp"
#include "Runtime/MP1/MP1.hpp"
#include "cmake-build-debug-llvm/hecl/DataSpecRegistry.hpp"
#include "amuse/BooBackend.hpp"
#include "../version.h"
using namespace std::literals;
static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt,
fmt::format_args args) {
AthenaLog.vreport(logvisor::Level(level), fmt, args);
}
class Limiter {
using delta_clock = std::chrono::steady_clock;
using nanotime_t = std::chrono::nanoseconds::rep;
public:
void Sleep(nanotime_t targetFrameTime) {
if (targetFrameTime == 0) {
return;
}
auto start = delta_clock::now();
nanotime_t adjustedSleepTime = ShouldSleep(targetFrameTime);
if (adjustedSleepTime > 0) {
std::this_thread::sleep_for(std::chrono::nanoseconds(adjustedSleepTime));
nanotime_t overslept = TimeSince(start) - adjustedSleepTime;
if (overslept < targetFrameTime) {
m_overheadTimes[m_overheadTimeIdx] = overslept;
m_overheadTimeIdx = (m_overheadTimeIdx + 1) % m_overheadTimes.size();
}
}
m_oldTime = delta_clock::now();
}
nanotime_t ShouldSleep(nanotime_t targetFrameTime) {
nanotime_t sleepTime = targetFrameTime - TimeSince(m_oldTime);
m_overhead = std::accumulate(m_overheadTimes.begin(), m_overheadTimes.end(), nanotime_t{}) /
static_cast<nanotime_t>(m_overheadTimes.size());
if (sleepTime > m_overhead) {
return sleepTime - m_overhead;
}
return 0;
}
private:
delta_clock::time_point m_oldTime;
std::array<nanotime_t, 4> m_overheadTimes{};
size_t m_overheadTimeIdx = 0;
nanotime_t m_overhead = 0;
nanotime_t TimeSince(delta_clock::time_point start) {
return std::chrono::duration_cast<std::chrono::nanoseconds>(delta_clock::now() - start).count();
}
};
extern hecl::SystemString ExeDir;
namespace metaforce {
static logvisor::Module Log{"Metaforce"};
std::optional<MP1::CMain> g_mainMP1;
static hecl::SystemString CPUFeatureString(const zeus::CPUInfo& cpuInf) {
hecl::SystemString features;
#if defined(__x86_64__) || defined(_M_X64)
auto AddFeature = [&features](const hecl::SystemChar* str) {
if (!features.empty())
features += _SYS_STR(", ");
features += str;
};
if (cpuInf.AESNI)
AddFeature(_SYS_STR("AES-NI"));
if (cpuInf.SSE1)
AddFeature(_SYS_STR("SSE"));
if (cpuInf.SSE2)
AddFeature(_SYS_STR("SSE2"));
if (cpuInf.SSE3)
AddFeature(_SYS_STR("SSE3"));
if (cpuInf.SSSE3)
AddFeature(_SYS_STR("SSSE3"));
if (cpuInf.SSE4a)
AddFeature(_SYS_STR("SSE4a"));
if (cpuInf.SSE41)
AddFeature(_SYS_STR("SSE4.1"));
if (cpuInf.SSE42)
AddFeature(_SYS_STR("SSE4.2"));
if (cpuInf.AVX)
AddFeature(_SYS_STR("AVX"));
if (cpuInf.AVX2)
AddFeature(_SYS_STR("AVX2"));
#endif
return features;
}
struct WindowCallback : boo::IWindowCallback {
bool m_fullscreenToggleRequested = false;
boo::SWindowRect m_lastRect;
bool m_rectDirty = false;
bool m_windowInvalid = false;
void resized(const boo::SWindowRect& rect, bool sync) override {
m_lastRect = rect;
m_rectDirty = true;
}
void mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->mouseDown(coord, button, mods);
}
}
}
void mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->mouseUp(coord, button, mods);
}
}
}
void mouseMove(const boo::SWindowCoord& coord) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->mouseMove(coord);
}
}
}
void scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->scroll(coord, scroll);
}
}
}
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->charKeyDown(charCode, mods, isRepeat);
}
}
}
void charKeyUp(unsigned long charCode, boo::EModifierKey mods) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->charKeyUp(charCode, mods);
}
}
}
void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->specialKeyDown(key, mods, isRepeat);
}
}
if (key == boo::ESpecialKey::Enter && True(mods & boo::EModifierKey::Alt))
m_fullscreenToggleRequested = true;
}
void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods) override {
if (g_mainMP1) {
if (MP1::CGameArchitectureSupport* as = g_mainMP1->GetArchSupport()) {
as->specialKeyUp(key, mods);
}
}
}
void destroyed() override { m_windowInvalid = true; }
};
struct Application : boo::IApplicationCallback {
std::shared_ptr<boo::IWindow> m_window;
WindowCallback m_windowCallback;
hecl::Runtime::FileStoreManager& m_fileMgr;
hecl::CVarManager& m_cvarManager;
hecl::CVarCommons& m_cvarCommons;
boo::ObjToken<boo::ITextureR> m_renderTex;
hecl::SystemString m_deferredProject;
std::unique_ptr<hecl::Database::Project> m_proj;
std::optional<amuse::BooBackendVoiceAllocator> m_amuseAllocWrapper;
std::unique_ptr<boo::IAudioVoiceEngine> m_voiceEngine;
std::unique_ptr<hecl::PipelineConverterBase> m_pipelineConv;
Limiter m_limiter{};
std::atomic_bool m_running = {true};
bool m_noShaderWarmup = false;
public:
Application(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr, hecl::CVarCommons& cvarCmns)
: m_fileMgr(fileMgr), m_cvarManager(cvarMgr), m_cvarCommons(cvarCmns) {}
int appMain(boo::IApplication* app) override {
initialize(app);
m_window = app->newWindow(_SYS_STR("Metaforce"sv));
if (!m_window) {
return 1;
}
m_window->setCallback(&m_windowCallback);
m_window->showWindow();
boo::SWindowRect rect = m_window->getWindowFrame();
m_window->getMainContextDataFactory()->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) {
m_renderTex = ctx.newRenderTexture(rect.size[0], rect.size[1], boo::TextureClampMode::ClampToEdge, 3, 3);
return true;
} BooTrace);
m_pipelineConv = hecl::NewPipelineConverter(m_window->getMainContextDataFactory());
hecl::conv = m_pipelineConv.get();
m_voiceEngine = boo::NewAudioVoiceEngine();
m_voiceEngine->setVolume(0.7f);
m_amuseAllocWrapper.emplace(*m_voiceEngine);
hecl::ProjectPath projectPath;
for (const auto& arg : app->getArgs()) {
hecl::Sstat theStat;
if (!hecl::Stat((arg + _SYS_STR("/out")).c_str(), &theStat) && S_ISDIR(theStat.st_mode)) {
hecl::ProjectRootPath rootPath(arg);
hecl::Database::Project tmp(rootPath); // Force project creation
}
if (m_deferredProject.empty() && hecl::SearchForProject(arg))
m_deferredProject = arg;
if (arg == _SYS_STR("--no-shader-warmup"))
m_noShaderWarmup = true;
else if (arg == _SYS_STR("--no-sound"))
m_voiceEngine->setVolume(0.f);
}
if (m_deferredProject.empty()) {
/* Default behavior - search upwards for packaged project containing the program */
if (hecl::ProjectRootPath projRoot = hecl::SearchForProject(ExeDir)) {
hecl::SystemString rootPath(projRoot.getAbsolutePath());
hecl::Sstat theStat;
if (hecl::Stat((rootPath + _SYS_STR("/out/files/MP1/Metroid1.upak")).c_str(), &theStat) == 0 &&
S_ISREG(theStat.st_mode))
m_deferredProject = rootPath + _SYS_STR("/out");
}
}
while (m_running) {
onAppIdle();
}
if (m_window) {
m_window->getCommandQueue()->stopRenderer();
}
if (g_mainMP1)
g_mainMP1->Shutdown();
g_mainMP1.reset();
m_voiceEngine.reset();
m_amuseAllocWrapper.reset();
CDvdFile::Shutdown();
return 0;
}
void initialize(boo::IApplication* app) {
zeus::detectCPU();
for (const boo::SystemString& arg : app->getArgs()) {
if (arg.find(_SYS_STR("--verbosity=")) == 0 || arg.find(_SYS_STR("-v=")) == 0) {
hecl::SystemUTF8Conv utf8Arg(arg.substr(arg.find_last_of('=') + 1));
hecl::VerbosityLevel = atoi(utf8Arg.c_str());
hecl::LogModule.report(logvisor::Info, FMT_STRING("Set verbosity level to {}"), hecl::VerbosityLevel);
}
}
const zeus::CPUInfo& cpuInf = zeus::cpuFeatures();
Log.report(logvisor::Info, FMT_STRING("CPU Name: {}"), cpuInf.cpuBrand);
Log.report(logvisor::Info, FMT_STRING("CPU Vendor: {}"), cpuInf.cpuVendor);
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("CPU Features: {}")), CPUFeatureString(cpuInf));
}
void onAppIdle() noexcept {
if (!m_deferredProject.empty()) {
hecl::SystemString subPath;
hecl::ProjectRootPath projPath = hecl::SearchForProject(m_deferredProject, subPath);
if (!projPath) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("project doesn't exist at '{}'")), m_deferredProject);
m_running.store(false);
return;
}
m_proj = std::make_unique<hecl::Database::Project>(projPath);
m_deferredProject.clear();
hecl::ProjectPath projectPath{m_proj->getProjectWorkingPath(), _SYS_STR("out/files/MP1")};
CDvdFile::Initialize(projectPath);
}
if (!m_proj) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Project directory not specified")));
m_running.store(false);
return;
}
m_cvarManager.proc();
if (m_windowCallback.m_windowInvalid) {
m_running.store(false);
return;
}
m_window->waitForRetrace();
boo::IGraphicsCommandQueue* gfxQ = m_window->getCommandQueue();
if (m_windowCallback.m_rectDirty) {
boo::SWindowRect& rect = m_windowCallback.m_lastRect;
gfxQ->resizeRenderTexture(m_renderTex, rect.size[0], rect.size[1]);
metaforce::CGraphics::SetViewportResolution({rect.size[0], rect.size[1]});
m_windowCallback.m_rectDirty = false;
}
if (m_windowCallback.m_fullscreenToggleRequested) {
m_window->setFullscreen(!m_window->isFullscreen());
m_windowCallback.m_fullscreenToggleRequested = false;
}
if (!g_mainMP1) {
g_mainMP1.emplace(nullptr, nullptr, m_window->getMainContextDataFactory(), gfxQ, m_renderTex.get());
g_mainMP1->Init(m_fileMgr, &m_cvarManager, m_window.get(), m_voiceEngine.get(), *m_amuseAllocWrapper);
if (!m_noShaderWarmup) {
g_mainMP1->WarmupShaders();
}
}
if (g_mainMP1->Proc()) {
m_running.store(false);
return;
}
gfxQ->setRenderTarget(m_renderTex);
{
OPTICK_EVENT("Draw");
if (g_Renderer)
g_Renderer->BeginScene();
g_mainMP1->Draw();
if (g_Renderer)
g_Renderer->EndScene();
gfxQ->execute();
}
gfxQ->resolveDisplay(m_renderTex);
if (g_ResFactory != nullptr) {
int64_t targetFrameTime = getTargetFrameTime();
do {
g_ResFactory->AsyncIdle();
} while (m_limiter.ShouldSleep(targetFrameTime) != 0);
m_limiter.Sleep(targetFrameTime);
}
if (m_voiceEngine) {
m_voiceEngine->pumpAndMixVoices();
}
CBooModel::ClearModelUniformCounters();
CGraphics::TickRenderTimings();
++logvisor::FrameIndex;
}
void appQuitting(boo::IApplication* /*unused*/) override { m_running.store(false); }
[[nodiscard]] std::string getGraphicsApi() const { return m_cvarCommons.getGraphicsApi(); }
[[nodiscard]] uint32_t getSamples() const { return m_cvarCommons.getSamples(); }
[[nodiscard]] uint32_t getAnisotropy() const { return m_cvarCommons.getAnisotropy(); }
[[nodiscard]] bool getDeepColor() const { return m_cvarCommons.getDeepColor(); }
[[nodiscard]] int64_t getTargetFrameTime() const { return m_cvarCommons.getVariableFrameTime() ? 0 : 1000000000L / 60; }
};
} // namespace metaforce
static hecl::SystemChar CwdBuf[1024];
hecl::SystemString ExeDir;
static void SetupBasics(bool logging) {
auto result = zeus::validateCPU();
if (!result.first) {
#if _WIN32 && !WINDOWS_STORE
std::wstring msg =
fmt::format(FMT_STRING(L"ERROR: This build of Metaforce requires the following CPU features:\n{}\n"),
metaforce::CPUFeatureString(result.second));
MessageBoxW(nullptr, msg.c_str(), L"CPU error", MB_OK | MB_ICONERROR);
#else
fmt::print(stderr, FMT_STRING("ERROR: This build of Metaforce requires the following CPU features:\n{}\n"),
metaforce::CPUFeatureString(result.second));
#endif
exit(1);
}
logvisor::RegisterStandardExceptions();
if (logging)
logvisor::RegisterConsoleLogger();
atSetExceptionHandler(AthenaExc);
#if SENTRY_ENABLED
hecl::Runtime::FileStoreManager fileMgr{_SYS_STR("sentry-native-metaforce")};
hecl::SystemUTF8Conv cacheDir{fileMgr.getStoreRoot()};
logvisor::RegisterSentry("metaforce", METAFORCE_WC_DESCRIBE, cacheDir.c_str());
#endif
}
static bool IsClientLoggingEnabled(int argc, const boo::SystemChar** argv) {
for (int i = 1; i < argc; ++i)
if (!hecl::StrNCmp(argv[i], _SYS_STR("-l"), 2))
return true;
return false;
}
#if !WINDOWS_STORE
#if _WIN32
int wmain(int argc, const boo::SystemChar** argv)
#else
int main(int argc, const boo::SystemChar** argv)
#endif
{
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
fmt::print(FMT_STRING("{}\n"), METAFORCE_DLPACKAGE);
return 100;
}
SetupBasics(IsClientLoggingEnabled(argc, argv));
hecl::Runtime::FileStoreManager fileMgr{_SYS_STR("metaforce")};
hecl::CVarManager cvarMgr{fileMgr};
hecl::CVarCommons cvarCmns{cvarMgr};
std::vector<boo::SystemString> args;
for (int i = 1; i < argc; ++i)
args.push_back(argv[i]);
cvarMgr.parseCommandLine(args);
hecl::SystemString logFile{hecl::SystemStringConv(cvarCmns.getLogFile()).c_str()};
hecl::SystemString logFilePath;
if (!logFile.empty()) {
std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
char buf[100];
std::strftime(buf, 100, "%Y-%m-%d_%H-%M-%S", std::localtime(&time));
hecl::SystemString timeStr = hecl::SystemStringConv(buf).c_str();
logFilePath = fmt::format(FMT_STRING(_SYS_STR("{}/{}-{}")), fileMgr.getStoreRoot(), timeStr, logFile);
logvisor::RegisterFileLogger(logFilePath.c_str());
}
if (hecl::SystemChar* cwd = hecl::Getcwd(CwdBuf, 1024)) {
if (hecl::PathRelative(argv[0]))
ExeDir = hecl::SystemString(cwd) + _SYS_STR('/');
hecl::SystemString Argv0(argv[0]);
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
if (lastIdx != hecl::SystemString::npos)
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
}
/* Handle -j argument */
hecl::SetCpuCountOverride(argc, argv);
metaforce::Application appCb(fileMgr, cvarMgr, cvarCmns);
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto, appCb, _SYS_STR("metaforce"),
_SYS_STR("Metaforce"), argc, argv, appCb.getGraphicsApi(), appCb.getSamples(),
appCb.getAnisotropy(), appCb.getDeepColor(), false);
return ret;
}
#endif
#if WINDOWS_STORE
#include "boo/UWPViewProvider.hpp"
using namespace Windows::ApplicationModel::Core;
[Platform::MTAThread] int WINAPIV main(Platform::Array<Platform::String ^> ^ params) {
SetupBasics(false);
metaforce::Application appCb;
auto viewProvider = ref new boo::ViewProvider(appCb, _SYS_STR("metaforce"), _SYS_STR("Metaforce"),
_SYS_STR("metaforce"), params, false);
CoreApplication::Run(viewProvider);
return 0;
}
#elif _WIN32
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i = 0; i < argc; ++i)
booArgv[i + 1] = argv[i];
const DWORD outType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
if (IsClientLoggingEnabled(argc + 1, booArgv) && outType == FILE_TYPE_UNKNOWN)
logvisor::CreateWin32Console();
return wmain(argc + 1, booArgv);
}
#endif

View File

@ -49,8 +49,9 @@ if(APPLE)
endif()
set(RUNTIME_SOURCES_A
MkCastTo.py
TCastTo.hpp TCastTo.cpp
RetroTypes.hpp RetroTypes.cpp
${CLIENT_SOURCES}
${MP1_SOURCES}
${AUDIO_SOURCES}
${AUTOMAPPER_SOURCES}
@ -114,8 +115,6 @@ set(RUNTIME_SOURCES_B
CGameHintInfo.hpp CGameHintInfo.cpp
rstl.hpp
GameGlobalObjects.hpp GameGlobalObjects.cpp
MkCastTo.py
TCastTo.hpp TCastTo.cpp
GCNTypes.hpp
CTextureCache.hpp CTextureCache.cpp
CMayaSpline.hpp CMayaSpline.cpp
@ -136,16 +135,82 @@ set(RUNTIME_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(RUNTIME_LIBRARIES ${HECL_APPLICATION_REPS_TARGETS_LIST} RetroDataSpec AssetNameMapNull NESEmulator
libjpeg-turbo jbus kabufuda discord-rpc logvisor OptickCore)
if(MSVC)
# WTF MS???? LINK.EXE is unable to address static libraries larger than 4GB.
# This is a hack to split this large library in two.
add_runtime_common_library(RuntimeCommon ${RUNTIME_SOURCES_A})
add_runtime_common_library(RuntimeCommonB ${RUNTIME_SOURCES_B})
target_include_directories(RuntimeCommonB PUBLIC ${RUNTIME_INCLUDES})
target_link_libraries(RuntimeCommonB PUBLIC ${RUNTIME_LIBRARIES})
target_link_libraries(RuntimeCommon PUBLIC RuntimeCommonB)
else()
add_runtime_common_library(RuntimeCommon ${RUNTIME_SOURCES_A} ${RUNTIME_SOURCES_B})
target_include_directories(RuntimeCommon PUBLIC ${RUNTIME_INCLUDES})
target_link_libraries(RuntimeCommon PUBLIC ${RUNTIME_LIBRARIES})
add_runtime_common_library(RuntimeCommon ${RUNTIME_SOURCES_A})
target_include_directories(RuntimeCommon PUBLIC ${RUNTIME_INCLUDES})
add_runtime_common_library(RuntimeCommonB ${RUNTIME_SOURCES_B})
target_link_libraries(RuntimeCommonB PUBLIC ${RUNTIME_LIBRARIES})
target_link_libraries(RuntimeCommon PUBLIC RuntimeCommonB)
if(WIN32)
configure_file(platforms/win/metaforce.rc.in "${CMAKE_CURRENT_SOURCE_DIR}/platforms/win/metaforce.rc" @ONLY)
set(PLAT_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/platforms/win/metaforce.rc" platforms/win/metaforce.manifest)
if(WINDOWS_STORE)
set(UWP_ASSETS
platforms/win/Assets/LargeTile.scale-100.png
platforms/win/Assets/LargeTile.scale-125.png
platforms/win/Assets/LargeTile.scale-150.png
platforms/win/Assets/LargeTile.scale-200.png
platforms/win/Assets/LargeTile.scale-400.png
platforms/win/Assets/SmallTile.scale-100.png
platforms/win/Assets/SmallTile.scale-125.png
platforms/win/Assets/SmallTile.scale-150.png
platforms/win/Assets/SmallTile.scale-200.png
platforms/win/Assets/SmallTile.scale-400.png
platforms/win/Assets/SplashScreen.scale-100.png
platforms/win/Assets/SplashScreen.scale-125.png
platforms/win/Assets/SplashScreen.scale-150.png
platforms/win/Assets/SplashScreen.scale-200.png
platforms/win/Assets/SplashScreen.scale-400.png
platforms/win/Assets/Square44x44Logo.scale-100.png
platforms/win/Assets/Square44x44Logo.scale-125.png
platforms/win/Assets/Square44x44Logo.scale-150.png
platforms/win/Assets/Square44x44Logo.scale-200.png
platforms/win/Assets/Square44x44Logo.scale-400.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-16.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-24.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-32.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-48.png
platforms/win/Assets/Square44x44Logo.altform-unplated_targetsize-256.png
platforms/win/Assets/Square150x150Logo.scale-100.png
platforms/win/Assets/Square150x150Logo.scale-125.png
platforms/win/Assets/Square150x150Logo.scale-150.png
platforms/win/Assets/Square150x150Logo.scale-200.png
platforms/win/Assets/Square150x150Logo.scale-400.png
platforms/win/Assets/metaforce.scale-100.png
platforms/win/Assets/metaforce.scale-125.png
platforms/win/Assets/metaforce.scale-150.png
platforms/win/Assets/metaforce.scale-200.png
platforms/win/Assets/metaforce.scale-400.png
platforms/win/Assets/WideTile.scale-100.png
platforms/win/Assets/WideTile.scale-125.png
platforms/win/Assets/WideTile.scale-150.png
platforms/win/Assets/WideTile.scale-200.png
platforms/win/Assets/WideTile.scale-400.png)
set_property(SOURCE platforms/win/Package.appxmanifest PROPERTY VS_DEPLOYMENT_CONTENT 1)
set_property(SOURCE ${UWP_ASSETS} PROPERTY VS_DEPLOYMENT_CONTENT 1)
set_property(SOURCE ${UWP_ASSETS} PROPERTY VS_DEPLOYMENT_LOCATION "Assets")
list(APPEND PLAT_SRCS ${UWP_ASSETS} platforms/win/Package.appxmanifest)
endif()
elseif(APPLE)
# nothing
elseif(UNIX)
add_subdirectory(platforms/freedesktop)
declare_wmicon_target()
set(PLAT_SRCS mainicon_netwm.cpp)
set(PLAT_LIBS rt)
endif()
add_executable(metaforce CMain.cpp ${PLAT_SRCS})
target_link_libraries(metaforce PUBLIC RuntimeCommon ${PLAT_LIBS})
if(COMMAND add_sanitizers)
add_sanitizers(metaforce)
endif()
if (NOT WINDOWS_STORE)
add_dependencies(metaforce visigen hecl)
else()
set_property(TARGET metaforce PROPERTY VS_WINRT_COMPONENT TRUE)
# This should match the Package.appxmanifest
set_property(TARGET metaforce PROPERTY VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION "10.0.14393.0")
endif()

View File

@ -1,7 +1,5 @@
#include "Runtime/Character/CModelData.hpp"
#include "Editor/ProjectManager.hpp"
#include "Runtime/CPlayerState.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"

View File

@ -1,17 +1,17 @@
#include "Runtime/Graphics/CMoviePlayer.hpp"
#include "Runtime/CDvdRequest.hpp"
#include "Runtime/Audio/g721.h"
#include "Runtime/CDvdRequest.hpp"
#include "Runtime/Graphics/CGraphics.hpp"
#include <amuse/DSPCodec.hpp>
#include <boo/graphicsdev/GLSLMacros.hpp>
#include <hecl/Pipeline.hpp>
#include <specter/View.hpp>
#include <turbojpeg.h>
namespace metaforce {
zeus::CMatrix4f g_PlatformMatrix;
/* used in the original to look up fixed-point dividends on a
* MIDI-style volume scale (0-127) -> (n/0x8000) */
static const u16 StaticVolumeLookup[] = {
@ -46,7 +46,14 @@ static float SfxVolume = 1.f;
static const char* BlockNames[] = {"SpecterViewBlock"};
static const char* TexNames[] = {"texY", "texU", "texV"};
void CMoviePlayer::Initialize() {
void CMoviePlayer::Initialize(boo::IGraphicsDataFactory* factory) {
switch (factory->platform()) {
case boo::IGraphicsDataFactory::Platform::Vulkan:
g_PlatformMatrix.m[1][1] = -1.f;
break;
default:
break;
}
YUVShaderPipeline = hecl::conv->convert(Shader_CMoviePlayerShader{});
TjHandle = tjInitDecompress();
}
@ -200,7 +207,7 @@ CMoviePlayer::CMoviePlayer(const char* path, float preLoadSeconds, bool loop, bo
/* All set for GPU resources */
CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) {
m_blockBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(m_viewVertBlock), 1);
m_vertBuf = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(specter::View::TexShaderVert), 4);
m_vertBuf = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(TexShaderVert), 4);
/* Allocate textures here (rather than at decode time) */
x80_textures.reserve(3);

View File

@ -6,12 +6,15 @@
#include "Runtime/CDvdFile.hpp"
#include "Runtime/RetroTypes.hpp"
#include <boo/IWindow.hpp>
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
#include <specter/View.hpp>
#include <zeus/CColor.hpp>
#include <zeus/CVector3f.hpp>
namespace metaforce {
extern zeus::CMatrix4f g_PlatformMatrix;
class CMoviePlayer : public CDvdFile {
public:
enum class EPlayMode { Stopped, Playing };
@ -31,26 +34,26 @@ private:
u32 firstFrameOffset;
u32 lastFrameOffset;
void swapBig();
} x28_thpHead;
} x28_thpHead{};
struct THPComponents {
u32 numComponents;
enum class Type : u8 { Video = 0x0, Audio = 0x1, None = 0xff } comps[16];
void swapBig();
} x58_thpComponents;
} x58_thpComponents{};
struct THPVideoInfo {
u32 width;
u32 height;
void swapBig();
} x6c_videoInfo;
} x6c_videoInfo{};
struct THPAudioInfo {
u32 numChannels;
u32 sampleRate;
u32 numSamples;
void swapBig();
} x74_audioInfo;
} x74_audioInfo{};
struct THPFrameHeader {
u32 nextSize;
@ -108,11 +111,30 @@ private:
std::unique_ptr<uint8_t[]> m_yuvBuf;
specter::View::ViewBlock m_viewVertBlock;
struct TexShaderVert {
zeus::CVector3f m_pos;
zeus::CVector2f m_uv;
};
struct ViewBlock {
zeus::CMatrix4f m_mv;
zeus::CColor m_color = zeus::skWhite;
void setViewRect(const boo::SWindowRect& root, const boo::SWindowRect& sub) {
m_mv[0][0] = 2.0f / root.size[0];
m_mv[1][1] = 2.0f / root.size[1];
m_mv[3][0] = sub.location[0] * m_mv[0][0] - 1.0f;
m_mv[3][1] = sub.location[1] * m_mv[1][1] - 1.0f;
}
void finalAssign(const ViewBlock& other) {
m_mv = g_PlatformMatrix * other.m_mv;
m_color = other.m_color;
}
};
ViewBlock m_viewVertBlock;
boo::ObjToken<boo::IGraphicsBufferD> m_blockBuf;
boo::ObjToken<boo::IGraphicsBufferD> m_vertBuf;
specter::View::TexShaderVert m_frame[4];
TexShaderVert m_frame[4];
static u32 THPAudioDecode(s16* buffer, const u8* audioFrame, bool stereo);
void DecodeFromRead(const void* data);
@ -145,7 +167,7 @@ public:
void Update(float dt);
std::pair<u32, u32> GetVideoDimensions() const { return {x6c_videoInfo.width, x6c_videoInfo.height}; }
static void Initialize();
static void Initialize(boo::IGraphicsDataFactory* factory);
static void Shutdown();
};

View File

@ -10,7 +10,7 @@ CGuiPane::CGuiPane(const CGuiWidgetParms& parms, const zeus::CVector2f& dim, con
void CGuiPane::ScaleDimensions(const zeus::CVector3f& scale) {
InitializeBuffers();
for (specter::View::TexShaderVert& v : xc0_verts) {
for (TexShaderVert& v : xc0_verts) {
v.m_pos -= xc8_scaleCenter;
v.m_pos *= scale;
v.m_pos += xc8_scaleCenter;

View File

@ -5,8 +5,6 @@
#include "Runtime/GuiSys/CGuiWidget.hpp"
#include <specter/View.hpp>
#include <zeus/CVector2f.hpp>
#include <zeus/CVector3f.hpp>
@ -16,8 +14,12 @@ class CGuiPane : public CGuiWidget {
protected:
zeus::CVector2f xb8_dim;
struct TexShaderVert {
zeus::CVector3f m_pos;
zeus::CVector2f m_uv;
};
/* Originally a vert-buffer pointer for GX */
std::vector<specter::View::TexShaderVert> xc0_verts;
std::vector<TexShaderVert> xc0_verts;
// u32 x104_ = 4; /* vert count */
zeus::CVector3f xc8_scaleCenter;

View File

@ -2,7 +2,6 @@
#include <memory>
#include "Editor/ProjectResourceFactoryBase.hpp"
#include "Runtime/rstl.hpp"
#include "Runtime/MP1/CInGameGuiManager.hpp"
#include "Runtime/MP1/CPauseScreenBase.hpp"

View File

@ -2,7 +2,9 @@
#include <algorithm>
#include "Editor/ProjectManager.hpp"
#include "Runtime/CArchitectureMessage.hpp"
#include "Runtime/CGameState.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/GameGlobalObjects.hpp"
namespace metaforce {

View File

@ -14,9 +14,9 @@
#include "DataSpec/DNAMP1/Tweaks/CTweakGuiColors.hpp"
#include "DataSpec/DNAMP1/Tweaks/CTweakPlayerGun.hpp"
#include "Editor/ProjectManager.hpp"
#include "Runtime/IMain.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/AutoMapper/CMappableObject.hpp"
#include "Runtime/World/CPlayerCameraBob.hpp"
#include <logvisor/logvisor.hpp>

View File

@ -269,6 +269,7 @@ CMain::BooSetter::BooSetter(boo::IGraphicsDataFactory* factory, boo::IGraphicsCo
CRandomStaticFilter::Initialize();
CEnvFxShaders::Initialize();
CNESShader::Initialize();
CMoviePlayer::Initialize(factory);
}
void CMain::RegisterResourceTweaks() {}
@ -407,7 +408,6 @@ void CMain::ResetGameState() {
void CMain::InitializeSubsystems() {
CBasics::Initialize();
CModelShaders::Initialize();
CMoviePlayer::Initialize();
CLineRenderer::Initialize();
CElementGen::Initialize();
CAnimData::InitializeCache();

View File

@ -2,7 +2,6 @@
#include <array>
#include "Editor/ProjectResourceFactoryBase.hpp"
#include "Runtime/CObjectList.hpp"
#include "Runtime/CToken.hpp"
#include "Runtime/RetroTypes.hpp"

View File

@ -1,7 +1,5 @@
#include "Runtime/World/ScriptLoader.hpp"
#include "Editor/ProjectResourceFactoryMP1.hpp"
#include "Runtime/Camera/CCinematicCamera.hpp"
#include "Runtime/Camera/CPathCamera.hpp"
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"

View File

Before

Width:  |  Height:  |  Size: 637 KiB

After

Width:  |  Height:  |  Size: 637 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 203 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,18 @@
add_executable(mkwmicon mkwmicon.c)
target_link_libraries(mkwmicon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
target_include_directories(mkwmicon PRIVATE ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
macro(declare_wmicon_target)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/Runtime/platforms/freedesktop/mainicon_netwm.bin
COMMAND $<TARGET_FILE:mkwmicon>
ARGS ${CMAKE_BINARY_DIR}/Runtime/platforms/freedesktop/mainicon_netwm.bin
DEPENDS
${CMAKE_SOURCE_DIR}/Runtime/platforms/freedesktop/128x128/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Runtime/platforms/freedesktop/64x64/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Runtime/platforms/freedesktop/48x48/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Runtime/platforms/freedesktop/32x32/apps/metaforce.png
${CMAKE_SOURCE_DIR}/Runtime/platforms/freedesktop/16x16/apps/metaforce.png
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Runtime/platforms/freedesktop
COMMENT "Generating mainicon_netwm.bin")
bintoc(mainicon_netwm.cpp ${CMAKE_BINARY_DIR}/Runtime/platforms/freedesktop/mainicon_netwm.bin MAINICON_NETWM)
endmacro()

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 140 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More