Added area/layer directory indexing for MP1/2

This commit is contained in:
Jack Andersen 2015-08-03 16:14:47 -10:00
parent 7b1ec6c4b9
commit 6e58666a80
13 changed files with 455 additions and 73 deletions

View File

@ -206,13 +206,51 @@ public:
} }
}; };
/* Resource extractor type */ struct UniqueResult
typedef struct
{ {
std::function<bool(PAKEntryReadStream&, const HECL::ProjectPath&)> func; enum Type
{
UNIQUE_NOTFOUND,
UNIQUE_LEVEL,
UNIQUE_AREA,
UNIQUE_LAYER
} type = UNIQUE_NOTFOUND;
const HECL::SystemString* areaName = nullptr;
const HECL::SystemString* layerName = nullptr;
UniqueResult() = default;
UniqueResult(Type tp) : type(tp) {}
inline HECL::ProjectPath uniquePath(const HECL::ProjectPath& pakPath) const
{
if (type == UNIQUE_AREA)
{
HECL::ProjectPath areaDir(pakPath, *areaName);
areaDir.makeDir();
return areaDir;
}
else if (type == UNIQUE_LAYER)
{
HECL::ProjectPath areaDir(pakPath, *areaName);
areaDir.makeDir();
HECL::ProjectPath layerDir(areaDir, *layerName);
layerDir.makeDir();
return layerDir;
}
return pakPath;
}
};
template <class BRIDGETYPE>
class PAKRouter;
/* Resource extractor type */
template <class PAKBRIDGE>
struct ResExtractor
{
std::function<bool(PAKEntryReadStream&, const HECL::ProjectPath&)> func_a;
std::function<bool(PAKEntryReadStream&, const HECL::ProjectPath&, PAKRouter<PAKBRIDGE>&)> func_b;
const char* fileExt; const char* fileExt;
unsigned weight; unsigned weight;
} ResExtractor; };
/* PAKRouter (for detecting shared entry locations) */ /* PAKRouter (for detecting shared entry locations) */
template <class BRIDGETYPE> template <class BRIDGETYPE>
@ -232,14 +270,17 @@ public:
PAKRouter(const HECL::ProjectPath& working, const HECL::ProjectPath& cooked) PAKRouter(const HECL::ProjectPath& working, const HECL::ProjectPath& cooked)
: m_gameWorking(working), m_gameCooked(cooked), : m_gameWorking(working), m_gameCooked(cooked),
m_sharedWorking(working, "Shared"), m_sharedCooked(cooked, "Shared") {} m_sharedWorking(working, "Shared"), m_sharedCooked(cooked, "Shared") {}
void build(const std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress) void build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress)
{ {
m_uniqueEntries.clear(); m_uniqueEntries.clear();
m_sharedEntries.clear(); m_sharedEntries.clear();
size_t count = 0; size_t count = 0;
float bridgesSz = bridges.size(); float bridgesSz = bridges.size();
for (const BRIDGETYPE& bridge : bridges)
/* Route entries unique/shared per-pak */
for (BRIDGETYPE& bridge : bridges)
{ {
bridge.build();
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK(); const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
for (const auto& entry : pak.m_idMap) for (const auto& entry : pak.m_idMap)
{ {
@ -274,7 +315,7 @@ public:
} }
HECL::ProjectPath getWorking(const typename BRIDGETYPE::PAKType::Entry* entry, HECL::ProjectPath getWorking(const typename BRIDGETYPE::PAKType::Entry* entry,
const ResExtractor& extractor) const const ResExtractor<BRIDGETYPE>& extractor) const
{ {
if (!m_pak) if (!m_pak)
LogDNACommon.report(LogVisor::FatalError, LogDNACommon.report(LogVisor::FatalError,
@ -282,10 +323,11 @@ public:
auto uniqueSearch = m_uniqueEntries.find(entry->id); auto uniqueSearch = m_uniqueEntries.find(entry->id);
if (uniqueSearch != m_uniqueEntries.end()) if (uniqueSearch != m_uniqueEntries.end())
{ {
HECL::ProjectPath uniquePath = entry->unique.uniquePath(m_pakWorking);
HECL::SystemString entName = m_pak->bestEntryName(*entry); HECL::SystemString entName = m_pak->bestEntryName(*entry);
if (extractor.fileExt) if (extractor.fileExt)
entName += extractor.fileExt; entName += extractor.fileExt;
return HECL::ProjectPath(m_pakWorking, entName); return HECL::ProjectPath(uniquePath, entName);
} }
auto sharedSearch = m_sharedEntries.find(entry->id); auto sharedSearch = m_sharedEntries.find(entry->id);
if (sharedSearch != m_sharedEntries.end()) if (sharedSearch != m_sharedEntries.end())
@ -295,7 +337,7 @@ public:
entName += extractor.fileExt; entName += extractor.fileExt;
HECL::ProjectPath sharedPath(m_sharedWorking, entName); HECL::ProjectPath sharedPath(m_sharedWorking, entName);
HECL::ProjectPath uniquePath(m_pakWorking, entName); HECL::ProjectPath uniquePath(m_pakWorking, entName);
if (extractor.func) if (extractor.func_a || extractor.func_b)
uniquePath.makeLinkTo(sharedPath); uniquePath.makeLinkTo(sharedPath);
m_sharedWorking.makeDir(); m_sharedWorking.makeDir();
return sharedPath; return sharedPath;
@ -312,7 +354,8 @@ public:
auto uniqueSearch = m_uniqueEntries.find(entry->id); auto uniqueSearch = m_uniqueEntries.find(entry->id);
if (uniqueSearch != m_uniqueEntries.end()) if (uniqueSearch != m_uniqueEntries.end())
{ {
return HECL::ProjectPath(m_pakCooked, m_pak->bestEntryName(*entry)); HECL::ProjectPath uniquePath = entry->unique.uniquePath(m_pakCooked);
return HECL::ProjectPath(uniquePath, m_pak->bestEntryName(*entry));
} }
auto sharedSearch = m_sharedEntries.find(entry->id); auto sharedSearch = m_sharedEntries.find(entry->id);
if (sharedSearch != m_sharedEntries.end()) if (sharedSearch != m_sharedEntries.end())
@ -334,7 +377,7 @@ public:
{ {
for (const auto& item : m_pak->m_idMap) for (const auto& item : m_pak->m_idMap)
{ {
ResExtractor extractor = BRIDGETYPE::LookupExtractor(*item.second); ResExtractor<BRIDGETYPE> extractor = BRIDGETYPE::LookupExtractor(*item.second);
if (extractor.weight != w) if (extractor.weight != w)
continue; continue;
@ -348,12 +391,20 @@ public:
} }
HECL::ProjectPath working = getWorking(item.second, extractor); HECL::ProjectPath working = getWorking(item.second, extractor);
if (extractor.func) if (extractor.func_a) /* Doesn't need PAKRouter access */
{ {
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE) if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
{ {
PAKEntryReadStream s = item.second->beginReadStream(*m_node); PAKEntryReadStream s = item.second->beginReadStream(*m_node);
extractor.func(s, working); extractor.func_a(s, working);
}
}
else if (extractor.func_b) /* Needs PAKRouter access */
{
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
{
PAKEntryReadStream s = item.second->beginReadStream(*m_node);
extractor.func_b(s, working, *this);
} }
} }

View File

@ -8,8 +8,11 @@ namespace Retro
namespace DNAMP1 namespace DNAMP1
{ {
bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReader& reader) bool CMDL::ReadToBlender(HECL::BlenderConnection& conn,
Athena::io::IStreamReader& reader,
PAKRouter<PAKBridge>& pakRouter)
{ {
return true;
reader.setEndian(Athena::BigEndian); reader.setEndian(Athena::BigEndian);
CMDL::Header head; CMDL::Header head;
@ -30,7 +33,7 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
/* Open Py Stream */ /* Open Py Stream */
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut(); HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut();
os << "import bmesh\n" os << "import bmesh\n"
"import bpy;\n"; "import bpy\n"
"bm = bmesh.new()\n"; "bm = bmesh.new()\n";
MaterialSet::RegisterMaterialProps(os); MaterialSet::RegisterMaterialProps(os);
@ -38,6 +41,7 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
{ {
atUint64 secStart = reader.position(); atUint64 secStart = reader.position();
/*
std::unique_ptr<atVec3f[]> vertPos; std::unique_ptr<atVec3f[]> vertPos;
std::unique_ptr<atVec3f[]> vertNorm; std::unique_ptr<atVec3f[]> vertNorm;
typedef atInt16 ShortVec3[3]; typedef atInt16 ShortVec3[3];
@ -45,11 +49,44 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
std::unique_ptr<atVec2f[]> vertUVs; std::unique_ptr<atVec2f[]> vertUVs;
typedef atInt16 ShortVec2[2]; typedef atInt16 ShortVec2[2];
std::unique_ptr<ShortVec2[]> vertUVsShort; std::unique_ptr<ShortVec2[]> vertUVsShort;
*/
std::vector<std::vector<unsigned>> matUVCounts;
matUVCounts.reserve(head.matSetCount);
bool visitedDLOffsets = false; bool visitedDLOffsets = false;
if (s < head.matSetCount) if (s < head.matSetCount)
{ {
MaterialSet matSet; MaterialSet matSet;
matSet.read(reader); matSet.read(reader);
/* Texmaps */
os << "texmap_list = []\n";
for (const UniqueID32& tex : matSet.head.textureIDs)
{
os.format("if '%s' in bpy.data.textures:\n"
" image = bpy.data.images['%s']\n"
" texture = bpy.data.textures[image.name]\n"
"else:\n"
" image_path = os.path.relpath('../../%s/textures/%s.png')\n"
" print(os.getcwd()+image_path)\n"
" image = bpy.data.images.load('//' + image_path)\n"
" image.name = '%s'\n"
" texture = bpy.data.textures.new(image.name, 'IMAGE')\n"
" texture.image = image\n"
"texmap_list.append(texture)\n"
"\n");
}
matUVCounts.emplace_back();
std::vector<unsigned>& uvCounts = matUVCounts.back();
uvCounts.reserve(matSet.head.materialCount);
unsigned m=0;
for (const MaterialSet::Material& mat : matSet.materials)
{
uvCounts.emplace_back();
unsigned& uvCount = uvCounts.back();
MaterialSet::ConstructMaterial(os, mat, s, m++, uvCount);
}
} }
else else
{ {
@ -59,31 +96,44 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
{ {
/* Positions */ /* Positions */
size_t vertCount = head.secSizes[s] / 12; size_t vertCount = head.secSizes[s] / 12;
vertPos.reset(new atVec3f[vertCount]); //vertPos.reset(new atVec3f[vertCount]);
for (size_t i=0 ; i<vertCount ; ++i) for (size_t i=0 ; i<vertCount ; ++i)
vertPos[i] = reader.readVec3f(); {
//vertPos[i] = reader.readVec3f();
atVec3f pos = reader.readVec3f();
os.format("bm.verts.new(co=(%f,%f,%f))\n",
pos.vec[0], pos.vec[1], pos.vec[2]);
}
break; break;
} }
case 1: case 1:
{ {
/* Normals */ /* Normals */
os << "normals = []\n";
if (head.flags.shortNormals()) if (head.flags.shortNormals())
{ {
size_t normCount = head.secSizes[s] / 6; size_t normCount = head.secSizes[s] / 6;
vertNormShort.reset(new ShortVec3[normCount]); //vertNormShort.reset(new ShortVec3[normCount]);
for (size_t i=0 ; i<normCount ; ++i) for (size_t i=0 ; i<normCount ; ++i)
{ {
vertNormShort[i][0] = reader.readInt16(); //vertNormShort[i][0] = reader.readInt16();
vertNormShort[i][1] = reader.readInt16(); //vertNormShort[i][1] = reader.readInt16();
vertNormShort[i][2] = reader.readInt16(); //vertNormShort[i][2] = reader.readInt16();
os.format("normals.append((%f,%f,%f))\n",
reader.readInt16(), reader.readInt16(), reader.readInt16());
} }
} }
else else
{ {
size_t normCount = head.secSizes[s] / 12; size_t normCount = head.secSizes[s] / 12;
vertNorm.reset(new atVec3f[normCount]); //vertNorm.reset(new atVec3f[normCount]);
for (size_t i=0 ; i<normCount ; ++i) for (size_t i=0 ; i<normCount ; ++i)
vertNorm[i] = reader.readVec3f(); {
//vertNorm[i] = reader.readVec3f();
atVec3f norm = reader.readVec3f();
os.format("normals.append((%f,%f,%f))\n",
norm.vec[0], norm.vec[1], norm.vec[2]);
}
} }
break; break;
} }
@ -95,23 +145,30 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
case 3: case 3:
{ {
/* Float UVs */ /* Float UVs */
os << "uv_list = []\n";
size_t uvCount = head.secSizes[s] / 8; size_t uvCount = head.secSizes[s] / 8;
vertUVs.reset(new atVec2f[uvCount]); //vertUVs.reset(new atVec2f[uvCount]);
for (size_t i=0 ; i<uvCount ; ++i) for (size_t i=0 ; i<uvCount ; ++i)
vertUVs[i] = reader.readVec2f(); {
//vertUVs[i] = reader.readVec2f();
atVec2f uv = reader.readVec2f();
os.format("uv_list.append((%f,%f))\n",
uv.vec[0], uv.vec[1]);
}
break; break;
} }
case 4: case 4:
{ {
/* Short UVs */ /* Short UVs */
os << "suv_list = []\n";
if (head.flags.shortUVs()) if (head.flags.shortUVs())
{ {
size_t uvCount = head.secSizes[s] / 4; size_t uvCount = head.secSizes[s] / 4;
vertUVsShort.reset(new ShortVec2[uvCount]); //vertUVsShort.reset(new ShortVec2[uvCount]);
for (size_t i=0 ; i<uvCount ; ++i) for (size_t i=0 ; i<uvCount ; ++i)
{ {
vertUVsShort[i][0] = reader.readInt16(); os.format("suv_list.append((%f,%f))\n",
vertUVsShort[i][1] = reader.readInt16(); reader.readInt16(), reader.readInt16());
} }
break; break;
} }
@ -131,7 +188,6 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
/* GX Display List (surface) */ /* GX Display List (surface) */
SurfaceHeader sHead; SurfaceHeader sHead;
sHead.read(reader); sHead.read(reader);
} }
} }
} }

View File

@ -4,6 +4,7 @@
#include "../DNACommon/DNACommon.hpp" #include "../DNACommon/DNACommon.hpp"
#include "CMDLMaterials.hpp" #include "CMDLMaterials.hpp"
#include "BlenderConnection.hpp" #include "BlenderConnection.hpp"
#include "DNAMP1.hpp"
namespace Retro namespace Retro
{ {
@ -48,14 +49,18 @@ struct CMDL
Align<32> align; Align<32> align;
}; };
static bool ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReader& reader); static bool ReadToBlender(HECL::BlenderConnection& conn,
Athena::io::IStreamReader& reader,
PAKRouter<PAKBridge>& pakRouter);
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath) static bool Extract(PAKEntryReadStream& rs,
const HECL::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter)
{ {
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection(); HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
if (!conn.createBlend(outPath.getAbsolutePath())) if (!conn.createBlend(outPath.getAbsolutePath()))
return false; return false;
return ReadToBlender(conn, rs); return ReadToBlender(conn, rs, pakRouter);
} }
}; };

View File

@ -18,11 +18,8 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
{ {
NOD::AthenaPartReadStream rs(node.beginReadStream()); NOD::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs); m_pak.read(rs);
}
HECL::SystemString PAKBridge::getLevelString() const /* Append Level String */
{
HECL::SystemString retval;
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,27 +33,147 @@ HECL::SystemString 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);
if (retval.size()) if (m_levelString.size())
retval += _S(", "); m_levelString += _S(", ");
retval += mlvlName.getSystemString(ENGL, 0); m_levelString += mlvlName.getSystemString(ENGL, 0);
} }
} }
} }
}
UniqueResult PAKBridge::uniqueCheck(const PAK::Entry& entry)
{
UniqueResult::Type result = UniqueResult::UNIQUE_NOTFOUND;
bool foundOneLayer = false;
UniqueID32 areaId;
unsigned layerIdx;
for (const auto& pair : m_areaDeps)
{
unsigned l=0;
for (const auto& layer : pair.second.layers)
{
if (layer.resources.find(entry.id) != layer.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_LAYER;
areaId = pair.first;
layerIdx = l;
foundOneLayer = true;
}
++l;
}
if (pair.second.resources.find(entry.id) != pair.second.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_AREA;
areaId = pair.first;
foundOneLayer = true;
}
}
UniqueResult retval = {result};
if (result == UniqueResult::UNIQUE_LAYER || result == UniqueResult::UNIQUE_AREA)
{
const PAKBridge::Area& area = m_areaDeps[areaId];
retval.areaName = &area.name;
if (result == UniqueResult::UNIQUE_LAYER)
{
const PAKBridge::Area::Layer& layer = area.layers[layerIdx];
retval.layerName = &layer.name;
}
}
return retval; return retval;
} }
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) static HECL::SystemString LayerName(const std::string& name)
{
#if HECL_UCS2
HECL::SystemString ret = HECL::UTF8ToWide(mlvl.layerNames[layerIdx++]);
#else
HECL::SystemString ret = name;
#endif
for (auto& ch : ret)
if (ch == _S('/') || ch == _S('\\'))
ch = _S('-');
return ret;
}
void PAKBridge::build()
{
/* First pass: build per-area/per-layer dependency map */
for (const PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == Retro::MLVL)
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
mlvl.read(rs);
m_areaDeps.reserve(mlvl.areaCount);
unsigned layerIdx = 0;
for (const MLVL::Area& area : mlvl.areas)
{
Area& areaDeps = m_areaDeps[area.areaMREAId];
const PAK::Entry* areaNameEnt = m_pak.lookupEntry(area.areaNameId);
if (areaNameEnt)
{
STRG areaName;
PAKEntryReadStream rs = areaNameEnt->beginReadStream(m_node);
areaName.read(rs);
areaDeps.name = areaName.getSystemString(Retro::ENGL, 0);
}
areaDeps.layers.reserve(area.depLayerCount-1);
unsigned r=0;
for (unsigned l=1 ; l<area.depLayerCount ; ++l)
{
areaDeps.layers.emplace_back();
Area::Layer& layer = areaDeps.layers.back();
layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.resources.reserve(area.depLayers[l] - r);
for (; r<area.depLayers[l] ; ++r)
layer.resources.emplace(area.deps[r].id);
}
areaDeps.resources.reserve(area.depCount - r);
for (; r<area.depCount ; ++r)
areaDeps.resources.emplace(area.deps[r].id);
}
}
}
/* Second pass: cross-compare uniqueness */
for (PAK::Entry& entry : m_pak.m_entries)
{
entry.unique = uniqueCheck(entry);
}
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const PAK::Entry& entry)
{ {
switch (entry.type.toUint32()) switch (entry.type.toUint32())
{ {
case SBIG('STRG'): case SBIG('STRG'):
return {STRG::Extract, ".yaml"}; return {STRG::Extract, nullptr, ".yaml"};
case SBIG('TXTR'): case SBIG('TXTR'):
return {TXTR::Extract, ".png"}; return {TXTR::Extract, nullptr, ".png"};
case SBIG('CMDL'): case SBIG('CMDL'):
return {CMDL::Extract, ".blend", 1}; return {nullptr, CMDL::Extract, ".blend", 1};
case SBIG('MLVL'): case SBIG('MLVL'):
return {MLVL::Extract, ".yaml"}; return {MLVL::Extract, nullptr, ".yaml"};
} }
return {}; return {};
} }

View File

@ -17,11 +17,26 @@ 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;
struct Area
{
HECL::SystemString name;
struct Layer
{
HECL::SystemString name;
std::unordered_set<UniqueID32> resources;
};
std::vector<Layer> layers;
std::unordered_set<UniqueID32> resources;
};
std::unordered_map<UniqueID32, Area> m_areaDeps;
HECL::SystemString m_levelString;
UniqueResult uniqueCheck(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);
static ResExtractor LookupExtractor(const PAK::Entry& entry); void build();
const std::string& getName() const {return m_node.getName();} static ResExtractor<PAKBridge> LookupExtractor(const PAK::Entry& entry);
HECL::SystemString getLevelString() const; inline const std::string& getName() const {return m_node.getName();}
inline const HECL::SystemString& getLevelString() const {return m_levelString;}
typedef PAK PAKType; typedef PAK PAKType;
inline const PAKType& getPAK() const {return m_pak;} inline const PAKType& getPAK() const {return m_pak;}

View File

@ -34,6 +34,7 @@ struct PAK : BigDNA
UniqueID32 id; UniqueID32 id;
Value<atUint32> size; Value<atUint32> size;
Value<atUint32> offset; Value<atUint32> offset;
UniqueResult unique;
std::unique_ptr<atUint8[]> getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const; std::unique_ptr<atUint8[]> getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const;
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

View File

@ -4,6 +4,7 @@
#include <unordered_map> #include <unordered_map>
#include "../DNACommon/DNACommon.hpp" #include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/STRG.hpp" #include "../DNACommon/STRG.hpp"
#include "DNAMP1.hpp"
namespace Retro namespace Retro
{ {

View File

@ -16,11 +16,8 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
{ {
NOD::AthenaPartReadStream rs(node.beginReadStream()); NOD::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs); m_pak.read(rs);
}
HECL::SystemString PAKBridge::getLevelString() const /* Append Level String */
{
HECL::SystemString retval;
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries) for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
{ {
if (entry.type == Retro::MLVL) if (entry.type == Retro::MLVL)
@ -34,23 +31,143 @@ HECL::SystemString 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);
if (retval.size()) if (m_levelString.size())
retval += _S(", "); m_levelString += _S(", ");
retval += mlvlName.getSystemString(ENGL, 0); m_levelString += mlvlName.getSystemString(ENGL, 0);
} }
} }
} }
}
UniqueResult PAKBridge::uniqueCheck(const DNAMP1::PAK::Entry& entry)
{
UniqueResult::Type result = UniqueResult::UNIQUE_NOTFOUND;
bool foundOneLayer = false;
UniqueID32 areaId;
unsigned layerIdx;
for (const auto& pair : m_areaDeps)
{
unsigned l=0;
for (const auto& layer : pair.second.layers)
{
if (layer.resources.find(entry.id) != layer.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_LAYER;
areaId = pair.first;
layerIdx = l;
foundOneLayer = true;
}
++l;
}
if (pair.second.resources.find(entry.id) != pair.second.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_AREA;
areaId = pair.first;
foundOneLayer = true;
}
}
UniqueResult retval = {result};
if (result == UniqueResult::UNIQUE_LAYER || result == UniqueResult::UNIQUE_AREA)
{
const PAKBridge::Area& area = m_areaDeps[areaId];
retval.areaName = &area.name;
if (result == UniqueResult::UNIQUE_LAYER)
{
const PAKBridge::Area::Layer& layer = area.layers[layerIdx];
retval.layerName = &layer.name;
}
}
return retval; return retval;
} }
ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry) static HECL::SystemString LayerName(const std::string& name)
{
#if HECL_UCS2
HECL::SystemString ret = HECL::UTF8ToWide(mlvl.layerNames[layerIdx++]);
#else
HECL::SystemString ret = name;
#endif
for (auto& ch : ret)
if (ch == _S('/') || ch == _S('\\'))
ch = _S('-');
return ret;
}
void PAKBridge::build()
{
/* First pass: build per-area/per-layer dependency map */
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == Retro::MLVL)
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
mlvl.read(rs);
m_areaDeps.reserve(mlvl.areaCount);
unsigned layerIdx = 0;
for (const MLVL::Area& area : mlvl.areas)
{
Area& areaDeps = m_areaDeps[area.areaMREAId];
const DNAMP1::PAK::Entry* areaNameEnt = m_pak.lookupEntry(area.areaNameId);
if (areaNameEnt)
{
STRG areaName;
PAKEntryReadStream rs = areaNameEnt->beginReadStream(m_node);
areaName.read(rs);
areaDeps.name = areaName.getSystemString(Retro::ENGL, 0);
}
areaDeps.layers.reserve(area.depLayerCount-1);
unsigned r=0;
for (unsigned l=1 ; l<area.depLayerCount ; ++l)
{
areaDeps.layers.emplace_back();
Area::Layer& layer = areaDeps.layers.back();
layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.resources.reserve(area.depLayers[l] - r);
for (; r<area.depLayers[l] ; ++r)
layer.resources.emplace(area.deps[r].id);
}
areaDeps.resources.reserve(area.depCount - r);
for (; r<area.depCount ; ++r)
areaDeps.resources.emplace(area.deps[r].id);
}
}
}
/* Second pass: cross-compare uniqueness */
for (DNAMP1::PAK::Entry& entry : m_pak.m_entries)
{
entry.unique = uniqueCheck(entry);
}
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
{ {
switch (entry.type.toUint32()) switch (entry.type.toUint32())
{ {
case SBIG('STRG'): case SBIG('STRG'):
return {STRG::Extract, ".as"}; return {STRG::Extract, nullptr, ".as"};
case SBIG('TXTR'): case SBIG('TXTR'):
return {TXTR::Extract, ".png"}; return {TXTR::Extract, nullptr, ".png"};
} }
return {}; return {};
} }

View File

@ -17,11 +17,26 @@ 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;
struct Area
{
HECL::SystemString name;
struct Layer
{
HECL::SystemString name;
std::unordered_set<UniqueID32> resources;
};
std::vector<Layer> layers;
std::unordered_set<UniqueID32> resources;
};
std::unordered_map<UniqueID32, Area> m_areaDeps;
HECL::SystemString m_levelString;
UniqueResult uniqueCheck(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);
static ResExtractor LookupExtractor(const DNAMP1::PAK::Entry& entry); void build();
const std::string& getName() const {return m_node.getName();} static ResExtractor<PAKBridge> LookupExtractor(const DNAMP1::PAK::Entry& entry);
HECL::SystemString getLevelString() const; inline const std::string& getName() const {return m_node.getName();}
inline const HECL::SystemString& getLevelString() const {return m_levelString;}
typedef DNAMP1::PAK PAKType; typedef DNAMP1::PAK PAKType;
inline const PAKType& getPAK() const {return m_pak;} inline const PAKType& getPAK() const {return m_pak;}

View File

@ -70,6 +70,7 @@ struct MLVL : BigDNA
String<-1> internalAreaName; String<-1> internalAreaName;
}; };
Vector<Area, DNA_COUNT(areaCount)> areas;
UniqueID32 worldMap; UniqueID32 worldMap;
Value<atUint8> unknown2; Value<atUint8> unknown2;

View File

@ -18,10 +18,8 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
{ {
NOD::AthenaPartReadStream rs(node.beginReadStream()); NOD::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs); m_pak.read(rs);
}
HECL::SystemString PAKBridge::getLevelString() const /* Append Level String */
{
std::set<HECL::SystemString, CaseInsensitiveCompare> uniq; std::set<HECL::SystemString, CaseInsensitiveCompare> uniq;
for (const PAK::Entry& entry : m_pak.m_entries) for (const PAK::Entry& entry : m_pak.m_entries)
{ {
@ -40,26 +38,28 @@ HECL::SystemString PAKBridge::getLevelString() const
} }
} }
} }
HECL::SystemString retval;
bool comma = false; bool comma = false;
for (const HECL::SystemString& str : uniq) for (const HECL::SystemString& str : uniq)
{ {
if (comma) if (comma)
retval += _S(", "); m_levelString += _S(", ");
comma = true; comma = true;
retval += str; m_levelString += str;
} }
return retval;
} }
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry) void PAKBridge::build()
{
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const PAK::Entry& entry)
{ {
switch (entry.type.toUint32()) switch (entry.type.toUint32())
{ {
case SBIG('STRG'): case SBIG('STRG'):
return {STRG::Extract, ".as"}; return {STRG::Extract, nullptr, ".as"};
case SBIG('TXTR'): case SBIG('TXTR'):
return {TXTR::Extract, ".png"}; return {TXTR::Extract, nullptr, ".png"};
} }
return {}; return {};
} }

View File

@ -17,11 +17,13 @@ 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;
HECL::SystemString m_levelString;
public: public:
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node); PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
static ResExtractor LookupExtractor(const PAK::Entry& entry); void build();
const std::string& getName() const {return m_node.getName();} static ResExtractor<PAKBridge> LookupExtractor(const PAK::Entry& entry);
HECL::SystemString getLevelString() const; inline const std::string& getName() const {return m_node.getName();}
inline HECL::SystemString getLevelString() const {return m_levelString;}
typedef PAK PAKType; typedef PAK PAKType;
inline const PAKType& getPAK() const {return m_pak;} inline const PAKType& getPAK() const {return m_pak;}

View File

@ -41,6 +41,7 @@ struct PAK : BigDNA
UniqueID64 id; UniqueID64 id;
Value<atUint32> size; Value<atUint32> size;
Value<atUint32> offset; Value<atUint32> offset;
UniqueResult unique;
std::unique_ptr<atUint8[]> getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const; std::unique_ptr<atUint8[]> getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const;
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