diff --git a/DataSpec/DNACommon/STRG.hpp b/DataSpec/DNACommon/STRG.hpp index b45c430bd..211801e6e 100644 --- a/DataSpec/DNACommon/STRG.hpp +++ b/DataSpec/DNACommon/STRG.hpp @@ -10,7 +10,7 @@ namespace Retro { -struct ISTRG +struct ISTRG : BigYAML { virtual ~ISTRG() {} diff --git a/DataSpec/DNAMP1/ANCS.hpp b/DataSpec/DNAMP1/ANCS.hpp index 2ca8ab5bf..c535a707b 100644 --- a/DataSpec/DNAMP1/ANCS.hpp +++ b/DataSpec/DNAMP1/ANCS.hpp @@ -148,10 +148,6 @@ struct ANCS : BigYAML struct IMetaAnim : BigYAML { Delete expl; - void read(Athena::io::IStreamReader&) {} - void write(Athena::io::IStreamWriter&) const {} - void fromYAML(Athena::io::YAMLDocReader&) {} - void toYAML(Athena::io::YAMLDocWriter&) const {} enum Type { MAPrimitive = 0, @@ -230,10 +226,6 @@ struct ANCS : BigYAML struct IMetaTrans : BigYAML { Delete expl; - void read(Athena::io::IStreamReader&) {} - void write(Athena::io::IStreamWriter&) const {} - void fromYAML(Athena::io::YAMLDocReader&) {} - void toYAML(Athena::io::YAMLDocWriter&) const {} enum Type { MTMetaAnim = 0, diff --git a/DataSpec/DNAMP1/STRG.cpp b/DataSpec/DNAMP1/STRG.cpp index 13d7c1fbe..43c6efed7 100644 --- a/DataSpec/DNAMP1/STRG.cpp +++ b/DataSpec/DNAMP1/STRG.cpp @@ -159,13 +159,13 @@ void STRG::fromYAML(Athena::io::YAMLDocReader& reader) langMap.clear(); langMap.reserve(langs.size()); - for (std::pair>& item : langs) + for (auto& item : langs) langMap.emplace(item.first, &item.second); } void STRG::toYAML(Athena::io::YAMLDocWriter& writer) const { - for (const std::pair>& lang : langs) + for (const auto& lang : langs) { writer.enterSubVector(lang.first.toString().c_str()); for (const std::wstring& str : lang.second) diff --git a/DataSpec/DNAMP1/STRG.hpp b/DataSpec/DNAMP1/STRG.hpp index 1e2589d1a..709e1d395 100644 --- a/DataSpec/DNAMP1/STRG.hpp +++ b/DataSpec/DNAMP1/STRG.hpp @@ -11,10 +11,10 @@ namespace Retro namespace DNAMP1 { -struct STRG : ISTRG, BigYAML +struct STRG : ISTRG { - DECL_EXPLICIT_DNA - DECL_EXPLICIT_YAML + DECL_YAML + Delete expl; void _read(Athena::io::IStreamReader& reader); std::vector>> langs; std::unordered_map*> langMap; @@ -58,7 +58,7 @@ struct STRG : ISTRG, BigYAML return HECL::SystemString(); } - static bool Extract(const SpecBase& dataspec, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) + static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) { STRG strg; strg.read(rs); diff --git a/DataSpec/DNAMP2/DNAMP2.cpp b/DataSpec/DNAMP2/DNAMP2.cpp index ac67034a1..1220d8972 100644 --- a/DataSpec/DNAMP2/DNAMP2.cpp +++ b/DataSpec/DNAMP2/DNAMP2.cpp @@ -183,7 +183,7 @@ ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& ent switch (entry.type.toUint32()) { case SBIG('STRG'): - return {STRG::Extract, nullptr, ".as"}; + return {STRG::Extract, nullptr, ".yaml"}; case SBIG('TXTR'): return {TXTR::Extract, nullptr, ".png"}; } diff --git a/DataSpec/DNAMP2/STRG.cpp b/DataSpec/DNAMP2/STRG.cpp index 7b35c30e1..063c7348d 100644 --- a/DataSpec/DNAMP2/STRG.cpp +++ b/DataSpec/DNAMP2/STRG.cpp @@ -139,5 +139,87 @@ void STRG::write(Athena::io::IStreamWriter& writer) const } } +void STRG::fromYAML(Athena::io::YAMLDocReader& reader) +{ + std::wstring_convert> wconv; + const Athena::io::YAMLNode* root = reader.getRootNode(); + + /* Validate Pass */ + if (root->m_type == YAML_MAPPING_NODE) + { + for (const auto& lang : root->m_mapChildren) + { + if (!lang.first.compare("names")) + continue; + if (lang.first.size() != 4) + { + Log.report(LogVisor::Warning, "STRG language string '%s' must be exactly 4 characters; skipping", lang.first.c_str()); + return; + } + if (lang.second->m_type != YAML_SEQUENCE_NODE) + { + Log.report(LogVisor::Warning, "STRG language string '%s' must contain a sequence; skipping", lang.first.c_str()); + return; + } + for (const auto& str : lang.second->m_seqChildren) + { + if (str->m_type != YAML_SCALAR_NODE) + { + Log.report(LogVisor::Warning, "STRG language '%s' must contain all scalars; skipping", lang.first.c_str()); + return; + } + } + } + } + else + { + Log.report(LogVisor::Warning, "STRG must have a mapping root node; skipping"); + return; + } + + /* Read Pass */ + langs.clear(); + for (const auto& lang : root->m_mapChildren) + { + std::vector strs; + for (const auto& str : lang.second->m_seqChildren) + strs.emplace_back(wconv.from_bytes(str->m_scalarString)); + langs.emplace_back(FourCC(lang.first.c_str()), strs); + } + + names.clear(); + const Athena::io::YAMLNode* namesNode = root->findMapChild("names"); + if (namesNode) + for (const auto& item : namesNode->m_mapChildren) + names[item.first] = Athena::io::NodeToVal(item.second.get()); + + langMap.clear(); + langMap.reserve(langs.size()); + for (std::pair>& item : langs) + langMap.emplace(item.first, &item.second); +} + +void STRG::toYAML(Athena::io::YAMLDocWriter& writer) const +{ + for (const auto& lang : langs) + { + writer.enterSubVector(lang.first.toString().c_str()); + for (const std::wstring& str : lang.second) + writer.writeWString(nullptr, str); + writer.leaveSubVector(); + } + if (names.size()) + { + writer.enterSubRecord("names"); + for (const auto& name : names) + { + writer.enterSubRecord(name.first.c_str()); + writer.writeInt32(nullptr, name.second); + writer.leaveSubRecord(); + } + writer.leaveSubRecord(); + } +} + } } diff --git a/DataSpec/DNAMP2/STRG.hpp b/DataSpec/DNAMP2/STRG.hpp index 9575c4afb..3e9b972cb 100644 --- a/DataSpec/DNAMP2/STRG.hpp +++ b/DataSpec/DNAMP2/STRG.hpp @@ -10,9 +10,10 @@ namespace Retro namespace DNAMP2 { -struct STRG : ISTRG, BigDNA +struct STRG : ISTRG { - DECL_EXPLICIT_DNA + DECL_YAML + Delete expl; void _read(Athena::io::IStreamReader& reader); std::vector>> langs; std::unordered_map*> langMap; @@ -63,13 +64,24 @@ struct STRG : ISTRG, BigDNA return HECL::SystemString(); } - static bool Extract(const SpecBase& dataspec, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) + static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) { + STRG strg; + strg.read(rs); + FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb")); + strg.toYAMLFile(fp); + fclose(fp); return true; } static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath) { + STRG strg; + FILE* fp = HECL::Fopen(inPath.getAbsolutePath().c_str(), _S("rb")); + strg.fromYAMLFile(fp); + fclose(fp); + Athena::io::FileWriter ws(outPath.getAbsolutePath()); + strg.write(ws); return true; } }; diff --git a/DataSpec/DNAMP3/DNAMP3.cpp b/DataSpec/DNAMP3/DNAMP3.cpp index dd3ea5f38..5f41d5b19 100644 --- a/DataSpec/DNAMP3/DNAMP3.cpp +++ b/DataSpec/DNAMP3/DNAMP3.cpp @@ -57,7 +57,7 @@ ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) switch (entry.type.toUint32()) { case SBIG('STRG'): - return {STRG::Extract, nullptr, ".as"}; + return {STRG::Extract, nullptr, ".yaml"}; case SBIG('TXTR'): return {TXTR::Extract, nullptr, ".png"}; } diff --git a/DataSpec/DNAMP3/STRG.cpp b/DataSpec/DNAMP3/STRG.cpp index b5d92d2e6..7f07dc63c 100644 --- a/DataSpec/DNAMP3/STRG.cpp +++ b/DataSpec/DNAMP3/STRG.cpp @@ -86,6 +86,72 @@ void STRG::read(Athena::io::IStreamReader& reader) _read(reader); } +void STRG::fromYAML(Athena::io::YAMLDocReader& reader) +{ + const Athena::io::YAMLNode* root = reader.getRootNode(); + + /* Validate Pass */ + if (root->m_type == YAML_MAPPING_NODE) + { + for (const auto& lang : root->m_mapChildren) + { + if (!lang.first.compare("names")) + continue; + if (lang.first.size() != 4) + { + Log.report(LogVisor::Warning, "STRG language string '%s' must be exactly 4 characters; skipping", lang.first.c_str()); + return; + } + if (lang.second->m_type != YAML_SEQUENCE_NODE) + { + Log.report(LogVisor::Warning, + "STRG language string '%s' must contain a sequence; skipping", lang.first.c_str()); + return; + } + for (const auto& str : lang.second->m_seqChildren) + { + if (str->m_type != YAML_SCALAR_NODE) + { + Log.report(LogVisor::Warning, "STRG language '%s' must contain all scalars; skipping", lang.first.c_str()); + return; + } + } + } + } + else + { + Log.report(LogVisor::Warning, "STRG must have a mapping root node; skipping"); + return; + } + + const Athena::io::YAMLNode* nameYAML = root->findMapChild("names"); + names.clear(); + if (nameYAML && nameYAML->m_type == YAML_MAPPING_NODE) + for (const auto& item : nameYAML->m_mapChildren) + if (item.second->m_type == YAML_SCALAR_NODE) + names[item.first] = Athena::io::NodeToVal(item.second.get()); + + langs.clear(); + langs.reserve(root->m_mapChildren.size()); + for (const auto& item : root->m_mapChildren) + { + if (!item.first.compare("names") || item.first.size() != 4 || + item.second->m_type != YAML_SEQUENCE_NODE) + continue; + + std::vector strs; + for (const auto& node : item.second->m_seqChildren) + if (node->m_type == YAML_SCALAR_NODE) + strs.emplace_back(node->m_scalarString); + langs.emplace_back(std::make_pair(FourCC(item.first.c_str()), std::move(strs))); + } + + langMap.clear(); + langMap.reserve(langs.size()); + for (std::pair>& item : langs) + langMap.emplace(item.first, &item.second); +} + void STRG::write(Athena::io::IStreamWriter& writer) const { writer.setEndian(Athena::BigEndian); @@ -147,5 +213,28 @@ void STRG::write(Athena::io::IStreamWriter& writer) const } } +void STRG::toYAML(Athena::io::YAMLDocWriter& writer) const +{ + for (const auto& item : langs) + { + writer.enterSubVector(item.first.toString().c_str()); + for (const std::string& str : item.second) + writer.writeString(nullptr, str); + writer.leaveSubVector(); + } + + if (names.size()) + { + writer.enterSubRecord("names"); + for (const auto& item : names) + { + writer.enterSubRecord(item.first.c_str()); + writer.writeInt32(nullptr, item.second); + writer.leaveSubRecord(); + } + writer.leaveSubRecord(); + } +} + } } diff --git a/DataSpec/DNAMP3/STRG.hpp b/DataSpec/DNAMP3/STRG.hpp index d44d71c71..9340658e9 100644 --- a/DataSpec/DNAMP3/STRG.hpp +++ b/DataSpec/DNAMP3/STRG.hpp @@ -10,9 +10,10 @@ namespace Retro namespace DNAMP3 { -struct STRG : ISTRG, BigDNA +struct STRG : ISTRG { - DECL_EXPLICIT_DNA + DECL_YAML + Delete expl; void _read(Athena::io::IStreamReader& reader); std::vector>> langs; std::unordered_map*> langMap; @@ -63,13 +64,23 @@ struct STRG : ISTRG, BigDNA return HECL::SystemString(); } - static bool Extract(const SpecBase& dataspec, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) + static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) { + std::unique_ptr strg = LoadSTRG(rs); + FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb")); + strg->toYAMLFile(fp); + fclose(fp); return true; } static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath) { + STRG strg; + FILE* fp = HECL::Fopen(inPath.getAbsolutePath().c_str(), _S("rb")); + strg.fromYAMLFile(fp); + fclose(fp); + Athena::io::FileWriter ws(outPath.getAbsolutePath()); + strg.write(ws); return true; } };