Remove Editor & specter

This commit is contained in:
Luke Street 2021-05-24 01:06:51 -04:00
parent 332ebee36c
commit 94f10bb002
134 changed files with 713 additions and 5088 deletions

4
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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