initial MLVL and STRG trilogy integration

This commit is contained in:
Jack Andersen 2015-07-12 20:29:12 -10:00
parent abb9d4c000
commit 7876d4c209
26 changed files with 1333 additions and 126 deletions

View File

@ -1,3 +1,5 @@
add_library(DNACommon add_library(DNACommon
STRG.hpp
STRG.cpp
DNACommon.hpp DNACommon.hpp
DNACommon.cpp) DNACommon.cpp)

View File

@ -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");
}

View File

@ -10,10 +10,13 @@ namespace Retro
/* This comes up a great deal */ /* This comes up a great deal */
typedef Athena::io::DNA<Athena::BigEndian> BigDNA; typedef Athena::io::DNA<Athena::BigEndian> BigDNA;
/* FourCC with DNA read/write */
class FourCC final : public BigDNA, public HECL::FourCC class FourCC final : public BigDNA, public HECL::FourCC
{ {
public: public:
FourCC() : HECL::FourCC() {} FourCC() : HECL::FourCC() {}
FourCC(const HECL::FourCC& other)
: HECL::FourCC() {num = other.toUint32();}
FourCC(const char* name) FourCC(const char* name)
: HECL::FourCC(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 */ /* Hash template-specializations for UniqueID types */
namespace std namespace std
{ {
template<>
struct hash<Retro::FourCC>
{
inline size_t operator()(const Retro::FourCC& fcc) const
{return fcc.toUint32();}
};
template<> template<>
struct hash<Retro::UniqueID32> struct hash<Retro::UniqueID32>
{ {

View File

@ -0,0 +1,42 @@
#include "STRG.hpp"
#include "../DNAMP1/STRG.hpp"
#include "../DNAMP2/STRG.hpp"
#include "../DNAMP3/STRG.hpp"
namespace Retro
{
std::unique_ptr<ISTRG> 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<ISTRG>();
}
uint32_t version = reader.readUint32();
switch (version)
{
case 0:
{
DNAMP1::STRG* newStrg = new struct DNAMP1::STRG;
newStrg->_read(reader);
return std::unique_ptr<ISTRG>(newStrg);
}
case 1:
{
DNAMP2::STRG* newStrg = new struct DNAMP2::STRG;
newStrg->_read(reader);
return std::unique_ptr<ISTRG>(newStrg);
}
case 3:
{
DNAMP3::STRG* newStrg = new struct DNAMP3::STRG;
newStrg->_read(reader);
return std::unique_ptr<ISTRG>(newStrg);
}
}
return std::unique_ptr<ISTRG>();
}
}

View File

@ -0,0 +1,22 @@
#ifndef __COMMON_STRG_HPP__
#define __COMMON_STRG_HPP__
#include <string>
#include <HECL/HECL.hpp>
#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<ISTRG> LoadSTRG(Athena::io::IStreamReader& reader);
}
#endif // __COMMON_STRG_HPP__

View File

@ -1,3 +1,6 @@
#ifndef __DNAMP1_MLVL_HPP__
#define __DNAMP1_MLVL_HPP__
#include "../DNACommon/DNACommon.hpp" #include "../DNACommon/DNACommon.hpp"
namespace Retro namespace Retro
@ -8,7 +11,7 @@ namespace DNAMP1
struct MLVL : BigDNA struct MLVL : BigDNA
{ {
DECL_DNA DECL_DNA
FourCC magic; Value<atUint32> magic;
Value<atUint32> version; Value<atUint32> version;
UniqueID32 worldNameId; UniqueID32 worldNameId;
UniqueID32 saveWorldId; UniqueID32 saveWorldId;
@ -104,3 +107,5 @@ struct MLVL : BigDNA
} }
} }
#endif // __DNAMP1_MLVL_HPP__

View File

@ -31,14 +31,9 @@ void PAK::read(Athena::io::IStreamReader& reader)
{ {
m_entries.emplace_back(); m_entries.emplace_back();
m_entries.back().read(reader); m_entries.back().read(reader);
m_idMap[m_entries.back().id] = &m_entries.back();
} }
atUint32 realCount = 0;
for (Entry& entry : m_entries) for (Entry& entry : m_entries)
{ m_idMap[entry.id] = &entry;
++realCount;
}
m_nameMap.clear(); m_nameMap.clear();
m_nameMap.reserve(nameCount); m_nameMap.reserve(nameCount);

View File

@ -11,12 +11,11 @@ namespace Retro
namespace DNAMP1 namespace DNAMP1
{ {
class PAK : public BigDNA struct PAK : BigDNA
{ {
public:
DECL_EXPLICIT_DNA DECL_EXPLICIT_DNA
struct NameEntry : public BigDNA struct NameEntry : BigDNA
{ {
DECL_DNA DECL_DNA
FourCC type; FourCC type;

View File

@ -6,84 +6,100 @@ namespace Retro
namespace DNAMP1 namespace DNAMP1
{ {
const HECL::FourCC ENGLfcc("ENGL"); void STRG::_read(Athena::io::IStreamReader& reader)
const HECL::FourCC FRENfcc("FREN"); {
const HECL::FourCC GERMfcc("GERM"); atUint32 langCount = reader.readUint32();
const HECL::FourCC SPANfcc("SPAN"); atUint32 strCount = reader.readUint32();
const HECL::FourCC ITALfcc("ITAL");
const HECL::FourCC JAPNfcc("JAPN"); std::vector<FourCC> readLangs;
readLangs.reserve(langCount);
for (atUint32 l=0 ; l<langCount ; ++l)
{
FourCC lang;
lang.read(reader);
readLangs.emplace_back(lang);
reader.seek(4);
}
langs.clear();
langs.reserve(langCount);
for (FourCC& lang : readLangs)
{
std::vector<std::wstring> strs;
reader.seek(strCount * 4 + 4);
for (atUint32 s=0 ; s<strCount ; ++s)
strs.emplace_back(reader.readWString());
langs.emplace(std::make_pair(lang, strs));
}
}
void STRG::read(Athena::io::IStreamReader& reader) void STRG::read(Athena::io::IStreamReader& reader)
{ {
reader.setEndian(Athena::BigEndian); reader.setEndian(Athena::BigEndian);
uint32_t magic = reader.readUint32(); atUint32 magic = reader.readUint32();
if (magic != 0x87654321) if (magic != 0x87654321)
LogModule.report(LogVisor::FatalError, "invalid STRG magic"); LogModule.report(LogVisor::Error, "invalid STRG magic");
version = reader.readUint32(); atUint32 version = reader.readUint32();
langCount = reader.readUint32(); if (version != 0)
strCount = reader.readUint32(); LogModule.report(LogVisor::Error, "invalid STRG version");
langs.clear(); _read(reader);
langs.reserve(langCount);
for (uint32_t l=0 ; l<langCount ; ++l)
{
langs.emplace_back();
Language& lang = langs.back();
lang.lang.read(reader);
reader.readUint32();
}
for (uint32_t l=0 ; l<langCount ; ++l)
{
Language& lang = langs[l];
reader.readUint32();
for (uint32_t s=0 ; s<strCount ; ++s)
reader.readUint32();
for (uint32_t s=0 ; s<strCount ; ++s)
lang.strings.push_back(reader.readWString());
}
} }
void STRG::write(Athena::io::IStreamWriter& writer) const void STRG::write(Athena::io::IStreamWriter& writer) const
{ {
writer.setEndian(Athena::BigEndian); writer.setEndian(Athena::BigEndian);
writer.writeUint32(0x87654321); writer.writeUint32(0x87654321);
writer.writeUint32(version); writer.writeUint32(0);
writer.writeUint32(langs.size()); writer.writeUint32(langs.size());
atUint32 strCount = STRG::count();
writer.writeUint32(strCount); writer.writeUint32(strCount);
uint32_t offset = 0; atUint32 offset = 0;
for (const Language& lang : langs) for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
{ {
lang.lang.write(writer); lang.first.write(writer);
writer.writeUint32(offset); writer.writeUint32(offset);
offset += strCount * 4 + 4; offset += strCount * 4 + 4;
for (uint32_t s=0 ; s<strCount ; ++s) atUint32 langStrCount = lang.second.size();
for (atUint32 s=0 ; s<strCount ; ++s)
{ {
if (s < lang.strings.size()) atUint32 chCount = lang.second[s].size();
offset += lang.strings[s].size() * 2 + 1; if (s < langStrCount)
offset += chCount * 2 + 1;
else else
offset += 1; offset += 1;
} }
} }
for (const Language& lang : langs) for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
{ {
atUint32 langStrCount = lang.second.size();
atUint32 tableSz = strCount * 4;
for (atUint32 s=0 ; s<strCount ; ++s)
{
if (s < langStrCount)
tableSz += lang.second[s].size() * 2 + 1;
else
tableSz += 1;
}
writer.writeUint32(tableSz);
offset = strCount * 4; offset = strCount * 4;
for (uint32_t s=0 ; s<strCount ; ++s) for (atUint32 s=0 ; s<strCount ; ++s)
{ {
writer.writeUint32(offset); writer.writeUint32(offset);
if (s < lang.strings.size()) if (s < langStrCount)
offset += lang.strings[s].size() * 2 + 1; offset += lang.second[s].size() * 2 + 1;
else else
offset += 1; offset += 1;
} }
for (uint32_t s=0 ; s<strCount ; ++s) for (atUint32 s=0 ; s<strCount ; ++s)
{ {
if (s < lang.strings.size()) if (s < langStrCount)
writer.writeWString(lang.strings[s]); writer.writeWString(lang.second[s]);
else else
writer.writeUByte(0); writer.writeUByte(0);
} }

View File

@ -1,33 +1,59 @@
#ifndef __DNAMP1_STRG_HPP__ #ifndef __DNAMP1_STRG_HPP__
#define __DNAMP1_STRG_HPP__ #define __DNAMP1_STRG_HPP__
#include <unordered_map>
#include "../DNACommon/DNACommon.hpp" #include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/STRG.hpp"
namespace Retro namespace Retro
{ {
namespace DNAMP1 namespace DNAMP1
{ {
extern const HECL::FourCC ENGLfcc; struct STRG : ISTRG, BigDNA
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
{ {
DECL_EXPLICIT_DNA DECL_EXPLICIT_DNA
atUint32 version; void _read(Athena::io::IStreamReader& reader);
atUint32 langCount; std::unordered_map<FourCC, std::vector<std::wstring>> langs;
atUint32 strCount;
struct Language inline int32_t lookupIdx(const std::string& name) const {return -1;}
inline size_t count() const
{ {
FourCC lang; size_t retval = 0;
std::vector<std::wstring> strings; for (auto item : langs)
}; {
std::vector<Language> 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();
}
}; };
} }

View File

@ -1,3 +1,5 @@
make_dnalist(liblist make_dnalist(liblist
MLVL) MLVL
add_library(DNAMP2 ${liblist}) STRG)
add_library(DNAMP2 ${liblist}
STRG.cpp)

View File

@ -1,3 +1,6 @@
#ifndef __DNAMP2_MLVL_HPP__
#define __DNAMP2_MLVL_HPP__
#include "../DNACommon/DNACommon.hpp" #include "../DNACommon/DNACommon.hpp"
namespace Retro namespace Retro
@ -8,7 +11,7 @@ namespace DNAMP2
struct MLVL : BigDNA struct MLVL : BigDNA
{ {
DECL_DNA DECL_DNA
FourCC magic; Value<atUint32> magic;
Value<atUint32> version; Value<atUint32> version;
UniqueID32 worldNameId; UniqueID32 worldNameId;
UniqueID32 darkWorldNameId; UniqueID32 darkWorldNameId;
@ -35,7 +38,7 @@ struct MLVL : BigDNA
{ {
DECL_DNA DECL_DNA
UniqueID32 id; UniqueID32 id;
HECL::FourCC type; FourCC type;
}; };
Vector<Dependency, DNA_COUNT(depCount)> deps; Vector<Dependency, DNA_COUNT(depCount)> deps;
@ -53,7 +56,7 @@ struct MLVL : BigDNA
Value<atUint32> areaIdx; Value<atUint32> areaIdx;
Value<atUint32> dockIdx; Value<atUint32> dockIdx;
}; };
HECL::FourCC type; FourCC type;
Vector<Endpoint, DNA_COUNT(endpointCount)> endpoints; Vector<Endpoint, DNA_COUNT(endpointCount)> endpoints;
Value<atUint32> planeVertCount; Value<atUint32> planeVertCount;
@ -66,7 +69,6 @@ struct MLVL : BigDNA
Value<atUint32> relOffsetCount; Value<atUint32> relOffsetCount;
Vector<atUint32, DNA_COUNT(relOffsetCount)> relOffsets; Vector<atUint32, DNA_COUNT(relOffsetCount)> relOffsets;
Value<atUint32> unk1;
String<-1> internalAreaName; String<-1> internalAreaName;
}; };
@ -92,3 +94,5 @@ struct MLVL : BigDNA
} }
} }
#endif // __DNAMP2_MLVL_HPP__

138
DataSpec/DNAMP2/STRG.cpp Normal file
View File

@ -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<FourCC> readLangs;
readLangs.reserve(langCount);
for (atUint32 l=0 ; l<langCount ; ++l)
{
FourCC lang;
lang.read(reader);
readLangs.emplace_back(lang);
reader.seek(8);
}
atUint32 nameCount = reader.readUint32();
atUint32 nameTableSz = reader.readUint32();
std::unique_ptr<uint8_t[]> 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<nameCount ; ++n)
{
const char* name = (char*)(nameTableBuf.get() + HECL::SBig(nameIndex[n].nameOff));
names[name] = HECL::SBig(nameIndex[n].strIdx);
}
langs.clear();
langs.reserve(langCount);
for (FourCC& lang : readLangs)
{
std::vector<std::wstring> strs;
reader.seek(strCount * 4);
for (atUint32 s=0 ; s<strCount ; ++s)
strs.emplace_back(reader.readWString());
langs.emplace(std::make_pair(lang, strs));
}
}
void STRG::read(Athena::io::IStreamReader& reader)
{
reader.setEndian(Athena::BigEndian);
atUint32 magic = reader.readUint32();
if (magic != 0x87654321)
LogModule.report(LogVisor::Error, "invalid STRG magic");
atUint32 version = reader.readUint32();
if (version != 1)
LogModule.report(LogVisor::Error, "invalid STRG version");
_read(reader);
}
void STRG::write(Athena::io::IStreamWriter& writer) const
{
writer.setEndian(Athena::BigEndian);
writer.writeUint32(0x87654321);
writer.writeUint32(1);
writer.writeUint32(langs.size());
atUint32 strCount = STRG::count();
writer.writeUint32(strCount);
atUint32 offset = 0;
for (const std::pair<FourCC, std::vector<std::wstring>>& 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<strCount ; ++s)
{
atUint32 chCount = lang.second[s].size();
if (s < langStrCount)
{
offset += chCount * 2 + 1;
tableSz += chCount * 2 + 1;
}
else
{
offset += 1;
tableSz += 1;
}
}
writer.writeUint32(tableSz);
}
atUint32 nameTableSz = names.size() * 8;
for (const std::pair<std::string, int32_t>& name : names)
nameTableSz += name.first.size() + 1;
writer.writeUint32(names.size());
writer.writeUint32(nameTableSz);
offset = names.size() * 8;
for (const std::pair<std::string, int32_t>& name : names)
{
writer.writeUint32(offset);
writer.writeInt32(name.second);
offset += name.first.size() + 1;
}
for (const std::pair<std::string, int32_t>& name : names)
writer.writeString(name.first);
for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
{
offset = strCount * 4;
atUint32 langStrCount = lang.second.size();
for (atUint32 s=0 ; s<strCount ; ++s)
{
writer.writeUint32(offset);
if (s < langStrCount)
offset += lang.second[s].size() * 2 + 1;
else
offset += 1;
}
for (atUint32 s=0 ; s<strCount ; ++s)
{
if (s < langStrCount)
writer.writeWString(lang.second[s]);
else
writer.writeUByte(0);
}
}
}
}
}

69
DataSpec/DNAMP2/STRG.hpp Normal file
View File

@ -0,0 +1,69 @@
#ifndef __DNAMP2_STRG_HPP__
#define __DNAMP2_STRG_HPP__
#include <unordered_map>
#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<FourCC, std::vector<std::wstring>> langs;
std::map<std::string, int32_t> 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__

View File

@ -1,4 +1,7 @@
make_dnalist(liblist make_dnalist(liblist
PAK) PAK
MLVL
STRG)
add_library(DNAMP3 ${liblist} add_library(DNAMP3 ${liblist}
PAK.cpp) PAK.cpp
STRG.cpp)

82
DataSpec/DNAMP3/MLVL.hpp Normal file
View File

@ -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<atUint32> magic;
Value<atUint32> version;
UniqueID64 worldNameId;
Value<atUint32> unk;
UniqueID64 saveWorldId;
UniqueID64 worldSkyboxId;
Value<atUint32> areaCount;
struct Area : BigDNA
{
DECL_DNA
UniqueID64 areaNameId;
Value<atVec4f> transformMtx[3];
Value<atVec3f> aabb[2];
UniqueID64 areaMREAId;
Value<atUint32> areaId;
Value<atUint32> attachedAreaCount;
Vector<atUint16, DNA_COUNT(attachedAreaCount)> attachedAreas;
Value<atUint32> dockCount;
struct Dock : BigDNA
{
DECL_DNA
Value<atUint32> endpointCount;
struct Endpoint : BigDNA
{
DECL_DNA
Value<atUint32> areaIdx;
Value<atUint32> dockIdx;
};
FourCC type;
Vector<Endpoint, DNA_COUNT(endpointCount)> endpoints;
Value<atUint32> planeVertCount;
Vector<atVec3f, DNA_COUNT(planeVertCount)> planeVerts;
};
Vector<Dock, DNA_COUNT(dockCount)> docks;
String<-1> internalAreaName;
};
UniqueID64 worldMap;
Value<atUint8> unknown2;
Value<atUint32> unknown3;
Value<atUint32> layerFlagCount;
struct LayerFlags : BigDNA
{
DECL_DNA
Value<atUint32> layerCount;
Value<atUint64> flags;
};
Vector<LayerFlags, DNA_COUNT(layerFlagCount)> layerFlags;
Value<atUint32> layerNameCount;
Vector<String<-1>, DNA_COUNT(layerNameCount)> layerNames;
Value<atUint32> layerIDCount;
Vector<UniqueID128, DNA_COUNT(layerIDCount)> layerIDs;
Value<atUint32> layerNameOffsetCount;
Vector<atUint32, DNA_COUNT(layerNameOffsetCount)> layerNameOffsets;
};
}
}
#endif // __DNAMP3_MLVL_HPP__

View File

@ -19,6 +19,7 @@ void PAK::read(Athena::io::IStreamReader& reader)
reader.seek(44, Athena::Current); reader.seek(44, Athena::Current);
m_dataOffset = 128 + strgSz + rshdSz; m_dataOffset = 128 + strgSz + rshdSz;
atUint64 strgBase = reader.position();
atUint32 nameCount = reader.readUint32(); atUint32 nameCount = reader.readUint32();
m_nameEntries.clear(); m_nameEntries.clear();
m_nameEntries.reserve(nameCount); m_nameEntries.reserve(nameCount);
@ -27,7 +28,10 @@ void PAK::read(Athena::io::IStreamReader& reader)
m_nameEntries.emplace_back(); m_nameEntries.emplace_back();
m_nameEntries.back().read(reader); 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(); atUint32 count = reader.readUint32();
m_entries.clear(); m_entries.clear();
@ -38,16 +42,17 @@ void PAK::read(Athena::io::IStreamReader& reader)
{ {
m_entries.emplace_back(); m_entries.emplace_back();
m_entries.back().read(reader); 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.clear();
m_nameMap.reserve(nameCount); m_nameMap.reserve(nameCount);
for (NameEntry& entry : m_nameEntries) for (NameEntry& entry : m_nameEntries)
{ {
std::unordered_map<UniqueID64, Entry*>::iterator found = m_idMap.find(entry.id); auto search = m_idMap.find(entry.id);
if (found != m_idMap.end()) if (search != m_idMap.end())
m_nameMap[entry.name] = found->second; m_nameMap[entry.name] = search->second;
} }
} }
void PAK::write(Athena::io::IStreamWriter& writer) const void PAK::write(Athena::io::IStreamWriter& writer) const

View File

@ -11,9 +11,8 @@ namespace Retro
namespace DNAMP3 namespace DNAMP3
{ {
class PAK : public BigDNA struct PAK : BigDNA
{ {
public:
struct Header : BigDNA struct Header : BigDNA
{ {
DECL_DNA DECL_DNA
@ -35,20 +34,18 @@ public:
{ {
DECL_DNA DECL_DNA
Value<atUint32> compressed; Value<atUint32> compressed;
HECL::FourCC type; FourCC type;
UniqueID64 id; UniqueID64 id;
Value<atUint32> size; Value<atUint32> size;
Value<atUint32> offset; Value<atUint32> offset;
}; };
private:
std::vector<NameEntry> m_nameEntries; std::vector<NameEntry> m_nameEntries;
std::vector<Entry> m_entries; std::vector<Entry> m_entries;
std::unordered_map<UniqueID64, Entry*> m_idMap; std::unordered_map<UniqueID64, Entry*> m_idMap;
std::unordered_map<std::string, Entry*> m_nameMap; std::unordered_map<std::string, Entry*> m_nameMap;
size_t m_dataOffset = 0; size_t m_dataOffset = 0;
public:
DECL_EXPLICIT_DNA DECL_EXPLICIT_DNA
inline const Entry* lookupEntry(const UniqueID64& id) const inline const Entry* lookupEntry(const UniqueID64& id) const
@ -67,8 +64,6 @@ public:
return nullptr; return nullptr;
} }
inline std::vector<Entry>::iterator begin() {return m_entries.begin();}
inline std::vector<Entry>::iterator end() {return m_entries.end();}
inline size_t getDataOffset() const {return m_dataOffset;} inline size_t getDataOffset() const {return m_dataOffset;}
}; };

119
DataSpec/DNAMP3/STRG.cpp Normal file
View File

@ -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<uint8_t[]> 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<nameCount ; ++n)
{
const char* name = (char*)(nameTableBuf.get() + HECL::SBig(nameIndex[n].nameOff));
names[name] = HECL::SBig(nameIndex[n].strIdx);
}
std::vector<FourCC> readLangs;
readLangs.reserve(langCount);
for (atUint32 l=0 ; l<langCount ; ++l)
{
FourCC lang;
lang.read(reader);
readLangs.emplace_back(lang);
reader.seek(strCount * 4 + 4);
}
langs.clear();
langs.reserve(langCount);
for (FourCC& lang : readLangs)
{
std::vector<std::string> strs;
for (atUint32 s=0 ; s<strCount ; ++s)
{
atUint32 len = reader.readUint32();
strs.emplace_back(reader.readString(len));
}
langs.emplace(lang, strs);
}
}
void STRG::read(Athena::io::IStreamReader& reader)
{
reader.setEndian(Athena::BigEndian);
atUint32 magic = reader.readUint32();
if (magic != 0x87654321)
LogModule.report(LogVisor::Error, "invalid STRG magic");
atUint32 version = reader.readUint32();
if (version != 3)
LogModule.report(LogVisor::Error, "invalid STRG version");
_read(reader);
}
void STRG::write(Athena::io::IStreamWriter& writer) const
{
writer.setEndian(Athena::BigEndian);
writer.writeUint32(0x87654321);
writer.writeUint32(3);
writer.writeUint32(langs.size());
atUint32 strCount = STRG::count();
writer.writeUint32(strCount);
atUint32 nameTableSz = names.size() * 8;
for (const std::pair<std::string, int32_t>& name : names)
nameTableSz += name.first.size() + 1;
writer.writeUint32(names.size());
writer.writeUint32(nameTableSz);
atUint32 offset = names.size() * 8;
for (const std::pair<std::string, int32_t>& name : names)
{
writer.writeUint32(offset);
writer.writeInt32(name.second);
offset += name.first.size() + 1;
}
for (const std::pair<std::string, int32_t>& name : names)
writer.writeString(name.first);
offset = 0;
for (const std::pair<FourCC, std::vector<std::string>>& 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<FourCC, std::vector<std::string>>& lang : langs)
{
for (const std::string& str : lang.second)
{
writer.writeUint32(str.size());
writer.writeString(str, str.size());
}
}
}
}
}

69
DataSpec/DNAMP3/STRG.hpp Normal file
View File

@ -0,0 +1,69 @@
#ifndef __DNAMP3_STRG_HPP__
#define __DNAMP3_STRG_HPP__
#include <unordered_map>
#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<FourCC, std::vector<std::string>> langs;
std::map<std::string, int32_t> 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__

View File

@ -8,10 +8,10 @@ LogVisor::LogModule LogModule("RetroDataSpec");
bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps) bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps)
{ {
bool isWii; bool isWii;
std::unique_ptr<NOD::DiscBase> disc = NOD::OpenDiscFromImage(info.srcpath->c_str(), isWii); std::unique_ptr<NOD::DiscBase> disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), isWii);
if (!disc) 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; return false;
} }
const char* gameID = disc->getHeader().gameID; const char* gameID = disc->getHeader().gameID;
@ -41,11 +41,11 @@ bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector<ExtractReport
} }
char region = disc->getHeader().gameID[3]; char region = disc->getHeader().gameID[3];
static const std::string regNONE = ""; static const HECL::SystemString regNONE = _S("");
static const std::string regE = "NTSC"; static const HECL::SystemString regE = _S("NTSC");
static const std::string regJ = "NTSC-J"; static const HECL::SystemString regJ = _S("NTSC-J");
static const std::string regP = "PAL"; static const HECL::SystemString regP = _S("PAL");
const std::string* regstr = &regNONE; const HECL::SystemString* regstr = &regNONE;
switch (region) switch (region)
{ {
case 'E': case 'E':

View File

@ -25,12 +25,12 @@ struct SpecBase : HECL::Database::IDataSpec
void doPackage(const HECL::Database::Project& project, const PackagePassInfo& info); void doPackage(const HECL::Database::Project& project, const PackagePassInfo& info);
virtual bool checkFromStandaloneDisc(NOD::DiscBase& disc, virtual bool checkFromStandaloneDisc(NOD::DiscBase& disc,
const std::string& regstr, const HECL::SystemString& regstr,
const std::vector<const HECL::SystemString*>& args, const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)=0; std::vector<ExtractReport>& reps)=0;
virtual bool checkFromTrilogyDisc(NOD::DiscBase& disc, virtual bool checkFromTrilogyDisc(NOD::DiscBase& disc,
const std::string& regstr, const HECL::SystemString& regstr,
const std::vector<const HECL::SystemString*>& args, const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)=0; std::vector<ExtractReport>& reps)=0;
virtual bool extractFromDisc()=0; virtual bool extractFromDisc()=0;

View File

@ -21,7 +21,7 @@ struct SpecMP1 : SpecBase
std::map<std::string, DiscPAK*, CaseInsensitiveCompare> m_orderedPaks; std::map<std::string, DiscPAK*, CaseInsensitiveCompare> m_orderedPaks;
void buildPaks(NOD::DiscBase::IPartition::Node& root, void buildPaks(NOD::DiscBase::IPartition::Node& root,
const std::vector<const HECL::SystemString*>& args, const std::vector<HECL::SystemString>& args,
ExtractReport& rep) ExtractReport& rep)
{ {
m_paks.clear(); m_paks.clear();
@ -46,10 +46,10 @@ struct SpecMP1 : SpecBase
if (!lowerName.compare(0, 7, "metroid")) if (!lowerName.compare(0, 7, "metroid"))
{ {
HECL::SystemChar idxChar = lowerName[7]; 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.size() == 1 && iswdigit(arg[0]))
if ((*arg)[0] == idxChar) if (arg[0] == idxChar)
good = true; good = true;
} }
} }
@ -58,12 +58,12 @@ struct SpecMP1 : SpecBase
if (!good) if (!good)
{ {
for (const HECL::SystemString* arg : args) for (const HECL::SystemString& arg : args)
{ {
#if HECL_UCS2 #if HECL_UCS2
std::string lowerArg = HECL::WideToUTF8(*arg); std::string lowerArg = HECL::WideToUTF8(arg);
#else #else
std::string lowerArg = *arg; std::string lowerArg = arg;
#endif #endif
std::transform(lowerArg.begin(), lowerArg.end(), lowerArg.begin(), tolower); std::transform(lowerArg.begin(), lowerArg.end(), lowerArg.begin(), tolower);
if (!lowerArg.compare(0, lowerBase.size(), lowerBase)) if (!lowerArg.compare(0, lowerBase.size(), lowerBase))
@ -97,8 +97,7 @@ struct SpecMP1 : SpecBase
DNAMP1::PAK& pak = item.second->pak; DNAMP1::PAK& pak = item.second->pak;
for (DNAMP1::PAK::Entry& entry : pak.m_entries) for (DNAMP1::PAK::Entry& entry : pak.m_entries)
{ {
static const HECL::FourCC MLVLfourcc("MLVL"); if (entry.type == MLVL)
if (entry.type == MLVLfourcc)
{ {
NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(entry.offset)); NOD::AthenaPartReadStream rs(item.second->node.beginReadStream(entry.offset));
DNAMP1::MLVL mlvl; DNAMP1::MLVL mlvl;
@ -111,11 +110,7 @@ struct SpecMP1 : SpecBase
mlvlName.read(rs); mlvlName.read(rs);
if (childRep.desc.size()) if (childRep.desc.size())
childRep.desc += _S(", "); childRep.desc += _S(", ");
#if HECL_UCS2 childRep.desc += mlvlName.getSystemString(ENGL, 0);
childRep.desc += mlvlName.langs[0].strings[0];
#else
childRep.desc += HECL::WideToUTF8(mlvlName.langs[0].strings[0]);
#endif
} }
} }
} }
@ -123,33 +118,83 @@ struct SpecMP1 : SpecBase
} }
bool checkFromStandaloneDisc(NOD::DiscBase& disc, bool checkFromStandaloneDisc(NOD::DiscBase& disc,
const std::string& regstr, const HECL::SystemString& regstr,
const std::vector<const HECL::SystemString*>& args, const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps) std::vector<ExtractReport>& reps)
{ {
NOD::DiscGCN::IPartition* partition = disc.getDataPartition(); NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf(); std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf();
const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19;
/* Root Report */
reps.emplace_back(); reps.emplace_back();
ExtractReport& rep = reps.back(); ExtractReport& rep = reps.back();
rep.name = "MP1"; rep.name = _S("MP1");
rep.desc = "Metroid Prime " + regstr; rep.desc = _S("Metroid Prime ") + regstr;
if (buildInfo) if (buildInfo)
rep.desc += " (" + std::string(buildInfo) + ")"; {
HECL::SystemStringView buildView(buildInfo);
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
}
/* Iterate PAKs and build level options */ /* 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); buildPaks(root, args, rep);
return true; return true;
} }
bool checkFromTrilogyDisc(NOD::DiscBase& disc, bool checkFromTrilogyDisc(NOD::DiscBase& disc,
const std::string& regstr, const HECL::SystemString& regstr,
const std::vector<const HECL::SystemString*>& args, const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps) std::vector<ExtractReport>& reps)
{ {
std::vector<HECL::SystemString> 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<uint8_t[]> 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; return true;
} }
bool extractFromDisc() bool extractFromDisc()

View File

@ -1,8 +1,241 @@
#include <utility>
#define NOD_ATHENA 1
#include "SpecBase.hpp" #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<DiscPAK> m_paks;
std::map<std::string, DiscPAK*, CaseInsensitiveCompare> m_orderedPaks;
void buildPaks(NOD::DiscBase::IPartition::Node& root,
const std::vector<HECL::SystemString>& 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<std::string, DiscPAK*> 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<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> 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<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
std::vector<HECL::SystemString> 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<uint8_t[]> 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<bool(const HECL::Database::ObjectBase&)>)
{
}
struct LevelSpec : public ILevelSpec
{
bool visitLevelObjects(std::function<bool(const HECL::Database::ObjectBase&)>)
{
}
struct AreaSpec : public IAreaSpec
{
bool visitAreaObjects(std::function<bool(const HECL::Database::ObjectBase&)>)
{
}
};
bool visitAreas(std::function<bool(const IAreaSpec&)>)
{
}
};
bool visitLevels(std::function<bool(const ILevelSpec&)>)
{
}
};
static HECL::Database::DataSpecEntry SpecMP2 static HECL::Database::DataSpecEntry SpecMP2
( (
_S("MP2"), _S("MP2"),
_S("Data specification for original Metroid Prime 2 engine"), _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;}
); );
}

View File

@ -1,9 +1,241 @@
#include <utility>
#define NOD_ATHENA 1
#include "SpecBase.hpp" #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<DiscPAK> m_paks;
std::map<std::string, DiscPAK*, CaseInsensitiveCompare> m_orderedPaks;
void buildPaks(NOD::DiscBase::IPartition::Node& root,
const std::vector<HECL::SystemString>& 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<std::string, DiscPAK*> 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<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> 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<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
std::vector<HECL::SystemString> 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<uint8_t[]> 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<bool(const HECL::Database::ObjectBase&)>)
{
}
struct LevelSpec : public ILevelSpec
{
bool visitLevelObjects(std::function<bool(const HECL::Database::ObjectBase&)>)
{
}
struct AreaSpec : public IAreaSpec
{
bool visitAreaObjects(std::function<bool(const HECL::Database::ObjectBase&)>)
{
}
};
bool visitAreas(std::function<bool(const IAreaSpec&)>)
{
}
};
bool visitLevels(std::function<bool(const ILevelSpec&)>)
{
}
};
static HECL::Database::DataSpecEntry SpecMP3 static HECL::Database::DataSpecEntry SpecMP3
( (
_S("MP3"), _S("MP3"),
_S("Data specification for original Metroid Prime 3 engine"), _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;}
); );
}

2
NODLib

@ -1 +1 @@
Subproject commit f30a9302b6bad6811efd8125b424c70069514658 Subproject commit 01f269e8e2a55d8b64e1e277d9a1f87f6877c446