1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-11-13 04:39:36 +00:00

Building on Macs (#1086)

* git subrepo pull (merge) tools/ZAPD

subrepo:
  subdir:   "tools/ZAPD"
  merged:   "945e6ca1a"
upstream:
  origin:   "https://github.com/zeldaret/ZAPD.git"
  branch:   "master"
  commit:   "50242eca9"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Fix extract_assets.py multithreading

* Update binutils doc a bit

* Remove * import, add multiprocessing option
and way to pass arguments to ZAPD

* Update format.sh to be more platform-independent

* git subrepo pull --force tools/ZAPD

subrepo:
  subdir:   "tools/ZAPD"
  merged:   "fd5a7f434"
upstream:
  origin:   "https://github.com/zeldaret/ZAPD.git"
  branch:   "master"
  commit:   "fd5a7f434"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Remove ;

* Update formatting script to not just use 11

* Add Python requirements,
move the Mac stuff in the README into its own doc

* Fix readme link

* Minor format thing

* .

* Move ZAPDArgs into its own function

* Update readme and remove requirements.txt

* Dragorn-inspired rewrite of processZAPDArgs

* git subrepo pull --force tools/ZAPD

subrepo:
  subdir:   "tools/ZAPD"
  merged:   "a0d3f7b68"
upstream:
  origin:   "https://github.com/zeldaret/ZAPD.git"
  branch:   "master"
  commit:   "a0d3f7b68"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Fix function definition

* Building docs overhaul

* Add Python packages to Mac and Cygwin

* Heading number

* format format.sh (!)

* Replace "currently"

* Remove Debian

* git subrepo pull (merge) --force tools/ZAPD

subrepo:
  subdir:   "tools/ZAPD"
  merged:   "0ba781304"
upstream:
  origin:   "https://github.com/zeldaret/ZAPD.git"
  branch:   "master"
  commit:   "0ba781304"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
EllipticEllipsis 2022-01-17 00:43:07 +00:00 committed by GitHub
parent 9450272503
commit 9b67778a00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 640 additions and 325 deletions

View file

@ -34,6 +34,8 @@ else
endif
endif
N_THREADS ?= $(shell nproc)
#### Tools ####
ifeq ($(shell type mips-linux-gnu-ld >/dev/null 2>/dev/null; echo $$?), 0)
MIPS_BINUTILS_PREFIX := mips-linux-gnu-
@ -203,7 +205,7 @@ setup:
$(MAKE) -C tools
python3 fixbaserom.py
python3 extract_baserom.py
python3 extract_assets.py
python3 extract_assets.py -j$(N_THREADS)
resources: $(ASSET_FILES_OUT)
test: $(ROM)

100
README.md
View file

@ -39,37 +39,24 @@ It builds the following ROM:
## Installation
We recommend using WSL on Windows, or native Linux, which the rest of this readme describes. We currently have instructions for
* [Windows](#Windows), with and without WSL
* [macOS](docs/BUILDING_MACOS.md)
* [Linux](#Linux-Native-or-under-WSL--VM), natively or using WSL / VM
* [Docker](docs/BUILDING_DOCKER.md)
(These will also depend on the Linux instructions.)
Some of these may also be out of date or unmaintained; usually our contributors use WSL, Linux, and macOS, so these instructions should be up to date.
### Windows
For Windows 10 or 11, install WSL and a distribution by following this
[WSL Installation Guide](https://docs.microsoft.com/en-us/windows/wsl/install).
We recommend using Debian or Ubuntu 20.04 Linux distributions.
We recommend using Ubuntu 20.04 as the Linux distribution.
For older versions of Windows, install a Linux VM or refer to either [Cygwin](#Cygwin) or [Docker](#Docker) instructions.
For older versions of Windows, install a Linux VM or refer to either [Cygwin](docs/BUILDING_CYGWIN.md) or [Docker](docs/BUILDING_DOCKER.md) instructions.
### macOS
For macOS, use homebrew to install the following dependencies:
* coreutils
* make
* python3
* md5sha1sum
* libpng
You can install them with the following commands:
```bash
brew update
brew install coreutils make python3 md5sha1sum libpng
```
You'll also need to [build and install mips-linux-binutils](docs/BUILDING_BINUTILS_MACOS.md).
Going forward in this guide, please use `gmake` whenever you encounter a `make` command.
The `make` that comes with macOS behaves differently than GNU make and is incompatible with this project.
You should now be able to continue from [step 2](#2-clone-the-repository) of the Linux instructions.
### Linux (Native or under WSL / VM)
@ -90,6 +77,12 @@ sudo apt-get update
sudo apt-get install git build-essential binutils-mips-linux-gnu python3 libpng-dev
```
To install the Python dependencies simply run in a terminal:
```bash
python3 -m pip install colorama
```
#### 2. Clone the repository
Clone `https://github.com/zeldaret/oot.git` where you wish to have the project, with a command such as:
@ -98,6 +91,12 @@ Clone `https://github.com/zeldaret/oot.git` where you wish to have the project,
git clone https://github.com/zeldaret/oot.git
```
This will copy the GitHub repository contents into a new folder in the current directory called `oot`. Change into this directory before doing anything else:
```bash
cd oot
```
#### 3. Prepare a base ROM
Copy over your copy of the Master Quest (Debug) ROM inside the root of this new project directory.
@ -145,57 +144,6 @@ This means that the built ROM isn't the same as the base one, so something went
Both of these have the disadvantage that the ordering of the terminal output is scrambled, so for debugging it is best to stick to one thread (i.e. not pass `-j` or `-jN`).
### Cygwin
If you want to use Cygwin, you will need to:
* Download and install [Git Bash](https://git-scm.com/download/win).
* Download and install [Cygwin](https://cygwin.com).
* [Build and install mips-linux-binutils](docs/BUILDING_BINUTILS_CYGWIN.md).
Once mips-linux-binutils is installed you will need to install the following packages using Cygwin's installer:
* libiconv
* dos2unix
* python3
* libpng-devel
Then you can continue from step [step 2](#2-clone-the-repository) of the Linux instructions.
Note that, before building anything, you will need to run the following commands to fix line endings:
```bash
dos2unix fixle.sh
./fixle.sh
```
### Docker
#### 1. Setup requirements
To use Docker, you'll need either Docker Desktop or Docker Toolbox installed and setup based on your system.
You'll also need to prepare a local version of the project with a copied base ROM (see steps [2](#2-clone-the-repository) and [3](#3-prepare-a-base-rom) of the Linux instructions).
#### 2. Create the Docker image
From inside your local project, run the following command:
```bash
docker build . -t oot
```
#### 3. Start the container
To start the container, you can mount your local filesystem into the Docker container and run an interactive bash session.
```bash
docker run -it --rm --mount type=bind,source="$(pwd)",destination=/oot oot /bin/bash
```
#### 4. Setup and Build the ROM
Once inside the container, you can follow steps [4](#4-setup-the-rom-and-build-process) and [5](#5-build-the-rom) of the Linux instructions to setup and build the ROM, or run any other command you need.
## Contributing

View file

@ -1,47 +0,0 @@
# Building mips-linux-binutils on MacOS
The following instructions are written for MacOS users but should apply to any unix-like system, with maybe some modifications at the end regarding the bash_profile.
Create destination dir for binutils
```bash
sudo mkdir -p /opt/cross
```
Create and enter local working dir
```bash
mkdir ~/binutils-tmp
cd ~/binutils-tmp
```
Get and extract binutils source
```bash
wget https://ftp.gnu.org/gnu/binutils/binutils-2.35.tar.bz2
tar xjf binutils-2.35.tar.bz2
```
Create and enter build dir
```bash
mkdir build-binutils
cd build-binutils
```
Configure the build
```bash
../binutils-2.35/configure --target=mips-linux-gnu --prefix=/opt/cross --disable-gprof --disable-gdb --disable-werror
```
Make and install binutils
```bash
make -j
sudo make install
```
Edit your ~/.bash_profile to add the new binutils binaries to the system PATH
```bash
echo "export PATH=$PATH:/opt/cross/bin" >> ~/.bash_profile
```
Reload ~/.bash_profile (or just launch a new terminal tab)
```bash
source ~/.bash_profile
```

View file

@ -1,4 +1,17 @@
# Building mips-linux-binutils on Windows using Cygwin
# Building with Cygwin
**N.B.** These have not been tested recently, you may find the requirements have changed a bit.
If you want to use Cygwin, you will need to:
## 1. Install Git Bash and Cygwin
* Download and install [Git Bash](https://git-scm.com/download/win).
* Download and install [Cygwin](https://cygwin.com).
## 2. Build mips-linux-binutils on Windows using Cygwin
First, you will need to install the following packages using the Cygwin installer:
- make
@ -40,3 +53,34 @@ Add the new binutils binaries to your system PATH:
You can do that by adding `PATH=$PATH:/opt/cross/bin` to `~/.bashrc` and then reloading `~/.bashrc`.
Alternatively you can edit the `Path` variable in `Edit the system environment variables`>`Environment Variables` (in which case you will need to relaunch your terminal).
## 3. Install required Cygwin packages
Once mips-linux-binutils is installed you will need to install the following packages using Cygwin's installer:
* libiconv
* dos2unix
* python3
* libpng-devel
## 4. Install required Python packages
To install the Python dependencies simply run in a terminal:
```bash
python3 -m pip install colorama
```
## 5. Continue with Linux instructions
You should be able to continue from step [step 2](../README.md#2-clone-the-repository) of the Linux instructions.
**N.B.** Before building anything, you will need to run the following commands to fix line endings:
```bash
dos2unix fixle.sh
./fixle.sh
```

27
docs/BUILDING_DOCKER.md Normal file
View file

@ -0,0 +1,27 @@
# Building using Docker
## 1. Setup requirements
To use Docker, you'll need either Docker Desktop or Docker Toolbox installed and setup based on your system.
You'll also need to prepare a local version of the project with a copied base ROM (see steps [2](../README.md#2-clone-the-repository) and [3](../README.md#3-prepare-a-base-rom) of the Linux instructions).
## 2. Create the Docker image
From inside your local project, run the following command:
```bash
docker build . -t oot
```
## 3. Start the container
To start the container, you can mount your local filesystem into the Docker container and run an interactive bash session.
```bash
docker run -it --rm --mount type=bind,source="$(pwd)",destination=/oot oot /bin/bash
```
## 4. Setup and Build the ROM
Once inside the container, you can follow steps [4](../README.md#4-setup-the-rom-and-build-process) and [5](../README.md#5-build-the-rom) of the Linux instructions to setup and build the ROM, or run any other command you need.

89
docs/BUILDING_MACOS.md Normal file
View file

@ -0,0 +1,89 @@
# Building on macOS
**N.B.** C++17 is required to build the asset processing program that we use (ZAPD), so check your OS version can support this before proceeding.
## 1. Dependencies
For macOS, use Homebrew to install the following dependencies:
* coreutils
* make
* python3
* libpng
* bash
* clang-format
You can install them with the following commands:
```bash
brew update
brew install coreutils make python3 libpng bash clang-format
```
(The repository expects Homebrew-installed programs to be either linked correctly in `$PATH` etc. or in their default locations.)
To install the Python dependencies simply run in a terminal:
```bash
python3 -m pip install colorama
```
## 2. Building mips-linux-binutils
The following instructions are written for MacOS users but should apply to any Unix-like system, with maybe some modifications at the end regarding the bash_profile.
Create destination dir for binutils
```bash
sudo mkdir -p /opt/cross
```
Create and enter local working dir
```bash
mkdir ~/binutils-tmp
cd ~/binutils-tmp
```
Get and extract binutils source
```bash
wget https://ftp.gnu.org/gnu/binutils/binutils-2.35.tar.bz2
tar xjf binutils-2.35.tar.bz2
```
(You may find this command does not work: if so, just access the URL in a browser and save it to `~/binutils-tmp`.)
Create and enter a build directory
```bash
mkdir build-binutils
cd build-binutils
```
Configure the build
```bash
../binutils-2.35/configure --target=mips-linux-gnu --prefix=/opt/cross --disable-gprof --disable-gdb --disable-werror
```
Make and install binutils
```bash
make -j
sudo make install
```
Edit your `~/.bash_profile`/`~/.zsh_profile` (or whichever shell you use) to add the new binutils binaries to the system PATH
```bash
echo "export PATH=$PATH:/opt/cross/bin" >> ~/.bash_profile
```
Reload `~/.bash_profile` (or just launch a new terminal tab)
```bash
source ~/.bash_profile
```
If this worked, you can now delete the temporary directory `~/binutils-tmp`.
## 3. Final note
Apple's version of `make` is very out-of-date, so you should use the brew-installed `gmake` in place of `make` in this repo from now on.
You should now be able to continue from [step 2](../README.md#2-clone-the-repository) of the Linux instructions.

View file

@ -1,7 +1,8 @@
#!/usr/bin/env python3
import argparse, json, os, signal, time
from multiprocessing import Pool, cpu_count, Event, Manager, ProcessError
import argparse, json, os, signal, time, colorama, multiprocessing
colorama.init()
EXTRACTED_ASSETS_NAMEFILE = ".extracted-assets.json"
@ -16,13 +17,13 @@ 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/MqDbg/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/MqDbg/Config.xml {ZAPDArgs}"
if "overlays" in xmlPath:
execStr += " --static"
if globalUnaccounted:
execStr += " -wu"
execStr += " -Wunaccounted"
print(execStr)
exitValue = os.system(execStr)
@ -67,16 +68,35 @@ def initializeWorker(abort, unaccounted: bool, extractedAssetsTracker: dict, man
globalExtractedAssetsTracker = extractedAssetsTracker
globalManager = manager
def processZAPDArgs(argsZ):
badZAPDArg = False
for z in argsZ:
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
if badZAPDArg:
exit(1)
ZAPDArgs = " ".join(f"-{z}" for z in argsZ)
print("Using extra ZAPD arguments: " + ZAPDArgs)
return ZAPDArgs
def main():
parser = argparse.ArgumentParser(description="baserom asset extractor")
parser.add_argument("-s", "--single", help="asset path relative to assets/, e.g. objects/gameplay_keep")
parser.add_argument("-f", "--force", help="Force the extraction of every xml instead of checking the touched ones.", action="store_true")
parser.add_argument("-j", "--jobs", help="Number of cpu cores to extract with.")
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 = processZAPDArgs(args.Z) if args.Z else ""
global mainAbort
mainAbort = Event()
manager = Manager()
mainAbort = multiprocessing.Event()
manager = multiprocessing.Manager()
signal.signal(signal.SIGINT, SignalHandler)
extractedAssetsTracker = manager.dict()
@ -88,7 +108,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)
@ -117,11 +137,13 @@ def main():
xmlFiles.append(fullPath)
try:
numCores = cpu_count()
print("Extracting assets with " + str(numCores) + " CPU cores.")
with Pool(numCores, initializer=initializeWorker, initargs=(mainAbort, args.unaccounted, extractedAssetsTracker, manager)) as p:
numCores = int(args.jobs or 0)
if numCores <= 0:
numCores = 1
print("Extracting assets with " + str(numCores) + " CPU core" + ("s" if numCores > 1 else "") + ".")
with multiprocessing.get_context("fork").Pool(numCores, initializer=initializeWorker, initargs=(mainAbort, args.unaccounted, extractedAssetsTracker, manager)) as p:
p.map(ExtractFunc, xmlFiles)
except (ProcessError, TypeError):
except (multiprocessing.ProcessError, TypeError):
print("Warning: Multiprocessing exception ocurred.", file=os.sys.stderr)
print("Disabling mutliprocessing.", file=os.sys.stderr)
@ -139,4 +161,4 @@ def main():
exit(1)
if __name__ == "__main__":
main()
main()

View file

@ -1,29 +1,57 @@
#!/usr/bin/env bash
FORMAT_VER="11"
FORMAT_OPTS="-i -style=file"
TIDY_OPTS="-p . --fix --fix-errors"
COMPILER_OPTS="-fno-builtin -std=gnu90 -Iinclude -Isrc -D_LANGUAGE_C -DNON_MATCHING"
# https://backreference.org/2010/05/23/sanitizing-files-with-no-trailing-newline/index.html
# "gets the last character of the file pipes it into read, which will exit with
# a nonzero exit code if it encounters EOF before newline (so, if the last
# character of the file isn't a newline). If read exits nonzero, then append a
# newline onto the file using echo (if read exits 0, that satisfies the ||, so
# the echo command isn't run)." (https://stackoverflow.com/a/34865616)
function add_final_newline () {
for file in "$@"
do
tail -c1 $file | read -r _ || echo >> $file
done
}
export -f add_final_newline
shopt -s globstar
if [ $(command -v clang-format) ]
then
CLANG_FORMAT="clang-format"
else
if [ $(command -v clang-format-${FORMAT_VER}) ]
then
CLANG_FORMAT="clang-format-${FORMAT_VER}"
else
echo "Neither clang-format nor clang-format-${FORMAT_VER} found. Exiting."
exit 1
fi
fi
if (( $# > 0 )); then
echo "Formatting file(s) $*"
echo "Running clang-format..."
clang-format ${FORMAT_OPTS} "$@"
${CLANG_FORMAT} ${FORMAT_OPTS} "$@"
echo "Running clang-tidy..."
clang-tidy ${TIDY_OPTS} "$@" -- ${COMPILER_OPTS} &> /dev/null
echo "Adding missing final new lines..."
sed -i -e '$a\' "$@"
add_final_newline "$@"
echo "Done formatting file(s) $*"
exit
fi
echo "Formatting C files. This will take a bit"
echo "Running clang-format..."
clang-format ${FORMAT_OPTS} src/**/*.c
${CLANG_FORMAT} ${FORMAT_OPTS} src/**/*.c
echo "Running clang-tidy..."
clang-tidy ${TIDY_OPTS} src/**/*.c -- ${COMPILER_OPTS} &> /dev/null
echo "Adding missing final new lines..."
find src/ -type f -name "*.c" -exec sed -i -e '$a\' {} \;
find assets/xml/ -type f -name "*.xml" -exec sed -i -e '$a\' {} \;
find src/ -type f -name "*.c" -exec bash -c 'add_final_newline "$@"' bash {} +
find assets/xml/ -type f -name "*.xml" -exec bash -c 'add_final_newline "$@"' bash {} +
echo "Done formatting all files."

View file

@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/zeldaret/ZAPD.git
branch = master
commit = a3363333d809e8089a55efc3ea32eaea9ddb8d8c
parent = 1cf11907fa8d636babba2df854850035f04bd4fc
commit = 0ba78130478ee1272bc0e2f2fec2d162e7f7f995
parent = 4afee2cfb8bc9cd5c7c217c38138174a9ce1fd99
method = merge
cmdver = 0.4.3

View file

@ -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))

View file

@ -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 -j $(nproc)'
}
}
}
}
}
@ -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'
}
}
}
}
}
}

View file

@ -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)

View file

@ -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
@ -111,6 +119,7 @@ ZAPD also accepts the following list of extra parameters:
- `-tm MODE`: Test Mode (enables certain experimental features). To enable it, set `MODE` to `1`.
- `-se` / `--set-exporter` : Sets which exporter to use.
- `--gcc-compat` : Enables GCC compatibly mode. Slower.
- `-us` / `--unaccounted-static` : Mark unaccounted data as `static`
- `-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

View file

@ -171,7 +171,7 @@ std::string Declaration::GetExternalDeclarationStr() const
std::string Declaration::GetExternStr() const
{
if (IsStatic() || varType == "")
if (IsStatic() || varType == "" || isUnaccounted)
{
return "";
}

View file

@ -12,8 +12,7 @@ typedef uint32_t offset_t;
enum class DeclarationAlignment
{
Align4,
Align8,
Align16
Align8
};
enum class StaticConfig

View file

@ -152,8 +152,8 @@ bool Globals::GetSegmentedPtrName(segptr_t segAddress, ZFile* currentFile,
}
}
const auto& symbolFromMap = Globals::Instance->symbolMap.find(segAddress);
if (symbolFromMap != Globals::Instance->symbolMap.end())
const auto& symbolFromMap = Globals::Instance->cfg.symbolMap.find(segAddress);
if (symbolFromMap != Globals::Instance->cfg.symbolMap.end())
{
declName = "&" + symbolFromMap->second;
return true;

View file

@ -58,11 +58,11 @@ public:
bool verboseUnaccounted = false;
bool gccCompat = false;
bool forceStatic = false;
bool forceUnaccountedStatic = false;
std::vector<ZFile*> files;
std::vector<ZFile*> externalFiles;
std::vector<int32_t> segments;
std::map<uint32_t, std::string> symbolMap;
std::string currentExporter;
static std::map<std::string, ExporterSet*>& GetExporterMap();

View file

@ -48,7 +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."
"\tZAPD has fallen and cannot get up.",
};
srand(time(nullptr));
@ -216,6 +216,10 @@ int main(int argc, char* argv[])
{
Globals::Instance->forceStatic = true;
}
else if (arg == "-us" || arg == "--unaccounted-static")
{
Globals::Instance->forceUnaccountedStatic = true;
}
}
// Parse File Mode
@ -430,6 +434,10 @@ void BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const f
std::string name = outPath.stem().string();
ZTexture tex(nullptr);
if (name.find("u32") != std::string::npos)
tex.dWordAligned = false;
tex.FromPNG(pngFilePath.string(), texType);
std::string cfgPath = StringHelper::Split(pngFilePath.string(), ".")[0] + ".cfg";

View file

@ -263,7 +263,7 @@ void Struct_800A5E28::DeclareReferences(const std::string& prefix)
ZResource::DeclareReferences(varPrefix);
if (unk_4 != 0 && GETSEGNUM(unk_4) == parent->segment)
if (unk_4 != SEGMENTED_NULL && GETSEGNUM(unk_4) == parent->segment)
{
const auto& res = unk_4_arr.at(0);
std::string unk_4_Str = res.GetDefaultName(varPrefix);
@ -293,7 +293,7 @@ void Struct_800A5E28::DeclareReferences(const std::string& prefix)
decl->text = entryStr;
}
if (unk_8 != 0 && GETSEGNUM(unk_8) == parent->segment)
if (unk_8 != SEGMENTED_NULL && GETSEGNUM(unk_8) == parent->segment)
{
uint32_t unk_8_Offset = Seg2Filespace(unk_8, parent->baseAddress);
@ -306,6 +306,8 @@ void Struct_800A5E28::DeclareReferences(const std::string& prefix)
std::string dListStr =
StringHelper::Sprintf("%sSkinLimbDL_%06X", varPrefix.c_str(), unk_8_Offset);
unk_8_dlist->SetName(dListStr);
unk_8_dlist->DeclareVar(varPrefix, "");
unk_8_dlist->DeclareReferences(varPrefix);
parent->AddResource(unk_8_dlist);
}
}

View file

@ -90,7 +90,8 @@ ZOverlay* ZOverlay::FromBuild(fs::path buildPath, fs::path cfgFolderPath)
std::vector<elfio*> readers;
for (size_t i = 1; i < cfgLines.size(); i++)
{
std::string elfPath = (buildPath / (cfgLines[i].substr(0, cfgLines[i].size() - 2) + ".o")).string();
std::string elfPath =
(buildPath / (cfgLines[i].substr(0, cfgLines[i].size() - 2) + ".o")).string();
elfio* reader = new elfio();
if (!reader->load(elfPath))

View file

@ -104,7 +104,7 @@ void ZNormalAnimation::DeclareReferences(const std::string& prefix)
valuesStr += "\n ";
}
parent->AddDeclarationArray(rotationValuesOffset, DeclarationAlignment::Align16,
parent->AddDeclarationArray(rotationValuesOffset, DeclarationAlignment::Align4,
rotationValues.size() * 2, "s16",
StringHelper::Sprintf("%sFrameData", defaultPrefix.c_str()),
rotationValues.size(), valuesStr);
@ -118,7 +118,7 @@ void ZNormalAnimation::DeclareReferences(const std::string& prefix)
indicesStr += "\n";
}
parent->AddDeclarationArray(rotationIndicesOffset, DeclarationAlignment::Align16,
parent->AddDeclarationArray(rotationIndicesOffset, DeclarationAlignment::Align4,
rotationIndices.size() * 6, "JointIndex",
StringHelper::Sprintf("%sJointIndices", defaultPrefix.c_str()),
rotationIndices.size(), indicesStr);
@ -385,7 +385,7 @@ size_t ZCurveAnimation::GetRawDataSize() const
DeclarationAlignment ZCurveAnimation::GetDeclarationAlignment() const
{
return DeclarationAlignment::Align16;
return DeclarationAlignment::Align4;
}
std::string ZCurveAnimation::GetSourceTypeName() const

View file

@ -101,11 +101,18 @@ std::string ZArray::GetBodySourceCode() const
const auto& res = resList[i];
output += "\t";
if (res->GetResourceType() == ZResourceType::Scalar ||
res->GetResourceType() == ZResourceType::Vertex)
switch (res->GetResourceType())
{
case ZResourceType::Pointer:
case ZResourceType::Scalar:
case ZResourceType::Vertex:
output += resList.at(i)->GetBodySourceCode();
else
break;
default:
output += StringHelper::Sprintf("{ %s }", resList.at(i)->GetBodySourceCode().c_str());
break;
}
if (i < arrayCnt - 1 || res->IsExternalResource())
output += ",\n";

View file

@ -1,5 +1,6 @@
#include "ZCollision.h"
#include <cassert>
#include <cstdint>
#include <string>
@ -76,9 +77,42 @@ void ZCollisionHeader::ParseRawData()
polygonTypes.push_back(
BitConverter::ToUInt64BE(rawData, polyTypeDefSegmentOffset + (i * 8)));
if (camDataAddress != 0)
camData = new CameraDataList(parent, name, rawData, camDataSegmentOffset,
polyTypeDefSegmentOffset, polygonTypes.size());
if (camDataAddress != SEGMENTED_NULL)
{
// Try to guess how many elements the CamDataList array has.
// The "guessing algorithm" is basically a "best effort" one and it
// is error-prone.
// This is based mostly on observation of how CollisionHeader data is
// usually ordered. If for some reason the data was in some other funny
// order, this would probably break.
// The most common ordering is:
// - *CamData*
// - SurfaceType
// - CollisionPoly
// - Vertices
// - WaterBoxes
// - CollisionHeader
offset_t upperCameraBoundary = polyTypeDefSegmentOffset;
if (upperCameraBoundary == 0)
{
upperCameraBoundary = polySegmentOffset;
}
if (upperCameraBoundary == 0)
{
upperCameraBoundary = vtxSegmentOffset;
}
if (upperCameraBoundary == 0)
{
upperCameraBoundary = waterBoxSegmentOffset;
}
if (upperCameraBoundary == 0)
{
upperCameraBoundary = rawDataIndex;
}
camData =
new CameraDataList(parent, name, rawData, camDataSegmentOffset, upperCameraBoundary);
}
for (uint16_t i = 0; i < numWaterBoxes; i++)
waterBoxes.push_back(WaterBoxHeader(
@ -106,8 +140,7 @@ void ZCollisionHeader::DeclareReferences(const std::string& prefix)
parent->AddDeclarationArray(
waterBoxSegmentOffset, DeclarationAlignment::Align4, 16 * waterBoxes.size(), "WaterBox",
StringHelper::Sprintf("%s_waterBoxes_%06X", auxName.c_str(), waterBoxSegmentOffset),
waterBoxes.size(), declaration);
StringHelper::Sprintf("%sWaterBoxes", auxName.c_str()), waterBoxes.size(), declaration);
}
if (polygons.size() > 0)
@ -126,8 +159,7 @@ void ZCollisionHeader::DeclareReferences(const std::string& prefix)
parent->AddDeclarationArray(
polySegmentOffset, DeclarationAlignment::Align4, polygons.size() * 16, "CollisionPoly",
StringHelper::Sprintf("%s_polygons_%08X", auxName.c_str(), polySegmentOffset),
polygons.size(), declaration);
StringHelper::Sprintf("%sPolygons", auxName.c_str()), polygons.size(), declaration);
}
declaration.clear();
@ -141,11 +173,10 @@ void ZCollisionHeader::DeclareReferences(const std::string& prefix)
}
if (polyTypeDefAddress != 0)
parent->AddDeclarationArray(
polyTypeDefSegmentOffset, DeclarationAlignment::Align4, polygonTypes.size() * 8,
"SurfaceType",
StringHelper::Sprintf("%s_surfaceType_%08X", auxName.c_str(), polyTypeDefSegmentOffset),
polygonTypes.size(), declaration);
parent->AddDeclarationArray(polyTypeDefSegmentOffset, DeclarationAlignment::Align4,
polygonTypes.size() * 8, "SurfaceType",
StringHelper::Sprintf("%sSurfaceType", auxName.c_str()),
polygonTypes.size(), declaration);
declaration.clear();
@ -167,8 +198,7 @@ void ZCollisionHeader::DeclareReferences(const std::string& prefix)
parent->AddDeclarationArray(
vtxSegmentOffset, first.GetDeclarationAlignment(),
vertices.size() * first.GetRawDataSize(), first.GetSourceTypeName(),
StringHelper::Sprintf("%s_vtx_%08X", auxName.c_str(), vtxSegmentOffset),
vertices.size(), declaration);
StringHelper::Sprintf("%sVertices", auxName.c_str()), vertices.size(), declaration);
}
}
@ -183,11 +213,11 @@ std::string ZCollisionHeader::GetBodySourceCode() const
std::string vtxName;
Globals::Instance->GetSegmentedPtrName(vtxAddress, parent, "Vec3s", vtxName);
declaration += StringHelper::Sprintf("\t%i,\n\t%s,\n", numVerts, vtxName.c_str());
declaration += StringHelper::Sprintf("\t%i, %s,\n", numVerts, vtxName.c_str());
std::string polyName;
Globals::Instance->GetSegmentedPtrName(polyAddress, parent, "CollisionPoly", polyName);
declaration += StringHelper::Sprintf("\t%i,\n\t%s,\n", numPolygons, polyName.c_str());
declaration += StringHelper::Sprintf("\t%i, %s,\n", numPolygons, polyName.c_str());
std::string surfaceName;
Globals::Instance->GetSegmentedPtrName(polyTypeDefAddress, parent, "SurfaceType", surfaceName);
@ -199,7 +229,7 @@ std::string ZCollisionHeader::GetBodySourceCode() const
std::string waterBoxName;
Globals::Instance->GetSegmentedPtrName(waterBoxAddress, parent, "WaterBox", waterBoxName);
declaration += StringHelper::Sprintf("\t%i,\n\t%s\n", numWaterBoxes, waterBoxName.c_str());
declaration += StringHelper::Sprintf("\t%i, %s\n", numWaterBoxes, waterBoxName.c_str());
return declaration;
}
@ -261,16 +291,17 @@ std::string WaterBoxHeader::GetBodySourceCode() const
}
CameraDataList::CameraDataList(ZFile* parent, const std::string& prefix,
const std::vector<uint8_t>& rawData, uint32_t rawDataIndex,
uint32_t polyTypeDefSegmentOffset,
[[maybe_unused]] uint32_t polygonTypesCnt)
const std::vector<uint8_t>& rawData, offset_t rawDataIndex,
offset_t upperCameraBoundary)
{
std::string declaration;
// Parse CameraDataEntries
int32_t numElements = (polyTypeDefSegmentOffset - rawDataIndex) / 8;
uint32_t cameraPosDataSeg = rawDataIndex;
for (int32_t i = 0; i < numElements; i++)
size_t numElements = (upperCameraBoundary - rawDataIndex) / 8;
assert(numElements < 10000);
offset_t cameraPosDataSeg = rawDataIndex;
for (size_t i = 0; i < numElements; i++)
{
CameraDataEntry* entry = new CameraDataEntry();
@ -302,8 +333,7 @@ CameraDataList::CameraDataList(ZFile* parent, const std::string& prefix,
{
int32_t index =
((entries[i]->cameraPosDataSeg & 0x00FFFFFF) - cameraPosDataOffset) / 0x6;
sprintf(camSegLine, "&%s_camPosData_%08X[%i]", prefix.c_str(), cameraPosDataOffset,
index);
sprintf(camSegLine, "&%sCamPosData[%i]", prefix.c_str(), index);
}
else
sprintf(camSegLine, "NULL");
@ -318,7 +348,7 @@ CameraDataList::CameraDataList(ZFile* parent, const std::string& prefix,
parent->AddDeclarationArray(
rawDataIndex, DeclarationAlignment::Align4, entries.size() * 8, "CamData",
StringHelper::Sprintf("%s_camDataList_%08X", prefix.c_str(), rawDataIndex), entries.size(),
StringHelper::Sprintf("%sCamDataList", prefix.c_str(), rawDataIndex), entries.size(),
declaration);
uint32_t numDataTotal = (rawDataIndex - cameraPosDataOffset) / 0x6;
@ -339,10 +369,9 @@ CameraDataList::CameraDataList(ZFile* parent, const std::string& prefix,
int32_t cameraPosDataIndex = GETSEGOFFSET(cameraPosDataSeg);
uint32_t entrySize = numDataTotal * 0x6;
parent->AddDeclarationArray(
cameraPosDataIndex, DeclarationAlignment::Align4, entrySize, "Vec3s",
StringHelper::Sprintf("%s_camPosData_%08X", prefix.c_str(), cameraPosDataIndex),
numDataTotal, declaration);
parent->AddDeclarationArray(cameraPosDataIndex, DeclarationAlignment::Align4, entrySize,
"Vec3s", StringHelper::Sprintf("%sCamPosData", prefix.c_str()),
numDataTotal, declaration);
}
}

View file

@ -55,8 +55,7 @@ public:
std::vector<CameraPositionData*> cameraPositionData;
CameraDataList(ZFile* parent, const std::string& prefix, const std::vector<uint8_t>& rawData,
uint32_t rawDataIndex, uint32_t polyTypeDefSegmentOffset,
uint32_t polygonTypesCnt);
offset_t rawDataIndex, offset_t upperCameraBoundary);
~CameraDataList();
};

View file

@ -3,6 +3,7 @@
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cinttypes>
#include <cmath>
#include "Globals.h"
@ -26,8 +27,8 @@ ZDisplayList::ZDisplayList(ZFile* nParent) : ZResource(nParent)
lastTexSizTest = F3DZEXTexSizes::G_IM_SIZ_16b;
lastTexLoaded = false;
lastTexIsPalette = false;
name = "";
dListType = Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX;
RegisterOptionalAttribute("Ucode");
}
ZDisplayList::~ZDisplayList()
@ -43,13 +44,29 @@ void ZDisplayList::ExtractFromXML(tinyxml2::XMLElement* reader, uint32_t nRawDat
{
rawDataIndex = nRawDataIndex;
ParseXML(reader);
// TODO add error handling here
bool ucodeSet = registeredAttributes.at("Ucode").wasSet;
std::string ucodeValue = registeredAttributes.at("Ucode").value;
if ((Globals::Instance->game == ZGame::OOT_SW97) || (ucodeValue == "f3dex"))
{
dListType = DListType::F3DEX;
}
else if (!ucodeSet || ucodeValue == "f3dex2")
{
dListType = DListType::F3DZEX;
}
else
{
HANDLE_ERROR_RESOURCE(
WarningType::InvalidAttributeValue, parent, this, rawDataIndex,
StringHelper::Sprintf("Invalid ucode type in node: %s\n", reader->Name()), "");
}
// Don't parse raw data of external files
if (parent->GetMode() != ZFileMode::ExternalFile)
{
int32_t rawDataSize = ZDisplayList::GetDListLength(
parent->GetRawData(), rawDataIndex,
Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX);
int32_t rawDataSize =
ZDisplayList::GetDListLength(parent->GetRawData(), rawDataIndex, dListType);
numInstructions = rawDataSize / 8;
ParseRawData();
}
@ -106,7 +123,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);
@ -221,7 +238,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:
@ -298,7 +315,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);
@ -688,20 +705,22 @@ 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 ==
@ -709,9 +728,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
{
@ -831,7 +850,7 @@ void ZDisplayList::Opcode_G_VTX(uint64_t data, char* line)
{
segptr_t segmented = data & 0xFFFFFFFF;
references.push_back(segmented);
parent->AddDeclaration(segmented, DeclarationAlignment::Align16, 16, "Vtx",
parent->AddDeclaration(segmented, DeclarationAlignment::Align8, 16, "Vtx",
StringHelper::Sprintf("0x%08X", segmented), "");
return;
}
@ -943,7 +962,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,
@ -1747,7 +1766,7 @@ void ZDisplayList::DeclareReferences(const std::string& prefix)
if (vertices.size() > 0)
{
std::vector<std::pair<uint32_t, std::vector<ZVtx>>> verticesSorted(vertices.begin(),
vertices.end());
vertices.end());
for (size_t i = 0; i < verticesSorted.size() - 1; i++)
{
@ -1795,7 +1814,7 @@ void ZDisplayList::DeclareReferences(const std::string& prefix)
if (vertices.size() > 0)
{
std::vector<std::pair<uint32_t, std::vector<ZVtx>>> verticesSorted(vertices.begin(),
vertices.end());
vertices.end());
for (size_t i = 0; i < verticesSorted.size() - 1; i++)
{
@ -1889,10 +1908,10 @@ std::string ZDisplayList::ProcessLegacy(const std::string& prefix)
}
auto end = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
int64_t diff = std::chrono::duration_cast<std::chrono::milliseconds>(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;
@ -1973,7 +1992,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])
{

View file

@ -8,6 +8,7 @@
#include "Globals.h"
#include "OutputFormatter.h"
#include "Utils/BinaryWriter.h"
#include "Utils/BitConverter.h"
#include "Utils/Directory.h"
#include "Utils/File.h"
#include "Utils/MemoryStream.h"
@ -192,7 +193,7 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename)
rawData = File::ReadAllBytes((basePath / name).string());
if (reader->Attribute("RangeEnd") == nullptr)
rangeEnd = rawData.size();
rangeEnd = rawData.size();
}
std::unordered_set<std::string> nameSet;
@ -378,7 +379,7 @@ void ZFile::ExtractResources()
ZResourceExporter* exporter = Globals::Instance->GetExporter(res->GetResourceType());
if (exporter != nullptr)
{
//exporter->Save(res, Globals::Instance->outputPath.string(), &writerFile);
// exporter->Save(res, Globals::Instance->outputPath.string(), &writerFile);
exporter->Save(res, Globals::Instance->outputPath.string(), &writerRes);
}
@ -391,7 +392,7 @@ void ZFile::ExtractResources()
File::WriteAllBytes(StringHelper::Sprintf("%s%s.bin",
Globals::Instance->outputPath.string().c_str(),
GetName().c_str()),
memStreamFile->ToVector());
memStreamFile->ToVector());
}
writerFile.Close();
@ -949,9 +950,6 @@ std::string ZFile::ProcessDeclarations()
defines += ProcessTextureIntersections(name);
// Account for padding/alignment
uint32_t lastAddr = 0;
// printf("RANGE START: 0x%06X - RANGE END: 0x%06X\n", rangeStart, rangeEnd);
// Optimization: See if there are any arrays side by side that can be merged...
@ -1002,43 +1000,6 @@ std::string ZFile::ProcessDeclarations()
{
while (item.second->size % 4 != 0)
item.second->size++;
if (lastAddr != 0)
{
if (item.second->alignment == DeclarationAlignment::Align16)
{
int32_t curPtr = lastAddr + declarations[lastAddr]->size;
while (curPtr % 4 != 0)
{
declarations[lastAddr]->size++;
curPtr++;
}
}
else if (item.second->alignment == DeclarationAlignment::Align8)
{
size_t curPtr = lastAddr + declarations[lastAddr]->size;
while (curPtr % 4 != 0)
{
declarations[lastAddr]->size++;
curPtr++;
}
while (curPtr % 8 != 0)
{
char buffer[2048];
sprintf(buffer, "u32 %s_align%02zX = 0;\n", name.c_str(), curPtr);
item.second->preText = buffer + item.second->preText;
declarations[lastAddr]->size += 4;
curPtr += 4;
}
}
}
lastAddr = item.first;
}
HandleUnaccountedData();
@ -1199,14 +1160,15 @@ void ZFile::HandleUnaccountedData()
{
uint32_t lastAddr = 0;
uint32_t lastSize = 0;
std::vector<uint32_t> declsAddresses;
std::vector<offset_t> declsAddresses;
for (const auto& item : declarations)
{
declsAddresses.push_back(item.first);
}
bool breakLoop = false;
for (uint32_t currentAddress : declsAddresses)
for (offset_t currentAddress : declsAddresses)
{
if (currentAddress >= rangeEnd)
{
@ -1235,7 +1197,7 @@ void ZFile::HandleUnaccountedData()
}
}
bool ZFile::HandleUnaccountedAddress(uint32_t currentAddress, uint32_t lastAddr, uint32_t& lastSize)
bool ZFile::HandleUnaccountedAddress(offset_t currentAddress, offset_t lastAddr, uint32_t& lastSize)
{
if (currentAddress != lastAddr && declarations.find(lastAddr) != declarations.end())
{
@ -1275,6 +1237,29 @@ bool ZFile::HandleUnaccountedAddress(uint32_t currentAddress, uint32_t lastAddr,
xmlFilePath.c_str(), currentAddress, name.c_str(), rawData.size()));
}
// Handle Align8
if (currentAddress % 8 == 0 && diff % 8 != 0)
{
Declaration* currentDecl = GetDeclaration(currentAddress);
if (currentDecl != nullptr)
{
if (currentDecl->alignment == DeclarationAlignment::Align8)
{
// Check removed bytes are zeroes
if (BitConverter::ToUInt32BE(rawData, unaccountedAddress + diff - 4) == 0)
{
diff -= 4;
}
}
if (diff == 0)
{
return false;
}
}
}
for (int i = 0; i < diff; i++)
{
uint8_t val = rawData.at(unaccountedAddress + i);
@ -1320,7 +1305,10 @@ bool ZFile::HandleUnaccountedAddress(uint32_t currentAddress, uint32_t lastAddr,
StringHelper::Sprintf("%s_%s_%06X", name.c_str(), unaccountedPrefix.c_str(),
unaccountedAddress),
diff, src);
decl->isUnaccounted = true;
if (Globals::Instance->forceUnaccountedStatic)
decl->staticConf = StaticConfig::On;
if (nonZeroUnaccounted)
{

View file

@ -133,5 +133,5 @@ protected:
std::string ProcessTextureIntersections(const std::string& prefix);
void HandleUnaccountedData();
bool HandleUnaccountedAddress(uint32_t currentAddress, uint32_t lastAddr, uint32_t& lastSize);
bool HandleUnaccountedAddress(offset_t currentAddress, offset_t lastAddr, uint32_t& lastSize);
};

View file

@ -228,8 +228,8 @@ std::string ZLimb::GetBodySourceCode() const
{
std::string childName;
std::string siblingName;
Globals::Instance->GetSegmentedPtrName(childPtr, parent, "Gfx", childName);
Globals::Instance->GetSegmentedPtrName(siblingPtr, parent, "Gfx", siblingName);
Globals::Instance->GetSegmentedPtrName(childPtr, parent, "LegacyLimb", childName);
Globals::Instance->GetSegmentedPtrName(siblingPtr, parent, "LegacyLimb", siblingName);
entryStr += StringHelper::Sprintf("%s,\n", dListStr.c_str());
entryStr +=

View file

@ -0,0 +1,57 @@
#include "ZPointer.h"
#include "Globals.h"
#include "Utils/BitConverter.h"
#include "Utils/StringHelper.h"
#include "WarningHandler.h"
#include "ZFile.h"
REGISTER_ZFILENODE(Pointer, ZPointer);
ZPointer::ZPointer(ZFile* nParent) : ZResource(nParent)
{
RegisterRequiredAttribute("Type");
}
void ZPointer::ParseXML(tinyxml2::XMLElement* reader)
{
ZResource::ParseXML(reader);
type = registeredAttributes.at("Type").value;
}
void ZPointer::ParseRawData()
{
auto& rawData = parent->GetRawData();
ptr = BitConverter::ToUInt32BE(rawData, rawDataIndex);
}
std::string ZPointer::GetBodySourceCode() const
{
std::string ptrName;
Globals::Instance->GetSegmentedPtrName(ptr, parent, "", ptrName);
return ptrName;
}
bool ZPointer::DoesSupportArray() const
{
return true;
}
std::string ZPointer::GetSourceTypeName() const
{
return type + "*";
}
ZResourceType ZPointer::GetResourceType() const
{
return ZResourceType::Pointer;
}
size_t ZPointer::GetRawDataSize() const
{
return 0x04;
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "ZResource.h"
class ZPointer : public ZResource
{
public:
segptr_t ptr = SEGMENTED_NULL;
std::string type;
ZPointer(ZFile* nParent);
void ParseXML(tinyxml2::XMLElement* reader) override;
void ParseRawData() override;
std::string GetBodySourceCode() const override;
bool DoesSupportArray() const override;
std::string GetSourceTypeName() const override;
ZResourceType GetResourceType() const override;
size_t GetRawDataSize() const override;
};

View file

@ -38,6 +38,7 @@ enum class ZResourceType
Mtx,
Path,
PlayerAnimationData,
Pointer,
Room,
RoomCommand,
Scalar,

View file

@ -152,7 +152,6 @@ void PolygonDlist::GetSourceOutputCode(const std::string& prefix)
DeclareVar(prefix, bodyStr);
else
decl->text = bodyStr;
}
std::string PolygonDlist::GetSourceTypeName() const

View file

@ -1,6 +1,8 @@
#include "SetSpecialObjects.h"
#include "Utils/BitConverter.h"
#include "Utils/StringHelper.h"
#include "ZRoom/ZNames.h"
SetSpecialObjects::SetSpecialObjects(ZFile* nParent) : ZRoomCommand(nParent)
{
@ -15,8 +17,10 @@ void SetSpecialObjects::ParseRawData()
std::string SetSpecialObjects::GetBodySourceCode() const
{
return StringHelper::Sprintf("SCENE_CMD_SPECIAL_FILES(0x%02X, 0x%04X)", elfMessage,
globalObject);
std::string objectName = ZNames::GetObjectName(globalObject);
return StringHelper::Sprintf("SCENE_CMD_SPECIAL_FILES(0x%02X, %s)", elfMessage,
objectName.c_str());
}
std::string SetSpecialObjects::GetCommandCName() const

View file

@ -2,7 +2,9 @@
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cinttypes>
#include <string_view>
#include "Commands/EndMarker.h"
#include "Commands/SetActorCutsceneList.h"
#include "Commands/SetActorList.h"
@ -259,9 +261,10 @@ void ZRoom::ParseRawData()
if (Globals::Instance->profile)
{
auto end = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
int64_t diff =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
if (diff > 50)
printf("OP: %s, TIME: %lims\n", cmd->GetCommandCName().c_str(), diff);
printf("OP: %s, TIME: %" PRIi64 "ms\n", cmd->GetCommandCName().c_str(), diff);
}
cmd->cmdIndex = currentIndex;

View file

@ -139,7 +139,7 @@ ZResourceType ZSkeleton::GetResourceType() const
DeclarationAlignment ZSkeleton::GetDeclarationAlignment() const
{
return DeclarationAlignment::Align16;
return DeclarationAlignment::Align4;
}
uint8_t ZSkeleton::GetLimbCount()

View file

@ -16,6 +16,7 @@ ZTexture::ZTexture(ZFile* nParent) : ZResource(nParent)
{
width = 0;
height = 0;
dWordAligned = true;
RegisterRequiredAttribute("Width");
RegisterRequiredAttribute("Height");
@ -105,10 +106,7 @@ void ZTexture::ParseXML(tinyxml2::XMLElement* reader)
void ZTexture::ParseRawData()
{
if (rawDataIndex % 8 != 0)
{
HANDLE_WARNING_RESOURCE(WarningType::NotImplemented, parent, this, rawDataIndex,
"this texture is not 64-bit aligned", "");
}
dWordAligned = false;
switch (format)
{
@ -724,7 +722,12 @@ void ZTexture::Save(const fs::path& outFolder)
if (!Directory::Exists(outPath.string()))
Directory::CreateDirectory(outPath.string());
auto outFileName = outPath / (outName + "." + GetExternalExtension() + ".png");
fs::path outFileName;
if (!dWordAligned)
outFileName = outPath / (outName + ".u32" + "." + GetExternalExtension() + ".png");
else
outFileName = outPath / (outName + +"." + GetExternalExtension() + ".png");
#ifdef TEXTURE_DEBUG
printf("Saving PNG: %s\n", outFileName.c_str());
@ -745,7 +748,7 @@ Declaration* ZTexture::DeclareVar(const std::string& prefix,
{
std::string auxName = name;
std::string auxOutName = outName;
std::string incStr;
if (auxName == "")
auxName = GetDefaultName(prefix);
@ -754,8 +757,12 @@ Declaration* ZTexture::DeclareVar(const std::string& prefix,
auto filepath = Globals::Instance->outputPath / fs::path(auxOutName).stem();
std::string incStr =
StringHelper::Sprintf("%s.%s.inc.c", filepath.c_str(), GetExternalExtension().c_str());
if (dWordAligned)
incStr =
StringHelper::Sprintf("%s.%s.inc.c", filepath.c_str(), GetExternalExtension().c_str());
else
incStr = StringHelper::Sprintf("%s.u32.%s.inc.c", filepath.c_str(),
GetExternalExtension().c_str());
if (!Globals::Instance->cfg.texturePool.empty())
{
@ -765,13 +772,19 @@ Declaration* ZTexture::DeclareVar(const std::string& prefix,
const auto& poolEntry = Globals::Instance->cfg.texturePool.find(hash);
if (poolEntry != Globals::Instance->cfg.texturePool.end())
{
incStr = StringHelper::Sprintf("%s.%s.inc.c", poolEntry->second.path.c_str(),
GetExternalExtension().c_str());
if (dWordAligned)
incStr = StringHelper::Sprintf("%s.%s.inc.c", poolEntry->second.path.c_str(),
GetExternalExtension().c_str());
else
incStr = StringHelper::Sprintf("%s.u32.%s.inc.c", poolEntry->second.path.c_str(),
GetExternalExtension().c_str());
}
}
size_t texSizeDivisor = (dWordAligned) ? 8 : 4;
Declaration* decl = parent->AddDeclarationIncludeArray(
rawDataIndex, incStr, GetRawDataSize(), GetSourceTypeName(), auxName, GetRawDataSize() / 8);
Declaration* decl = parent->AddDeclarationIncludeArray(rawDataIndex, incStr, GetRawDataSize(),
GetSourceTypeName(), auxName,
GetRawDataSize() / texSizeDivisor);
decl->staticConf = staticConf;
return decl;
}
@ -779,15 +792,17 @@ Declaration* ZTexture::DeclareVar(const std::string& prefix,
std::string ZTexture::GetBodySourceCode() const
{
std::string sourceOutput;
for (size_t i = 0; i < textureDataRaw.size(); i += 8)
size_t texSizeInc = (dWordAligned) ? 8 : 4;
for (size_t i = 0; i < textureDataRaw.size(); i += texSizeInc)
{
if (i % 32 == 0)
sourceOutput += " ";
sourceOutput +=
StringHelper::Sprintf("0x%016llX, ", BitConverter::ToUInt64BE(textureDataRaw, i));
if (dWordAligned)
sourceOutput +=
StringHelper::Sprintf("0x%016llX, ", BitConverter::ToUInt64BE(textureDataRaw, i));
else
sourceOutput +=
StringHelper::Sprintf("0x%08llX, ", BitConverter::ToUInt32BE(textureDataRaw, i));
if (i % 32 == 24)
sourceOutput += StringHelper::Sprintf(" // 0x%06X \n", rawDataIndex + ((i / 32) * 32));
}
@ -812,7 +827,7 @@ ZResourceType ZTexture::GetResourceType() const
std::string ZTexture::GetSourceTypeName() const
{
return "u64";
return dWordAligned ? "u64" : "u32";
}
void ZTexture::CalcHash()

View file

@ -54,6 +54,7 @@ public:
ZTexture(ZFile* nParent);
bool isPalette = false;
bool dWordAligned = true;
void ExtractFromBinary(uint32_t nRawDataIndex, int32_t nWidth, int32_t nHeight,
TextureType nType, bool nIsPalette);

View file

@ -82,5 +82,5 @@ std::string ZVtx::GetExternalExtension() const
DeclarationAlignment ZVtx::GetDeclarationAlignment() const
{
return DeclarationAlignment::Align16;
return DeclarationAlignment::Align8;
}

View file

@ -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))

View file

@ -109,6 +109,6 @@ public:
static bool IEquals(const std::string& a, const std::string& b)
{
return std::equal(a.begin(), a.end(), b.begin(), b.end(),
[](char a, char b) { return tolower(a) == tolower(b); });
[](char a, char b) { return tolower(a) == tolower(b); });
}
};

View file

@ -9,6 +9,7 @@ This document aims to be a small reference of how to create a compatible xml fil
- [Basic XML](#basic-xml)
- [Resources types](#resources-types)
- [File](#file)
- [ExternalFile](#externalfile)
- [Texture](#texture)
- [Background](#background)
- [Blob](#blob)
@ -33,6 +34,7 @@ This document aims to be a small reference of how to create a compatible xml fil
- [Array](#array)
- [Path](#path)
- [PlayerAnimationData](#playeranimationdata)
- [Pointer](#pointer)
## Basic XML
@ -589,7 +591,7 @@ Vec3s D_04002040[24] = {
The `Array` element is special, because it needs an inner element to work. It will declare an array of its inner element.
Currently, only [`Scalar`](#scalar), [`Vector`](#vector) and [`Vtx`](#vtx) support being wrapped in an array.
Currently, only [`Pointer`](#pointer), [`Scalar`](#scalar), [`Vector`](#vector) and [`Vtx`](#vtx) support being wrapped in an array.
- Example:
@ -637,3 +639,24 @@ Allows the extraction of the specific data of the player animations which are fo
- `FrameCount`: Required. The length of the animation in frames. It must be a positive integer.
-------------------------
### Pointer
Allows the extraction of a variable that contains a pointer
- Example:
```xml
<Array Name="object_hanareyama_obj_DLArray_004638" Count="54" Offset="0x4638">
<Pointer Type="Gfx"/>
</Array>
```
- Attributes:
- `Name`: Required.
- `Type`: Required. The type of the extracted pointer.
※ Can be wrapped in an [`Array`](#array) tag.
-------------------------