diff options
Diffstat (limited to 'src/factories/bk64/GeoLayoutFactory.cpp')
| -rw-r--r-- | src/factories/bk64/GeoLayoutFactory.cpp | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/src/factories/bk64/GeoLayoutFactory.cpp b/src/factories/bk64/GeoLayoutFactory.cpp new file mode 100644 index 0000000..e1e08fa --- /dev/null +++ b/src/factories/bk64/GeoLayoutFactory.cpp @@ -0,0 +1,715 @@ +#include "GeoLayoutFactory.h" + +#include "Companion.h" +#include "spdlog/spdlog.h" +#include "types/RawBuffer.h" +#include "utils/Decompressor.h" +#include "utils/TorchUtils.h" +#include <cstring> +#include <deque> + +#define ALIGN8(val) (((val) + 7) & ~7) +#define YAML_HEX(num) YAML::Hex << (num) << YAML::Dec + +namespace BK64 { + +ExportResult GeoLayoutHeaderExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, + std::string& entryName, YAML::Node& node, std::string* replacement) { + const auto symbol = GetSafeNode(node, "symbol", entryName); + + if (Companion::Instance->IsOTRMode()) { + write << "static const ALIGN_ASSET(2) char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n"; + return std::nullopt; + } + + return std::nullopt; +} + +ExportResult GeoLayoutCodeExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, + std::string& entryName, YAML::Node& node, std::string* replacement) { + auto offset = GetSafeNode<uint32_t>(node, "offset"); + auto geo = std::static_pointer_cast<GeoLayoutData>(raw); + + return offset; +} + +// Serialized size of one geo command, 8-byte header included. +static uint32_t GetGeoCommandByteSize(const GeoLayoutCommand& cmd) { + uint32_t bodySize = 0; + switch (cmd.opCode) { + case GeoLayoutOpCode::UnknownCmd0: + bodySize = 16; + break; // 2+2+4+4+4 + case GeoLayoutOpCode::Sort: + bodySize = 32; + break; // 4*6+2+2+4 (matches GeoCmd1) + case GeoLayoutOpCode::Bone: + bodySize = 4; + break; // 1+1+2 + case GeoLayoutOpCode::LoadDL: + bodySize = 4; + break; // 2+2 + case GeoLayoutOpCode::Skinning: + // 2 per arg + 2 for terminator + bodySize = static_cast<uint32_t>(cmd.args.size()) * 2 + 2; + break; + case GeoLayoutOpCode::Branch: + bodySize = 4; + break; // 4 + case GeoLayoutOpCode::UnknownCmd7: + bodySize = 4; + break; // 2+2 + case GeoLayoutOpCode::LOD: + bodySize = 24; + break; // 4*5+4 + case GeoLayoutOpCode::ReferencePoint: + bodySize = 16; + break; // 2+2+4+4+4 + case GeoLayoutOpCode::Selector: + // 2+2 + (args.size()-2)*4 + bodySize = 4 + static_cast<uint32_t>(cmd.args.size() - 2) * 4; + break; + case GeoLayoutOpCode::DrawDistance: + bodySize = 16; + break; // 2*8 + case GeoLayoutOpCode::UnknownCmdE: + bodySize = 12; + break; // 2*6 + case GeoLayoutOpCode::UnknownCmdF: + bodySize = 16; + break; // 2+1+1+12 + case GeoLayoutOpCode::UnknownCmd10: + bodySize = 4; + break; // 4 + default: + break; + } + return 8 + bodySize; // 8 = opcode(4) + cmdLength(4) +} + +ExportResult BK64::GeoLayoutBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, + std::string& entryName, YAML::Node& node, std::string* replacement) { + const auto geo = std::static_pointer_cast<GeoLayoutData>(raw); + + // Size the buffer from each command's original offset plus its length. + uint32_t totalSize = 0; + for (const auto& cmd : geo->mCmds) { + uint32_t end = cmd.originalOffset + GetGeoCommandByteSize(cmd); + if (end > totalSize) + totalSize = end; + } + + // Zero-fill, then drop each command back at the offset it came from. + std::vector<uint8_t> buffer(totalSize, 0); + + for (const auto& cmd : geo->mCmds) { + uint32_t pos = cmd.originalOffset; + const auto& arguments = cmd.args; + + // Little helpers: write at pos, bump pos + auto writeU8 = [&](uint8_t v) { buffer[pos++] = v; }; + auto writeU16 = [&](uint16_t v) { + memcpy(&buffer[pos], &v, 2); + pos += 2; + }; + auto writeS16 = [&](int16_t v) { + memcpy(&buffer[pos], &v, 2); + pos += 2; + }; + auto writeU32 = [&](uint32_t v) { + memcpy(&buffer[pos], &v, 4); + pos += 4; + }; + auto writeS32 = [&](int32_t v) { + memcpy(&buffer[pos], &v, 4); + pos += 4; + }; + auto writeF32 = [&](float v) { + memcpy(&buffer[pos], &v, 4); + pos += 4; + }; + + // Header is opcode then cmdLength + writeU32(static_cast<uint32_t>(cmd.opCode)); + writeU32(cmd.cmdLength); + + switch (cmd.opCode) { + case GeoLayoutOpCode::UnknownCmd0: + writeU16(std::get<uint16_t>(arguments[0])); + writeU16(std::get<uint16_t>(arguments[1])); + writeF32(std::get<float>(arguments[2])); + writeF32(std::get<float>(arguments[3])); + writeF32(std::get<float>(arguments[4])); + break; + case GeoLayoutOpCode::Sort: + writeF32(std::get<float>(arguments[0])); + writeF32(std::get<float>(arguments[1])); + writeF32(std::get<float>(arguments[2])); + writeF32(std::get<float>(arguments[3])); + writeF32(std::get<float>(arguments[4])); + writeF32(std::get<float>(arguments[5])); + // [port] Decomp reads unk20 as s16, unk22 as s16, unk24 as s32, so + // we match the GeoCmd1 struct layout here, not the N64 BE byte layout. + writeS16(static_cast<int16_t>(std::get<uint8_t>(arguments[6]))); // unk20 (layoutOrder) + writeS16(static_cast<int16_t>(std::get<uint16_t>(arguments[7]))); // unk22 (firstChildOffset) + writeS32(static_cast<int32_t>(std::get<uint16_t>(arguments[8]))); // unk24 (secondChildOffset) + break; + case GeoLayoutOpCode::Bone: + writeU8(std::get<uint8_t>(arguments[0])); + writeU8(std::get<uint8_t>(arguments[1])); + writeU16(std::get<uint16_t>(arguments[2])); + break; + case GeoLayoutOpCode::LoadDL: + writeU16(std::get<uint16_t>(arguments[0])); + writeU16(std::get<uint16_t>(arguments[1])); + break; + case GeoLayoutOpCode::Skinning: + writeU16(std::get<uint16_t>(arguments[0])); + for (size_t i = 1; i < arguments.size(); i++) + writeU16(std::get<uint16_t>(arguments[i])); + writeU16(0); // terminator + break; + case GeoLayoutOpCode::Branch: + writeU32(std::get<uint32_t>(arguments[0])); + break; + case GeoLayoutOpCode::UnknownCmd7: + writeU16(0); // pad + writeU16(std::get<uint16_t>(arguments[0])); + break; + case GeoLayoutOpCode::LOD: + writeF32(std::get<float>(arguments[0])); + writeF32(std::get<float>(arguments[1])); + writeF32(std::get<float>(arguments[2])); + writeF32(std::get<float>(arguments[3])); + writeF32(std::get<float>(arguments[4])); + writeU32(std::get<uint32_t>(arguments[5])); + break; + case GeoLayoutOpCode::ReferencePoint: + writeU16(std::get<uint16_t>(arguments[0])); + writeU16(std::get<uint16_t>(arguments[1])); + writeF32(std::get<float>(arguments[2])); + writeF32(std::get<float>(arguments[3])); + writeF32(std::get<float>(arguments[4])); + break; + case GeoLayoutOpCode::Selector: + writeU16(std::get<uint16_t>(arguments[0])); + writeU16(std::get<uint16_t>(arguments[1])); + for (size_t i = 2; i < arguments.size(); i++) + writeU32(std::get<uint32_t>(arguments[i])); + break; + case GeoLayoutOpCode::DrawDistance: + writeS16(std::get<int16_t>(arguments[0])); + writeS16(std::get<int16_t>(arguments[1])); + writeS16(std::get<int16_t>(arguments[2])); + writeS16(std::get<int16_t>(arguments[3])); + writeS16(std::get<int16_t>(arguments[4])); + writeS16(std::get<int16_t>(arguments[5])); + writeS16(std::get<int16_t>(arguments[6])); + writeS16(std::get<int16_t>(arguments[7])); + break; + case GeoLayoutOpCode::UnknownCmdE: + writeS16(std::get<int16_t>(arguments[0])); + writeS16(std::get<int16_t>(arguments[1])); + writeS16(std::get<int16_t>(arguments[2])); + writeS16(std::get<int16_t>(arguments[3])); + writeS16(std::get<int16_t>(arguments[4])); + writeS16(std::get<int16_t>(arguments[5])); + break; + case GeoLayoutOpCode::UnknownCmdF: + writeU16(std::get<uint16_t>(arguments[0])); + writeU8(std::get<uint8_t>(arguments[1])); + writeU8(std::get<uint8_t>(arguments[2])); + for (size_t i = 3; i < arguments.size(); i++) + writeU8(std::get<uint8_t>(arguments[i])); + break; + case GeoLayoutOpCode::UnknownCmd10: + writeS32(std::get<int32_t>(arguments[0])); + break; + default: + throw std::runtime_error("BK64::GeoLayoutBinaryExporter: Unknown OpCode Found " + + std::to_string(static_cast<uint32_t>(cmd.opCode))); + } + } + + LUS::BinaryWriter output = LUS::BinaryWriter(); + WriteHeader(output, Torch::ResourceType::Blob, 0); + + output.Write(static_cast<uint32_t>(buffer.size())); + output.Write(reinterpret_cast<char*>(buffer.data()), buffer.size()); + output.Finish(write); + output.Close(); + + return std::nullopt; +} + +ExportResult GeoLayoutModdingExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, + std::string& entryName, YAML::Node& node, std::string* replacement) { + auto geo = std::static_pointer_cast<GeoLayoutData>(raw); + const auto symbol = GetSafeNode(node, "symbol", entryName); + + *replacement += ".yaml"; + + YAML::Emitter out; + out << YAML::BeginMap; + out << YAML::Key << symbol; + out << YAML::Value; + out.SetIndent(2); + out << YAML::BeginSeq; + std::deque<std::tuple<uint32_t, uint32_t, uint32_t>> childrenStack; + + for (auto& [opCode, cmdLength, arguments, origOff_] : geo->mCmds) { + uint32_t numChildren = 0; + uint32_t i = 0; + + out << YAML::Value; + out << YAML::BeginMap; + + switch (opCode) { + case GeoLayoutOpCode::UnknownCmd0: + out << YAML::Key << "UnknownCmd0"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "childOffset" << YAML::Value << YAML_HEX(std::get<uint16_t>(arguments.at(0))); + out << YAML::Key << "shouldRotatePitch" << YAML::Value << (bool)std::get<uint16_t>(arguments.at(1)); + out << YAML::Key << "x" << YAML::Value << std::get<float>(arguments.at(2)); + out << YAML::Key << "y" << YAML::Value << std::get<float>(arguments.at(3)); + out << YAML::Key << "z" << YAML::Value << std::get<float>(arguments.at(4)); + break; + case GeoLayoutOpCode::Sort: + out << YAML::Key << "Sort"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "x1" << YAML::Value << std::get<float>(arguments.at(0)); + out << YAML::Key << "y1" << YAML::Value << std::get<float>(arguments.at(1)); + out << YAML::Key << "z1" << YAML::Value << std::get<float>(arguments.at(2)); + out << YAML::Key << "x2" << YAML::Value << std::get<float>(arguments.at(3)); + out << YAML::Key << "y2" << YAML::Value << std::get<float>(arguments.at(4)); + out << YAML::Key << "z2" << YAML::Value << std::get<float>(arguments.at(5)); + out << YAML::Key << "layoutOrder" << YAML::Value << (uint32_t)std::get<uint8_t>(arguments.at(6)); + out << YAML::Key << "firstChildOffset" << YAML::Value << YAML_HEX(std::get<uint16_t>(arguments.at(7))); + out << YAML::Key << "secondChildOffset" << YAML::Value << YAML_HEX(std::get<uint16_t>(arguments.at(8))); + + if (std::get<uint16_t>(arguments.at(7)) != 0) { + numChildren++; + } + if (std::get<uint16_t>(arguments.at(8)) != 0) { + numChildren++; + } + break; + case GeoLayoutOpCode::Bone: + out << YAML::Key << "Bone"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "childOffset" << YAML::Value + << YAML_HEX((uint32_t)std::get<uint8_t>(arguments.at(0))); + out << YAML::Key << "boneId" << YAML::Value << (uint32_t)std::get<uint8_t>(arguments.at(1)); + out << YAML::Key << "unkBoneInfo" << YAML::Value << std::get<uint16_t>(arguments.at(2)); + if (std::get<uint8_t>(arguments.at(0)) != 0) { + numChildren++; + } + break; + case GeoLayoutOpCode::LoadDL: + out << YAML::Key << "LoadDL"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "dlIndex" << YAML::Value << std::get<uint16_t>(arguments.at(0)); + out << YAML::Key << "triCount" << YAML::Value << std::get<uint16_t>(arguments.at(1)); + break; + case GeoLayoutOpCode::Skinning: + out << YAML::Key << "Skinning"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "dlOffsetPreviousBone" << YAML::Value << std::get<uint16_t>(arguments.at(0)); + out << YAML::Key << "dlOffsets" << YAML::Value; + out << YAML::BeginSeq; + for (size_t j = 1; j < arguments.size(); j++) { + out << YAML::Value << std::get<uint16_t>(arguments.at(j)); + } + out << YAML::EndSeq; + break; + case GeoLayoutOpCode::Branch: + out << YAML::Key << "Branch"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "cmdTargetOffset" << YAML::Value << std::get<uint32_t>(arguments.at(0)); + break; + case GeoLayoutOpCode::UnknownCmd7: + out << YAML::Key << "UnknownCmd7"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "dlIndex" << YAML::Value << std::get<uint16_t>(arguments.at(0)); + break; + case GeoLayoutOpCode::LOD: + out << YAML::Key << "LOD"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "maxDistance" << YAML::Value << std::get<float>(arguments.at(0)); + out << YAML::Key << "minDistance" << YAML::Value << std::get<float>(arguments.at(1)); + out << YAML::Key << "x" << YAML::Value << std::get<float>(arguments.at(2)); + out << YAML::Key << "y" << YAML::Value << std::get<float>(arguments.at(3)); + out << YAML::Key << "z" << YAML::Value << std::get<float>(arguments.at(4)); + out << YAML::Key << "childOffset" << YAML::Value << YAML_HEX(std::get<uint32_t>(arguments.at(5))); + if (std::get<uint32_t>(arguments.at(5)) != 0) { + numChildren++; + } + break; + case GeoLayoutOpCode::ReferencePoint: + out << YAML::Key << "ReferencePoint"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "referencePointIndex" << YAML::Value << std::get<uint16_t>(arguments.at(0)); + out << YAML::Key << "boneIndex" << YAML::Value << std::get<uint16_t>(arguments.at(1)); + out << YAML::Key << "boneOffsetX" << YAML::Value << std::get<float>(arguments.at(2)); + out << YAML::Key << "boneOffsetY" << YAML::Value << std::get<float>(arguments.at(3)); + out << YAML::Key << "boneOffsetZ" << YAML::Value << std::get<float>(arguments.at(4)); + break; + case GeoLayoutOpCode::Selector: + out << YAML::Key << "Selector"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "childCount" << YAML::Value << std::get<uint16_t>(arguments.at(0)); + out << YAML::Key << "selectorIndex" << YAML::Value << std::get<uint16_t>(arguments.at(1)); + out << YAML::Key << "childOffsets" << YAML::Value; + out << YAML::BeginSeq; + for (size_t j = 2; j < arguments.size(); j++) { + out << YAML::Value << YAML_HEX(std::get<uint32_t>(arguments.at(j))); + if (std::get<uint32_t>(arguments.at(j)) != 0) { + numChildren++; + } + } + out << YAML::EndSeq; + break; + case GeoLayoutOpCode::DrawDistance: + out << YAML::Key << "DrawDistance"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "negX" << YAML::Value << std::get<int16_t>(arguments.at(0)); + out << YAML::Key << "negY" << YAML::Value << std::get<int16_t>(arguments.at(1)); + out << YAML::Key << "negZ" << YAML::Value << std::get<int16_t>(arguments.at(2)); + out << YAML::Key << "posX" << YAML::Value << std::get<int16_t>(arguments.at(3)); + out << YAML::Key << "posY" << YAML::Value << std::get<int16_t>(arguments.at(4)); + out << YAML::Key << "posZ" << YAML::Value << std::get<int16_t>(arguments.at(5)); + out << YAML::Key << "unk14" << YAML::Value << std::get<int16_t>(arguments.at(6)); + out << YAML::Key << "unk16" << YAML::Value << std::get<int16_t>(arguments.at(7)); + break; + case GeoLayoutOpCode::UnknownCmdE: + out << YAML::Key << "UnknownCmdE"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "coords1X" << YAML::Value << std::get<int16_t>(arguments.at(0)); + out << YAML::Key << "coords1Y" << YAML::Value << std::get<int16_t>(arguments.at(1)); + out << YAML::Key << "coords1Z" << YAML::Value << std::get<int16_t>(arguments.at(2)); + out << YAML::Key << "coords2X" << YAML::Value << std::get<int16_t>(arguments.at(3)); + out << YAML::Key << "coords2Y" << YAML::Value << std::get<int16_t>(arguments.at(4)); + out << YAML::Key << "coords2Z" << YAML::Value << std::get<int16_t>(arguments.at(5)); + break; + case GeoLayoutOpCode::UnknownCmdF: + out << YAML::Key << "UnknownCmdF"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "childOffset" << YAML::Value << YAML_HEX(std::get<uint16_t>(arguments.at(0))); + out << YAML::Key << "unkA" << YAML::Value << (uint32_t)std::get<uint8_t>(arguments.at(1)); + out << YAML::Key << "unkB" << YAML::Value << (uint32_t)std::get<uint8_t>(arguments.at(2)); + out << YAML::Key << "unkCBuf" << YAML::Value; + out << YAML::BeginSeq; + for (uint32_t j = 0; j < 12; j++) { + out << YAML::Value << (uint32_t)std::get<uint8_t>(arguments.at(j + 3)); + } + out << YAML::EndSeq; + if (std::get<uint16_t>(arguments.at(0)) != 0) { + numChildren++; + } + break; + case GeoLayoutOpCode::UnknownCmd10: + out << YAML::Key << "UnknownCmd10"; + out << YAML::Value << YAML::BeginMap; + out << YAML::Key << "wrapMode" << YAML::Value << std::get<int32_t>(arguments.at(0)); + break; + default: + throw std::runtime_error("BK64::GeoLayoutModdingExporter: Unknown OpCode Found " + + std::to_string(static_cast<uint32_t>(opCode))); + } + + out << YAML::Key << "CMD_LEN" << YAML::Value << cmdLength; + + if (numChildren > 0) { + childrenStack.emplace_back(0, numChildren, cmdLength); + out << YAML::Key << "Children" << YAML::Value << YAML::BeginMap; + out << YAML::Key << "Child0" << YAML::Value << YAML::BeginSeq; + continue; + } + + out << YAML::EndMap; + out << YAML::EndMap; + + while (cmdLength == 0 && !childrenStack.empty()) { + auto& [childrenProcessed, totalChildren, parentCmdLength] = childrenStack.back(); + if (++childrenProcessed >= totalChildren) { + out << YAML::EndSeq; + out << YAML::EndMap; + out << YAML::EndMap; + out << YAML::EndMap; + cmdLength = parentCmdLength; + childrenStack.pop_back(); + // Exit Child + } else { + // Go To Next Child + out << YAML::EndSeq; + out << YAML::Key << ("Child" + std::to_string(childrenProcessed)) << YAML::Value << YAML::BeginSeq; + break; + } + } + } + + out << YAML::EndSeq; + out << YAML::EndMap; + + write.write(out.c_str(), out.size()); + + return std::nullopt; +} + +std::optional<std::shared_ptr<IParsedData>> GeoLayoutFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { + auto [_, segment] = Decompressor::AutoDecode(node, buffer); + LUS::BinaryReader reader(segment.data, segment.size); + reader.SetEndianness(Torch::Endianness::Big); + const auto symbol = GetSafeNode<std::string>(node, "symbol"); + const auto offset = GetSafeNode<uint32_t>(node, "offset"); + + std::vector<GeoLayoutCommand> cmds; + + std::deque<uint32_t> offsetStack; + + offsetStack.push_back(0); + + while (true) { + std::vector<GeoLayoutArg> args; + auto localOffset = offsetStack.back(); + if (localOffset + 8 > segment.size) { + break; + } + reader.Seek(localOffset, LUS::SeekOffsetType::Start); + auto opCode = reader.ReadUInt32(); + auto cmdLength = reader.ReadUInt32(); + + offsetStack.back() += cmdLength; + + if (cmdLength == 0) { + offsetStack.pop_back(); + } + + switch (static_cast<GeoLayoutOpCode>(opCode)) { + case GeoLayoutOpCode::UnknownCmd0: { + auto childOffset = reader.ReadUInt16(); + auto shouldRotatePitch = reader.ReadUInt16(); + auto x = reader.ReadFloat(); + auto y = reader.ReadFloat(); + auto z = reader.ReadFloat(); + + args.emplace_back(childOffset); + args.emplace_back(shouldRotatePitch); + args.emplace_back(x); + args.emplace_back(y); + args.emplace_back(z); + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + break; + } + case GeoLayoutOpCode::Sort: { + auto x1 = reader.ReadFloat(); + auto y1 = reader.ReadFloat(); + auto z1 = reader.ReadFloat(); + auto x2 = reader.ReadFloat(); + auto y2 = reader.ReadFloat(); + auto z2 = reader.ReadFloat(); + + reader.ReadUByte(); // pad + auto layoutOrder = reader.ReadUByte(); + auto firstChildOffset = reader.ReadUInt16(); + reader.ReadUInt16(); // pad + auto secondChildOffset = reader.ReadUInt16(); + + args.emplace_back(x1); + args.emplace_back(y1); + args.emplace_back(z1); + args.emplace_back(x2); + args.emplace_back(y2); + args.emplace_back(z2); + args.emplace_back(layoutOrder); + args.emplace_back(firstChildOffset); + args.emplace_back(secondChildOffset); + + if (firstChildOffset != 0) { + offsetStack.push_back(localOffset + firstChildOffset); + } + if (secondChildOffset != 0) { + offsetStack.push_back(localOffset + secondChildOffset); + } + break; + } + case GeoLayoutOpCode::Bone: { + auto childOffset = reader.ReadUByte(); + auto boneId = reader.ReadUByte(); + auto unkBoneInfo = reader.ReadUInt16(); + + args.emplace_back(childOffset); + args.emplace_back(boneId); + args.emplace_back(unkBoneInfo); + + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + break; + } + case GeoLayoutOpCode::LoadDL: { + auto dlIndex = reader.ReadUInt16(); + auto triCount = reader.ReadUInt16(); + args.emplace_back(dlIndex); + args.emplace_back(triCount); + break; + } + case GeoLayoutOpCode::Skinning: { + auto dlOffsetPreviousBone = reader.ReadUInt16(); + + args.emplace_back(dlOffsetPreviousBone); + + while (true) { + auto dlOffset = reader.ReadUInt16(); + if (dlOffset == 0) { + break; + } + args.emplace_back(dlOffset); + } + break; + } + case GeoLayoutOpCode::Branch: { + auto cmdTargetOffset = reader.ReadUInt32(); + + args.emplace_back(cmdTargetOffset); + break; + } + case GeoLayoutOpCode::UnknownCmd7: { + reader.ReadUInt16(); // pad + auto dlIndex = reader.ReadUInt16(); + + args.emplace_back(dlIndex); + break; + } + case GeoLayoutOpCode::LOD: { + auto maxDistance = reader.ReadFloat(); + auto minDistance = reader.ReadFloat(); + auto x = reader.ReadFloat(); + auto y = reader.ReadFloat(); + auto z = reader.ReadFloat(); + auto childOffset = reader.ReadUInt32(); + args.emplace_back(maxDistance); + args.emplace_back(minDistance); + args.emplace_back(x); + args.emplace_back(y); + args.emplace_back(z); + args.emplace_back(childOffset); + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + break; + } + case GeoLayoutOpCode::ReferencePoint: { + auto referencePointIndex = reader.ReadUInt16(); + auto boneIndex = reader.ReadUInt16(); + auto boneOffsetX = reader.ReadFloat(); + auto boneOffsetY = reader.ReadFloat(); + auto boneOffsetZ = reader.ReadFloat(); + + args.emplace_back(referencePointIndex); + args.emplace_back(boneIndex); + args.emplace_back(boneOffsetX); + args.emplace_back(boneOffsetY); + args.emplace_back(boneOffsetZ); + break; + } + case GeoLayoutOpCode::Selector: { + auto childCount = reader.ReadUInt16(); + auto selectorIndex = reader.ReadUInt16(); + + args.emplace_back(childCount); + args.emplace_back(selectorIndex); + + for (uint16_t i = 0; i < childCount; i++) { + auto childOffset = reader.ReadUInt32(); + + args.emplace_back(childOffset); + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + } + break; + } + case GeoLayoutOpCode::DrawDistance: { + auto negX = reader.ReadInt16(); + auto negY = reader.ReadInt16(); + auto negZ = reader.ReadInt16(); + auto posX = reader.ReadInt16(); + auto posY = reader.ReadInt16(); + auto posZ = reader.ReadInt16(); + auto childOffset = reader.ReadInt16(); + auto unk16 = reader.ReadInt16(); + + args.emplace_back(negX); + args.emplace_back(negY); + args.emplace_back(negZ); + args.emplace_back(posX); + args.emplace_back(posY); + args.emplace_back(posZ); + args.emplace_back(childOffset); + args.emplace_back(unk16); + // [port] unk14 is a child offset; the renderer follows it to recurse + // into child geo commands + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + break; + } + case GeoLayoutOpCode::UnknownCmdE: { + auto coords1X = reader.ReadInt16(); + auto coords1Y = reader.ReadInt16(); + auto coords1Z = reader.ReadInt16(); + auto unkE = reader.ReadInt16(); + auto childOffset = reader.ReadInt16(); + auto unk12 = reader.ReadInt16(); + + args.emplace_back(coords1X); + args.emplace_back(coords1Y); + args.emplace_back(coords1Z); + args.emplace_back(unkE); + args.emplace_back(childOffset); + args.emplace_back(unk12); + // [port] unk10 is a child offset; the renderer follows it to recurse + // into child geo commands + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + break; + } + case GeoLayoutOpCode::UnknownCmdF: { + auto childOffset = reader.ReadUInt16(); + auto unkA = reader.ReadUByte(); + auto unkB = reader.ReadUByte(); + args.emplace_back(childOffset); + args.emplace_back(unkA); + args.emplace_back(unkB); + for (int32_t i = 0; i < 12; i++) { + auto unkCBuf = reader.ReadUByte(); + args.emplace_back(unkCBuf); + } + if (childOffset != 0) { + offsetStack.push_back(localOffset + childOffset); + } + break; + } + case GeoLayoutOpCode::UnknownCmd10: { + auto wrapMode = reader.ReadInt32(); + args.emplace_back(wrapMode); + break; + } + default: + throw std::runtime_error("BK64::GeoLayoutFactory: Unknown OpCode Found " + std::to_string(opCode)); + } + cmds.emplace_back(static_cast<GeoLayoutOpCode>(opCode), cmdLength, args, localOffset); + + if (offsetStack.size() == 0) { + break; + } + } + + return std::make_shared<GeoLayoutData>(cmds); +} + +} // namespace BK64 |
