diff --git a/DataSpec/CMakeLists.txt b/DataSpec/CMakeLists.txt index c76e59d7a..f6c61ee60 100644 --- a/DataSpec/CMakeLists.txt +++ b/DataSpec/CMakeLists.txt @@ -24,6 +24,3 @@ add_library(RetroDataSpec SpecMP1.cpp SpecMP2.cpp SpecMP3.cpp) -target_link_libraries(RetroDataSpec - DNAMP1 - DNAMP3) diff --git a/DataSpec/DNACommon/DNACommon.cpp b/DataSpec/DNACommon/DNACommon.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/DataSpec/DNACommon/DNACommon.hpp b/DataSpec/DNACommon/DNACommon.hpp index 0a7ec8556..b76e4aa43 100644 --- a/DataSpec/DNACommon/DNACommon.hpp +++ b/DataSpec/DNACommon/DNACommon.hpp @@ -107,6 +107,20 @@ public: } }; +struct CaseInsensitiveCompare +{ + inline bool operator()(const std::string& lhs, const std::string& rhs) const + { + std::string lhsl = lhs; + std::transform(lhsl.begin(), lhsl.end(), lhsl.begin(), tolower); + std::string rhsl = rhs; + std::transform(rhsl.begin(), rhsl.end(), rhsl.begin(), tolower); + if (lhsl.compare(rhsl) < 0) + return true; + return false; + } +}; + } /* Hash template-specializations for UniqueID types */ diff --git a/DataSpec/DNAMP1/CMakeLists.txt b/DataSpec/DNAMP1/CMakeLists.txt index 3c3a1ab08..42cbca662 100644 --- a/DataSpec/DNAMP1/CMakeLists.txt +++ b/DataSpec/DNAMP1/CMakeLists.txt @@ -1,4 +1,7 @@ make_dnalist(liblist PAK - MLVL) -add_library(DNAMP1 ${liblist}) + MLVL + STRG) +add_library(DNAMP1 ${liblist} + PAK.cpp + STRG.cpp) diff --git a/DataSpec/DNAMP1/PAK.cpp b/DataSpec/DNAMP1/PAK.cpp index e603bab94..a8394fffc 100644 --- a/DataSpec/DNAMP1/PAK.cpp +++ b/DataSpec/DNAMP1/PAK.cpp @@ -1,65 +1,66 @@ -/* Auto generated atdna implementation */ -#include -#include -#include - #include "PAK.hpp" -void Retro::DNAMP1::PAK::NameEntry::read(Athena::io::IStreamReader& __dna_reader) +namespace Retro { - /* type */ - type.read(__dna_reader); - /* id */ - id.read(__dna_reader); - __dna_reader.setEndian(Athena::BigEndian); - /* nameLen */ - nameLen = __dna_reader.readUint32(); - /* name */ - name = __dna_reader.readString(nameLen); +namespace DNAMP1 +{ + +void PAK::read(Athena::io::IStreamReader& reader) +{ + reader.setEndian(Athena::BigEndian); + atUint32 version = reader.readUint32(); + if (version != 0x00030005) + LogModule.report(LogVisor::FatalError, "unexpected PAK magic"); + reader.readUint32(); + + atUint32 nameCount = reader.readUint32(); + m_nameEntries.clear(); + m_nameEntries.reserve(nameCount); + for (atUint32 n=0 ; n::iterator found = m_idMap.find(entry.id); + if (found != m_idMap.end()) + m_nameMap[entry.name] = found->second; + } } -void Retro::DNAMP1::PAK::NameEntry::write(Athena::io::IStreamWriter& __dna_writer) const +void PAK::write(Athena::io::IStreamWriter& writer) const { - /* type */ - type.write(__dna_writer); - /* id */ - id.write(__dna_writer); - __dna_writer.setEndian(Athena::BigEndian); - /* nameLen */ - __dna_writer.writeUint32(nameLen); - /* name */ - __dna_writer.writeString(name, nameLen); + writer.setEndian(Athena::BigEndian); + writer.writeUint32(0x00030005); + writer.writeUint32(0); + + writer.writeUint32(m_nameEntries.size()); + for (const NameEntry& entry : m_nameEntries) + { + ((NameEntry&)entry).nameLen = entry.name.size(); + entry.write(writer); + } + + writer.writeUint32(m_entries.size()); + for (const Entry& entry : m_entries) + entry.write(writer); } -void Retro::DNAMP1::PAK::Entry::read(Athena::io::IStreamReader& __dna_reader) -{ - __dna_reader.setEndian(Athena::BigEndian); - /* compressed */ - compressed = __dna_reader.readUint32(); - /* type */ - type.read(__dna_reader); - /* id */ - id.read(__dna_reader); - __dna_reader.setEndian(Athena::BigEndian); - /* size */ - size = __dna_reader.readUint32(); - /* offset */ - offset = __dna_reader.readUint32(); } - -void Retro::DNAMP1::PAK::Entry::write(Athena::io::IStreamWriter& __dna_writer) const -{ - __dna_writer.setEndian(Athena::BigEndian); - /* compressed */ - __dna_writer.writeUint32(compressed); - /* type */ - type.write(__dna_writer); - /* id */ - id.write(__dna_writer); - __dna_writer.setEndian(Athena::BigEndian); - /* size */ - __dna_writer.writeUint32(size); - /* offset */ - __dna_writer.writeUint32(offset); } - diff --git a/DataSpec/DNAMP1/PAK.hpp b/DataSpec/DNAMP1/PAK.hpp index e911d2346..14a84dca9 100644 --- a/DataSpec/DNAMP1/PAK.hpp +++ b/DataSpec/DNAMP1/PAK.hpp @@ -38,64 +38,9 @@ private: std::vector m_entries; std::unordered_map m_idMap; std::unordered_map m_nameMap; - Delete expl; public: - void read(Athena::io::IStreamReader& reader) - { - reader.setEndian(Athena::BigEndian); - atUint32 version = reader.readUint32(); - if (version != 0x00030005) - LogModule.report(LogVisor::FatalError, "unexpected PAK magic"); - reader.readUint32(); - - atUint32 nameCount = reader.readUint32(); - m_nameEntries.clear(); - m_nameEntries.reserve(nameCount); - for (atUint32 n=0 ; n::iterator found = m_idMap.find(entry.id); - if (found != m_idMap.end()) - m_nameMap[entry.name] = found->second; - } - } - void write(Athena::io::IStreamWriter& writer) const - { - writer.setEndian(Athena::BigEndian); - writer.writeUint32(0x00030005); - writer.writeUint32(0); - - writer.writeUint32(m_nameEntries.size()); - for (const NameEntry& entry : m_nameEntries) - { - ((NameEntry&)entry).nameLen = entry.name.size(); - entry.write(writer); - } - - writer.writeUint32(m_entries.size()); - for (const Entry& entry : m_entries) - entry.write(writer); - } + DECL_EXPLICIT_DNA inline const Entry* lookupEntry(const UniqueID32& id) const { @@ -112,6 +57,9 @@ public: return result->second; return nullptr; } + + inline std::vector::iterator begin() {return m_entries.begin();} + inline std::vector::iterator end() {return m_entries.end();} }; } diff --git a/DataSpec/DNAMP1/STRG.cpp b/DataSpec/DNAMP1/STRG.cpp new file mode 100644 index 000000000..b6ec8be4d --- /dev/null +++ b/DataSpec/DNAMP1/STRG.cpp @@ -0,0 +1,92 @@ +#include "STRG.hpp" +#include "../Logging.hpp" + +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) +{ + uint32_t magic = reader.readUint32(); + if (magic != 0x87654321) + LogModule.report(LogVisor::FatalError, "invalid STRG magic"); + + version = reader.readUint32(); + langCount = reader.readUint32(); + strCount = reader.readUint32(); + + langs.clear(); + langs.reserve(langCount); + for (uint32_t l=0 ; l strings; + }; + std::vector langs; +}; + +} +} + +#endif // __DNAMP1_STRG_HPP__ diff --git a/DataSpec/DNAMP2/CMakeLists.txt b/DataSpec/DNAMP2/CMakeLists.txt index 8b1378917..8b7874c19 100644 --- a/DataSpec/DNAMP2/CMakeLists.txt +++ b/DataSpec/DNAMP2/CMakeLists.txt @@ -1 +1,3 @@ - +make_dnalist(liblist + MLVL) +add_library(DNAMP2 ${liblist}) diff --git a/DataSpec/DNAMP2/MLVL.hpp b/DataSpec/DNAMP2/MLVL.hpp new file mode 100644 index 000000000..b39e95ea0 --- /dev/null +++ b/DataSpec/DNAMP2/MLVL.hpp @@ -0,0 +1,94 @@ +#include "../DNACommon/DNACommon.hpp" + +namespace Retro +{ +namespace DNAMP2 +{ + +struct MLVL : BigDNA +{ + DECL_DNA + HECL::FourCC magic; + Value version; + UniqueID32 worldNameId; + UniqueID32 darkWorldNameId; + Value unk; + UniqueID32 saveWorldId; + UniqueID32 worldSkyboxId; + + Value areaCount; + struct Area : BigDNA + { + DECL_DNA + UniqueID32 areaNameId; + Value transformMtx[3]; + Value aabb[2]; + UniqueID32 areaMREAId; + Value areaId; + + Value attachedAreaCount; + Vector attachedAreas; + Value padding; + + Value depCount; + struct Dependency : BigDNA + { + DECL_DNA + UniqueID32 id; + HECL::FourCC type; + }; + Vector deps; + + Value depLayerCount; + Vector depLayers; + + Value dockCount; + struct Dock : BigDNA + { + DECL_DNA + Value endpointCount; + struct Endpoint : BigDNA + { + DECL_DNA + Value areaIdx; + Value dockIdx; + }; + HECL::FourCC type; + Vector endpoints; + + Value planeVertCount; + Vector planeVerts; + }; + Vector docks; + + Value relCount; + Vector, DNA_COUNT(relCount)> relFilenames; + Value relOffsetCount; + Vector relOffsets; + + Value unk1; + String<-1> internalAreaName; + }; + + UniqueID32 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 layerNameOffsetCount; + Vector layerNameOffsets; +}; + +} +} diff --git a/DataSpec/DNAMP3/CMakeLists.txt b/DataSpec/DNAMP3/CMakeLists.txt index 8f7502e60..4eca7750b 100644 --- a/DataSpec/DNAMP3/CMakeLists.txt +++ b/DataSpec/DNAMP3/CMakeLists.txt @@ -1,3 +1,4 @@ make_dnalist(liblist PAK) -add_library(DNAMP3 ${liblist}) +add_library(DNAMP3 ${liblist} + PAK.cpp) diff --git a/DataSpec/DNAMP3/PAK.cpp b/DataSpec/DNAMP3/PAK.cpp index c153a1b6d..8385eec4a 100644 --- a/DataSpec/DNAMP3/PAK.cpp +++ b/DataSpec/DNAMP3/PAK.cpp @@ -1,145 +1,93 @@ -/* Auto generated atdna implementation */ -#include -#include -#include - #include "PAK.hpp" -void Retro::DNAMP3::PAK::Header::read(Athena::io::IStreamReader& __dna_reader) +namespace Retro { - __dna_reader.setEndian(Athena::BigEndian); - /* version */ - version = __dna_reader.readUint32(); - /* headSz */ - headSz = __dna_reader.readUint32(); - /* md5sum[0] */ - md5sum[0] = __dna_reader.readUByte(); - /* md5sum[1] */ - md5sum[1] = __dna_reader.readUByte(); - /* md5sum[2] */ - md5sum[2] = __dna_reader.readUByte(); - /* md5sum[3] */ - md5sum[3] = __dna_reader.readUByte(); - /* md5sum[4] */ - md5sum[4] = __dna_reader.readUByte(); - /* md5sum[5] */ - md5sum[5] = __dna_reader.readUByte(); - /* md5sum[6] */ - md5sum[6] = __dna_reader.readUByte(); - /* md5sum[7] */ - md5sum[7] = __dna_reader.readUByte(); - /* md5sum[8] */ - md5sum[8] = __dna_reader.readUByte(); - /* md5sum[9] */ - md5sum[9] = __dna_reader.readUByte(); - /* md5sum[10] */ - md5sum[10] = __dna_reader.readUByte(); - /* md5sum[11] */ - md5sum[11] = __dna_reader.readUByte(); - /* md5sum[12] */ - md5sum[12] = __dna_reader.readUByte(); - /* md5sum[13] */ - md5sum[13] = __dna_reader.readUByte(); - /* md5sum[14] */ - md5sum[14] = __dna_reader.readUByte(); - /* md5sum[15] */ - md5sum[15] = __dna_reader.readUByte(); - /* seek */ - __dna_reader.seek(40, Athena::Current); +namespace DNAMP3 +{ + +void PAK::read(Athena::io::IStreamReader& reader) +{ + reader.setEndian(Athena::BigEndian); + m_header.read(reader); + if (m_header.version != 2) + LogModule.report(LogVisor::FatalError, "unexpected PAK magic"); + + reader.seek(8, Athena::Current); + atUint32 strgSz = reader.readUint32(); + reader.seek(4, Athena::Current); + atUint32 rshdSz = reader.readUint32(); + reader.seek(44, Athena::Current); + m_dataOffset = 128 + strgSz + rshdSz; + + atUint32 nameCount = reader.readUint32(); + m_nameEntries.clear(); + m_nameEntries.reserve(nameCount); + for (atUint32 n=0 ; n::iterator found = m_idMap.find(entry.id); + if (found != m_idMap.end()) + m_nameMap[entry.name] = found->second; + } +} +void PAK::write(Athena::io::IStreamWriter& writer) const +{ + writer.setEndian(Athena::BigEndian); + m_header.write(writer); + + HECL::FourCC("STRG").write(writer); + atUint32 strgSz = 4; + for (const NameEntry& entry : m_nameEntries) + strgSz += entry.name.size() + 13; + atUint32 strgPad = ((strgSz + 63) & ~63) - strgSz; + strgSz += strgPad; + writer.writeUint32(strgSz); + + HECL::FourCC("RSHD").write(writer); + atUint32 rshdSz = 4 + 24 * m_entries.size(); + atUint32 rshdPad = ((rshdSz + 63) & ~63) - rshdSz; + rshdSz += rshdPad; + writer.writeUint32(rshdSz); + + HECL::FourCC("DATA").write(writer); + atUint32 dataSz = 0; + for (const Entry& entry : m_entries) + dataSz += (entry.size + 63) & ~63; + atUint32 dataPad = ((dataSz + 63) & ~63) - dataSz; + dataSz += dataPad; + writer.writeUint32(dataSz); + writer.seek(36, Athena::Current); + + writer.writeUint32(m_nameEntries.size()); + for (const NameEntry& entry : m_nameEntries) + entry.write(writer); + writer.seek(strgPad, Athena::Current); + + writer.writeUint32(m_entries.size()); + for (const Entry& entry : m_entries) + entry.write(writer); + writer.seek(rshdPad, Athena::Current); } -void Retro::DNAMP3::PAK::Header::write(Athena::io::IStreamWriter& __dna_writer) const -{ - __dna_writer.setEndian(Athena::BigEndian); - /* version */ - __dna_writer.writeUint32(version); - /* headSz */ - __dna_writer.writeUint32(headSz); - /* md5sum[0] */ - __dna_writer.writeUByte(md5sum[0]); - /* md5sum[1] */ - __dna_writer.writeUByte(md5sum[1]); - /* md5sum[2] */ - __dna_writer.writeUByte(md5sum[2]); - /* md5sum[3] */ - __dna_writer.writeUByte(md5sum[3]); - /* md5sum[4] */ - __dna_writer.writeUByte(md5sum[4]); - /* md5sum[5] */ - __dna_writer.writeUByte(md5sum[5]); - /* md5sum[6] */ - __dna_writer.writeUByte(md5sum[6]); - /* md5sum[7] */ - __dna_writer.writeUByte(md5sum[7]); - /* md5sum[8] */ - __dna_writer.writeUByte(md5sum[8]); - /* md5sum[9] */ - __dna_writer.writeUByte(md5sum[9]); - /* md5sum[10] */ - __dna_writer.writeUByte(md5sum[10]); - /* md5sum[11] */ - __dna_writer.writeUByte(md5sum[11]); - /* md5sum[12] */ - __dna_writer.writeUByte(md5sum[12]); - /* md5sum[13] */ - __dna_writer.writeUByte(md5sum[13]); - /* md5sum[14] */ - __dna_writer.writeUByte(md5sum[14]); - /* md5sum[15] */ - __dna_writer.writeUByte(md5sum[15]); - /* seek */ - __dna_writer.seek(40, Athena::Current); } - -void Retro::DNAMP3::PAK::NameEntry::read(Athena::io::IStreamReader& __dna_reader) -{ - /* name */ - name = __dna_reader.readString(-1); - /* type */ - type.read(__dna_reader); - /* id */ - id.read(__dna_reader); } - -void Retro::DNAMP3::PAK::NameEntry::write(Athena::io::IStreamWriter& __dna_writer) const -{ - /* name */ - __dna_writer.writeString(name, -1); - /* type */ - type.write(__dna_writer); - /* id */ - id.write(__dna_writer); -} - -void Retro::DNAMP3::PAK::Entry::read(Athena::io::IStreamReader& __dna_reader) -{ - __dna_reader.setEndian(Athena::BigEndian); - /* compressed */ - compressed = __dna_reader.readUint32(); - /* type */ - type.read(__dna_reader); - /* id */ - id.read(__dna_reader); - __dna_reader.setEndian(Athena::BigEndian); - /* size */ - size = __dna_reader.readUint32(); - /* offset */ - offset = __dna_reader.readUint32(); -} - -void Retro::DNAMP3::PAK::Entry::write(Athena::io::IStreamWriter& __dna_writer) const -{ - __dna_writer.setEndian(Athena::BigEndian); - /* compressed */ - __dna_writer.writeUint32(compressed); - /* type */ - type.write(__dna_writer); - /* id */ - id.write(__dna_writer); - __dna_writer.setEndian(Athena::BigEndian); - /* size */ - __dna_writer.writeUint32(size); - /* offset */ - __dna_writer.writeUint32(offset); -} - diff --git a/DataSpec/DNAMP3/PAK.hpp b/DataSpec/DNAMP3/PAK.hpp index bf354e06d..1d0142ebb 100644 --- a/DataSpec/DNAMP3/PAK.hpp +++ b/DataSpec/DNAMP3/PAK.hpp @@ -47,92 +47,9 @@ private: std::unordered_map m_idMap; std::unordered_map m_nameMap; size_t m_dataOffset = 0; - Delete expl; public: - void read(Athena::io::IStreamReader& reader) - { - reader.setEndian(Athena::BigEndian); - m_header.read(reader); - if (m_header.version != 2) - LogModule.report(LogVisor::FatalError, "unexpected PAK magic"); - - reader.seek(8, Athena::Current); - atUint32 strgSz = reader.readUint32(); - reader.seek(4, Athena::Current); - atUint32 rshdSz = reader.readUint32(); - reader.seek(44, Athena::Current); - m_dataOffset = 128 + strgSz + rshdSz; - - atUint32 nameCount = reader.readUint32(); - m_nameEntries.clear(); - m_nameEntries.reserve(nameCount); - for (atUint32 n=0 ; n::iterator found = m_idMap.find(entry.id); - if (found != m_idMap.end()) - m_nameMap[entry.name] = found->second; - } - } - void write(Athena::io::IStreamWriter& writer) const - { - writer.setEndian(Athena::BigEndian); - m_header.write(writer); - - HECL::FourCC("STRG").write(writer); - atUint32 strgSz = 4; - for (const NameEntry& entry : m_nameEntries) - strgSz += entry.name.size() + 13; - atUint32 strgPad = ((strgSz + 63) & ~63) - strgSz; - strgSz += strgPad; - writer.writeUint32(strgSz); - - HECL::FourCC("RSHD").write(writer); - atUint32 rshdSz = 4 + 24 * m_entries.size(); - atUint32 rshdPad = ((rshdSz + 63) & ~63) - rshdSz; - rshdSz += rshdPad; - writer.writeUint32(rshdSz); - - HECL::FourCC("DATA").write(writer); - atUint32 dataSz = 0; - for (const Entry& entry : m_entries) - dataSz += (entry.size + 63) & ~63; - atUint32 dataPad = ((dataSz + 63) & ~63) - dataSz; - dataSz += dataPad; - writer.writeUint32(dataSz); - writer.seek(36, Athena::Current); - - writer.writeUint32(m_nameEntries.size()); - for (const NameEntry& entry : m_nameEntries) - entry.write(writer); - writer.seek(strgPad, Athena::Current); - - writer.writeUint32(m_entries.size()); - for (const Entry& entry : m_entries) - entry.write(writer); - writer.seek(rshdPad, Athena::Current); - } + DECL_EXPLICIT_DNA inline const Entry* lookupEntry(const UniqueID64& id) const { @@ -150,6 +67,8 @@ 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/SpecBase.cpp b/DataSpec/SpecBase.cpp index c91fefd36..ea2698eac 100644 --- a/DataSpec/SpecBase.cpp +++ b/DataSpec/SpecBase.cpp @@ -5,34 +5,41 @@ namespace Retro LogVisor::LogModule LogModule("RetroDataSpec"); -bool SpecBase::canExtract(const ExtractPassInfo& info) +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; + bool valid = false; if (isWii) { - if (!memcmp(gameID, "R3ME01", 6)) - return true; - if (!memcmp(gameID, "R3MP01", 6)) - return true; - if (!memcmp(gameID, "R3IJ01", 6)) - return true; + if (!memcmp(gameID, "R3M", 3)) + valid = true; + else if (!memcmp(gameID, "R3IJ01", 6)) + valid = true; } else { - if (!memcmp(gameID, "GM8E01", 6)) - return true; + if (!memcmp(gameID, "GM8", 3)) + valid = true; } - LogModule.report(LogVisor::Error, "%.6s (%s) is not supported", gameID, disc->getHeader().gameTitle); - return false; + if (!valid) + { + LogModule.report(LogVisor::Error, "%.6s (%s) is not supported", gameID, disc->getHeader().gameTitle); + return false; + } + + if (isWii) + return checkFromWiiDisc(*(NOD::DiscWii*)disc.get(), info.extractArgs, reps); + else + return checkFromGCNDisc(*(NOD::DiscGCN*)disc.get(), info.extractArgs, reps); } void SpecBase::doExtract(const HECL::Database::Project& project, const ExtractPassInfo& info) diff --git a/DataSpec/SpecBase.hpp b/DataSpec/SpecBase.hpp index bb5e64bd6..5a39ce4c3 100644 --- a/DataSpec/SpecBase.hpp +++ b/DataSpec/SpecBase.hpp @@ -13,7 +13,7 @@ extern LogVisor::LogModule LogModule; struct SpecBase : HECL::Database::IDataSpec { - bool canExtract(const ExtractPassInfo& info); + bool canExtract(const ExtractPassInfo& info, std::vector& reps); void doExtract(const HECL::Database::Project& project, const ExtractPassInfo& info); bool canCook(const HECL::Database::Project& project, const CookTaskInfo& info); @@ -24,11 +24,13 @@ struct SpecBase : HECL::Database::IDataSpec std::unordered_set& implicitsOut); void doPackage(const HECL::Database::Project& project, const PackagePassInfo& info); - virtual bool checkFromGCNDisc(NOD::DiscGCN& disc, ExtractOption& opts)=0; - virtual bool readFromGCNDisc(NOD::DiscGCN& disc)=0; + virtual bool checkFromGCNDisc(NOD::DiscGCN& disc, const std::vector& args, + std::vector& reps)=0; + virtual bool readFromGCNDisc(NOD::DiscGCN& disc, const std::vector& args)=0; - virtual bool checkFromWiiDisc(NOD::DiscWii& disc, ExtractOption& opts)=0; - virtual bool readFromWiiDisc(NOD::DiscWii& disc)=0; + virtual bool checkFromWiiDisc(NOD::DiscWii& disc, const std::vector& args, + std::vector& reps)=0; + virtual bool readFromWiiDisc(NOD::DiscWii& disc, const std::vector& args)=0; virtual bool checkFromProject(HECL::Database::Project& proj)=0; virtual bool readFromProject(HECL::Database::Project& proj)=0; diff --git a/DataSpec/SpecMP1.cpp b/DataSpec/SpecMP1.cpp index 5512a9ba3..a663e3aa1 100644 --- a/DataSpec/SpecMP1.cpp +++ b/DataSpec/SpecMP1.cpp @@ -3,54 +3,163 @@ #define NOD_ATHENA 1 #include "SpecBase.hpp" #include "DNAMP1/PAK.hpp" +#include "DNAMP1/MLVL.hpp" +#include "DNAMP1/STRG.hpp" namespace Retro { struct SpecMP1 : SpecBase { - std::map> m_worldPaks; + std::vector> m_paks; - bool checkFromGCNDisc(NOD::DiscGCN& disc, ExtractOption& opts) + bool checkFromGCNDisc(NOD::DiscGCN& disc, + const std::vector& args, + std::vector& reps) { if (memcmp(disc.getHeader().gameID, "GM8", 3)) return false; + char region = disc.getHeader().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; + switch (region) + { + case 'E': + regstr = ®E; + break; + case 'J': + regstr = ®J; + break; + case 'P': + regstr = ®P; + break; + } + + NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); + std::unique_ptr dolBuf = partition->getDOLBuf(); + const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 16; + + reps.emplace_back(); + ExtractReport& rep = reps.back(); + rep.name = "MP1"; + rep.desc = "Metroid Prime " + *regstr; + if (buildInfo) + rep.desc += " (" + std::string(buildInfo) + ")"; /* Iterate PAKs and build level options */ - m_worldPaks.clear(); + std::map, + CaseInsensitiveCompare> orderedPaks; NOD::DiscBase::IPartition::Node& root = disc.getDataPartition()->getFSTRoot(); for (const NOD::DiscBase::IPartition::Node& child : root) { - std::string name = child.getName(); - std::transform(name.begin(), name.end(), name.begin(), tolower); - if (!name.compare(0, 7, "metroid") && !name.compare(8, 4, ".pak")) + const std::string& name = child.getName(); + std::string lowerName = name; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); + if (name.size() > 4) { - /* This is a world pak */ - std::pair>::iterator,bool> res = - m_worldPaks.emplace(std::make_pair(name, std::make_pair(child.getName(), DNAMP1::PAK()))); - if (res.second) + std::string::iterator extit = lowerName.end() - 4; + if (!std::string(extit, lowerName.end()).compare(".pak")) { - NOD::AthenaPartReadStream rs(child.beginReadStream()); - res.first->second.second.read(rs); + /* 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; + } + } + + 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(std::make_pair(name, DNAMP1::PAK())); + std::pair& res = m_paks.back(); + NOD::AthenaPartReadStream rs(child.beginReadStream()); + res.second.read(rs); + orderedPaks[name] = std::make_pair(&child, &res.second); + } } } + } + for (std::pair> item : orderedPaks) + { + rep.childOpts.emplace_back(); + ExtractReport& childRep = rep.childOpts.back(); + childRep.name = item.first; + + for (DNAMP1::PAK::Entry& entry : *item.second.second) + { + static const HECL::FourCC MLVLfourcc("MLVL"); + if (entry.type == MLVLfourcc) + { + NOD::AthenaPartReadStream rs(item.second.first->beginReadStream(entry.offset)); + DNAMP1::MLVL mlvl; + mlvl.read(rs); + const DNAMP1::PAK::Entry* nameEnt = item.second.second->lookupEntry(mlvl.worldNameId); + if (nameEnt) + { + DNAMP1::STRG mlvlName; + rs.seek(nameEnt->offset, Athena::Begin); + 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 + } + } + } } return true; } - bool readFromGCNDisc(NOD::DiscGCN& disc) + bool readFromGCNDisc(NOD::DiscGCN& disc, + const std::vector& args) { } - bool checkFromWiiDisc(NOD::DiscWii& disc, ExtractOption& opts) + bool checkFromWiiDisc(NOD::DiscWii& disc, + const std::vector& args, + std::vector& reps) { if (memcmp(disc.getHeader().gameID, "R3M", 3)) return false; return true; } - bool readFromWiiDisc(NOD::DiscWii& disc) + bool readFromWiiDisc(NOD::DiscWii& disc, + const std::vector& args) { } diff --git a/NODLib b/NODLib index b6969a82d..b8236ba6d 160000 --- a/NODLib +++ b/NODLib @@ -1 +1 @@ -Subproject commit b6969a82dc0b486240302092c41d6a0596d89777 +Subproject commit b8236ba6d749e20411345fd276d4e4aec98256de