From dea341d27b52e60ab3b8f4ee7e7b808ef516b39d Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 17 Jul 2015 18:33:38 -1000 Subject: [PATCH] All cooked resources extracting; decompression bug-fixes --- DataSpec/DNACommon/DNACommon.hpp | 7 + DataSpec/DNAMP1/DNAMP1.cpp | 31 ++++- DataSpec/DNAMP1/DNAMP1.hpp | 6 +- DataSpec/DNAMP1/PAK.cpp | 28 ++-- DataSpec/DNAMP1/PAK.hpp | 7 +- DataSpec/DNAMP1/STRG.cpp | 27 +++- DataSpec/DNAMP1/STRG.hpp | 24 ++-- DataSpec/DNAMP2/DNAMP2.cpp | 29 +++- DataSpec/DNAMP2/DNAMP2.hpp | 6 +- DataSpec/DNAMP2/STRG.cpp | 36 ++++- DataSpec/DNAMP2/STRG.hpp | 23 ++-- DataSpec/DNAMP3/DNAMP3.cpp | 40 ++++-- DataSpec/DNAMP3/DNAMP3.hpp | 6 +- DataSpec/DNAMP3/PAK.hpp | 5 +- DataSpec/DNAMP3/STRG.cpp | 34 ++++- DataSpec/DNAMP3/STRG.hpp | 31 +++-- DataSpec/SpecBase.cpp | 34 +++-- DataSpec/SpecBase.hpp | 4 +- DataSpec/SpecMP1.cpp | 58 ++++++-- DataSpec/SpecMP2.cpp | 65 +++++++-- DataSpec/SpecMP3.cpp | 229 ++++++++++++++++++++++++++----- NODLib | 2 +- 22 files changed, 564 insertions(+), 168 deletions(-) diff --git a/DataSpec/DNACommon/DNACommon.hpp b/DataSpec/DNACommon/DNACommon.hpp index 26d028219..34f479d7e 100644 --- a/DataSpec/DNACommon/DNACommon.hpp +++ b/DataSpec/DNACommon/DNACommon.hpp @@ -149,6 +149,12 @@ class PAKEntryReadStream : public Athena::io::IStreamReader atUint64 m_sz; atUint64 m_pos; public: + PAKEntryReadStream() {} + operator bool() const {return m_buf.operator bool();} + PAKEntryReadStream(const PAKEntryReadStream& other) = delete; + PAKEntryReadStream(PAKEntryReadStream&& other) = default; + PAKEntryReadStream& operator=(const PAKEntryReadStream& other) = delete; + PAKEntryReadStream& operator=(PAKEntryReadStream&& other) = default; PAKEntryReadStream(std::unique_ptr&& buf, atUint64 sz, atUint64 pos) : m_buf(std::move(buf)), m_sz(sz), m_pos(pos) { @@ -168,6 +174,7 @@ public: } inline atUint64 position() const {return m_pos;} inline atUint64 length() const {return m_sz;} + inline const atUint8* data() const {return m_buf.get();} inline atUint64 readUBytesToBuf(void* buf, atUint64 len) { atUint64 bufEnd = m_pos + len; diff --git a/DataSpec/DNAMP1/DNAMP1.cpp b/DataSpec/DNAMP1/DNAMP1.cpp index 45cc4ef76..0a44fb0f1 100644 --- a/DataSpec/DNAMP1/DNAMP1.cpp +++ b/DataSpec/DNAMP1/DNAMP1.cpp @@ -1,3 +1,5 @@ +#include + #define NOD_ATHENA 1 #include "DNAMP1.hpp" #include "STRG.hpp" @@ -10,7 +12,7 @@ namespace DNAMP1 LogVisor::LogModule Log("Retro::DNAMP1"); PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node) -: m_project(project), m_node(node) +: m_project(project), m_node(node), m_pak(false) { NOD::AthenaPartReadStream rs(node.beginReadStream()); m_pak.read(rs); @@ -34,7 +36,7 @@ std::string PAKBridge::getLevelString() const mlvlName.read(rs); if (retval.size()) retval += _S(", "); - retval += mlvlName.getSystemString(ENGL, 0); + retval += mlvlName.getUTF8(ENGL, 0); } } } @@ -44,20 +46,35 @@ std::string PAKBridge::getLevelString() const ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) { if (entry.type == Retro::STRG) - return {STRG::Extract, ".strg"}; + return {STRG::Extract, ".as"}; return {}; } -bool PAKBridge::extractResources(const HECL::ProjectPath& dirOut) +bool PAKBridge::extractResources(const HECL::ProjectPath& workingOut, + const HECL::ProjectPath& cookedOut, + bool force) { for (const std::pair& item : m_pak.m_idMap) { + PAKEntryReadStream s; ResExtractor extractor = LookupExtractor(*item.second); if (extractor.func) { - PAKEntryReadStream strgIn = item.second->beginReadStream(m_node); - HECL::ProjectPath resPath(dirOut, m_pak.bestEntryName(*item.second) + extractor.fileExt); - extractor.func(strgIn, resPath); + HECL::ProjectPath workPath(workingOut, m_pak.bestEntryName(*item.second) + extractor.fileExt); + if (force || workPath.getPathType() == HECL::ProjectPath::PT_NONE) + { + s = item.second->beginReadStream(m_node); + extractor.func(s, workPath); + } + } + HECL::ProjectPath cookPath(cookedOut, m_pak.bestEntryName(*item.second)); + if (force || cookPath.getPathType() == HECL::ProjectPath::PT_NONE) + { + if (!s) + s = item.second->beginReadStream(m_node); + FILE* fout = HECL::Fopen(cookPath.getAbsolutePath().c_str(), _S("wb")); + fwrite(s.data(), 1, s.length(), fout); + fclose(fout); } } return true; diff --git a/DataSpec/DNAMP1/DNAMP1.hpp b/DataSpec/DNAMP1/DNAMP1.hpp index 54a7316ff..45c61c3f8 100644 --- a/DataSpec/DNAMP1/DNAMP1.hpp +++ b/DataSpec/DNAMP1/DNAMP1.hpp @@ -17,14 +17,14 @@ class PAKBridge HECL::Database::Project& m_project; const NOD::DiscBase::IPartition::Node& m_node; PAK m_pak; - static ResExtractor LookupExtractor(const PAK::Entry& entry); - public: PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node); const std::string& getName() const {return m_node.getName();} std::string getLevelString() const; - bool extractResources(const HECL::ProjectPath& dirOut); + bool extractResources(const HECL::ProjectPath& dirOut, + const HECL::ProjectPath& cookedOut, + bool force); }; } diff --git a/DataSpec/DNAMP1/PAK.cpp b/DataSpec/DNAMP1/PAK.cpp index 3bef42968..8881a9220 100644 --- a/DataSpec/DNAMP1/PAK.cpp +++ b/DataSpec/DNAMP1/PAK.cpp @@ -33,7 +33,10 @@ void PAK::read(Athena::io::IStreamReader& reader) for (atUint32 e=0 ; e PAK::Entry::getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const +std::unique_ptr +PAK::Entry::getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const { if (compressed) { @@ -78,22 +87,17 @@ std::unique_ptr PAK::Entry::getBuffer(const NOD::DiscBase::IPartition atUint8* buf = new atUint8[decompSz]; atUint8* bufCur = buf; - atUint16 zlibCheck; - strm->read(&zlibCheck, 2); - zlibCheck = HECL::SBig(zlibCheck); - strm->seek(-2, SEEK_CUR); - - atUint8 compBuf[0x4000]; - if ((zlibCheck % 31) == 0) + atUint8 compBuf[0x8000]; + if (compressed == 1) { atUint32 compRem = size - 4; - z_stream zs; + z_stream zs = {}; inflateInit(&zs); zs.avail_out = decompSz; zs.next_out = buf; while (zs.avail_out) { - atUint64 readSz = strm->read(compBuf, MIN(compRem, 0x4000)); + atUint64 readSz = strm->read(compBuf, MIN(compRem, 0x8000)); compRem -= readSz; zs.avail_in = readSz; zs.next_in = compBuf; diff --git a/DataSpec/DNAMP1/PAK.hpp b/DataSpec/DNAMP1/PAK.hpp index 42ad62b4a..5bd2e8686 100644 --- a/DataSpec/DNAMP1/PAK.hpp +++ b/DataSpec/DNAMP1/PAK.hpp @@ -13,6 +13,8 @@ namespace DNAMP1 struct PAK : BigDNA { + bool m_useLzo; + PAK(bool useLzo) : m_useLzo(useLzo) {} DECL_EXPLICIT_DNA struct NameEntry : BigDNA @@ -37,7 +39,8 @@ struct PAK : BigDNA inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const { atUint64 sz; - return PAKEntryReadStream(getBuffer(pak, sz), sz, off); + std::unique_ptr buf = getBuffer(pak, sz); + return PAKEntryReadStream(std::move(buf), sz, off); } }; @@ -70,7 +73,7 @@ struct PAK : BigDNA return nentry.name; /* Otherwise return ID format string */ - return entry.id.toString(); + return entry.type.toString() + '_' + entry.id.toString(); } }; diff --git a/DataSpec/DNAMP1/STRG.cpp b/DataSpec/DNAMP1/STRG.cpp index d42d52ab7..8aad7c375 100644 --- a/DataSpec/DNAMP1/STRG.cpp +++ b/DataSpec/DNAMP1/STRG.cpp @@ -29,8 +29,13 @@ void STRG::_read(Athena::io::IStreamReader& reader) reader.seek(strCount * 4 + 4); for (atUint32 s=0 ; s>& item : langs) + langMap.emplace(item.first, &item.second); } void STRG::read(Athena::io::IStreamReader& reader) @@ -128,6 +133,7 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in) } /* Read pass */ + langs.clear(); for (AngelScript::asUINT i=0 ; i strs; for (const std::string* str : strsin) strs.emplace_back(wconv.from_bytes(*str)); - langs.emplace(std::make_pair(FourCC(name), strs)); + langs.emplace_back(FourCC(name), strs); } } + langMap.clear(); + langMap.reserve(langs.size()); + for (std::pair>& item : langs) + langMap.emplace(item.first, &item.second); + return true; } void STRG::writeAngelScript(std::ofstream& out) const { - std::wbuffer_convert> wconv(out.rdbuf()); - std::wostream wout(&wconv); + std::wstring_convert> wconv; for (const std::pair>& lang : langs) { out << "STRG::Language " << lang.first.toString() << "({"; bool comma = false; + unsigned idx = 0; for (const std::wstring& str : lang.second) { - out << (comma?", \"":"\""); - wout << str; + if (comma) + out << ","; + out << "\n/* " << idx++ << " */ \""; + out << wconv.to_bytes(str); out << "\""; comma = true; } - out << "});\n"; + out << "\n});\n"; } } diff --git a/DataSpec/DNAMP1/STRG.hpp b/DataSpec/DNAMP1/STRG.hpp index d14e10dc9..07251352c 100644 --- a/DataSpec/DNAMP1/STRG.hpp +++ b/DataSpec/DNAMP1/STRG.hpp @@ -14,7 +14,8 @@ struct STRG : ISTRG, BigDNA { DECL_EXPLICIT_DNA void _read(Athena::io::IStreamReader& reader); - std::unordered_map> langs; + std::vector>> langs; + std::unordered_map*> langMap; inline int32_t lookupIdx(const std::string& name) const {return -1;} @@ -31,26 +32,26 @@ struct STRG : ISTRG, BigDNA } 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)); + auto search = langMap.find(lang); + if (search != langMap.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); + auto search = langMap.find(lang); + if (search != langMap.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()) + auto search = langMap.find(lang); + if (search != langMap.end()) #if HECL_UCS2 - return search->second.at(idx); + return search->second->at(idx); #else - return HECL::WideToUTF8(search->second.at(idx)); + return HECL::WideToUTF8(search->second->at(idx)); #endif return std::string(); } @@ -58,7 +59,6 @@ struct STRG : ISTRG, BigDNA bool readAngelScript(const AngelScript::asIScriptModule& in); void writeAngelScript(std::ofstream& out) const; - }; } diff --git a/DataSpec/DNAMP2/DNAMP2.cpp b/DataSpec/DNAMP2/DNAMP2.cpp index 9add94371..5d9f19f30 100644 --- a/DataSpec/DNAMP2/DNAMP2.cpp +++ b/DataSpec/DNAMP2/DNAMP2.cpp @@ -11,7 +11,7 @@ namespace DNAMP2 LogVisor::LogModule Log("Retro::DNAMP2"); PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node) -: m_project(project), m_node(node) +: m_project(project), m_node(node), m_pak(true) { NOD::AthenaPartReadStream rs(node.beginReadStream()); m_pak.read(rs); @@ -35,7 +35,7 @@ std::string PAKBridge::getLevelString() const mlvlName.read(rs); if (retval.size()) retval += _S(", "); - retval += mlvlName.getSystemString(ENGL, 0); + retval += mlvlName.getUTF8(ENGL, 0); } } } @@ -45,20 +45,35 @@ std::string PAKBridge::getLevelString() const ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry) { if (entry.type == Retro::STRG) - return {STRG::Extract, ".strg"}; + return {STRG::Extract, ".as"}; return {}; } -bool PAKBridge::extractResources(const HECL::ProjectPath& dirOut) +bool PAKBridge::extractResources(const HECL::ProjectPath& workingOut, + const HECL::ProjectPath& cookedOut, + bool force) { for (const std::pair& item : m_pak.m_idMap) { + PAKEntryReadStream s; ResExtractor extractor = LookupExtractor(*item.second); if (extractor.func) { - PAKEntryReadStream strgIn = item.second->beginReadStream(m_node); - HECL::ProjectPath resPath(dirOut, m_pak.bestEntryName(*item.second) + extractor.fileExt); - extractor.func(strgIn, resPath); + HECL::ProjectPath workPath(workingOut, m_pak.bestEntryName(*item.second) + extractor.fileExt); + if (force || workPath.getPathType() == HECL::ProjectPath::PT_NONE) + { + s = item.second->beginReadStream(m_node); + extractor.func(s, workPath); + } + } + HECL::ProjectPath cookPath(cookedOut, m_pak.bestEntryName(*item.second)); + if (force || cookPath.getPathType() == HECL::ProjectPath::PT_NONE) + { + if (!s) + s = item.second->beginReadStream(m_node); + FILE* fout = HECL::Fopen(cookPath.getAbsolutePath().c_str(), _S("wb")); + fwrite(s.data(), 1, s.length(), fout); + fclose(fout); } } return true; diff --git a/DataSpec/DNAMP2/DNAMP2.hpp b/DataSpec/DNAMP2/DNAMP2.hpp index 1f82651d4..a175f7833 100644 --- a/DataSpec/DNAMP2/DNAMP2.hpp +++ b/DataSpec/DNAMP2/DNAMP2.hpp @@ -17,14 +17,14 @@ class PAKBridge HECL::Database::Project& m_project; const NOD::DiscBase::IPartition::Node& m_node; DNAMP1::PAK m_pak; - static ResExtractor LookupExtractor(const DNAMP1::PAK::Entry& entry); - public: PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node); const std::string& getName() const {return m_node.getName();} std::string getLevelString() const; - bool extractResources(const HECL::ProjectPath& dirOut); + bool extractResources(const HECL::ProjectPath& dirOut, + const HECL::ProjectPath& cookedOut, + bool force); }; } diff --git a/DataSpec/DNAMP2/STRG.cpp b/DataSpec/DNAMP2/STRG.cpp index cba2b1009..24611ebe4 100644 --- a/DataSpec/DNAMP2/STRG.cpp +++ b/DataSpec/DNAMP2/STRG.cpp @@ -44,8 +44,13 @@ void STRG::_read(Athena::io::IStreamReader& reader) reader.seek(strCount * 4); for (atUint32 s=0 ; s>& item : langs) + langMap.emplace(item.first, &item.second); } void STRG::read(Athena::io::IStreamReader& reader) @@ -140,6 +145,35 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in) void STRG::writeAngelScript(std::ofstream& out) const { + std::wstring_convert> wconv; + for (const std::pair>& lang : langs) + { + out << "STRG::Language " << lang.first.toString() << "({"; + bool comma = false; + unsigned idx = 0; + for (const std::wstring& str : lang.second) + { + if (comma) + out << ","; + out << "\n/* " << idx++ << " */ \""; + out << wconv.to_bytes(str); + out << "\""; + comma = true; + } + out << "\n});\n"; + } + + out << "STRG::Names NAMES({"; + bool comma = false; + for (const std::pair& name : names) + { + if (comma) + out << ","; + out << "\n "; + comma = true; + out << "{\"" << name.first << "\", " << name.second << "}"; + } + out << "\n});\n"; } } diff --git a/DataSpec/DNAMP2/STRG.hpp b/DataSpec/DNAMP2/STRG.hpp index 92b3d7203..5cb687e8d 100644 --- a/DataSpec/DNAMP2/STRG.hpp +++ b/DataSpec/DNAMP2/STRG.hpp @@ -14,7 +14,8 @@ struct STRG : ISTRG, BigDNA { DECL_EXPLICIT_DNA void _read(Athena::io::IStreamReader& reader); - std::unordered_map> langs; + std::vector>> langs; + std::unordered_map*> langMap; std::map names; inline int32_t lookupIdx(const std::string& name) const @@ -38,26 +39,26 @@ struct STRG : ISTRG, BigDNA } 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)); + auto search = langMap.find(lang); + if (search != langMap.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); + auto search = langMap.find(lang); + if (search != langMap.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()) + auto search = langMap.find(lang); + if (search != langMap.end()) #if HECL_UCS2 - return search->second.at(idx); + return search->second->at(idx); #else - return HECL::WideToUTF8(search->second.at(idx)); + return HECL::WideToUTF8(search->second->at(idx)); #endif return std::string(); } diff --git a/DataSpec/DNAMP3/DNAMP3.cpp b/DataSpec/DNAMP3/DNAMP3.cpp index f6a0a278a..c5e4999ba 100644 --- a/DataSpec/DNAMP3/DNAMP3.cpp +++ b/DataSpec/DNAMP3/DNAMP3.cpp @@ -21,8 +21,7 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar std::string PAKBridge::getLevelString() const { - std::string retval; - std::set worldNames; + std::set uniq; for (const PAK::Entry& entry : m_pak.m_entries) { if (entry.type == Retro::MLVL) @@ -36,16 +35,18 @@ std::string PAKBridge::getLevelString() const PAKEntryReadStream rs = nameEnt->beginReadStream(m_node); STRG mlvlName; mlvlName.read(rs); - worldNames.emplace(mlvlName.getSystemString(ENGL, 0)); + uniq.insert(mlvlName.getUTF8(ENGL, 0)); } } } - - for (const std::string& name : worldNames) + std::string retval; + bool comma = false; + for (const std::string& str : uniq) { - if (retval.size()) + if (comma) retval += _S(", "); - retval += name; + comma = true; + retval += str; } return retval; } @@ -53,20 +54,35 @@ std::string PAKBridge::getLevelString() const ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) { if (entry.type == Retro::STRG) - return {STRG::Extract, ".strg"}; + return {STRG::Extract, ".as"}; return {}; } -bool PAKBridge::extractResources(const HECL::ProjectPath& dirOut) +bool PAKBridge::extractResources(const HECL::ProjectPath& workingOut, + const HECL::ProjectPath& cookedOut, + bool force) { for (const std::pair& item : m_pak.m_idMap) { + PAKEntryReadStream s; ResExtractor extractor = LookupExtractor(*item.second); if (extractor.func) { - PAKEntryReadStream strgIn = item.second->beginReadStream(m_node); - HECL::ProjectPath resPath(dirOut, m_pak.bestEntryName(*item.second) + extractor.fileExt); - extractor.func(strgIn, resPath); + HECL::ProjectPath workPath(workingOut, m_pak.bestEntryName(*item.second) + extractor.fileExt); + if (force || workPath.getPathType() == HECL::ProjectPath::PT_NONE) + { + s = item.second->beginReadStream(m_node); + extractor.func(s, workPath); + } + } + HECL::ProjectPath cookPath(cookedOut, m_pak.bestEntryName(*item.second)); + if (force || cookPath.getPathType() == HECL::ProjectPath::PT_NONE) + { + if (!s) + s = item.second->beginReadStream(m_node); + FILE* fout = HECL::Fopen(cookPath.getAbsolutePath().c_str(), _S("wb")); + fwrite(s.data(), 1, s.length(), fout); + fclose(fout); } } return true; diff --git a/DataSpec/DNAMP3/DNAMP3.hpp b/DataSpec/DNAMP3/DNAMP3.hpp index 3f008970b..c721dd437 100644 --- a/DataSpec/DNAMP3/DNAMP3.hpp +++ b/DataSpec/DNAMP3/DNAMP3.hpp @@ -17,14 +17,14 @@ class PAKBridge HECL::Database::Project& m_project; const NOD::DiscBase::IPartition::Node& m_node; PAK m_pak; - static ResExtractor LookupExtractor(const PAK::Entry& entry); - public: PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node); const std::string& getName() const {return m_node.getName();} std::string getLevelString() const; - bool extractResources(const HECL::ProjectPath& dirOut); + bool extractResources(const HECL::ProjectPath& dirOut, + const HECL::ProjectPath& cookedOut, + bool force); }; } diff --git a/DataSpec/DNAMP3/PAK.hpp b/DataSpec/DNAMP3/PAK.hpp index c70227169..a0630c132 100644 --- a/DataSpec/DNAMP3/PAK.hpp +++ b/DataSpec/DNAMP3/PAK.hpp @@ -46,7 +46,8 @@ struct PAK : BigDNA inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const { atUint64 sz; - return PAKEntryReadStream(getBuffer(pak, sz), sz, off); + std::unique_ptr buf = getBuffer(pak, sz); + return PAKEntryReadStream(std::move(buf), sz, off); } }; @@ -81,7 +82,7 @@ struct PAK : BigDNA return nentry.name; /* Otherwise return ID format string */ - return entry.id.toString(); + return entry.type.toString() + '_' + entry.id.toString(); } }; diff --git a/DataSpec/DNAMP3/STRG.cpp b/DataSpec/DNAMP3/STRG.cpp index 75f516249..f1ab8d5d1 100644 --- a/DataSpec/DNAMP3/STRG.cpp +++ b/DataSpec/DNAMP3/STRG.cpp @@ -57,8 +57,13 @@ void STRG::_read(Athena::io::IStreamReader& reader) atUint32 len = reader.readUint32(); strs.emplace_back(reader.readString(len)); } - langs.emplace(readLangs[l], strs); + langs.emplace_back(readLangs[l], strs); } + + langMap.clear(); + langMap.reserve(langCount); + for (std::pair>& item : langs) + langMap.emplace(item.first, &item.second); } void STRG::read(Athena::io::IStreamReader& reader) @@ -148,6 +153,33 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in) void STRG::writeAngelScript(std::ofstream& out) const { + for (const std::pair>& lang : langs) + { + out << "STRG::Language " << lang.first.toString() << "({"; + bool comma = false; + unsigned idx = 0; + for (const std::string& str : lang.second) + { + if (comma) + out << ","; + out << "\n/* " << idx++ << " */ \""; + out << str << "\""; + comma = true; + } + out << "\n});\n"; + } + + out << "STRG::Names NAMES({"; + bool comma = false; + for (const std::pair& name : names) + { + if (comma) + out << ","; + out << "\n "; + comma = true; + out << "{\"" << name.first << "\", " << name.second << "}"; + } + out << "\n});\n"; } } diff --git a/DataSpec/DNAMP3/STRG.hpp b/DataSpec/DNAMP3/STRG.hpp index 3cb750774..bf74ad9fd 100644 --- a/DataSpec/DNAMP3/STRG.hpp +++ b/DataSpec/DNAMP3/STRG.hpp @@ -14,7 +14,8 @@ struct STRG : ISTRG, BigDNA { DECL_EXPLICIT_DNA void _read(Athena::io::IStreamReader& reader); - std::unordered_map> langs; + std::vector>> langs; + std::unordered_map*> langMap; std::map names; inline int32_t lookupIdx(const std::string& name) const @@ -38,32 +39,40 @@ struct STRG : ISTRG, BigDNA } 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); + auto search = langMap.find(lang); + if (search != langMap.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)); + auto search = langMap.find(lang); + if (search != langMap.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()) + auto search = langMap.find(lang); + if (search != langMap.end()) #if HECL_UCS2 - return HECL::UTF8ToWide(search->second.at(idx)); + return HECL::UTF8ToWide(search->second->at(idx)); #else - return search->second.at(idx); + return search->second->at(idx); #endif return std::string(); } bool readAngelScript(const AngelScript::asIScriptModule& in); void writeAngelScript(std::ofstream& out) const; + + static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) + { + std::unique_ptr strg = LoadSTRG(rs); + std::ofstream strgOut(outPath.getAbsolutePath()); + strg->writeAngelScript(strgOut); + return true; + } }; } diff --git a/DataSpec/SpecBase.cpp b/DataSpec/SpecBase.cpp index 06c1eed5a..d6fdf6c20 100644 --- a/DataSpec/SpecBase.cpp +++ b/DataSpec/SpecBase.cpp @@ -6,17 +6,16 @@ namespace Retro bool SpecBase::canExtract(HECL::Database::Project& project, const ExtractPassInfo& info, std::vector& reps) { - bool isWii; - m_disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), isWii); + m_disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), m_isWii); if (!m_disc) return false; const char* gameID = m_disc->getHeader().gameID; - bool standalone = true; - if (isWii && !memcmp(gameID, "R3M", 3)) - standalone = false; + m_standalone = true; + if (m_isWii && !memcmp(gameID, "R3M", 3)) + m_standalone = false; - if (standalone && !checkStandaloneID(gameID)) + if (m_standalone && !checkStandaloneID(gameID)) return false; char region = m_disc->getHeader().gameID[3]; @@ -38,15 +37,32 @@ bool SpecBase::canExtract(HECL::Database::Project& project, break; } - if (standalone) + if (m_standalone) return checkFromStandaloneDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps); else return checkFromTrilogyDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps); } -void SpecBase::doExtract(HECL::Database::Project& project, const ExtractPassInfo&) +void SpecBase::doExtract(HECL::Database::Project& project, const ExtractPassInfo& info) { - extractFromDisc(project, *m_disc.get()); + if (m_isWii) + { + /* Extract update partition for repacking later */ + const HECL::SystemString& target = project.getProjectRootPath().getAbsolutePath(); + NOD::DiscBase::IPartition* update = m_disc->getUpdatePartition(); + if (update) + update->getFSTRoot().extractToDirectory(target, info.force); + + if (!m_standalone) + { + NOD::DiscBase::IPartition* data = m_disc->getDataPartition(); + const NOD::DiscBase::IPartition::Node& root = data->getFSTRoot(); + for (const NOD::DiscBase::IPartition::Node& child : root) + if (child.getKind() == NOD::DiscBase::IPartition::Node::NODE_FILE) + child.extractToDirectory(target, info.force); + } + } + extractFromDisc(project, *m_disc.get(), info.force); } bool SpecBase::canCook(const HECL::Database::Project& project, const CookTaskInfo& info) diff --git a/DataSpec/SpecBase.hpp b/DataSpec/SpecBase.hpp index 1035289dd..991187b97 100644 --- a/DataSpec/SpecBase.hpp +++ b/DataSpec/SpecBase.hpp @@ -34,7 +34,7 @@ struct SpecBase : HECL::Database::IDataSpec const HECL::SystemString& regstr, const std::vector& args, std::vector& reps)=0; - virtual bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc)=0; + virtual bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc, bool force)=0; virtual bool checkFromProject(HECL::Database::Project& proj)=0; virtual bool readFromProject(HECL::Database::Project& proj)=0; @@ -53,6 +53,8 @@ struct SpecBase : HECL::Database::IDataSpec private: std::unique_ptr m_disc; + bool m_isWii; + bool m_standalone; }; } diff --git a/DataSpec/SpecMP1.cpp b/DataSpec/SpecMP1.cpp index 085cbfc8d..41b6893e6 100644 --- a/DataSpec/SpecMP1.cpp +++ b/DataSpec/SpecMP1.cpp @@ -8,6 +8,7 @@ namespace Retro { static LogVisor::LogModule Log("Retro::SpecMP1"); +extern HECL::Database::DataSpecEntry SpecEntMP1; struct SpecMP1 : SpecBase { @@ -18,6 +19,8 @@ struct SpecMP1 : SpecBase return false; } + bool doMP1 = false; + std::vector m_nonPaks; std::vector m_paks; std::map m_orderedPaks; @@ -26,9 +29,11 @@ struct SpecMP1 : SpecBase const std::vector& args, ExtractReport& rep) { + m_nonPaks.clear(); m_paks.clear(); for (const NOD::DiscBase::IPartition::Node& child : root) { + bool isPak = false; const std::string& name = child.getName(); std::string lowerName = name; std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); @@ -38,6 +43,7 @@ struct SpecMP1 : SpecBase if (!std::string(extit, lowerName.end()).compare(".pak")) { /* This is a pak */ + isPak = true; std::string lowerBase(lowerName.begin(), extit); /* Needs filter */ @@ -79,6 +85,9 @@ struct SpecMP1 : SpecBase } } + + if (!isPak) + m_nonPaks.push_back(&child); } /* Sort PAKs alphabetically */ @@ -102,6 +111,7 @@ struct SpecMP1 : SpecBase const std::vector& args, std::vector& reps) { + doMP1 = true; NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); std::unique_ptr dolBuf = partition->getDOLBuf(); const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; @@ -115,7 +125,7 @@ struct SpecMP1 : SpecBase { std::string buildStr(buildInfo); HECL::SystemStringView buildView(buildStr); - rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + rep.desc += _S(" (") + buildView + _S(")"); } /* Iterate PAKs and build level options */ @@ -137,18 +147,24 @@ struct SpecMP1 : SpecBase /* 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; + HECL::ToLower(lowerArg); + if (!lowerArg.compare(0, 3, "mp1")) { - HECL::SystemString lowerArg(arg.begin(), arg.begin() + slashPos); - HECL::ToLower(lowerArg); - if (!lowerArg.compare(_S("mp1"))) + doMP1 = true; + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) mp1args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); } } } + else + doMP1 = true; + + if (!doMP1) + return true; NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); @@ -168,7 +184,7 @@ struct SpecMP1 : SpecBase { std::string buildStr(buildInfo); HECL::SystemStringView buildView(buildStr); - rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + rep.desc += _S(" (") + buildView + _S(")"); } /* Iterate PAKs and build level options */ @@ -180,9 +196,20 @@ struct SpecMP1 : SpecBase return true; } - bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc) + bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force) { - HECL::ProjectPath mp1Path(project.getProjectRootPath(), "MP1"); + if (!doMP1) + return true; + + HECL::ProjectPath mp1WorkPath(project.getProjectRootPath(), "MP1"); + mp1WorkPath.makeDir(); + for (const NOD::DiscBase::IPartition::Node* node : m_nonPaks) + node->extractToDirectory(mp1WorkPath.getAbsolutePath(), force); + + const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP1); + cookPath.makeDir(); + HECL::ProjectPath mp1CookPath(cookPath, "MP1"); + mp1CookPath.makeDir(); for (DNAMP1::PAKBridge& pak : m_paks) { @@ -190,8 +217,11 @@ struct SpecMP1 : SpecBase std::string::const_iterator extit = name.end() - 4; std::string baseName(name.begin(), extit); - HECL::ProjectPath pakPath(mp1Path, baseName); - pak.extractResources(pakPath); + HECL::ProjectPath pakWorkPath(mp1WorkPath, baseName); + pakWorkPath.makeDir(); + HECL::ProjectPath pakCookPath(mp1CookPath, baseName); + pakCookPath.makeDir(); + pak.extractResources(pakWorkPath, pakCookPath, force); } return true; @@ -227,7 +257,7 @@ struct SpecMP1 : SpecBase } }; -HECL::Database::DataSpecEntry SpecMP1 = +HECL::Database::DataSpecEntry SpecEntMP1 = { _S("MP1"), _S("Data specification for original Metroid Prime engine"), diff --git a/DataSpec/SpecMP2.cpp b/DataSpec/SpecMP2.cpp index 85588a6d1..b48dbf321 100644 --- a/DataSpec/SpecMP2.cpp +++ b/DataSpec/SpecMP2.cpp @@ -7,6 +7,7 @@ namespace Retro { static LogVisor::LogModule Log("Retro::SpecMP2"); +extern HECL::Database::DataSpecEntry SpecEntMP2; struct SpecMP2 : SpecBase { @@ -17,6 +18,8 @@ struct SpecMP2 : SpecBase return false; } + bool doMP2 = false; + std::vector m_nonPaks; std::vector m_paks; std::map m_orderedPaks; @@ -25,9 +28,11 @@ struct SpecMP2 : SpecBase const std::vector& args, ExtractReport& rep) { + m_nonPaks.clear(); m_paks.clear(); for (const NOD::DiscBase::IPartition::Node& child : root) { + bool isPak = false; const std::string& name = child.getName(); std::string lowerName = name; std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); @@ -37,6 +42,7 @@ struct SpecMP2 : SpecBase if (!std::string(extit, lowerName.end()).compare(".pak")) { /* This is a pak */ + isPak = true; std::string lowerBase(lowerName.begin(), extit); /* Needs filter */ @@ -77,6 +83,9 @@ struct SpecMP2 : SpecBase m_paks.emplace_back(project, child); } } + + if (!isPak) + m_nonPaks.push_back(&child); } /* Sort PAKs alphabetically */ @@ -100,6 +109,7 @@ struct SpecMP2 : SpecBase const std::vector& args, std::vector& reps) { + doMP2 = true; NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); std::unique_ptr dolBuf = partition->getDOLBuf(); const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; @@ -113,7 +123,7 @@ struct SpecMP2 : SpecBase { std::string buildStr(buildInfo); HECL::SystemStringView buildView(buildStr); - rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + rep.desc += _S(" (") + buildView + _S(")"); } /* Iterate PAKs and build level options */ @@ -135,18 +145,24 @@ struct SpecMP2 : SpecBase /* 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; + HECL::ToLower(lowerArg); + if (!lowerArg.compare(0, 3, "mp2")) { - HECL::SystemString lowerArg(arg.begin(), arg.begin() + slashPos); - HECL::ToLower(lowerArg); - if (!lowerArg.compare(_S("mp2"))) + doMP2 = true; + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) mp2args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); } } } + else + doMP2 = true; + + if (!doMP2) + return true; NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); @@ -166,7 +182,7 @@ struct SpecMP2 : SpecBase { std::string buildStr(buildInfo); HECL::SystemStringView buildView(buildStr); - rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + rep.desc += _S(" (") + buildView + _S(")"); } /* Iterate PAKs and build level options */ @@ -178,8 +194,35 @@ struct SpecMP2 : SpecBase return true; } - bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc) + bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force) { + if (!doMP2) + return true; + + HECL::ProjectPath mp2WorkPath(project.getProjectRootPath(), "MP2"); + mp2WorkPath.makeDir(); + for (const NOD::DiscBase::IPartition::Node* node : m_nonPaks) + node->extractToDirectory(mp2WorkPath.getAbsolutePath(), force); + + const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP2); + cookPath.makeDir(); + HECL::ProjectPath mp2CookPath(cookPath, "MP2"); + mp2CookPath.makeDir(); + + for (DNAMP2::PAKBridge& pak : m_paks) + { + const std::string& name = pak.getName(); + std::string::const_iterator extit = name.end() - 4; + std::string baseName(name.begin(), extit); + + HECL::ProjectPath pakWorkPath(mp2WorkPath, baseName); + pakWorkPath.makeDir(); + HECL::ProjectPath pakCookPath(mp2CookPath, baseName); + pakCookPath.makeDir(); + pak.extractResources(pakWorkPath, pakCookPath, force); + } + + return true; } bool checkFromProject(HECL::Database::Project& proj) @@ -212,7 +255,7 @@ struct SpecMP2 : SpecBase } }; -static HECL::Database::DataSpecEntry SpecMP2 +HECL::Database::DataSpecEntry SpecEntMP2 ( _S("MP2"), _S("Data specification for original Metroid Prime 2 engine"), diff --git a/DataSpec/SpecMP3.cpp b/DataSpec/SpecMP3.cpp index 74896531a..66892ebc6 100644 --- a/DataSpec/SpecMP3.cpp +++ b/DataSpec/SpecMP3.cpp @@ -8,6 +8,7 @@ namespace Retro { static LogVisor::LogModule Log("Retro::SpecMP3"); +extern HECL::Database::DataSpecEntry SpecEntMP3; struct SpecMP3 : SpecBase { @@ -18,17 +19,36 @@ struct SpecMP3 : SpecBase return false; } + bool doMP3 = false; + std::vector m_nonPaks; std::vector m_paks; std::map m_orderedPaks; + /* These are populated when extracting MPT's frontend (uses MP3's DataSpec) */ + bool doMPTFE = false; + std::vector m_feNonPaks; + std::vector m_fePaks; + std::map m_feOrderedPaks; + void buildPaks(HECL::Database::Project& project, NOD::DiscBase::IPartition::Node& root, const std::vector& args, - ExtractReport& rep) + ExtractReport& rep, + bool fe) { - m_paks.clear(); + if (fe) + { + m_feNonPaks.clear(); + m_fePaks.clear(); + } + else + { + m_nonPaks.clear(); + m_paks.clear(); + } for (const NOD::DiscBase::IPartition::Node& child : root) { + bool isPak = false; const std::string& name = child.getName(); std::string lowerName = name; std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); @@ -38,6 +58,7 @@ struct SpecMP3 : SpecBase if (!std::string(extit, lowerName.end()).compare(".pak")) { /* This is a pak */ + isPak = true; std::string lowerBase(lowerName.begin(), extit); /* Needs filter */ @@ -75,18 +96,40 @@ struct SpecMP3 : SpecBase } if (good) - m_paks.emplace_back(project, child); + { + if (fe) + m_fePaks.emplace_back(project, child); + else + m_paks.emplace_back(project, child); + } } } + + if (!isPak) + { + if (fe) + m_feNonPaks.push_back(&child); + else + m_nonPaks.push_back(&child); + } } /* Sort PAKs alphabetically */ - m_orderedPaks.clear(); - for (DNAMP3::PAKBridge& dpak : m_paks) - m_orderedPaks[dpak.getName()] = &dpak; + if (fe) + { + m_feOrderedPaks.clear(); + for (DNAMP3::PAKBridge& dpak : m_fePaks) + m_feOrderedPaks[dpak.getName()] = &dpak; + } + else + { + m_orderedPaks.clear(); + for (DNAMP3::PAKBridge& dpak : m_paks) + m_orderedPaks[dpak.getName()] = &dpak; + } /* Assemble extract report */ - for (const std::pair& item : m_orderedPaks) + for (const std::pair& item : fe ? m_feOrderedPaks : m_orderedPaks) { rep.childOpts.emplace_back(); ExtractReport& childRep = rep.childOpts.back(); @@ -109,6 +152,7 @@ struct SpecMP3 : SpecBase const std::vector& args, std::vector& reps) { + doMP3 = true; NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); std::unique_ptr dolBuf = partition->getDOLBuf(); const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; @@ -122,12 +166,12 @@ struct SpecMP3 : SpecBase { std::string buildStr(buildInfo); HECL::SystemStringView buildView(buildStr); - rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + rep.desc += _S(" (") + buildView + _S(")"); } /* Iterate PAKs and build level options */ NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot(); - buildPaks(project, root, args, rep); + buildPaks(project, root, args, rep, false); return true; } @@ -139,56 +183,165 @@ struct SpecMP3 : SpecBase std::vector& reps) { std::vector mp3args; + std::vector feargs; 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; + HECL::ToLower(lowerArg); + if (!lowerArg.compare(0, 3, "mp3")) { - HECL::SystemString lowerArg(arg.begin(), arg.begin() + slashPos); - HECL::ToLower(lowerArg); - if (!lowerArg.compare(_S("mp3"))) + doMP3 = true; + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) mp3args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); } } + + for (const HECL::SystemString& arg : args) + { + HECL::SystemString lowerArg = arg; + HECL::ToLower(lowerArg); + if (!lowerArg.compare(0, 2, "fe")) + { + doMPTFE = true; + size_t slashPos = arg.find(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = arg.find(_S('\\')); + if (slashPos != HECL::SystemString::npos) + feargs.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end())); + } + } + } + else + { + doMP3 = true; + doMPTFE = true; } 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) + /* MP3 extract */ + if (doMP3) { - std::string buildStr(buildInfo); - HECL::SystemStringView buildView(buildStr); - rep.desc += _S(" (") + buildView.sys_str() + _S(")"); + 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 + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node::DirectoryIterator mp3It = root.find("MP3"); + if (mp3It == root.end()) + return false; + buildPaks(project, *mp3It, mp3args, rep, false); } - /* Iterate PAKs and build level options */ - NOD::DiscBase::IPartition::Node::DirectoryIterator mp3It = root.find("MP3"); - if (mp3It == root.end()) - return false; - buildPaks(project, *mp3It, mp3args, rep); + /* MPT Frontend extract */ + if (doMPTFE) + { + NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5fe_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("fe"); + rep.desc = _S("Metroid Prime Trilogy Frontend ") + regstr; + if (buildInfo) + { + std::string buildStr(buildInfo); + HECL::SystemStringView buildView(buildStr); + rep.desc += _S(" (") + buildView + _S(")"); + } + + /* Iterate PAKs and build level options */ + NOD::DiscBase::IPartition::Node::DirectoryIterator feIt = root.find("fe"); + if (feIt == root.end()) + return false; + buildPaks(project, *feIt, feargs, rep, true); + } return true; } - bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc) + bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force) { + if (doMP3) + { + HECL::ProjectPath mp3WorkPath(project.getProjectRootPath(), "MP3"); + mp3WorkPath.makeDir(); + for (const NOD::DiscBase::IPartition::Node* node : m_nonPaks) + node->extractToDirectory(mp3WorkPath.getAbsolutePath(), force); + + const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP3); + cookPath.makeDir(); + HECL::ProjectPath mp3CookPath(cookPath, "MP3"); + mp3CookPath.makeDir(); + + for (DNAMP3::PAKBridge& pak : m_paks) + { + const std::string& name = pak.getName(); + std::string::const_iterator extit = name.end() - 4; + std::string baseName(name.begin(), extit); + + HECL::ProjectPath pakWorkPath(mp3WorkPath, baseName); + pakWorkPath.makeDir(); + HECL::ProjectPath pakCookPath(mp3CookPath, baseName); + pakCookPath.makeDir(); + pak.extractResources(pakWorkPath, pakCookPath, force); + } + } + + if (doMPTFE) + { + HECL::ProjectPath feWorkPath(project.getProjectRootPath(), "fe"); + feWorkPath.makeDir(); + for (const NOD::DiscBase::IPartition::Node* node : m_feNonPaks) + node->extractToDirectory(feWorkPath.getAbsolutePath(), force); + + const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP3); + cookPath.makeDir(); + HECL::ProjectPath feCookPath(cookPath, "fe"); + feCookPath.makeDir(); + + for (DNAMP3::PAKBridge& pak : m_fePaks) + { + const std::string& name = pak.getName(); + std::string::const_iterator extit = name.end() - 4; + std::string baseName(name.begin(), extit); + + HECL::ProjectPath pakWorkPath(feWorkPath, baseName); + pakWorkPath.makeDir(); + HECL::ProjectPath pakCookPath(feCookPath, baseName); + pakCookPath.makeDir(); + pak.extractResources(pakWorkPath, pakCookPath, force); + } + } + + return true; } bool checkFromProject(HECL::Database::Project& proj) @@ -221,7 +374,7 @@ struct SpecMP3 : SpecBase } }; -static HECL::Database::DataSpecEntry SpecMP3 +HECL::Database::DataSpecEntry SpecEntMP3 ( _S("MP3"), _S("Data specification for original Metroid Prime 3 engine"), diff --git a/NODLib b/NODLib index bcf67ca8e..f2c1ea646 160000 --- a/NODLib +++ b/NODLib @@ -1 +1 @@ -Subproject commit bcf67ca8eb94acbf3834fe01de033a4382626a13 +Subproject commit f2c1ea646900f7fcca12b45878923014816295e9