metaforce/DataSpec/DNAMP2/DNAMP2.cpp

292 lines
10 KiB
C++
Raw Normal View History

2015-07-17 00:01:05 +00:00
#define NOD_ATHENA 1
2015-07-16 01:57:34 +00:00
#include "DNAMP2.hpp"
2015-07-17 00:01:05 +00:00
#include "STRG.hpp"
#include "MLVL.hpp"
#include "CMDL.hpp"
#include "ANCS.hpp"
2020-03-17 22:56:14 +00:00
#include "CINF.hpp"
2015-09-09 00:49:20 +00:00
#include "MREA.hpp"
#include "MAPA.hpp"
2017-03-20 05:09:53 +00:00
#include "MAPU.hpp"
2020-03-18 06:12:43 +00:00
#include "PATH.hpp"
#include "AFSM.hpp"
2016-07-24 01:51:15 +00:00
#include "SAVW.hpp"
2016-09-18 23:47:48 +00:00
#include "AGSC.hpp"
2016-09-08 02:01:29 +00:00
#include "../DNAMP1/HINT.hpp"
2016-09-18 23:47:48 +00:00
#include "../DNAMP1/CSNG.hpp"
2018-06-29 20:21:36 +00:00
#include "DataSpec/DNACommon/FSM2.hpp"
#include "DataSpec/DNACommon/TXTR.hpp"
#include "DataSpec/DNACommon/FONT.hpp"
#include "DataSpec/DNACommon/DGRP.hpp"
#include "DataSpec/DNACommon/ATBL.hpp"
2016-10-01 23:20:20 +00:00
#include "Runtime/GCNTypes.hpp"
2015-07-16 01:57:34 +00:00
2018-12-08 05:30:43 +00:00
namespace DataSpec::DNAMP2 {
2021-04-10 08:42:06 +00:00
logvisor::Module Log("DataSpec::DNAMP2");
2015-07-17 00:01:05 +00:00
2018-12-08 05:30:43 +00:00
static bool GetNoShare(std::string_view name) {
std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
2020-04-10 02:46:09 +00:00
if (lowerName.compare(0, 7, "metroid") == 0)
return false;
if (lowerName.compare(0, 8, "frontend") == 0)
2018-12-08 05:30:43 +00:00
return false;
return true;
}
2018-12-08 05:30:43 +00:00
PAKBridge::PAKBridge(const nod::Node& node, bool doExtract)
: m_node(node), m_pak(true, GetNoShare(node.getName())), m_doExtract(doExtract) {
nod::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs);
2015-07-17 00:01:05 +00:00
2018-12-08 05:30:43 +00:00
/* Append Level String */
for (const auto& entry : m_pak.m_entries) {
const DNAMP2::PAK::Entry& e = entry.second;
if (e.type == FOURCC('MLVL')) {
PAKEntryReadStream rs = e.beginReadStream(m_node);
MLVL mlvl;
mlvl.read(rs);
const DNAMP2::PAK::Entry* nameEnt = m_pak.lookupEntry(mlvl.worldNameId);
if (nameEnt) {
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
STRG mlvlName;
mlvlName.read(rs);
if (m_levelString.size())
m_levelString += ", ";
m_levelString += mlvlName.getUTF8(FOURCC('ENGL'), 0);
2018-12-08 05:30:43 +00:00
}
}
2018-12-08 05:30:43 +00:00
}
}
static std::string LayerName(std::string_view name) {
std::string ret(name);
2018-12-08 05:30:43 +00:00
for (auto& ch : ret)
if (ch == '/' || ch == '\\')
ch = '-';
2018-12-08 05:30:43 +00:00
return ret;
}
2018-12-08 05:30:43 +00:00
void PAKBridge::build() {
/* First pass: build per-area/per-layer dependency map */
for (const auto& entry : m_pak.m_entries) {
const DNAMP2::PAK::Entry& e = entry.second;
if (e.type == FOURCC('MLVL')) {
Level& level = m_levelDeps[e.id];
2018-12-08 05:30:43 +00:00
MLVL mlvl;
{
PAKEntryReadStream rs = e.beginReadStream(m_node);
mlvl.read(rs);
}
2019-10-01 07:38:03 +00:00
std::string catalogueName;
level.name = m_pak.bestEntryName(m_node, e, catalogueName);
2018-12-08 05:30:43 +00:00
level.areas.reserve(mlvl.areaCount);
unsigned layerIdx = 0;
2015-09-17 19:50:43 +00:00
2018-12-08 05:30:43 +00:00
/* Make MAPW available to lookup MAPAs */
const DNAMP2::PAK::Entry* worldMapEnt = m_pak.lookupEntry(mlvl.worldMap);
std::vector<UniqueID32> mapw;
if (worldMapEnt) {
PAKEntryReadStream rs = worldMapEnt->beginReadStream(m_node);
rs.seek(8, athena::SeekOrigin::Current);
2018-12-08 05:30:43 +00:00
atUint32 areaCount = rs.readUint32Big();
mapw.reserve(areaCount);
for (atUint32 i = 0; i < areaCount; ++i)
mapw.emplace_back(rs);
}
2015-10-26 02:31:09 +00:00
2018-12-08 05:30:43 +00:00
/* Index areas */
unsigned ai = 0;
for (const MLVL::Area& area : mlvl.areas) {
Level::Area& areaDeps = level.areas[area.areaMREAId];
MLVL::LayerFlags& layerFlags = mlvl.layerFlags[ai];
const DNAMP2::PAK::Entry* areaNameEnt = m_pak.lookupEntry(area.areaNameId);
if (areaNameEnt) {
STRG areaName;
{
PAKEntryReadStream rs = areaNameEnt->beginReadStream(m_node);
areaName.read(rs);
}
areaDeps.name = areaName.getUTF8(FOURCC('ENGL'), 0);
2018-12-08 05:30:43 +00:00
areaDeps.name = hecl::StringUtils::TrimWhitespace(areaDeps.name);
}
if (areaDeps.name.empty()) {
areaDeps.name = area.internalAreaName;
2018-12-08 05:30:43 +00:00
if (areaDeps.name.empty()) {
areaDeps.name = "MREA_" + area.areaMREAId.toString();
2018-12-08 05:30:43 +00:00
}
}
std::string num = fmt::format(FMT_STRING("{:02d} "), ai);
2018-12-08 05:30:43 +00:00
areaDeps.name = num + areaDeps.name;
2018-12-08 05:30:43 +00:00
areaDeps.layers.reserve(area.depLayerCount - 1);
unsigned r = 0;
for (unsigned l = 1; l < area.depLayerCount; ++l) {
areaDeps.layers.emplace_back();
Level::Area::Layer& layer = areaDeps.layers.back();
layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.active = layerFlags.flags >> (l - 1) & 0x1;
layer.name = hecl::StringUtils::TrimWhitespace(layer.name);
num = fmt::format(FMT_STRING("{:02d} "), l - 1);
2018-12-08 05:30:43 +00:00
layer.name = num + layer.name;
2015-09-01 02:54:02 +00:00
2018-12-08 05:30:43 +00:00
layer.resources.reserve(area.depLayers[l] - r);
for (; r < area.depLayers[l]; ++r)
layer.resources.emplace(area.deps[r].id);
}
2018-12-08 05:30:43 +00:00
areaDeps.resources.reserve(area.depCount - r + 2);
for (; r < area.depCount; ++r)
areaDeps.resources.emplace(area.deps[r].id);
areaDeps.resources.emplace(area.areaMREAId);
if (mapw.size() > ai)
areaDeps.resources.emplace(mapw[ai]);
++ai;
}
}
2018-12-08 05:30:43 +00:00
}
2018-12-08 05:30:43 +00:00
/* Second pass: cross-compare uniqueness */
for (auto& entry : m_pak.m_entries) {
entry.second.unique.checkEntry(*this, entry.second);
}
}
2018-12-08 05:30:43 +00:00
void PAKBridge::addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter, CharacterAssociations<UniqueID32>& charAssoc) const {
for (const auto& entry : m_pak.m_entries) {
2018-12-08 05:30:43 +00:00
if (entry.second.type == FOURCC('ANCS')) {
PAKEntryReadStream rs = entry.second.beginReadStream(m_node);
ANCS ancs;
ancs.read(rs);
for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters) {
2019-10-01 07:38:03 +00:00
charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskr] =
2020-04-11 22:51:39 +00:00
std::make_pair(entry.second.id, fmt::format(FMT_STRING("{}_{}.CSKR"), ci.name, ci.cskr));
2019-07-20 04:27:21 +00:00
if (ci.cmdlIce.isValid()) {
2019-10-01 07:38:03 +00:00
charAssoc.m_cmdlRigs[ci.cmdlIce] = {ci.cskrIce, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskrIce] =
2020-04-11 22:51:39 +00:00
std::make_pair(entry.second.id, fmt::format(FMT_STRING("{}.ICE_{}.CSKR"), ci.name, ci.cskrIce));
}
2018-12-08 05:30:43 +00:00
}
}
2018-12-08 05:30:43 +00:00
}
}
2020-03-18 06:12:43 +00:00
void PAKBridge::addPATHToMREA(PAKRouter<PAKBridge>& pakRouter,
std::unordered_map<UniqueID32, UniqueID32>& pathToMrea) const {
for (const auto& [id, entry] : m_pak.m_entries) {
if (entry.type == FOURCC('MREA')) {
PAKEntryReadStream rs = entry.beginReadStream(m_node);
UniqueID32 pathID = MREA::GetPATHId(rs);
if (pathID.isValid())
pathToMrea[pathID] = id;
}
}
}
2018-12-08 01:49:15 +00:00
static const atVec4f BottomRow = {{0.f, 0.f, 0.f, 1.f}};
2016-10-01 23:20:20 +00:00
void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
2018-12-08 05:30:43 +00:00
std::unordered_map<UniqueID32, zeus::CMatrix4f>& addTo,
std::unordered_map<UniqueID32, hecl::ProjectPath>& pathOverrides) const {
for (const auto& entry : m_pak.m_entries) {
2018-12-08 05:30:43 +00:00
if (entry.second.type == FOURCC('MLVL')) {
MLVL mlvl;
{
PAKEntryReadStream rs = entry.second.beginReadStream(m_node);
mlvl.read(rs);
}
hecl::ProjectPath mlvlDirPath = pakRouter.getWorking(&entry.second).getParentPath();
2016-10-01 23:20:20 +00:00
2019-07-20 04:27:21 +00:00
if (mlvl.worldNameId.isValid())
2021-06-07 19:29:18 +00:00
pathOverrides[mlvl.worldNameId] =
hecl::ProjectPath(mlvlDirPath, fmt::format(FMT_STRING("!name_{}.yaml"), mlvl.worldNameId));
2016-10-01 23:20:20 +00:00
2018-12-08 05:30:43 +00:00
for (const MLVL::Area& area : mlvl.areas) {
2020-03-18 06:12:43 +00:00
{
/* Get PATH transform */
const nod::Node* areaNode;
const PAK::Entry* areaEntry = pakRouter.lookupEntry(area.areaMREAId, &areaNode);
PAKEntryReadStream rs = areaEntry->beginReadStream(*areaNode);
UniqueID32 pathId = MREA::GetPATHId(rs);
if (pathId.isValid())
addTo[pathId] = zeus::CMatrix4f(area.transformMtx[0], area.transformMtx[1], area.transformMtx[2], BottomRow)
2021-06-07 19:29:18 +00:00
.transposed();
2020-03-18 06:12:43 +00:00
}
2018-12-08 05:30:43 +00:00
hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath();
2019-07-20 04:27:21 +00:00
if (area.areaNameId.isValid())
2021-06-07 19:29:18 +00:00
pathOverrides[area.areaNameId] =
hecl::ProjectPath(areaDirPath, fmt::format(FMT_STRING("!name_{}.yaml"), area.areaNameId));
2018-12-08 05:30:43 +00:00
}
2016-10-01 23:20:20 +00:00
2019-07-20 04:27:21 +00:00
if (mlvl.worldMap.isValid()) {
2018-12-08 05:30:43 +00:00
const nod::Node* mapNode;
const DNAMP2::PAK::Entry* mapEntry = pakRouter.lookupEntry(mlvl.worldMap, &mapNode);
if (mapEntry) {
PAKEntryReadStream rs = mapEntry->beginReadStream(*mapNode);
u32 magic = rs.readUint32Big();
if (magic == 0xDEADF00D) {
rs.readUint32Big();
u32 count = rs.readUint32Big();
for (u32 i = 0; i < count && i < mlvl.areas.size(); ++i) {
MLVL::Area& areaData = mlvl.areas[i];
UniqueID32 mapaId;
mapaId.read(rs);
addTo[mapaId] = zeus::CMatrix4f(areaData.transformMtx[0], areaData.transformMtx[1],
areaData.transformMtx[2], BottomRow)
.transposed();
2016-10-01 23:20:20 +00:00
}
2018-12-08 05:30:43 +00:00
}
2016-10-01 23:20:20 +00:00
}
2018-12-08 05:30:43 +00:00
}
2016-10-01 23:20:20 +00:00
}
2018-12-08 05:30:43 +00:00
}
2016-10-01 23:20:20 +00:00
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, const DNAMP2::PAK& pak,
2018-12-08 05:30:43 +00:00
const DNAMP2::PAK::Entry& entry) {
2019-07-20 04:27:21 +00:00
switch (entry.type.toUint32()) {
2018-12-08 05:30:43 +00:00
case SBIG('HINT'):
return {DNAMP1::HINT::Extract, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('STRG'):
return {STRG::Extract, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('TXTR'):
return {TXTR::Extract, {".png"}};
2018-12-08 05:30:43 +00:00
case SBIG('AFSM'):
return {AFSM::Extract, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('SAVW'):
return {SAVWCommon::ExtractSAVW<SAVW>, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('CMDL'):
return {CMDL::Extract, {".blend"}, 1};
2020-03-17 22:56:14 +00:00
case SBIG('CINF'):
return {CINF::Extract<PAKBridge>, {".blend"}, 1};
2018-12-08 05:30:43 +00:00
case SBIG('ANCS'):
return {ANCS::Extract, {".yaml", ".blend"}, 2};
2018-12-08 05:30:43 +00:00
case SBIG('MLVL'):
return {MLVL::Extract, {".yaml", ".blend"}, 3};
2018-12-08 05:30:43 +00:00
case SBIG('MREA'):
return {MREA::Extract, {".blend"}, 4};
2018-12-08 05:30:43 +00:00
case SBIG('MAPA'):
return {MAPA::Extract, {".blend"}, 4};
2018-12-08 05:30:43 +00:00
case SBIG('MAPU'):
return {MAPU::Extract, {".blend"}, 5};
2020-04-09 05:19:50 +00:00
case SBIG('PATH'):
return {PATH::Extract, {".blend"}, 5};
2018-12-08 05:30:43 +00:00
case SBIG('FSM2'):
return {DNAFSM2::ExtractFSM2<UniqueID32>, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('FONT'):
return {DNAFont::ExtractFONT<UniqueID32>, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('DGRP'):
return {DNADGRP::ExtractDGRP<UniqueID32>, {".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('AGSC'):
return {AGSC::Extract, {}};
case SBIG('CSNG'):
return {DNAMP1::CSNG::Extract, {".mid", ".yaml"}};
2018-12-08 05:30:43 +00:00
case SBIG('ATBL'):
return {DNAAudio::ATBL::Extract, {".yaml"}};
2018-12-08 05:30:43 +00:00
}
return {};
2015-07-17 00:01:05 +00:00
}
2018-12-08 05:30:43 +00:00
} // namespace DataSpec::DNAMP2