mirror of https://github.com/AxioDL/metaforce.git
All cooked resources extracting; decompression bug-fixes
This commit is contained in:
parent
94d84d8991
commit
dea341d27b
|
@ -149,6 +149,12 @@ class PAKEntryReadStream : public Athena::io::IStreamReader
|
||||||
atUint64 m_sz;
|
atUint64 m_sz;
|
||||||
atUint64 m_pos;
|
atUint64 m_pos;
|
||||||
public:
|
public:
|
||||||
|
PAKEntryReadStream() {}
|
||||||
|
operator bool() const {return m_buf.operator bool();}
|
||||||
|
PAKEntryReadStream(const PAKEntryReadStream& other) = delete;
|
||||||
|
PAKEntryReadStream(PAKEntryReadStream&& other) = default;
|
||||||
|
PAKEntryReadStream& operator=(const PAKEntryReadStream& other) = delete;
|
||||||
|
PAKEntryReadStream& operator=(PAKEntryReadStream&& other) = default;
|
||||||
PAKEntryReadStream(std::unique_ptr<atUint8[]>&& buf, atUint64 sz, atUint64 pos)
|
PAKEntryReadStream(std::unique_ptr<atUint8[]>&& buf, atUint64 sz, atUint64 pos)
|
||||||
: m_buf(std::move(buf)), m_sz(sz), m_pos(pos)
|
: m_buf(std::move(buf)), m_sz(sz), m_pos(pos)
|
||||||
{
|
{
|
||||||
|
@ -168,6 +174,7 @@ public:
|
||||||
}
|
}
|
||||||
inline atUint64 position() const {return m_pos;}
|
inline atUint64 position() const {return m_pos;}
|
||||||
inline atUint64 length() const {return m_sz;}
|
inline atUint64 length() const {return m_sz;}
|
||||||
|
inline const atUint8* data() const {return m_buf.get();}
|
||||||
inline atUint64 readUBytesToBuf(void* buf, atUint64 len)
|
inline atUint64 readUBytesToBuf(void* buf, atUint64 len)
|
||||||
{
|
{
|
||||||
atUint64 bufEnd = m_pos + len;
|
atUint64 bufEnd = m_pos + len;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define NOD_ATHENA 1
|
#define NOD_ATHENA 1
|
||||||
#include "DNAMP1.hpp"
|
#include "DNAMP1.hpp"
|
||||||
#include "STRG.hpp"
|
#include "STRG.hpp"
|
||||||
|
@ -10,7 +12,7 @@ namespace DNAMP1
|
||||||
LogVisor::LogModule Log("Retro::DNAMP1");
|
LogVisor::LogModule Log("Retro::DNAMP1");
|
||||||
|
|
||||||
PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node)
|
PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node)
|
||||||
: m_project(project), m_node(node)
|
: m_project(project), m_node(node), m_pak(false)
|
||||||
{
|
{
|
||||||
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
||||||
m_pak.read(rs);
|
m_pak.read(rs);
|
||||||
|
@ -34,7 +36,7 @@ std::string PAKBridge::getLevelString() const
|
||||||
mlvlName.read(rs);
|
mlvlName.read(rs);
|
||||||
if (retval.size())
|
if (retval.size())
|
||||||
retval += _S(", ");
|
retval += _S(", ");
|
||||||
retval += mlvlName.getSystemString(ENGL, 0);
|
retval += mlvlName.getUTF8(ENGL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,20 +46,35 @@ std::string PAKBridge::getLevelString() const
|
||||||
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::STRG)
|
if (entry.type == Retro::STRG)
|
||||||
return {STRG::Extract<STRG>, ".strg"};
|
return {STRG::Extract<STRG>, ".as"};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PAKBridge::extractResources(const HECL::ProjectPath& dirOut)
|
bool PAKBridge::extractResources(const HECL::ProjectPath& workingOut,
|
||||||
|
const HECL::ProjectPath& cookedOut,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
for (const std::pair<UniqueID32, PAK::Entry*>& item : m_pak.m_idMap)
|
for (const std::pair<UniqueID32, PAK::Entry*>& item : m_pak.m_idMap)
|
||||||
{
|
{
|
||||||
|
PAKEntryReadStream s;
|
||||||
ResExtractor extractor = LookupExtractor(*item.second);
|
ResExtractor extractor = LookupExtractor(*item.second);
|
||||||
if (extractor.func)
|
if (extractor.func)
|
||||||
{
|
{
|
||||||
PAKEntryReadStream strgIn = item.second->beginReadStream(m_node);
|
HECL::ProjectPath workPath(workingOut, m_pak.bestEntryName(*item.second) + extractor.fileExt);
|
||||||
HECL::ProjectPath resPath(dirOut, m_pak.bestEntryName(*item.second) + extractor.fileExt);
|
if (force || workPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
extractor.func(strgIn, resPath);
|
{
|
||||||
|
s = item.second->beginReadStream(m_node);
|
||||||
|
extractor.func(s, workPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HECL::ProjectPath cookPath(cookedOut, m_pak.bestEntryName(*item.second));
|
||||||
|
if (force || cookPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
|
{
|
||||||
|
if (!s)
|
||||||
|
s = item.second->beginReadStream(m_node);
|
||||||
|
FILE* fout = HECL::Fopen(cookPath.getAbsolutePath().c_str(), _S("wb"));
|
||||||
|
fwrite(s.data(), 1, s.length(), fout);
|
||||||
|
fclose(fout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,14 +17,14 @@ class PAKBridge
|
||||||
HECL::Database::Project& m_project;
|
HECL::Database::Project& m_project;
|
||||||
const NOD::DiscBase::IPartition::Node& m_node;
|
const NOD::DiscBase::IPartition::Node& m_node;
|
||||||
PAK m_pak;
|
PAK m_pak;
|
||||||
|
|
||||||
static ResExtractor LookupExtractor(const PAK::Entry& entry);
|
static ResExtractor LookupExtractor(const PAK::Entry& entry);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||||
const std::string& getName() const {return m_node.getName();}
|
const std::string& getName() const {return m_node.getName();}
|
||||||
std::string getLevelString() const;
|
std::string getLevelString() const;
|
||||||
bool extractResources(const HECL::ProjectPath& dirOut);
|
bool extractResources(const HECL::ProjectPath& dirOut,
|
||||||
|
const HECL::ProjectPath& cookedOut,
|
||||||
|
bool force);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,10 @@ void PAK::read(Athena::io::IStreamReader& reader)
|
||||||
for (atUint32 e=0 ; e<count ; ++e)
|
for (atUint32 e=0 ; e<count ; ++e)
|
||||||
{
|
{
|
||||||
m_entries.emplace_back();
|
m_entries.emplace_back();
|
||||||
m_entries.back().read(reader);
|
Entry& ent = m_entries.back();
|
||||||
|
ent.read(reader);
|
||||||
|
if (ent.compressed && m_useLzo)
|
||||||
|
ent.compressed = 2;
|
||||||
}
|
}
|
||||||
for (Entry& entry : m_entries)
|
for (Entry& entry : m_entries)
|
||||||
m_idMap[entry.id] = &entry;
|
m_idMap[entry.id] = &entry;
|
||||||
|
@ -63,10 +66,16 @@ void PAK::write(Athena::io::IStreamWriter& writer) const
|
||||||
|
|
||||||
writer.writeUint32(m_entries.size());
|
writer.writeUint32(m_entries.size());
|
||||||
for (const Entry& entry : m_entries)
|
for (const Entry& entry : m_entries)
|
||||||
entry.write(writer);
|
{
|
||||||
|
Entry tmp = entry;
|
||||||
|
if (tmp.compressed)
|
||||||
|
tmp.compressed = 1;
|
||||||
|
tmp.write(writer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<atUint8[]> PAK::Entry::getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const
|
std::unique_ptr<atUint8[]>
|
||||||
|
PAK::Entry::getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const
|
||||||
{
|
{
|
||||||
if (compressed)
|
if (compressed)
|
||||||
{
|
{
|
||||||
|
@ -78,22 +87,17 @@ std::unique_ptr<atUint8[]> PAK::Entry::getBuffer(const NOD::DiscBase::IPartition
|
||||||
atUint8* buf = new atUint8[decompSz];
|
atUint8* buf = new atUint8[decompSz];
|
||||||
atUint8* bufCur = buf;
|
atUint8* bufCur = buf;
|
||||||
|
|
||||||
atUint16 zlibCheck;
|
atUint8 compBuf[0x8000];
|
||||||
strm->read(&zlibCheck, 2);
|
if (compressed == 1)
|
||||||
zlibCheck = HECL::SBig(zlibCheck);
|
|
||||||
strm->seek(-2, SEEK_CUR);
|
|
||||||
|
|
||||||
atUint8 compBuf[0x4000];
|
|
||||||
if ((zlibCheck % 31) == 0)
|
|
||||||
{
|
{
|
||||||
atUint32 compRem = size - 4;
|
atUint32 compRem = size - 4;
|
||||||
z_stream zs;
|
z_stream zs = {};
|
||||||
inflateInit(&zs);
|
inflateInit(&zs);
|
||||||
zs.avail_out = decompSz;
|
zs.avail_out = decompSz;
|
||||||
zs.next_out = buf;
|
zs.next_out = buf;
|
||||||
while (zs.avail_out)
|
while (zs.avail_out)
|
||||||
{
|
{
|
||||||
atUint64 readSz = strm->read(compBuf, MIN(compRem, 0x4000));
|
atUint64 readSz = strm->read(compBuf, MIN(compRem, 0x8000));
|
||||||
compRem -= readSz;
|
compRem -= readSz;
|
||||||
zs.avail_in = readSz;
|
zs.avail_in = readSz;
|
||||||
zs.next_in = compBuf;
|
zs.next_in = compBuf;
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace DNAMP1
|
||||||
|
|
||||||
struct PAK : BigDNA
|
struct PAK : BigDNA
|
||||||
{
|
{
|
||||||
|
bool m_useLzo;
|
||||||
|
PAK(bool useLzo) : m_useLzo(useLzo) {}
|
||||||
DECL_EXPLICIT_DNA
|
DECL_EXPLICIT_DNA
|
||||||
|
|
||||||
struct NameEntry : BigDNA
|
struct NameEntry : BigDNA
|
||||||
|
@ -37,7 +39,8 @@ struct PAK : BigDNA
|
||||||
inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const
|
inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const
|
||||||
{
|
{
|
||||||
atUint64 sz;
|
atUint64 sz;
|
||||||
return PAKEntryReadStream(getBuffer(pak, sz), sz, off);
|
std::unique_ptr<atUint8[]> buf = getBuffer(pak, sz);
|
||||||
|
return PAKEntryReadStream(std::move(buf), sz, off);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,7 +73,7 @@ struct PAK : BigDNA
|
||||||
return nentry.name;
|
return nentry.name;
|
||||||
|
|
||||||
/* Otherwise return ID format string */
|
/* Otherwise return ID format string */
|
||||||
return entry.id.toString();
|
return entry.type.toString() + '_' + entry.id.toString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,13 @@ void STRG::_read(Athena::io::IStreamReader& reader)
|
||||||
reader.seek(strCount * 4 + 4);
|
reader.seek(strCount * 4 + 4);
|
||||||
for (atUint32 s=0 ; s<strCount ; ++s)
|
for (atUint32 s=0 ; s<strCount ; ++s)
|
||||||
strs.emplace_back(reader.readWString());
|
strs.emplace_back(reader.readWString());
|
||||||
langs.emplace(std::make_pair(lang, strs));
|
langs.emplace_back(lang, strs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
langMap.clear();
|
||||||
|
langMap.reserve(langCount);
|
||||||
|
for (std::pair<FourCC, std::vector<std::wstring>>& item : langs)
|
||||||
|
langMap.emplace(item.first, &item.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void STRG::read(Athena::io::IStreamReader& reader)
|
void STRG::read(Athena::io::IStreamReader& reader)
|
||||||
|
@ -128,6 +133,7 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read pass */
|
/* Read pass */
|
||||||
|
langs.clear();
|
||||||
for (AngelScript::asUINT i=0 ; i<in.GetGlobalVarCount() ; ++i)
|
for (AngelScript::asUINT i=0 ; i<in.GetGlobalVarCount() ; ++i)
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
|
@ -140,29 +146,36 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
|
||||||
std::vector<std::wstring> strs;
|
std::vector<std::wstring> strs;
|
||||||
for (const std::string* str : strsin)
|
for (const std::string* str : strsin)
|
||||||
strs.emplace_back(wconv.from_bytes(*str));
|
strs.emplace_back(wconv.from_bytes(*str));
|
||||||
langs.emplace(std::make_pair(FourCC(name), strs));
|
langs.emplace_back(FourCC(name), strs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
langMap.clear();
|
||||||
|
langMap.reserve(langs.size());
|
||||||
|
for (std::pair<FourCC, std::vector<std::wstring>>& item : langs)
|
||||||
|
langMap.emplace(item.first, &item.second);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void STRG::writeAngelScript(std::ofstream& out) const
|
void STRG::writeAngelScript(std::ofstream& out) const
|
||||||
{
|
{
|
||||||
std::wbuffer_convert<std::codecvt_utf8<wchar_t>> wconv(out.rdbuf());
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> wconv;
|
||||||
std::wostream wout(&wconv);
|
|
||||||
for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
|
for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
|
||||||
{
|
{
|
||||||
out << "STRG::Language " << lang.first.toString() << "({";
|
out << "STRG::Language " << lang.first.toString() << "({";
|
||||||
bool comma = false;
|
bool comma = false;
|
||||||
|
unsigned idx = 0;
|
||||||
for (const std::wstring& str : lang.second)
|
for (const std::wstring& str : lang.second)
|
||||||
{
|
{
|
||||||
out << (comma?", \"":"\"");
|
if (comma)
|
||||||
wout << str;
|
out << ",";
|
||||||
|
out << "\n/* " << idx++ << " */ \"";
|
||||||
|
out << wconv.to_bytes(str);
|
||||||
out << "\"";
|
out << "\"";
|
||||||
comma = true;
|
comma = true;
|
||||||
}
|
}
|
||||||
out << "});\n";
|
out << "\n});\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ struct STRG : ISTRG, BigDNA
|
||||||
{
|
{
|
||||||
DECL_EXPLICIT_DNA
|
DECL_EXPLICIT_DNA
|
||||||
void _read(Athena::io::IStreamReader& reader);
|
void _read(Athena::io::IStreamReader& reader);
|
||||||
std::unordered_map<FourCC, std::vector<std::wstring>> langs;
|
std::vector<std::pair<FourCC, std::vector<std::wstring>>> langs;
|
||||||
|
std::unordered_map<FourCC, std::vector<std::wstring>*> langMap;
|
||||||
|
|
||||||
inline int32_t lookupIdx(const std::string& name) const {return -1;}
|
inline int32_t lookupIdx(const std::string& name) const {return -1;}
|
||||||
|
|
||||||
|
@ -31,26 +32,26 @@ struct STRG : ISTRG, BigDNA
|
||||||
}
|
}
|
||||||
inline std::string getUTF8(const FourCC& lang, size_t idx) const
|
inline std::string getUTF8(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
return HECL::WideToUTF8(search->second.at(idx));
|
return HECL::WideToUTF8(search->second->at(idx));
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
inline std::wstring getUTF16(const FourCC& lang, size_t idx) const
|
inline std::wstring getUTF16(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
return search->second.at(idx);
|
return search->second->at(idx);
|
||||||
return std::wstring();
|
return std::wstring();
|
||||||
}
|
}
|
||||||
inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const
|
inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
#if HECL_UCS2
|
#if HECL_UCS2
|
||||||
return search->second.at(idx);
|
return search->second->at(idx);
|
||||||
#else
|
#else
|
||||||
return HECL::WideToUTF8(search->second.at(idx));
|
return HECL::WideToUTF8(search->second->at(idx));
|
||||||
#endif
|
#endif
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
@ -58,7 +59,6 @@ struct STRG : ISTRG, BigDNA
|
||||||
bool readAngelScript(const AngelScript::asIScriptModule& in);
|
bool readAngelScript(const AngelScript::asIScriptModule& in);
|
||||||
void writeAngelScript(std::ofstream& out) const;
|
void writeAngelScript(std::ofstream& out) const;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace DNAMP2
|
||||||
LogVisor::LogModule Log("Retro::DNAMP2");
|
LogVisor::LogModule Log("Retro::DNAMP2");
|
||||||
|
|
||||||
PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node)
|
PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node)
|
||||||
: m_project(project), m_node(node)
|
: m_project(project), m_node(node), m_pak(true)
|
||||||
{
|
{
|
||||||
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
||||||
m_pak.read(rs);
|
m_pak.read(rs);
|
||||||
|
@ -35,7 +35,7 @@ std::string PAKBridge::getLevelString() const
|
||||||
mlvlName.read(rs);
|
mlvlName.read(rs);
|
||||||
if (retval.size())
|
if (retval.size())
|
||||||
retval += _S(", ");
|
retval += _S(", ");
|
||||||
retval += mlvlName.getSystemString(ENGL, 0);
|
retval += mlvlName.getUTF8(ENGL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,20 +45,35 @@ std::string PAKBridge::getLevelString() const
|
||||||
ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
|
ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::STRG)
|
if (entry.type == Retro::STRG)
|
||||||
return {STRG::Extract<STRG>, ".strg"};
|
return {STRG::Extract<STRG>, ".as"};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PAKBridge::extractResources(const HECL::ProjectPath& dirOut)
|
bool PAKBridge::extractResources(const HECL::ProjectPath& workingOut,
|
||||||
|
const HECL::ProjectPath& cookedOut,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
for (const std::pair<UniqueID32, DNAMP1::PAK::Entry*>& item : m_pak.m_idMap)
|
for (const std::pair<UniqueID32, DNAMP1::PAK::Entry*>& item : m_pak.m_idMap)
|
||||||
{
|
{
|
||||||
|
PAKEntryReadStream s;
|
||||||
ResExtractor extractor = LookupExtractor(*item.second);
|
ResExtractor extractor = LookupExtractor(*item.second);
|
||||||
if (extractor.func)
|
if (extractor.func)
|
||||||
{
|
{
|
||||||
PAKEntryReadStream strgIn = item.second->beginReadStream(m_node);
|
HECL::ProjectPath workPath(workingOut, m_pak.bestEntryName(*item.second) + extractor.fileExt);
|
||||||
HECL::ProjectPath resPath(dirOut, m_pak.bestEntryName(*item.second) + extractor.fileExt);
|
if (force || workPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
extractor.func(strgIn, resPath);
|
{
|
||||||
|
s = item.second->beginReadStream(m_node);
|
||||||
|
extractor.func(s, workPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HECL::ProjectPath cookPath(cookedOut, m_pak.bestEntryName(*item.second));
|
||||||
|
if (force || cookPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
|
{
|
||||||
|
if (!s)
|
||||||
|
s = item.second->beginReadStream(m_node);
|
||||||
|
FILE* fout = HECL::Fopen(cookPath.getAbsolutePath().c_str(), _S("wb"));
|
||||||
|
fwrite(s.data(), 1, s.length(), fout);
|
||||||
|
fclose(fout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,14 +17,14 @@ class PAKBridge
|
||||||
HECL::Database::Project& m_project;
|
HECL::Database::Project& m_project;
|
||||||
const NOD::DiscBase::IPartition::Node& m_node;
|
const NOD::DiscBase::IPartition::Node& m_node;
|
||||||
DNAMP1::PAK m_pak;
|
DNAMP1::PAK m_pak;
|
||||||
|
|
||||||
static ResExtractor LookupExtractor(const DNAMP1::PAK::Entry& entry);
|
static ResExtractor LookupExtractor(const DNAMP1::PAK::Entry& entry);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||||
const std::string& getName() const {return m_node.getName();}
|
const std::string& getName() const {return m_node.getName();}
|
||||||
std::string getLevelString() const;
|
std::string getLevelString() const;
|
||||||
bool extractResources(const HECL::ProjectPath& dirOut);
|
bool extractResources(const HECL::ProjectPath& dirOut,
|
||||||
|
const HECL::ProjectPath& cookedOut,
|
||||||
|
bool force);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,13 @@ void STRG::_read(Athena::io::IStreamReader& reader)
|
||||||
reader.seek(strCount * 4);
|
reader.seek(strCount * 4);
|
||||||
for (atUint32 s=0 ; s<strCount ; ++s)
|
for (atUint32 s=0 ; s<strCount ; ++s)
|
||||||
strs.emplace_back(reader.readWString());
|
strs.emplace_back(reader.readWString());
|
||||||
langs.emplace(std::make_pair(lang, strs));
|
langs.emplace_back(lang, strs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
langMap.clear();
|
||||||
|
langMap.reserve(langCount);
|
||||||
|
for (std::pair<FourCC, std::vector<std::wstring>>& item : langs)
|
||||||
|
langMap.emplace(item.first, &item.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void STRG::read(Athena::io::IStreamReader& reader)
|
void STRG::read(Athena::io::IStreamReader& reader)
|
||||||
|
@ -140,6 +145,35 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
|
||||||
|
|
||||||
void STRG::writeAngelScript(std::ofstream& out) const
|
void STRG::writeAngelScript(std::ofstream& out) const
|
||||||
{
|
{
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> wconv;
|
||||||
|
for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
|
||||||
|
{
|
||||||
|
out << "STRG::Language " << lang.first.toString() << "({";
|
||||||
|
bool comma = false;
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (const std::wstring& str : lang.second)
|
||||||
|
{
|
||||||
|
if (comma)
|
||||||
|
out << ",";
|
||||||
|
out << "\n/* " << idx++ << " */ \"";
|
||||||
|
out << wconv.to_bytes(str);
|
||||||
|
out << "\"";
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
out << "\n});\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "STRG::Names NAMES({";
|
||||||
|
bool comma = false;
|
||||||
|
for (const std::pair<std::string, int32_t>& name : names)
|
||||||
|
{
|
||||||
|
if (comma)
|
||||||
|
out << ",";
|
||||||
|
out << "\n ";
|
||||||
|
comma = true;
|
||||||
|
out << "{\"" << name.first << "\", " << name.second << "}";
|
||||||
|
}
|
||||||
|
out << "\n});\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ struct STRG : ISTRG, BigDNA
|
||||||
{
|
{
|
||||||
DECL_EXPLICIT_DNA
|
DECL_EXPLICIT_DNA
|
||||||
void _read(Athena::io::IStreamReader& reader);
|
void _read(Athena::io::IStreamReader& reader);
|
||||||
std::unordered_map<FourCC, std::vector<std::wstring>> langs;
|
std::vector<std::pair<FourCC, std::vector<std::wstring>>> langs;
|
||||||
|
std::unordered_map<FourCC, std::vector<std::wstring>*> langMap;
|
||||||
std::map<std::string, int32_t> names;
|
std::map<std::string, int32_t> names;
|
||||||
|
|
||||||
inline int32_t lookupIdx(const std::string& name) const
|
inline int32_t lookupIdx(const std::string& name) const
|
||||||
|
@ -38,26 +39,26 @@ struct STRG : ISTRG, BigDNA
|
||||||
}
|
}
|
||||||
inline std::string getUTF8(const FourCC& lang, size_t idx) const
|
inline std::string getUTF8(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
return HECL::WideToUTF8(search->second.at(idx));
|
return HECL::WideToUTF8(search->second->at(idx));
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
inline std::wstring getUTF16(const FourCC& lang, size_t idx) const
|
inline std::wstring getUTF16(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
return search->second.at(idx);
|
return search->second->at(idx);
|
||||||
return std::wstring();
|
return std::wstring();
|
||||||
}
|
}
|
||||||
inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const
|
inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
#if HECL_UCS2
|
#if HECL_UCS2
|
||||||
return search->second.at(idx);
|
return search->second->at(idx);
|
||||||
#else
|
#else
|
||||||
return HECL::WideToUTF8(search->second.at(idx));
|
return HECL::WideToUTF8(search->second->at(idx));
|
||||||
#endif
|
#endif
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,7 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
||||||
|
|
||||||
std::string PAKBridge::getLevelString() const
|
std::string PAKBridge::getLevelString() const
|
||||||
{
|
{
|
||||||
std::string retval;
|
std::set<std::string, CaseInsensitiveCompare> uniq;
|
||||||
std::set<HECL::SystemString> worldNames;
|
|
||||||
for (const PAK::Entry& entry : m_pak.m_entries)
|
for (const PAK::Entry& entry : m_pak.m_entries)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::MLVL)
|
if (entry.type == Retro::MLVL)
|
||||||
|
@ -36,16 +35,18 @@ std::string PAKBridge::getLevelString() const
|
||||||
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
|
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
|
||||||
STRG mlvlName;
|
STRG mlvlName;
|
||||||
mlvlName.read(rs);
|
mlvlName.read(rs);
|
||||||
worldNames.emplace(mlvlName.getSystemString(ENGL, 0));
|
uniq.insert(mlvlName.getUTF8(ENGL, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::string retval;
|
||||||
for (const std::string& name : worldNames)
|
bool comma = false;
|
||||||
|
for (const std::string& str : uniq)
|
||||||
{
|
{
|
||||||
if (retval.size())
|
if (comma)
|
||||||
retval += _S(", ");
|
retval += _S(", ");
|
||||||
retval += name;
|
comma = true;
|
||||||
|
retval += str;
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -53,20 +54,35 @@ std::string PAKBridge::getLevelString() const
|
||||||
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::STRG)
|
if (entry.type == Retro::STRG)
|
||||||
return {STRG::Extract<STRG>, ".strg"};
|
return {STRG::Extract, ".as"};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PAKBridge::extractResources(const HECL::ProjectPath& dirOut)
|
bool PAKBridge::extractResources(const HECL::ProjectPath& workingOut,
|
||||||
|
const HECL::ProjectPath& cookedOut,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
for (const std::pair<UniqueID64, PAK::Entry*>& item : m_pak.m_idMap)
|
for (const std::pair<UniqueID64, PAK::Entry*>& item : m_pak.m_idMap)
|
||||||
{
|
{
|
||||||
|
PAKEntryReadStream s;
|
||||||
ResExtractor extractor = LookupExtractor(*item.second);
|
ResExtractor extractor = LookupExtractor(*item.second);
|
||||||
if (extractor.func)
|
if (extractor.func)
|
||||||
{
|
{
|
||||||
PAKEntryReadStream strgIn = item.second->beginReadStream(m_node);
|
HECL::ProjectPath workPath(workingOut, m_pak.bestEntryName(*item.second) + extractor.fileExt);
|
||||||
HECL::ProjectPath resPath(dirOut, m_pak.bestEntryName(*item.second) + extractor.fileExt);
|
if (force || workPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
extractor.func(strgIn, resPath);
|
{
|
||||||
|
s = item.second->beginReadStream(m_node);
|
||||||
|
extractor.func(s, workPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HECL::ProjectPath cookPath(cookedOut, m_pak.bestEntryName(*item.second));
|
||||||
|
if (force || cookPath.getPathType() == HECL::ProjectPath::PT_NONE)
|
||||||
|
{
|
||||||
|
if (!s)
|
||||||
|
s = item.second->beginReadStream(m_node);
|
||||||
|
FILE* fout = HECL::Fopen(cookPath.getAbsolutePath().c_str(), _S("wb"));
|
||||||
|
fwrite(s.data(), 1, s.length(), fout);
|
||||||
|
fclose(fout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,14 +17,14 @@ class PAKBridge
|
||||||
HECL::Database::Project& m_project;
|
HECL::Database::Project& m_project;
|
||||||
const NOD::DiscBase::IPartition::Node& m_node;
|
const NOD::DiscBase::IPartition::Node& m_node;
|
||||||
PAK m_pak;
|
PAK m_pak;
|
||||||
|
|
||||||
static ResExtractor LookupExtractor(const PAK::Entry& entry);
|
static ResExtractor LookupExtractor(const PAK::Entry& entry);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||||
const std::string& getName() const {return m_node.getName();}
|
const std::string& getName() const {return m_node.getName();}
|
||||||
std::string getLevelString() const;
|
std::string getLevelString() const;
|
||||||
bool extractResources(const HECL::ProjectPath& dirOut);
|
bool extractResources(const HECL::ProjectPath& dirOut,
|
||||||
|
const HECL::ProjectPath& cookedOut,
|
||||||
|
bool force);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,8 @@ struct PAK : BigDNA
|
||||||
inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const
|
inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const
|
||||||
{
|
{
|
||||||
atUint64 sz;
|
atUint64 sz;
|
||||||
return PAKEntryReadStream(getBuffer(pak, sz), sz, off);
|
std::unique_ptr<atUint8[]> buf = getBuffer(pak, sz);
|
||||||
|
return PAKEntryReadStream(std::move(buf), sz, off);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ struct PAK : BigDNA
|
||||||
return nentry.name;
|
return nentry.name;
|
||||||
|
|
||||||
/* Otherwise return ID format string */
|
/* Otherwise return ID format string */
|
||||||
return entry.id.toString();
|
return entry.type.toString() + '_' + entry.id.toString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,13 @@ void STRG::_read(Athena::io::IStreamReader& reader)
|
||||||
atUint32 len = reader.readUint32();
|
atUint32 len = reader.readUint32();
|
||||||
strs.emplace_back(reader.readString(len));
|
strs.emplace_back(reader.readString(len));
|
||||||
}
|
}
|
||||||
langs.emplace(readLangs[l], strs);
|
langs.emplace_back(readLangs[l], strs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
langMap.clear();
|
||||||
|
langMap.reserve(langCount);
|
||||||
|
for (std::pair<FourCC, std::vector<std::string>>& item : langs)
|
||||||
|
langMap.emplace(item.first, &item.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void STRG::read(Athena::io::IStreamReader& reader)
|
void STRG::read(Athena::io::IStreamReader& reader)
|
||||||
|
@ -148,6 +153,33 @@ bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
|
||||||
|
|
||||||
void STRG::writeAngelScript(std::ofstream& out) const
|
void STRG::writeAngelScript(std::ofstream& out) const
|
||||||
{
|
{
|
||||||
|
for (const std::pair<FourCC, std::vector<std::string>>& lang : langs)
|
||||||
|
{
|
||||||
|
out << "STRG::Language " << lang.first.toString() << "({";
|
||||||
|
bool comma = false;
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (const std::string& str : lang.second)
|
||||||
|
{
|
||||||
|
if (comma)
|
||||||
|
out << ",";
|
||||||
|
out << "\n/* " << idx++ << " */ \"";
|
||||||
|
out << str << "\"";
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
out << "\n});\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "STRG::Names NAMES({";
|
||||||
|
bool comma = false;
|
||||||
|
for (const std::pair<std::string, int32_t>& name : names)
|
||||||
|
{
|
||||||
|
if (comma)
|
||||||
|
out << ",";
|
||||||
|
out << "\n ";
|
||||||
|
comma = true;
|
||||||
|
out << "{\"" << name.first << "\", " << name.second << "}";
|
||||||
|
}
|
||||||
|
out << "\n});\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ struct STRG : ISTRG, BigDNA
|
||||||
{
|
{
|
||||||
DECL_EXPLICIT_DNA
|
DECL_EXPLICIT_DNA
|
||||||
void _read(Athena::io::IStreamReader& reader);
|
void _read(Athena::io::IStreamReader& reader);
|
||||||
std::unordered_map<FourCC, std::vector<std::string>> langs;
|
std::vector<std::pair<FourCC, std::vector<std::string>>> langs;
|
||||||
|
std::unordered_map<FourCC, std::vector<std::string>*> langMap;
|
||||||
std::map<std::string, int32_t> names;
|
std::map<std::string, int32_t> names;
|
||||||
|
|
||||||
inline int32_t lookupIdx(const std::string& name) const
|
inline int32_t lookupIdx(const std::string& name) const
|
||||||
|
@ -38,32 +39,40 @@ struct STRG : ISTRG, BigDNA
|
||||||
}
|
}
|
||||||
inline std::string getUTF8(const FourCC& lang, size_t idx) const
|
inline std::string getUTF8(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
return search->second.at(idx);
|
return search->second->at(idx);
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
inline std::wstring getUTF16(const FourCC& lang, size_t idx) const
|
inline std::wstring getUTF16(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
return HECL::UTF8ToWide(search->second.at(idx));
|
return HECL::UTF8ToWide(search->second->at(idx));
|
||||||
return std::wstring();
|
return std::wstring();
|
||||||
}
|
}
|
||||||
inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const
|
inline HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const
|
||||||
{
|
{
|
||||||
auto search = langs.find(lang);
|
auto search = langMap.find(lang);
|
||||||
if (search != langs.end())
|
if (search != langMap.end())
|
||||||
#if HECL_UCS2
|
#if HECL_UCS2
|
||||||
return HECL::UTF8ToWide(search->second.at(idx));
|
return HECL::UTF8ToWide(search->second->at(idx));
|
||||||
#else
|
#else
|
||||||
return search->second.at(idx);
|
return search->second->at(idx);
|
||||||
#endif
|
#endif
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readAngelScript(const AngelScript::asIScriptModule& in);
|
bool readAngelScript(const AngelScript::asIScriptModule& in);
|
||||||
void writeAngelScript(std::ofstream& out) const;
|
void writeAngelScript(std::ofstream& out) const;
|
||||||
|
|
||||||
|
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ISTRG> strg = LoadSTRG(rs);
|
||||||
|
std::ofstream strgOut(outPath.getAbsolutePath());
|
||||||
|
strg->writeAngelScript(strgOut);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,16 @@ namespace Retro
|
||||||
bool SpecBase::canExtract(HECL::Database::Project& project,
|
bool SpecBase::canExtract(HECL::Database::Project& project,
|
||||||
const ExtractPassInfo& info, std::vector<ExtractReport>& reps)
|
const ExtractPassInfo& info, std::vector<ExtractReport>& reps)
|
||||||
{
|
{
|
||||||
bool isWii;
|
m_disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), m_isWii);
|
||||||
m_disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), isWii);
|
|
||||||
if (!m_disc)
|
if (!m_disc)
|
||||||
return false;
|
return false;
|
||||||
const char* gameID = m_disc->getHeader().gameID;
|
const char* gameID = m_disc->getHeader().gameID;
|
||||||
|
|
||||||
bool standalone = true;
|
m_standalone = true;
|
||||||
if (isWii && !memcmp(gameID, "R3M", 3))
|
if (m_isWii && !memcmp(gameID, "R3M", 3))
|
||||||
standalone = false;
|
m_standalone = false;
|
||||||
|
|
||||||
if (standalone && !checkStandaloneID(gameID))
|
if (m_standalone && !checkStandaloneID(gameID))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char region = m_disc->getHeader().gameID[3];
|
char region = m_disc->getHeader().gameID[3];
|
||||||
|
@ -38,15 +37,32 @@ bool SpecBase::canExtract(HECL::Database::Project& project,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (standalone)
|
if (m_standalone)
|
||||||
return checkFromStandaloneDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps);
|
return checkFromStandaloneDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps);
|
||||||
else
|
else
|
||||||
return checkFromTrilogyDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps);
|
return checkFromTrilogyDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpecBase::doExtract(HECL::Database::Project& project, const ExtractPassInfo&)
|
void SpecBase::doExtract(HECL::Database::Project& project, const ExtractPassInfo& info)
|
||||||
{
|
{
|
||||||
extractFromDisc(project, *m_disc.get());
|
if (m_isWii)
|
||||||
|
{
|
||||||
|
/* Extract update partition for repacking later */
|
||||||
|
const HECL::SystemString& target = project.getProjectRootPath().getAbsolutePath();
|
||||||
|
NOD::DiscBase::IPartition* update = m_disc->getUpdatePartition();
|
||||||
|
if (update)
|
||||||
|
update->getFSTRoot().extractToDirectory(target, info.force);
|
||||||
|
|
||||||
|
if (!m_standalone)
|
||||||
|
{
|
||||||
|
NOD::DiscBase::IPartition* data = m_disc->getDataPartition();
|
||||||
|
const NOD::DiscBase::IPartition::Node& root = data->getFSTRoot();
|
||||||
|
for (const NOD::DiscBase::IPartition::Node& child : root)
|
||||||
|
if (child.getKind() == NOD::DiscBase::IPartition::Node::NODE_FILE)
|
||||||
|
child.extractToDirectory(target, info.force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extractFromDisc(project, *m_disc.get(), info.force);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpecBase::canCook(const HECL::Database::Project& project, const CookTaskInfo& info)
|
bool SpecBase::canCook(const HECL::Database::Project& project, const CookTaskInfo& info)
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct SpecBase : HECL::Database::IDataSpec
|
||||||
const HECL::SystemString& regstr,
|
const HECL::SystemString& regstr,
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
std::vector<ExtractReport>& reps)=0;
|
std::vector<ExtractReport>& reps)=0;
|
||||||
virtual bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc)=0;
|
virtual bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc, bool force)=0;
|
||||||
|
|
||||||
virtual bool checkFromProject(HECL::Database::Project& proj)=0;
|
virtual bool checkFromProject(HECL::Database::Project& proj)=0;
|
||||||
virtual bool readFromProject(HECL::Database::Project& proj)=0;
|
virtual bool readFromProject(HECL::Database::Project& proj)=0;
|
||||||
|
@ -53,6 +53,8 @@ struct SpecBase : HECL::Database::IDataSpec
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<NOD::DiscBase> m_disc;
|
std::unique_ptr<NOD::DiscBase> m_disc;
|
||||||
|
bool m_isWii;
|
||||||
|
bool m_standalone;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Retro
|
||||||
{
|
{
|
||||||
|
|
||||||
static LogVisor::LogModule Log("Retro::SpecMP1");
|
static LogVisor::LogModule Log("Retro::SpecMP1");
|
||||||
|
extern HECL::Database::DataSpecEntry SpecEntMP1;
|
||||||
|
|
||||||
struct SpecMP1 : SpecBase
|
struct SpecMP1 : SpecBase
|
||||||
{
|
{
|
||||||
|
@ -18,6 +19,8 @@ struct SpecMP1 : SpecBase
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool doMP1 = false;
|
||||||
|
std::vector<const NOD::DiscBase::IPartition::Node*> m_nonPaks;
|
||||||
std::vector<DNAMP1::PAKBridge> m_paks;
|
std::vector<DNAMP1::PAKBridge> m_paks;
|
||||||
std::map<std::string, DNAMP1::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
|
std::map<std::string, DNAMP1::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
|
||||||
|
|
||||||
|
@ -26,9 +29,11 @@ struct SpecMP1 : SpecBase
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
ExtractReport& rep)
|
ExtractReport& rep)
|
||||||
{
|
{
|
||||||
|
m_nonPaks.clear();
|
||||||
m_paks.clear();
|
m_paks.clear();
|
||||||
for (const NOD::DiscBase::IPartition::Node& child : root)
|
for (const NOD::DiscBase::IPartition::Node& child : root)
|
||||||
{
|
{
|
||||||
|
bool isPak = false;
|
||||||
const std::string& name = child.getName();
|
const std::string& name = child.getName();
|
||||||
std::string lowerName = name;
|
std::string lowerName = name;
|
||||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
|
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
|
||||||
|
@ -38,6 +43,7 @@ struct SpecMP1 : SpecBase
|
||||||
if (!std::string(extit, lowerName.end()).compare(".pak"))
|
if (!std::string(extit, lowerName.end()).compare(".pak"))
|
||||||
{
|
{
|
||||||
/* This is a pak */
|
/* This is a pak */
|
||||||
|
isPak = true;
|
||||||
std::string lowerBase(lowerName.begin(), extit);
|
std::string lowerBase(lowerName.begin(), extit);
|
||||||
|
|
||||||
/* Needs filter */
|
/* Needs filter */
|
||||||
|
@ -79,6 +85,9 @@ struct SpecMP1 : SpecBase
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isPak)
|
||||||
|
m_nonPaks.push_back(&child);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort PAKs alphabetically */
|
/* Sort PAKs alphabetically */
|
||||||
|
@ -102,6 +111,7 @@ struct SpecMP1 : SpecBase
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
std::vector<ExtractReport>& reps)
|
std::vector<ExtractReport>& reps)
|
||||||
{
|
{
|
||||||
|
doMP1 = true;
|
||||||
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;
|
||||||
|
@ -115,7 +125,7 @@ struct SpecMP1 : SpecBase
|
||||||
{
|
{
|
||||||
std::string buildStr(buildInfo);
|
std::string buildStr(buildInfo);
|
||||||
HECL::SystemStringView buildView(buildStr);
|
HECL::SystemStringView buildView(buildStr);
|
||||||
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate PAKs and build level options */
|
/* Iterate PAKs and build level options */
|
||||||
|
@ -137,18 +147,24 @@ struct SpecMP1 : SpecBase
|
||||||
/* Needs filter */
|
/* Needs filter */
|
||||||
for (const HECL::SystemString& arg : args)
|
for (const HECL::SystemString& arg : args)
|
||||||
{
|
{
|
||||||
|
HECL::SystemString lowerArg = arg;
|
||||||
|
HECL::ToLower(lowerArg);
|
||||||
|
if (!lowerArg.compare(0, 3, "mp1"))
|
||||||
|
{
|
||||||
|
doMP1 = true;
|
||||||
size_t slashPos = arg.find(_S('/'));
|
size_t slashPos = arg.find(_S('/'));
|
||||||
if (slashPos == HECL::SystemString::npos)
|
if (slashPos == HECL::SystemString::npos)
|
||||||
slashPos = arg.find(_S('\\'));
|
slashPos = arg.find(_S('\\'));
|
||||||
if (slashPos != HECL::SystemString::npos)
|
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()));
|
mp1args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
doMP1 = true;
|
||||||
|
|
||||||
|
if (!doMP1)
|
||||||
|
return true;
|
||||||
|
|
||||||
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
|
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
|
||||||
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
||||||
|
@ -168,7 +184,7 @@ struct SpecMP1 : SpecBase
|
||||||
{
|
{
|
||||||
std::string buildStr(buildInfo);
|
std::string buildStr(buildInfo);
|
||||||
HECL::SystemStringView buildView(buildStr);
|
HECL::SystemStringView buildView(buildStr);
|
||||||
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate PAKs and build level options */
|
/* Iterate PAKs and build level options */
|
||||||
|
@ -180,9 +196,20 @@ struct SpecMP1 : SpecBase
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc)
|
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force)
|
||||||
{
|
{
|
||||||
HECL::ProjectPath mp1Path(project.getProjectRootPath(), "MP1");
|
if (!doMP1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
HECL::ProjectPath mp1WorkPath(project.getProjectRootPath(), "MP1");
|
||||||
|
mp1WorkPath.makeDir();
|
||||||
|
for (const NOD::DiscBase::IPartition::Node* node : m_nonPaks)
|
||||||
|
node->extractToDirectory(mp1WorkPath.getAbsolutePath(), force);
|
||||||
|
|
||||||
|
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP1);
|
||||||
|
cookPath.makeDir();
|
||||||
|
HECL::ProjectPath mp1CookPath(cookPath, "MP1");
|
||||||
|
mp1CookPath.makeDir();
|
||||||
|
|
||||||
for (DNAMP1::PAKBridge& pak : m_paks)
|
for (DNAMP1::PAKBridge& pak : m_paks)
|
||||||
{
|
{
|
||||||
|
@ -190,8 +217,11 @@ struct SpecMP1 : SpecBase
|
||||||
std::string::const_iterator extit = name.end() - 4;
|
std::string::const_iterator extit = name.end() - 4;
|
||||||
std::string baseName(name.begin(), extit);
|
std::string baseName(name.begin(), extit);
|
||||||
|
|
||||||
HECL::ProjectPath pakPath(mp1Path, baseName);
|
HECL::ProjectPath pakWorkPath(mp1WorkPath, baseName);
|
||||||
pak.extractResources(pakPath);
|
pakWorkPath.makeDir();
|
||||||
|
HECL::ProjectPath pakCookPath(mp1CookPath, baseName);
|
||||||
|
pakCookPath.makeDir();
|
||||||
|
pak.extractResources(pakWorkPath, pakCookPath, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -227,7 +257,7 @@ struct SpecMP1 : SpecBase
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HECL::Database::DataSpecEntry SpecMP1 =
|
HECL::Database::DataSpecEntry SpecEntMP1 =
|
||||||
{
|
{
|
||||||
_S("MP1"),
|
_S("MP1"),
|
||||||
_S("Data specification for original Metroid Prime engine"),
|
_S("Data specification for original Metroid Prime engine"),
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace Retro
|
||||||
{
|
{
|
||||||
|
|
||||||
static LogVisor::LogModule Log("Retro::SpecMP2");
|
static LogVisor::LogModule Log("Retro::SpecMP2");
|
||||||
|
extern HECL::Database::DataSpecEntry SpecEntMP2;
|
||||||
|
|
||||||
struct SpecMP2 : SpecBase
|
struct SpecMP2 : SpecBase
|
||||||
{
|
{
|
||||||
|
@ -17,6 +18,8 @@ struct SpecMP2 : SpecBase
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool doMP2 = false;
|
||||||
|
std::vector<const NOD::DiscBase::IPartition::Node*> m_nonPaks;
|
||||||
std::vector<DNAMP2::PAKBridge> m_paks;
|
std::vector<DNAMP2::PAKBridge> m_paks;
|
||||||
std::map<std::string, DNAMP2::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
|
std::map<std::string, DNAMP2::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
|
||||||
|
|
||||||
|
@ -25,9 +28,11 @@ struct SpecMP2 : SpecBase
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
ExtractReport& rep)
|
ExtractReport& rep)
|
||||||
{
|
{
|
||||||
|
m_nonPaks.clear();
|
||||||
m_paks.clear();
|
m_paks.clear();
|
||||||
for (const NOD::DiscBase::IPartition::Node& child : root)
|
for (const NOD::DiscBase::IPartition::Node& child : root)
|
||||||
{
|
{
|
||||||
|
bool isPak = false;
|
||||||
const std::string& name = child.getName();
|
const std::string& name = child.getName();
|
||||||
std::string lowerName = name;
|
std::string lowerName = name;
|
||||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
|
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
|
||||||
|
@ -37,6 +42,7 @@ struct SpecMP2 : SpecBase
|
||||||
if (!std::string(extit, lowerName.end()).compare(".pak"))
|
if (!std::string(extit, lowerName.end()).compare(".pak"))
|
||||||
{
|
{
|
||||||
/* This is a pak */
|
/* This is a pak */
|
||||||
|
isPak = true;
|
||||||
std::string lowerBase(lowerName.begin(), extit);
|
std::string lowerBase(lowerName.begin(), extit);
|
||||||
|
|
||||||
/* Needs filter */
|
/* Needs filter */
|
||||||
|
@ -77,6 +83,9 @@ struct SpecMP2 : SpecBase
|
||||||
m_paks.emplace_back(project, child);
|
m_paks.emplace_back(project, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isPak)
|
||||||
|
m_nonPaks.push_back(&child);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort PAKs alphabetically */
|
/* Sort PAKs alphabetically */
|
||||||
|
@ -100,6 +109,7 @@ struct SpecMP2 : SpecBase
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
std::vector<ExtractReport>& reps)
|
std::vector<ExtractReport>& reps)
|
||||||
{
|
{
|
||||||
|
doMP2 = true;
|
||||||
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;
|
||||||
|
@ -113,7 +123,7 @@ struct SpecMP2 : SpecBase
|
||||||
{
|
{
|
||||||
std::string buildStr(buildInfo);
|
std::string buildStr(buildInfo);
|
||||||
HECL::SystemStringView buildView(buildStr);
|
HECL::SystemStringView buildView(buildStr);
|
||||||
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate PAKs and build level options */
|
/* Iterate PAKs and build level options */
|
||||||
|
@ -135,18 +145,24 @@ struct SpecMP2 : SpecBase
|
||||||
/* Needs filter */
|
/* Needs filter */
|
||||||
for (const HECL::SystemString& arg : args)
|
for (const HECL::SystemString& arg : args)
|
||||||
{
|
{
|
||||||
|
HECL::SystemString lowerArg = arg;
|
||||||
|
HECL::ToLower(lowerArg);
|
||||||
|
if (!lowerArg.compare(0, 3, "mp2"))
|
||||||
|
{
|
||||||
|
doMP2 = true;
|
||||||
size_t slashPos = arg.find(_S('/'));
|
size_t slashPos = arg.find(_S('/'));
|
||||||
if (slashPos == HECL::SystemString::npos)
|
if (slashPos == HECL::SystemString::npos)
|
||||||
slashPos = arg.find(_S('\\'));
|
slashPos = arg.find(_S('\\'));
|
||||||
if (slashPos != HECL::SystemString::npos)
|
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()));
|
mp2args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
doMP2 = true;
|
||||||
|
|
||||||
|
if (!doMP2)
|
||||||
|
return true;
|
||||||
|
|
||||||
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
|
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
|
||||||
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
||||||
|
@ -166,7 +182,7 @@ struct SpecMP2 : SpecBase
|
||||||
{
|
{
|
||||||
std::string buildStr(buildInfo);
|
std::string buildStr(buildInfo);
|
||||||
HECL::SystemStringView buildView(buildStr);
|
HECL::SystemStringView buildView(buildStr);
|
||||||
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate PAKs and build level options */
|
/* Iterate PAKs and build level options */
|
||||||
|
@ -178,8 +194,35 @@ struct SpecMP2 : SpecBase
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc)
|
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force)
|
||||||
{
|
{
|
||||||
|
if (!doMP2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
HECL::ProjectPath mp2WorkPath(project.getProjectRootPath(), "MP2");
|
||||||
|
mp2WorkPath.makeDir();
|
||||||
|
for (const NOD::DiscBase::IPartition::Node* node : m_nonPaks)
|
||||||
|
node->extractToDirectory(mp2WorkPath.getAbsolutePath(), force);
|
||||||
|
|
||||||
|
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP2);
|
||||||
|
cookPath.makeDir();
|
||||||
|
HECL::ProjectPath mp2CookPath(cookPath, "MP2");
|
||||||
|
mp2CookPath.makeDir();
|
||||||
|
|
||||||
|
for (DNAMP2::PAKBridge& pak : m_paks)
|
||||||
|
{
|
||||||
|
const std::string& name = pak.getName();
|
||||||
|
std::string::const_iterator extit = name.end() - 4;
|
||||||
|
std::string baseName(name.begin(), extit);
|
||||||
|
|
||||||
|
HECL::ProjectPath pakWorkPath(mp2WorkPath, baseName);
|
||||||
|
pakWorkPath.makeDir();
|
||||||
|
HECL::ProjectPath pakCookPath(mp2CookPath, baseName);
|
||||||
|
pakCookPath.makeDir();
|
||||||
|
pak.extractResources(pakWorkPath, pakCookPath, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkFromProject(HECL::Database::Project& proj)
|
bool checkFromProject(HECL::Database::Project& proj)
|
||||||
|
@ -212,7 +255,7 @@ struct SpecMP2 : SpecBase
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static HECL::Database::DataSpecEntry SpecMP2
|
HECL::Database::DataSpecEntry SpecEntMP2
|
||||||
(
|
(
|
||||||
_S("MP2"),
|
_S("MP2"),
|
||||||
_S("Data specification for original Metroid Prime 2 engine"),
|
_S("Data specification for original Metroid Prime 2 engine"),
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Retro
|
||||||
{
|
{
|
||||||
|
|
||||||
static LogVisor::LogModule Log("Retro::SpecMP3");
|
static LogVisor::LogModule Log("Retro::SpecMP3");
|
||||||
|
extern HECL::Database::DataSpecEntry SpecEntMP3;
|
||||||
|
|
||||||
struct SpecMP3 : SpecBase
|
struct SpecMP3 : SpecBase
|
||||||
{
|
{
|
||||||
|
@ -18,17 +19,36 @@ struct SpecMP3 : SpecBase
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool doMP3 = false;
|
||||||
|
std::vector<const NOD::DiscBase::IPartition::Node*> m_nonPaks;
|
||||||
std::vector<DNAMP3::PAKBridge> m_paks;
|
std::vector<DNAMP3::PAKBridge> m_paks;
|
||||||
std::map<std::string, DNAMP3::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
|
std::map<std::string, DNAMP3::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
|
||||||
|
|
||||||
|
/* These are populated when extracting MPT's frontend (uses MP3's DataSpec) */
|
||||||
|
bool doMPTFE = false;
|
||||||
|
std::vector<const NOD::DiscBase::IPartition::Node*> m_feNonPaks;
|
||||||
|
std::vector<DNAMP3::PAKBridge> m_fePaks;
|
||||||
|
std::map<std::string, DNAMP3::PAKBridge*, CaseInsensitiveCompare> m_feOrderedPaks;
|
||||||
|
|
||||||
void buildPaks(HECL::Database::Project& project,
|
void buildPaks(HECL::Database::Project& project,
|
||||||
NOD::DiscBase::IPartition::Node& root,
|
NOD::DiscBase::IPartition::Node& root,
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
ExtractReport& rep)
|
ExtractReport& rep,
|
||||||
|
bool fe)
|
||||||
{
|
{
|
||||||
|
if (fe)
|
||||||
|
{
|
||||||
|
m_feNonPaks.clear();
|
||||||
|
m_fePaks.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_nonPaks.clear();
|
||||||
m_paks.clear();
|
m_paks.clear();
|
||||||
|
}
|
||||||
for (const NOD::DiscBase::IPartition::Node& child : root)
|
for (const NOD::DiscBase::IPartition::Node& child : root)
|
||||||
{
|
{
|
||||||
|
bool isPak = false;
|
||||||
const std::string& name = child.getName();
|
const std::string& name = child.getName();
|
||||||
std::string lowerName = name;
|
std::string lowerName = name;
|
||||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
|
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
|
||||||
|
@ -38,6 +58,7 @@ struct SpecMP3 : SpecBase
|
||||||
if (!std::string(extit, lowerName.end()).compare(".pak"))
|
if (!std::string(extit, lowerName.end()).compare(".pak"))
|
||||||
{
|
{
|
||||||
/* This is a pak */
|
/* This is a pak */
|
||||||
|
isPak = true;
|
||||||
std::string lowerBase(lowerName.begin(), extit);
|
std::string lowerBase(lowerName.begin(), extit);
|
||||||
|
|
||||||
/* Needs filter */
|
/* Needs filter */
|
||||||
|
@ -75,18 +96,40 @@ struct SpecMP3 : SpecBase
|
||||||
}
|
}
|
||||||
|
|
||||||
if (good)
|
if (good)
|
||||||
|
{
|
||||||
|
if (fe)
|
||||||
|
m_fePaks.emplace_back(project, child);
|
||||||
|
else
|
||||||
m_paks.emplace_back(project, child);
|
m_paks.emplace_back(project, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isPak)
|
||||||
|
{
|
||||||
|
if (fe)
|
||||||
|
m_feNonPaks.push_back(&child);
|
||||||
|
else
|
||||||
|
m_nonPaks.push_back(&child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Sort PAKs alphabetically */
|
/* Sort PAKs alphabetically */
|
||||||
|
if (fe)
|
||||||
|
{
|
||||||
|
m_feOrderedPaks.clear();
|
||||||
|
for (DNAMP3::PAKBridge& dpak : m_fePaks)
|
||||||
|
m_feOrderedPaks[dpak.getName()] = &dpak;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
m_orderedPaks.clear();
|
m_orderedPaks.clear();
|
||||||
for (DNAMP3::PAKBridge& dpak : m_paks)
|
for (DNAMP3::PAKBridge& dpak : m_paks)
|
||||||
m_orderedPaks[dpak.getName()] = &dpak;
|
m_orderedPaks[dpak.getName()] = &dpak;
|
||||||
|
}
|
||||||
|
|
||||||
/* Assemble extract report */
|
/* Assemble extract report */
|
||||||
for (const std::pair<std::string, DNAMP3::PAKBridge*>& item : m_orderedPaks)
|
for (const std::pair<std::string, DNAMP3::PAKBridge*>& item : fe ? m_feOrderedPaks : m_orderedPaks)
|
||||||
{
|
{
|
||||||
rep.childOpts.emplace_back();
|
rep.childOpts.emplace_back();
|
||||||
ExtractReport& childRep = rep.childOpts.back();
|
ExtractReport& childRep = rep.childOpts.back();
|
||||||
|
@ -109,6 +152,7 @@ struct SpecMP3 : SpecBase
|
||||||
const std::vector<HECL::SystemString>& args,
|
const std::vector<HECL::SystemString>& args,
|
||||||
std::vector<ExtractReport>& reps)
|
std::vector<ExtractReport>& reps)
|
||||||
{
|
{
|
||||||
|
doMP3 = true;
|
||||||
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;
|
||||||
|
@ -122,12 +166,12 @@ struct SpecMP3 : SpecBase
|
||||||
{
|
{
|
||||||
std::string buildStr(buildInfo);
|
std::string buildStr(buildInfo);
|
||||||
HECL::SystemStringView buildView(buildStr);
|
HECL::SystemStringView buildView(buildStr);
|
||||||
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate PAKs and build level options */
|
/* Iterate PAKs and build level options */
|
||||||
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
||||||
buildPaks(project, root, args, rep);
|
buildPaks(project, root, args, rep, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -139,26 +183,52 @@ struct SpecMP3 : SpecBase
|
||||||
std::vector<ExtractReport>& reps)
|
std::vector<ExtractReport>& reps)
|
||||||
{
|
{
|
||||||
std::vector<HECL::SystemString> mp3args;
|
std::vector<HECL::SystemString> mp3args;
|
||||||
|
std::vector<HECL::SystemString> feargs;
|
||||||
if (args.size())
|
if (args.size())
|
||||||
{
|
{
|
||||||
/* Needs filter */
|
/* Needs filter */
|
||||||
for (const HECL::SystemString& arg : args)
|
for (const HECL::SystemString& arg : args)
|
||||||
{
|
{
|
||||||
|
HECL::SystemString lowerArg = arg;
|
||||||
|
HECL::ToLower(lowerArg);
|
||||||
|
if (!lowerArg.compare(0, 3, "mp3"))
|
||||||
|
{
|
||||||
|
doMP3 = true;
|
||||||
size_t slashPos = arg.find(_S('/'));
|
size_t slashPos = arg.find(_S('/'));
|
||||||
if (slashPos == HECL::SystemString::npos)
|
if (slashPos == HECL::SystemString::npos)
|
||||||
slashPos = arg.find(_S('\\'));
|
slashPos = arg.find(_S('\\'));
|
||||||
if (slashPos != HECL::SystemString::npos)
|
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()));
|
mp3args.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const HECL::SystemString& arg : args)
|
||||||
|
{
|
||||||
|
HECL::SystemString lowerArg = arg;
|
||||||
|
HECL::ToLower(lowerArg);
|
||||||
|
if (!lowerArg.compare(0, 2, "fe"))
|
||||||
|
{
|
||||||
|
doMPTFE = true;
|
||||||
|
size_t slashPos = arg.find(_S('/'));
|
||||||
|
if (slashPos == HECL::SystemString::npos)
|
||||||
|
slashPos = arg.find(_S('\\'));
|
||||||
|
if (slashPos != HECL::SystemString::npos)
|
||||||
|
feargs.emplace_back(HECL::SystemString(arg.begin() + slashPos + 1, arg.end()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
doMP3 = true;
|
||||||
|
doMPTFE = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
|
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
|
||||||
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
|
||||||
|
|
||||||
|
/* MP3 extract */
|
||||||
|
if (doMP3)
|
||||||
|
{
|
||||||
NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5mp3_p.dol");
|
NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5mp3_p.dol");
|
||||||
if (dolIt == root.end())
|
if (dolIt == root.end())
|
||||||
return false;
|
return false;
|
||||||
|
@ -175,20 +245,103 @@ struct SpecMP3 : SpecBase
|
||||||
{
|
{
|
||||||
std::string buildStr(buildInfo);
|
std::string buildStr(buildInfo);
|
||||||
HECL::SystemStringView buildView(buildStr);
|
HECL::SystemStringView buildView(buildStr);
|
||||||
rep.desc += _S(" (") + buildView.sys_str() + _S(")");
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate PAKs and build level options */
|
/* Iterate PAKs and build level options */
|
||||||
NOD::DiscBase::IPartition::Node::DirectoryIterator mp3It = root.find("MP3");
|
NOD::DiscBase::IPartition::Node::DirectoryIterator mp3It = root.find("MP3");
|
||||||
if (mp3It == root.end())
|
if (mp3It == root.end())
|
||||||
return false;
|
return false;
|
||||||
buildPaks(project, *mp3It, mp3args, rep);
|
buildPaks(project, *mp3It, mp3args, rep, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MPT Frontend extract */
|
||||||
|
if (doMPTFE)
|
||||||
|
{
|
||||||
|
NOD::DiscBase::IPartition::Node::DirectoryIterator dolIt = root.find("rs5fe_p.dol");
|
||||||
|
if (dolIt == root.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::unique_ptr<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("fe");
|
||||||
|
rep.desc = _S("Metroid Prime Trilogy Frontend ") + regstr;
|
||||||
|
if (buildInfo)
|
||||||
|
{
|
||||||
|
std::string buildStr(buildInfo);
|
||||||
|
HECL::SystemStringView buildView(buildStr);
|
||||||
|
rep.desc += _S(" (") + buildView + _S(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate PAKs and build level options */
|
||||||
|
NOD::DiscBase::IPartition::Node::DirectoryIterator feIt = root.find("fe");
|
||||||
|
if (feIt == root.end())
|
||||||
|
return false;
|
||||||
|
buildPaks(project, *feIt, feargs, rep, true);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc)
|
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force)
|
||||||
{
|
{
|
||||||
|
if (doMP3)
|
||||||
|
{
|
||||||
|
HECL::ProjectPath mp3WorkPath(project.getProjectRootPath(), "MP3");
|
||||||
|
mp3WorkPath.makeDir();
|
||||||
|
for (const NOD::DiscBase::IPartition::Node* node : m_nonPaks)
|
||||||
|
node->extractToDirectory(mp3WorkPath.getAbsolutePath(), force);
|
||||||
|
|
||||||
|
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP3);
|
||||||
|
cookPath.makeDir();
|
||||||
|
HECL::ProjectPath mp3CookPath(cookPath, "MP3");
|
||||||
|
mp3CookPath.makeDir();
|
||||||
|
|
||||||
|
for (DNAMP3::PAKBridge& pak : m_paks)
|
||||||
|
{
|
||||||
|
const std::string& name = pak.getName();
|
||||||
|
std::string::const_iterator extit = name.end() - 4;
|
||||||
|
std::string baseName(name.begin(), extit);
|
||||||
|
|
||||||
|
HECL::ProjectPath pakWorkPath(mp3WorkPath, baseName);
|
||||||
|
pakWorkPath.makeDir();
|
||||||
|
HECL::ProjectPath pakCookPath(mp3CookPath, baseName);
|
||||||
|
pakCookPath.makeDir();
|
||||||
|
pak.extractResources(pakWorkPath, pakCookPath, force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doMPTFE)
|
||||||
|
{
|
||||||
|
HECL::ProjectPath feWorkPath(project.getProjectRootPath(), "fe");
|
||||||
|
feWorkPath.makeDir();
|
||||||
|
for (const NOD::DiscBase::IPartition::Node* node : m_feNonPaks)
|
||||||
|
node->extractToDirectory(feWorkPath.getAbsolutePath(), force);
|
||||||
|
|
||||||
|
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP3);
|
||||||
|
cookPath.makeDir();
|
||||||
|
HECL::ProjectPath feCookPath(cookPath, "fe");
|
||||||
|
feCookPath.makeDir();
|
||||||
|
|
||||||
|
for (DNAMP3::PAKBridge& pak : m_fePaks)
|
||||||
|
{
|
||||||
|
const std::string& name = pak.getName();
|
||||||
|
std::string::const_iterator extit = name.end() - 4;
|
||||||
|
std::string baseName(name.begin(), extit);
|
||||||
|
|
||||||
|
HECL::ProjectPath pakWorkPath(feWorkPath, baseName);
|
||||||
|
pakWorkPath.makeDir();
|
||||||
|
HECL::ProjectPath pakCookPath(feCookPath, baseName);
|
||||||
|
pakCookPath.makeDir();
|
||||||
|
pak.extractResources(pakWorkPath, pakCookPath, force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkFromProject(HECL::Database::Project& proj)
|
bool checkFromProject(HECL::Database::Project& proj)
|
||||||
|
@ -221,7 +374,7 @@ struct SpecMP3 : SpecBase
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static HECL::Database::DataSpecEntry SpecMP3
|
HECL::Database::DataSpecEntry SpecEntMP3
|
||||||
(
|
(
|
||||||
_S("MP3"),
|
_S("MP3"),
|
||||||
_S("Data specification for original Metroid Prime 3 engine"),
|
_S("Data specification for original Metroid Prime 3 engine"),
|
||||||
|
|
2
NODLib
2
NODLib
|
@ -1 +1 @@
|
||||||
Subproject commit bcf67ca8eb94acbf3834fe01de033a4382626a13
|
Subproject commit f2c1ea646900f7fcca12b45878923014816295e9
|
Loading…
Reference in New Issue