Remove Editor & specter
|
@ -5,7 +5,7 @@ version.h
|
||||||
*.autosave
|
*.autosave
|
||||||
docs/*
|
docs/*
|
||||||
.idea/
|
.idea/
|
||||||
Editor/platforms/win/metaforce.rc
|
Runtime/platforms/win/metaforce.rc
|
||||||
.vs/
|
.vs/
|
||||||
out/
|
out/
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
|
|
|
@ -395,7 +395,6 @@ add_subdirectory(extern/athena)
|
||||||
add_subdirectory(extern/boo)
|
add_subdirectory(extern/boo)
|
||||||
add_subdirectory(hecl/shaderc)
|
add_subdirectory(hecl/shaderc)
|
||||||
include(hecl/ApplicationTools.cmake)
|
include(hecl/ApplicationTools.cmake)
|
||||||
add_subdirectory(specter/shaders)
|
|
||||||
add_subdirectory(Shaders)
|
add_subdirectory(Shaders)
|
||||||
add_subdirectory(extern/libSquish)
|
add_subdirectory(extern/libSquish)
|
||||||
add_subdirectory(extern/libpng)
|
add_subdirectory(extern/libpng)
|
||||||
|
@ -441,23 +440,14 @@ add_subdirectory(extern/freetype2)
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
target_compile_options(freetype PRIVATE -Wno-implicit-fallthrough)
|
target_compile_options(freetype PRIVATE -Wno-implicit-fallthrough)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(specter)
|
|
||||||
target_include_directories(specter PRIVATE ${CMAKE_SOURCE_DIR})
|
|
||||||
target_link_libraries(specter PRIVATE nod)
|
|
||||||
add_subdirectory(assetnameparser)
|
add_subdirectory(assetnameparser)
|
||||||
add_compile_definitions(URDE_ZIP_INPUT_STREAM=1) # Enable CZipInputStream now that zlib header is known
|
add_compile_definitions(URDE_ZIP_INPUT_STREAM=1) # Enable CZipInputStream now that zlib header is known
|
||||||
add_subdirectory(DataSpec)
|
add_subdirectory(DataSpec)
|
||||||
add_subdirectory(extern/kabufuda)
|
add_subdirectory(extern/kabufuda)
|
||||||
add_subdirectory(Editor)
|
|
||||||
|
|
||||||
add_subdirectory(extern/jbus)
|
add_subdirectory(extern/jbus)
|
||||||
set(JBUS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/jbus/include)
|
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(NESEmulator)
|
||||||
add_subdirectory(Runtime)
|
add_subdirectory(Runtime)
|
||||||
add_subdirectory(mpcksum)
|
add_subdirectory(mpcksum)
|
||||||
|
|
|
@ -75,7 +75,7 @@ add_library(AssetNameMapNull
|
||||||
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
|
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
|
||||||
target_include_directories(RetroDataSpec PUBLIC ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
|
target_include_directories(RetroDataSpec PUBLIC ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
|
||||||
${HECL_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_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)
|
if(COMMAND add_sanitizers)
|
||||||
add_sanitizers(RetroDataSpec)
|
add_sanitizers(RetroDataSpec)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
namespace DataSpec::DNAMP1 {
|
namespace DataSpec::DNAMP1 {
|
||||||
struct Actor : IScriptObject {
|
struct Actor : IScriptObject {
|
||||||
AT_DECL_DNA_YAMLV
|
AT_DECL_DNA_YAMLV
|
||||||
String<-1> name SO_NAME_SPECPROP();
|
String<-1> name;
|
||||||
Value<atVec3f> location SO_LOCATION_SPECPROP();
|
Value<atVec3f> location;
|
||||||
Value<atVec3f> orientation SO_ORIENTATION_SPECPROP();
|
Value<atVec3f> orientation;
|
||||||
Value<atVec3f> scale SO_SCALE_SPECPROP();
|
Value<atVec3f> scale;
|
||||||
Value<atVec3f> collisionExtent SO_COLLISION_EXTENT_SPECPROP();
|
Value<atVec3f> collisionExtent;
|
||||||
Value<atVec3f> collisionOffset SO_COLLISION_OFFSET_SPECPROP();
|
Value<atVec3f> collisionOffset;
|
||||||
Value<float> mass;
|
Value<float> mass;
|
||||||
Value<float> zMomentum;
|
Value<float> zMomentum;
|
||||||
HealthInfo healthInfo;
|
HealthInfo healthInfo;
|
||||||
|
|
|
@ -3,18 +3,9 @@
|
||||||
#include "../DNAMP1.hpp"
|
#include "../DNAMP1.hpp"
|
||||||
#include "../SAVW.hpp"
|
#include "../SAVW.hpp"
|
||||||
#include "zeus/CAABox.hpp"
|
#include "zeus/CAABox.hpp"
|
||||||
#include "specter/genie.hpp"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#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 {
|
namespace DataSpec::DNAMP1 {
|
||||||
|
|
||||||
zeus::CTransform ConvertEditorEulerToTransform4f(const zeus::CVector3f& scale, const zeus::CVector3f& orientation,
|
zeus::CTransform ConvertEditorEulerToTransform4f(const zeus::CVector3f& scale, const zeus::CVector3f& orientation,
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include "../../DNACommon/DNACommon.hpp"
|
#include "../../DNACommon/DNACommon.hpp"
|
||||||
#include "../DNAMP1.hpp"
|
#include "../DNAMP1.hpp"
|
||||||
#include "../SAVW.hpp"
|
#include "../SAVW.hpp"
|
||||||
#include "specter/genie.hpp"
|
|
||||||
|
|
||||||
namespace DataSpec::DNAMP1 {
|
namespace DataSpec::DNAMP1 {
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ enum class EPickupType : atUint32 {
|
||||||
World = 38,
|
World = 38,
|
||||||
Spirit = 39,
|
Spirit = 39,
|
||||||
Newborn = 40
|
Newborn = 40
|
||||||
} SPECTER_ENUM("Pickup Type", "", EPickupType);
|
};
|
||||||
|
|
||||||
enum class ESpecialFunctionType : atUint32 {
|
enum class ESpecialFunctionType : atUint32 {
|
||||||
What,
|
What,
|
||||||
|
@ -85,7 +84,7 @@ enum class ESpecialFunctionType : atUint32 {
|
||||||
Ending,
|
Ending,
|
||||||
FusionRelay,
|
FusionRelay,
|
||||||
WeaponSwitch // PAL Only
|
WeaponSwitch // PAL Only
|
||||||
} SPECTER_ENUM("Special Function", "", ESpecialFunctionType);
|
};
|
||||||
|
|
||||||
struct AnimationParameters : BigDNA {
|
struct AnimationParameters : BigDNA {
|
||||||
AT_DECL_DNA_YAML
|
AT_DECL_DNA_YAML
|
||||||
|
@ -211,9 +210,9 @@ struct GrappleParameters : BigDNA {
|
||||||
struct HealthInfo : BigDNA {
|
struct HealthInfo : BigDNA {
|
||||||
AT_DECL_DNA_YAML
|
AT_DECL_DNA_YAML
|
||||||
Value<atUint32> propertyCount;
|
Value<atUint32> propertyCount;
|
||||||
Value<float> health SPECTER_PROPERTY("Health", "Base health for object");
|
Value<float> health;
|
||||||
Value<float> knockbackResistance SPECTER_PROPERTY("Knockback Resistance", "");
|
Value<float> knockbackResistance;
|
||||||
} SPECTER_PROPERTY("Health Info", "");
|
};
|
||||||
|
|
||||||
struct LightParameters : BigDNA {
|
struct LightParameters : BigDNA {
|
||||||
AT_DECL_DNA_YAML
|
AT_DECL_DNA_YAML
|
||||||
|
|
|
@ -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()
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,3 +0,0 @@
|
||||||
#include "InformationCenter.hpp"
|
|
||||||
|
|
||||||
namespace metaforce {}
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,3 +0,0 @@
|
||||||
#include "ParticleEditor.hpp"
|
|
||||||
|
|
||||||
namespace metaforce {}
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
262
Editor/Space.cpp
|
@ -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
|
|
378
Editor/Space.hpp
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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)
|
|
Before Width: | Height: | Size: 1.7 KiB |
|
@ -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()
|
|
|
@ -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()
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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
|
|
Before Width: | Height: | Size: 54 KiB |
|
@ -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()
|
|
|
@ -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()
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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)
|
|
|
@ -1,3 +0,0 @@
|
||||||
name: "British English"
|
|
||||||
en_GB:
|
|
||||||
color: "Colour"
|
|
|
@ -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"
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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: "最近使用したファイル"
|
|
|
@ -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
|
|
|
@ -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
|
|
247
Editor/main.cpp
|
@ -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
|
|
|
@ -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()
|
|
|
@ -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
|
|
@ -49,8 +49,9 @@ if(APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(RUNTIME_SOURCES_A
|
set(RUNTIME_SOURCES_A
|
||||||
|
MkCastTo.py
|
||||||
|
TCastTo.hpp TCastTo.cpp
|
||||||
RetroTypes.hpp RetroTypes.cpp
|
RetroTypes.hpp RetroTypes.cpp
|
||||||
${CLIENT_SOURCES}
|
|
||||||
${MP1_SOURCES}
|
${MP1_SOURCES}
|
||||||
${AUDIO_SOURCES}
|
${AUDIO_SOURCES}
|
||||||
${AUTOMAPPER_SOURCES}
|
${AUTOMAPPER_SOURCES}
|
||||||
|
@ -114,8 +115,6 @@ set(RUNTIME_SOURCES_B
|
||||||
CGameHintInfo.hpp CGameHintInfo.cpp
|
CGameHintInfo.hpp CGameHintInfo.cpp
|
||||||
rstl.hpp
|
rstl.hpp
|
||||||
GameGlobalObjects.hpp GameGlobalObjects.cpp
|
GameGlobalObjects.hpp GameGlobalObjects.cpp
|
||||||
MkCastTo.py
|
|
||||||
TCastTo.hpp TCastTo.cpp
|
|
||||||
GCNTypes.hpp
|
GCNTypes.hpp
|
||||||
CTextureCache.hpp CTextureCache.cpp
|
CTextureCache.hpp CTextureCache.cpp
|
||||||
CMayaSpline.hpp CMayaSpline.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
|
set(RUNTIME_LIBRARIES ${HECL_APPLICATION_REPS_TARGETS_LIST} RetroDataSpec AssetNameMapNull NESEmulator
|
||||||
libjpeg-turbo jbus kabufuda discord-rpc logvisor OptickCore)
|
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(RuntimeCommon ${RUNTIME_SOURCES_A})
|
||||||
|
target_include_directories(RuntimeCommon PUBLIC ${RUNTIME_INCLUDES})
|
||||||
add_runtime_common_library(RuntimeCommonB ${RUNTIME_SOURCES_B})
|
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(RuntimeCommonB PUBLIC ${RUNTIME_LIBRARIES})
|
||||||
target_link_libraries(RuntimeCommon PUBLIC RuntimeCommonB)
|
target_link_libraries(RuntimeCommon PUBLIC RuntimeCommonB)
|
||||||
else()
|
|
||||||
add_runtime_common_library(RuntimeCommon ${RUNTIME_SOURCES_A} ${RUNTIME_SOURCES_B})
|
if(WIN32)
|
||||||
target_include_directories(RuntimeCommon PUBLIC ${RUNTIME_INCLUDES})
|
configure_file(platforms/win/metaforce.rc.in "${CMAKE_CURRENT_SOURCE_DIR}/platforms/win/metaforce.rc" @ONLY)
|
||||||
target_link_libraries(RuntimeCommon PUBLIC ${RUNTIME_LIBRARIES})
|
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()
|
endif()
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "Runtime/Character/CModelData.hpp"
|
#include "Runtime/Character/CModelData.hpp"
|
||||||
|
|
||||||
#include "Editor/ProjectManager.hpp"
|
|
||||||
|
|
||||||
#include "Runtime/CPlayerState.hpp"
|
#include "Runtime/CPlayerState.hpp"
|
||||||
#include "Runtime/CStateManager.hpp"
|
#include "Runtime/CStateManager.hpp"
|
||||||
#include "Runtime/GameGlobalObjects.hpp"
|
#include "Runtime/GameGlobalObjects.hpp"
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
#include "Runtime/Graphics/CMoviePlayer.hpp"
|
#include "Runtime/Graphics/CMoviePlayer.hpp"
|
||||||
|
|
||||||
#include "Runtime/CDvdRequest.hpp"
|
|
||||||
#include "Runtime/Audio/g721.h"
|
#include "Runtime/Audio/g721.h"
|
||||||
|
#include "Runtime/CDvdRequest.hpp"
|
||||||
#include "Runtime/Graphics/CGraphics.hpp"
|
#include "Runtime/Graphics/CGraphics.hpp"
|
||||||
|
|
||||||
#include <amuse/DSPCodec.hpp>
|
#include <amuse/DSPCodec.hpp>
|
||||||
#include <boo/graphicsdev/GLSLMacros.hpp>
|
|
||||||
#include <hecl/Pipeline.hpp>
|
#include <hecl/Pipeline.hpp>
|
||||||
#include <specter/View.hpp>
|
|
||||||
#include <turbojpeg.h>
|
#include <turbojpeg.h>
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
|
|
||||||
|
zeus::CMatrix4f g_PlatformMatrix;
|
||||||
|
|
||||||
/* used in the original to look up fixed-point dividends on a
|
/* used in the original to look up fixed-point dividends on a
|
||||||
* MIDI-style volume scale (0-127) -> (n/0x8000) */
|
* MIDI-style volume scale (0-127) -> (n/0x8000) */
|
||||||
static const u16 StaticVolumeLookup[] = {
|
static const u16 StaticVolumeLookup[] = {
|
||||||
|
@ -46,7 +46,14 @@ static float SfxVolume = 1.f;
|
||||||
static const char* BlockNames[] = {"SpecterViewBlock"};
|
static const char* BlockNames[] = {"SpecterViewBlock"};
|
||||||
static const char* TexNames[] = {"texY", "texU", "texV"};
|
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{});
|
YUVShaderPipeline = hecl::conv->convert(Shader_CMoviePlayerShader{});
|
||||||
TjHandle = tjInitDecompress();
|
TjHandle = tjInitDecompress();
|
||||||
}
|
}
|
||||||
|
@ -200,7 +207,7 @@ CMoviePlayer::CMoviePlayer(const char* path, float preLoadSeconds, bool loop, bo
|
||||||
/* All set for GPU resources */
|
/* All set for GPU resources */
|
||||||
CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) {
|
CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) {
|
||||||
m_blockBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(m_viewVertBlock), 1);
|
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) */
|
/* Allocate textures here (rather than at decode time) */
|
||||||
x80_textures.reserve(3);
|
x80_textures.reserve(3);
|
||||||
|
|
|
@ -6,12 +6,15 @@
|
||||||
#include "Runtime/CDvdFile.hpp"
|
#include "Runtime/CDvdFile.hpp"
|
||||||
#include "Runtime/RetroTypes.hpp"
|
#include "Runtime/RetroTypes.hpp"
|
||||||
|
|
||||||
|
#include <boo/IWindow.hpp>
|
||||||
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
|
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
|
||||||
#include <specter/View.hpp>
|
#include <zeus/CColor.hpp>
|
||||||
#include <zeus/CVector3f.hpp>
|
#include <zeus/CVector3f.hpp>
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
|
|
||||||
|
extern zeus::CMatrix4f g_PlatformMatrix;
|
||||||
|
|
||||||
class CMoviePlayer : public CDvdFile {
|
class CMoviePlayer : public CDvdFile {
|
||||||
public:
|
public:
|
||||||
enum class EPlayMode { Stopped, Playing };
|
enum class EPlayMode { Stopped, Playing };
|
||||||
|
@ -31,26 +34,26 @@ private:
|
||||||
u32 firstFrameOffset;
|
u32 firstFrameOffset;
|
||||||
u32 lastFrameOffset;
|
u32 lastFrameOffset;
|
||||||
void swapBig();
|
void swapBig();
|
||||||
} x28_thpHead;
|
} x28_thpHead{};
|
||||||
|
|
||||||
struct THPComponents {
|
struct THPComponents {
|
||||||
u32 numComponents;
|
u32 numComponents;
|
||||||
enum class Type : u8 { Video = 0x0, Audio = 0x1, None = 0xff } comps[16];
|
enum class Type : u8 { Video = 0x0, Audio = 0x1, None = 0xff } comps[16];
|
||||||
void swapBig();
|
void swapBig();
|
||||||
} x58_thpComponents;
|
} x58_thpComponents{};
|
||||||
|
|
||||||
struct THPVideoInfo {
|
struct THPVideoInfo {
|
||||||
u32 width;
|
u32 width;
|
||||||
u32 height;
|
u32 height;
|
||||||
void swapBig();
|
void swapBig();
|
||||||
} x6c_videoInfo;
|
} x6c_videoInfo{};
|
||||||
|
|
||||||
struct THPAudioInfo {
|
struct THPAudioInfo {
|
||||||
u32 numChannels;
|
u32 numChannels;
|
||||||
u32 sampleRate;
|
u32 sampleRate;
|
||||||
u32 numSamples;
|
u32 numSamples;
|
||||||
void swapBig();
|
void swapBig();
|
||||||
} x74_audioInfo;
|
} x74_audioInfo{};
|
||||||
|
|
||||||
struct THPFrameHeader {
|
struct THPFrameHeader {
|
||||||
u32 nextSize;
|
u32 nextSize;
|
||||||
|
@ -108,11 +111,30 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<uint8_t[]> m_yuvBuf;
|
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_blockBuf;
|
||||||
boo::ObjToken<boo::IGraphicsBufferD> m_vertBuf;
|
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);
|
static u32 THPAudioDecode(s16* buffer, const u8* audioFrame, bool stereo);
|
||||||
void DecodeFromRead(const void* data);
|
void DecodeFromRead(const void* data);
|
||||||
|
@ -145,7 +167,7 @@ public:
|
||||||
void Update(float dt);
|
void Update(float dt);
|
||||||
std::pair<u32, u32> GetVideoDimensions() const { return {x6c_videoInfo.width, x6c_videoInfo.height}; }
|
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();
|
static void Shutdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ CGuiPane::CGuiPane(const CGuiWidgetParms& parms, const zeus::CVector2f& dim, con
|
||||||
void CGuiPane::ScaleDimensions(const zeus::CVector3f& scale) {
|
void CGuiPane::ScaleDimensions(const zeus::CVector3f& scale) {
|
||||||
InitializeBuffers();
|
InitializeBuffers();
|
||||||
|
|
||||||
for (specter::View::TexShaderVert& v : xc0_verts) {
|
for (TexShaderVert& v : xc0_verts) {
|
||||||
v.m_pos -= xc8_scaleCenter;
|
v.m_pos -= xc8_scaleCenter;
|
||||||
v.m_pos *= scale;
|
v.m_pos *= scale;
|
||||||
v.m_pos += xc8_scaleCenter;
|
v.m_pos += xc8_scaleCenter;
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
|
|
||||||
#include "Runtime/GuiSys/CGuiWidget.hpp"
|
#include "Runtime/GuiSys/CGuiWidget.hpp"
|
||||||
|
|
||||||
#include <specter/View.hpp>
|
|
||||||
|
|
||||||
#include <zeus/CVector2f.hpp>
|
#include <zeus/CVector2f.hpp>
|
||||||
#include <zeus/CVector3f.hpp>
|
#include <zeus/CVector3f.hpp>
|
||||||
|
|
||||||
|
@ -16,8 +14,12 @@ class CGuiPane : public CGuiWidget {
|
||||||
protected:
|
protected:
|
||||||
zeus::CVector2f xb8_dim;
|
zeus::CVector2f xb8_dim;
|
||||||
|
|
||||||
|
struct TexShaderVert {
|
||||||
|
zeus::CVector3f m_pos;
|
||||||
|
zeus::CVector2f m_uv;
|
||||||
|
};
|
||||||
/* Originally a vert-buffer pointer for GX */
|
/* Originally a vert-buffer pointer for GX */
|
||||||
std::vector<specter::View::TexShaderVert> xc0_verts;
|
std::vector<TexShaderVert> xc0_verts;
|
||||||
// u32 x104_ = 4; /* vert count */
|
// u32 x104_ = 4; /* vert count */
|
||||||
|
|
||||||
zeus::CVector3f xc8_scaleCenter;
|
zeus::CVector3f xc8_scaleCenter;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "Editor/ProjectResourceFactoryBase.hpp"
|
|
||||||
#include "Runtime/rstl.hpp"
|
#include "Runtime/rstl.hpp"
|
||||||
#include "Runtime/MP1/CInGameGuiManager.hpp"
|
#include "Runtime/MP1/CInGameGuiManager.hpp"
|
||||||
#include "Runtime/MP1/CPauseScreenBase.hpp"
|
#include "Runtime/MP1/CPauseScreenBase.hpp"
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Editor/ProjectManager.hpp"
|
#include "Runtime/CArchitectureMessage.hpp"
|
||||||
|
#include "Runtime/CGameState.hpp"
|
||||||
|
#include "Runtime/CSimplePool.hpp"
|
||||||
#include "Runtime/GameGlobalObjects.hpp"
|
#include "Runtime/GameGlobalObjects.hpp"
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
#include "DataSpec/DNAMP1/Tweaks/CTweakGuiColors.hpp"
|
#include "DataSpec/DNAMP1/Tweaks/CTweakGuiColors.hpp"
|
||||||
#include "DataSpec/DNAMP1/Tweaks/CTweakPlayerGun.hpp"
|
#include "DataSpec/DNAMP1/Tweaks/CTweakPlayerGun.hpp"
|
||||||
|
|
||||||
#include "Editor/ProjectManager.hpp"
|
#include "Runtime/IMain.hpp"
|
||||||
|
|
||||||
#include "Runtime/GameGlobalObjects.hpp"
|
#include "Runtime/GameGlobalObjects.hpp"
|
||||||
|
#include "Runtime/AutoMapper/CMappableObject.hpp"
|
||||||
#include "Runtime/World/CPlayerCameraBob.hpp"
|
#include "Runtime/World/CPlayerCameraBob.hpp"
|
||||||
|
|
||||||
#include <logvisor/logvisor.hpp>
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
|
@ -269,6 +269,7 @@ CMain::BooSetter::BooSetter(boo::IGraphicsDataFactory* factory, boo::IGraphicsCo
|
||||||
CRandomStaticFilter::Initialize();
|
CRandomStaticFilter::Initialize();
|
||||||
CEnvFxShaders::Initialize();
|
CEnvFxShaders::Initialize();
|
||||||
CNESShader::Initialize();
|
CNESShader::Initialize();
|
||||||
|
CMoviePlayer::Initialize(factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMain::RegisterResourceTweaks() {}
|
void CMain::RegisterResourceTweaks() {}
|
||||||
|
@ -407,7 +408,6 @@ void CMain::ResetGameState() {
|
||||||
void CMain::InitializeSubsystems() {
|
void CMain::InitializeSubsystems() {
|
||||||
CBasics::Initialize();
|
CBasics::Initialize();
|
||||||
CModelShaders::Initialize();
|
CModelShaders::Initialize();
|
||||||
CMoviePlayer::Initialize();
|
|
||||||
CLineRenderer::Initialize();
|
CLineRenderer::Initialize();
|
||||||
CElementGen::Initialize();
|
CElementGen::Initialize();
|
||||||
CAnimData::InitializeCache();
|
CAnimData::InitializeCache();
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "Editor/ProjectResourceFactoryBase.hpp"
|
|
||||||
#include "Runtime/CObjectList.hpp"
|
#include "Runtime/CObjectList.hpp"
|
||||||
#include "Runtime/CToken.hpp"
|
#include "Runtime/CToken.hpp"
|
||||||
#include "Runtime/RetroTypes.hpp"
|
#include "Runtime/RetroTypes.hpp"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "Runtime/World/ScriptLoader.hpp"
|
#include "Runtime/World/ScriptLoader.hpp"
|
||||||
|
|
||||||
#include "Editor/ProjectResourceFactoryMP1.hpp"
|
|
||||||
|
|
||||||
#include "Runtime/Camera/CCinematicCamera.hpp"
|
#include "Runtime/Camera/CCinematicCamera.hpp"
|
||||||
#include "Runtime/Camera/CPathCamera.hpp"
|
#include "Runtime/Camera/CPathCamera.hpp"
|
||||||
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"
|
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"
|
||||||
|
|
Before Width: | Height: | Size: 637 KiB After Width: | Height: | Size: 637 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 203 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
@ -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()
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |