mirror of https://github.com/AxioDL/metaforce.git
Added area/layer directory indexing for MP1/2
This commit is contained in:
parent
7b1ec6c4b9
commit
6e58666a80
|
@ -206,13 +206,51 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/* Resource extractor type */
|
||||
typedef struct
|
||||
struct UniqueResult
|
||||
{
|
||||
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;
|
||||
unsigned weight;
|
||||
} ResExtractor;
|
||||
};
|
||||
|
||||
/* PAKRouter (for detecting shared entry locations) */
|
||||
template <class BRIDGETYPE>
|
||||
|
@ -232,14 +270,17 @@ public:
|
|||
PAKRouter(const HECL::ProjectPath& working, const HECL::ProjectPath& cooked)
|
||||
: m_gameWorking(working), m_gameCooked(cooked),
|
||||
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_sharedEntries.clear();
|
||||
size_t count = 0;
|
||||
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();
|
||||
for (const auto& entry : pak.m_idMap)
|
||||
{
|
||||
|
@ -274,7 +315,7 @@ public:
|
|||
}
|
||||
|
||||
HECL::ProjectPath getWorking(const typename BRIDGETYPE::PAKType::Entry* entry,
|
||||
const ResExtractor& extractor) const
|
||||
const ResExtractor<BRIDGETYPE>& extractor) const
|
||||
{
|
||||
if (!m_pak)
|
||||
LogDNACommon.report(LogVisor::FatalError,
|
||||
|
@ -282,10 +323,11 @@ public:
|
|||
auto uniqueSearch = m_uniqueEntries.find(entry->id);
|
||||
if (uniqueSearch != m_uniqueEntries.end())
|
||||
{
|
||||
HECL::ProjectPath uniquePath = entry->unique.uniquePath(m_pakWorking);
|
||||
HECL::SystemString entName = m_pak->bestEntryName(*entry);
|
||||
if (extractor.fileExt)
|
||||
entName += extractor.fileExt;
|
||||
return HECL::ProjectPath(m_pakWorking, entName);
|
||||
return HECL::ProjectPath(uniquePath, entName);
|
||||
}
|
||||
auto sharedSearch = m_sharedEntries.find(entry->id);
|
||||
if (sharedSearch != m_sharedEntries.end())
|
||||
|
@ -295,7 +337,7 @@ public:
|
|||
entName += extractor.fileExt;
|
||||
HECL::ProjectPath sharedPath(m_sharedWorking, entName);
|
||||
HECL::ProjectPath uniquePath(m_pakWorking, entName);
|
||||
if (extractor.func)
|
||||
if (extractor.func_a || extractor.func_b)
|
||||
uniquePath.makeLinkTo(sharedPath);
|
||||
m_sharedWorking.makeDir();
|
||||
return sharedPath;
|
||||
|
@ -312,7 +354,8 @@ public:
|
|||
auto uniqueSearch = m_uniqueEntries.find(entry->id);
|
||||
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);
|
||||
if (sharedSearch != m_sharedEntries.end())
|
||||
|
@ -334,7 +377,7 @@ public:
|
|||
{
|
||||
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)
|
||||
continue;
|
||||
|
||||
|
@ -348,12 +391,20 @@ public:
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,11 @@ namespace Retro
|
|||
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);
|
||||
|
||||
CMDL::Header head;
|
||||
|
@ -30,7 +33,7 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
|
|||
/* Open Py Stream */
|
||||
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut();
|
||||
os << "import bmesh\n"
|
||||
"import bpy;\n";
|
||||
"import bpy\n"
|
||||
"bm = bmesh.new()\n";
|
||||
MaterialSet::RegisterMaterialProps(os);
|
||||
|
||||
|
@ -38,6 +41,7 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
|
|||
{
|
||||
atUint64 secStart = reader.position();
|
||||
|
||||
/*
|
||||
std::unique_ptr<atVec3f[]> vertPos;
|
||||
std::unique_ptr<atVec3f[]> vertNorm;
|
||||
typedef atInt16 ShortVec3[3];
|
||||
|
@ -45,11 +49,44 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
|
|||
std::unique_ptr<atVec2f[]> vertUVs;
|
||||
typedef atInt16 ShortVec2[2];
|
||||
std::unique_ptr<ShortVec2[]> vertUVsShort;
|
||||
*/
|
||||
std::vector<std::vector<unsigned>> matUVCounts;
|
||||
matUVCounts.reserve(head.matSetCount);
|
||||
bool visitedDLOffsets = false;
|
||||
if (s < head.matSetCount)
|
||||
{
|
||||
MaterialSet matSet;
|
||||
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
|
||||
{
|
||||
|
@ -59,31 +96,44 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
|
|||
{
|
||||
/* Positions */
|
||||
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)
|
||||
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;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
/* Normals */
|
||||
os << "normals = []\n";
|
||||
if (head.flags.shortNormals())
|
||||
{
|
||||
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)
|
||||
{
|
||||
vertNormShort[i][0] = reader.readInt16();
|
||||
vertNormShort[i][1] = reader.readInt16();
|
||||
vertNormShort[i][2] = reader.readInt16();
|
||||
//vertNormShort[i][0] = reader.readInt16();
|
||||
//vertNormShort[i][1] = reader.readInt16();
|
||||
//vertNormShort[i][2] = reader.readInt16();
|
||||
os.format("normals.append((%f,%f,%f))\n",
|
||||
reader.readInt16(), reader.readInt16(), reader.readInt16());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
@ -95,23 +145,30 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
|
|||
case 3:
|
||||
{
|
||||
/* Float UVs */
|
||||
os << "uv_list = []\n";
|
||||
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)
|
||||
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;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
/* Short UVs */
|
||||
os << "suv_list = []\n";
|
||||
if (head.flags.shortUVs())
|
||||
{
|
||||
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)
|
||||
{
|
||||
vertUVsShort[i][0] = reader.readInt16();
|
||||
vertUVsShort[i][1] = reader.readInt16();
|
||||
os.format("suv_list.append((%f,%f))\n",
|
||||
reader.readInt16(), reader.readInt16());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -131,7 +188,6 @@ bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReade
|
|||
/* GX Display List (surface) */
|
||||
SurfaceHeader sHead;
|
||||
sHead.read(reader);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../DNACommon/DNACommon.hpp"
|
||||
#include "CMDLMaterials.hpp"
|
||||
#include "BlenderConnection.hpp"
|
||||
#include "DNAMP1.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
|
@ -48,14 +49,18 @@ struct CMDL
|
|||
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();
|
||||
if (!conn.createBlend(outPath.getAbsolutePath()))
|
||||
return false;
|
||||
return ReadToBlender(conn, rs);
|
||||
return ReadToBlender(conn, rs, pakRouter);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -18,11 +18,8 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
|||
{
|
||||
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
||||
m_pak.read(rs);
|
||||
}
|
||||
|
||||
HECL::SystemString PAKBridge::getLevelString() const
|
||||
{
|
||||
HECL::SystemString retval;
|
||||
/* Append Level String */
|
||||
for (const PAK::Entry& entry : m_pak.m_entries)
|
||||
{
|
||||
if (entry.type == Retro::MLVL)
|
||||
|
@ -36,27 +33,147 @@ HECL::SystemString PAKBridge::getLevelString() const
|
|||
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
|
||||
STRG mlvlName;
|
||||
mlvlName.read(rs);
|
||||
if (retval.size())
|
||||
retval += _S(", ");
|
||||
retval += mlvlName.getSystemString(ENGL, 0);
|
||||
if (m_levelString.size())
|
||||
m_levelString += _S(", ");
|
||||
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;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
case SBIG('STRG'):
|
||||
return {STRG::Extract, ".yaml"};
|
||||
return {STRG::Extract, nullptr, ".yaml"};
|
||||
case SBIG('TXTR'):
|
||||
return {TXTR::Extract, ".png"};
|
||||
return {TXTR::Extract, nullptr, ".png"};
|
||||
case SBIG('CMDL'):
|
||||
return {CMDL::Extract, ".blend", 1};
|
||||
return {nullptr, CMDL::Extract, ".blend", 1};
|
||||
case SBIG('MLVL'):
|
||||
return {MLVL::Extract, ".yaml"};
|
||||
return {MLVL::Extract, nullptr, ".yaml"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -17,11 +17,26 @@ class PAKBridge
|
|||
HECL::Database::Project& m_project;
|
||||
const NOD::DiscBase::IPartition::Node& m_node;
|
||||
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:
|
||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||
static ResExtractor LookupExtractor(const PAK::Entry& entry);
|
||||
const std::string& getName() const {return m_node.getName();}
|
||||
HECL::SystemString getLevelString() const;
|
||||
void build();
|
||||
static ResExtractor<PAKBridge> LookupExtractor(const PAK::Entry& entry);
|
||||
inline const std::string& getName() const {return m_node.getName();}
|
||||
inline const HECL::SystemString& getLevelString() const {return m_levelString;}
|
||||
|
||||
typedef PAK PAKType;
|
||||
inline const PAKType& getPAK() const {return m_pak;}
|
||||
|
|
|
@ -34,6 +34,7 @@ struct PAK : BigDNA
|
|||
UniqueID32 id;
|
||||
Value<atUint32> size;
|
||||
Value<atUint32> offset;
|
||||
UniqueResult unique;
|
||||
|
||||
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
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <unordered_map>
|
||||
#include "../DNACommon/DNACommon.hpp"
|
||||
#include "../DNACommon/STRG.hpp"
|
||||
#include "DNAMP1.hpp"
|
||||
|
||||
namespace Retro
|
||||
{
|
||||
|
|
|
@ -16,11 +16,8 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
|||
{
|
||||
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
||||
m_pak.read(rs);
|
||||
}
|
||||
|
||||
HECL::SystemString PAKBridge::getLevelString() const
|
||||
{
|
||||
HECL::SystemString retval;
|
||||
/* Append Level String */
|
||||
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
|
||||
{
|
||||
if (entry.type == Retro::MLVL)
|
||||
|
@ -34,23 +31,143 @@ HECL::SystemString PAKBridge::getLevelString() const
|
|||
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
|
||||
STRG mlvlName;
|
||||
mlvlName.read(rs);
|
||||
if (retval.size())
|
||||
retval += _S(", ");
|
||||
retval += mlvlName.getSystemString(ENGL, 0);
|
||||
if (m_levelString.size())
|
||||
m_levelString += _S(", ");
|
||||
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;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
case SBIG('STRG'):
|
||||
return {STRG::Extract, ".as"};
|
||||
return {STRG::Extract, nullptr, ".as"};
|
||||
case SBIG('TXTR'):
|
||||
return {TXTR::Extract, ".png"};
|
||||
return {TXTR::Extract, nullptr, ".png"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -17,11 +17,26 @@ class PAKBridge
|
|||
HECL::Database::Project& m_project;
|
||||
const NOD::DiscBase::IPartition::Node& m_node;
|
||||
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:
|
||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||
static ResExtractor LookupExtractor(const DNAMP1::PAK::Entry& entry);
|
||||
const std::string& getName() const {return m_node.getName();}
|
||||
HECL::SystemString getLevelString() const;
|
||||
void build();
|
||||
static ResExtractor<PAKBridge> LookupExtractor(const DNAMP1::PAK::Entry& entry);
|
||||
inline const std::string& getName() const {return m_node.getName();}
|
||||
inline const HECL::SystemString& getLevelString() const {return m_levelString;}
|
||||
|
||||
typedef DNAMP1::PAK PAKType;
|
||||
inline const PAKType& getPAK() const {return m_pak;}
|
||||
|
|
|
@ -70,6 +70,7 @@ struct MLVL : BigDNA
|
|||
|
||||
String<-1> internalAreaName;
|
||||
};
|
||||
Vector<Area, DNA_COUNT(areaCount)> areas;
|
||||
|
||||
UniqueID32 worldMap;
|
||||
Value<atUint8> unknown2;
|
||||
|
|
|
@ -18,10 +18,8 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
|||
{
|
||||
NOD::AthenaPartReadStream rs(node.beginReadStream());
|
||||
m_pak.read(rs);
|
||||
}
|
||||
|
||||
HECL::SystemString PAKBridge::getLevelString() const
|
||||
{
|
||||
/* Append Level String */
|
||||
std::set<HECL::SystemString, CaseInsensitiveCompare> uniq;
|
||||
for (const PAK::Entry& entry : m_pak.m_entries)
|
||||
{
|
||||
|
@ -40,26 +38,28 @@ HECL::SystemString PAKBridge::getLevelString() const
|
|||
}
|
||||
}
|
||||
}
|
||||
HECL::SystemString retval;
|
||||
bool comma = false;
|
||||
for (const HECL::SystemString& str : uniq)
|
||||
{
|
||||
if (comma)
|
||||
retval += _S(", ");
|
||||
m_levelString += _S(", ");
|
||||
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())
|
||||
{
|
||||
case SBIG('STRG'):
|
||||
return {STRG::Extract, ".as"};
|
||||
return {STRG::Extract, nullptr, ".as"};
|
||||
case SBIG('TXTR'):
|
||||
return {TXTR::Extract, ".png"};
|
||||
return {TXTR::Extract, nullptr, ".png"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -17,11 +17,13 @@ class PAKBridge
|
|||
HECL::Database::Project& m_project;
|
||||
const NOD::DiscBase::IPartition::Node& m_node;
|
||||
PAK m_pak;
|
||||
HECL::SystemString m_levelString;
|
||||
public:
|
||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||
static ResExtractor LookupExtractor(const PAK::Entry& entry);
|
||||
const std::string& getName() const {return m_node.getName();}
|
||||
HECL::SystemString getLevelString() const;
|
||||
void build();
|
||||
static ResExtractor<PAKBridge> LookupExtractor(const PAK::Entry& entry);
|
||||
inline const std::string& getName() const {return m_node.getName();}
|
||||
inline HECL::SystemString getLevelString() const {return m_levelString;}
|
||||
|
||||
typedef PAK PAKType;
|
||||
inline const PAKType& getPAK() const {return m_pak;}
|
||||
|
|
|
@ -41,6 +41,7 @@ struct PAK : BigDNA
|
|||
UniqueID64 id;
|
||||
Value<atUint32> size;
|
||||
Value<atUint32> offset;
|
||||
UniqueResult unique;
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue