mirror of
https://github.com/AxioDL/PrimeDep.git
synced 2026-07-11 15:18:39 -07:00
Add Decal uncooking and HINT
This commit is contained in:
@@ -49,6 +49,7 @@ add_library(
|
||||
src/PrimeDep/Resources/MetroidPrime/SkinRules.cpp
|
||||
src/PrimeDep/Resources/MetroidPrime/StringTable.cpp
|
||||
src/PrimeDep/Resources/MetroidPrime/Texture.cpp
|
||||
src/PrimeDep/Resources/MetroidPrime/Hints.cpp
|
||||
src/PrimeDep/texture_decode.cpp
|
||||
src/PrimeDep/WorldCommon/MetroidPrime/MappableObject.cpp
|
||||
src/PrimeDep/Particles/ParticleDataFactory.cpp
|
||||
|
||||
@@ -49,6 +49,10 @@ public:
|
||||
|
||||
static constexpr FourCC kInvalidFourCC = {};
|
||||
|
||||
static constexpr FourCC FOURCCByChar(const char a, const char b, const char c, const char d) {
|
||||
const char fcc[4] = {a, b, c, d};
|
||||
return FourCC(fcc);
|
||||
}
|
||||
} // namespace axdl::primedep
|
||||
|
||||
#define FOURCC(fcc) axdl::primedep::FourCC(SBIG(fcc))
|
||||
|
||||
@@ -1,17 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "PrimeDep/IResource.hpp"
|
||||
|
||||
#include "PrimeDep/Particles/ParticleProperty.hpp"
|
||||
|
||||
namespace axdl::primedep::MetroidPrime {
|
||||
class DecalData final : public TypedResource('DPSC', ".dpsm", ".dpsm.dpsc", DESCRIPTION("Decal Particle System")) {
|
||||
|
||||
class DecalData final : public TypedResource('DPSC', ".dpsm", ".dpsm.dpsc", DESCRIPTION("Decal Particle System")),
|
||||
particles::IPropertyContainer {
|
||||
public:
|
||||
template <char Num>
|
||||
struct QuadDescription {
|
||||
QuadDescription(IPropertyContainer* parent)
|
||||
: m_lifetime(FOURCCByChar(Num, 'L', 'F', 'T'), std::format("Quad{}Lifetime", Num), parent)
|
||||
, m_size(FOURCCByChar(Num, 'S', 'Z', 'E'), std::format("Quad{}Size", Num), parent)
|
||||
, m_rotation(FOURCCByChar(Num, 'R', 'O', 'T'), std::format("Quad{}Rotation", Num) + "Rotation", parent)
|
||||
, m_offset(FOURCCByChar(Num, 'O', 'F', 'F'), std::format("Quad{}Offset", Num) + "Offset", parent)
|
||||
, m_color(FOURCCByChar(Num, 'C', 'L', 'R'), std::format("Quad{}Color", Num), parent)
|
||||
, m_texture(FOURCCByChar(Num, 'T', 'E', 'X'), std::format("Quad{}Texture", Num), parent)
|
||||
, m_additiveBlend(false, FOURCCByChar(Num, 'A', 'D', 'D'), std::format("Quad{}AdditiveBlend", Num), parent) {}
|
||||
particles::IntElementProperty m_lifetime;
|
||||
particles::RealElementProperty m_size;
|
||||
particles::RealElementProperty m_rotation;
|
||||
particles::VectorElementProperty m_offset;
|
||||
particles::ColorElementProperty m_color;
|
||||
particles::UVElementProperty m_texture;
|
||||
particles::BoolElementProperty m_additiveBlend;
|
||||
};
|
||||
DecalData();
|
||||
DecalData(const char* ptr, std::size_t size);
|
||||
static std::shared_ptr<IResource> loadCooked(const char* ptr, std::size_t size);
|
||||
|
||||
bool writeUncooked(const std::string_view path) const override;
|
||||
|
||||
static bool canIngest(const nlohmann::ordered_json& metadata) {
|
||||
return metadata["ResourceType"] == ResourceType().toString();
|
||||
}
|
||||
static std::shared_ptr<IResource> ingest(const nlohmann::ordered_json& metadata, std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
QuadDescription<'1'> m_quad1;
|
||||
QuadDescription<'2'> m_quad2;
|
||||
particles::AssetID32BigElementProperty m_model;
|
||||
particles::IntElementProperty m_lifetime;
|
||||
particles::VectorElementProperty m_modelOffset;
|
||||
particles::VectorElementProperty m_modelRotation;
|
||||
particles::VectorElementProperty m_modelScale;
|
||||
particles::ColorElementProperty m_modelColor;
|
||||
particles::BoolElementProperty m_modelAdditiveBlend;
|
||||
particles::BoolElementProperty m_modelRotationGlobal;
|
||||
};
|
||||
} // namespace axdl::primedep::MetroidPrime
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "PrimeDep/IResource.hpp"
|
||||
|
||||
namespace axdl::primedep::MetroidPrime {
|
||||
class Hints final
|
||||
: public TypedResource('HINT', ".hintr", ".hints", DESCRIPTION("Hint definitions for in-game events")) {
|
||||
public:
|
||||
static constexpr uint32_t kMagic = 0x00BADBAD;
|
||||
static constexpr uint32_t kVersion = 1;
|
||||
struct HintLocation {
|
||||
explicit HintLocation(athena::io::IStreamReader& in);
|
||||
explicit HintLocation(const nlohmann::ordered_json& in);
|
||||
|
||||
void PutTo(athena::io::IStreamWriter& out) const;
|
||||
void PutTo(nlohmann::ordered_json& out) const;
|
||||
|
||||
AssetId32Big m_worldId;
|
||||
AssetId32Big m_areaId;
|
||||
uint32_t m_roomId;
|
||||
AssetId32Big m_stringId;
|
||||
};
|
||||
|
||||
struct Hint {
|
||||
explicit Hint(athena::io::IStreamReader& in);
|
||||
explicit Hint(const nlohmann::ordered_json& in);
|
||||
|
||||
void PutTo(athena::io::IStreamWriter& out) const;
|
||||
void PutTo(nlohmann::ordered_json& out) const;
|
||||
|
||||
std::string m_name;
|
||||
float m_immediateTime;
|
||||
float m_normalTime;
|
||||
AssetId32Big m_stringId;
|
||||
uint32_t m_textTime;
|
||||
std::vector<HintLocation> m_locations;
|
||||
};
|
||||
Hints(const char* ptr, std::size_t size);
|
||||
explicit Hints(const nlohmann::ordered_json& in);
|
||||
|
||||
bool writeCooked(std::string_view path) const override;
|
||||
bool writeUncooked(std::string_view path) const override;
|
||||
|
||||
static std::shared_ptr<IResource> loadCooked(const char* ptr, std::size_t size);
|
||||
|
||||
static bool canIngest(const nlohmann::ordered_json& metadata) {
|
||||
return metadata["ResourceType"] == ResourceType().toString();
|
||||
}
|
||||
static std::shared_ptr<IResource> ingest(const nlohmann::ordered_json& metadata, std::string_view path);
|
||||
|
||||
private:
|
||||
std::vector<Hint> m_hints;
|
||||
};
|
||||
} // namespace axdl::primedep::MetroidPrime
|
||||
@@ -2,8 +2,63 @@
|
||||
|
||||
#include "athena/MemoryReader.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace axdl::primedep::MetroidPrime {
|
||||
DecalData::DecalData(const char* ptr, const std::size_t size) { athena::io::MemoryReader mr(ptr, size, true); }
|
||||
DecalData::DecalData()
|
||||
: m_quad1(this)
|
||||
, m_quad2(this)
|
||||
, m_model(FOURCC('CMDL'), FOURCC('DMDL'), "Model", this)
|
||||
, m_lifetime(FOURCC('DLFT'), "Lifetime", this)
|
||||
, m_modelOffset(FOURCC('DMOP'), "ModelOffset", this)
|
||||
, m_modelRotation(FOURCC('DMRT'), "ModelRotation", this)
|
||||
, m_modelScale(FOURCC('DMSC'), "ModelScale", this)
|
||||
, m_modelColor(FOURCC('DMCL'), "ModelColor", this)
|
||||
, m_modelAdditiveBlend(false, FOURCC('DMAB'), "ModelAdditiveBlend", this)
|
||||
, m_modelRotationGlobal(false, FOURCC('DMOO'), "ModelRotationGlobal", this) {}
|
||||
|
||||
DecalData::DecalData(const char* ptr, const std::size_t size) : DecalData() {
|
||||
athena::io::MemoryReader in(ptr, size, true);
|
||||
|
||||
if (particles::ParticleDataFactory::GetClassID(in) != FOURCC('DPSM')) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto classId = particles::ParticleDataFactory::GetClassID(in);
|
||||
|
||||
int loadOrder = 0;
|
||||
while (classId != FOURCC('_END') && !in.hasError()) {
|
||||
if (auto* cls = propertyForClass(classId)) {
|
||||
cls->loadValue(in);
|
||||
cls->setLoadOrder(loadOrder);
|
||||
++loadOrder;
|
||||
} else {
|
||||
std::cout << "Unhandled class " << classId.toString() << std::endl;
|
||||
}
|
||||
classId = particles::ParticleDataFactory::GetClassID(in);
|
||||
}
|
||||
|
||||
sortProperties();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DecalData::writeUncooked(const std::string_view path) const {
|
||||
const auto p = rawPath(path);
|
||||
|
||||
nlohmann::ordered_json data = nlohmann::ordered_json::object();
|
||||
for (const auto& property : m_properties) {
|
||||
if (property->loadOrder() == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
property->PutTo(data);
|
||||
}
|
||||
|
||||
athena::io::FileWriter writer(p.generic_string());
|
||||
const auto js = data.dump(4) + "\n";
|
||||
writer.writeString(js, js.length());
|
||||
return !writer.hasError();
|
||||
}
|
||||
|
||||
std::shared_ptr<IResource> DecalData::loadCooked(const char* ptr, std::size_t size) {
|
||||
return std::make_shared<DecalData>(ptr, size);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "PrimeDep/Resources/MetroidPrime/Hints.hpp"
|
||||
|
||||
#include "PrimeDep/Resources/MetroidPrime/AnimPOIData.hpp"
|
||||
|
||||
#include <athena/FileReader.hpp>
|
||||
#include <athena/MemoryReader.hpp>
|
||||
|
||||
namespace axdl::primedep::MetroidPrime {
|
||||
|
||||
Hints::HintLocation::HintLocation(athena::io::IStreamReader& in)
|
||||
: m_worldId(in, FOURCC('MLVL'))
|
||||
, m_areaId(in, FOURCC('MREA'))
|
||||
, m_roomId(in.readUint32Big())
|
||||
, m_stringId(in, FOURCC('STRG')) {}
|
||||
|
||||
Hints::HintLocation::HintLocation(const nlohmann::ordered_json& in)
|
||||
: m_worldId(in.value("World", nlohmann::ordered_json()), FOURCC('MLVL'))
|
||||
, m_areaId(in.value("Area", nlohmann::ordered_json()), FOURCC('MREA'))
|
||||
, m_roomId(in.value("World", -1))
|
||||
, m_stringId(in.value("String", nlohmann::ordered_json()), FOURCC('STRG')) {}
|
||||
|
||||
void Hints::HintLocation::PutTo(athena::io::IStreamWriter& out) const {
|
||||
m_worldId.PutTo(out);
|
||||
m_areaId.PutTo(out);
|
||||
out.writeUint32Big(m_roomId);
|
||||
m_stringId.PutTo(out);
|
||||
}
|
||||
void Hints::HintLocation::PutTo(nlohmann::ordered_json& out) const {
|
||||
m_worldId.PutTo(out["World"], FOURCC('MLVL'));
|
||||
m_areaId.PutTo(out["Area"], FOURCC('MREA'));
|
||||
out["Room"] = m_roomId;
|
||||
m_stringId.PutTo(out["String"], FOURCC('STRG'));
|
||||
}
|
||||
|
||||
Hints::Hint::Hint(athena::io::IStreamReader& in)
|
||||
: m_name(in.readString())
|
||||
, m_immediateTime(in.readFloatBig())
|
||||
, m_normalTime(in.readFloatBig())
|
||||
, m_stringId(in, FOURCC('STRG'))
|
||||
, m_textTime(in.readInt32Big()) {
|
||||
uint32_t count = in.readUint32Big();
|
||||
while (count--) {
|
||||
m_locations.emplace_back(in);
|
||||
}
|
||||
}
|
||||
|
||||
Hints::Hint::Hint(const nlohmann::ordered_json& in)
|
||||
: m_name(in.value("Name", ""))
|
||||
, m_immediateTime(in.value("ImmediateTime", 0.f))
|
||||
, m_normalTime(in.value("NormalTime", 0.f))
|
||||
, m_stringId(in.value("String", nlohmann::ordered_json()), FOURCC('STRG'))
|
||||
, m_textTime(in.value("TextTime", 0)) {
|
||||
|
||||
if (in.contains("Locations")) {
|
||||
for (const auto& locations = in["Locations"]; const auto& location : locations) {
|
||||
m_locations.emplace_back(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hints::Hint::PutTo(athena::io::IStreamWriter& out) const {
|
||||
out.writeString(m_name);
|
||||
out.writeFloatBig(m_immediateTime);
|
||||
out.writeFloatBig(m_normalTime);
|
||||
m_stringId.PutTo(out);
|
||||
out.writeInt32Big(m_textTime);
|
||||
out.writeUint32Big(m_locations.size());
|
||||
for (const auto& location : m_locations) {
|
||||
location.PutTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
void Hints::Hint::PutTo(nlohmann::ordered_json& out) const {
|
||||
out["Name"] = m_name;
|
||||
out["ImmediateTime"] = m_immediateTime;
|
||||
out["NormalTime"] = m_normalTime;
|
||||
m_stringId.PutTo(out["String"], FOURCC('STRG'));
|
||||
out["TextTime"] = m_textTime;
|
||||
auto& locations = out["Locations"];
|
||||
for (const auto& location : m_locations) {
|
||||
location.PutTo(locations.emplace_back());
|
||||
}
|
||||
}
|
||||
|
||||
Hints::Hints(const char* ptr, std::size_t size) {
|
||||
athena::io::MemoryReader in(ptr, size);
|
||||
const auto magic = in.readUint32Big();
|
||||
if (magic != kMagic) {
|
||||
printf("Unexpected Hint magic, got %08X, expected %08X\n", magic, kMagic);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto version = in.readUint32Big();
|
||||
if (version != kVersion) {
|
||||
printf("Unexpected Hint version, got %i, expected %i\n", version, kVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
auto count = in.readUint32Big();
|
||||
|
||||
while (count--) {
|
||||
m_hints.emplace_back(in);
|
||||
}
|
||||
}
|
||||
|
||||
Hints::Hints(const nlohmann::ordered_json& in) {
|
||||
if (in.contains("Hints")) {
|
||||
const auto hints = in["Hints"];
|
||||
for (const auto& hint : hints) {
|
||||
m_hints.emplace_back(hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Hints::writeCooked(const std::string_view path) const {
|
||||
const auto p = cookedPath(path);
|
||||
athena::io::FileWriter out(p.generic_string());
|
||||
out.writeUint32Big(kMagic);
|
||||
out.writeUint32Big(kVersion);
|
||||
|
||||
out.writeUint32Big(m_hints.size());
|
||||
for (const auto& hint : m_hints) {
|
||||
hint.PutTo(out);
|
||||
}
|
||||
|
||||
return !out.hasError();
|
||||
}
|
||||
|
||||
bool Hints::writeUncooked(const std::string_view path) const {
|
||||
const auto p = rawPath(path);
|
||||
|
||||
nlohmann::ordered_json j;
|
||||
auto& hints = j["Hints"];
|
||||
hints = nlohmann::ordered_json::array();
|
||||
for (const auto& hint : m_hints) {
|
||||
hint.PutTo(hints.emplace_back());
|
||||
}
|
||||
|
||||
athena::io::FileWriter writer(p.generic_string());
|
||||
const auto js = j.dump(4) + "\n";
|
||||
writer.writeString(js, js.length());
|
||||
return !writer.hasError();
|
||||
}
|
||||
|
||||
std::shared_ptr<IResource> Hints::loadCooked(const char* ptr, std::size_t size) {
|
||||
return std::make_shared<Hints>(ptr, size);
|
||||
}
|
||||
|
||||
std::shared_ptr<IResource> Hints::ingest([[maybe_unused]] const nlohmann::ordered_json& metadata,
|
||||
const std::string_view path) {
|
||||
athena::io::FileReader in(path);
|
||||
auto js = nlohmann::ordered_json::parse(in.readString());
|
||||
return std::make_shared<Hints>(js);
|
||||
}
|
||||
} // namespace axdl::primedep::MetroidPrime
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <PrimeDep/Resources/MetroidPrime/DecalData.hpp>
|
||||
#include <PrimeDep/Resources/MetroidPrime/DependencyGroup.hpp>
|
||||
#include <PrimeDep/Resources/MetroidPrime/GuiFrame.hpp>
|
||||
#include <PrimeDep/Resources/MetroidPrime/Hints.hpp>
|
||||
#include <PrimeDep/Resources/MetroidPrime/MapArea.hpp>
|
||||
#include <PrimeDep/Resources/MetroidPrime/MapUniverse.hpp>
|
||||
#include <PrimeDep/Resources/MetroidPrime/MapWorld.hpp>
|
||||
@@ -100,6 +101,7 @@ void addFactories(axdl::primedep::ResourceFactory& factory) {
|
||||
axdl::primedep::RegisterFactory<axdl::primedep::MetroidPrime::SaveWorld>(factory);
|
||||
axdl::primedep::RegisterFactory<axdl::primedep::MetroidPrime::MetroidWorld>(factory);
|
||||
axdl::primedep::RegisterFactory<axdl::primedep::MetroidPrime::DependencyGroup>(factory);
|
||||
axdl::primedep::RegisterFactory<axdl::primedep::MetroidPrime::Hints>(factory);
|
||||
axdl::primedep::RegisterSpecialFactory<axdl::primedep::MetroidPrime::ITweak>(factory);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user