From 7876d4c209a1f5ce85eede73f8e34d4b9f6ca2bc Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 12 Jul 2015 20:29:12 -1000 Subject: [PATCH] initial MLVL and STRG trilogy integration --- DataSpec/DNACommon/CMakeLists.txt | 2 + DataSpec/DNACommon/DNACommon.cpp | 49 +++++++ DataSpec/DNACommon/DNACommon.hpp | 55 +++++++ DataSpec/DNACommon/STRG.cpp | 42 ++++++ DataSpec/DNACommon/STRG.hpp | 22 +++ DataSpec/DNAMP1/MLVL.hpp | 7 +- DataSpec/DNAMP1/PAK.cpp | 7 +- DataSpec/DNAMP1/PAK.hpp | 5 +- DataSpec/DNAMP1/STRG.cpp | 104 +++++++------ DataSpec/DNAMP1/STRG.hpp | 58 ++++++-- DataSpec/DNAMP2/CMakeLists.txt | 6 +- DataSpec/DNAMP2/MLVL.hpp | 12 +- DataSpec/DNAMP2/STRG.cpp | 138 ++++++++++++++++++ DataSpec/DNAMP2/STRG.hpp | 69 +++++++++ DataSpec/DNAMP3/CMakeLists.txt | 7 +- DataSpec/DNAMP3/MLVL.hpp | 82 +++++++++++ DataSpec/DNAMP3/PAK.cpp | 15 +- DataSpec/DNAMP3/PAK.hpp | 9 +- DataSpec/DNAMP3/STRG.cpp | 119 +++++++++++++++ DataSpec/DNAMP3/STRG.hpp | 69 +++++++++ DataSpec/SpecBase.cpp | 14 +- DataSpec/SpecBase.hpp | 8 +- DataSpec/SpecMP1.cpp | 89 ++++++++--- DataSpec/SpecMP2.cpp | 235 +++++++++++++++++++++++++++++- DataSpec/SpecMP3.cpp | 234 ++++++++++++++++++++++++++++- NODLib | 2 +- 26 files changed, 1333 insertions(+), 126 deletions(-) create mode 100644 DataSpec/DNACommon/STRG.cpp create mode 100644 DataSpec/DNACommon/STRG.hpp create mode 100644 DataSpec/DNAMP2/STRG.cpp create mode 100644 DataSpec/DNAMP2/STRG.hpp create mode 100644 DataSpec/DNAMP3/MLVL.hpp create mode 100644 DataSpec/DNAMP3/STRG.cpp create mode 100644 DataSpec/DNAMP3/STRG.hpp diff --git a/DataSpec/DNACommon/CMakeLists.txt b/DataSpec/DNACommon/CMakeLists.txt index e9ac42213..bc0a36683 100644 --- a/DataSpec/DNACommon/CMakeLists.txt +++ b/DataSpec/DNACommon/CMakeLists.txt @@ -1,3 +1,5 @@ add_library(DNACommon + STRG.hpp + STRG.cpp DNACommon.hpp DNACommon.cpp) diff --git a/DataSpec/DNACommon/DNACommon.cpp b/DataSpec/DNACommon/DNACommon.cpp index e69de29bb..1e3313f5e 100644 --- a/DataSpec/DNACommon/DNACommon.cpp +++ b/DataSpec/DNACommon/DNACommon.cpp @@ -0,0 +1,49 @@ +#include "DNACommon.hpp" + +namespace Retro +{ + +const HECL::FourCC ENGL("ENGL"); +const HECL::FourCC FREN("FREN"); +const HECL::FourCC GERM("GERM"); +const HECL::FourCC SPAN("SPAN"); +const HECL::FourCC ITAL("ITAL"); +const HECL::FourCC JAPN("JAPN"); + +const HECL::FourCC AFSM("AFSM"); +const HECL::FourCC AGSC("AGSC"); +const HECL::FourCC ANCS("ANCS"); +const HECL::FourCC ANIM("ANIM"); +const HECL::FourCC ATBL("ATBL"); +const HECL::FourCC CINF("CINF"); +const HECL::FourCC CMDL("CMDL"); +const HECL::FourCC CRSC("CRSC"); +const HECL::FourCC CSKR("CSKR"); +const HECL::FourCC CSMP("CSMP"); +const HECL::FourCC CSNG("CSNG"); +const HECL::FourCC CTWK("CTWK"); +const HECL::FourCC DGRP("DGRP"); +const HECL::FourCC DPSC("DPSC"); +const HECL::FourCC DUMB("DUMB"); +const HECL::FourCC ELSC("ELSC"); +const HECL::FourCC EVNT("EVNT"); +const HECL::FourCC FONT("FONT"); +const HECL::FourCC FRME("FRME"); +const HECL::FourCC HINT("HINT"); +const HECL::FourCC MAPA("MAPA"); +const HECL::FourCC MAPU("MAPU"); +const HECL::FourCC MAPW("MAPW"); +const HECL::FourCC MLVL("MLVL"); +const HECL::FourCC MREA("MREA"); +const HECL::FourCC PART("PART"); +const HECL::FourCC PATH("PATH"); +const HECL::FourCC RFRM("RFRM"); +const HECL::FourCC ROOM("ROOM"); +const HECL::FourCC SAVW("SAVW"); +const HECL::FourCC SCAN("SCAN"); +const HECL::FourCC STRG("STRG"); +const HECL::FourCC SWHC("SWHC"); +const HECL::FourCC TXTR("TXTR"); +const HECL::FourCC WPSC("WPSC"); + +} diff --git a/DataSpec/DNACommon/DNACommon.hpp b/DataSpec/DNACommon/DNACommon.hpp index e3c493291..4c8990bfc 100644 --- a/DataSpec/DNACommon/DNACommon.hpp +++ b/DataSpec/DNACommon/DNACommon.hpp @@ -10,10 +10,13 @@ namespace Retro /* This comes up a great deal */ typedef Athena::io::DNA BigDNA; +/* FourCC with DNA read/write */ class FourCC final : public BigDNA, public HECL::FourCC { public: FourCC() : HECL::FourCC() {} + FourCC(const HECL::FourCC& other) + : HECL::FourCC() {num = other.toUint32();} FourCC(const char* name) : HECL::FourCC(name) {} @@ -135,11 +138,63 @@ struct CaseInsensitiveCompare } }; +/* Language-identifiers */ +extern const HECL::FourCC ENGL; +extern const HECL::FourCC FREN; +extern const HECL::FourCC GERM; +extern const HECL::FourCC SPAN; +extern const HECL::FourCC ITAL; +extern const HECL::FourCC JAPN; + +/* Resource types */ +extern const HECL::FourCC AFSM; +extern const HECL::FourCC AGSC; +extern const HECL::FourCC ANCS; +extern const HECL::FourCC ANIM; +extern const HECL::FourCC ATBL; +extern const HECL::FourCC CINF; +extern const HECL::FourCC CMDL; +extern const HECL::FourCC CRSC; +extern const HECL::FourCC CSKR; +extern const HECL::FourCC CSMP; +extern const HECL::FourCC CSNG; +extern const HECL::FourCC CTWK; +extern const HECL::FourCC DGRP; +extern const HECL::FourCC DPSC; +extern const HECL::FourCC DUMB; +extern const HECL::FourCC ELSC; +extern const HECL::FourCC EVNT; +extern const HECL::FourCC FONT; +extern const HECL::FourCC FRME; +extern const HECL::FourCC HINT; +extern const HECL::FourCC MAPA; +extern const HECL::FourCC MAPU; +extern const HECL::FourCC MAPW; +extern const HECL::FourCC MLVL; +extern const HECL::FourCC MREA; +extern const HECL::FourCC PART; +extern const HECL::FourCC PATH; +extern const HECL::FourCC RFRM; +extern const HECL::FourCC ROOM; +extern const HECL::FourCC SAVW; +extern const HECL::FourCC SCAN; +extern const HECL::FourCC STRG; +extern const HECL::FourCC SWHC; +extern const HECL::FourCC TXTR; +extern const HECL::FourCC WPSC; + } /* Hash template-specializations for UniqueID types */ namespace std { +template<> +struct hash +{ + inline size_t operator()(const Retro::FourCC& fcc) const + {return fcc.toUint32();} +}; + template<> struct hash { diff --git a/DataSpec/DNACommon/STRG.cpp b/DataSpec/DNACommon/STRG.cpp new file mode 100644 index 000000000..ed175ef66 --- /dev/null +++ b/DataSpec/DNACommon/STRG.cpp @@ -0,0 +1,42 @@ +#include "STRG.hpp" +#include "../DNAMP1/STRG.hpp" +#include "../DNAMP2/STRG.hpp" +#include "../DNAMP3/STRG.hpp" + +namespace Retro +{ +std::unique_ptr LoadSTRG(Athena::io::IStreamReader& reader) +{ + reader.setEndian(Athena::BigEndian); + uint32_t magic = reader.readUint32(); + if (magic != 0x87654321) + { + LogModule.report(LogVisor::Error, "invalid STRG magic"); + return std::unique_ptr(); + } + + uint32_t version = reader.readUint32(); + switch (version) + { + case 0: + { + DNAMP1::STRG* newStrg = new struct DNAMP1::STRG; + newStrg->_read(reader); + return std::unique_ptr(newStrg); + } + case 1: + { + DNAMP2::STRG* newStrg = new struct DNAMP2::STRG; + newStrg->_read(reader); + return std::unique_ptr(newStrg); + } + case 3: + { + DNAMP3::STRG* newStrg = new struct DNAMP3::STRG; + newStrg->_read(reader); + return std::unique_ptr(newStrg); + } + } + return std::unique_ptr(); +} +} diff --git a/DataSpec/DNACommon/STRG.hpp b/DataSpec/DNACommon/STRG.hpp new file mode 100644 index 000000000..9f8490e96 --- /dev/null +++ b/DataSpec/DNACommon/STRG.hpp @@ -0,0 +1,22 @@ +#ifndef __COMMON_STRG_HPP__ +#define __COMMON_STRG_HPP__ + +#include +#include +#include "DNACommon.hpp" +#include "../Logging.hpp" + +namespace Retro +{ +struct ISTRG +{ + virtual size_t count() const=0; + virtual std::string getUTF8(const FourCC& lang, size_t idx) const=0; + virtual std::wstring getUTF16(const FourCC& lang, size_t idx) const=0; + virtual HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const=0; + virtual int32_t lookupIdx(const std::string& name) const=0; +}; +std::unique_ptr LoadSTRG(Athena::io::IStreamReader& reader); +} + +#endif // __COMMON_STRG_HPP__ diff --git a/DataSpec/DNAMP1/MLVL.hpp b/DataSpec/DNAMP1/MLVL.hpp index 457c540a0..1e16eb673 100644 --- a/DataSpec/DNAMP1/MLVL.hpp +++ b/DataSpec/DNAMP1/MLVL.hpp @@ -1,3 +1,6 @@ +#ifndef __DNAMP1_MLVL_HPP__ +#define __DNAMP1_MLVL_HPP__ + #include "../DNACommon/DNACommon.hpp" namespace Retro @@ -8,7 +11,7 @@ namespace DNAMP1 struct MLVL : BigDNA { DECL_DNA - FourCC magic; + Value magic; Value version; UniqueID32 worldNameId; UniqueID32 saveWorldId; @@ -104,3 +107,5 @@ struct MLVL : BigDNA } } + +#endif // __DNAMP1_MLVL_HPP__ diff --git a/DataSpec/DNAMP1/PAK.cpp b/DataSpec/DNAMP1/PAK.cpp index 686a08e8a..cf910dea2 100644 --- a/DataSpec/DNAMP1/PAK.cpp +++ b/DataSpec/DNAMP1/PAK.cpp @@ -31,14 +31,9 @@ void PAK::read(Athena::io::IStreamReader& reader) { m_entries.emplace_back(); m_entries.back().read(reader); - m_idMap[m_entries.back().id] = &m_entries.back(); } - - atUint32 realCount = 0; for (Entry& entry : m_entries) - { - ++realCount; - } + m_idMap[entry.id] = &entry; m_nameMap.clear(); m_nameMap.reserve(nameCount); diff --git a/DataSpec/DNAMP1/PAK.hpp b/DataSpec/DNAMP1/PAK.hpp index 0fd223905..58e1ec82e 100644 --- a/DataSpec/DNAMP1/PAK.hpp +++ b/DataSpec/DNAMP1/PAK.hpp @@ -11,12 +11,11 @@ namespace Retro namespace DNAMP1 { -class PAK : public BigDNA +struct PAK : BigDNA { -public: DECL_EXPLICIT_DNA - struct NameEntry : public BigDNA + struct NameEntry : BigDNA { DECL_DNA FourCC type; diff --git a/DataSpec/DNAMP1/STRG.cpp b/DataSpec/DNAMP1/STRG.cpp index 9f9baa7b6..aea6e9d38 100644 --- a/DataSpec/DNAMP1/STRG.cpp +++ b/DataSpec/DNAMP1/STRG.cpp @@ -6,84 +6,100 @@ namespace Retro namespace DNAMP1 { -const HECL::FourCC ENGLfcc("ENGL"); -const HECL::FourCC FRENfcc("FREN"); -const HECL::FourCC GERMfcc("GERM"); -const HECL::FourCC SPANfcc("SPAN"); -const HECL::FourCC ITALfcc("ITAL"); -const HECL::FourCC JAPNfcc("JAPN"); +void STRG::_read(Athena::io::IStreamReader& reader) +{ + atUint32 langCount = reader.readUint32(); + atUint32 strCount = reader.readUint32(); + + std::vector readLangs; + readLangs.reserve(langCount); + for (atUint32 l=0 ; l strs; + reader.seek(strCount * 4 + 4); + for (atUint32 s=0 ; s>& lang : langs) { - lang.lang.write(writer); + lang.first.write(writer); writer.writeUint32(offset); offset += strCount * 4 + 4; - for (uint32_t s=0 ; s>& lang : langs) { + atUint32 langStrCount = lang.second.size(); + atUint32 tableSz = strCount * 4; + for (atUint32 s=0 ; s #include "../DNACommon/DNACommon.hpp" +#include "../DNACommon/STRG.hpp" namespace Retro { namespace DNAMP1 { -extern const HECL::FourCC ENGLfcc; -extern const HECL::FourCC FRENfcc; -extern const HECL::FourCC GERMfcc; -extern const HECL::FourCC SPANfcc; -extern const HECL::FourCC ITALfcc; -extern const HECL::FourCC JAPNfcc; - -struct STRG : BigDNA +struct STRG : ISTRG, BigDNA { DECL_EXPLICIT_DNA - atUint32 version; - atUint32 langCount; - atUint32 strCount; + void _read(Athena::io::IStreamReader& reader); + std::unordered_map> langs; - struct Language + inline int32_t lookupIdx(const std::string& name) const {return -1;} + + inline size_t count() const { - FourCC lang; - std::vector strings; - }; - std::vector langs; + size_t retval = 0; + for (auto item : langs) + { + size_t sz = item.second.size(); + if (sz > retval) + retval = sz; + } + return retval; + } + inline std::string getUTF8(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) + return HECL::WideToUTF8(search->second.at(idx)); + return std::string(); + } + inline std::wstring getUTF16(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) + return search->second.at(idx); + return std::wstring(); + } + inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) +#if HECL_UCS2 + return search->second.at(idx); +#else + return HECL::WideToUTF8(search->second.at(idx)); +#endif + return std::string(); + } }; } diff --git a/DataSpec/DNAMP2/CMakeLists.txt b/DataSpec/DNAMP2/CMakeLists.txt index 8b7874c19..90f9c7609 100644 --- a/DataSpec/DNAMP2/CMakeLists.txt +++ b/DataSpec/DNAMP2/CMakeLists.txt @@ -1,3 +1,5 @@ make_dnalist(liblist - MLVL) -add_library(DNAMP2 ${liblist}) + MLVL + STRG) +add_library(DNAMP2 ${liblist} + STRG.cpp) diff --git a/DataSpec/DNAMP2/MLVL.hpp b/DataSpec/DNAMP2/MLVL.hpp index 6fbabf086..9ef93e052 100644 --- a/DataSpec/DNAMP2/MLVL.hpp +++ b/DataSpec/DNAMP2/MLVL.hpp @@ -1,3 +1,6 @@ +#ifndef __DNAMP2_MLVL_HPP__ +#define __DNAMP2_MLVL_HPP__ + #include "../DNACommon/DNACommon.hpp" namespace Retro @@ -8,7 +11,7 @@ namespace DNAMP2 struct MLVL : BigDNA { DECL_DNA - FourCC magic; + Value magic; Value version; UniqueID32 worldNameId; UniqueID32 darkWorldNameId; @@ -35,7 +38,7 @@ struct MLVL : BigDNA { DECL_DNA UniqueID32 id; - HECL::FourCC type; + FourCC type; }; Vector deps; @@ -53,7 +56,7 @@ struct MLVL : BigDNA Value areaIdx; Value dockIdx; }; - HECL::FourCC type; + FourCC type; Vector endpoints; Value planeVertCount; @@ -66,7 +69,6 @@ struct MLVL : BigDNA Value relOffsetCount; Vector relOffsets; - Value unk1; String<-1> internalAreaName; }; @@ -92,3 +94,5 @@ struct MLVL : BigDNA } } + +#endif // __DNAMP2_MLVL_HPP__ diff --git a/DataSpec/DNAMP2/STRG.cpp b/DataSpec/DNAMP2/STRG.cpp new file mode 100644 index 000000000..2e14f5ecb --- /dev/null +++ b/DataSpec/DNAMP2/STRG.cpp @@ -0,0 +1,138 @@ +#include "STRG.hpp" +#include "../Logging.hpp" + +namespace Retro +{ +namespace DNAMP2 +{ + +void STRG::_read(Athena::io::IStreamReader& reader) +{ + atUint32 langCount = reader.readUint32(); + atUint32 strCount = reader.readUint32(); + + std::vector readLangs; + readLangs.reserve(langCount); + for (atUint32 l=0 ; l nameTableBuf(new uint8_t[nameTableSz]); + reader.readUBytesToBuf(nameTableBuf.get(), nameTableSz); + struct NameIdxEntry + { + atUint32 nameOff; + atUint32 strIdx; + }* nameIndex = (NameIdxEntry*)nameTableBuf.get(); + for (atUint32 n=0 ; n strs; + reader.seek(strCount * 4); + for (atUint32 s=0 ; s>& lang : langs) + { + lang.first.write(writer); + writer.writeUint32(offset); + offset += strCount * 4 + 4; + atUint32 langStrCount = lang.second.size(); + atUint32 tableSz = strCount * 4; + for (atUint32 s=0 ; s& name : names) + nameTableSz += name.first.size() + 1; + writer.writeUint32(names.size()); + writer.writeUint32(nameTableSz); + offset = names.size() * 8; + for (const std::pair& name : names) + { + writer.writeUint32(offset); + writer.writeInt32(name.second); + offset += name.first.size() + 1; + } + for (const std::pair& name : names) + writer.writeString(name.first); + + for (const std::pair>& lang : langs) + { + offset = strCount * 4; + atUint32 langStrCount = lang.second.size(); + for (atUint32 s=0 ; s +#include "../DNACommon/DNACommon.hpp" +#include "../DNACommon/STRG.hpp" + +namespace Retro +{ +namespace DNAMP2 +{ + +struct STRG : ISTRG, BigDNA +{ + DECL_EXPLICIT_DNA + void _read(Athena::io::IStreamReader& reader); + std::unordered_map> langs; + std::map names; + + inline int32_t lookupIdx(const std::string& name) const + { + auto search = names.find(name); + if (search == names.end()) + return -1; + return search->second; + } + + inline size_t count() const + { + size_t retval = 0; + for (auto item : langs) + { + size_t sz = item.second.size(); + if (sz > retval) + retval = sz; + } + return retval; + } + inline std::string getUTF8(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) + return HECL::WideToUTF8(search->second.at(idx)); + return std::string(); + } + inline std::wstring getUTF16(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) + return search->second.at(idx); + return std::wstring(); + } + inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) +#if HECL_UCS2 + return search->second.at(idx); +#else + return HECL::WideToUTF8(search->second.at(idx)); +#endif + return std::string(); + } +}; + +} +} + +#endif // __DNAMP2_STRG_HPP__ diff --git a/DataSpec/DNAMP3/CMakeLists.txt b/DataSpec/DNAMP3/CMakeLists.txt index 4eca7750b..b84527b2f 100644 --- a/DataSpec/DNAMP3/CMakeLists.txt +++ b/DataSpec/DNAMP3/CMakeLists.txt @@ -1,4 +1,7 @@ make_dnalist(liblist - PAK) + PAK + MLVL + STRG) add_library(DNAMP3 ${liblist} - PAK.cpp) + PAK.cpp + STRG.cpp) diff --git a/DataSpec/DNAMP3/MLVL.hpp b/DataSpec/DNAMP3/MLVL.hpp new file mode 100644 index 000000000..7f357e114 --- /dev/null +++ b/DataSpec/DNAMP3/MLVL.hpp @@ -0,0 +1,82 @@ +#ifndef __DNAMP3_MLVL_HPP__ +#define __DNAMP3_MLVL_HPP__ + +#include "../DNACommon/DNACommon.hpp" + +namespace Retro +{ +namespace DNAMP3 +{ + +struct MLVL : BigDNA +{ + DECL_DNA + Value magic; + Value version; + UniqueID64 worldNameId; + Value unk; + UniqueID64 saveWorldId; + UniqueID64 worldSkyboxId; + + Value areaCount; + struct Area : BigDNA + { + DECL_DNA + UniqueID64 areaNameId; + Value transformMtx[3]; + Value aabb[2]; + UniqueID64 areaMREAId; + Value areaId; + + Value attachedAreaCount; + Vector attachedAreas; + + Value dockCount; + struct Dock : BigDNA + { + DECL_DNA + Value endpointCount; + struct Endpoint : BigDNA + { + DECL_DNA + Value areaIdx; + Value dockIdx; + }; + FourCC type; + Vector endpoints; + + Value planeVertCount; + Vector planeVerts; + }; + Vector docks; + + String<-1> internalAreaName; + }; + + UniqueID64 worldMap; + Value unknown2; + Value unknown3; + + Value layerFlagCount; + struct LayerFlags : BigDNA + { + DECL_DNA + Value layerCount; + Value flags; + }; + Vector layerFlags; + + Value layerNameCount; + Vector, DNA_COUNT(layerNameCount)> layerNames; + + Value layerIDCount; + Vector layerIDs; + + Value layerNameOffsetCount; + Vector layerNameOffsets; +}; + +} +} + +#endif // __DNAMP3_MLVL_HPP__ diff --git a/DataSpec/DNAMP3/PAK.cpp b/DataSpec/DNAMP3/PAK.cpp index 44adad77f..53b6e0651 100644 --- a/DataSpec/DNAMP3/PAK.cpp +++ b/DataSpec/DNAMP3/PAK.cpp @@ -19,6 +19,7 @@ void PAK::read(Athena::io::IStreamReader& reader) reader.seek(44, Athena::Current); m_dataOffset = 128 + strgSz + rshdSz; + atUint64 strgBase = reader.position(); atUint32 nameCount = reader.readUint32(); m_nameEntries.clear(); m_nameEntries.reserve(nameCount); @@ -27,7 +28,10 @@ void PAK::read(Athena::io::IStreamReader& reader) m_nameEntries.emplace_back(); m_nameEntries.back().read(reader); } - reader.seek((reader.position() + 63) & ~63, Athena::Begin); + atUint64 start = reader.position(); + reader.seek(strgBase + strgSz, Athena::Begin); + atUint64 end = reader.position(); + atUint64 diff = end - start; atUint32 count = reader.readUint32(); m_entries.clear(); @@ -38,16 +42,17 @@ void PAK::read(Athena::io::IStreamReader& reader) { m_entries.emplace_back(); m_entries.back().read(reader); - m_idMap[m_entries.back().id] = &m_entries.back(); } + for (Entry& entry : m_entries) + m_idMap[entry.id] = &entry; m_nameMap.clear(); m_nameMap.reserve(nameCount); for (NameEntry& entry : m_nameEntries) { - std::unordered_map::iterator found = m_idMap.find(entry.id); - if (found != m_idMap.end()) - m_nameMap[entry.name] = found->second; + auto search = m_idMap.find(entry.id); + if (search != m_idMap.end()) + m_nameMap[entry.name] = search->second; } } void PAK::write(Athena::io::IStreamWriter& writer) const diff --git a/DataSpec/DNAMP3/PAK.hpp b/DataSpec/DNAMP3/PAK.hpp index c06a6703f..2ffeede46 100644 --- a/DataSpec/DNAMP3/PAK.hpp +++ b/DataSpec/DNAMP3/PAK.hpp @@ -11,9 +11,8 @@ namespace Retro namespace DNAMP3 { -class PAK : public BigDNA +struct PAK : BigDNA { -public: struct Header : BigDNA { DECL_DNA @@ -35,20 +34,18 @@ public: { DECL_DNA Value compressed; - HECL::FourCC type; + FourCC type; UniqueID64 id; Value size; Value offset; }; -private: std::vector m_nameEntries; std::vector m_entries; std::unordered_map m_idMap; std::unordered_map m_nameMap; size_t m_dataOffset = 0; -public: DECL_EXPLICIT_DNA inline const Entry* lookupEntry(const UniqueID64& id) const @@ -67,8 +64,6 @@ public: return nullptr; } - inline std::vector::iterator begin() {return m_entries.begin();} - inline std::vector::iterator end() {return m_entries.end();} inline size_t getDataOffset() const {return m_dataOffset;} }; diff --git a/DataSpec/DNAMP3/STRG.cpp b/DataSpec/DNAMP3/STRG.cpp new file mode 100644 index 000000000..0ec10bb90 --- /dev/null +++ b/DataSpec/DNAMP3/STRG.cpp @@ -0,0 +1,119 @@ +#include "STRG.hpp" +#include "../Logging.hpp" + +namespace Retro +{ +namespace DNAMP3 +{ + +void STRG::_read(Athena::io::IStreamReader& reader) +{ + atUint32 langCount = reader.readUint32(); + atUint32 strCount = reader.readUint32(); + + atUint32 nameCount = reader.readUint32(); + atUint32 nameTableSz = reader.readUint32(); + std::unique_ptr nameTableBuf(new uint8_t[nameTableSz]); + reader.readUBytesToBuf(nameTableBuf.get(), nameTableSz); + struct NameIdxEntry + { + atUint32 nameOff; + atUint32 strIdx; + }* nameIndex = (NameIdxEntry*)nameTableBuf.get(); + for (atUint32 n=0 ; n readLangs; + readLangs.reserve(langCount); + for (atUint32 l=0 ; l strs; + for (atUint32 s=0 ; s& name : names) + nameTableSz += name.first.size() + 1; + writer.writeUint32(names.size()); + writer.writeUint32(nameTableSz); + atUint32 offset = names.size() * 8; + for (const std::pair& name : names) + { + writer.writeUint32(offset); + writer.writeInt32(name.second); + offset += name.first.size() + 1; + } + for (const std::pair& name : names) + writer.writeString(name.first); + + offset = 0; + for (const std::pair>& lang : langs) + { + lang.first.write(writer); + + atUint32 langSz = 0; + for (const std::string& str : lang.second) + langSz += str.size() + 4; + writer.writeUint32(langSz); + + for (const std::string& str : lang.second) + { + writer.writeUint32(offset); + offset += str.size() + 4; + } + } + + for (const std::pair>& lang : langs) + { + for (const std::string& str : lang.second) + { + writer.writeUint32(str.size()); + writer.writeString(str, str.size()); + } + } +} + +} +} diff --git a/DataSpec/DNAMP3/STRG.hpp b/DataSpec/DNAMP3/STRG.hpp new file mode 100644 index 000000000..53fd375e2 --- /dev/null +++ b/DataSpec/DNAMP3/STRG.hpp @@ -0,0 +1,69 @@ +#ifndef __DNAMP3_STRG_HPP__ +#define __DNAMP3_STRG_HPP__ + +#include +#include "../DNACommon/DNACommon.hpp" +#include "../DNACommon/STRG.hpp" + +namespace Retro +{ +namespace DNAMP3 +{ + +struct STRG : ISTRG, BigDNA +{ + DECL_EXPLICIT_DNA + void _read(Athena::io::IStreamReader& reader); + std::unordered_map> langs; + std::map names; + + inline int32_t lookupIdx(const std::string& name) const + { + auto search = names.find(name); + if (search == names.end()) + return -1; + return search->second; + } + + inline size_t count() const + { + size_t retval = 0; + for (auto item : langs) + { + size_t sz = item.second.size(); + if (sz > retval) + retval = sz; + } + return retval; + } + inline std::string getUTF8(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) + return search->second.at(idx); + return std::string(); + } + inline std::wstring getUTF16(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) + return HECL::UTF8ToWide(search->second.at(idx)); + return std::wstring(); + } + inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const + { + auto search = langs.find(lang); + if (search != langs.end()) +#if HECL_UCS2 + return HECL::UTF8ToWide(search->second.at(idx)); +#else + return search->second.at(idx); +#endif + return std::string(); + } +}; + +} +} + +#endif // __DNAMP2_STRG_HPP__ diff --git a/DataSpec/SpecBase.cpp b/DataSpec/SpecBase.cpp index 8c991a540..b34fbef80 100644 --- a/DataSpec/SpecBase.cpp +++ b/DataSpec/SpecBase.cpp @@ -8,10 +8,10 @@ LogVisor::LogModule LogModule("RetroDataSpec"); bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector& reps) { bool isWii; - std::unique_ptr disc = NOD::OpenDiscFromImage(info.srcpath->c_str(), isWii); + std::unique_ptr disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), isWii); if (!disc) { - LogModule.report(LogVisor::Error, _S("'%s' not a valid Nintendo disc image"), info.srcpath->c_str()); + LogModule.report(LogVisor::Error, _S("'%s' not a valid Nintendo disc image"), info.srcpath.c_str()); return false; } const char* gameID = disc->getHeader().gameID; @@ -41,11 +41,11 @@ bool SpecBase::canExtract(const ExtractPassInfo& info, std::vectorgetHeader().gameID[3]; - static const std::string regNONE = ""; - static const std::string regE = "NTSC"; - static const std::string regJ = "NTSC-J"; - static const std::string regP = "PAL"; - const std::string* regstr = ®NONE; + static const HECL::SystemString regNONE = _S(""); + static const HECL::SystemString regE = _S("NTSC"); + static const HECL::SystemString regJ = _S("NTSC-J"); + static const HECL::SystemString regP = _S("PAL"); + const HECL::SystemString* regstr = ®NONE; switch (region) { case 'E': diff --git a/DataSpec/SpecBase.hpp b/DataSpec/SpecBase.hpp index 62aec499c..13c8da49c 100644 --- a/DataSpec/SpecBase.hpp +++ b/DataSpec/SpecBase.hpp @@ -25,12 +25,12 @@ struct SpecBase : HECL::Database::IDataSpec void doPackage(const HECL::Database::Project& project, const PackagePassInfo& info); virtual bool checkFromStandaloneDisc(NOD::DiscBase& disc, - const std::string& regstr, - const std::vector& args, + const HECL::SystemString& regstr, + const std::vector& args, std::vector& reps)=0; virtual bool checkFromTrilogyDisc(NOD::DiscBase& disc, - const std::string& regstr, - const std::vector& args, + const HECL::SystemString& regstr, + const std::vector& args, std::vector& reps)=0; virtual bool extractFromDisc()=0; diff --git a/DataSpec/SpecMP1.cpp b/DataSpec/SpecMP1.cpp index 9274b0eb2..a09ae582a 100644 --- a/DataSpec/SpecMP1.cpp +++ b/DataSpec/SpecMP1.cpp @@ -21,7 +21,7 @@ struct SpecMP1 : SpecBase std::map m_orderedPaks; void buildPaks(NOD::DiscBase::IPartition::Node& root, - const std::vector& args, + const std::vector& args, ExtractReport& rep) { m_paks.clear(); @@ -46,10 +46,10 @@ struct SpecMP1 : SpecBase if (!lowerName.compare(0, 7, "metroid")) { HECL::SystemChar idxChar = lowerName[7]; - for (const HECL::SystemString* arg : args) + for (const HECL::SystemString& arg : args) { - if (arg->size() == 1 && iswdigit((*arg)[0])) - if ((*arg)[0] == idxChar) + if (arg.size() == 1 && iswdigit(arg[0])) + if (arg[0] == idxChar) good = true; } } @@ -58,12 +58,12 @@ struct SpecMP1 : SpecBase if (!good) { - for (const HECL::SystemString* arg : args) + for (const HECL::SystemString& arg : args) { #if HECL_UCS2 - std::string lowerArg = HECL::WideToUTF8(*arg); + std::string lowerArg = HECL::WideToUTF8(arg); #else - std::string lowerArg = *arg; + std::string lowerArg = arg; #endif std::transform(lowerArg.begin(), lowerArg.end(), lowerArg.begin(), tolower); if (!lowerArg.compare(0, lowerBase.size(), lowerBase)) @@ -97,8 +97,7 @@ struct SpecMP1 : SpecBase DNAMP1::PAK& pak = item.second->pak; for (DNAMP1::PAK::Entry& entry : pak.m_entries) { - static const HECL::FourCC MLVLfourcc("MLVL"); - if (entry.type == MLVLfourcc) + if (entry.type == MLVL) { NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(entry.offset)); DNAMP1::MLVL mlvl; @@ -111,11 +110,7 @@ struct SpecMP1 : SpecBase mlvlName.read(rs); if (childRep.desc.size()) childRep.desc += _S(", "); -#if HECL_UCS2 - childRep.desc += mlvlName.langs[0].strings[0]; -#else - childRep.desc += HECL::WideToUTF8(mlvlName.langs[0].strings[0]); -#endif + childRep.desc += mlvlName.getSystemString(ENGL, 0); } } } @@ -123,33 +118,83 @@ struct SpecMP1 : SpecBase } bool checkFromStandaloneDisc(NOD::DiscBase& disc, - const std::string& regstr, - const std::vector& args, + const HECL::SystemString& regstr, + const std::vector& args, std::vector& reps) { NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); std::unique_ptr dolBuf = partition->getDOLBuf(); const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; + /* Root Report */ reps.emplace_back(); ExtractReport& rep = reps.back(); - rep.name = "MP1"; - rep.desc = "Metroid Prime " + regstr; + rep.name = _S("MP1"); + rep.desc = _S("Metroid Prime ") + regstr; if (buildInfo) - rep.desc += " (" + std::string(buildInfo) + ")"; + { + HECL::SystemStringView buildView(buildInfo); + rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + } /* Iterate PAKs and build level options */ - NOD::DiscBase::IPartition::Node& root = disc.getDataPartition()->getFSTRoot(); + NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); buildPaks(root, args, rep); return true; } bool checkFromTrilogyDisc(NOD::DiscBase& disc, - const std::string& regstr, - const std::vector& args, + const HECL::SystemString& regstr, + const std::vector& args, std::vector& reps) { + std::vector mp1args; + if (args.size()) + { + /* Needs filter */ + for (const HECL::SystemString& arg : args) + { + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) + { + HECL::SystemString lowerArg(arg.begin(), arg.begin() + slashPos); + HECL::ToLower(lowerArg); + if (!lowerArg.compare(_S("mp1"))) + mp1args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); + } + } + } + + NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); + NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); + NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5mp1_p.dol"); + if (dolIt == root.end()) + return false; + + std::unique_ptr dolBuf = dolIt->getBuf(); + const char* buildInfo = (char*)memmem(dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16) + 19; + + /* Root Report */ + reps.emplace_back(); + ExtractReport& rep = reps.back(); + rep.name = _S("MP1"); + rep.desc = _S("Metroid Prime ") + regstr; + if (buildInfo) + { + std::string buildStr(buildInfo); + HECL::SystemStringView buildView(buildStr); + rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node::DirectoryIterator mp1It = root.find("MP1"); + if (mp1It == root.end()) + return false; + buildPaks(*mp1It, mp1args, rep); + return true; } bool extractFromDisc() diff --git a/DataSpec/SpecMP2.cpp b/DataSpec/SpecMP2.cpp index 991bcd01d..b84865a2d 100644 --- a/DataSpec/SpecMP2.cpp +++ b/DataSpec/SpecMP2.cpp @@ -1,8 +1,241 @@ +#include + +#define NOD_ATHENA 1 #include "SpecBase.hpp" +#include "DNAMP1/PAK.hpp" +#include "DNAMP2/MLVL.hpp" +#include "DNAMP2/STRG.hpp" + +namespace Retro +{ + +struct SpecMP2 : SpecBase +{ + struct DiscPAK + { + const NOD::DiscBase::IPartition::Node& node; + DNAMP1::PAK pak; + DiscPAK(const NOD::DiscBase::IPartition::Node& n) : node(n) {} + }; + std::vector m_paks; + std::map m_orderedPaks; + + void buildPaks(NOD::DiscBase::IPartition::Node& root, + const std::vector& args, + ExtractReport& rep) + { + m_paks.clear(); + for (const NOD::DiscBase::IPartition::Node& child : root) + { + const std::string& name = child.getName(); + std::string lowerName = name; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); + if (name.size() > 4) + { + std::string::iterator extit = lowerName.end() - 4; + if (!std::string(extit, lowerName.end()).compare(".pak")) + { + /* This is a pak */ + std::string lowerBase(lowerName.begin(), extit); + + /* Needs filter */ + bool good = true; + if (args.size()) + { + good = false; + if (!lowerName.compare(0, 7, "metroid")) + { + HECL::SystemChar idxChar = lowerName[7]; + for (const HECL::SystemString& arg : args) + { + if (arg.size() == 1 && iswdigit(arg[0])) + if (arg[0] == idxChar) + good = true; + } + } + else + good = true; + + if (!good) + { + for (const HECL::SystemString& arg : args) + { +#if HECL_UCS2 + std::string lowerArg = HECL::WideToUTF8(arg); +#else + std::string lowerArg = arg; +#endif + std::transform(lowerArg.begin(), lowerArg.end(), lowerArg.begin(), tolower); + if (!lowerArg.compare(0, lowerBase.size(), lowerBase)) + good = true; + } + } + } + + if (good) + { + m_paks.emplace_back(child); + NOD::AthenaPartReadStream rs(child.beginReadStream()); + m_paks.back().pak.read(rs); + } + } + } + } + + /* Sort PAKs alphabetically */ + m_orderedPaks.clear(); + for (DiscPAK& dpak : m_paks) + m_orderedPaks[dpak.node.getName()] = &dpak; + + /* Assemble extract report */ + for (std::pair item : m_orderedPaks) + { + rep.childOpts.emplace_back(); + ExtractReport& childRep = rep.childOpts.back(); + childRep.name = item.first; + + DNAMP1::PAK& pak = item.second->pak; + for (DNAMP1::PAK::Entry& entry : pak.m_entries) + { + if (entry.type == MLVL) + { + NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(entry.offset)); + DNAMP2::MLVL mlvl; + mlvl.read(rs); + const DNAMP1::PAK::Entry* nameEnt = pak.lookupEntry(mlvl.worldNameId); + if (nameEnt) + { + DNAMP2::STRG mlvlName; + NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(nameEnt->offset)); + mlvlName.read(rs); + if (childRep.desc.size()) + childRep.desc += _S(", "); + childRep.desc += mlvlName.getSystemString(ENGL, 0); + } + } + } + } + } + + bool checkFromStandaloneDisc(NOD::DiscBase& disc, + const HECL::SystemString& regstr, + const std::vector& args, + std::vector& reps) + { + NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); + std::unique_ptr dolBuf = partition->getDOLBuf(); + const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; + + /* Root Report */ + reps.emplace_back(); + ExtractReport& rep = reps.back(); + rep.name = _S("MP2"); + rep.desc = _S("Metroid Prime 2 ") + regstr; + if (buildInfo) + { + HECL::SystemStringView buildView(buildInfo); + rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); + buildPaks(root, args, rep); + + return true; + } + + bool checkFromTrilogyDisc(NOD::DiscBase& disc, + const HECL::SystemString& regstr, + const std::vector& args, + std::vector& reps) + { + std::vector mp2args; + if (args.size()) + { + /* Needs filter */ + for (const HECL::SystemString& arg : args) + { + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) + { + HECL::SystemString lowerArg(arg.begin(), arg.begin() + slashPos); + HECL::ToLower(lowerArg); + if (!lowerArg.compare(_S("mp2"))) + mp2args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); + } + } + } + + NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); + NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); + NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5mp2_p.dol"); + if (dolIt == root.end()) + return false; + + std::unique_ptr dolBuf = dolIt->getBuf(); + const char* buildInfo = (char*)memmem(dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16) + 19; + + /* Root Report */ + reps.emplace_back(); + ExtractReport& rep = reps.back(); + rep.name = _S("MP2"); + rep.desc = _S("Metroid Prime 2 ") + regstr; + if (buildInfo) + { + std::string buildStr(buildInfo); + HECL::SystemStringView buildView(buildStr); + rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node::DirectoryIterator mp2It = root.find("MP2"); + if (mp2It == root.end()) + return false; + buildPaks(*mp2It, mp2args, rep); + + return true; + } + bool extractFromDisc() + { + } + + bool checkFromProject(HECL::Database::Project& proj) + { + } + bool readFromProject(HECL::Database::Project& proj) + { + } + + bool visitGameObjects(std::function) + { + } + struct LevelSpec : public ILevelSpec + { + bool visitLevelObjects(std::function) + { + } + struct AreaSpec : public IAreaSpec + { + bool visitAreaObjects(std::function) + { + } + }; + bool visitAreas(std::function) + { + } + }; + bool visitLevels(std::function) + { + } +}; static HECL::Database::DataSpecEntry SpecMP2 ( _S("MP2"), _S("Data specification for original Metroid Prime 2 engine"), - [](HECL::Database::DataSpecTool tool) -> HECL::Database::IDataSpec* {return nullptr;} + [](HECL::Database::DataSpecTool tool) -> HECL::Database::IDataSpec* {return new struct SpecMP2;} ); + +} diff --git a/DataSpec/SpecMP3.cpp b/DataSpec/SpecMP3.cpp index 5bf8199ed..df3f34ef4 100644 --- a/DataSpec/SpecMP3.cpp +++ b/DataSpec/SpecMP3.cpp @@ -1,9 +1,241 @@ +#include + +#define NOD_ATHENA 1 #include "SpecBase.hpp" +#include "DNAMP3/PAK.hpp" +#include "DNAMP3/MLVL.hpp" +#include "DNAMP3/STRG.hpp" + +namespace Retro +{ + +struct SpecMP3 : SpecBase +{ + struct DiscPAK + { + const NOD::DiscBase::IPartition::Node& node; + DNAMP3::PAK pak; + DiscPAK(const NOD::DiscBase::IPartition::Node& n) : node(n) {} + }; + std::vector m_paks; + std::map m_orderedPaks; + + void buildPaks(NOD::DiscBase::IPartition::Node& root, + const std::vector& args, + ExtractReport& rep) + { + m_paks.clear(); + for (const NOD::DiscBase::IPartition::Node& child : root) + { + const std::string& name = child.getName(); + std::string lowerName = name; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); + if (name.size() > 4) + { + std::string::iterator extit = lowerName.end() - 4; + if (!std::string(extit, lowerName.end()).compare(".pak")) + { + /* This is a pak */ + std::string lowerBase(lowerName.begin(), extit); + + /* Needs filter */ + bool good = true; + if (args.size()) + { + good = false; + if (!lowerName.compare(0, 7, "metroid")) + { + HECL::SystemChar idxChar = lowerName[7]; + for (const HECL::SystemString& arg : args) + { + if (arg.size() == 1 && iswdigit(arg[0])) + if (arg[0] == idxChar) + good = true; + } + } + else + good = true; + + if (!good) + { + for (const HECL::SystemString& arg : args) + { +#if HECL_UCS2 + std::string lowerArg = HECL::WideToUTF8(arg); +#else + std::string lowerArg = arg; +#endif + std::transform(lowerArg.begin(), lowerArg.end(), lowerArg.begin(), tolower); + if (!lowerArg.compare(0, lowerBase.size(), lowerBase)) + good = true; + } + } + } + + if (good) + { + m_paks.emplace_back(child); + NOD::AthenaPartReadStream rs(child.beginReadStream()); + m_paks.back().pak.read(rs); + } + } + } + } + + /* Sort PAKs alphabetically */ + m_orderedPaks.clear(); + for (DiscPAK& dpak : m_paks) + m_orderedPaks[dpak.node.getName()] = &dpak; + + /* Assemble extract report */ + for (std::pair item : m_orderedPaks) + { + rep.childOpts.emplace_back(); + ExtractReport& childRep = rep.childOpts.back(); + childRep.name = item.first; + + DNAMP3::PAK& pak = item.second->pak; + for (DNAMP3::PAK::Entry& entry : pak.m_entries) + { + if (entry.type == MLVL) + { + NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(entry.offset)); + DNAMP3::MLVL mlvl; + mlvl.read(rs); + const DNAMP3::PAK::Entry* nameEnt = pak.lookupEntry(mlvl.worldNameId); + if (nameEnt) + { + DNAMP3::STRG mlvlName; + NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(nameEnt->offset)); + mlvlName.read(rs); + if (childRep.desc.size()) + childRep.desc += _S(", "); + childRep.desc += mlvlName.getSystemString(ENGL, 0); + } + } + } + } + } + + bool checkFromStandaloneDisc(NOD::DiscBase& disc, + const HECL::SystemString& regstr, + const std::vector& args, + std::vector& reps) + { + NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); + std::unique_ptr dolBuf = partition->getDOLBuf(); + const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; + + /* Root Report */ + reps.emplace_back(); + ExtractReport& rep = reps.back(); + rep.name = _S("MP3"); + rep.desc = _S("Metroid Prime 3 ") + regstr; + if (buildInfo) + { + HECL::SystemStringView buildView(buildInfo); + rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); + buildPaks(root, args, rep); + + return true; + } + + bool checkFromTrilogyDisc(NOD::DiscBase& disc, + const HECL::SystemString& regstr, + const std::vector& args, + std::vector& reps) + { + std::vector mp3args; + if (args.size()) + { + /* Needs filter */ + for (const HECL::SystemString& arg : args) + { + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) + { + HECL::SystemString lowerArg(arg.begin(), arg.begin() + slashPos); + HECL::ToLower(lowerArg); + if (!lowerArg.compare(_S("mp3"))) + mp3args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); + } + } + } + + NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); + NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); + NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5mp3_p.dol"); + if (dolIt == root.end()) + return false; + + std::unique_ptr dolBuf = dolIt->getBuf(); + const char* buildInfo = (char*)memmem(dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16) + 19; + + /* Root Report */ + reps.emplace_back(); + ExtractReport& rep = reps.back(); + rep.name = _S("MP3"); + rep.desc = _S("Metroid Prime 3 ") + regstr; + if (buildInfo) + { + std::string buildStr(buildInfo); + HECL::SystemStringView buildView(buildStr); + rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node::DirectoryIterator mp3It = root.find("MP3"); + if (mp3It == root.end()) + return false; + buildPaks(*mp3It, mp3args, rep); + + return true; + } + bool extractFromDisc() + { + } + + bool checkFromProject(HECL::Database::Project& proj) + { + } + bool readFromProject(HECL::Database::Project& proj) + { + } + + bool visitGameObjects(std::function) + { + } + struct LevelSpec : public ILevelSpec + { + bool visitLevelObjects(std::function) + { + } + struct AreaSpec : public IAreaSpec + { + bool visitAreaObjects(std::function) + { + } + }; + bool visitAreas(std::function) + { + } + }; + bool visitLevels(std::function) + { + } +}; static HECL::Database::DataSpecEntry SpecMP3 ( _S("MP3"), _S("Data specification for original Metroid Prime 3 engine"), - [](HECL::Database::DataSpecTool tool) -> HECL::Database::IDataSpec* {return nullptr;} + [](HECL::Database::DataSpecTool tool) -> HECL::Database::IDataSpec* {return new struct SpecMP3;} ); +} diff --git a/NODLib b/NODLib index f30a9302b..01f269e8e 160000 --- a/NODLib +++ b/NODLib @@ -1 +1 @@ -Subproject commit f30a9302b6bad6811efd8125b424c70069514658 +Subproject commit 01f269e8e2a55d8b64e1e277d9a1f87f6877c446