diff --git a/extract_assets.py b/extract_assets.py
index 066c1de3cd..a7825af1fe 100755
--- a/extract_assets.py
+++ b/extract_assets.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
-import argparse, json, os, signal, time
+import argparse, json, os, signal, time, colorama
from multiprocessing import Pool, Event, Manager
+colorama.init();
+
EXTRACTED_ASSETS_NAMEFILE = ".extracted-assets.json"
def SignalHandler(sig, frame):
@@ -15,10 +17,10 @@ def ExtractFile(xmlPath, outputPath, outputSourcePath):
# Don't extract if another file wasn't extracted properly.
return
- execStr = "tools/ZAPD/ZAPD.out e -eh -i %s -b baserom/ -o %s -osf %s -gsf 1 -rconf tools/ZAPDConfigs/MM/Config.xml" % (xmlPath, outputPath, outputSourcePath)
+ execStr = f"tools/ZAPD/ZAPD.out e -eh -i {xmlPath} -b baserom/ -o {outputPath} -osf {outputSourcePath} -gsf 1 -rconf tools/ZAPDConfigs/MM/Config.xml {ZAPDArgs}"
if globalUnaccounted:
- execStr += " -wu"
+ execStr += " -Wunaccounted"
print(execStr)
exitValue = os.system(execStr)
@@ -72,8 +74,27 @@ def main():
parser.add_argument("-t", "--threads", help="Number of cpu cores to extract with.")
parser.add_argument("-f", "--force", help="Force the extraction of every xml instead of checking the touched ones.", action="store_true")
parser.add_argument("-u", "--unaccounted", help="Enables ZAPD unaccounted detector warning system.", action="store_true")
+ parser.add_argument("-Z", help="Pass the argument on to ZAPD, e.g. `-ZWunaccounted` to warn about unaccounted blocks in XMLs. Each argument should be passed separately, *without* the leading dash.", metavar="ZAPD_ARG", action="append")
args = parser.parse_args()
+ global ZAPDArgs;
+ ZAPDArgs = "";
+ if args.Z is not None:
+ badZAPDArg = False;
+ for i in range(len(args.Z)):
+ z = args.Z[i]
+ if z[0] == '-':
+ print(f"{colorama.Fore.LIGHTRED_EX}error{colorama.Fore.RESET}: argument \"{z}\" starts with \"-\", which is not supported.", file=os.sys.stderr);
+ badZAPDArg = True;
+ else:
+ args.Z[i] = "-" + z;
+
+ if badZAPDArg:
+ exit(1);
+
+ ZAPDArgs = " ".join(args.Z);
+ print("Using extra ZAPD arguments: " + ZAPDArgs);
+
global mainAbort
mainAbort = Event()
manager = Manager()
@@ -88,7 +109,7 @@ def main():
if asset_path is not None:
fullPath = os.path.join("assets", "xml", asset_path + ".xml")
if not os.path.exists(fullPath):
- print(f"Error. File {fullPath} doesn't exists.", file=os.sys.stderr)
+ print(f"Error. File {fullPath} does not exist.", file=os.sys.stderr)
exit(1)
initializeWorker(mainAbort, args.unaccounted, extractedAssetsTracker, manager)
@@ -107,7 +128,7 @@ def main():
numCores = int(args.threads or 0)
if numCores <= 0:
numCores = 1
- print("Extracting assets with " + str(numCores) + " CPU cores.")
+ print("Extracting assets with " + str(numCores) + " CPU core" + ("s" if numCores > 1 else "") + ".")
with Pool(numCores, initializer=initializeWorker, initargs=(mainAbort, args.unaccounted, extractedAssetsTracker, manager)) as p:
p.map(ExtractFunc, xmlFiles)
diff --git a/src/code/sys_initial_check.c b/src/code/sys_initial_check.c
index 84871255b8..ec61d07b56 100644
--- a/src/code/sys_initial_check.c
+++ b/src/code/sys_initial_check.c
@@ -10,8 +10,16 @@
#include "misc/locerrmsg/locerrmsg.h"
#include "misc/memerrmsg/memerrmsg.h"
-#define SIZEOF_LOCERRMSG (sizeof(gNotDesignedForSystemErrorTex))
-#define SIZEOF_MEMERRMSG (sizeof(gExpansionPakNotInstalledErrorTex) + sizeof(gSeeInstructionBookletErrorTex))
+#define LOCERRMSG_WIDTH 208
+#define LOCERRMSG_HEIGHT 16
+#define NUMBEROF_LOCERRMSGS 1
+
+#define MEMERRMSG_WIDTH 128
+#define MEMERRMSG_HEIGHT 37
+#define NUMBEROF_MEMERRMSGS 2
+
+#define SIZEOF_LOCERRMSG (LOCERRMSG_WIDTH * LOCERRMSG_HEIGHT / 2 * NUMBEROF_LOCERRMSGS)
+#define SIZEOF_MEMERRMSG (MEMERRMSG_WIDTH * MEMERRMSG_HEIGHT / 2 * NUMBEROF_MEMERRMSGS)
// Address with enough room after to load either of the error message image files before the fault screen buffer at the
// end of RDRAM
@@ -64,9 +72,9 @@ void Check_ClearRGBA16(u16* buffer) {
void Check_DrawExpansionPakErrorMessage(void) {
DmaMgr_SendRequest0(CHECK_ERRMSG_STATIC_SEGMENT, SEGMENT_ROM_START(memerrmsg), SEGMENT_SIZE(memerrmsg));
Check_ClearRGBA16((u16*)FAULT_FB_ADDRESS);
- Check_DrawI4Texture((u16*)FAULT_FB_ADDRESS, 96, 71, 128, 37, CHECK_ERRMSG_STATIC_SEGMENT);
- Check_DrawI4Texture((u16*)FAULT_FB_ADDRESS, 96, 127, 128, 37,
- CHECK_ERRMSG_STATIC_SEGMENT + sizeof(gExpansionPakNotInstalledErrorTex));
+ Check_DrawI4Texture((u16*)FAULT_FB_ADDRESS, 96, 71, MEMERRMSG_WIDTH, MEMERRMSG_HEIGHT, CHECK_ERRMSG_STATIC_SEGMENT);
+ Check_DrawI4Texture((u16*)FAULT_FB_ADDRESS, 96, 127, MEMERRMSG_WIDTH, MEMERRMSG_HEIGHT,
+ CHECK_ERRMSG_STATIC_SEGMENT + MEMERRMSG_WIDTH * MEMERRMSG_HEIGHT / 2);
osWritebackDCacheAll();
osViSwapBuffer((u16*)FAULT_FB_ADDRESS);
osViBlack(false);
@@ -78,7 +86,8 @@ void Check_DrawExpansionPakErrorMessage(void) {
void Check_DrawRegionLockErrorMessage(void) {
DmaMgr_SendRequest0(CHECK_ERRMSG_STATIC_SEGMENT, SEGMENT_ROM_START(locerrmsg), SEGMENT_SIZE(locerrmsg));
Check_ClearRGBA16((u16*)FAULT_FB_ADDRESS);
- Check_DrawI4Texture((u16*)FAULT_FB_ADDRESS, 56, 112, 208, 16, CHECK_ERRMSG_STATIC_SEGMENT);
+ Check_DrawI4Texture((u16*)FAULT_FB_ADDRESS, 56, 112, LOCERRMSG_WIDTH, LOCERRMSG_HEIGHT,
+ CHECK_ERRMSG_STATIC_SEGMENT);
osWritebackDCacheAll();
osViSwapBuffer((u16*)FAULT_FB_ADDRESS);
osViBlack(false);
diff --git a/tools/ZAPD/.gitrepo b/tools/ZAPD/.gitrepo
index 94da913d0d..a942638a2e 100644
--- a/tools/ZAPD/.gitrepo
+++ b/tools/ZAPD/.gitrepo
@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/zeldaret/ZAPD.git
branch = master
- commit = 4f7b8393ec8a3abd59649c2ba669e951fb61f3d2
- parent = 346df1bbc8ca21673fe63d884b6734c23a4d9f4b
+ commit = 50242eca96a9c36fd84f438bab24548e73b42303
+ parent = 744955732b1fc9e8a835c62440aa97799cfe8940
method = merge
cmdver = 0.4.3
diff --git a/tools/ZAPD/ExporterTest/ExporterTest.vcxproj b/tools/ZAPD/ExporterTest/ExporterTest.vcxproj
index a709a35091..839d451023 100644
--- a/tools/ZAPD/ExporterTest/ExporterTest.vcxproj
+++ b/tools/ZAPD/ExporterTest/ExporterTest.vcxproj
@@ -79,7 +79,7 @@
true
- $(SolutionDir)\ZAPD\;$(SolutionDir)ZAPDUtils;$(SolutionDir)lib\tinyxml2;$(SolutionDir)lib\libgfxd;$(SolutionDir)lib\elfio;$(SolutionDir)lib\stb;$(ProjectDir);$(IncludePath)
+ $(ProjectDir)..\ZAPD\;$(ProjectDir)..\ZAPDUtils;$(ProjectDir)..\lib\tinyxml2;$(ProjectDir)..\lib\libgfxd;$(ProjectDir)..\lib\elfio;$(ProjectDir)..\lib\stb;$(ProjectDir);$(IncludePath)
false
@@ -120,6 +120,7 @@
true
stdcpp17
stdc11
+ MultiThreadedDebug
Console
@@ -156,4 +157,4 @@
-
+
\ No newline at end of file
diff --git a/tools/ZAPD/ExporterTest/Main.cpp b/tools/ZAPD/ExporterTest/Main.cpp
index 4f683a1ba3..07fdbeeced 100644
--- a/tools/ZAPD/ExporterTest/Main.cpp
+++ b/tools/ZAPD/ExporterTest/Main.cpp
@@ -1,7 +1,7 @@
-#include
-#include
-#include
-#include
+#include "CollisionExporter.h"
+#include "Globals.h"
+#include "RoomExporter.h"
+#include "TextureExporter.h"
enum class ExporterFileMode
{
diff --git a/tools/ZAPD/ExporterTest/Makefile b/tools/ZAPD/ExporterTest/Makefile
index a2bfa9a814..42033b7c0c 100644
--- a/tools/ZAPD/ExporterTest/Makefile
+++ b/tools/ZAPD/ExporterTest/Makefile
@@ -1,7 +1,7 @@
# 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*")
+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))
diff --git a/tools/ZAPD/ExporterTest/RoomExporter.cpp b/tools/ZAPD/ExporterTest/RoomExporter.cpp
index c4a6844fb5..6c5552d8f8 100644
--- a/tools/ZAPD/ExporterTest/RoomExporter.cpp
+++ b/tools/ZAPD/ExporterTest/RoomExporter.cpp
@@ -1,24 +1,24 @@
#include "RoomExporter.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include "CollisionExporter.h"
+#include "Utils/BinaryWriter.h"
+#include "Utils/File.h"
+#include "Utils/MemoryStream.h"
+#include "ZRoom/Commands/SetCameraSettings.h"
+#include "ZRoom/Commands/SetCollisionHeader.h"
+#include "ZRoom/Commands/SetCsCamera.h"
+#include "ZRoom/Commands/SetEchoSettings.h"
+#include "ZRoom/Commands/SetEntranceList.h"
+#include "ZRoom/Commands/SetLightingSettings.h"
+#include "ZRoom/Commands/SetMesh.h"
+#include "ZRoom/Commands/SetRoomBehavior.h"
+#include "ZRoom/Commands/SetRoomList.h"
+#include "ZRoom/Commands/SetSkyboxModifier.h"
+#include "ZRoom/Commands/SetSkyboxSettings.h"
+#include "ZRoom/Commands/SetSoundSettings.h"
+#include "ZRoom/Commands/SetSpecialObjects.h"
+#include "ZRoom/Commands/SetStartPositionList.h"
+#include "ZRoom/Commands/SetTimeSettings.h"
+#include "ZRoom/Commands/SetWind.h"
void ExporterExample_Room::Save(ZResource* res, fs::path outPath, BinaryWriter* writer)
{
diff --git a/tools/ZAPD/ExporterTest/TextureExporter.h b/tools/ZAPD/ExporterTest/TextureExporter.h
index ffe6001dca..41c4e79be2 100644
--- a/tools/ZAPD/ExporterTest/TextureExporter.h
+++ b/tools/ZAPD/ExporterTest/TextureExporter.h
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include "Utils/BinaryWriter.h"
#include "ZResource.h"
#include "ZTexture.h"
diff --git a/tools/ZAPD/Jenkinsfile b/tools/ZAPD/Jenkinsfile
index 051e5f9982..8db69a1e30 100644
--- a/tools/ZAPD/Jenkinsfile
+++ b/tools/ZAPD/Jenkinsfile
@@ -7,7 +7,7 @@ pipeline {
// Non-parallel ZAPD stage
stage('Build ZAPD') {
steps {
- sh 'make -j'
+ sh 'make -j WERROR=1'
}
}
@@ -22,13 +22,13 @@ pipeline {
}
}
- 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'
+ // }
+ // }
+ // }
}
}
@@ -51,20 +51,20 @@ pipeline {
}
}
- stage('Setup MM') {
- steps {
- dir('mm') {
- sh 'cp /usr/local/etc/roms/mm.us.rev1.z64 baserom.mm.us.rev1.z64'
+ // 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'
- }
- }
- }
+ // // 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'
+ // }
+ // }
+ // }
}
}
@@ -78,14 +78,14 @@ pipeline {
}
}
}
- stage('Build mm') {
- steps {
- dir('mm') {
- sh 'make -j disasm'
- sh 'make -j all'
- }
- }
- }
+ // 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 737b85ecf7..2b47a8039b 100644
--- a/tools/ZAPD/Makefile
+++ b/tools/ZAPD/Makefile
@@ -6,6 +6,7 @@ DEPRECATION_ON ?= 1
DEBUG ?= 0
COPYCHECK_ARGS ?=
LLD ?= 0
+WERROR ?= 0
# Use clang++ if available, else use g++
ifeq ($(shell command -v clang++ >/dev/null 2>&1; echo $$?),0)
@@ -23,14 +24,16 @@ ifneq ($(DEBUG),0)
CXXFLAGS += -g3 -DDEVELOPMENT -D_DEBUG
COPYCHECK_ARGS += --devel
DEPRECATION_ON = 0
-else
+endif
+
+ifneq ($(WERROR),0)
CXXFLAGS += -Werror
endif
ifeq ($(OPTIMIZATION_ON),0)
OPTFLAGS := -O0
else
- OPTFLAGS := -O2 -march=native -mtune=native
+ OPTFLAGS := -O2
endif
ifneq ($(ASAN),0)
@@ -53,10 +56,23 @@ ifneq ($(LLD),0)
endif
UNAME := $(shell uname)
+UNAMEM := $(shell uname -m)
ifneq ($(UNAME), Darwin)
- LDFLAGS += -Wl,-export-dynamic -lstdc++fs
+ LDFLAGS += -Wl,-export-dynamic -lstdc++fs
+ EXPORTERS := -Wl,--whole-archive ExporterTest/ExporterTest.a -Wl,--no-whole-archive
+else
+ EXPORTERS := -Wl,-force_load ExporterTest/ExporterTest.a
+ ifeq ($(UNAMEM),arm64)
+ ifeq ($(shell brew list libpng > /dev/null 2>&1; echo $$?),0)
+ LDFLAGS += -L $(shell brew --prefix)/lib
+ INC += -I $(shell brew --prefix)/include
+ else
+ $(error Please install libpng via Homebrew)
+ endif
+ endif
endif
+
ZAPD_SRC_DIRS := $(shell find ZAPD -type d)
SRC_DIRS = $(ZAPD_SRC_DIRS) lib/tinyxml2
@@ -115,4 +131,4 @@ 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)
+ $(CXX) $(CXXFLAGS) $(O_FILES) lib/libgfxd/libgfxd.a ZAPDUtils/ZAPDUtils.a $(EXPORTERS) $(LDFLAGS) $(OUTPUT_OPTION)
diff --git a/tools/ZAPD/README.md b/tools/ZAPD/README.md
index e2764b6277..44d26b8b3e 100644
--- a/tools/ZAPD/README.md
+++ b/tools/ZAPD/README.md
@@ -16,6 +16,14 @@ In a Debian/Ubuntu based environment, those could be installed with the followin
sudo apt install libpng-dev
```
+On a Mac, you will need to install libpng with Homebrew or MacPorts; we currently only support Homebrew. You can run
+
+```bash
+brew install libpng
+```
+
+to install it via Homebrew.
+
### Building
#### Linux / *nix
@@ -109,11 +117,51 @@ ZAPD also accepts the following list of extra parameters:
- Could be useful for looking at raw data or testing.
- 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.
- `-se` / `--set-exporter` : Sets which exporter to use.
-- `--gcc-compat` : Enables GCC compatible mode. Slower.
+- `--gcc-compat` : Enables GCC compatibly mode. Slower.
- `-s` / `--static` : Mark every asset as `static`.
- This behaviour can be overridden per asset using `Static=` in the respective XML node.
+- `-W...`: warning flags, see below
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.
+
+### Warning flags
+
+ZAPD contains a variety of warning types, with similar syntax to GCC or Clang's compiler warnings. Warnings can have three levels:
+
+- Off (does not display anything)
+- Warn (print a warning but continue processing)
+- Err (behave like an error, i.e. print and throw an exception to crash ZAPD when occurs)
+
+Each warning type uses one of these by default, but can be modified with flags, similarly to GCC or Clang:
+
+- `-Wfoo` enables warnings of type `foo`
+- `-Wno-foo` disables warnings of type `foo`
+- `-Werror=foo` escalates `foo` to behave like an error
+- `-Weverything` enables all warnings (they may be turned off using `-Wno-` flags afterwards)
+- `-Werror` escalates all enabled warnings to errors
+
+All warning types currently implemented, with their default levels:
+
+| Warning type | Default level | Description |
+| --------------------------- | ------------- | ------------------------------------------------------------------------ |
+| `-Wdeprecated` | Warn | Deprecated features |
+| `-Whardcoded-pointer` | Warn | ZAPD lacks the info to make a symbol, so must output a hardcoded pointer |
+| `-Wintersection` | Warn | Two assets intersect |
+| `-Winvalid-attribute-value` | Err | Attribute declared in XML is wrong |
+| `-Winvalid-extracted-data` | Err | Extracted data does not have correct form |
+| `-Winvalid-jpeg` | Err | JPEG file does not conform to the game's format requirements |
+| `-Winvalid-png` | Err | Issues arising when processing PNG data |
+| `-Winvalid-xml` | Err | XML has syntax errors |
+| `-Wmissing-attribute` | Warn | Required attribute missing in XML tag |
+| `-Wmissing-offsets` | Warn | Offset attribute missing in XML tag |
+| `-Wmissing-segment` | Warn | Segment not given in File tag in XML |
+| `-Wnot-implemented` | Warn | ZAPD does not currently support this feature |
+| `-Wunaccounted` | Off | Large blocks of unaccounted |
+| `-Wunknown-attribute` | Warn | Unknown attribute in XML entry tag |
+
+There are also errors that do not have a type, and cannot be disabled.
+
+For example, here we have invoked ZAPD in the usual way to extract using a (rather badly-written) XML, but escalating `-Wintersection` to an error:
+
+
diff --git a/tools/ZAPD/ZAPD/Declaration.cpp b/tools/ZAPD/ZAPD/Declaration.cpp
index d2a86ffcc5..be06b0bea7 100644
--- a/tools/ZAPD/ZAPD/Declaration.cpp
+++ b/tools/ZAPD/ZAPD/Declaration.cpp
@@ -92,20 +92,20 @@ std::string Declaration::GetNormalDeclarationStr() const
if (isArray)
{
- if (arrayItemCntStr != "")
+ if (arrayItemCntStr != "" && (IsStatic() || forceArrayCnt))
{
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
+ else if (arrayItemCnt != 0 && (IsStatic() || forceArrayCnt))
{
output += StringHelper::Sprintf("%s %s[%i] = {\n", varType.c_str(), varName.c_str(),
arrayItemCnt);
}
+ else
+ {
+ output += StringHelper::Sprintf("%s %s[] = {\n", varType.c_str(), varName.c_str());
+ }
output += text + "\n";
}
@@ -145,16 +145,16 @@ std::string Declaration::GetExternalDeclarationStr() const
output += "static ";
}
- if (arrayItemCntStr != "")
+ if (arrayItemCntStr != "" && (IsStatic() || forceArrayCnt))
+ output += StringHelper::Sprintf("%s %s[%s] = ", varType.c_str(), varName.c_str(),
+ arrayItemCntStr.c_str());
+ else if (arrayItemCnt != 0 && (IsStatic() || forceArrayCnt))
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());
+ StringHelper::Sprintf("%s %s[%i] = ", varType.c_str(), varName.c_str(), arrayItemCnt);
else
- output += StringHelper::Sprintf("%s %s[] = {\n#include \"%s\"\n};", varType.c_str(),
- varName.c_str(), includePath.c_str());
+ output += StringHelper::Sprintf("%s %s[] = ", varType.c_str(), varName.c_str());
+
+ output += StringHelper::Sprintf("{\n#include \"%s\"\n};", includePath.c_str());
if (rightText != "")
output += " " + rightText + "";
@@ -178,14 +178,16 @@ std::string Declaration::GetExternStr() const
if (isArray)
{
- if (arrayItemCntStr != "")
+ if (arrayItemCntStr != "" && (IsStatic() || forceArrayCnt))
{
return StringHelper::Sprintf("extern %s %s[%s];\n", varType.c_str(), varName.c_str(),
arrayItemCntStr.c_str());
}
- else if (arrayItemCnt != 0)
+ else if (arrayItemCnt != 0 && (IsStatic() || forceArrayCnt))
+ {
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());
}
diff --git a/tools/ZAPD/ZAPD/Declaration.h b/tools/ZAPD/ZAPD/Declaration.h
index 138524a4c2..b7bb0d30d2 100644
--- a/tools/ZAPD/ZAPD/Declaration.h
+++ b/tools/ZAPD/ZAPD/Declaration.h
@@ -38,10 +38,12 @@ public:
std::string varType;
std::string varName;
std::string includePath;
+
bool isExternal = false;
bool isArray = false;
+ bool forceArrayCnt = false;
size_t arrayItemCnt = 0;
- std::string arrayItemCntStr;
+ std::string arrayItemCntStr = "";
std::vector references;
bool isUnaccounted = false;
bool isPlaceholder = false;
diff --git a/tools/ZAPD/ZAPD/GameConfig.cpp b/tools/ZAPD/ZAPD/GameConfig.cpp
index f197493a4a..ae29ba28fa 100644
--- a/tools/ZAPD/ZAPD/GameConfig.cpp
+++ b/tools/ZAPD/ZAPD/GameConfig.cpp
@@ -25,7 +25,7 @@ GameConfig::~GameConfig()
void GameConfig::ReadTexturePool(const fs::path& texturePoolXmlPath)
{
tinyxml2::XMLDocument doc;
- tinyxml2::XMLError eResult = doc.LoadFile(texturePoolXmlPath.c_str());
+ tinyxml2::XMLError eResult = doc.LoadFile(texturePoolXmlPath.string().c_str());
if (eResult != tinyxml2::XML_SUCCESS)
{
@@ -155,7 +155,7 @@ void GameConfig::ReadConfigFile(const fs::path& argConfigFilePath)
{"ExternalFile", &GameConfig::ConfigFunc_ExternalFile},
};
- configFilePath = argConfigFilePath;
+ configFilePath = argConfigFilePath.string();
tinyxml2::XMLDocument doc;
tinyxml2::XMLError eResult = doc.LoadFile(configFilePath.c_str());
diff --git a/tools/ZAPD/ZAPD/Globals.cpp b/tools/ZAPD/ZAPD/Globals.cpp
index 036e4a5cbf..528a09d256 100644
--- a/tools/ZAPD/ZAPD/Globals.cpp
+++ b/tools/ZAPD/ZAPD/Globals.cpp
@@ -3,8 +3,9 @@
#include
#include
-#include
-#include
+#include "Utils/File.h"
+#include "Utils/Path.h"
+#include "WarningHandler.h"
#include "tinyxml2.h"
Globals* Globals::Instance;
diff --git a/tools/ZAPD/ZAPD/Globals.h b/tools/ZAPD/ZAPD/Globals.h
index 265f1af241..19e193f123 100644
--- a/tools/ZAPD/ZAPD/Globals.h
+++ b/tools/ZAPD/ZAPD/Globals.h
@@ -20,6 +20,7 @@ typedef bool (*ExporterSetFuncBool)(ZFileMode fileMode);
typedef void (*ExporterSetFuncVoid)(int argc, char* argv[], int& i);
typedef void (*ExporterSetFuncVoid2)(const std::string& buildMode, ZFileMode& fileMode);
typedef void (*ExporterSetFuncVoid3)();
+typedef void (*ExporterSetResSave)(ZResource* res, BinaryWriter& writer);
class ExporterSet
{
@@ -34,6 +35,7 @@ public:
ExporterSetFunc endFileFunc = nullptr;
ExporterSetFuncVoid3 beginXMLFunc = nullptr;
ExporterSetFuncVoid3 endXMLFunc = nullptr;
+ ExporterSetResSave resSaveFunc = nullptr;
};
class Globals
@@ -53,9 +55,6 @@ public:
TextureType texType;
ZGame game;
GameConfig cfg;
- bool warnUnaccounted = false;
- bool warnNoOffset = false;
- bool errorNoOffset = false;
bool verboseUnaccounted = false;
bool gccCompat = false;
bool forceStatic = false;
diff --git a/tools/ZAPD/ZAPD/ImageBackend.cpp b/tools/ZAPD/ZAPD/ImageBackend.cpp
index 137e326c33..8accb6b4e1 100644
--- a/tools/ZAPD/ZAPD/ImageBackend.cpp
+++ b/tools/ZAPD/ZAPD/ImageBackend.cpp
@@ -6,6 +6,7 @@
#include
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
/* ImageBackend */
@@ -20,19 +21,28 @@ void ImageBackend::ReadPng(const char* filename)
FILE* fp = fopen(filename, "rb");
if (fp == nullptr)
- throw std::runtime_error(StringHelper::Sprintf(
- "ImageBackend::ReadPng: Error.\n\t Couldn't open file '%s'.", filename));
+ {
+ std::string errorHeader = StringHelper::Sprintf("could not open file '%s'", filename);
+ HANDLE_ERROR(WarningType::InvalidPNG, errorHeader, "");
+ }
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
- if (!png)
- throw std::runtime_error("ImageBackend::ReadPng: Error.\n\t Couldn't create png struct.");
+ if (png == nullptr)
+ {
+ HANDLE_ERROR(WarningType::InvalidPNG, "could not create png struct", "");
+ }
png_infop info = png_create_info_struct(png);
- if (!info)
- throw std::runtime_error("ImageBackend::ReadPng: Error.\n\t Couldn't create png info.");
+ if (info == nullptr)
+ {
+ HANDLE_ERROR(WarningType::InvalidPNG, "could not create png info", "");
+ }
if (setjmp(png_jmpbuf(png)))
- throw std::runtime_error("ImageBackend::ReadPng: Error.\n\t setjmp(png_jmpbuf(png)).");
+ {
+ // TODO: better warning explanation
+ HANDLE_ERROR(WarningType::InvalidPNG, "setjmp(png_jmpbuf(png))", "");
+ }
png_init_io(png, fp);
@@ -145,20 +155,30 @@ void ImageBackend::WritePng(const char* filename)
assert(hasImageData);
FILE* fp = fopen(filename, "wb");
- if (!fp)
- throw std::runtime_error(StringHelper::Sprintf(
- "ImageBackend::WritePng: Error.\n\t Couldn't open file '%s' in write mode.", filename));
+ if (fp == nullptr)
+ {
+ std::string errorHeader =
+ StringHelper::Sprintf("could not open file '%s' in write mode", filename);
+ HANDLE_ERROR(WarningType::InvalidPNG, errorHeader, "");
+ }
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
- if (!png)
- throw std::runtime_error("ImageBackend::WritePng: Error.\n\t Couldn't create png struct.");
+ if (png == nullptr)
+ {
+ HANDLE_ERROR(WarningType::InvalidPNG, "could not create png struct", "");
+ }
png_infop info = png_create_info_struct(png);
- if (!info)
- throw std::runtime_error("ImageBackend::WritePng: Error.\n\t Couldn't create png info.");
+ if (info == nullptr)
+ {
+ HANDLE_ERROR(WarningType::InvalidPNG, "could not create png info", "");
+ }
if (setjmp(png_jmpbuf(png)))
- throw std::runtime_error("ImageBackend::WritePng: Error.\n\t setjmp(png_jmpbuf(png)).");
+ {
+ // TODO: better warning description
+ HANDLE_ERROR(WarningType::InvalidPNG, "setjmp(png_jmpbuf(png))", "");
+ }
png_init_io(png, fp);
@@ -441,7 +461,7 @@ double ImageBackend::GetBytesPerPixel() const
return 1 * bitDepth / 8;
default:
- throw std::invalid_argument("ImageBackend::GetBytesPerPixel():\n\t Invalid color type.");
+ HANDLE_ERROR(WarningType::InvalidPNG, "invalid color type", "");
}
}
diff --git a/tools/ZAPD/ZAPD/Main.cpp b/tools/ZAPD/ZAPD/Main.cpp
index 298ca02629..fd2ed06dc0 100644
--- a/tools/ZAPD/ZAPD/Main.cpp
+++ b/tools/ZAPD/ZAPD/Main.cpp
@@ -1,8 +1,9 @@
-#include
-#include
-#include
#include "Globals.h"
#include "Overlays/ZOverlay.h"
+#include "Utils/Directory.h"
+#include "Utils/File.h"
+#include "Utils/Path.h"
+#include "WarningHandler.h"
#include "ZAnimation.h"
#include "ZBackground.h"
#include "ZBlob.h"
@@ -12,10 +13,10 @@
#if !defined(_MSC_VER) && !defined(__CYGWIN__)
#include
#include
+#include
#include // for __cxa_demangle
#include // for dladdr
#include
-#include
#include
#endif
@@ -47,6 +48,7 @@ void ErrorHandler(int sig)
const char* crashEasterEgg[] = {
"\tYou've met with a terrible fate, haven't you?",
"\tSEA BEARS FOAM. SLEEP BEARS DREAMS. \n\tBOTH END IN THE SAME WAY: CRASSSH!",
+ "ZAPD has fallen and cannot get up."
};
srand(time(nullptr));
@@ -97,6 +99,9 @@ int main(int argc, char* argv[])
return 1;
}
+ Globals* g = new Globals();
+ WarningHandler::Init(argc, argv);
+
for (int i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "--version"))
@@ -109,12 +114,12 @@ int main(int argc, char* argv[])
printf("Congratulations!\n");
printf("You just found the (unimplemented and undocumented) ZAPD's help message.\n");
printf("Feel free to implement it if you want :D\n");
+
+ WarningHandler::PrintHelp();
return 0;
}
}
- Globals* g = new Globals;
-
// Parse other "commands"
for (int32_t i = 2; i < argc; i++)
{
@@ -186,26 +191,15 @@ int main(int argc, char* argv[])
signal(SIGSEGV, ErrorHandler);
signal(SIGABRT, ErrorHandler);
#else
- fprintf(stderr,
- "Warning: Tried to set error handler, but this build lacks support for one.\n");
+ HANDLE_WARNING(WarningType::Always,
+ "tried to set error handler, but this ZAPD build lacks support for one",
+ "");
#endif
}
else if (arg == "-v") // Verbose
{
Globals::Instance->verbosity = static_cast(strtol(argv[++i], NULL, 16));
}
- else if (arg == "-wu" || arg == "--warn-unaccounted") // Warn unaccounted
- {
- Globals::Instance->warnUnaccounted = true;
- }
- else if (arg == "-wno" || arg == "--warn-no-offset")
- {
- Globals::Instance->warnNoOffset = true;
- }
- else if (arg == "-eno" || arg == "--error-no-offset")
- {
- Globals::Instance->errorNoOffset = true;
- }
else if (arg == "-vu" || arg == "--verbose-unaccounted") // Verbose unaccounted
{
Globals::Instance->verboseUnaccounted = true;
@@ -262,6 +256,11 @@ int main(int argc, char* argv[])
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
printf("ZAPD: Zelda Asset Processor For Decomp: %s\n", gBuildHash);
+ if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG)
+ {
+ WarningHandler::PrintWarningsDebugInfo();
+ }
+
// TODO: switch
if (fileMode == ZFileMode::Extract || fileMode == ZFileMode::BuildSourceFile)
{
@@ -334,7 +333,9 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
if (eResult != tinyxml2::XML_SUCCESS)
{
- fprintf(stderr, "Invalid xml file: '%s'\n", xmlFilePath.c_str());
+ // TODO: use XMLDocument::ErrorIDToName to get more specific error messages here
+ HANDLE_ERROR(WarningType::InvalidXML,
+ StringHelper::Sprintf("invalid XML file: '%s'", xmlFilePath.c_str()), "");
return false;
}
@@ -342,7 +343,9 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
if (root == nullptr)
{
- fprintf(stderr, "Missing Root tag in xml file: '%s'\n", xmlFilePath.c_str());
+ HANDLE_WARNING(
+ WarningType::InvalidXML,
+ StringHelper::Sprintf("missing Root tag in xml file: '%s'", xmlFilePath.c_str()), "");
return false;
}
@@ -392,10 +395,11 @@ bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path
}
else
{
- throw std::runtime_error(StringHelper::Sprintf(
- "Parse: Fatal error in '%s'.\n\t A resource was found outside of "
- "a File element: '%s'\n",
- xmlFilePath.c_str(), child->Name()));
+ std::string errorHeader =
+ StringHelper::Sprintf("when parsing file '%s'", xmlFilePath.c_str());
+ std::string errorBody = StringHelper::Sprintf(
+ "Found a resource outside a File element: '%s'", child->Name());
+ HANDLE_ERROR(WarningType::InvalidXML, errorHeader, errorBody);
}
}
diff --git a/tools/ZAPD/ZAPD/NuGet/libpng.static.txt b/tools/ZAPD/ZAPD/NuGet/libpng.static.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tools/ZAPD/ZAPD/Overlays/ZOverlay.cpp b/tools/ZAPD/ZAPD/Overlays/ZOverlay.cpp
index a842a7e7d9..510de19aba 100644
--- a/tools/ZAPD/ZAPD/Overlays/ZOverlay.cpp
+++ b/tools/ZAPD/ZAPD/Overlays/ZOverlay.cpp
@@ -1,13 +1,13 @@
#include "ZOverlay.h"
-#include
+#include
#include
-
-#include
-#include
-#include
-#include
#include "Globals.h"
+#include "Utils/Directory.h"
+#include "Utils/File.h"
+#include "Utils/Path.h"
+#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
using namespace ELFIO;
@@ -90,7 +90,7 @@ ZOverlay* ZOverlay::FromBuild(fs::path buildPath, fs::path cfgFolderPath)
std::vector readers;
for (size_t i = 1; i < cfgLines.size(); i++)
{
- std::string elfPath = buildPath / (cfgLines[i].substr(0, cfgLines[i].size() - 2) + ".o");
+ std::string elfPath = (buildPath / (cfgLines[i].substr(0, cfgLines[i].size() - 2) + ".o")).string();
elfio* reader = new elfio();
if (!reader->load(elfPath))
@@ -128,7 +128,9 @@ ZOverlay* ZOverlay::FromBuild(fs::path buildPath, fs::path cfgFolderPath)
SectionType sectionType = GetSectionTypeFromStr(pSec->get_name());
if (sectionType == SectionType::ERROR)
- fprintf(stderr, "WARNING: One of the section types returned ERROR\n");
+ {
+ HANDLE_WARNING(WarningType::Always, "one of the section types returned ERROR", "");
+ }
relocation_section_accessor relocs(*curReader, pSec);
for (Elf_Xword j = 0; j < relocs.get_entries_num(); j++)
diff --git a/tools/ZAPD/ZAPD/WarningHandler.cpp b/tools/ZAPD/ZAPD/WarningHandler.cpp
new file mode 100644
index 0000000000..29f8352a71
--- /dev/null
+++ b/tools/ZAPD/ZAPD/WarningHandler.cpp
@@ -0,0 +1,443 @@
+/**
+ * ZAPD Warning- and Error-handling system
+ * =======================================
+ *
+ * This provides a common standard way to write ZAPD warnings/errors, which should be used for all
+ * such. It will pretty-print them in a uniform way, with styles defined in the header.
+ *
+ * Warnings/errors should be constructed using the macros given in the header; there are now plenty
+ * of examples in the codebase of how to do this. Their purposes are noted above each category in
+ * the header. Each warning has a type, one of the ones in warningStringToInitMap, or
+ * WarningType::Always, which is used for warnings that cannot be disabled and do not display a
+ * type.
+ *
+ * Currently there are three levels of alert a warning can have:
+ * - Off (does not display anything)
+ * - Warn (print a warning but continue processing)
+ * - Err (behave like an error, i.e. print and throw an exception to crash ZAPD when occurs)
+ *
+ * Flag use:
+ * - -Wfoo enables warnings of type foo
+ * - -Wno-foo disables warnings of type foo
+ * - -Werror=foo escalates foo to behave like an error
+ * - -Weverything enables all warnings
+ * - -Werror escalates all enabled warnings to errors
+ *
+ * Errors do not have types, and will always throw an exception; they cannot be disabled.
+ *
+ * Format
+ * ===
+ * Each printed warning/error contains the same three sections:
+ * - Preamble: automatically generated; the content varies depending on category. It will print the
+ * file and function that the warning is from, and information about the files being processed
+ * or extracted.
+ * - Header: begins with 'warning: ' or 'error:', should contain essential information about the
+ * warning/error, ends with the warning type if applicable. Printed with emphasis to make it
+ * stand out. Does not start with a capital letter or end with a '.'
+ * - Body (optional): indented, should contain further diagnostic information useful for identifying
+ * and fixing the warning/error. Can be a sentence with captialisation and '.' on the end.
+ *
+ * Please think of what the end user will find most useful when writing the header and body, and try
+ * to keep it brief without sacrificing important information! Also remember that if the user is
+ * only looking at stderr, they will normally have no other context.
+ *
+ * Warning vs error
+ * ===
+ * The principle that we have operated on so far is
+ * - issue a warning if ZAPD will still be able to produce a valid, compilable C file that will
+ * match
+ * - if this cannot happen, use an error.
+ * but at the end of the day, it is up to the programmer's discretion what it should be possible to
+ * disable.
+ *
+ * Documentation
+ * ===
+ * Remember that all warnings also need to be documented in the README.md. The help is generated
+ * automatically.
+ */
+#include "WarningHandler.h"
+
+#include
+#include "Globals.h"
+#include "Utils/StringHelper.h"
+
+typedef struct
+{
+ WarningType type;
+ WarningLevel defaultLevel;
+ std::string description;
+} WarningInfoInit;
+
+typedef struct
+{
+ WarningLevel level;
+ std::string name;
+ std::string description;
+} WarningInfo;
+
+/**
+ * Master list of all default warning types and features
+ *
+ * To add a warning type, fill in a new row of this map. Think carefully about what its default
+ * level should be, and try and make the description both brief and informative: it is used in the
+ * help message, so again, think about what the end user needs to know.
+ */
+// clang-format off
+static const std::unordered_map warningStringToInitMap = {
+ {"deprecated", {WarningType::Deprecated,
+#ifdef DEPRECATION_ON
+ WarningLevel::Warn,
+#else
+ WarningLevel::Off,
+#endif
+ "Deprecated features"}},
+ {"unaccounted", {WarningType::Unaccounted, WarningLevel::Off, "Large blocks of unaccounted"}},
+ {"missing-offsets", {WarningType::MissingOffsets, WarningLevel::Warn, "Offset attribute missing in XML tag"}},
+ {"intersection", {WarningType::Intersection, WarningLevel::Warn, "Two assets intersect"}},
+ {"missing-attribute", {WarningType::MissingAttribute, WarningLevel::Warn, "Required attribute missing in XML tag"}},
+ {"invalid-attribute-value", {WarningType::InvalidAttributeValue, WarningLevel::Err, "Attribute declared in XML is wrong"}},
+ {"unknown-attribute", {WarningType::UnknownAttribute, WarningLevel::Warn, "Unknown attribute in XML entry tag"}},
+ {"invalid-xml", {WarningType::InvalidXML, WarningLevel::Err, "XML has syntax errors"}},
+ {"invalid-jpeg", {WarningType::InvalidJPEG, WarningLevel::Err, "JPEG file does not conform to the game's format requirements"}},
+ {"invalid-png", {WarningType::InvalidPNG, WarningLevel::Err, "Issues arising when processing PNG data"}},
+ {"invalid-extracted-data", {WarningType::InvalidExtractedData, WarningLevel::Err, "Extracted data does not have correct form"}},
+ {"missing-segment", {WarningType::MissingSegment, WarningLevel::Warn, "Segment not given in File tag in XML"}},
+ {"hardcoded-pointer", {WarningType::HardcodedPointer, WarningLevel::Warn, "ZAPD lacks the info to make a symbol, so must output a hardcoded pointer"}},
+ {"not-implemented", {WarningType::NotImplemented, WarningLevel::Warn, "ZAPD does not currently support this feature"}},
+};
+
+/**
+ * Map constructed at runtime to contain the warning features as set by the user using -W flags.
+ */
+static std::unordered_map warningTypeToInfoMap;
+
+void WarningHandler::ConstructTypeToInfoMap() {
+ for (auto& entry : warningStringToInitMap) {
+ warningTypeToInfoMap[entry.second.type] = {entry.second.defaultLevel, entry.first, entry.second.description};
+ }
+ warningTypeToInfoMap[WarningType::Always] = {WarningLevel::Warn, "always", "you shouldn't be reading this"};
+ assert(warningTypeToInfoMap.size() == static_cast(WarningType::Max));
+}
+
+/**
+ * Initialises the main warning type map and reads flags passed to set each warning type's level.
+ */
+void WarningHandler::Init(int argc, char* argv[]) {
+ ConstructTypeToInfoMap();
+
+ bool werror = false;
+ for (int i = 1; i < argc; i++) {
+ // If it doesn't start with "-W" skip it.
+ if (argv[i][0] != '-' || argv[i][1] != 'W' || argv[i][2] == '\0') {
+ continue;
+ }
+
+ WarningLevel warningTypeOn = WarningLevel::Warn;
+ size_t startingIndex = 2;
+
+ // "-Wno-"
+ if (argv[i][2] == 'n' && argv[i][3] == 'o' && argv[i][4] == '-' && argv[i][5] != '\0') {
+ warningTypeOn = WarningLevel::Off;
+ startingIndex = 5;
+ }
+
+ // Read starting after the "-W" or "-Wno-"
+ std::string_view currentArgv = &argv[i][startingIndex];
+
+ if (currentArgv == "error") {
+ werror = warningTypeOn != WarningLevel::Off;
+ } else if (currentArgv == "everything") {
+ for (auto& it: warningTypeToInfoMap) {
+ if (it.second.level <= WarningLevel::Warn) {
+ it.second.level = warningTypeOn;
+ }
+ }
+ } else {
+ // "-Werror=" / "-Wno-error=" parser
+ if (currentArgv.rfind("error=", 0) == 0) {
+ // Read starting after the "error=" part
+ currentArgv = &argv[i][startingIndex + 6];
+ warningTypeOn = warningTypeOn != WarningLevel::Off ? WarningLevel::Err : WarningLevel::Warn;
+ }
+
+ auto it = warningStringToInitMap.find(std::string(currentArgv));
+ if (it != warningStringToInitMap.end()) {
+ warningTypeToInfoMap[it->second.type].level = warningTypeOn;
+ }
+ else {
+ HANDLE_WARNING(WarningType::Always, StringHelper::Sprintf("unknown warning flag '%s'", argv[i]), "");
+ }
+ }
+ }
+
+ if (werror) {
+ for (auto& it: warningTypeToInfoMap) {
+ if (it.second.level >= WarningLevel::Warn) {
+ it.second.level = WarningLevel::Err;
+ }
+ }
+ }
+}
+
+bool WarningHandler::IsWarningEnabled(WarningType warnType) {
+ assert(static_cast(warnType) >= 0 && warnType < WarningType::Max);
+
+ return warningTypeToInfoMap.at(warnType).level != WarningLevel::Off;
+}
+
+bool WarningHandler::WasElevatedToError(WarningType warnType) {
+ assert(static_cast(warnType) >= 0 && warnType < WarningType::Max);
+
+ if (!IsWarningEnabled(warnType)) {
+ return false;
+ }
+
+ return warningTypeToInfoMap.at(warnType).level >= WarningLevel::Err;
+}
+
+/**
+ * Print file/line/function info for debugging
+ */
+void WarningHandler::FunctionPreamble(const char* filename, int32_t line, const char* function) {
+ if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) {
+ fprintf(stderr, "%s:%i: in function %s:\n", filename, line, function);
+ }
+}
+
+/**
+ * Print the information about the file(s) being processed (XML for extraction, png etc. for building)
+ */
+void WarningHandler::ProcessedFilePreamble() {
+ if (Globals::Instance->inputPath != "") {
+ fprintf(stderr, "When processing file %s: ", Globals::Instance->inputPath.c_str());
+ }
+}
+
+/**
+ * Print information about the binary file being extracted
+ */
+void WarningHandler::ExtractedFilePreamble(const ZFile *parent, const ZResource* res, const uint32_t offset) {
+ fprintf(stderr, "in input binary file %s, ", parent->GetName().c_str());
+ if (res != nullptr) {
+ fprintf(stderr, "resource '%s' at ", res->GetName().c_str());
+ }
+ fprintf(stderr, "offset 0x%06X: \n\t", offset);
+}
+
+/**
+ * Construct the rest of the message, after warning:/error. The message is filled in one character at a time, with indents added after newlines
+ */
+std::string WarningHandler::ConstructMessage(std::string message, const std::string& header, const std::string& body) {
+ message.reserve(message.size() + header.size() + body.size() + 10 * (sizeof(HANG_INDT) - 1));
+ message += StringHelper::Sprintf(HILITE("%s"), header.c_str());
+ message += "\n";
+
+ if (body == "") {
+ return message;
+ }
+
+ message += HANG_INDT;
+ for (const char* ptr = body.c_str(); *ptr != '\0'; ptr++) {
+ message += *ptr;
+ if (*ptr == '\n') {
+ message += HANG_INDT;
+ }
+ }
+ message += "\n";
+
+ return message;
+}
+
+/* Error module functions */
+
+void WarningHandler::PrintErrorAndThrow(const std::string& header, const std::string& body) {
+ std::string errorMsg = ERR_FMT("error: ");
+ throw std::runtime_error(ConstructMessage(errorMsg, header, body));
+}
+
+/* Error types, to be used via the macros */
+
+void WarningHandler::ErrorType(WarningType warnType, const std::string& header, const std::string& body) {
+ std::string headerMsg = header;
+
+ for (const auto& iter: warningStringToInitMap) {
+ if (iter.second.type == warnType) {
+ headerMsg += StringHelper::Sprintf(" [%s]", iter.first.c_str());
+ }
+ }
+
+ PrintErrorAndThrow(headerMsg, body);
+}
+
+void WarningHandler::Error_Plain(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) {
+ FunctionPreamble(filename, line, function);
+
+ ErrorType(warnType, header, body);
+}
+
+void WarningHandler::Error_Process(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) {
+ FunctionPreamble(filename, line, function);
+ ProcessedFilePreamble();
+
+ ErrorType(warnType, header, body);
+}
+
+void WarningHandler::Error_Resource(const char* filename, int32_t line, const char* function, WarningType warnType, const ZFile *parent, const ZResource* res, const uint32_t offset, const std::string& header, const std::string& body) {
+ assert(parent != nullptr);
+
+ FunctionPreamble(filename, line, function);
+ ProcessedFilePreamble();
+ ExtractedFilePreamble(parent, res, offset);
+
+ ErrorType(warnType, header, body);
+}
+
+/* Warning module functions */
+
+void WarningHandler::PrintWarningBody(const std::string& header, const std::string& body) {
+ std::string errorMsg = WARN_FMT("warning: ");
+ fprintf(stderr, "%s", ConstructMessage(errorMsg, header, body).c_str());
+}
+
+void WarningHandler::WarningTypeAndChooseEscalate(WarningType warnType, const std::string& header, const std::string& body) {
+ std::string headerMsg = header;
+
+ for (const auto& iter: warningStringToInitMap) {
+ if (iter.second.type == warnType) {
+ headerMsg += StringHelper::Sprintf(" [-W%s]", iter.first.c_str());
+ }
+ }
+
+ if (WasElevatedToError(warnType)) {
+ PrintErrorAndThrow(headerMsg, body);
+ } else {
+ PrintWarningBody(headerMsg, body);
+ }
+}
+
+
+/* Warning types, to be used via the macros */
+
+void WarningHandler::Warning_Plain(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) {
+ if (!IsWarningEnabled(warnType)) {
+ return;
+ }
+
+ FunctionPreamble(filename, line, function);
+
+ WarningTypeAndChooseEscalate(warnType, header, body);
+}
+
+void WarningHandler::Warning_Process(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) {
+ if (!IsWarningEnabled(warnType)) {
+ return;
+ }
+
+ FunctionPreamble(filename, line, function);
+ ProcessedFilePreamble();
+
+ WarningTypeAndChooseEscalate(warnType, header, body);
+}
+
+void WarningHandler::Warning_Resource(const char* filename, int32_t line, const char* function, WarningType warnType, const ZFile *parent, const ZResource* res, const uint32_t offset, const std::string& header, const std::string& body) {
+ assert(parent != nullptr);
+
+ if (!IsWarningEnabled(warnType)) {
+ return;
+ }
+
+ FunctionPreamble(filename, line, function);
+ ProcessedFilePreamble();
+ ExtractedFilePreamble(parent, res, offset);
+
+ WarningTypeAndChooseEscalate(warnType, header, body);
+}
+
+
+/* Help-related functions */
+
+#include
+
+/**
+ * Print each warning name, default status, and description using the init map
+ */
+void WarningHandler::PrintHelp() {
+ std::set sortedKeys;
+ WarningInfoInit warningInfo;
+ uint32_t columnWidth = 25;
+ std::string dt;
+
+ // Sort keys through the magic of `set`, to print in alphabetical order
+ for (auto& it : warningStringToInitMap) {
+ sortedKeys.insert(it.first);
+ }
+
+ printf("\nWarning types ( * means enabled by default)\n");
+ for (auto& key : sortedKeys) {
+ warningInfo = warningStringToInitMap.at(key);
+ if (warningInfo.defaultLevel <= WarningLevel::Warn) {
+ dt = "-W";
+ dt += key;
+ if (warningInfo.defaultLevel == WarningLevel::Warn) {
+ dt += " *";
+ }
+ printf(HELP_DT_INDT "%-*s", columnWidth, dt.c_str());
+
+ if (dt.length() + 2 > columnWidth) {
+ printf("\n" HELP_DT_INDT "%-*s", columnWidth, "");
+ }
+ printf("%s\n", warningInfo.description.c_str());
+ }
+ }
+
+ printf("\nDefault errors\n");
+ for (auto& key : sortedKeys) {
+ if (warningInfo.defaultLevel > WarningLevel::Warn) {
+ dt = "-W";
+ dt += key;
+ printf(HELP_DT_INDT "%-*s", columnWidth, dt.c_str());
+
+ if (dt.length() + 2 > columnWidth) {
+ printf("\n" HELP_DT_INDT "%*s", columnWidth, "");
+ }
+ printf("%s\n", warningInfo.description.c_str());
+ }
+ }
+
+ printf("\n");
+ printf("Other\n" HELP_DT_INDT "-Weverything will enable all existing warnings.\n" HELP_DT_INDT "-Werror will promote all warnings to errors.\n");
+
+ printf("\n");
+ printf("Warnings can be disabled using -Wno-... instead of -W...; -Weverything will override any -Wno-... flags passed before it.\n");
+}
+
+/**
+ * Print which warnings are currently enabled
+ */
+void WarningHandler::PrintWarningsDebugInfo()
+{
+ std::string dt;
+
+ printf("Warnings status:\n");
+ for (auto& it: warningTypeToInfoMap) {
+ dt = it.second.name;
+ dt += ": ";
+
+ printf(HELP_DT_INDT "%-25s", dt.c_str());
+ switch (it.second.level)
+ {
+ case WarningLevel::Off:
+ printf(VT_FGCOL(LIGHTGRAY) "Off" VT_RST);
+ break;
+ case WarningLevel::Warn:
+ printf(VT_FGCOL(YELLOW) "Warn" VT_RST);
+ break;
+ case WarningLevel::Err:
+ printf(VT_FGCOL(RED) "Err" VT_RST);
+ break;
+
+ }
+ printf("\n");
+ }
+ printf("\n");
+}
diff --git a/tools/ZAPD/ZAPD/WarningHandler.h b/tools/ZAPD/ZAPD/WarningHandler.h
new file mode 100644
index 0000000000..bb0360a813
--- /dev/null
+++ b/tools/ZAPD/ZAPD/WarningHandler.h
@@ -0,0 +1,145 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "Utils/vt.h"
+#include "ZFile.h"
+
+#ifdef _MSC_VER
+#define __PRETTY_FUNCTION__ __FUNCSIG__
+#elif not defined(__GNUC__)
+#define __PRETTY_FUNCTION__ __func__
+#endif
+
+// =======================================
+/* Formatting macros */
+
+// TODO: move this somewhere else so it can be used by other help
+#define HELP_DT_INDT " "
+
+/* Macros for formatting warnings/errors */
+#define VT_HILITE VT_BOLD_FGCOL(WHITE)
+#define VT_WARN VT_BOLD_FGCOL(PURPLE)
+#define VT_ERR VT_BOLD_FGCOL(RED)
+
+#define HILITE(string) (VT_HILITE string VT_RST)
+#define WARN_FMT(string) (VT_WARN string VT_RST)
+#define ERR_FMT(string) (VT_ERR string VT_RST)
+
+// Maybe make WARN_LF instead
+// Currently 8 spaces
+#define WARN_INDT " "
+// Currently 16 spaces
+#define HANG_INDT " "
+
+// =======================================
+/* Warning and error macros */
+// TODO: better names
+
+// General-purpose, plain style (only prints function,file,line in the preamble)
+#define HANDLE_ERROR(warningType, header, body) \
+ WarningHandler::Error_Plain(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, body)
+#define HANDLE_WARNING(warningType, header, body) \
+ WarningHandler::Warning_Plain(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, \
+ body)
+
+// For processing XMLs or textures/blobs (preamble contains function,file,line; processed file)
+#define HANDLE_ERROR_PROCESS(warningType, header, body) \
+ WarningHandler::Error_Process(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, \
+ body)
+#define HANDLE_WARNING_PROCESS(warningType, header, body) \
+ WarningHandler::Warning_Process(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, \
+ body)
+
+// For ZResource-related stuff (preamble contains function,file,line; processed file; extracted file
+// and offset)
+#define HANDLE_ERROR_RESOURCE(warningType, parent, resource, offset, header, body) \
+ WarningHandler::Error_Resource(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, parent, \
+ resource, offset, header, body)
+#define HANDLE_WARNING_RESOURCE(warningType, parent, resource, offset, header, body) \
+ WarningHandler::Warning_Resource(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, parent, \
+ resource, offset, header, body)
+
+// =======================================
+
+enum class WarningType
+{
+ Always, // Warnings of this type are always printed, cannot be disabled.
+ Deprecated,
+ Unaccounted,
+ MissingOffsets,
+ Intersection,
+ MissingAttribute,
+ InvalidAttributeValue,
+ UnknownAttribute,
+ InvalidXML,
+ InvalidJPEG,
+ InvalidPNG,
+ InvalidExtractedData,
+ MissingSegment,
+ HardcodedPointer,
+ NotImplemented,
+ Max,
+};
+
+enum class WarningLevel
+{
+ Off,
+ Warn,
+ Err,
+};
+
+class WarningHandler
+{
+public:
+ static void ConstructTypeToInfoMap();
+
+ static void Init(int argc, char* argv[]);
+
+ static bool IsWarningEnabled(WarningType warnType);
+ static bool WasElevatedToError(WarningType warnType);
+
+ static void FunctionPreamble(const char* filename, int32_t line, const char* function);
+ static void ProcessedFilePreamble();
+ static void ExtractedFilePreamble(const ZFile* parent, const ZResource* res,
+ const uint32_t offset);
+ static std::string ConstructMessage(std::string message, const std::string& header,
+ const std::string& body);
+
+ [[noreturn]] static void PrintErrorAndThrow(const std::string& header, const std::string& body);
+ static void PrintWarningBody(const std::string& header, const std::string& body);
+
+ [[noreturn]] static void ErrorType(WarningType warnType, const std::string& header,
+ const std::string& body);
+ [[noreturn]] static void Error_Plain(const char* filename, int32_t line, const char* function,
+ WarningType warnType, const std::string& header,
+ const std::string& body);
+ [[noreturn]] static void Error_Process(const char* filename, int32_t line, const char* function,
+ WarningType warnType, const std::string& header,
+ const std::string& body);
+ [[noreturn]] static void Error_Resource(const char* filename, int32_t line,
+ const char* function, WarningType warnType,
+ const ZFile* parent, const ZResource* res,
+ const uint32_t offset, const std::string& header,
+ const std::string& body);
+
+ static void WarningTypeAndChooseEscalate(WarningType warnType, const std::string& header,
+ const std::string& body);
+
+ static void Warning_Plain(const char* filename, int32_t line, const char* function,
+ WarningType warnType, const std::string& header,
+ const std::string& body);
+ static void Warning_Process(const char* filename, int32_t line, const char* function,
+ WarningType warnType, const std::string& header,
+ const std::string& body);
+ static void Warning_Resource(const char* filename, int32_t line, const char* function,
+ WarningType warnType, const ZFile* parent, const ZResource* res,
+ const uint32_t offset, const std::string& header,
+ const std::string& body);
+
+ static void PrintHelp();
+ static void PrintWarningsDebugInfo();
+};
diff --git a/tools/ZAPD/ZAPD/ZAPD.vcxproj b/tools/ZAPD/ZAPD/ZAPD.vcxproj
index c74d28cbec..431abb73b9 100644
--- a/tools/ZAPD/ZAPD/ZAPD.vcxproj
+++ b/tools/ZAPD/ZAPD/ZAPD.vcxproj
@@ -1,5 +1,6 @@
+
Debug
@@ -71,8 +72,8 @@
- $(SolutionDir)lib\libgfxd;$(SolutionDir)x64\Debug;$(SolutionDir)packages\libpng.1.6.28.1\build\native\lib\x64\v140\dynamic\Debug;$(LibraryPath)
- $(SolutionDir)ZAPDUtils;$(SolutionDir)lib\tinyxml2;$(SolutionDir)lib\libgfxd;$(SolutionDir)lib\elfio;$(SolutionDir)lib\stb;$(ProjectDir);$(IncludePath)
+ $(OutDir);$(ProjectDir)..\lib\libgfxd;$(ProjectDir)..\packages\libpng-v142.1.6.37.2\build\native\lib\x64\v142\Debug\;$(LibraryPath)
+ $(ProjectDir)..\ZAPDUtils;$(ProjectDir)..\lib\tinyxml2;$(ProjectDir)..\lib\libgfxd;$(ProjectDir)..\lib\elfio;$(ProjectDir)..\lib\stb;$(ProjectDir);$(IncludePath)
$(IncludePath)
@@ -105,13 +106,16 @@
_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
EnableFastChecks
stdc11
+ MultiThreadedDebug
true
- libpng16.lib;ZAPDUtils.lib;/WHOLEARCHIVE:ExporterExample.lib;%(AdditionalDependencies)
+ ZAPDUtils.lib;/WHOLEARCHIVE:ExporterExample.lib;%(AdditionalDependencies)
+ false
cd ..
+mkdir build\ZAPD
python3 ZAPD/genbuildinfo.py
@@ -146,6 +150,7 @@ python3 ZAPD/genbuildinfo.py
+
@@ -153,19 +158,22 @@ python3 ZAPD/genbuildinfo.py
-
+
+
+
+
@@ -213,6 +221,7 @@ python3 ZAPD/genbuildinfo.py
+
@@ -237,10 +246,13 @@ python3 ZAPD/genbuildinfo.py
+
+
+
@@ -253,6 +265,7 @@ python3 ZAPD/genbuildinfo.py
+
@@ -294,6 +307,7 @@ python3 ZAPD/genbuildinfo.py
+
@@ -301,24 +315,29 @@ python3 ZAPD/genbuildinfo.py
true
+
+
-
-
+
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
+
+
+
+
\ No newline at end of file
diff --git a/tools/ZAPD/ZAPD/ZAPD.vcxproj.filters b/tools/ZAPD/ZAPD/ZAPD.vcxproj.filters
index 2d3050ed42..b8a7daaacf 100644
--- a/tools/ZAPD/ZAPD/ZAPD.vcxproj.filters
+++ b/tools/ZAPD/ZAPD/ZAPD.vcxproj.filters
@@ -49,6 +49,15 @@
{85600275-99fe-491d-8189-bcc3dc1a8903}
+
+ {ba9990b0-1082-48bb-874c-6108534b5455}
+
+
+ {ce9d91b0-ba20-4296-bc2d-8630965bb392}
+
+
+ {730beb67-6d59-4849-9d9b-702c4a565fc0}
+
@@ -135,9 +144,6 @@
Source Files\Z64\ZRoom\Commands
-
- Source Files\Libraries
-
Source Files\Z64
@@ -258,6 +264,24 @@
Source Files\Z64
+
+ Source Files
+
+
+ Source Files\Z64
+
+
+ Source Files\Z64
+
+
+ Source Files
+
+
+ Source Files\Z64
+
+
+ Source Files
+
@@ -497,11 +521,32 @@
Header Files\Z64
+
+ Header Files
+
+
+ Header Files\Z64
+
+
+ Header Files\Z64
+
+
+ Header Files\Z64
+
+
+ Header Files
+
Resource Files
+
+ any\any
+
+
+ NuGet
+
diff --git a/tools/ZAPD/ZAPD/ZAnimation.cpp b/tools/ZAPD/ZAPD/ZAnimation.cpp
index 9242f657a8..adb0ae7c9a 100644
--- a/tools/ZAPD/ZAPD/ZAnimation.cpp
+++ b/tools/ZAPD/ZAPD/ZAnimation.cpp
@@ -6,6 +6,7 @@
#include "Utils/BitConverter.h"
#include "Utils/File.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZFile.h"
REGISTER_ZFILENODE(Animation, ZNormalAnimation);
@@ -218,11 +219,9 @@ void ZCurveAnimation::ParseXML(tinyxml2::XMLElement* reader)
std::string skelOffsetXml = registeredAttributes.at("SkelOffset").value;
if (skelOffsetXml == "")
{
- throw std::runtime_error(
- StringHelper::Sprintf("ZCurveAnimation::ParseXML: Fatal error in '%s'.\n"
- "\t Missing 'SkelOffset' attribute in ZCurveAnimation.\n"
- "\t You need to provide the offset of the curve skeleton.",
- name.c_str()));
+ HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex,
+ "missing 'SkelOffset' attribute in ",
+ "You need to provide the offset of the curve skeleton.");
}
skelOffset = StringHelper::StrToL(skelOffsetXml, 0);
}
diff --git a/tools/ZAPD/ZAPD/ZAnimation.h b/tools/ZAPD/ZAPD/ZAnimation.h
index e5b69d3ef8..2c04b4ff81 100644
--- a/tools/ZAPD/ZAPD/ZAnimation.h
+++ b/tools/ZAPD/ZAPD/ZAnimation.h
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include
#include
#include
#include "Vec3s.h"
diff --git a/tools/ZAPD/ZAPD/ZArray.cpp b/tools/ZAPD/ZAPD/ZArray.cpp
index b1ba3a6693..ebfb13ee74 100644
--- a/tools/ZAPD/ZAPD/ZArray.cpp
+++ b/tools/ZAPD/ZAPD/ZArray.cpp
@@ -4,6 +4,7 @@
#include "Globals.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZFile.h"
REGISTER_ZFILENODE(Array, ZArray);
@@ -25,13 +26,18 @@ void ZArray::ParseXML(tinyxml2::XMLElement* reader)
ZResource::ParseXML(reader);
arrayCnt = reader->IntAttribute("Count", 0);
- // TODO: do a better check.
- assert(arrayCnt > 0);
+ if (arrayCnt <= 0)
+ {
+ HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex,
+ "invalid value found for 'Count' attribute", "");
+ }
tinyxml2::XMLElement* child = reader->FirstChildElement();
if (child == nullptr)
- throw std::runtime_error(
- StringHelper::Sprintf("Error! Array needs at least one sub-element.\n"));
+ {
+ HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex,
+ " needs one sub-element", "");
+ }
childName = child->Name();
@@ -42,9 +48,10 @@ void ZArray::ParseXML(tinyxml2::XMLElement* reader)
ZResource* res = nodeMap->at(childName)(parent);
if (!res->DoesSupportArray())
{
- throw std::runtime_error(StringHelper::Sprintf(
- "Error! Resource %s does not support being wrapped in an array!\n",
- childName.c_str()));
+ std::string errorHeader = StringHelper::Sprintf(
+ "resource <%s> does not support being wrapped in an ", childName.c_str());
+ HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex, errorHeader,
+ "");
}
res->parent = parent;
res->SetInnerNode(true);
@@ -87,7 +94,7 @@ Declaration* ZArray::DeclareVar(const std::string& prefix, const std::string& bo
std::string ZArray::GetBodySourceCode() const
{
- std::string output;
+ std::string output = "";
for (size_t i = 0; i < arrayCnt; i++)
{
diff --git a/tools/ZAPD/ZAPD/ZArray.h b/tools/ZAPD/ZAPD/ZArray.h
index 46a04d7329..b78a8edfd8 100644
--- a/tools/ZAPD/ZAPD/ZArray.h
+++ b/tools/ZAPD/ZAPD/ZArray.h
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include
#include
#include
#include "ZResource.h"
diff --git a/tools/ZAPD/ZAPD/ZBackground.cpp b/tools/ZAPD/ZAPD/ZBackground.cpp
index 4125f239f3..0ed1eb7471 100644
--- a/tools/ZAPD/ZAPD/ZBackground.cpp
+++ b/tools/ZAPD/ZAPD/ZBackground.cpp
@@ -5,6 +5,7 @@
#include "Utils/File.h"
#include "Utils/Path.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZFile.h"
REGISTER_ZFILENODE(Background, ZBackground);
@@ -63,52 +64,46 @@ void ZBackground::CheckValidJpeg(const std::string& filepath)
uint32_t jpegMarker = BitConverter::ToUInt32BE(data, 0);
if (jpegMarker != JPEG_MARKER)
{
- fprintf(stderr,
- "ZBackground::CheckValidJpeg: Warning.\n"
- "\t Missing jpeg marker at the beginning of file: '%s'.\n"
- "\t The game will skip this jpeg.\n",
- filename.c_str());
+ HANDLE_WARNING_PROCESS(
+ WarningType::InvalidJPEG,
+ StringHelper::Sprintf("missing jpeg marker at beginning of file: '%s'",
+ filename.c_str()),
+ "The game will skip this jpeg.");
}
if (data.at(6) != 'J' || data.at(7) != 'F' || data.at(8) != 'I' || data.at(9) != 'F' ||
data.at(10) != '\0')
{
std::string jfifIdentifier(data.begin() + 6, data.begin() + 6 + 5);
- fprintf(stderr,
- "ZBackground::CheckValidJpeg: Warning.\n"
- "\t Missing 'JFIF' identifier. File: '%s'.\n"
- "\t This image may be corrupted or not be a jpeg iamge.\n"
- "\t The identifier found was '%s'.\n",
- filename.c_str(), jfifIdentifier.c_str());
+ HANDLE_WARNING_PROCESS(
+ WarningType::InvalidJPEG, "missing 'JFIF' identifier",
+ StringHelper::Sprintf(
+ "This image may be corrupted, or not a jpeg. The identifier found was: '%s'",
+ jfifIdentifier.c_str()));
}
uint8_t majorVersion = data.at(11);
uint8_t minorVersion = data.at(12);
if (majorVersion != 0x01 || minorVersion != 0x01)
{
- fprintf(stderr,
- "ZBackground::CheckValidJpeg: Warning.\n"
- "\t Wrong JFIF version '%i.%02i'. File: '%s'.\n"
- "\t The expected version is '1.01'. The game may not be able to decode this image "
- "properly.\n",
- majorVersion, minorVersion, filename.c_str());
+ HANDLE_WARNING_PROCESS(
+ WarningType::InvalidJPEG,
+ StringHelper::Sprintf("wrong JFIF version '%i.%02i'", majorVersion, minorVersion),
+ "The expected version is '1.01'. The game may be unable to decode this image "
+ "correctly.");
}
if (BitConverter::ToUInt16BE(data, 20) != MARKER_DQT)
{
// This may happen when creating a custom image with Exif, XMP, thumbnail, progressive, etc.
// enabled.
- fprintf(stderr,
- "ZBackground::CheckValidJpeg: Warning.\n"
- "\t There seems to be extra data before the image data in file: '%s'.\n"
- "\t The game may not be able to decode this image properly.\n",
- filename.c_str());
+ HANDLE_WARNING_PROCESS(WarningType::InvalidJPEG,
+ "there seems to be extra data before the image data in this file",
+ "The game may not be able to decode this image correctly.");
}
if (data.size() > GetRawDataSize())
{
- fprintf(stderr,
- "ZBackground::CheckValidJpeg: Warning.\n"
- "\t The image is bigger than the screen buffer. File: '%s'.\n"
- "\t Image size: %zu bytes.\n"
- "\t Screen buffer size: %zu bytes.\n",
- filename.c_str(), data.size(), GetRawDataSize());
+ HANDLE_WARNING_PROCESS(
+ WarningType::InvalidJPEG, "the image is bigger than the screen buffer",
+ StringHelper::Sprintf("Image size: %zu bytes\nScreen buffer size: %zu bytes",
+ data.size(), GetRawDataSize()));
}
}
@@ -138,6 +133,7 @@ Declaration* ZBackground::DeclareVar(const std::string& prefix,
Declaration* decl = parent->AddDeclarationIncludeArray(rawDataIndex, incStr, GetRawDataSize(),
GetSourceTypeName(), auxName, 0);
decl->arrayItemCntStr = "SCREEN_WIDTH * SCREEN_HEIGHT / 4";
+ decl->forceArrayCnt = true;
decl->staticConf = staticConf;
return decl;
}
diff --git a/tools/ZAPD/ZAPD/ZBlob.cpp b/tools/ZAPD/ZAPD/ZBlob.cpp
index c1f0788206..6812bfaee9 100644
--- a/tools/ZAPD/ZAPD/ZBlob.cpp
+++ b/tools/ZAPD/ZAPD/ZBlob.cpp
@@ -83,11 +83,6 @@ std::string ZBlob::GetBodySourceCode() const
return sourceOutput;
}
-std::string ZBlob::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix)
-{
- return StringHelper::Sprintf("extern u8 %s[];\n", name.c_str());
-}
-
void ZBlob::Save(const fs::path& outFolder)
{
File::WriteAllBytes((outFolder / (name + ".bin")).string(), blobData);
diff --git a/tools/ZAPD/ZAPD/ZBlob.h b/tools/ZAPD/ZAPD/ZBlob.h
index 86623b5119..d7a7feff12 100644
--- a/tools/ZAPD/ZAPD/ZBlob.h
+++ b/tools/ZAPD/ZAPD/ZBlob.h
@@ -16,7 +16,6 @@ public:
Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override;
std::string GetBodySourceCode() const override;
- std::string GetSourceOutputHeader(const std::string& prefix) override;
void Save(const fs::path& outFolder) override;
bool IsExternalResource() const override;
diff --git a/tools/ZAPD/ZAPD/ZCollision.cpp b/tools/ZAPD/ZAPD/ZCollision.cpp
index f9c0bf7d62..1dfa46b1dd 100644
--- a/tools/ZAPD/ZAPD/ZCollision.cpp
+++ b/tools/ZAPD/ZAPD/ZCollision.cpp
@@ -88,7 +88,7 @@ void ZCollisionHeader::ParseRawData()
void ZCollisionHeader::DeclareReferences(const std::string& prefix)
{
- std::string declaration;
+ std::string declaration = "";
std::string auxName = name;
if (name == "")
@@ -174,7 +174,7 @@ void ZCollisionHeader::DeclareReferences(const std::string& prefix)
std::string ZCollisionHeader::GetBodySourceCode() const
{
- std::string declaration;
+ std::string declaration = "";
declaration += "\n";
diff --git a/tools/ZAPD/ZAPD/ZCutscene.cpp b/tools/ZAPD/ZAPD/ZCutscene.cpp
index 237481079f..ba8fe89638 100644
--- a/tools/ZAPD/ZAPD/ZCutscene.cpp
+++ b/tools/ZAPD/ZAPD/ZCutscene.cpp
@@ -2,6 +2,7 @@
#include "Utils/BitConverter.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZResource.h"
REGISTER_ZFILENODE(Cutscene, ZCutscene);
@@ -87,7 +88,7 @@ CutsceneCommandSceneTransFX::~CutsceneCommandSceneTransFX()
std::string ZCutscene::GetBodySourceCode() const
{
- std::string output;
+ std::string output = "";
uint32_t curPtr = 0;
output += StringHelper::Sprintf(" CS_BEGIN_CUTSCENE(%i, %i),\n", commands.size(), endFrame);
@@ -225,8 +226,9 @@ void ZCutscene::ParseRawData()
cmd = new CutsceneCommandEnd(rawData, currentPtr);
break;
case CutsceneCommands::Error:
- fprintf(stderr, "Cutscene command error %d %s %d\n", (int32_t)cmdID, __FILE__,
- __LINE__);
+ HANDLE_WARNING_RESOURCE(WarningType::NotImplemented, parent, this, rawDataIndex,
+ StringHelper::Sprintf("cutscene command error %d", cmdID),
+ "");
break;
}
@@ -404,7 +406,9 @@ CutsceneCommands ZCutscene::GetCommandFromID(int32_t id)
return CutsceneCommands::Unknown;
}
- fprintf(stderr, "WARNING: Could not identify cutscene command ID 0x%04X\n", id);
+ HANDLE_WARNING_RESOURCE(
+ WarningType::NotImplemented, parent, this, rawDataIndex,
+ StringHelper::Sprintf("could not identify cutscene command. ID 0x%04X", id), "");
return CutsceneCommands::Error;
}
diff --git a/tools/ZAPD/ZAPD/ZCutscene.h b/tools/ZAPD/ZAPD/ZCutscene.h
index cbb6a78b02..8e901e3075 100644
--- a/tools/ZAPD/ZAPD/ZCutscene.h
+++ b/tools/ZAPD/ZAPD/ZCutscene.h
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include
#include
#include
#include "ZFile.h"
diff --git a/tools/ZAPD/ZAPD/ZCutsceneMM.h b/tools/ZAPD/ZAPD/ZCutsceneMM.h
index 41b7de37f3..44b108d6c1 100644
--- a/tools/ZAPD/ZAPD/ZCutsceneMM.h
+++ b/tools/ZAPD/ZAPD/ZCutsceneMM.h
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include
#include
#include
#include "ZCutscene.h"
diff --git a/tools/ZAPD/ZAPD/ZDisplayList.cpp b/tools/ZAPD/ZAPD/ZDisplayList.cpp
index 63c5684228..bdb7374fe1 100644
--- a/tools/ZAPD/ZAPD/ZDisplayList.cpp
+++ b/tools/ZAPD/ZAPD/ZDisplayList.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include "Globals.h"
@@ -11,6 +12,7 @@
#include "Utils/File.h"
#include "Utils/Path.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "gfxd.h"
REGISTER_ZFILENODE(DList, ZDisplayList);
@@ -105,7 +107,7 @@ void ZDisplayList::ParseF3DZEX(F3DZEXOpcode opcode, uint64_t data, int32_t i,
switch (opcode)
{
case F3DZEXOpcode::G_NOOP:
- sprintf(line, "gsDPNoOpTag(0x%08lX),", data & 0xFFFFFFFF);
+ sprintf(line, "gsDPNoOpTag(0x%08" PRIX64 "),", data & 0xFFFFFFFF);
break;
case F3DZEXOpcode::G_DL:
Opcode_G_DL(data, prefix, line);
@@ -220,7 +222,7 @@ void ZDisplayList::ParseF3DZEX(F3DZEXOpcode opcode, uint64_t data, int32_t i,
break;
case F3DZEXOpcode::G_POPMTX:
{
- sprintf(line, "gsSPPopMatrix(%li),", data);
+ sprintf(line, "gsSPPopMatrix(%" PRIi64 "),", data);
}
break;
case F3DZEXOpcode::G_LOADTLUT:
@@ -297,7 +299,7 @@ void ZDisplayList::ParseF3DEX(F3DEXOpcode opcode, uint64_t data, const std::stri
switch (opcode)
{
case F3DEXOpcode::G_NOOP:
- sprintf(line, "gsDPNoOpTag(0x%08lX),", data & 0xFFFFFFFF);
+ sprintf(line, "gsDPNoOpTag(0x%08" PRIX64 "),", data & 0xFFFFFFFF);
break;
case F3DEXOpcode::G_VTX:
Opcode_G_VTX(data, line);
@@ -445,11 +447,12 @@ int32_t ZDisplayList::GetDListLength(const std::vector& rawData, uint32
{
if (ptr >= rawDataSize)
{
- throw std::runtime_error(StringHelper::Sprintf(
- "%s: Fatal error.\n"
- "\t End of file found when trying to find the end of the "
- "DisplayList at offset: '0x%X'.\n",
- "Raw data size: 0x%zX.\n", __PRETTY_FUNCTION__, rawDataIndex, rawDataSize));
+ std::string errorHeader =
+ StringHelper::Sprintf("reached end of file when trying to find the end of the "
+ "DisplayList starting at offset 0x%X",
+ rawDataIndex);
+ std::string errorBody = StringHelper::Sprintf("Raw data size: 0x%zX.", rawDataSize);
+ HANDLE_ERROR_PROCESS(WarningType::Always, errorHeader, errorBody);
}
uint8_t opcode = rawData.at(ptr);
@@ -686,20 +689,20 @@ void ZDisplayList::Opcode_G_DL(uint64_t data, const std::string& prefix, char* l
if (pp != 0)
{
if (!Globals::Instance->HasSegment(segNum))
- sprintf(line, "gsSPBranchList(0x%08lX),", data & 0xFFFFFFFF);
+ sprintf(line, "gsSPBranchList(0x%08" PRIX64 "),", data & 0xFFFFFFFF);
else if (dListDecl != nullptr)
sprintf(line, "gsSPBranchList(%s),", dListDecl->varName.c_str());
else
- sprintf(line, "gsSPBranchList(%sDlist0x%06lX),", prefix.c_str(), GETSEGOFFSET(data));
+ sprintf(line, "gsSPBranchList(%sDlist0x%06" PRIX64 "),", prefix.c_str(), GETSEGOFFSET(data));
}
else
{
if (!Globals::Instance->HasSegment(segNum))
- sprintf(line, "gsSPDisplayList(0x%08lX),", data & 0xFFFFFFFF);
+ sprintf(line, "gsSPDisplayList(0x%08" PRIX64 "),", data & 0xFFFFFFFF);
else if (dListDecl != nullptr)
sprintf(line, "gsSPDisplayList(%s),", dListDecl->varName.c_str());
else
- sprintf(line, "gsSPDisplayList(%sDlist0x%06lX),", prefix.c_str(), GETSEGOFFSET(data));
+ sprintf(line, "gsSPDisplayList(%sDlist0x%06" PRIX64 "),", prefix.c_str(), GETSEGOFFSET(data));
}
// if (segNum == 8 || segNum == 9 || segNum == 10 || segNum == 11 || segNum == 12 || segNum ==
@@ -707,9 +710,9 @@ void ZDisplayList::Opcode_G_DL(uint64_t data, const std::string& prefix, char* l
if (!Globals::Instance->HasSegment(segNum))
{
if (pp != 0)
- sprintf(line, "gsSPBranchList(0x%08lX),", data & 0xFFFFFFFF);
+ sprintf(line, "gsSPBranchList(0x%08" PRIX64 "),", data & 0xFFFFFFFF);
else
- sprintf(line, "gsSPDisplayList(0x%08lX),", data & 0xFFFFFFFF);
+ sprintf(line, "gsSPDisplayList(0x%08" PRIX64 "),", data & 0xFFFFFFFF);
}
else
{
@@ -941,7 +944,7 @@ void ZDisplayList::Opcode_G_SETTIMG(uint64_t data, const std::string& prefix, ch
sprintf(texStr, "%sTex_%06X", prefix.c_str(), texAddress);
else
{
- sprintf(texStr, "0x%08lX", data & 0xFFFFFFFF);
+ sprintf(texStr, "0x%08" PRIX64, data & 0xFFFFFFFF);
}
sprintf(line, "gsDPSetTextureImage(%s, %s, %i, %s),", fmtTbl[fmt], sizTbl[siz], www + 1,
@@ -1522,11 +1525,6 @@ void ZDisplayList::Opcode_G_ENDDL([[maybe_unused]] const std::string& prefix, ch
TextureGenCheck();
}
-std::string ZDisplayList::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix)
-{
- return "";
-}
-
static int32_t GfxdCallback_FormatSingleEntry()
{
ZDisplayList* self = static_cast(gfxd_udata_get());
@@ -1737,7 +1735,7 @@ static int32_t GfxdCallback_Matrix(uint32_t seg)
return 1;
}
-std::string ZDisplayList::GetSourceOutputCode(const std::string& prefix)
+void ZDisplayList::DeclareReferences(const std::string& prefix)
{
std::string sourceOutput;
@@ -1750,7 +1748,7 @@ std::string ZDisplayList::GetSourceOutputCode(const std::string& prefix)
if (vertices.size() > 0)
{
std::vector>> verticesSorted(vertices.begin(),
- vertices.end());
+ vertices.end());
for (size_t i = 0; i < verticesSorted.size() - 1; i++)
{
@@ -1775,15 +1773,13 @@ std::string ZDisplayList::GetSourceOutputCode(const std::string& prefix)
// Generate Vertex Declarations
for (auto& item : vertices)
{
- std::string declaration;
+ std::string declaration = "";
offset_t curAddr = item.first;
auto& firstVtx = item.second.at(0);
for (auto vtx : item.second)
- {
declaration += StringHelper::Sprintf("\t%s,\n", vtx.GetBodySourceCode().c_str());
- }
Declaration* decl = parent->AddDeclarationArray(
curAddr, firstVtx.GetDeclarationAlignment(),
@@ -1800,7 +1796,7 @@ std::string ZDisplayList::GetSourceOutputCode(const std::string& prefix)
if (vertices.size() > 0)
{
std::vector>> verticesSorted(vertices.begin(),
- vertices.end());
+ vertices.end());
for (size_t i = 0; i < verticesSorted.size() - 1; i++)
{
@@ -1863,11 +1859,6 @@ std::string ZDisplayList::GetSourceOutputCode(const std::string& prefix)
}
}
}
-
- if (parent != nullptr)
- return "";
-
- return sourceOutput;
}
std::string ZDisplayList::ProcessLegacy(const std::string& prefix)
@@ -1899,10 +1890,10 @@ std::string ZDisplayList::ProcessLegacy(const std::string& prefix)
}
auto end = std::chrono::steady_clock::now();
- auto diff = std::chrono::duration_cast(end - start).count();
+ int64_t diff = std::chrono::duration_cast(end - start).count();
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG && diff > 5)
- printf("F3DOP: 0x%02X, TIME: %lims\n", opcode, diff);
+ printf("F3DOP: 0x%02X, TIME: %" PRIi64 "ms\n", opcode, diff);
sourceOutput += line;
@@ -1983,7 +1974,7 @@ bool ZDisplayList::TextureGenCheck(int32_t texWidth, int32_t texHeight, uint32_t
}
else
{
- // Try to find a non-external file (ie, one we are actually extracting)
+ // Try to find a non-external file (i.e., one we are actually extracting)
// which has the same segment number we are looking for.
for (auto& otherFile : Globals::Instance->cfg.segmentRefFiles[segmentNumber])
{
diff --git a/tools/ZAPD/ZAPD/ZDisplayList.h b/tools/ZAPD/ZAPD/ZDisplayList.h
index d538666750..96808315dc 100644
--- a/tools/ZAPD/ZAPD/ZDisplayList.h
+++ b/tools/ZAPD/ZAPD/ZDisplayList.h
@@ -363,8 +363,7 @@ public:
size_t GetRawDataSize() const override;
DeclarationAlignment GetDeclarationAlignment() const override;
- std::string GetSourceOutputHeader(const std::string& prefix) override;
- std::string GetSourceOutputCode(const std::string& prefix) override;
+ void DeclareReferences(const std::string& prefix) override;
std::string ProcessLegacy(const std::string& prefix);
std::string ProcessGfxDis(const std::string& prefix);
diff --git a/tools/ZAPD/ZAPD/ZFile.cpp b/tools/ZAPD/ZAPD/ZFile.cpp
index 7cbfeba886..77387fc72c 100644
--- a/tools/ZAPD/ZAPD/ZFile.cpp
+++ b/tools/ZAPD/ZAPD/ZFile.cpp
@@ -13,6 +13,7 @@
#include "Utils/MemoryStream.h"
#include "Utils/Path.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZAnimation.h"
#include "ZArray.h"
#include "ZBackground.h"
@@ -73,19 +74,13 @@ ZFile::ZFile(ZFileMode nMode, tinyxml2::XMLElement* reader, const fs::path& nBas
ZFile::~ZFile()
{
for (ZResource* res : resources)
- {
delete res;
- }
for (auto d : declarations)
- {
delete d.second;
- }
for (auto sym : symbolResources)
- {
delete sym.second;
- }
}
void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
@@ -114,8 +109,11 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
else if (std::string_view(gameStr) == "OOT")
Globals::Instance->game = ZGame::OOT_RETAIL;
else
- throw std::runtime_error(
- StringHelper::Sprintf("Error: Game type %s not supported.", gameStr));
+ {
+ std::string errorHeader =
+ StringHelper::Sprintf("'Game' type '%s' is not supported.", gameStr);
+ HANDLE_ERROR_PROCESS(WarningType::InvalidAttributeValue, errorHeader, "");
+ }
}
if (reader->Attribute("BaseAddress") != nullptr)
@@ -128,16 +126,22 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
rangeEnd = StringHelper::StrToL(reader->Attribute("RangeEnd"), 16);
if (rangeStart > rangeEnd)
- throw std::runtime_error("Error: RangeStart must be before than RangeEnd.");
+ HANDLE_ERROR_PROCESS(
+ WarningType::Always,
+ StringHelper::Sprintf("'RangeStart' 0x%06X must be before 'RangeEnd' 0x%06X",
+ rangeStart, rangeEnd),
+ "");
const char* segmentXml = reader->Attribute("Segment");
if (segmentXml != nullptr)
{
if (!StringHelper::HasOnlyDigits(segmentXml))
{
- throw std::runtime_error(StringHelper::Sprintf(
- "error: Invalid segment value '%s': must be a decimal between 0 and 15 inclusive",
- segmentXml));
+ HANDLE_ERROR_PROCESS(WarningType::Always,
+ StringHelper::Sprintf("error: Invalid segment value '%s': must be "
+ "a decimal between 0 and 15 inclusive",
+ segmentXml),
+ "");
}
segment = StringHelper::StrToL(segmentXml, 10);
@@ -146,16 +150,19 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
if (segment == 128)
{
#ifdef DEPRECATION_ON
- fprintf(stderr, "warning: segment 128 is deprecated.\n\tRemove "
- "'Segment=\"128\"' from the xml to use virtual addresses\n");
+ HANDLE_WARNING_PROCESS(
+ WarningType::Always, "warning: segment 128 is deprecated.",
+ "Remove 'Segment=\"128\"' from the xml to use virtual addresses\n");
#endif
}
else
{
- throw std::runtime_error(
+ HANDLE_ERROR_PROCESS(
+ WarningType::Always,
StringHelper::Sprintf("error: invalid segment value '%s': must be a decimal "
"number between 0 and 15 inclusive",
- segmentXml));
+ segmentXml),
+ "");
}
}
}
@@ -176,18 +183,16 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
if (mode == ZFileMode::Extract || mode == ZFileMode::ExternalFile)
{
if (!File::Exists((basePath / name).string()))
- throw std::runtime_error(
- StringHelper::Sprintf("Error! File %s does not exist.", (basePath / name).c_str()));
+ {
+ std::string errorHeader = StringHelper::Sprintf("binary file '%s' does not exist.",
+ (basePath / name).c_str());
+ HANDLE_ERROR_PROCESS(WarningType::Always, errorHeader, "");
+ }
rawData = File::ReadAllBytes((basePath / name).string());
- /*
- * TODO: In OoT repo ovl_Boss_Sst has a wrong RangeEnd (0xAD40 instead of 0xAD70),
- * so uncommenting the following produces wrong behavior.
- * If somebody fixes that in OoT repo, uncomment this. I'm too tired of fixing XMLs.
- */
- // if (reader->Attribute("RangeEnd") == nullptr)
- // rangeEnd = rawData.size();
+ if (reader->Attribute("RangeEnd") == nullptr)
+ rangeEnd = rawData.size();
}
std::unordered_set nameSet;
@@ -211,20 +216,17 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
if (offsetSet.find(offsetXml) != offsetSet.end())
{
- throw std::runtime_error(StringHelper::Sprintf(
- "ZFile::ParseXML: Error in '%s'.\n\t Repeated 'Offset' attribute: %s \n",
- name.c_str(), offsetXml));
+ std::string errorHeader =
+ StringHelper::Sprintf("repeated 'Offset' attribute: %s", offsetXml);
+ HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, "");
}
offsetSet.insert(offsetXml);
}
- else if (Globals::Instance->warnNoOffset)
+ else
{
- fprintf(stderr, "Warning No offset specified for: %s", nameXml);
- }
- else if (Globals::Instance->errorNoOffset)
- {
- throw std::runtime_error(
- StringHelper::Sprintf("Error no offset specified for %s", nameXml));
+ HANDLE_WARNING_RESOURCE(WarningType::MissingOffsets, this, nullptr, rawDataIndex,
+ StringHelper::Sprintf("no offset specified for %s.", nameXml),
+ "");
}
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
@@ -234,9 +236,9 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
{
if (outNameSet.find(outNameXml) != outNameSet.end())
{
- throw std::runtime_error(StringHelper::Sprintf(
- "ZFile::ParseXML: Error in '%s'.\n\t Repeated 'OutName' attribute: %s \n",
- name.c_str(), outNameXml));
+ std::string errorHeader =
+ StringHelper::Sprintf("repeated 'OutName' attribute: %s", outNameXml);
+ HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, "");
}
outNameSet.insert(outNameXml);
}
@@ -244,9 +246,9 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
{
if (nameSet.find(nameXml) != nameSet.end())
{
- throw std::runtime_error(StringHelper::Sprintf(
- "ZFile::ParseXML: Error in '%s'.\n\t Repeated 'Name' attribute: %s \n",
- name.c_str(), nameXml));
+ std::string errorHeader =
+ StringHelper::Sprintf("repeated 'Name' attribute: %s", nameXml);
+ HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, "");
}
nameSet.insert(nameXml);
}
@@ -279,16 +281,14 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
}
else if (std::string_view(child->Name()) == "File")
{
- throw std::runtime_error(StringHelper::Sprintf(
- "ZFile::ParseXML: Error in '%s'.\n\t Can't declare a File inside a File.\n",
- name.c_str()));
+ std::string errorHeader = "Can't declare a inside a ";
+ HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, "");
}
else
{
- throw std::runtime_error(
- StringHelper::Sprintf("ZFile::ParseXML: Error in '%s'.\n\t Unknown element found "
- "inside a File element: '%s'.\n",
- name.c_str(), nodeName.c_str()));
+ std::string errorHeader = StringHelper::Sprintf(
+ "Unknown element found inside a element: %s", nodeName.c_str());
+ HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, "");
}
}
}
@@ -307,7 +307,7 @@ void ZFile::BuildSourceFile()
return;
if (!Directory::Exists(outputPath))
- Directory::CreateDirectory(outputPath);
+ Directory::CreateDirectory(outputPath.string());
GenerateSourceFiles();
}
@@ -317,6 +317,11 @@ std::string ZFile::GetName() const
return name;
}
+std::string ZFile::GetOutName() const
+{
+ return outName.string();
+}
+
ZFileMode ZFile::GetMode() const
{
return mode;
@@ -338,10 +343,10 @@ void ZFile::ExtractResources()
return;
if (!Directory::Exists(outputPath))
- Directory::CreateDirectory(outputPath);
+ Directory::CreateDirectory(outputPath.string());
if (!Directory::Exists(GetSourceOutputFolderPath()))
- Directory::CreateDirectory(GetSourceOutputFolderPath());
+ Directory::CreateDirectory(GetSourceOutputFolderPath().string());
for (size_t i = 0; i < resources.size(); i++)
resources[i]->ParseRawDataLate();
@@ -351,8 +356,8 @@ void ZFile::ExtractResources()
if (Globals::Instance->genSourceFile)
GenerateSourceFiles();
- MemoryStream* memStream = new MemoryStream();
- BinaryWriter writer = BinaryWriter(memStream);
+ auto memStreamFile = std::shared_ptr(new MemoryStream());
+ BinaryWriter writerFile = BinaryWriter(memStreamFile);
ExporterSet* exporterSet = Globals::Instance->GetExporterSet();
@@ -361,6 +366,9 @@ void ZFile::ExtractResources()
for (ZResource* res : resources)
{
+ auto memStreamRes = std::shared_ptr(new MemoryStream());
+ BinaryWriter writerRes = BinaryWriter(memStreamRes);
+
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
printf("Saving resource %s\n", res->GetName().c_str());
@@ -369,18 +377,24 @@ void ZFile::ExtractResources()
// Check if we have an exporter "registered" for this resource type
ZResourceExporter* exporter = Globals::Instance->GetExporter(res->GetResourceType());
if (exporter != nullptr)
- exporter->Save(res, Globals::Instance->outputPath.string(), &writer);
+ {
+ //exporter->Save(res, Globals::Instance->outputPath.string(), &writerFile);
+ exporter->Save(res, Globals::Instance->outputPath.string(), &writerRes);
+ }
+
+ if (exporterSet != nullptr && exporterSet->resSaveFunc != nullptr)
+ exporterSet->resSaveFunc(res, writerRes);
}
- if (memStream->GetLength() > 0)
+ if (memStreamFile->GetLength() > 0)
{
File::WriteAllBytes(StringHelper::Sprintf("%s%s.bin",
Globals::Instance->outputPath.string().c_str(),
GetName().c_str()),
- memStream->ToVector());
+ memStreamFile->ToVector());
}
- writer.Close();
+ writerFile.Close();
if (exporterSet != nullptr && exporterSet->endFileFunc != nullptr)
exporterSet->endFileFunc(this);
@@ -1070,8 +1084,6 @@ std::string ZFile::ProcessDeclarations()
}
}
- output += "\n";
-
return output;
}
@@ -1234,11 +1246,12 @@ bool ZFile::HandleUnaccountedAddress(uint32_t currentAddress, uint32_t lastAddr,
{
Declaration* currentDecl = declarations.at(currentAddress);
- fprintf(stderr,
- "WARNING: Intersection detected from 0x%06X:0x%06X (%s), conflicts with "
- "0x%06X (%s)\n",
- lastAddr, lastAddr + lastSize, lastDecl->varName.c_str(), currentAddress,
- currentDecl->varName.c_str());
+ std::string intersectionInfo = StringHelper::Sprintf(
+ "Resource from 0x%06X:0x%06X (%s) conflicts with 0x%06X (%s).", lastAddr,
+ lastAddr + lastSize, lastDecl->varName.c_str(), currentAddress,
+ currentDecl->varName.c_str());
+ HANDLE_WARNING_RESOURCE(WarningType::Intersection, this, nullptr, currentAddress,
+ "intersection detected", intersectionInfo);
}
}
@@ -1309,25 +1322,17 @@ bool ZFile::HandleUnaccountedAddress(uint32_t currentAddress, uint32_t lastAddr,
diff, src);
decl->isUnaccounted = true;
- if (Globals::Instance->warnUnaccounted)
+ if (nonZeroUnaccounted)
{
- if (nonZeroUnaccounted)
- {
- fprintf(stderr,
- "Warning in file: %s (%s)\n"
- "\t A non-zero unaccounted block was found at offset '0x%06X'.\n"
- "\t Block size: '0x%X'.\n",
- xmlFilePath.c_str(), name.c_str(), unaccountedAddress, diff);
- }
- else if (diff >= 16)
- {
- fprintf(stderr,
- "Warning in file: %s (%s)\n"
- "\t A big (size>=0x10) zero-only unaccounted block was found "
- "at offset '0x%06X'.\n"
- "\t Block size: '0x%X'.\n",
- xmlFilePath.c_str(), name.c_str(), unaccountedAddress, diff);
- }
+ HANDLE_WARNING_RESOURCE(WarningType::Unaccounted, this, nullptr, unaccountedAddress,
+ "a non-zero unaccounted block was found",
+ StringHelper::Sprintf("Block size: '0x%X'", diff));
+ }
+ else if (diff >= 16)
+ {
+ HANDLE_WARNING_RESOURCE(WarningType::Unaccounted, this, nullptr, unaccountedAddress,
+ "a big (size>=0x10) zero-only unaccounted block was found",
+ StringHelper::Sprintf("Block size: '0x%X'", diff));
}
}
}
diff --git a/tools/ZAPD/ZAPD/ZFile.h b/tools/ZAPD/ZAPD/ZFile.h
index 9de6950e4a..7918d5f595 100644
--- a/tools/ZAPD/ZAPD/ZFile.h
+++ b/tools/ZAPD/ZAPD/ZFile.h
@@ -1,6 +1,5 @@
#pragma once
-#include
#include
#include
@@ -46,6 +45,7 @@ public:
~ZFile();
std::string GetName() const;
+ std::string GetOutName() const;
ZFileMode GetMode() const;
const fs::path& GetXmlFilePath() const;
const std::vector& GetRawData() const;
diff --git a/tools/ZAPD/ZAPD/ZLimb.cpp b/tools/ZAPD/ZAPD/ZLimb.cpp
index 77c66871fe..a47615d6c6 100644
--- a/tools/ZAPD/ZAPD/ZLimb.cpp
+++ b/tools/ZAPD/ZAPD/ZLimb.cpp
@@ -4,7 +4,7 @@
#include "Globals.h"
#include "Utils/BitConverter.h"
-#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
REGISTER_ZFILENODE(Limb, ZLimb);
@@ -37,17 +37,15 @@ void ZLimb::ParseXML(tinyxml2::XMLElement* reader)
if (limbType == "")
{
- throw std::runtime_error(StringHelper::Sprintf("ZLimb::ParseXML: Error in '%s'.\n"
- "\t Missing 'LimbType' attribute in xml.\n",
- name.c_str()));
+ HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex,
+ "missing 'LimbType' attribute in ", "");
}
type = GetTypeByAttributeName(limbType);
if (type == ZLimbType::Invalid)
{
- throw std::runtime_error(StringHelper::Sprintf("ZLimb::ParseXML: Error in '%s'.\n"
- "\t Invalid 'LimbType' found: '%s'.\n",
- name.c_str(), limbType.c_str()));
+ HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex,
+ "invalid value found for 'LimbType' attribute", "");
}
}
@@ -109,8 +107,12 @@ void ZLimb::ParseRawData()
}
break;
- default:
- throw std::runtime_error("Invalid ZLimb type");
+ case ZLimbType::Curve:
+ case ZLimbType::Legacy:
+ break;
+
+ case ZLimbType::Invalid:
+ assert(!"whoops");
break;
}
}
diff --git a/tools/ZAPD/ZAPD/ZPath.cpp b/tools/ZAPD/ZAPD/ZPath.cpp
index 4a95c5b91e..e19513db34 100644
--- a/tools/ZAPD/ZAPD/ZPath.cpp
+++ b/tools/ZAPD/ZAPD/ZPath.cpp
@@ -3,6 +3,7 @@
#include "Globals.h"
#include "Utils/BitConverter.h"
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZFile.h"
REGISTER_ZFILENODE(Path, ZPath);
@@ -20,10 +21,12 @@ void ZPath::ParseXML(tinyxml2::XMLElement* reader)
numPaths = StringHelper::StrToL(registeredAttributes.at("NumPaths").value);
if (numPaths < 1)
- throw std::runtime_error(
- StringHelper::Sprintf("ZPath::ParseXML: Fatal error in '%s'.\n"
- "\t Invalid value for attribute 'NumPaths': '%i'\n",
- name.c_str(), numPaths));
+ {
+ HANDLE_ERROR_RESOURCE(
+ WarningType::InvalidAttributeValue, parent, this, rawDataIndex,
+ StringHelper::Sprintf("invalid value '%d' found for 'NumPaths' attribute", numPaths),
+ "Should be at least '1'");
+ }
}
void ZPath::ParseRawData()
@@ -144,7 +147,7 @@ void PathwayEntry::DeclareReferences(const std::string& prefix)
if (addressFound)
return;
- std::string declaration;
+ std::string declaration = "";
size_t index = 0;
for (const auto& point : points)
diff --git a/tools/ZAPD/ZAPD/ZResource.cpp b/tools/ZAPD/ZAPD/ZResource.cpp
index cb811f4c3b..2dfe6d5eaa 100644
--- a/tools/ZAPD/ZAPD/ZResource.cpp
+++ b/tools/ZAPD/ZAPD/ZResource.cpp
@@ -4,6 +4,7 @@
#include
#include "Utils/StringHelper.h"
+#include "WarningHandler.h"
#include "ZFile.h"
ZResource::ZResource(ZFile* nParent)
@@ -85,29 +86,33 @@ void ZResource::ParseXML(tinyxml2::XMLElement* reader)
}
if (!attrDeclared)
- fprintf(stderr,
- "ZResource::ParseXML: Warning while parsing '%s'.\n"
- "\t Unexpected '%s' attribute in resource '%s'.\n",
- parent->GetName().c_str(), attrName.c_str(), reader->Name());
+ {
+ HANDLE_WARNING_RESOURCE(
+ WarningType::UnknownAttribute, parent, this, rawDataIndex,
+ StringHelper::Sprintf("unexpected '%s' attribute in resource <%s>",
+ attrName.c_str(), reader->Name()),
+ "");
+ }
attrs = attrs->Next();
}
if (!canHaveInner && !reader->NoChildren())
{
- throw std::runtime_error(
- StringHelper::Sprintf("ZResource::ParseXML: Fatal error in '%s'.\n"
- "\t Resource '%s' with inner element/child detected.\n",
- name.c_str(), reader->Name()));
+ std::string errorHeader = StringHelper::Sprintf(
+ "resource '%s' with inner element/child detected", reader->Name());
+ HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, "");
}
for (const auto& attr : registeredAttributes)
{
if (attr.second.isRequired && attr.second.value == "")
- throw std::runtime_error(StringHelper::Sprintf(
- "ZResource::ParseXML: Fatal error while parsing '%s'.\n"
- "\t Missing required attribute '%s' in resource '%s'.\n"
- "\t Aborting...",
- parent->GetName().c_str(), attr.first.c_str(), reader->Name()));
+ {
+ std::string headerMsg =
+ StringHelper::Sprintf("missing required attribute '%s' in resource <%s>",
+ attr.first.c_str(), reader->Name());
+ HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex,
+ headerMsg, "");
+ }
}
name = registeredAttributes.at("Name").value;
@@ -118,10 +123,8 @@ void ZResource::ParseXML(tinyxml2::XMLElement* reader)
{
if (!std::regex_match(name, r))
{
- throw std::domain_error(
- StringHelper::Sprintf("ZResource::ParseXML: Fatal error in '%s'.\n"
- "\t Resource with invalid 'Name' attribute.\n",
- name.c_str()));
+ HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this,
+ rawDataIndex, "invalid value found for 'Name' attribute", "");
}
}
@@ -146,7 +149,9 @@ void ZResource::ParseXML(tinyxml2::XMLElement* reader)
}
else
{
- throw std::runtime_error("Invalid value for 'Static' attribute.");
+ HANDLE_ERROR_RESOURCE(
+ WarningType::InvalidAttributeValue, parent, this, rawDataIndex,
+ StringHelper::Sprintf("invalid value '%s' for 'Static' attribute", staticConf), "");
}
declaredInXml = true;
@@ -253,18 +258,21 @@ std::string ZResource::GetDefaultName(const std::string& prefix) const
rawDataIndex);
}
-std::string ZResource::GetSourceOutputCode([[maybe_unused]] const std::string& prefix)
+void ZResource::GetSourceOutputCode([[maybe_unused]] const std::string& prefix)
{
std::string bodyStr = GetBodySourceCode();
- Declaration* decl = parent->GetDeclaration(rawDataIndex);
- if (decl == nullptr || decl->isPlaceholder)
- decl = DeclareVar(prefix, bodyStr);
- else
- decl->text = bodyStr;
- decl->staticConf = staticConf;
+ if (bodyStr != "ERROR")
+ {
+ Declaration* decl = parent->GetDeclaration(rawDataIndex);
- return "";
+ if (decl == nullptr || decl->isPlaceholder)
+ decl = DeclareVar(prefix, bodyStr);
+ else
+ decl->text = bodyStr;
+
+ decl->staticConf = staticConf;
+ }
}
std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix)
@@ -312,13 +320,13 @@ offset_t Seg2Filespace(segptr_t segmentedAddress, uint32_t parentBaseAddress)
uint32_t parentBaseOffset = GETSEGOFFSET(parentBaseAddress);
if (parentBaseOffset > currentPtr)
{
- throw std::runtime_error(
- StringHelper::Sprintf("\nSeg2Filespace: Segmented address is smaller than "
- "'BaseAddress'. Maybe your 'BaseAddress' is wrong?\n"
- "\t SegmentedAddress: 0x%08X\n"
- "\t BaseAddress: 0x%08X\n",
- segmentedAddress, parentBaseAddress));
+ HANDLE_ERROR(WarningType::Always,
+ StringHelper::Sprintf(
+ "resource address 0x%08X is smaller than 'BaseAddress' 0x%08X",
+ segmentedAddress, parentBaseAddress),
+ "Maybe your 'BaseAddress' is wrong?");
}
+
currentPtr -= parentBaseOffset;
}
diff --git a/tools/ZAPD/ZAPD/ZResource.h b/tools/ZAPD/ZAPD/ZResource.h
index ff35786fac..4dad398955 100644
--- a/tools/ZAPD/ZAPD/ZResource.h
+++ b/tools/ZAPD/ZAPD/ZResource.h
@@ -1,16 +1,15 @@
#pragma once
-#include
+#include
#include
Application
@@ -116,6 +116,8 @@
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ MultiThreadedDebug
+ Default
Console
@@ -155,6 +157,7 @@
+
diff --git a/tools/ZAPD/ZAPDUtils/ZAPDUtils.vcxproj.filters b/tools/ZAPD/ZAPDUtils/ZAPDUtils.vcxproj.filters
index 48117c845f..4765ad5d45 100644
--- a/tools/ZAPD/ZAPDUtils/ZAPDUtils.vcxproj.filters
+++ b/tools/ZAPD/ZAPDUtils/ZAPDUtils.vcxproj.filters
@@ -19,6 +19,9 @@
{e047919d-7186-49ca-b115-e48fbb5c8743}
+
+ {3de9dd46-0dfd-4d48-9f20-9f24e5b80fe0}
+
@@ -74,5 +77,8 @@
Source Files\Utils
+
+ Source Files\Libraries
+
\ No newline at end of file
diff --git a/tools/ZAPD/docs/zapd_warning_example.png b/tools/ZAPD/docs/zapd_warning_example.png
new file mode 100644
index 0000000000..a001c64d6a
Binary files /dev/null and b/tools/ZAPD/docs/zapd_warning_example.png differ
diff --git a/tools/asm-differ/.gitrepo b/tools/asm-differ/.gitrepo
index d3e4418c9e..241c0ad6b6 100644
--- a/tools/asm-differ/.gitrepo
+++ b/tools/asm-differ/.gitrepo
@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/simonlindholm/asm-differ.git
branch = main
- commit = f30d43aceba6291aa3c08b7317b0458b6d734321
- parent = e977dfeb374c6de4076ca9a0f44ba5a6a701d849
+ commit = 70c33cc125666495f84f8c7bee60d3158b4b1164
+ parent = 62341d8db0c29d1d4f0c360196e428309ac373b6
method = merge
cmdver = 0.4.3
diff --git a/tools/asm-differ/diff.py b/tools/asm-differ/diff.py
index d7ac3f3440..2ec117a126 100755
--- a/tools/asm-differ/diff.py
+++ b/tools/asm-differ/diff.py
@@ -640,6 +640,7 @@ class Text:
class TableMetadata:
headers: Tuple[Text, ...]
current_score: int
+ max_score: int
previous_score: Optional[int]
@@ -832,6 +833,7 @@ class JsonFormatter(Formatter):
for h, name in zip(meta.headers, ("base", "current", "previous"))
}
output["current_score"] = meta.current_score
+ output["max_score"] = meta.max_score
if meta.previous_score is not None:
output["previous_score"] = meta.previous_score
output_rows: List[Dict[str, Any]] = []
@@ -1184,8 +1186,9 @@ def parse_elf_data_references(data: bytes) -> List[Tuple[int, int, str]]:
assert len(symtab_sections) == 1
symtab = sections[symtab_sections[0]]
- text_sections = [i for i in range(e_shnum) if sec_names[i] == b".text"]
- assert len(text_sections) == 1
+ text_sections = [i for i in range(e_shnum) if sec_names[i] == b".text" and sections[i].sh_size != 0]
+ if len(text_sections) != 1:
+ return []
text_section = text_sections[0]
ret: List[Tuple[int, int, str]] = []
@@ -1327,11 +1330,23 @@ def dump_binary(
(objdump_flags + flags2, project.myimg, None),
)
+DATA_POOL_PLACEHOLDER = "DATA_POOL_PLACEHOLDER-OFFSET_{}"
+DATA_POOL_PLACEHOLDER_PATTERN = re.compile(
+ r"DATA_POOL_PLACEHOLDER-OFFSET_([a-zA-z0-9]+)"
+)
-class DifferenceNormalizer:
+# Example: "ldr r4, [pc, #56] ; (4c )"
+ARM32_LOAD_POOL_PATTERN = r"(ldr\s+r([0-9]|1[0-3]),\s+\[pc,.*;\s*)(\([a-fA-F0-9]+.*\))"
+
+
+# The base class is a no-op.
+class AsmProcessor:
def __init__(self, config: Config) -> None:
self.config = config
+ def process_reloc(self, row: str, prev: str) -> str:
+ return prev
+
def normalize(self, mnemonic: str, row: str) -> str:
"""This should be called exactly once for each line."""
arch = self.config.arch
@@ -1342,9 +1357,143 @@ class DifferenceNormalizer:
def _normalize_arch_specific(self, mnemonic: str, row: str) -> str:
return row
+
+ def post_process(self, lines: List["Line"]) -> None:
+ return
-class DifferenceNormalizerAArch64(DifferenceNormalizer):
+class AsmProcessorMIPS(AsmProcessor):
+ def process_reloc(self, row: str, prev: str) -> str:
+ arch = self.config.arch
+ if "R_MIPS_NONE" in row:
+ # GNU as emits no-op relocations immediately after real ones when
+ # assembling with -mabi=64. Return without trying to parse 'imm' as an
+ # integer.
+ return prev
+ before, imm, after = parse_relocated_line(prev)
+ repl = row.split()[-1]
+ if imm != "0":
+ # MIPS uses relocations with addends embedded in the code as immediates.
+ # If there is an immediate, show it as part of the relocation. Ideally
+ # we'd show this addend in both %lo/%hi, but annoyingly objdump's output
+ # doesn't include enough information to pair up %lo's and %hi's...
+ # TODO: handle unambiguous cases where all addends for a symbol are the
+ # same, or show "+???".
+ mnemonic = prev.split()[0]
+ if (
+ mnemonic in arch.instructions_with_address_immediates
+ and not imm.startswith("0x")
+ ):
+ imm = "0x" + imm
+ repl += "+" + imm if int(imm, 0) > 0 else imm
+ if "R_MIPS_LO16" in row:
+ repl = f"%lo({repl})"
+ elif "R_MIPS_HI16" in row:
+ # Ideally we'd pair up R_MIPS_LO16 and R_MIPS_HI16 to generate a
+ # correct addend for each, but objdump doesn't give us the order of
+ # the relocations, so we can't find the right LO16. :(
+ repl = f"%hi({repl})"
+ elif "R_MIPS_26" in row:
+ # Function calls
+ pass
+ elif "R_MIPS_PC16" in row:
+ # Branch to glabel. This gives confusing output, but there's not much
+ # we can do here.
+ pass
+ else:
+ assert False, f"unknown relocation type '{row}' for line '{prev}'"
+ return before + repl + after
+
+
+class AsmProcessorPPC(AsmProcessor):
+ def process_reloc(self, row: str, prev: str) -> str:
+ arch = self.config.arch
+ assert any(
+ r in row for r in ["R_PPC_REL24", "R_PPC_ADDR16", "R_PPC_EMB_SDA21"]
+ ), f"unknown relocation type '{row}' for line '{prev}'"
+ before, imm, after = parse_relocated_line(prev)
+ repl = row.split()[-1]
+ if "R_PPC_REL24" in row:
+ # function calls
+ pass
+ elif "R_PPC_ADDR16_HI" in row:
+ # absolute hi of addr
+ repl = f"{repl}@h"
+ elif "R_PPC_ADDR16_HA" in row:
+ # adjusted hi of addr
+ repl = f"{repl}@ha"
+ elif "R_PPC_ADDR16_LO" in row:
+ # lo of addr
+ repl = f"{repl}@l"
+ elif "R_PPC_ADDR16" in row:
+ # 16-bit absolute addr
+ if "+0x7" in repl:
+ # remove the very large addends as they are an artifact of (label-_SDA(2)_BASE_)
+ # computations and are unimportant in a diff setting.
+ if int(repl.split("+")[1], 16) > 0x70000000:
+ repl = repl.split("+")[0]
+ elif "R_PPC_EMB_SDA21" in row:
+ # small data area
+ pass
+ return before + repl + after
+
+
+class AsmProcessorARM32(AsmProcessor):
+ def process_reloc(self, row: str, prev: str) -> str:
+ arch = self.config.arch
+ before, imm, after = parse_relocated_line(prev)
+ repl = row.split()[-1]
+ return before + repl + after
+
+ def _normalize_arch_specific(self, mnemonic: str, row: str) -> str:
+ if self.config.ignore_addr_diffs:
+ row = self._normalize_bl(mnemonic, row)
+ row = self._normalize_data_pool(row)
+ return row
+
+ def _normalize_bl(self, mnemonic: str, row: str) -> str:
+ if mnemonic != "bl":
+ return row
+
+ row, _ = split_off_address(row)
+ return row + ""
+
+ def _normalize_data_pool(self, row: str) -> str:
+ pool_match = re.search(ARM32_LOAD_POOL_PATTERN, row)
+ if pool_match:
+ offset = pool_match.group(3).split(" ")[0][1:]
+ repl = DATA_POOL_PLACEHOLDER.format(offset)
+ return pool_match.group(1) + repl
+ return row
+
+ def post_process(self, lines: List["Line"]) -> None:
+ lines_by_line_number = {}
+ for line in lines:
+ lines_by_line_number[line.line_num] = line
+ for line in lines:
+ reloc_match = re.search(
+ DATA_POOL_PLACEHOLDER_PATTERN, line.normalized_original
+ )
+ if reloc_match is None:
+ continue
+
+ # Get value at relocation
+ reloc = reloc_match.group(0)
+ line_number = re.search(
+ DATA_POOL_PLACEHOLDER_PATTERN, reloc).group(1)
+ line_original = lines_by_line_number[int(line_number, 16)].original
+ value = line_original.split()[1]
+
+ # Replace relocation placeholder with value
+ replaced = re.sub(
+ DATA_POOL_PLACEHOLDER_PATTERN,
+ f"={value} ({line_number})",
+ line.normalized_original,
+ )
+ line.original = replaced
+
+
+class AsmProcessorAArch64(AsmProcessor):
def __init__(self, config: Config) -> None:
super().__init__(config)
self._adrp_pair_registers: Set[str] = set()
@@ -1394,23 +1543,6 @@ class DifferenceNormalizerAArch64(DifferenceNormalizer):
return row
-class DifferenceNormalizerARM32(DifferenceNormalizer):
- def __init__(self, config: Config) -> None:
- super().__init__(config)
-
- def _normalize_arch_specific(self, mnemonic: str, row: str) -> str:
- if self.config.ignore_addr_diffs:
- row = self._normalize_bl(mnemonic, row)
- return row
-
- def _normalize_bl(self, mnemonic: str, row: str) -> str:
- if mnemonic != "bl":
- return row
-
- row, _ = split_off_address(row)
- return row + ""
-
-
@dataclass
class ArchSettings:
name: str
@@ -1420,14 +1552,15 @@ class ArchSettings:
re_sprel: Pattern[str]
re_large_imm: Pattern[str]
re_imm: Pattern[str]
+ re_reloc: Pattern[str]
branch_instructions: Set[str]
instructions_with_address_immediates: Set[str]
forbidden: Set[str] = field(default_factory=lambda: set(string.ascii_letters + "_"))
arch_flags: List[str] = field(default_factory=list)
branch_likely_instructions: Set[str] = field(default_factory=set)
- difference_normalizer: Type[DifferenceNormalizer] = DifferenceNormalizer
+ proc: Type[AsmProcessor] = AsmProcessor
big_endian: Optional[bool] = True
-
+ delay_slot_instructions: Set[str] = field(default_factory=set)
MIPS_BRANCH_LIKELY_INSTRUCTIONS = {
"beql",
@@ -1543,10 +1676,13 @@ MIPS_SETTINGS = ArchSettings(
re_sprel=re.compile(r"(?<=,)([0-9]+|0x[0-9a-f]+)\(sp\)"),
re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"),
re_imm=re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)"),
+ re_reloc=re.compile(r"R_MIPS_"),
arch_flags=["-m", "mips:4300"],
branch_likely_instructions=MIPS_BRANCH_LIKELY_INSTRUCTIONS,
branch_instructions=MIPS_BRANCH_INSTRUCTIONS,
instructions_with_address_immediates=MIPS_BRANCH_INSTRUCTIONS.union({"jal", "j"}),
+ delay_slot_instructions=MIPS_BRANCH_INSTRUCTIONS.union({"j", "jal", "jr", "jalr"}),
+ proc=AsmProcessorMIPS,
)
MIPSEL_SETTINGS = replace(MIPS_SETTINGS, name="mipsel", big_endian=False)
@@ -1566,9 +1702,10 @@ ARM32_SETTINGS = ArchSettings(
re_sprel=re.compile(r"sp, #-?(0x[0-9a-fA-F]+|[0-9]+)\b"),
re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"),
re_imm=re.compile(r"(? Tuple[str, str, str]:
return before, imm, after
-def process_mips_reloc(row: str, prev: str, arch: ArchSettings) -> str:
- if "R_MIPS_NONE" in row:
- # GNU as emits no-op relocations immediately after real ones when
- # assembling with -mabi=64. Return without trying to parse 'imm' as an
- # integer.
- return prev
- before, imm, after = parse_relocated_line(prev)
- repl = row.split()[-1]
- if imm != "0":
- # MIPS uses relocations with addends embedded in the code as immediates.
- # If there is an immediate, show it as part of the relocation. Ideally
- # we'd show this addend in both %lo/%hi, but annoyingly objdump's output
- # doesn't include enough information to pair up %lo's and %hi's...
- # TODO: handle unambiguous cases where all addends for a symbol are the
- # same, or show "+???".
- mnemonic = prev.split()[0]
- if (
- mnemonic in arch.instructions_with_address_immediates
- and not imm.startswith("0x")
- ):
- imm = "0x" + imm
- repl += "+" + imm if int(imm, 0) > 0 else imm
- if "R_MIPS_LO16" in row:
- repl = f"%lo({repl})"
- elif "R_MIPS_HI16" in row:
- # Ideally we'd pair up R_MIPS_LO16 and R_MIPS_HI16 to generate a
- # correct addend for each, but objdump doesn't give us the order of
- # the relocations, so we can't find the right LO16. :(
- repl = f"%hi({repl})"
- elif "R_MIPS_26" in row:
- # Function calls
- pass
- elif "R_MIPS_PC16" in row:
- # Branch to glabel. This gives confusing output, but there's not much
- # we can do here.
- pass
- else:
- assert False, f"unknown relocation type '{row}' for line '{prev}'"
- return before + repl + after
-
-
-def process_ppc_reloc(row: str, prev: str) -> str:
- assert any(
- r in row for r in ["R_PPC_REL24", "R_PPC_ADDR16", "R_PPC_EMB_SDA21"]
- ), f"unknown relocation type '{row}' for line '{prev}'"
- before, imm, after = parse_relocated_line(prev)
- repl = row.split()[-1]
- if "R_PPC_REL24" in row:
- # function calls
- pass
- elif "R_PPC_ADDR16_HI" in row:
- # absolute hi of addr
- repl = f"{repl}@h"
- elif "R_PPC_ADDR16_HA" in row:
- # adjusted hi of addr
- repl = f"{repl}@ha"
- elif "R_PPC_ADDR16_LO" in row:
- # lo of addr
- repl = f"{repl}@l"
- elif "R_PPC_ADDR16" in row:
- # 16-bit absolute addr
- if "+0x7" in repl:
- # remove the very large addends as they are an artifact of (label-_SDA(2)_BASE_)
- # computations and are unimportant in a diff setting.
- if int(repl.split("+")[1], 16) > 0x70000000:
- repl = repl.split("+")[0]
- elif "R_PPC_EMB_SDA21" in row:
- # small data area
- pass
- return before + repl + after
-
-
-def process_arm_reloc(row: str, prev: str, arch: ArchSettings) -> str:
- before, imm, after = parse_relocated_line(prev)
- repl = row.split()[-1]
- return before + repl + after
-
-
def pad_mnemonic(line: str) -> str:
if "\t" not in line:
return line
@@ -1741,7 +1803,7 @@ class Line:
def process(dump: str, config: Config) -> List[Line]:
arch = config.arch
- normalizer = arch.difference_normalizer(config)
+ processor = arch.proc(config)
skip_next = False
source_lines = []
source_filename = None
@@ -1783,7 +1845,7 @@ def process(dump: str, config: Config) -> List[Line]:
)
break
- if not re.match(r"^ +[0-9a-f]+:\t", row):
+ if not re.match(r"^\s+[0-9a-f]+:\s+", row):
# This regex is conservative, and assumes the file path does not contain "weird"
# characters like colons, tabs, or angle brackets.
if re.match(
@@ -1797,10 +1859,11 @@ def process(dump: str, config: Config) -> List[Line]:
m_comment = re.search(arch.re_comment, row)
comment = m_comment[0] if m_comment else None
row = re.sub(arch.re_comment, "", row)
+ line_num_str = row.split(":")[0]
row = row.rstrip()
tabs = row.split("\t")
row = "\t".join(tabs[2:])
- line_num = eval_line_num(tabs[0].strip())
+ line_num = eval_line_num(line_num_str.strip())
if line_num in data_refs:
refs = data_refs[line_num]
@@ -1835,20 +1898,13 @@ def process(dump: str, config: Config) -> List[Line]:
while i < len(lines):
reloc_row = lines[i]
- if "R_AARCH64_" in reloc_row:
- # TODO: handle relocation
- pass
- elif "R_MIPS_" in reloc_row:
- original = process_mips_reloc(reloc_row, original, arch)
- elif "R_PPC_" in reloc_row:
- original = process_ppc_reloc(reloc_row, original)
- elif "R_ARM_" in reloc_row:
- original = process_arm_reloc(reloc_row, original, arch)
+ if re.search(arch.re_reloc, reloc_row):
+ original = processor.process_reloc(reloc_row, original)
else:
break
i += 1
- normalized_original = normalizer.normalize(mnemonic, original)
+ normalized_original = processor.normalize(mnemonic, original)
scorable_line = normalized_original
if not config.score_stack_differences:
@@ -1904,6 +1960,7 @@ def process(dump: str, config: Config) -> List[Line]:
elif stop_after_delay_slot:
break
+ processor.post_process(output)
return output
@@ -2123,8 +2180,15 @@ class OutputLine:
class Diff:
lines: List[OutputLine]
score: int
+ max_score: int
+def trim_nops(lines: List[Line], arch: ArchSettings) -> List[Line]:
+ lines = lines[:]
+ while lines and lines[-1].mnemonic == "nop" and (len(lines) == 1 or lines[-2].mnemonic not in arch.delay_slot_instructions):
+ lines.pop()
+ return lines
+
def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
if config.show_source:
import cxxfilt
@@ -2152,8 +2216,12 @@ def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
btset.add(bt)
sc(str(bt))
+ lines1 = trim_nops(lines1, arch)
+ lines2 = trim_nops(lines2, arch)
+
diffed_lines = diff_lines(lines1, lines2, config.algorithm)
score = score_diff_lines(diffed_lines, config)
+ max_score = len(lines1) * config.penalty_deletion
line_num_base = -1
line_num_offset = 0
@@ -2385,7 +2453,7 @@ def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
)
output = output[config.skip_lines :]
- return Diff(lines=output, score=score)
+ return Diff(lines=output, score=score, max_score=max_score)
def chunk_diff_lines(
@@ -2461,6 +2529,7 @@ def align_diffs(
Text(f"{padding}PREVIOUS ({old_diff.score})"),
),
current_score=new_diff.score,
+ max_score=new_diff.max_score,
previous_score=old_diff.score,
)
old_chunks = chunk_diff_lines(old_diff.lines)
@@ -2504,6 +2573,7 @@ def align_diffs(
Text(f"{padding}CURRENT ({new_diff.score})"),
),
current_score=new_diff.score,
+ max_score=new_diff.max_score,
previous_score=None,
)
diff_lines = [(line, line) for line in new_diff.lines]
diff --git a/tools/z64compress/.gitrepo b/tools/z64compress/.gitrepo
index 9507f81ce9..905b330d9c 100644
--- a/tools/z64compress/.gitrepo
+++ b/tools/z64compress/.gitrepo
@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/z64me/z64compress.git
branch = main
- commit = 98ef0ac25f7adb47235c839838e29496d3546917
- parent = 69cc19eea8c30e50e280f400e8cf06fe66efca60
+ commit = ac5b1a0d0b23346362a68a107da3478227f8e703
+ parent = 33e3aa92181a0543826adc10a5f3304d60b391dd
method = merge
cmdver = 0.4.3
diff --git a/tools/z64compress/src/main.c b/tools/z64compress/src/main.c
index 71d1d20400..bd044059d1 100644
--- a/tools/z64compress/src/main.c
+++ b/tools/z64compress/src/main.c
@@ -221,7 +221,7 @@ wow_main
}
}
- fprintf(printer, "welcome to z64compress 1.0.1 \n");
+ fprintf(printer, "welcome to z64compress 1.0.2 \n");
if (argc <= 1)
{
@@ -356,7 +356,6 @@ wow_main
ARG_ZERO_TEST(Ain , "--in" );
ARG_ZERO_TEST(Aout , "--out" );
- ARG_ZERO_TEST(Amb , "--mb" );
ARG_ZERO_TEST(Acodec, "--codec");
#undef ARG_ZERO_TEST
diff --git a/tools/z64compress/src/rom.c b/tools/z64compress/src/rom.c
index decf8ca1d0..008597b524 100644
--- a/tools/z64compress/src/rom.c
+++ b/tools/z64compress/src/rom.c
@@ -68,7 +68,9 @@ for (dma = rom->dma; (unsigned)(dma - rom->dma) < rom->dma_num; ++dma)
#define PROGRESS_A_B (int)(dma - rom->dma), rom->dma_num
-#define ALIGN16(x) (((x) + 0xF) & ~0xF)
+#define ALIGN(x, n) (((x) + ((n)-1)) & ~((n)-1))
+#define ALIGN16(x) ALIGN(x, 16)
+#define ALIGN8MB(x) ALIGN(x, 8 * 0x100000)
/*
*
@@ -936,7 +938,7 @@ void rom_compress(struct rom *rom, int mb, int numThreads, bool matching)
cache = rom->cache;
- if (compsz > rom->data_sz || mb <= 0)
+ if (compsz > rom->data_sz || mb < 0)
die("invalid mb argument %d", mb);
/* get encoding functions */
@@ -1057,31 +1059,13 @@ void rom_compress(struct rom *rom, int mb, int numThreads, bool matching)
/* sort by original start, ascending */
DMASORT(rom, sortfunc_dma_start_ascend);
- if (matching)
- {
- /* fill the entire (compressed) rom space with 00010203...FF...
- in order to match retail rom padding */
- unsigned char n = 0; // will intentionally overflow
- for (unsigned int j = 0; j < compsz; j++, n++)
- {
- rom->data[j] = n;
- }
- }
- else
- {
- /* zero the entire (compressed) rom space */
- memset(rom->data, 0, compsz);
- }
-
- /* go through dma table, injecting compressed files */
+ /* determine physical addresses for each segment */
comp_total = 0;
DMA_FOR_EACH
{
- unsigned char *dst;
char *fn = dma->compname;
unsigned int sz;
unsigned int sz16;
- fprintf(printer, "\r""injecting file %d/%d: ", PROGRESS_A_B);
if (dma->deleted)
continue;
@@ -1114,9 +1098,6 @@ void rom_compress(struct rom *rom, int mb, int numThreads, bool matching)
if (sz16 & 15)
sz16 += 16 - (sz16 & 15);
- /* put the files in */
- dst = rom->data + comp_total;
-
dma->Pstart = comp_total;
if (dma->compress)
{
@@ -1130,26 +1111,66 @@ void rom_compress(struct rom *rom, int mb, int numThreads, bool matching)
dma->Pend = 0;
comp_total += sz16;
- if (dma->Pend > compsz)
+ if (mb != 0 && dma->Pend > compsz)
die("ran out of compressed rom space");
+ }
+
+ /* adaptive final size */
+ if (mb == 0)
+ compsz = ALIGN8MB(comp_total);
+
+ if (matching)
+ {
+ /* fill the entire (compressed) rom space with 00010203...FF...
+ in order to match retail rom padding */
+ unsigned char n = 0; /* will intentionally overflow */
+ for (unsigned int j = 0; j < compsz; j++, n++)
+ {
+ rom->data[j] = n;
+ }
+ }
+ else
+ {
+ /* zero the entire (compressed) rom space */
+ memset(rom->data, 0, compsz);
+ }
+
+ /* inject compressed files */
+ comp_total = 0;
+ DMA_FOR_EACH
+ {
+ unsigned char *dst;
+ char *fn = dma->compname;
+ unsigned int sz;
+ fprintf(printer, "\r""injecting file %d/%d: ", PROGRESS_A_B);
+
+ if (dma->deleted)
+ continue;
+
+ dst = rom->data + dma->Pstart;
/* external cached file logic */
if (cache)
{
+ /* skip entries that don't reference compressed files */
+ if (!fn)
+ continue;
+
/* load file into rom at offset */
dst = file_load_into(cache_codec, fn, &sz, dst);
}
-
/* otherwise, a simple memcpy */
else
{
memcpy(dst, dma->compbuf, dma->compSz);
- if (matching)
- {
- // since matching rom padding is not zero but file padding is zero,
- // fill file padding space with zeros
- memset(dst + dma->compSz, 0, ALIGN16(dma->compSz) - dma->compSz);
- }
+ sz = dma->compSz;
+ }
+
+ if (matching)
+ {
+ /* since matching rom padding is not zero but file padding is zero,
+ fill file padding space with zeros */
+ memset(dst + sz, 0, ALIGN16(sz) - sz);
}
}
fprintf(printer, "\r""injecting file %d/%d: ", dma_num, dma_num);