mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-12-22 08:08:53 -05:00
428 lines
11 KiB
C++
428 lines
11 KiB
C++
|
#include "ZRoom.h"
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <chrono>
|
||
|
#include <cinttypes>
|
||
|
#include <string_view>
|
||
|
|
||
|
#include "Commands/EndMarker.h"
|
||
|
#include "Commands/SetActorCutsceneList.h"
|
||
|
#include "Commands/SetActorList.h"
|
||
|
#include "Commands/SetAlternateHeaders.h"
|
||
|
#include "Commands/SetAnimatedMaterialList.h"
|
||
|
#include "Commands/SetCameraSettings.h"
|
||
|
#include "Commands/SetCollisionHeader.h"
|
||
|
#include "Commands/SetCsCamera.h"
|
||
|
#include "Commands/SetCutscenes.h"
|
||
|
#include "Commands/SetEchoSettings.h"
|
||
|
#include "Commands/SetEntranceList.h"
|
||
|
#include "Commands/SetExitList.h"
|
||
|
#include "Commands/SetLightList.h"
|
||
|
#include "Commands/SetLightingSettings.h"
|
||
|
#include "Commands/SetMesh.h"
|
||
|
#include "Commands/SetMinimapChests.h"
|
||
|
#include "Commands/SetMinimapList.h"
|
||
|
#include "Commands/SetObjectList.h"
|
||
|
#include "Commands/SetPathways.h"
|
||
|
#include "Commands/SetRoomBehavior.h"
|
||
|
#include "Commands/SetRoomList.h"
|
||
|
#include "Commands/SetSkyboxModifier.h"
|
||
|
#include "Commands/SetSkyboxSettings.h"
|
||
|
#include "Commands/SetSoundSettings.h"
|
||
|
#include "Commands/SetSpecialObjects.h"
|
||
|
#include "Commands/SetStartPositionList.h"
|
||
|
#include "Commands/SetTimeSettings.h"
|
||
|
#include "Commands/SetTransitionActorList.h"
|
||
|
#include "Commands/SetWind.h"
|
||
|
#include "Commands/SetWorldMapVisited.h"
|
||
|
#include "Commands/Unused09.h"
|
||
|
#include "Commands/Unused1D.h"
|
||
|
#include "Commands/ZRoomCommandUnk.h"
|
||
|
#include "Globals.h"
|
||
|
#include "Utils/File.h"
|
||
|
#include "Utils/Path.h"
|
||
|
#include "Utils/StringHelper.h"
|
||
|
#include "WarningHandler.h"
|
||
|
#include "ZBlob.h"
|
||
|
#include "ZCutscene.h"
|
||
|
#include "ZFile.h"
|
||
|
|
||
|
REGISTER_ZFILENODE(Room, ZRoom);
|
||
|
REGISTER_ZFILENODE(Scene, ZRoom);
|
||
|
REGISTER_ZFILENODE(AltHeader, ZRoom);
|
||
|
|
||
|
ZRoom::ZRoom(ZFile* nParent) : ZResource(nParent)
|
||
|
{
|
||
|
roomCount = -1;
|
||
|
canHaveInner = true;
|
||
|
RegisterOptionalAttribute("HackMode");
|
||
|
}
|
||
|
|
||
|
ZRoom::~ZRoom()
|
||
|
{
|
||
|
for (ZRoomCommand* cmd : commands)
|
||
|
delete cmd;
|
||
|
}
|
||
|
|
||
|
void ZRoom::ExtractFromXML(tinyxml2::XMLElement* reader, uint32_t nRawDataIndex)
|
||
|
{
|
||
|
ZResource::ExtractFromXML(reader, nRawDataIndex);
|
||
|
|
||
|
if (hackMode == "syotes_room")
|
||
|
{
|
||
|
SyotesRoomHack();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DeclareVar(name, "");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ZRoom::ExtractFromBinary(uint32_t nRawDataIndex, ZResourceType parentType)
|
||
|
{
|
||
|
rawDataIndex = nRawDataIndex;
|
||
|
name = GetDefaultName(parent->GetName());
|
||
|
|
||
|
zroomType = ZResourceType::AltHeader;
|
||
|
switch (parentType)
|
||
|
{
|
||
|
case ZResourceType::Scene:
|
||
|
case ZResourceType::Room:
|
||
|
case ZResourceType::AltHeader:
|
||
|
parentZroomType = parentType;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// TODO: error message or something
|
||
|
assert(false);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ParseRawData();
|
||
|
DeclareVar(name, "");
|
||
|
}
|
||
|
|
||
|
void ZRoom::ParseXML(tinyxml2::XMLElement* reader)
|
||
|
{
|
||
|
ZResource::ParseXML(reader);
|
||
|
|
||
|
// TODO: HACK: remove this specific check when the repo uses the correct HackMode="syotes_room"
|
||
|
if (name == "syotes_room_0")
|
||
|
{
|
||
|
hackMode = "syotes_room";
|
||
|
}
|
||
|
|
||
|
std::string nodeName = std::string(reader->Name());
|
||
|
if (nodeName == "Scene")
|
||
|
{
|
||
|
zroomType = ZResourceType::Scene;
|
||
|
}
|
||
|
else if (nodeName == "Room")
|
||
|
zroomType = ZResourceType::Room;
|
||
|
else if (nodeName == "AltHeader")
|
||
|
zroomType = ZResourceType::AltHeader;
|
||
|
|
||
|
if (reader->Attribute("HackMode") != nullptr)
|
||
|
{
|
||
|
hackMode = std::string(reader->Attribute("HackMode"));
|
||
|
if (hackMode != "syotes_room")
|
||
|
{
|
||
|
std::string headerError = StringHelper::Sprintf(
|
||
|
"invalid value found for 'HackMode' attribute: '%s'", hackMode.c_str());
|
||
|
HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex,
|
||
|
headerError, "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ZRoom::ParseRawData()
|
||
|
{
|
||
|
if (hackMode == "syotes_room")
|
||
|
return;
|
||
|
|
||
|
bool shouldContinue = true;
|
||
|
uint32_t currentIndex = 0;
|
||
|
uint32_t currentPtr = rawDataIndex;
|
||
|
|
||
|
const auto& rawData = parent->GetRawData();
|
||
|
while (shouldContinue)
|
||
|
{
|
||
|
RoomCommand opcode = static_cast<RoomCommand>(rawData.at(currentPtr));
|
||
|
|
||
|
ZRoomCommand* cmd = nullptr;
|
||
|
|
||
|
auto start = std::chrono::steady_clock::now();
|
||
|
|
||
|
switch (opcode)
|
||
|
{
|
||
|
case RoomCommand::SetStartPositionList:
|
||
|
cmd = new SetStartPositionList(parent);
|
||
|
break; // 0x00
|
||
|
case RoomCommand::SetActorList:
|
||
|
cmd = new SetActorList(parent);
|
||
|
break; // 0x01
|
||
|
case RoomCommand::SetCsCamera:
|
||
|
cmd = new SetCsCamera(parent);
|
||
|
break; // 0x02 (MM-ONLY)
|
||
|
case RoomCommand::SetCollisionHeader:
|
||
|
cmd = new SetCollisionHeader(parent);
|
||
|
break; // 0x03
|
||
|
case RoomCommand::SetRoomList:
|
||
|
cmd = new SetRoomList(parent);
|
||
|
break; // 0x04
|
||
|
case RoomCommand::SetWind:
|
||
|
cmd = new SetWind(parent);
|
||
|
break; // 0x05
|
||
|
case RoomCommand::SetEntranceList:
|
||
|
cmd = new SetEntranceList(parent);
|
||
|
break; // 0x06
|
||
|
case RoomCommand::SetSpecialObjects:
|
||
|
cmd = new SetSpecialObjects(parent);
|
||
|
break; // 0x07
|
||
|
case RoomCommand::SetRoomBehavior:
|
||
|
cmd = new SetRoomBehavior(parent);
|
||
|
break; // 0x08
|
||
|
case RoomCommand::Unused09:
|
||
|
cmd = new Unused09(parent);
|
||
|
break; // 0x09
|
||
|
case RoomCommand::SetMesh:
|
||
|
cmd = new SetMesh(parent);
|
||
|
break; // 0x0A
|
||
|
case RoomCommand::SetObjectList:
|
||
|
cmd = new SetObjectList(parent);
|
||
|
break; // 0x0B
|
||
|
case RoomCommand::SetLightList:
|
||
|
cmd = new SetLightList(parent);
|
||
|
break; // 0x0C (MM-ONLY)
|
||
|
case RoomCommand::SetPathways:
|
||
|
cmd = new SetPathways(parent);
|
||
|
break; // 0x0D
|
||
|
case RoomCommand::SetTransitionActorList:
|
||
|
cmd = new SetTransitionActorList(parent);
|
||
|
break; // 0x0E
|
||
|
case RoomCommand::SetLightingSettings:
|
||
|
cmd = new SetLightingSettings(parent);
|
||
|
break; // 0x0F
|
||
|
case RoomCommand::SetTimeSettings:
|
||
|
cmd = new SetTimeSettings(parent);
|
||
|
break; // 0x10
|
||
|
case RoomCommand::SetSkyboxSettings:
|
||
|
cmd = new SetSkyboxSettings(parent);
|
||
|
break; // 0x11
|
||
|
case RoomCommand::SetSkyboxModifier:
|
||
|
cmd = new SetSkyboxModifier(parent);
|
||
|
break; // 0x12
|
||
|
case RoomCommand::SetExitList:
|
||
|
cmd = new SetExitList(parent);
|
||
|
break; // 0x13
|
||
|
case RoomCommand::EndMarker:
|
||
|
cmd = new EndMarker(parent);
|
||
|
break; // 0x14
|
||
|
case RoomCommand::SetSoundSettings:
|
||
|
cmd = new SetSoundSettings(parent);
|
||
|
break; // 0x15
|
||
|
case RoomCommand::SetEchoSettings:
|
||
|
cmd = new SetEchoSettings(parent);
|
||
|
break; // 0x16
|
||
|
case RoomCommand::SetCutscenes:
|
||
|
cmd = new SetCutscenes(parent);
|
||
|
break; // 0x17
|
||
|
case RoomCommand::SetAlternateHeaders:
|
||
|
cmd = new SetAlternateHeaders(parent);
|
||
|
break; // 0x18
|
||
|
case RoomCommand::SetCameraSettings:
|
||
|
if (Globals::Instance->game == ZGame::MM_RETAIL)
|
||
|
cmd = new SetWorldMapVisited(parent);
|
||
|
else
|
||
|
cmd = new SetCameraSettings(parent);
|
||
|
break; // 0x19
|
||
|
case RoomCommand::SetAnimatedMaterialList:
|
||
|
cmd = new SetAnimatedMaterialList(parent);
|
||
|
break; // 0x1A (MM-ONLY)
|
||
|
case RoomCommand::SetActorCutsceneList:
|
||
|
cmd = new SetActorCutsceneList(parent);
|
||
|
break; // 0x1B (MM-ONLY)
|
||
|
case RoomCommand::SetMinimapList:
|
||
|
cmd = new SetMinimapList(parent);
|
||
|
break; // 0x1C (MM-ONLY)
|
||
|
case RoomCommand::Unused1D:
|
||
|
cmd = new Unused1D(parent);
|
||
|
break; // 0x1D
|
||
|
case RoomCommand::SetMinimapChests:
|
||
|
cmd = new SetMinimapChests(parent);
|
||
|
break; // 0x1E (MM-ONLY)
|
||
|
default:
|
||
|
cmd = new ZRoomCommandUnk(parent);
|
||
|
}
|
||
|
|
||
|
cmd->commandSet = rawDataIndex;
|
||
|
cmd->ExtractCommandFromRoom(this, currentPtr);
|
||
|
|
||
|
if (Globals::Instance->profile)
|
||
|
{
|
||
|
auto end = std::chrono::steady_clock::now();
|
||
|
int64_t diff =
|
||
|
std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||
|
if (diff > 50)
|
||
|
printf("OP: %s, TIME: %" PRIi64 "ms\n", cmd->GetCommandCName().c_str(), diff);
|
||
|
}
|
||
|
|
||
|
cmd->cmdIndex = currentIndex;
|
||
|
|
||
|
commands.push_back(cmd);
|
||
|
|
||
|
if (opcode == RoomCommand::EndMarker)
|
||
|
shouldContinue = false;
|
||
|
|
||
|
currentPtr += 8;
|
||
|
currentIndex++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ZRoom::DeclareReferences(const std::string& prefix)
|
||
|
{
|
||
|
for (auto& cmd : commands)
|
||
|
cmd->DeclareReferences(prefix);
|
||
|
}
|
||
|
|
||
|
void ZRoom::ParseRawDataLate()
|
||
|
{
|
||
|
for (auto& cmd : commands)
|
||
|
cmd->ParseRawDataLate();
|
||
|
}
|
||
|
|
||
|
void ZRoom::DeclareReferencesLate(const std::string& prefix)
|
||
|
{
|
||
|
for (auto& cmd : commands)
|
||
|
cmd->DeclareReferencesLate(prefix);
|
||
|
}
|
||
|
|
||
|
Declaration* ZRoom::DeclareVar(const std::string& prefix, const std::string& body)
|
||
|
{
|
||
|
std::string auxName = name;
|
||
|
if (auxName == "")
|
||
|
auxName = GetDefaultName(prefix);
|
||
|
if (zroomType == ZResourceType::Scene || zroomType == ZResourceType::Room)
|
||
|
auxName = StringHelper::Sprintf("%sCommands", name.c_str());
|
||
|
|
||
|
Declaration* decl =
|
||
|
parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(),
|
||
|
GetSourceTypeName(), auxName, commands.size(), body);
|
||
|
decl->staticConf = staticConf;
|
||
|
return decl;
|
||
|
}
|
||
|
|
||
|
std::string ZRoom::GetBodySourceCode() const
|
||
|
{
|
||
|
std::string declaration;
|
||
|
|
||
|
for (size_t i = 0; i < commands.size(); i++)
|
||
|
{
|
||
|
ZRoomCommand* cmd = commands[i];
|
||
|
declaration += StringHelper::Sprintf("\t%s,", cmd->GetBodySourceCode().c_str());
|
||
|
|
||
|
if (i + 1 < commands.size())
|
||
|
declaration += "\n";
|
||
|
}
|
||
|
|
||
|
return declaration;
|
||
|
}
|
||
|
|
||
|
std::string ZRoom::GetDefaultName(const std::string& prefix) const
|
||
|
{
|
||
|
return StringHelper::Sprintf("%sSet_%06X", prefix.c_str(), rawDataIndex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* There is one room in Ocarina of Time that lacks a header. Room 120, "Syotes", dates
|
||
|
* back to very early in the game's development. Since this room is a special case,
|
||
|
* declare automatically the data its contains whitout the need of a header.
|
||
|
*/
|
||
|
void ZRoom::SyotesRoomHack()
|
||
|
{
|
||
|
PolygonType2 poly(parent, 0, this);
|
||
|
|
||
|
poly.ParseRawData();
|
||
|
poly.DeclareReferences(GetName());
|
||
|
parent->AddDeclaration(0, poly.GetDeclarationAlignment(), poly.GetRawDataSize(),
|
||
|
poly.GetSourceTypeName(), poly.GetDefaultName(GetName()),
|
||
|
poly.GetBodySourceCode());
|
||
|
}
|
||
|
|
||
|
ZRoomCommand* ZRoom::FindCommandOfType(RoomCommand cmdType)
|
||
|
{
|
||
|
for (size_t i = 0; i < commands.size(); i++)
|
||
|
{
|
||
|
if (commands[i]->GetRoomCommand() == cmdType)
|
||
|
return commands[i];
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
size_t ZRoom::GetDeclarationSizeFromNeighbor(uint32_t declarationAddress)
|
||
|
{
|
||
|
auto currentDecl = parent->declarations.find(declarationAddress);
|
||
|
if (currentDecl == parent->declarations.end())
|
||
|
return 0;
|
||
|
|
||
|
auto nextDecl = currentDecl;
|
||
|
std::advance(nextDecl, 1);
|
||
|
if (nextDecl == parent->declarations.end())
|
||
|
return parent->GetRawData().size() - currentDecl->first;
|
||
|
|
||
|
return nextDecl->first - currentDecl->first;
|
||
|
}
|
||
|
|
||
|
size_t ZRoom::GetCommandSizeFromNeighbor(ZRoomCommand* cmd)
|
||
|
{
|
||
|
int32_t cmdIndex = -1;
|
||
|
|
||
|
for (size_t i = 0; i < commands.size(); i++)
|
||
|
{
|
||
|
if (commands[i] == cmd)
|
||
|
{
|
||
|
cmdIndex = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cmdIndex != -1)
|
||
|
{
|
||
|
if (cmdIndex + 1 < (int32_t)commands.size())
|
||
|
return commands[cmdIndex + 1]->cmdAddress - commands[cmdIndex]->cmdAddress;
|
||
|
else
|
||
|
return parent->GetRawData().size() - commands[cmdIndex]->cmdAddress;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void ZRoom::GetSourceOutputCode([[maybe_unused]] const std::string& prefix)
|
||
|
{
|
||
|
if (hackMode != "syotes_room")
|
||
|
DeclareVar(prefix, GetBodySourceCode());
|
||
|
}
|
||
|
|
||
|
size_t ZRoom::GetRawDataSize() const
|
||
|
{
|
||
|
size_t size = 0;
|
||
|
|
||
|
for (ZRoomCommand* cmd : commands)
|
||
|
size += cmd->GetRawDataSize();
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
std::string ZRoom::GetSourceTypeName() const
|
||
|
{
|
||
|
return "SceneCmd";
|
||
|
}
|
||
|
|
||
|
ZResourceType ZRoom::GetResourceType() const
|
||
|
{
|
||
|
assert(zroomType == ZResourceType::Scene || zroomType == ZResourceType::Room ||
|
||
|
zroomType == ZResourceType::AltHeader);
|
||
|
return zroomType;
|
||
|
}
|