diff --git a/extract_assets.py b/extract_assets.py
index 7f02be04ca..6a7afd1e37 100755
--- a/extract_assets.py
+++ b/extract_assets.py
@@ -20,6 +20,10 @@ def ExtractFile(xmlPath, outputPath, outputSourcePath):
return
execStr = "tools/ZAPD/ZAPD.out e -eh -i %s -b baserom/ -o %s -osf %s -gsf 1 -rconf tools/ZAPDConfigs/MqDbg/Config.xml" % (xmlPath, outputPath, outputSourcePath)
+
+ if "overlays" in xmlPath:
+ execStr += " --static"
+
if globalUnaccounted:
execStr += " -wu"
diff --git a/tools/ZAPD/.gitignore b/tools/ZAPD/.gitignore
index 4a9671810a..68c502e36a 100644
--- a/tools/ZAPD/.gitignore
+++ b/tools/ZAPD/.gitignore
@@ -333,5 +333,9 @@ ASALocalRun/
*.o
*.d
lib/libgfxd/libgfxd.a
+ExporterTest/ExporterTest.a
+ZAPDUtils/ZAPDUtils.a
.vscode/
-ZAPD/BuildInfo.h
\ No newline at end of file
+build/
+ZAPDUtils/build/
+ZAPD/BuildInfo.h
diff --git a/tools/ZAPD/.gitrepo b/tools/ZAPD/.gitrepo
index 73ce262034..1034fcf320 100644
--- a/tools/ZAPD/.gitrepo
+++ b/tools/ZAPD/.gitrepo
@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/zeldaret/ZAPD.git
branch = master
- commit = 6be9af65d39a1608ef854efbaf0bf9399aac5da1
- parent = 913b88b160c735d81f2d4f1d14717de8c7ba8dee
+ commit = d0cd6b3974706fc4d89c9b6545b8c17dd424b664
+ parent = c0e02913038e871221b2fcfc8be65b8dc281aa4a
method = merge
cmdver = 0.4.3
diff --git a/tools/ZAPD/ExporterTest/CollisionExporter.cpp b/tools/ZAPD/ExporterTest/CollisionExporter.cpp
new file mode 100644
index 0000000000..e00f5c1b0b
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/CollisionExporter.cpp
@@ -0,0 +1,74 @@
+#include "CollisionExporter.h"
+
+void ExporterExample_Collision::Save(ZResource* res, [[maybe_unused]] fs::path outPath,
+ BinaryWriter* writer)
+{
+ ZCollisionHeader* col = (ZCollisionHeader*)res;
+
+ writer->Write(col->absMinX);
+ writer->Write(col->absMinY);
+ writer->Write(col->absMinZ);
+
+ writer->Write(col->absMaxX);
+ writer->Write(col->absMaxY);
+ writer->Write(col->absMaxZ);
+
+ writer->Write(col->numVerts);
+ writer->Write(col->vtxAddress);
+
+ writer->Write(col->numPolygons);
+ writer->Write(col->polyAddress);
+ writer->Write(col->polyTypeDefAddress);
+ writer->Write(col->camDataAddress);
+
+ writer->Write(col->numWaterBoxes);
+ writer->Write(col->waterBoxAddress);
+
+ writer->Write(col->vtxSegmentOffset);
+ writer->Write(col->polySegmentOffset);
+ writer->Write(col->polyTypeDefSegmentOffset);
+ writer->Write(col->camDataSegmentOffset);
+ writer->Write(col->waterBoxSegmentOffset);
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+
+ writer->Seek(col->vtxSegmentOffset, SeekOffsetType::Start);
+
+ for (uint16_t i = 0; i < col->vertices.size(); i++)
+ {
+ for (uint32_t j = 0; j < col->vertices[i].dimensions; j++)
+ {
+ writer->Write(col->vertices[i].scalars[j].scalarData.s16);
+ }
+ }
+
+ writer->Seek(col->polySegmentOffset, SeekOffsetType::Start);
+
+ for (uint16_t i = 0; i < col->polygons.size(); i++)
+ {
+ writer->Write(col->polygons[i].type);
+ writer->Write(col->polygons[i].vtxA);
+ writer->Write(col->polygons[i].vtxB);
+ writer->Write(col->polygons[i].vtxC);
+ writer->Write(col->polygons[i].a);
+ writer->Write(col->polygons[i].b);
+ writer->Write(col->polygons[i].c);
+ writer->Write(col->polygons[i].d);
+ }
+
+ writer->Seek(col->polyTypeDefSegmentOffset, SeekOffsetType::Start);
+
+ for (uint16_t i = 0; i < col->polygonTypes.size(); i++)
+ writer->Write(col->polygonTypes[i]);
+
+ writer->Seek(col->camDataSegmentOffset, SeekOffsetType::Start);
+
+ for (auto entry : col->camData->entries)
+ {
+ writer->Write(entry->cameraSType);
+ writer->Write(entry->numData);
+ writer->Write(entry->cameraPosDataSeg);
+ }
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+}
diff --git a/tools/ZAPD/ExporterTest/CollisionExporter.h b/tools/ZAPD/ExporterTest/CollisionExporter.h
new file mode 100644
index 0000000000..5f48e6557e
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/CollisionExporter.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "ZCollision.h"
+#include "ZResource.h"
+
+class ExporterExample_Collision : public ZResourceExporter
+{
+public:
+ void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) override;
+};
\ No newline at end of file
diff --git a/tools/ZAPD/ExporterTest/ExporterTest.vcxproj b/tools/ZAPD/ExporterTest/ExporterTest.vcxproj
new file mode 100644
index 0000000000..a709a35091
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/ExporterTest.vcxproj
@@ -0,0 +1,159 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {65608eb0-1a47-45ad-ab66-192fb64c762c}
+ ExporterTest
+ 10.0
+ ExporterExample
+
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+ StaticLibrary
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+ true
+ $(SolutionDir)\ZAPD\;$(SolutionDir)ZAPDUtils;$(SolutionDir)lib\tinyxml2;$(SolutionDir)lib\libgfxd;$(SolutionDir)lib\elfio;$(SolutionDir)lib\stb;$(ProjectDir);$(IncludePath)
+
+
+ false
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+ stdc11
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/ZAPD/ExporterTest/ExporterTest.vcxproj.filters b/tools/ZAPD/ExporterTest/ExporterTest.vcxproj.filters
new file mode 100644
index 0000000000..166f563a17
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/ExporterTest.vcxproj.filters
@@ -0,0 +1,42 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/tools/ZAPD/ExporterTest/Main.cpp b/tools/ZAPD/ExporterTest/Main.cpp
new file mode 100644
index 0000000000..732dcedc2f
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/Main.cpp
@@ -0,0 +1,79 @@
+#include
+#include
+#include
+#include
+
+enum class ExporterFileMode
+{
+ ModeExample1 = (int)ZFileMode::Custom + 1,
+ ModeExample2 = (int)ZFileMode::Custom + 2,
+ ModeExample3 = (int)ZFileMode::Custom + 3,
+};
+
+static void ExporterParseFileMode(const std::string& buildMode, ZFileMode& fileMode)
+{
+ if (buildMode == "me1")
+ fileMode = (ZFileMode)ExporterFileMode::ModeExample1;
+ else if (buildMode == "me2")
+ fileMode = (ZFileMode)ExporterFileMode::ModeExample2;
+ else if (buildMode == "me3")
+ fileMode = (ZFileMode)ExporterFileMode::ModeExample3;
+}
+
+static void ExporterParseArgs([[maybe_unused]] int argc, char* argv[], int& i)
+{
+ std::string arg = argv[i];
+
+ if (arg == "--do-x")
+ {
+ }
+ else if (arg == "--do-y")
+ {
+ }
+}
+
+static bool ExporterProcessFileMode(ZFileMode fileMode)
+{
+ // Do whatever work is associated with these custom file modes...
+ // Return true to indicate one of our own file modes is being processed
+ if (fileMode == (ZFileMode)ExporterFileMode::ModeExample1)
+ return true;
+ else if (fileMode == (ZFileMode)ExporterFileMode::ModeExample2)
+ return true;
+ else if (fileMode == (ZFileMode)ExporterFileMode::ModeExample3)
+ return true;
+
+ return false;
+}
+
+static void ExporterFileBegin(ZFile* file)
+{
+ printf("ExporterFileBegin() called on ZFile %s.\n", file->GetName().c_str());
+}
+
+static void ExporterFileEnd(ZFile* file)
+{
+ printf("ExporterFileEnd() called on ZFile %s.\n", file->GetName().c_str());
+}
+
+static void ImportExporters()
+{
+ // In this example we set up a new exporter called "EXAMPLE".
+ // By running ZAPD with the argument -se EXAMPLE, we tell it that we want to use this exporter
+ // for our resources.
+ ExporterSet* exporterSet = new ExporterSet();
+ exporterSet->processFileModeFunc = ExporterProcessFileMode;
+ exporterSet->parseFileModeFunc = ExporterParseFileMode;
+ exporterSet->parseArgsFunc = ExporterParseArgs;
+ exporterSet->beginFileFunc = ExporterFileBegin;
+ exporterSet->endFileFunc = ExporterFileEnd;
+ exporterSet->exporters[ZResourceType::Texture] = new ExporterExample_Texture();
+ exporterSet->exporters[ZResourceType::Room] = new ExporterExample_Room();
+ exporterSet->exporters[ZResourceType::CollisionHeader] = new ExporterExample_Collision();
+
+ Globals::AddExporter("EXAMPLE", exporterSet);
+}
+
+// When ZAPD starts up, it will automatically call the below function, which in turn sets up our
+// exporters.
+REGISTER_EXPORTER(ImportExporters)
\ No newline at end of file
diff --git a/tools/ZAPD/ExporterTest/Makefile b/tools/ZAPD/ExporterTest/Makefile
new file mode 100644
index 0000000000..a2bfa9a814
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/Makefile
@@ -0,0 +1,28 @@
+# Only used for standalone compilation, usually inherits these from the main makefile
+CXXFLAGS ?= -Wall -Wextra -O2 -g -std=c++17
+
+SRC_DIRS := $(shell find -type d -not -path "*build*")
+CPP_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cpp))
+H_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.h))
+
+O_FILES := $(foreach f,$(CPP_FILES:.cpp=.o),build/$f)
+LIB := ExporterTest.a
+
+# create build directories
+$(shell mkdir -p $(foreach dir,$(SRC_DIRS),build/$(dir)))
+
+all: $(LIB)
+
+clean:
+ rm -rf build $(LIB)
+
+format:
+ clang-format-11 -i $(CPP_FILES) $(H_FILES)
+
+.PHONY: all clean format
+
+build/%.o: %.cpp
+ $(CXX) $(CXXFLAGS) $(OPTFLAGS) -I ./ -I ../ZAPD -I ../ZAPDUtils -I ../lib/tinyxml2 -c $(OUTPUT_OPTION) $<
+
+$(LIB): $(O_FILES)
+ $(AR) rcs $@ $^
diff --git a/tools/ZAPD/ExporterTest/RoomExporter.cpp b/tools/ZAPD/ExporterTest/RoomExporter.cpp
new file mode 100644
index 0000000000..c4a6844fb5
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/RoomExporter.cpp
@@ -0,0 +1,372 @@
+#include "RoomExporter.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void ExporterExample_Room::Save(ZResource* res, fs::path outPath, BinaryWriter* writer)
+{
+ ZRoom* room = dynamic_cast(res);
+
+ // MemoryStream* memStream = new MemoryStream();
+ // BinaryWriter* writer = new BinaryWriter(memStream);
+
+ for (size_t i = 0; i < room->commands.size() * 8; i++)
+ writer->Write((uint8_t)0);
+
+ for (size_t i = 0; i < room->commands.size(); i++)
+ {
+ ZRoomCommand* cmd = room->commands[i];
+
+ writer->Seek(i * 8, SeekOffsetType::Start);
+
+ writer->Write((uint8_t)cmd->cmdID);
+
+ switch (cmd->cmdID)
+ {
+ case RoomCommand::SetWind:
+ {
+ SetWind* cmdSetWind = (SetWind*)cmd;
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+
+ writer->Write(cmdSetWind->windWest); // 0x04
+ writer->Write(cmdSetWind->windVertical); // 0x05
+ writer->Write(cmdSetWind->windSouth); // 0x06
+ writer->Write(cmdSetWind->clothFlappingStrength); // 0x07
+ }
+ break;
+ case RoomCommand::SetTimeSettings:
+ {
+ SetTimeSettings* cmdTime = (SetTimeSettings*)cmd;
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+
+ writer->Write(cmdTime->hour); // 0x04
+ writer->Write(cmdTime->min); // 0x05
+ writer->Write(cmdTime->unk); // 0x06
+ writer->Write((uint8_t)0); // 0x07
+ }
+ break;
+ case RoomCommand::SetSkyboxModifier:
+ {
+ SetSkyboxModifier* cmdSkybox = (SetSkyboxModifier*)cmd;
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+
+ writer->Write(cmdSkybox->disableSky); // 0x04
+ writer->Write(cmdSkybox->disableSunMoon); // 0x05
+ writer->Write((uint8_t)0); // 0x06
+ writer->Write((uint8_t)0); // 0x07
+ }
+ break;
+ case RoomCommand::SetEchoSettings:
+ {
+ SetEchoSettings* cmdEcho = (SetEchoSettings*)cmd;
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write((uint8_t)0); // 0x04
+ writer->Write((uint8_t)0); // 0x05
+ writer->Write((uint8_t)0); // 0x06
+ writer->Write((uint8_t)cmdEcho->echo); // 0x07
+ }
+ break;
+ case RoomCommand::SetSoundSettings:
+ {
+ SetSoundSettings* cmdSound = (SetSoundSettings*)cmd;
+
+ writer->Write((uint8_t)cmdSound->reverb); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write((uint8_t)0); // 0x04
+ writer->Write((uint8_t)0); // 0x05
+
+ writer->Write(cmdSound->nightTimeSFX); // 0x06
+ writer->Write(cmdSound->musicSequence); // 0x07
+ }
+ break;
+ case RoomCommand::SetSkyboxSettings:
+ {
+ SetSkyboxSettings* cmdSkybox = (SetSkyboxSettings*)cmd;
+
+ writer->Write((uint8_t)cmdSkybox->unk1); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write((uint8_t)cmdSkybox->skyboxNumber); // 0x04
+ writer->Write((uint8_t)cmdSkybox->cloudsType); // 0x05
+ writer->Write((uint8_t)cmdSkybox->isIndoors); // 0x06
+ }
+ break;
+ case RoomCommand::SetRoomBehavior:
+ {
+ SetRoomBehavior* cmdRoom = (SetRoomBehavior*)cmd;
+
+ writer->Write((uint8_t)cmdRoom->gameplayFlags); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write(cmdRoom->gameplayFlags2); // 0x04
+ }
+ break;
+ case RoomCommand::SetCsCamera:
+ {
+ SetCsCamera* cmdCsCam = (SetCsCamera*)cmd;
+
+ writer->Write((uint8_t)cmdCsCam->cameras.size()); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+
+ writer->Write(cmdCsCam->segmentOffset); // 0x04
+ }
+ break;
+ case RoomCommand::SetMesh:
+ {
+ SetMesh* cmdMesh = (SetMesh*)cmd;
+
+ int baseStreamEnd = writer->GetStream().get()->GetLength();
+
+ writer->Write((uint8_t)cmdMesh->data); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+
+ writer->Write(baseStreamEnd); // 0x04
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+ writer->Seek(baseStreamEnd, SeekOffsetType::Start);
+
+ // TODO: NOT DONE
+
+ writer->Write(cmdMesh->meshHeaderType);
+
+ if (cmdMesh->meshHeaderType == 0)
+ {
+ // writer->Write(cmdMesh->)
+ }
+ else if (cmdMesh->meshHeaderType == 1)
+ {
+ }
+ else if (cmdMesh->meshHeaderType == 2)
+ {
+ }
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+ }
+ break;
+ case RoomCommand::SetCameraSettings:
+ {
+ SetCameraSettings* cmdCam = (SetCameraSettings*)cmd;
+
+ writer->Write((uint8_t)cmdCam->cameraMovement); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write(cmdCam->mapHighlight); // 0x04
+ }
+ break;
+ case RoomCommand::SetLightingSettings:
+ {
+ SetLightingSettings* cmdLight = (SetLightingSettings*)cmd;
+
+ writer->Write((uint8_t)cmdLight->settings.size()); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write(cmdLight->segmentOffset); // 0x04
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+ writer->Seek(cmdLight->segmentOffset, SeekOffsetType::Start);
+
+ for (LightingSettings setting : cmdLight->settings)
+ {
+ writer->Write(setting.ambientClrR);
+ writer->Write(setting.ambientClrG);
+ writer->Write(setting.ambientClrB);
+
+ writer->Write(setting.diffuseClrA_R);
+ writer->Write(setting.diffuseClrA_G);
+ writer->Write(setting.diffuseClrA_B);
+
+ writer->Write(setting.diffuseDirA_X);
+ writer->Write(setting.diffuseDirA_Y);
+ writer->Write(setting.diffuseDirA_Z);
+
+ writer->Write(setting.diffuseClrB_R);
+ writer->Write(setting.diffuseClrB_G);
+ writer->Write(setting.diffuseClrB_B);
+
+ writer->Write(setting.diffuseDirB_X);
+ writer->Write(setting.diffuseDirB_Y);
+ writer->Write(setting.diffuseDirB_Z);
+
+ writer->Write(setting.fogClrR);
+ writer->Write(setting.fogClrG);
+ writer->Write(setting.fogClrB);
+
+ writer->Write(setting.unk);
+ writer->Write(setting.drawDistance);
+ }
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+ }
+ break;
+ case RoomCommand::SetRoomList:
+ {
+ SetRoomList* cmdRoom = (SetRoomList*)cmd;
+
+ writer->Write((uint8_t)cmdRoom->romfile->rooms.size()); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+
+ auto baseStreamEnd = writer->GetLength();
+ writer->Write(baseStreamEnd); // 0x04
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+ writer->Seek(baseStreamEnd, SeekOffsetType::Start);
+
+ for (const auto& entry : cmdRoom->romfile->rooms)
+ {
+ writer->Write(entry.virtualAddressStart);
+ writer->Write(entry.virtualAddressEnd);
+ }
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+ }
+ break;
+ case RoomCommand::SetCollisionHeader:
+ {
+ SetCollisionHeader* cmdCollHeader = (SetCollisionHeader*)cmd;
+
+ int streamEnd = writer->GetStream().get()->GetLength();
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write(streamEnd); // 0x04
+
+ // TODO: NOT DONE
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+ writer->Seek(streamEnd, SeekOffsetType::Start);
+
+ ExporterExample_Collision colExp = ExporterExample_Collision();
+
+ colExp.Save(cmdCollHeader->collisionHeader, outPath, writer);
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+ }
+ break;
+ case RoomCommand::SetEntranceList:
+ {
+ SetEntranceList* cmdEntrance = (SetEntranceList*)cmd;
+
+ uint32_t baseStreamEnd = writer->GetStream().get()->GetLength();
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write(baseStreamEnd); // 0x04
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+ writer->Seek(baseStreamEnd, SeekOffsetType::Start);
+
+ for (EntranceEntry entry : cmdEntrance->entrances)
+ {
+ writer->Write((uint8_t)entry.startPositionIndex);
+ writer->Write((uint8_t)entry.roomToLoad);
+ }
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+ }
+ break;
+ case RoomCommand::SetSpecialObjects:
+ {
+ SetSpecialObjects* cmdSpecObj = (SetSpecialObjects*)cmd;
+
+ writer->Write((uint8_t)cmdSpecObj->elfMessage); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write((uint8_t)0); // 0x04
+ writer->Write((uint8_t)0); // 0x05
+ writer->Write((uint16_t)cmdSpecObj->globalObject); // 0x06
+ }
+ break;
+ case RoomCommand::SetStartPositionList:
+ {
+ SetStartPositionList* cmdStartPos = (SetStartPositionList*)cmd;
+
+ uint32_t baseStreamEnd = writer->GetStream().get()->GetLength();
+
+ writer->Write((uint8_t)cmdStartPos->actors.size()); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write(baseStreamEnd); // 0x04
+
+ uint32_t oldOffset = writer->GetBaseAddress();
+ writer->Seek(baseStreamEnd, SeekOffsetType::Start);
+
+ for (ActorSpawnEntry entry : cmdStartPos->actors)
+ {
+ writer->Write(entry.actorNum);
+ writer->Write(entry.posX);
+ writer->Write(entry.posY);
+ writer->Write(entry.posZ);
+ writer->Write(entry.rotX);
+ writer->Write(entry.rotY);
+ writer->Write(entry.rotZ);
+ writer->Write(entry.initVar);
+ }
+
+ writer->Seek(oldOffset, SeekOffsetType::Start);
+ }
+ break;
+ case RoomCommand::EndMarker:
+ {
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write((uint8_t)0); // 0x04
+ writer->Write((uint8_t)0); // 0x05
+ writer->Write((uint8_t)0); // 0x06
+ writer->Write((uint8_t)0); // 0x07
+ }
+ break;
+ default:
+ printf("UNIMPLEMENTED COMMAND: %i\n", (int)cmd->cmdID);
+
+ writer->Write((uint8_t)0); // 0x01
+ writer->Write((uint8_t)0); // 0x02
+ writer->Write((uint8_t)0); // 0x03
+ writer->Write((uint8_t)0); // 0x04
+ writer->Write((uint8_t)0); // 0x05
+ writer->Write((uint8_t)0); // 0x06
+ writer->Write((uint8_t)0); // 0x07
+
+ break;
+ }
+ }
+
+ // writer->Close();
+ // File::WriteAllBytes(StringHelper::Sprintf("%s", res->GetName().c_str()),
+ // memStream->ToVector());
+}
diff --git a/tools/ZAPD/ExporterTest/RoomExporter.h b/tools/ZAPD/ExporterTest/RoomExporter.h
new file mode 100644
index 0000000000..ee531dc87b
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/RoomExporter.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "ZResource.h"
+#include "ZRoom/ZRoom.h"
+
+class ExporterExample_Room : public ZResourceExporter
+{
+public:
+ void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) override;
+};
\ No newline at end of file
diff --git a/tools/ZAPD/ExporterTest/TextureExporter.cpp b/tools/ZAPD/ExporterTest/TextureExporter.cpp
new file mode 100644
index 0000000000..6488bed3a2
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/TextureExporter.cpp
@@ -0,0 +1,14 @@
+#include "TextureExporter.h"
+#include "../ZAPD/ZFile.h"
+
+void ExporterExample_Texture::Save(ZResource* res, [[maybe_unused]] fs::path outPath,
+ BinaryWriter* writer)
+{
+ ZTexture* tex = (ZTexture*)res;
+
+ auto data = tex->parent->GetRawData();
+
+ for (offset_t i = tex->GetRawDataIndex(); i < tex->GetRawDataIndex() + tex->GetRawDataSize();
+ i++)
+ writer->Write(data[i]);
+}
diff --git a/tools/ZAPD/ExporterTest/TextureExporter.h b/tools/ZAPD/ExporterTest/TextureExporter.h
new file mode 100644
index 0000000000..ffe6001dca
--- /dev/null
+++ b/tools/ZAPD/ExporterTest/TextureExporter.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include
+#include "ZResource.h"
+#include "ZTexture.h"
+
+class ExporterExample_Texture : public ZResourceExporter
+{
+public:
+ void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) override;
+};
\ No newline at end of file
diff --git a/tools/ZAPD/Jenkinsfile b/tools/ZAPD/Jenkinsfile
index 3062c4851b..051e5f9982 100644
--- a/tools/ZAPD/Jenkinsfile
+++ b/tools/ZAPD/Jenkinsfile
@@ -1,70 +1,90 @@
pipeline {
agent {
- label "ZAPD"
+ label 'ZAPD'
}
stages {
+ // Non-parallel ZAPD stage
stage('Build ZAPD') {
steps {
sh 'make -j'
}
}
- stage('Checkout oot') {
- steps {
- dir('oot') {
- git url: 'https://github.com/zeldaret/oot.git'
- }
- }
- }
- stage('Set up oot') {
- steps {
- dir('oot') {
- sh 'cp /usr/local/etc/roms/baserom_oot.z64 baserom_original.z64'
- // Identical to `make setup` except for copying our newer ZAPD.out into oot
- sh 'git submodule update --init --recursive'
- sh 'make -C tools'
- sh 'cp ../ZAPD.out tools/ZAPD/'
- sh 'python3 fixbaserom.py'
- sh 'python3 extract_baserom.py'
- sh 'python3 extract_assets.py'
+ // CHECKOUT THE REPOS
+ stage('Checkout Repos') {
+ parallel {
+ stage('Checkout oot') {
+ steps {
+ dir('oot') {
+ git url: 'https://github.com/zeldaret/oot.git'
+ }
+ }
}
- }
- }
- stage('Build oot') {
- steps {
- dir('oot') {
- sh 'make -j'
+
+ stage('Checkout mm') {
+ steps{
+ dir('mm') {
+ git url: 'https://github.com/zeldaret/mm.git'
+ }
+ }
}
}
}
- stage('Checkout mm') {
- steps {
- dir('mm') {
- git url: 'https://github.com/zeldaret/mm.git'
- }
- }
- }
- stage('Set up mm') {
- steps {
- dir('mm') {
- sh 'cp /usr/local/etc/roms/mm.us.rev1.z64 baserom.mm.us.rev1.z64'
+ // SETUP THE REPOS
+ stage('Set up repos') {
+ parallel {
+ stage('Setup OOT') {
+ steps {
+ dir('oot') {
+ sh 'cp /usr/local/etc/roms/baserom_oot.z64 baserom_original.z64'
- // Identical to `make setup` except for copying our newer ZAPD.out into mm
- sh 'git submodule update --init --recursive'
- sh 'make -C tools'
- sh 'cp ../ZAPD.out tools/ZAPD/'
- sh 'python3 tools/extract_rom.py baserom.mm.us.rev1.z64'
- sh 'python3 extract_assets.py'
+ // Identical to `make setup` except for copying our newer ZAPD.out into oot
+ sh 'git submodule update --init --recursive'
+ sh 'make -C tools'
+ sh 'cp ../ZAPD.out tools/ZAPD/'
+ sh 'python3 fixbaserom.py'
+ sh 'python3 extract_baserom.py'
+ sh 'python3 extract_assets.py'
+ }
+ }
+ }
+
+ stage('Setup MM') {
+ steps {
+ dir('mm') {
+ sh 'cp /usr/local/etc/roms/mm.us.rev1.z64 baserom.mm.us.rev1.z64'
+
+ // Identical to `make setup` except for copying our newer ZAPD.out into mm
+ sh 'make -C tools'
+ sh 'cp ../ZAPD.out tools/ZAPD/'
+ sh 'python3 tools/fixbaserom.py'
+ sh 'python3 tools/extract_baserom.py'
+ sh 'python3 extract_assets.py -t 4'
+ }
+ }
}
}
}
- stage('Build mm') {
- steps {
- dir('mm') {
- sh 'make assembly -j'
- sh 'make -j'
+
+ // BUILD THE REPOS
+ stage('Build repos') {
+ parallel {
+ stage('Build oot') {
+ steps {
+ dir('oot') {
+ sh 'make -j'
+ }
+ }
+ }
+ stage('Build mm') {
+ steps {
+ dir('mm') {
+ sh 'make -j disasm'
+ sh 'make -j all'
+ }
+ }
}
}
}
diff --git a/tools/ZAPD/Makefile b/tools/ZAPD/Makefile
index afd50e0167..737b85ecf7 100644
--- a/tools/ZAPD/Makefile
+++ b/tools/ZAPD/Makefile
@@ -1,78 +1,118 @@
+# use variables in submakes
+export
OPTIMIZATION_ON ?= 1
ASAN ?= 0
DEPRECATION_ON ?= 1
DEBUG ?= 0
-CXXFLAGS ?=
-COPYCHECK_ARGS ?=
+COPYCHECK_ARGS ?=
+LLD ?= 0
-CXX := g++
-INC := -I ZAPD -I lib/assimp/include -I lib/elfio -I lib/json/include -I lib/stb -I lib/tinygltf -I lib/libgfxd -I lib/tinyxml2
-CXXFLAGS += -fpic -std=c++17 -Wall -fno-omit-frame-pointer
+# Use clang++ if available, else use g++
+ifeq ($(shell command -v clang++ >/dev/null 2>&1; echo $$?),0)
+ CXX := clang++
+else
+ CXX := g++
+endif
+
+INC := -I ZAPD -I lib/elfio -I lib/libgfxd -I lib/tinyxml2 -I ZAPDUtils
+CXXFLAGS := -fpic -std=c++17 -Wall -Wextra -fno-omit-frame-pointer
+OPTFLAGS :=
ifneq ($(DEBUG),0)
OPTIMIZATION_ON = 0
- DEPRECATION_OFF = 1
- CXXFLAGS += -g3 -DDEVELOPMENT
+ CXXFLAGS += -g3 -DDEVELOPMENT -D_DEBUG
COPYCHECK_ARGS += --devel
DEPRECATION_ON = 0
+else
+ CXXFLAGS += -Werror
endif
ifeq ($(OPTIMIZATION_ON),0)
- CXXFLAGS += -O0
+ OPTFLAGS := -O0
else
-CXXFLAGS += -O2 -march=native -mtune=native
+ OPTFLAGS := -O2 -march=native -mtune=native
endif
ifneq ($(ASAN),0)
- CXXFLAGS += -fsanitize=address
+ CXXFLAGS += -fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=undefined
endif
ifneq ($(DEPRECATION_ON),0)
CXXFLAGS += -DDEPRECATION_ON
endif
# CXXFLAGS += -DTEXTURE_DEBUG
-LDFLAGS := -lstdc++ -lm -ldl -lpng
+LDFLAGS := -lm -ldl -lpng
+
+# Use LLD if available. Set LLD=0 to not use it
+ifeq ($(shell command -v ld.lld >/dev/null 2>&1; echo $$?),0)
+ LLD := 1
+endif
+
+ifneq ($(LLD),0)
+ LDFLAGS += -fuse-ld=lld
+endif
UNAME := $(shell uname)
ifneq ($(UNAME), Darwin)
LDFLAGS += -Wl,-export-dynamic -lstdc++fs
endif
-SRC_DIRS := ZAPD ZAPD/ZRoom ZAPD/ZRoom/Commands ZAPD/Overlays ZAPD/HighLevel
+ZAPD_SRC_DIRS := $(shell find ZAPD -type d)
+SRC_DIRS = $(ZAPD_SRC_DIRS) lib/tinyxml2
-ZAPD_CPP_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cpp))
-ZAPD_H_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.h))
+ZAPD_CPP_FILES := $(foreach dir,$(ZAPD_SRC_DIRS),$(wildcard $(dir)/*.cpp))
+ZAPD_H_FILES := $(foreach dir,$(ZAPD_SRC_DIRS),$(wildcard $(dir)/*.h))
CPP_FILES += $(ZAPD_CPP_FILES) lib/tinyxml2/tinyxml2.cpp
-O_FILES := $(CPP_FILES:.cpp=.o)
+O_FILES := $(foreach f,$(CPP_FILES:.cpp=.o),build/$f)
+O_FILES += build/ZAPD/BuildInfo.o
+# create build directories
+$(shell mkdir -p $(foreach dir,$(SRC_DIRS),build/$(dir)))
+
+
+# Main targets
all: ZAPD.out copycheck
-genbuildinfo:
+build/ZAPD/BuildInfo.o:
python3 ZAPD/genbuildinfo.py $(COPYCHECK_ARGS)
+ $(CXX) $(CXXFLAGS) $(OPTFLAGS) $(INC) -c $(OUTPUT_OPTION) build/ZAPD/BuildInfo.cpp
copycheck: ZAPD.out
python3 copycheck.py
clean:
- rm -f $(O_FILES) ZAPD.out
+ rm -rf build ZAPD.out
$(MAKE) -C lib/libgfxd clean
+ $(MAKE) -C ZAPDUtils clean
+ $(MAKE) -C ExporterTest clean
rebuild: clean all
format:
clang-format-11 -i $(ZAPD_CPP_FILES) $(ZAPD_H_FILES)
+ $(MAKE) -C ZAPDUtils format
+ $(MAKE) -C ExporterTest format
-.PHONY: all genbuildinfo copycheck clean rebuild format
+.PHONY: all build/ZAPD/BuildInfo.o copycheck clean rebuild format
-%.o: %.cpp
- $(CXX) $(CXXFLAGS) $(INC) -c $< -o $@
+build/%.o: %.cpp
+ $(CXX) $(CXXFLAGS) $(OPTFLAGS) $(INC) -c $(OUTPUT_OPTION) $<
-ZAPD/Main.o: genbuildinfo ZAPD/Main.cpp
- $(CXX) $(CXXFLAGS) $(INC) -c ZAPD/Main.cpp -o $@
+# Submakes
lib/libgfxd/libgfxd.a:
$(MAKE) -C lib/libgfxd
-ZAPD.out: $(O_FILES) lib/libgfxd/libgfxd.a
- $(CXX) $(CXXFLAGS) $(INC) $(O_FILES) lib/libgfxd/libgfxd.a -o $@ $(FS_INC) $(LDFLAGS)
+.PHONY: ExporterTest
+ExporterTest:
+ $(MAKE) -C ExporterTest
+
+.PHONY: ZAPDUtils
+ZAPDUtils:
+ $(MAKE) -C ZAPDUtils
+
+
+# Linking
+ZAPD.out: $(O_FILES) lib/libgfxd/libgfxd.a ExporterTest ZAPDUtils
+ $(CXX) $(CXXFLAGS) $(O_FILES) lib/libgfxd/libgfxd.a ZAPDUtils/ZAPDUtils.a -Wl,--whole-archive ExporterTest/ExporterTest.a -Wl,--no-whole-archive $(LDFLAGS) $(OUTPUT_OPTION)
diff --git a/tools/ZAPD/README.md b/tools/ZAPD/README.md
index 719492d92b..e2764b6277 100644
--- a/tools/ZAPD/README.md
+++ b/tools/ZAPD/README.md
@@ -30,7 +30,8 @@ You can configure a bit your ZAPD build with the following options:
- `DEBUG`: If non-zero, ZAPD will be compiled in _development mode_. This implies the following:
- Debugging symbols enabled (`-g3`). They are disabled by default.
- `OPTIMIZATION_ON=0`: Disables optimizations (`-O0`).
- - `DEPRECATION_OFF=1`: Disables deprecation warnings.
+ - `DEPRECATION_ON=0`: Disables deprecation warnings.
+- `LLD=1`: builds with the LLVM linker `ld.lld` instead of the system default.
As an example, if you want to build ZAPD with optimizations disabled and use the address sanitizer, you could use the following command:
@@ -109,6 +110,10 @@ ZAPD also accepts the following list of extra parameters:
- Can be used only in `e` or `bsf` modes.
- `-tm MODE`: Test Mode (enables certain experimental features). To enable it, set `MODE` to `1`.
- `-wno` / `--warn-no-offsets` : Enable warnings for nodes that dont have offsets specified. Takes priority over `-eno`/ `--error-no-offsets`.
-- `-eno`/ `--error-no-offsets` : Enable errors for nodes that dont have offsets specified.
+- `-eno` / `--error-no-offsets` : Enable errors for nodes that dont have offsets specified.
+- `-se` / `--set-exporter` : Sets which exporter to use.
+- `--gcc-compat` : Enables GCC compatible mode. Slower.
+- `-s` / `--static` : Mark every asset as `static`.
+ - This behaviour can be overridden per asset using `Static=` in the respective XML node.
Additionally, you can pass the flag `--version` to see the current ZAPD version. If that flag is passed, ZAPD will ignore any other parameter passed.
diff --git a/tools/ZAPD/ZAPD.sln b/tools/ZAPD/ZAPD.sln
index 4280808b49..82538dd9f4 100644
--- a/tools/ZAPD/ZAPD.sln
+++ b/tools/ZAPD/ZAPD.sln
@@ -5,6 +5,10 @@ VisualStudioVersion = 16.0.30320.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZAPD", "ZAPD\ZAPD.vcxproj", "{B53F9E5B-0A58-4BAE-9AFE-856C8CBB8D36}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExporterExample", "ExporterTest\ExporterTest.vcxproj", "{65608EB0-1A47-45AD-AB66-192FB64C762C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZAPDUtils", "ZAPDUtils\ZAPDUtils.vcxproj", "{A2E01C3E-D647-45D1-9788-043DEBC1A908}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -33,6 +37,38 @@ Global
{B53F9E5B-0A58-4BAE-9AFE-856C8CBB8D36}.RelWithDebInfo|x64.Build.0 = Release|x64
{B53F9E5B-0A58-4BAE-9AFE-856C8CBB8D36}.RelWithDebInfo|x86.ActiveCfg = Release|Win32
{B53F9E5B-0A58-4BAE-9AFE-856C8CBB8D36}.RelWithDebInfo|x86.Build.0 = Release|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Debug|x64.ActiveCfg = Debug|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Debug|x64.Build.0 = Debug|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Debug|x86.ActiveCfg = Debug|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Debug|x86.Build.0 = Debug|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.MinSizeRel|x64.ActiveCfg = Debug|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.MinSizeRel|x64.Build.0 = Debug|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.MinSizeRel|x86.ActiveCfg = Debug|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.MinSizeRel|x86.Build.0 = Debug|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Release|x64.ActiveCfg = Release|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Release|x64.Build.0 = Release|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Release|x86.ActiveCfg = Release|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.Release|x86.Build.0 = Release|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.RelWithDebInfo|x64.ActiveCfg = Release|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.RelWithDebInfo|x64.Build.0 = Release|x64
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.RelWithDebInfo|x86.ActiveCfg = Release|Win32
+ {65608EB0-1A47-45AD-AB66-192FB64C762C}.RelWithDebInfo|x86.Build.0 = Release|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x64.ActiveCfg = Debug|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x64.Build.0 = Debug|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x86.ActiveCfg = Debug|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x86.Build.0 = Debug|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.MinSizeRel|x64.ActiveCfg = Debug|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.MinSizeRel|x64.Build.0 = Debug|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.MinSizeRel|x86.ActiveCfg = Debug|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.MinSizeRel|x86.Build.0 = Debug|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x64.ActiveCfg = Release|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x64.Build.0 = Release|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x86.ActiveCfg = Release|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x86.Build.0 = Release|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.RelWithDebInfo|x64.ActiveCfg = Release|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.RelWithDebInfo|x64.Build.0 = Release|x64
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.RelWithDebInfo|x86.ActiveCfg = Release|Win32
+ {A2E01C3E-D647-45D1-9788-043DEBC1A908}.RelWithDebInfo|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/tools/ZAPD/ZAPD/Declaration.cpp b/tools/ZAPD/ZAPD/Declaration.cpp
index 63a3918050..d2a86ffcc5 100644
--- a/tools/ZAPD/ZAPD/Declaration.cpp
+++ b/tools/ZAPD/ZAPD/Declaration.cpp
@@ -1,37 +1,31 @@
#include "Declaration.h"
-Declaration::Declaration(DeclarationAlignment nAlignment, DeclarationPadding nPadding, size_t nSize,
- std::string nText)
+#include "Globals.h"
+#include "Utils/StringHelper.h"
+
+Declaration::Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nText)
{
+ address = nAddress;
alignment = nAlignment;
- padding = nPadding;
size = nSize;
text = nText;
}
-Declaration::Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, std::string nText)
- : Declaration(nAlignment, DeclarationPadding::None, nSize, nText)
+Declaration::Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ const std::string& nText)
+ : Declaration(nAddress, nAlignment, nSize, nText)
{
varType = nVarType;
varName = nVarName;
isArray = nIsArray;
}
-Declaration::Declaration(DeclarationAlignment nAlignment, DeclarationPadding nPadding, size_t nSize,
- std::string nVarType, std::string nVarName, bool nIsArray,
- std::string nText)
- : Declaration(nAlignment, nPadding, nSize, nText)
-{
- varType = nVarType;
- varName = nVarName;
- isArray = nIsArray;
-}
-
-Declaration::Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, size_t nArrayItemCnt,
- std::string nText)
- : Declaration(nAlignment, DeclarationPadding::None, nSize, nText)
+Declaration::Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ size_t nArrayItemCnt, const std::string& nText)
+ : Declaration(nAddress, nAlignment, nSize, nText)
{
varType = nVarType;
varName = nVarName;
@@ -39,10 +33,10 @@ Declaration::Declaration(DeclarationAlignment nAlignment, size_t nSize, std::str
arrayItemCnt = nArrayItemCnt;
}
-Declaration::Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, std::string nArrayItemCntStr,
- std::string nText)
- : Declaration(nAlignment, DeclarationPadding::None, nSize, nText)
+Declaration::Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ const std::string& nArrayItemCntStr, const std::string& nText)
+ : Declaration(nAddress, nAlignment, nSize, nText)
{
varType = nVarType;
varName = nVarName;
@@ -50,30 +44,179 @@ Declaration::Declaration(DeclarationAlignment nAlignment, size_t nSize, std::str
arrayItemCntStr = nArrayItemCntStr;
}
-Declaration::Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, size_t nArrayItemCnt,
- std::string nText, bool nIsExternal)
- : Declaration(nAlignment, nSize, nVarType, nVarName, nIsArray, nArrayItemCnt, nText)
+Declaration::Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ size_t nArrayItemCnt, const std::string& nText, bool nIsExternal)
+ : Declaration(nAddress, nAlignment, nSize, nVarType, nVarName, nIsArray, nArrayItemCnt, nText)
{
isExternal = nIsExternal;
}
-Declaration::Declaration(DeclarationAlignment nAlignment, DeclarationPadding nPadding, size_t nSize,
- std::string nVarType, std::string nVarName, bool nIsArray,
- size_t nArrayItemCnt, std::string nText)
- : Declaration(nAlignment, nPadding, nSize, nText)
-{
- varType = nVarType;
- varName = nVarName;
- isArray = nIsArray;
- arrayItemCnt = nArrayItemCnt;
-}
-
-Declaration::Declaration(std::string nIncludePath, size_t nSize, std::string nVarType,
- std::string nVarName)
- : Declaration(DeclarationAlignment::None, DeclarationPadding::None, nSize, "")
+Declaration::Declaration(offset_t nAddress, const std::string& nIncludePath, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName)
+ : Declaration(nAddress, DeclarationAlignment::Align4, nSize, "")
{
includePath = nIncludePath;
varType = nVarType;
varName = nVarName;
}
+
+bool Declaration::IsStatic() const
+{
+ switch (staticConf)
+ {
+ case StaticConfig::Off:
+ return false;
+
+ case StaticConfig::Global:
+ return Globals::Instance->forceStatic;
+
+ case StaticConfig::On:
+ return true;
+ }
+
+ return false;
+}
+
+std::string Declaration::GetNormalDeclarationStr() const
+{
+ std::string output;
+
+ if (preText != "")
+ output += preText + "\n";
+
+ if (IsStatic())
+ {
+ output += "static ";
+ }
+
+ if (isArray)
+ {
+ if (arrayItemCntStr != "")
+ {
+ output += StringHelper::Sprintf("%s %s[%s];\n", varType.c_str(), varName.c_str(),
+ arrayItemCntStr.c_str());
+ }
+ else if (arrayItemCnt == 0)
+ {
+ output += StringHelper::Sprintf("%s %s[] = {\n", varType.c_str(), varName.c_str());
+ }
+ else
+ {
+ output += StringHelper::Sprintf("%s %s[%i] = {\n", varType.c_str(), varName.c_str(),
+ arrayItemCnt);
+ }
+
+ output += text + "\n";
+ }
+ else
+ {
+ output += StringHelper::Sprintf("%s %s = { ", varType.c_str(), varName.c_str());
+ output += text;
+ }
+
+ if (output.back() == '\n')
+ output += "};";
+ else
+ output += " };";
+
+ if (rightText != "")
+ output += " " + rightText + "";
+
+ output += "\n";
+
+ if (postText != "")
+ output += postText + "\n";
+
+ output += "\n";
+
+ return output;
+}
+
+std::string Declaration::GetExternalDeclarationStr() const
+{
+ std::string output;
+
+ if (preText != "")
+ output += preText + "\n";
+
+ if (IsStatic())
+ {
+ output += "static ";
+ }
+
+ if (arrayItemCntStr != "")
+ output +=
+ StringHelper::Sprintf("%s %s[%s] = {\n#include \"%s\"\n};", varType.c_str(),
+ varName.c_str(), arrayItemCntStr.c_str(), includePath.c_str());
+ else if (arrayItemCnt != 0)
+ output += StringHelper::Sprintf("%s %s[%i] = {\n#include \"%s\"\n};", varType.c_str(),
+ varName.c_str(), arrayItemCnt, includePath.c_str());
+ else
+ output += StringHelper::Sprintf("%s %s[] = {\n#include \"%s\"\n};", varType.c_str(),
+ varName.c_str(), includePath.c_str());
+
+ if (rightText != "")
+ output += " " + rightText + "";
+
+ output += "\n";
+
+ if (postText != "")
+ output += postText + "\n";
+
+ output += "\n";
+
+ return output;
+}
+
+std::string Declaration::GetExternStr() const
+{
+ if (IsStatic() || varType == "")
+ {
+ return "";
+ }
+
+ if (isArray)
+ {
+ if (arrayItemCntStr != "")
+ {
+ return StringHelper::Sprintf("extern %s %s[%s];\n", varType.c_str(), varName.c_str(),
+ arrayItemCntStr.c_str());
+ }
+ else if (arrayItemCnt != 0)
+ return StringHelper::Sprintf("extern %s %s[%i];\n", varType.c_str(), varName.c_str(),
+ arrayItemCnt);
+ else
+ return StringHelper::Sprintf("extern %s %s[];\n", varType.c_str(), varName.c_str());
+ }
+
+ return StringHelper::Sprintf("extern %s %s;\n", varType.c_str(), varName.c_str());
+}
+
+std::string Declaration::GetStaticForwardDeclarationStr() const
+{
+ if (!IsStatic() || isUnaccounted)
+ return "";
+
+ if (isArray)
+ {
+ if (arrayItemCntStr == "" && arrayItemCnt == 0)
+ {
+ // Forward declaring static arrays without specifying the size is not allowed.
+ return "";
+ }
+
+ if (arrayItemCntStr != "")
+ {
+ return StringHelper::Sprintf("static %s %s[%s];\n", varType.c_str(), varName.c_str(),
+ arrayItemCntStr.c_str());
+ }
+ else
+ {
+ return StringHelper::Sprintf("static %s %s[%i];\n", varType.c_str(), varName.c_str(),
+ arrayItemCnt);
+ }
+ }
+
+ return StringHelper::Sprintf("static %s %s;\n", varType.c_str(), varName.c_str());
+}
diff --git a/tools/ZAPD/ZAPD/Declaration.h b/tools/ZAPD/ZAPD/Declaration.h
index fa8653b858..138524a4c2 100644
--- a/tools/ZAPD/ZAPD/Declaration.h
+++ b/tools/ZAPD/ZAPD/Declaration.h
@@ -3,28 +3,32 @@
#include
#include
+// TODO: should we drop the `_t` suffix because of UNIX compliance?
+typedef uint32_t segptr_t;
+typedef uint32_t offset_t;
+
+#define SEGMENTED_NULL ((segptr_t)0)
+
enum class DeclarationAlignment
{
- None,
Align4,
Align8,
Align16
};
-enum class DeclarationPadding
+enum class StaticConfig
{
- None,
- Pad4,
- Pad8,
- Pad16
+ Off,
+ Global,
+ On
};
class Declaration
{
public:
+ offset_t address;
DeclarationAlignment alignment;
- DeclarationPadding padding;
- size_t size = 0;
+ size_t size;
std::string preText;
std::string text;
std::string rightText;
@@ -38,28 +42,38 @@ public:
bool isArray = false;
size_t arrayItemCnt = 0;
std::string arrayItemCntStr;
- std::vector references;
+ std::vector references;
bool isUnaccounted = false;
bool isPlaceholder = false;
+ bool declaredInXml = false;
+ StaticConfig staticConf = StaticConfig::Global;
- Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, std::string nText);
- Declaration(DeclarationAlignment nAlignment, DeclarationPadding nPadding, size_t nSize,
- std::string nVarType, std::string nVarName, bool nIsArray, std::string nText);
- Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, size_t nArrayItemCnt, std::string nText);
- Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, std::string nArrayItemCntStr,
- std::string nText);
- Declaration(DeclarationAlignment nAlignment, size_t nSize, std::string nVarType,
- std::string nVarName, bool nIsArray, size_t nArrayItemCnt, std::string nText,
- bool nIsExternal);
- Declaration(DeclarationAlignment nAlignment, DeclarationPadding nPadding, size_t nSize,
- std::string nVarType, std::string nVarName, bool nIsArray, size_t nArrayItemCnt,
- std::string nText);
- Declaration(std::string nIncludePath, size_t nSize, std::string nVarType, std::string nVarName);
+ Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ const std::string& nText);
+ Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ size_t nArrayItemCnt, const std::string& nText);
+ Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ const std::string& nArrayItemCntStr, const std::string& nText);
+ Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName, bool nIsArray,
+ size_t nArrayItemCnt, const std::string& nText, bool nIsExternal);
+
+ Declaration(offset_t nAddress, const std::string& nIncludePath, size_t nSize,
+ const std::string& nVarType, const std::string& nVarName);
+
+ bool IsStatic() const;
+
+ std::string GetNormalDeclarationStr() const;
+ std::string GetExternalDeclarationStr() const;
+
+ std::string GetExternStr() const;
+
+ std::string GetStaticForwardDeclarationStr() const;
protected:
- Declaration(DeclarationAlignment nAlignment, DeclarationPadding nPadding, size_t nSize,
- std::string nText);
+ Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize,
+ const std::string& nText);
};
diff --git a/tools/ZAPD/ZAPD/GameConfig.cpp b/tools/ZAPD/ZAPD/GameConfig.cpp
new file mode 100644
index 0000000000..f197493a4a
--- /dev/null
+++ b/tools/ZAPD/ZAPD/GameConfig.cpp
@@ -0,0 +1,184 @@
+#include "GameConfig.h"
+
+#include
+#include
+
+#include "Utils/Directory.h"
+#include "Utils/File.h"
+#include "Utils/Path.h"
+#include "ZFile.h"
+#include "tinyxml2.h"
+
+using ConfigFunc = void (GameConfig::*)(const tinyxml2::XMLElement&);
+
+GameConfig::~GameConfig()
+{
+ for (auto& declPair : segmentRefFiles)
+ {
+ for (auto& file : declPair.second)
+ {
+ delete file;
+ }
+ }
+}
+
+void GameConfig::ReadTexturePool(const fs::path& texturePoolXmlPath)
+{
+ tinyxml2::XMLDocument doc;
+ tinyxml2::XMLError eResult = doc.LoadFile(texturePoolXmlPath.c_str());
+
+ if (eResult != tinyxml2::XML_SUCCESS)
+ {
+ fprintf(stderr, "Warning: Unable to read texture pool XML with error code %i\n", eResult);
+ return;
+ }
+
+ tinyxml2::XMLNode* root = doc.FirstChild();
+
+ if (root == nullptr)
+ return;
+
+ for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != nullptr;
+ child = child->NextSiblingElement())
+ {
+ if (std::string_view(child->Name()) == "Texture")
+ {
+ std::string crcStr = child->Attribute("CRC");
+ fs::path texPath = child->Attribute("Path");
+ std::string texName;
+
+ uint32_t crc = strtoul(crcStr.c_str(), nullptr, 16);
+
+ texturePool[crc].path = texPath;
+ }
+ }
+}
+
+void GameConfig::GenSymbolMap(const fs::path& symbolMapPath)
+{
+ auto symbolLines = File::ReadAllLines(symbolMapPath);
+
+ for (std::string& symbolLine : symbolLines)
+ {
+ auto split = StringHelper::Split(symbolLine, " ");
+ uint32_t addr = strtoul(split[0].c_str(), nullptr, 16);
+ std::string symbolName = split[1];
+
+ symbolMap[addr] = std::move(symbolName);
+ }
+}
+
+void GameConfig::ConfigFunc_SymbolMap(const tinyxml2::XMLElement& element)
+{
+ std::string fileName = element.Attribute("File");
+ GenSymbolMap(Path::GetDirectoryName(configFilePath) / fileName);
+}
+
+void GameConfig::ConfigFunc_ActorList(const tinyxml2::XMLElement& element)
+{
+ std::string fileName = element.Attribute("File");
+ std::vector lines =
+ File::ReadAllLines(Path::GetDirectoryName(configFilePath) / fileName);
+
+ for (auto& line : lines)
+ actorList.emplace_back(std::move(line));
+}
+
+void GameConfig::ConfigFunc_ObjectList(const tinyxml2::XMLElement& element)
+{
+ std::string fileName = element.Attribute("File");
+ std::vector lines =
+ File::ReadAllLines(Path::GetDirectoryName(configFilePath) / fileName);
+
+ for (auto& line : lines)
+ objectList.emplace_back(std::move(line));
+}
+
+void GameConfig::ConfigFunc_TexturePool(const tinyxml2::XMLElement& element)
+{
+ std::string fileName = element.Attribute("File");
+ ReadTexturePool(Path::GetDirectoryName(configFilePath) / fileName);
+}
+
+void GameConfig::ConfigFunc_BGConfig(const tinyxml2::XMLElement& element)
+{
+ bgScreenWidth = element.IntAttribute("ScreenWidth", 320);
+ bgScreenHeight = element.IntAttribute("ScreenHeight", 240);
+}
+
+void GameConfig::ConfigFunc_ExternalXMLFolder(const tinyxml2::XMLElement& element)
+{
+ const char* pathValue = element.Attribute("Path");
+ if (pathValue == nullptr)
+ {
+ throw std::runtime_error(
+ StringHelper::Sprintf("Parse: Fatal error in configuration file.\n"
+ "\t Missing 'Path' attribute in `ExternalXMLFolder` element.\n"));
+ }
+ if (externalXmlFolder != "")
+ {
+ throw std::runtime_error(StringHelper::Sprintf("Parse: Fatal error in configuration file.\n"
+ "\t `ExternalXMLFolder` is duplicated.\n"));
+ }
+ externalXmlFolder = pathValue;
+}
+
+void GameConfig::ConfigFunc_ExternalFile(const tinyxml2::XMLElement& element)
+{
+ const char* xmlPathValue = element.Attribute("XmlPath");
+ if (xmlPathValue == nullptr)
+ {
+ throw std::runtime_error(
+ StringHelper::Sprintf("Parse: Fatal error in configuration file.\n"
+ "\t Missing 'XmlPath' attribute in `ExternalFile` element.\n"));
+ }
+ const char* outPathValue = element.Attribute("OutPath");
+ if (outPathValue == nullptr)
+ {
+ throw std::runtime_error(
+ StringHelper::Sprintf("Parse: Fatal error in configuration file.\n"
+ "\t Missing 'OutPath' attribute in `ExternalFile` element.\n"));
+ }
+
+ externalFiles.push_back(ExternalFile(fs::path(xmlPathValue), fs::path(outPathValue)));
+}
+
+void GameConfig::ReadConfigFile(const fs::path& argConfigFilePath)
+{
+ static const std::map ConfigFuncDictionary = {
+ {"SymbolMap", &GameConfig::ConfigFunc_SymbolMap},
+ {"ActorList", &GameConfig::ConfigFunc_ActorList},
+ {"ObjectList", &GameConfig::ConfigFunc_ObjectList},
+ {"TexturePool", &GameConfig::ConfigFunc_TexturePool},
+ {"BGConfig", &GameConfig::ConfigFunc_BGConfig},
+ {"ExternalXMLFolder", &GameConfig::ConfigFunc_ExternalXMLFolder},
+ {"ExternalFile", &GameConfig::ConfigFunc_ExternalFile},
+ };
+
+ configFilePath = argConfigFilePath;
+ tinyxml2::XMLDocument doc;
+ tinyxml2::XMLError eResult = doc.LoadFile(configFilePath.c_str());
+
+ if (eResult != tinyxml2::XML_SUCCESS)
+ {
+ throw std::runtime_error("Error: Unable to read config file.");
+ }
+
+ tinyxml2::XMLNode* root = doc.FirstChild();
+
+ if (root == nullptr)
+ return;
+
+ for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != nullptr;
+ child = child->NextSiblingElement())
+ {
+ auto it = ConfigFuncDictionary.find(child->Name());
+ if (it == ConfigFuncDictionary.end())
+ {
+ fprintf(stderr, "Unsupported configuration variable: %s\n", child->Name());
+ continue;
+ }
+
+ std::invoke(it->second, *this, *child);
+ }
+}
diff --git a/tools/ZAPD/ZAPD/GameConfig.h b/tools/ZAPD/ZAPD/GameConfig.h
new file mode 100644
index 0000000000..c478d16ceb
--- /dev/null
+++ b/tools/ZAPD/ZAPD/GameConfig.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include
+#include