mirror of https://github.com/AxioDL/metaforce.git
initial MLVL and STRG trilogy integration
This commit is contained in:
parent
abb9d4c000
commit
7876d4c209
|
@ -1,3 +1,5 @@
|
||||||
add_library(DNACommon
|
add_library(DNACommon
|
||||||
|
STRG.hpp
|
||||||
|
STRG.cpp
|
||||||
DNACommon.hpp
|
DNACommon.hpp
|
||||||
DNACommon.cpp)
|
DNACommon.cpp)
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "DNACommon.hpp"
|
||||||
|
|
||||||
|
namespace Retro
|
||||||
|
{
|
||||||
|
|
||||||
|
const HECL::FourCC ENGL("ENGL");
|
||||||
|
const HECL::FourCC FREN("FREN");
|
||||||
|
const HECL::FourCC GERM("GERM");
|
||||||
|
const HECL::FourCC SPAN("SPAN");
|
||||||
|
const HECL::FourCC ITAL("ITAL");
|
||||||
|
const HECL::FourCC JAPN("JAPN");
|
||||||
|
|
||||||
|
const HECL::FourCC AFSM("AFSM");
|
||||||
|
const HECL::FourCC AGSC("AGSC");
|
||||||
|
const HECL::FourCC ANCS("ANCS");
|
||||||
|
const HECL::FourCC ANIM("ANIM");
|
||||||
|
const HECL::FourCC ATBL("ATBL");
|
||||||
|
const HECL::FourCC CINF("CINF");
|
||||||
|
const HECL::FourCC CMDL("CMDL");
|
||||||
|
const HECL::FourCC CRSC("CRSC");
|
||||||
|
const HECL::FourCC CSKR("CSKR");
|
||||||
|
const HECL::FourCC CSMP("CSMP");
|
||||||
|
const HECL::FourCC CSNG("CSNG");
|
||||||
|
const HECL::FourCC CTWK("CTWK");
|
||||||
|
const HECL::FourCC DGRP("DGRP");
|
||||||
|
const HECL::FourCC DPSC("DPSC");
|
||||||
|
const HECL::FourCC DUMB("DUMB");
|
||||||
|
const HECL::FourCC ELSC("ELSC");
|
||||||
|
const HECL::FourCC EVNT("EVNT");
|
||||||
|
const HECL::FourCC FONT("FONT");
|
||||||
|
const HECL::FourCC FRME("FRME");
|
||||||
|
const HECL::FourCC HINT("HINT");
|
||||||
|
const HECL::FourCC MAPA("MAPA");
|
||||||
|
const HECL::FourCC MAPU("MAPU");
|
||||||
|
const HECL::FourCC MAPW("MAPW");
|
||||||
|
const HECL::FourCC MLVL("MLVL");
|
||||||
|
const HECL::FourCC MREA("MREA");
|
||||||
|
const HECL::FourCC PART("PART");
|
||||||
|
const HECL::FourCC PATH("PATH");
|
||||||
|
const HECL::FourCC RFRM("RFRM");
|
||||||
|
const HECL::FourCC ROOM("ROOM");
|
||||||
|
const HECL::FourCC SAVW("SAVW");
|
||||||
|
const HECL::FourCC SCAN("SCAN");
|
||||||
|
const HECL::FourCC STRG("STRG");
|
||||||
|
const HECL::FourCC SWHC("SWHC");
|
||||||
|
const HECL::FourCC TXTR("TXTR");
|
||||||
|
const HECL::FourCC WPSC("WPSC");
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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__
|
|
@ -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__
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
make_dnalist(liblist
|
make_dnalist(liblist
|
||||||
MLVL)
|
MLVL
|
||||||
add_library(DNAMP2 ${liblist})
|
STRG)
|
||||||
|
add_library(DNAMP2 ${liblist}
|
||||||
|
STRG.cpp)
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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__
|
|
@ -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)
|
||||||
|
|
|
@ -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__
|
|
@ -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
|
||||||
|
|
|
@ -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;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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__
|
|
@ -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 = ®NONE;
|
const HECL::SystemString* regstr = ®NONE;
|
||||||
switch (region)
|
switch (region)
|
||||||
{
|
{
|
||||||
case 'E':
|
case 'E':
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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
2
NODLib
|
@ -1 +1 @@
|
||||||
Subproject commit f30a9302b6bad6811efd8125b424c70069514658
|
Subproject commit 01f269e8e2a55d8b64e1e277d9a1f87f6877c446
|
Loading…
Reference in New Issue