mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-25 01:34:18 +00:00
Setup rom decompression and compression (#1614)
* decompress baserom * cleanup * specific hash check * rename baserom * git subrepo clone (merge) --branch=5da3132606e4fd427a8d72b8428e4f921cd6e56f git@github.com:z64tools/z64compress.git tools/z64compress subrepo: subdir: "tools/z64compress" merged: "5da313260" upstream: origin: "git@github.com:z64tools/z64compress.git" branch: "5da3132606e4fd427a8d72b8428e4f921cd6e56f" commit: "5da313260" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596" * setup compression * Add all compressed segments to the spec * Update md5 files * readme instructions * cleanup * Setup python dependencies on Jenkinsfile * Update Makefile Co-authored-by: cadmic <cadmic24@gmail.com> * review * . .venv/bin/activate * update readme * whoops * Yeet other versions from decompress_baserom.py * my bad * Move everything to baseroms/VERSION/ * Active venv on each command * jenkinsfile: use multiline syntax * Put the correct path on the jenkinsfile * Forgot to call per_version_fixes * CC0 * Update readme * Change where baserom segments are put into * Update Makefile Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * Update crunch64 * Label compressed instead of uncompressed * Update README.md Co-authored-by: fig02 <fig02srl@gmail.com> * Fix * `make rom` * baserom-uncompressed * Update README.md Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * Update README.md Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * Update README.md Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * Update README.md Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * Update README.md Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * review * baserom-decompressed.z64 * ignore baseroms * rm -rf tools/z64compress * wip crunch64-based compress.py * OK compress * use ipl3checksum sum directly for cic update on compressed rom * multithreading * "docs" * fix MT: move set_sigint_ignored to global level for pickling * license compress.py * rm junk * Fix (or at least sort out) compress_ranges.txt dependencies * Update tools/overlayhelpers/damage_table.py Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> --------- Co-authored-by: cadmic <cadmic24@gmail.com> Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> Co-authored-by: fig02 <fig02srl@gmail.com>
This commit is contained in:
parent
5e406f754e
commit
3d9db8d34d
36 changed files with 2282 additions and 187 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -10,12 +10,14 @@ __pycache__/
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
venv/
|
venv/
|
||||||
|
.venv/
|
||||||
|
|
||||||
# Project-specific ignores
|
# Project-specific ignores
|
||||||
build/
|
build/
|
||||||
expected/
|
expected/
|
||||||
notes/
|
notes/
|
||||||
baserom/
|
baserom/
|
||||||
|
baseroms/*/segments/
|
||||||
docs/doxygen/
|
docs/doxygen/
|
||||||
*.elf
|
*.elf
|
||||||
*.sra
|
*.sra
|
||||||
|
@ -25,6 +27,7 @@ docs/doxygen/
|
||||||
*.map
|
*.map
|
||||||
*.dump
|
*.dump
|
||||||
out.txt
|
out.txt
|
||||||
|
*.ram
|
||||||
|
|
||||||
# Tool artifacts
|
# Tool artifacts
|
||||||
tools/mipspro7.2_compiler/
|
tools/mipspro7.2_compiler/
|
||||||
|
|
23
Jenkinsfile
vendored
23
Jenkinsfile
vendored
|
@ -4,10 +4,21 @@ pipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
stage('Install Python dependencies') {
|
||||||
|
steps {
|
||||||
|
echo 'Installing Python dependencies'
|
||||||
|
sh 'python3 -m venv .venv'
|
||||||
|
sh '''. .venv/bin/activate
|
||||||
|
python3 -m pip install -U -r requirements.txt
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
stage('Setup') {
|
stage('Setup') {
|
||||||
steps {
|
steps {
|
||||||
sh 'cp /usr/local/etc/roms/baserom_oot.z64 baserom_original.z64'
|
sh 'cp /usr/local/etc/roms/baserom_oot.z64 baseroms/gc-eu-mq-dbg/baserom.z64'
|
||||||
sh 'make -j setup'
|
sh '''. .venv/bin/activate
|
||||||
|
make -j setup
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build (qemu-irix)') {
|
stage('Build (qemu-irix)') {
|
||||||
|
@ -15,7 +26,9 @@ pipeline {
|
||||||
branch 'main'
|
branch 'main'
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
sh 'ORIG_COMPILER=1 make -j'
|
sh '''. .venv/bin/activate
|
||||||
|
make -j ORIG_COMPILER=1
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
|
@ -25,7 +38,9 @@ pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
sh 'make -j'
|
sh '''. .venv/bin/activate
|
||||||
|
make -j
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Report Progress') {
|
stage('Report Progress') {
|
||||||
|
|
64
Makefile
64
Makefile
|
@ -17,6 +17,8 @@ COMPILER := ido
|
||||||
# Target game version. Currently only the following version is supported:
|
# Target game version. Currently only the following version is supported:
|
||||||
# gc-eu-mq-dbg GameCube Europe/PAL Master Quest Debug (default)
|
# gc-eu-mq-dbg GameCube Europe/PAL Master Quest Debug (default)
|
||||||
VERSION := gc-eu-mq-dbg
|
VERSION := gc-eu-mq-dbg
|
||||||
|
# Number of threads to extract and compress with
|
||||||
|
N_THREADS := $(shell nproc)
|
||||||
|
|
||||||
CFLAGS ?=
|
CFLAGS ?=
|
||||||
CPPFLAGS ?=
|
CPPFLAGS ?=
|
||||||
|
@ -72,8 +74,6 @@ else
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
N_THREADS := $(shell nproc)
|
|
||||||
|
|
||||||
#### Tools ####
|
#### Tools ####
|
||||||
ifneq ($(shell type $(MIPS_BINUTILS_PREFIX)ld >/dev/null 2>/dev/null; echo $$?), 0)
|
ifneq ($(shell type $(MIPS_BINUTILS_PREFIX)ld >/dev/null 2>/dev/null; echo $$?), 0)
|
||||||
$(error Unable to find $(MIPS_BINUTILS_PREFIX)ld. Please install or build MIPS binutils, commonly mips-linux-gnu. (or set MIPS_BINUTILS_PREFIX if your MIPS binutils install uses another prefix))
|
$(error Unable to find $(MIPS_BINUTILS_PREFIX)ld. Please install or build MIPS binutils, commonly mips-linux-gnu. (or set MIPS_BINUTILS_PREFIX if your MIPS binutils install uses another prefix))
|
||||||
|
@ -105,6 +105,7 @@ AS := $(MIPS_BINUTILS_PREFIX)as
|
||||||
LD := $(MIPS_BINUTILS_PREFIX)ld
|
LD := $(MIPS_BINUTILS_PREFIX)ld
|
||||||
OBJCOPY := $(MIPS_BINUTILS_PREFIX)objcopy
|
OBJCOPY := $(MIPS_BINUTILS_PREFIX)objcopy
|
||||||
OBJDUMP := $(MIPS_BINUTILS_PREFIX)objdump
|
OBJDUMP := $(MIPS_BINUTILS_PREFIX)objdump
|
||||||
|
NM := $(MIPS_BINUTILS_PREFIX)nm
|
||||||
|
|
||||||
N64_EMULATOR ?=
|
N64_EMULATOR ?=
|
||||||
|
|
||||||
|
@ -119,6 +120,7 @@ MKDMADATA := tools/mkdmadata
|
||||||
ELF2ROM := tools/elf2rom
|
ELF2ROM := tools/elf2rom
|
||||||
ZAPD := tools/ZAPD/ZAPD.out
|
ZAPD := tools/ZAPD/ZAPD.out
|
||||||
FADO := tools/fado/fado.elf
|
FADO := tools/fado/fado.elf
|
||||||
|
PYTHON ?= python3
|
||||||
|
|
||||||
# Command to replace path variables in the spec file. We can't use the C
|
# Command to replace path variables in the spec file. We can't use the C
|
||||||
# preprocessor for this because it won't substitute inside string literals.
|
# preprocessor for this because it won't substitute inside string literals.
|
||||||
|
@ -161,6 +163,7 @@ OBJDUMP_FLAGS := -d -r -z -Mreg-names=32
|
||||||
#### Files ####
|
#### Files ####
|
||||||
|
|
||||||
# ROM image
|
# ROM image
|
||||||
|
ROMC := oot-$(VERSION)-compressed.z64
|
||||||
ROM := oot-$(VERSION).z64
|
ROM := oot-$(VERSION).z64
|
||||||
ELF := $(ROM:.z64=.elf)
|
ELF := $(ROM:.z64=.elf)
|
||||||
# description of ROM segments
|
# description of ROM segments
|
||||||
|
@ -184,9 +187,10 @@ UNDECOMPILED_DATA_DIRS := $(shell find data -type d)
|
||||||
# source files
|
# source files
|
||||||
C_FILES := $(filter-out %.inc.c,$(foreach dir,$(SRC_DIRS) $(ASSET_BIN_DIRS),$(wildcard $(dir)/*.c)))
|
C_FILES := $(filter-out %.inc.c,$(foreach dir,$(SRC_DIRS) $(ASSET_BIN_DIRS),$(wildcard $(dir)/*.c)))
|
||||||
S_FILES := $(foreach dir,$(SRC_DIRS) $(UNDECOMPILED_DATA_DIRS),$(wildcard $(dir)/*.s))
|
S_FILES := $(foreach dir,$(SRC_DIRS) $(UNDECOMPILED_DATA_DIRS),$(wildcard $(dir)/*.s))
|
||||||
|
BASEROM_BIN_FILES := $(wildcard baseroms/$(VERSION)/segments/*)
|
||||||
O_FILES := $(foreach f,$(S_FILES:.s=.o),$(BUILD_DIR)/$f) \
|
O_FILES := $(foreach f,$(S_FILES:.s=.o),$(BUILD_DIR)/$f) \
|
||||||
$(foreach f,$(C_FILES:.c=.o),$(BUILD_DIR)/$f) \
|
$(foreach f,$(C_FILES:.c=.o),$(BUILD_DIR)/$f) \
|
||||||
$(foreach f,$(wildcard baserom/*),$(BUILD_DIR)/$f.o)
|
$(foreach f,$(BASEROM_BIN_FILES),$(BUILD_DIR)/baserom/$(notdir $f).o)
|
||||||
|
|
||||||
OVL_RELOC_FILES := $(shell $(CPP) $(CPPFLAGS) $(SPEC) | $(SPEC_REPLACE_VARS) | grep -o '[^"]*_reloc.o' )
|
OVL_RELOC_FILES := $(shell $(CPP) $(CPPFLAGS) $(SPEC) | $(SPEC_REPLACE_VARS) | grep -o '[^"]*_reloc.o' )
|
||||||
|
|
||||||
|
@ -246,11 +250,11 @@ $(BUILD_DIR)/src/libultra/rmon/%.o: CC := $(CC_OLD)
|
||||||
$(BUILD_DIR)/src/code/jpegutils.o: CC := $(CC_OLD)
|
$(BUILD_DIR)/src/code/jpegutils.o: CC := $(CC_OLD)
|
||||||
$(BUILD_DIR)/src/code/jpegdecoder.o: CC := $(CC_OLD)
|
$(BUILD_DIR)/src/code/jpegdecoder.o: CC := $(CC_OLD)
|
||||||
|
|
||||||
$(BUILD_DIR)/src/boot/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
$(BUILD_DIR)/src/boot/%.o: CC := $(PYTHON) tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
||||||
$(BUILD_DIR)/src/code/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
$(BUILD_DIR)/src/code/%.o: CC := $(PYTHON) tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
||||||
$(BUILD_DIR)/src/overlays/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
$(BUILD_DIR)/src/overlays/%.o: CC := $(PYTHON) tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
||||||
|
|
||||||
$(BUILD_DIR)/assets/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
$(BUILD_DIR)/assets/%.o: CC := $(PYTHON) tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
|
||||||
else
|
else
|
||||||
$(BUILD_DIR)/src/libultra/libc/ll.o: OPTFLAGS := -Ofast
|
$(BUILD_DIR)/src/libultra/libc/ll.o: OPTFLAGS := -Ofast
|
||||||
$(BUILD_DIR)/src/%.o: CC := $(CC) -fexec-charset=euc-jp
|
$(BUILD_DIR)/src/%.o: CC := $(CC) -fexec-charset=euc-jp
|
||||||
|
@ -258,14 +262,20 @@ endif
|
||||||
|
|
||||||
#### Main Targets ###
|
#### Main Targets ###
|
||||||
|
|
||||||
all: $(ROM)
|
rom: $(ROM)
|
||||||
ifeq ($(COMPARE),1)
|
ifneq ($(COMPARE),0)
|
||||||
@md5sum $(ROM)
|
@md5sum $(ROM)
|
||||||
@md5sum -c checksum.md5
|
@md5sum -c baseroms/$(VERSION)/checksum.md5
|
||||||
|
endif
|
||||||
|
|
||||||
|
compressed: $(ROMC)
|
||||||
|
ifneq ($(COMPARE),0)
|
||||||
|
@md5sum $(ROMC)
|
||||||
|
@md5sum -c baseroms/$(VERSION)/checksum-compressed.md5
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) -r $(ROM) $(ELF) $(BUILD_DIR)
|
$(RM) -r $(ROMC) $(ROM) $(ELF) $(BUILD_DIR)
|
||||||
|
|
||||||
assetclean:
|
assetclean:
|
||||||
$(RM) -r $(ASSET_BIN_DIRS)
|
$(RM) -r $(ASSET_BIN_DIRS)
|
||||||
|
@ -274,14 +284,14 @@ assetclean:
|
||||||
$(RM) -r .extracted-assets.json
|
$(RM) -r .extracted-assets.json
|
||||||
|
|
||||||
distclean: clean assetclean
|
distclean: clean assetclean
|
||||||
$(RM) -r baserom/
|
$(RM) -r baseroms/$(VERSION)/segments
|
||||||
$(MAKE) -C tools distclean
|
$(MAKE) -C tools distclean
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
$(MAKE) -C tools
|
$(MAKE) -C tools
|
||||||
python3 fixbaserom.py
|
$(PYTHON) tools/decompress_baserom.py $(VERSION)
|
||||||
python3 extract_baserom.py
|
$(PYTHON) extract_baserom.py
|
||||||
python3 extract_assets.py -j$(N_THREADS)
|
$(PYTHON) extract_assets.py -j$(N_THREADS)
|
||||||
|
|
||||||
run: $(ROM)
|
run: $(ROM)
|
||||||
ifeq ($(N64_EMULATOR),)
|
ifeq ($(N64_EMULATOR),)
|
||||||
|
@ -290,13 +300,20 @@ endif
|
||||||
$(N64_EMULATOR) $<
|
$(N64_EMULATOR) $<
|
||||||
|
|
||||||
|
|
||||||
.PHONY: all clean setup run distclean assetclean
|
.PHONY: all rom compressed clean setup run distclean assetclean
|
||||||
|
.DEFAULT_GOAL := rom
|
||||||
|
all: rom compressed
|
||||||
|
|
||||||
#### Various Recipes ####
|
#### Various Recipes ####
|
||||||
|
|
||||||
$(ROM): $(ELF)
|
$(ROM): $(ELF)
|
||||||
$(ELF2ROM) -cic 6105 $< $@
|
$(ELF2ROM) -cic 6105 $< $@
|
||||||
|
|
||||||
|
$(ROMC): $(ROM) $(ELF) $(BUILD_DIR)/compress_ranges.txt
|
||||||
|
# note: $(BUILD_DIR)/compress_ranges.txt should only be used for nonmatching builds. it works by chance for matching builds too though
|
||||||
|
$(PYTHON) tools/compress.py --in $(ROM) --out $@ --dma-range `./tools/dmadata_range.sh $(NM) $(ELF)` --compress `cat $(BUILD_DIR)/compress_ranges.txt` --threads $(N_THREADS)
|
||||||
|
$(PYTHON) -m ipl3checksum sum --cic 6105 --update $@
|
||||||
|
|
||||||
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) $(OVL_RELOC_FILES) $(BUILD_DIR)/ldscript.txt $(BUILD_DIR)/undefined_syms.txt
|
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) $(OVL_RELOC_FILES) $(BUILD_DIR)/ldscript.txt $(BUILD_DIR)/undefined_syms.txt
|
||||||
$(LD) -T $(BUILD_DIR)/undefined_syms.txt -T $(BUILD_DIR)/ldscript.txt --no-check-sections --accept-unknown-input-arch --emit-relocs -Map $(BUILD_DIR)/z64.map -o $@
|
$(LD) -T $(BUILD_DIR)/undefined_syms.txt -T $(BUILD_DIR)/ldscript.txt --no-check-sections --accept-unknown-input-arch --emit-relocs -Map $(BUILD_DIR)/z64.map -o $@
|
||||||
|
|
||||||
|
@ -321,14 +338,14 @@ $(BUILD_DIR)/ldscript.txt: $(BUILD_DIR)/$(SPEC)
|
||||||
$(BUILD_DIR)/undefined_syms.txt: undefined_syms.txt
|
$(BUILD_DIR)/undefined_syms.txt: undefined_syms.txt
|
||||||
$(CPP) $(CPPFLAGS) $< > $@
|
$(CPP) $(CPPFLAGS) $< > $@
|
||||||
|
|
||||||
$(BUILD_DIR)/baserom/%.o: baserom/%
|
$(BUILD_DIR)/baserom/%.o: baseroms/$(VERSION)/segments/%
|
||||||
$(OBJCOPY) -I binary -O elf32-big $< $@
|
$(OBJCOPY) -I binary -O elf32-big $< $@
|
||||||
|
|
||||||
$(BUILD_DIR)/data/%.o: data/%.s
|
$(BUILD_DIR)/data/%.o: data/%.s
|
||||||
$(AS) $(ASFLAGS) $< -o $@
|
$(AS) $(ASFLAGS) $< -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/assets/text/%.enc.h: assets/text/%.h assets/text/charmap.txt
|
$(BUILD_DIR)/assets/text/%.enc.h: assets/text/%.h assets/text/charmap.txt
|
||||||
python3 tools/msgenc.py assets/text/charmap.txt $< $@
|
$(PYTHON) tools/msgenc.py assets/text/charmap.txt $< $@
|
||||||
|
|
||||||
# Dependencies for files including message data headers
|
# Dependencies for files including message data headers
|
||||||
# TODO remove when full header dependencies are used.
|
# TODO remove when full header dependencies are used.
|
||||||
|
@ -345,8 +362,8 @@ $(BUILD_DIR)/assets/%.o: assets/%.c
|
||||||
$(BUILD_DIR)/src/%.o: src/%.s
|
$(BUILD_DIR)/src/%.o: src/%.s
|
||||||
$(CPP) $(CPPFLAGS) -Iinclude $< | $(AS) $(ASFLAGS) -o $@
|
$(CPP) $(CPPFLAGS) -Iinclude $< | $(AS) $(ASFLAGS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/dmadata_table_spec.h: $(BUILD_DIR)/$(SPEC)
|
$(BUILD_DIR)/dmadata_table_spec.h $(BUILD_DIR)/compress_ranges.txt: $(BUILD_DIR)/$(SPEC)
|
||||||
$(MKDMADATA) $< $@
|
$(MKDMADATA) $< $(BUILD_DIR)/dmadata_table_spec.h $(BUILD_DIR)/compress_ranges.txt
|
||||||
|
|
||||||
# Dependencies for files that may include the dmadata header automatically generated from the spec file
|
# Dependencies for files that may include the dmadata header automatically generated from the spec file
|
||||||
$(BUILD_DIR)/src/boot/z_std_dma.o: $(BUILD_DIR)/dmadata_table_spec.h
|
$(BUILD_DIR)/src/boot/z_std_dma.o: $(BUILD_DIR)/dmadata_table_spec.h
|
||||||
|
@ -370,13 +387,13 @@ $(BUILD_DIR)/src/%.o: src/%.c
|
||||||
$(BUILD_DIR)/src/libultra/libc/ll.o: src/libultra/libc/ll.c
|
$(BUILD_DIR)/src/libultra/libc/ll.o: src/libultra/libc/ll.c
|
||||||
$(CC_CHECK) $<
|
$(CC_CHECK) $<
|
||||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||||
python3 tools/set_o32abi_bit.py $@
|
$(PYTHON) tools/set_o32abi_bit.py $@
|
||||||
@$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s)
|
@$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s)
|
||||||
|
|
||||||
$(BUILD_DIR)/src/libultra/libc/llcvt.o: src/libultra/libc/llcvt.c
|
$(BUILD_DIR)/src/libultra/libc/llcvt.o: src/libultra/libc/llcvt.c
|
||||||
$(CC_CHECK) $<
|
$(CC_CHECK) $<
|
||||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||||
python3 tools/set_o32abi_bit.py $@
|
$(PYTHON) tools/set_o32abi_bit.py $@
|
||||||
@$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s)
|
@$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s)
|
||||||
|
|
||||||
$(BUILD_DIR)/src/overlays/%_reloc.o: $(BUILD_DIR)/$(SPEC)
|
$(BUILD_DIR)/src/overlays/%_reloc.o: $(BUILD_DIR)/$(SPEC)
|
||||||
|
@ -393,3 +410,6 @@ $(BUILD_DIR)/assets/%.jpg.inc.c: assets/%.jpg
|
||||||
$(ZAPD) bren -eh -i $< -o $@
|
$(ZAPD) bren -eh -i $< -o $@
|
||||||
|
|
||||||
-include $(DEP_FILES)
|
-include $(DEP_FILES)
|
||||||
|
|
||||||
|
# Print target for debugging
|
||||||
|
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
|
||||||
|
|
42
README.md
42
README.md
|
@ -68,13 +68,15 @@ The build process has the following package requirements:
|
||||||
* build-essential
|
* build-essential
|
||||||
* binutils-mips-linux-gnu
|
* binutils-mips-linux-gnu
|
||||||
* python3
|
* python3
|
||||||
|
* python3-pip
|
||||||
|
* python3-venv
|
||||||
* libpng-dev
|
* libpng-dev
|
||||||
|
|
||||||
Under Debian / Ubuntu (which we recommend using), you can install them with the following commands:
|
Under Debian / Ubuntu (which we recommend using), you can install them with the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install git build-essential binutils-mips-linux-gnu python3 libpng-dev
|
sudo apt-get install git build-essential binutils-mips-linux-gnu python3 python3-pip python3-venv libpng-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using GCC as the compiler for Ocarina of Time, you will also need:
|
If you are using GCC as the compiler for Ocarina of Time, you will also need:
|
||||||
|
@ -98,12 +100,37 @@ This will copy the GitHub repository contents into a new folder in the current d
|
||||||
cd oot
|
cd oot
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Prepare a base ROM
|
#### 3. Install python dependencies
|
||||||
|
|
||||||
Copy over your copy of the Master Quest (Debug) ROM inside the root of this new project directory.
|
The build process has a few python packages required that are located in `requirements.txt`.
|
||||||
Rename the file to "baserom_original.z64", "baserom_original.n64" or "baserom_original.v64", depending on the original extension.
|
|
||||||
|
|
||||||
#### 4. Setup the ROM and build process
|
It is recommended to set up a virtual environment for python to contain all dependencies. To create a virtual environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .venv
|
||||||
|
```
|
||||||
|
|
||||||
|
To start using the virtual environment in your current terminal run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep in mind that for each new terminal session, you will need to **activate** the Python virtual environment again. That is, run the above `. .venv/bin/activate` command.
|
||||||
|
|
||||||
|
Now you can install the Python dependencies, to do so run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m pip install -U -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Prepare a base ROM
|
||||||
|
|
||||||
|
Place a copy of the Master Quest (Debug) ROM inside the `baseroms/gc-eu-mq-dbg/` folder.
|
||||||
|
|
||||||
|
Rename the file to `baserom.z64`, `baserom.n64` or `baserom.v64`, depending on the original extension.
|
||||||
|
|
||||||
|
#### 5. Setup the ROM and build process
|
||||||
|
|
||||||
Setup and extract everything from your ROM with the following command:
|
Setup and extract everything from your ROM with the following command:
|
||||||
|
|
||||||
|
@ -111,10 +138,10 @@ Setup and extract everything from your ROM with the following command:
|
||||||
make setup
|
make setup
|
||||||
```
|
```
|
||||||
|
|
||||||
This will generate a new ROM called "baserom.z64" that will have the overdump removed and the header patched.
|
This will generate a new ROM "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64" that will have the overdump removed and the header patched.
|
||||||
It will also extract the individual assets from the ROM.
|
It will also extract the individual assets from the ROM.
|
||||||
|
|
||||||
#### 5. Build the ROM
|
#### 6. Build the ROM
|
||||||
|
|
||||||
Run make to build the ROM.
|
Run make to build the ROM.
|
||||||
Make sure your path to the project is not too long, otherwise this process may error.
|
Make sure your path to the project is not too long, otherwise this process may error.
|
||||||
|
@ -145,7 +172,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`).
|
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`).
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
All contributions are welcome. This is a group effort, and even small contributions can make a difference.
|
All contributions are welcome. This is a group effort, and even small contributions can make a difference.
|
||||||
|
|
1
baseroms/gc-eu-mq-dbg/checksum-compressed.md5
Normal file
1
baseroms/gc-eu-mq-dbg/checksum-compressed.md5
Normal file
|
@ -0,0 +1 @@
|
||||||
|
5831385a7f216370cdbea55616b12fed oot-gc-eu-mq-dbg-compressed.z64
|
|
@ -10,13 +10,13 @@
|
||||||
.balign 16
|
.balign 16
|
||||||
|
|
||||||
glabel gSoundFontTable
|
glabel gSoundFontTable
|
||||||
.incbin "baserom.z64", 0xBCC270, 0x270
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCC270, 0x270
|
||||||
|
|
||||||
glabel gSequenceFontTable
|
glabel gSequenceFontTable
|
||||||
.incbin "baserom.z64", 0xBCC4E0, 0x1C0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCC4E0, 0x1C0
|
||||||
|
|
||||||
glabel gSequenceTable
|
glabel gSequenceTable
|
||||||
.incbin "baserom.z64", 0xBCC6A0, 0x6F0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCC6A0, 0x6F0
|
||||||
|
|
||||||
glabel gSampleBankTable
|
glabel gSampleBankTable
|
||||||
.incbin "baserom.z64", 0xBCCD90, 0x80
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCCD90, 0x80
|
||||||
|
|
|
@ -10,21 +10,21 @@
|
||||||
.balign 16
|
.balign 16
|
||||||
|
|
||||||
glabel aspMainDataStart
|
glabel aspMainDataStart
|
||||||
.incbin "baserom.z64", 0xBCCE10, 0x2E0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCCE10, 0x2E0
|
||||||
glabel aspMainDataEnd
|
glabel aspMainDataEnd
|
||||||
|
|
||||||
glabel gspF3DZEX2_NoN_PosLight_fifoTextStart
|
glabel gspF3DZEX2_NoN_PosLight_fifoTextStart
|
||||||
.incbin "baserom.z64", 0xBCD0F0, 0x1630
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCD0F0, 0x1630
|
||||||
glabel gspF3DZEX2_NoN_PosLight_fifoTextEnd
|
glabel gspF3DZEX2_NoN_PosLight_fifoTextEnd
|
||||||
|
|
||||||
glabel gspF3DZEX2_NoN_PosLight_fifoDataStart
|
glabel gspF3DZEX2_NoN_PosLight_fifoDataStart
|
||||||
.incbin "baserom.z64", 0xBCE720, 0x420
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCE720, 0x420
|
||||||
glabel gspF3DZEX2_NoN_PosLight_fifoDataEnd
|
glabel gspF3DZEX2_NoN_PosLight_fifoDataEnd
|
||||||
|
|
||||||
glabel gspS2DEX2d_fifoDataStart
|
glabel gspS2DEX2d_fifoDataStart
|
||||||
.incbin "baserom.z64", 0xBCEB40, 0x390
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCEB40, 0x390
|
||||||
glabel gspS2DEX2d_fifoDataEnd
|
glabel gspS2DEX2d_fifoDataEnd
|
||||||
|
|
||||||
glabel njpgdspMainDataStart
|
glabel njpgdspMainDataStart
|
||||||
.incbin "baserom.z64", 0xBCEED0, 0x60
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBCEED0, 0x60
|
||||||
glabel njpgdspMainDataEnd
|
glabel njpgdspMainDataEnd
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
.balign 16
|
.balign 16
|
||||||
|
|
||||||
glabel aspMainTextStart
|
glabel aspMainTextStart
|
||||||
.incbin "baserom.z64", 0xB89260, 0xFB0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xB89260, 0xFB0
|
||||||
glabel aspMainTextEnd
|
glabel aspMainTextEnd
|
||||||
|
|
||||||
glabel gspS2DEX2d_fifoTextStart
|
glabel gspS2DEX2d_fifoTextStart
|
||||||
.incbin "baserom.z64", 0xB8A210, 0x18C0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xB8A210, 0x18C0
|
||||||
glabel gspS2DEX2d_fifoTextEnd
|
glabel gspS2DEX2d_fifoTextEnd
|
||||||
|
|
||||||
glabel njpgdspMainTextStart
|
glabel njpgdspMainTextStart
|
||||||
.incbin "baserom.z64", 0xB8BAD0, 0xAF0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xB8BAD0, 0xAF0
|
||||||
glabel njpgdspMainTextEnd
|
glabel njpgdspMainTextEnd
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
.balign 16
|
.balign 16
|
||||||
|
|
||||||
glabel rspbootTextStart
|
glabel rspbootTextStart
|
||||||
.incbin "baserom.z64", 0x9F20, 0xD0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0x9F20, 0xD0
|
||||||
glabel rspbootTextEnd
|
glabel rspbootTextEnd
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
# temporary file name, rename to something more appropriate when decompiled
|
# temporary file name, rename to something more appropriate when decompiled
|
||||||
|
|
||||||
glabel gMojiFontTLUTs
|
glabel gMojiFontTLUTs
|
||||||
.incbin "baserom.z64", 0xBA18E0, 0x80
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBA18E0, 0x80
|
||||||
|
|
||||||
glabel gMojiFontTex
|
glabel gMojiFontTex
|
||||||
.incbin "baserom.z64", 0xBA1960, 0x400
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0xBA1960, 0x400
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
def apply(config, args):
|
def apply(config, args):
|
||||||
config['mapfile'] = 'build/gc-eu-mq-dbg/z64.map'
|
config['mapfile'] = 'build/gc-eu-mq-dbg/z64.map'
|
||||||
config['myimg'] = 'oot-gc-eu-mq-dbg.z64'
|
config['myimg'] = 'oot-gc-eu-mq-dbg.z64'
|
||||||
config['baseimg'] = 'baserom.z64'
|
config['baseimg'] = 'baseroms/gc-eu-mq-dbg/baserom-decompressed.z64'
|
||||||
config['makeflags'] = []
|
config['makeflags'] = []
|
||||||
config['source_directories'] = ['src', 'include', 'spec']
|
config['source_directories'] = ['src', 'include', 'spec']
|
||||||
|
|
|
@ -229,7 +229,7 @@ which initialises common properties of actor using an InitChain, which is usuall
|
||||||
The InitChain script is also in the tools directory, and is called `ichaindis.py`. Simply passing it the ROM address will spit out the entire contents of the InitChain, in this case:
|
The InitChain script is also in the tools directory, and is called `ichaindis.py`. Simply passing it the ROM address will spit out the entire contents of the InitChain, in this case:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./tools/ichaindis.py baserom.z64 80A88CE0
|
$ ./tools/ichaindis.py baseroms/gc-eu-mq-dbg/baserom-decompressed.z64 80A88CE0
|
||||||
static InitChainEntry sInitChain[] = {
|
static InitChainEntry sInitChain[] = {
|
||||||
ICHAIN_VEC3F_DIV1000(unk_50, 87, ICHAIN_CONTINUE),
|
ICHAIN_VEC3F_DIV1000(unk_50, 87, ICHAIN_CONTINUE),
|
||||||
ICHAIN_F32(unk_F4, 4000, ICHAIN_CONTINUE),
|
ICHAIN_F32(unk_F4, 4000, ICHAIN_CONTINUE),
|
||||||
|
@ -240,7 +240,7 @@ static InitChainEntry sInitChain[] = {
|
||||||
|
|
||||||
However, some of these variables have now been given names in the Actor struct. Pass it `--names` to fill these in automatically:
|
However, some of these variables have now been given names in the Actor struct. Pass it `--names` to fill these in automatically:
|
||||||
```
|
```
|
||||||
$ ./tools/ichaindis.py --names baserom.z64 80A88CE0
|
$ ./tools/ichaindis.py --names baseroms/gc-eu-mq-dbg/baserom-decompressed.z64 80A88CE0
|
||||||
static InitChainEntry sInitChain[] = {
|
static InitChainEntry sInitChain[] = {
|
||||||
ICHAIN_VEC3F_DIV1000(scale, 87, ICHAIN_CONTINUE),
|
ICHAIN_VEC3F_DIV1000(scale, 87, ICHAIN_CONTINUE),
|
||||||
ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE),
|
ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE),
|
||||||
|
|
|
@ -557,7 +557,7 @@ Ignore the first line: `gDmaDataTable` is always different if the ROM is shifted
|
||||||
|
|
||||||
To fix this, we use a binary diff program. A suitable one is `vbindiff`: run it on the baserom and the zelda_whatever one the compiler generates:
|
To fix this, we use a binary diff program. A suitable one is `vbindiff`: run it on the baserom and the zelda_whatever one the compiler generates:
|
||||||
```
|
```
|
||||||
vbindiff baserom.z64 oot-gc-eu-mq-dbg.z64
|
vbindiff baseroms/gc-eu-mq-dbg/baserom-decompressed.z64 oot-gc-eu-mq-dbg.z64
|
||||||
```
|
```
|
||||||
In this, press `g` to open up goto position, and paste in the address `0xE3ED10` from the first important line of the `first_diff` output. This gives us the following:
|
In this, press `g` to open up goto position, and paste in the address `0xE3ED10` from the first important line of the `first_diff` output. This gives us the following:
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ Add the following to (or create) the `.vscode/settings.json` file for VSCode to
|
||||||
"search.useIgnoreFiles": false,
|
"search.useIgnoreFiles": false,
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/.git": true,
|
"**/.git": true,
|
||||||
"baserom/**": true,
|
"baseroms/**": true,
|
||||||
"build/**": true,
|
"build/**": true,
|
||||||
"expected/**": true,
|
"expected/**": true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,7 +28,7 @@ def ExtractFile(xmlPath, outputPath, outputSourcePath):
|
||||||
Path(outputPath).mkdir(parents=True, exist_ok=True)
|
Path(outputPath).mkdir(parents=True, exist_ok=True)
|
||||||
Path(outputSourcePath).mkdir(parents=True, exist_ok=True)
|
Path(outputSourcePath).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
execStr = f"{zapdPath} e -eh -i {xmlPath} -b baserom -o {outputPath} -osf {outputSourcePath} -gsf 1 -rconf {configPath} {ZAPDArgs}"
|
execStr = f"{zapdPath} e -eh -i {xmlPath} -b baseroms/gc-eu-mq-dbg/segments -o {outputPath} -osf {outputSourcePath} -gsf 1 -rconf {configPath} {ZAPDArgs}"
|
||||||
|
|
||||||
if "overlays" in xmlPath:
|
if "overlays" in xmlPath:
|
||||||
execStr += " --static"
|
execStr += " --static"
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import struct
|
import struct
|
||||||
from multiprocessing import Pool, cpu_count
|
from multiprocessing import Pool, cpu_count
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
ROM_FILE_NAME = 'baserom.z64'
|
ROM_FILE_PATH = Path('baseroms/gc-eu-mq-dbg/baserom-decompressed.z64')
|
||||||
|
SEGMENTS_PATH = Path('baseroms/gc-eu-mq-dbg/segments/')
|
||||||
FILE_TABLE_OFFSET = 0x12F70
|
FILE_TABLE_OFFSET = 0x12F70
|
||||||
|
|
||||||
FILE_NAMES = [
|
FILE_NAMES = [
|
||||||
|
@ -1562,7 +1562,7 @@ def write_output_file(name, offset, size):
|
||||||
print('failed to write file ' + name)
|
print('failed to write file ' + name)
|
||||||
|
|
||||||
def ExtractFunc(i):
|
def ExtractFunc(i):
|
||||||
filename = 'baserom/' + FILE_NAMES[i]
|
filename = SEGMENTS_PATH / FILE_NAMES[i]
|
||||||
entryOffset = FILE_TABLE_OFFSET + 16 * i
|
entryOffset = FILE_TABLE_OFFSET + 16 * i
|
||||||
|
|
||||||
virtStart = read_uint32_be(entryOffset + 0)
|
virtStart = read_uint32_be(entryOffset + 0)
|
||||||
|
@ -1571,32 +1571,24 @@ def ExtractFunc(i):
|
||||||
physEnd = read_uint32_be(entryOffset + 12)
|
physEnd = read_uint32_be(entryOffset + 12)
|
||||||
|
|
||||||
if physEnd == 0: # uncompressed
|
if physEnd == 0: # uncompressed
|
||||||
compressed = False
|
|
||||||
size = virtEnd - virtStart
|
size = virtEnd - virtStart
|
||||||
else: # compressed
|
else: # compressed
|
||||||
compressed = True
|
|
||||||
size = physEnd - physStart
|
size = physEnd - physStart
|
||||||
|
|
||||||
print('extracting ' + filename + " (0x%08X, 0x%08X)" % (virtStart, virtEnd))
|
print(f'extracting {filename} (0x{virtStart:08X}, 0x{virtEnd:08X})')
|
||||||
write_output_file(filename, physStart, size)
|
write_output_file(filename, physStart, size)
|
||||||
if compressed:
|
|
||||||
os.system('tools/yaz0 -d ' + filename + ' ' + filename)
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
SEGMENTS_PATH.mkdir(parents=True, exist_ok=True)
|
||||||
os.mkdir('baserom')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# read baserom data
|
# read baserom data
|
||||||
try:
|
try:
|
||||||
with open(ROM_FILE_NAME, 'rb') as f:
|
rom_data = ROM_FILE_PATH.read_bytes()
|
||||||
rom_data = f.read()
|
|
||||||
except IOError:
|
except IOError:
|
||||||
print('failed to read file' + ROM_FILE_NAME)
|
print(f'failed to read file {ROM_FILE_PATH}')
|
||||||
sys.exit(1)
|
exit(1)
|
||||||
|
|
||||||
# extract files
|
# extract files
|
||||||
num_cores = cpu_count()
|
num_cores = cpu_count()
|
||||||
|
|
|
@ -49,7 +49,7 @@ def firstDiffMain():
|
||||||
BUILTROM = Path(f"oot-{args.version}.z64")
|
BUILTROM = Path(f"oot-{args.version}.z64")
|
||||||
BUILTMAP = buildFolder / "z64.map"
|
BUILTMAP = buildFolder / "z64.map"
|
||||||
|
|
||||||
EXPECTEDROM = Path("baserom.z64")
|
EXPECTEDROM = Path(f"baseroms/{args.version}/baserom-decompressed.z64")
|
||||||
EXPECTEDMAP = "expected" / BUILTMAP
|
EXPECTEDMAP = "expected" / BUILTMAP
|
||||||
|
|
||||||
mapfile_parser.frontends.first_diff.doFirstDiff(BUILTMAP, EXPECTEDMAP, BUILTROM, EXPECTEDROM, args.count, mismatchSize=True, addColons=args.add_colons, bytesConverterCallback=decodeInstruction)
|
mapfile_parser.frontends.first_diff.doFirstDiff(BUILTMAP, EXPECTEDMAP, BUILTROM, EXPECTEDROM, args.count, mismatchSize=True, addColons=args.add_colons, bytesConverterCallback=decodeInstruction)
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
from os import path
|
|
||||||
import sys
|
|
||||||
import struct
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
|
|
||||||
def get_str_hash(byte_array):
|
|
||||||
return str(hashlib.md5(byte_array).hexdigest())
|
|
||||||
|
|
||||||
|
|
||||||
# If the baserom exists and is correct, we don't need to change anything
|
|
||||||
if path.exists("baserom.z64"):
|
|
||||||
with open("baserom.z64", mode="rb") as file:
|
|
||||||
fileContent = bytearray(file.read())
|
|
||||||
if get_str_hash(fileContent) == "f0b7f35375f9cc8ca1b2d59d78e35405":
|
|
||||||
print("Found valid baserom - exiting early")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Determine if we have a ROM file
|
|
||||||
romFileExtensions = ["z64", "n64", "v64"]
|
|
||||||
|
|
||||||
def find_baserom_original():
|
|
||||||
for romFileExtLower in romFileExtensions:
|
|
||||||
for romFileExt in (romFileExtLower, romFileExtLower.upper()):
|
|
||||||
romFileNameCandidate = "baserom_original." + romFileExt
|
|
||||||
if path.exists(romFileNameCandidate):
|
|
||||||
return romFileNameCandidate
|
|
||||||
return None
|
|
||||||
|
|
||||||
romFileName = find_baserom_original()
|
|
||||||
|
|
||||||
if romFileName is None:
|
|
||||||
print("Error: Could not find baserom_original.z64/baserom_original.n64/baserom_original.v64.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Read in the original ROM
|
|
||||||
print("File '" + romFileName + "' found.")
|
|
||||||
with open(romFileName, mode="rb") as file:
|
|
||||||
fileContent = bytearray(file.read())
|
|
||||||
|
|
||||||
# Strip the overdump
|
|
||||||
print("Stripping overdump...")
|
|
||||||
fileContent = fileContent[0:0x3600000]
|
|
||||||
|
|
||||||
fileContentLen = len(fileContent)
|
|
||||||
|
|
||||||
# Check if ROM needs to be byte/word swapped
|
|
||||||
# Little-endian
|
|
||||||
if fileContent[0] == 0x40:
|
|
||||||
# Word Swap ROM
|
|
||||||
print("ROM needs to be word swapped...")
|
|
||||||
words = str(int(fileContentLen/4))
|
|
||||||
little_byte_format = "<" + words + "I"
|
|
||||||
big_byte_format = ">" + words + "I"
|
|
||||||
tmp = struct.unpack_from(little_byte_format, fileContent, 0)
|
|
||||||
struct.pack_into(big_byte_format, fileContent, 0, *tmp)
|
|
||||||
|
|
||||||
print("Word swapping done.")
|
|
||||||
|
|
||||||
# Byte-swapped
|
|
||||||
elif fileContent[0] == 0x37:
|
|
||||||
# Byte Swap ROM
|
|
||||||
print("ROM needs to be byte swapped...")
|
|
||||||
halfwords = str(int(fileContentLen/2))
|
|
||||||
little_byte_format = "<" + halfwords + "H"
|
|
||||||
big_byte_format = ">" + halfwords + "H"
|
|
||||||
tmp = struct.unpack_from(little_byte_format, fileContent, 0)
|
|
||||||
struct.pack_into(big_byte_format, fileContent, 0, *tmp)
|
|
||||||
|
|
||||||
print("Byte swapping done.")
|
|
||||||
|
|
||||||
# Patch the header
|
|
||||||
print("Patching header...")
|
|
||||||
fileContent[0x3E] = 0x50
|
|
||||||
|
|
||||||
for i in range(0x35CF000, len(fileContent)):
|
|
||||||
fileContent[i] = 0xFF
|
|
||||||
|
|
||||||
# Check to see if the ROM is a "vanilla" Debug ROM
|
|
||||||
str_hash = get_str_hash(bytearray(fileContent))
|
|
||||||
if str_hash != "f0b7f35375f9cc8ca1b2d59d78e35405":
|
|
||||||
print("Error: Expected a hash of f0b7f35375f9cc8ca1b2d59d78e35405 but got " + str_hash + ". " +
|
|
||||||
"The baserom has probably been tampered, find a new one")
|
|
||||||
|
|
||||||
if str_hash == "32fe2770c0f9b1a9cd2a4d449348c1cb":
|
|
||||||
print("The provided baserom is a rom which has been edited with ZeldaEdit and is not suitable for use with decomp. " +
|
|
||||||
"Find a new one.")
|
|
||||||
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Write out our new ROM
|
|
||||||
print("Writing new ROM 'baserom.z64'.")
|
|
||||||
with open("baserom.z64", mode="wb") as file:
|
|
||||||
file.write(bytes(fileContent))
|
|
||||||
|
|
||||||
print("Done!")
|
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Setup and compression
|
||||||
|
crunch64>=0.3.1,<1.0.0
|
||||||
|
ipl3checksum>=1.2.0,<2.0.0
|
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
.section .text
|
.section .text
|
||||||
|
|
||||||
.incbin "baserom.z64", 0x40, 0xFC0
|
.incbin "baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", 0x40, 0xFC0
|
||||||
|
|
|
@ -13,7 +13,7 @@ asm_dir = root_dir + "asm/non_matchings/overlays/actors"
|
||||||
build_dir = root_dir + "build/gc-eu-mq-dbg/"
|
build_dir = root_dir + "build/gc-eu-mq-dbg/"
|
||||||
|
|
||||||
def read_rom():
|
def read_rom():
|
||||||
with open("baserom.z64", "rb") as f:
|
with open("baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", "rb") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
|
342
tools/compress.py
Normal file
342
tools/compress.py
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 zeldaret
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
import dataclasses
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
import multiprocessing
|
||||||
|
import multiprocessing.pool
|
||||||
|
|
||||||
|
import crunch64
|
||||||
|
|
||||||
|
|
||||||
|
STRUCT_IIII = struct.Struct(">IIII")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class DmaEntry:
|
||||||
|
"""
|
||||||
|
A Python counterpart to the dmadata entry struct:
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
/* 0x00 */ uintptr_t vromStart;
|
||||||
|
/* 0x04 */ uintptr_t vromEnd;
|
||||||
|
/* 0x08 */ uintptr_t romStart;
|
||||||
|
/* 0x0C */ uintptr_t romEnd;
|
||||||
|
} DmaEntry;
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
vromStart: int
|
||||||
|
vromEnd: int
|
||||||
|
romStart: int
|
||||||
|
romEnd: int
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
"DmaEntry("
|
||||||
|
f"vromStart=0x{self.vromStart:08X}, "
|
||||||
|
f"vromEnd=0x{self.vromEnd:08X}, "
|
||||||
|
f"romStart=0x{self.romStart:08X}, "
|
||||||
|
f"romEnd=0x{self.romEnd:08X}"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
SIZE_BYTES = STRUCT_IIII.size
|
||||||
|
|
||||||
|
def to_bin(self, data: memoryview):
|
||||||
|
STRUCT_IIII.pack_into(
|
||||||
|
data,
|
||||||
|
0,
|
||||||
|
self.vromStart,
|
||||||
|
self.vromEnd,
|
||||||
|
self.romStart,
|
||||||
|
self.romEnd,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_bin(data: memoryview):
|
||||||
|
return DmaEntry(*STRUCT_IIII.unpack_from(data))
|
||||||
|
|
||||||
|
|
||||||
|
DMA_ENTRY_ZERO = DmaEntry(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def align(v: int):
|
||||||
|
v += 0xF
|
||||||
|
return v // 0x10 * 0x10
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class RomSegment:
|
||||||
|
vromStart: int
|
||||||
|
vromEnd: int
|
||||||
|
is_compressed: bool
|
||||||
|
data: memoryview | None
|
||||||
|
data_async: multiprocessing.pool.AsyncResult | None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uncompressed_size(self):
|
||||||
|
return self.vromEnd - self.vromStart
|
||||||
|
|
||||||
|
|
||||||
|
# Make interrupting the compression with ^C less jank
|
||||||
|
# https://stackoverflow.com/questions/72967793/keyboardinterrupt-with-python-multiprocessing-pool
|
||||||
|
def set_sigint_ignored():
|
||||||
|
import signal
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
|
|
||||||
|
|
||||||
|
def compress_rom(
|
||||||
|
rom_data: memoryview,
|
||||||
|
dmadata_offset_start: int,
|
||||||
|
dmadata_offset_end: int,
|
||||||
|
compress_entries_indices: set[int],
|
||||||
|
n_threads: int = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
rom_data: the uncompressed rom data
|
||||||
|
dmadata_offset_start: the offset in the rom where the dmadata starts (inclusive)
|
||||||
|
dmadata_offset_end: the offset in the rom where the dmadata ends (exclusive)
|
||||||
|
compress_entries_indices: the indices in the dmadata of the segments that should be compressed
|
||||||
|
n_threads: how many cores to use for compression
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Segments of the compressed rom (not all are compressed)
|
||||||
|
compressed_rom_segments: list[RomSegment] = []
|
||||||
|
|
||||||
|
with multiprocessing.Pool(n_threads, initializer=set_sigint_ignored) as p:
|
||||||
|
# Extract each segment from the input rom
|
||||||
|
for entry_index, dmadata_offset in enumerate(
|
||||||
|
range(dmadata_offset_start, dmadata_offset_end, DmaEntry.SIZE_BYTES)
|
||||||
|
):
|
||||||
|
dma_entry = DmaEntry.from_bin(rom_data[dmadata_offset:])
|
||||||
|
if dma_entry == DMA_ENTRY_ZERO:
|
||||||
|
continue
|
||||||
|
|
||||||
|
segment_rom_start = dma_entry.romStart
|
||||||
|
segment_rom_end = dma_entry.romStart + (
|
||||||
|
dma_entry.vromEnd - dma_entry.vromStart
|
||||||
|
)
|
||||||
|
segment_data_uncompressed = rom_data[segment_rom_start:segment_rom_end]
|
||||||
|
|
||||||
|
is_compressed = entry_index in compress_entries_indices
|
||||||
|
|
||||||
|
if is_compressed:
|
||||||
|
segment_data = None
|
||||||
|
segment_data_async = p.apply_async(
|
||||||
|
crunch64.yaz0.compress,
|
||||||
|
(bytes(segment_data_uncompressed),),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
segment_data = segment_data_uncompressed
|
||||||
|
segment_data_async = None
|
||||||
|
|
||||||
|
compressed_rom_segments.append(
|
||||||
|
RomSegment(
|
||||||
|
dma_entry.vromStart,
|
||||||
|
dma_entry.vromEnd,
|
||||||
|
is_compressed,
|
||||||
|
segment_data,
|
||||||
|
segment_data_async,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Technically optional but required for matching.
|
||||||
|
compressed_rom_segments.sort(key=lambda segment: segment.vromStart)
|
||||||
|
|
||||||
|
# Wait on compression of all compressed segments
|
||||||
|
waiting_on_segments = [
|
||||||
|
segment for segment in compressed_rom_segments if segment.is_compressed
|
||||||
|
]
|
||||||
|
total_uncompressed_size_of_data_to_compress = sum(
|
||||||
|
segment.uncompressed_size for segment in waiting_on_segments
|
||||||
|
)
|
||||||
|
uncompressed_size_of_data_compressed_so_far = 0
|
||||||
|
while waiting_on_segments:
|
||||||
|
# Show progress
|
||||||
|
progress = (
|
||||||
|
uncompressed_size_of_data_compressed_so_far
|
||||||
|
/ total_uncompressed_size_of_data_to_compress
|
||||||
|
)
|
||||||
|
print(f"Compressing... {progress * 100:.1f}%", end="\r")
|
||||||
|
|
||||||
|
# The segments for which the compression is not finished yet are
|
||||||
|
# added to this list
|
||||||
|
still_waiting_on_segments = []
|
||||||
|
got_some_results = False
|
||||||
|
for segment in waiting_on_segments:
|
||||||
|
assert segment.data is None
|
||||||
|
assert segment.data_async is not None
|
||||||
|
|
||||||
|
try:
|
||||||
|
compressed_data = segment.data_async.get(0)
|
||||||
|
except multiprocessing.TimeoutError:
|
||||||
|
# Compression not finished yet
|
||||||
|
still_waiting_on_segments.append(segment)
|
||||||
|
else:
|
||||||
|
# Compression finished!
|
||||||
|
assert isinstance(compressed_data, bytes)
|
||||||
|
segment.data = memoryview(compressed_data)
|
||||||
|
uncompressed_size_of_data_compressed_so_far += (
|
||||||
|
segment.uncompressed_size
|
||||||
|
)
|
||||||
|
got_some_results = True
|
||||||
|
segment.data_async = None
|
||||||
|
|
||||||
|
if not got_some_results and still_waiting_on_segments:
|
||||||
|
# Nothing happened this wait iteration, idle a bit
|
||||||
|
time.sleep(0.010)
|
||||||
|
|
||||||
|
waiting_on_segments = still_waiting_on_segments
|
||||||
|
|
||||||
|
print("Putting together the compressed rom...")
|
||||||
|
|
||||||
|
# Put together the compressed rom
|
||||||
|
compressed_rom_size = sum(
|
||||||
|
align(len(segment.data)) for segment in compressed_rom_segments
|
||||||
|
)
|
||||||
|
pad_to_multiple_of = 8 * 2**20 # 8 MiB
|
||||||
|
compressed_rom_size_padded = (
|
||||||
|
(compressed_rom_size + pad_to_multiple_of - 1)
|
||||||
|
// pad_to_multiple_of
|
||||||
|
* pad_to_multiple_of
|
||||||
|
)
|
||||||
|
compressed_rom_data = memoryview(bytearray(compressed_rom_size_padded))
|
||||||
|
compressed_rom_dma_entries: list[DmaEntry] = []
|
||||||
|
rom_offset = 0
|
||||||
|
for segment in compressed_rom_segments:
|
||||||
|
assert segment.data is not None
|
||||||
|
|
||||||
|
segment_rom_start = rom_offset
|
||||||
|
segment_rom_end = align(segment_rom_start + len(segment.data))
|
||||||
|
|
||||||
|
i = segment_rom_start + len(segment.data)
|
||||||
|
assert i <= len(compressed_rom_data)
|
||||||
|
compressed_rom_data[segment_rom_start:i] = segment.data
|
||||||
|
|
||||||
|
compressed_rom_dma_entries.append(
|
||||||
|
DmaEntry(
|
||||||
|
segment.vromStart,
|
||||||
|
segment.vromEnd,
|
||||||
|
segment_rom_start,
|
||||||
|
segment_rom_end if segment.is_compressed else 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rom_offset = segment_rom_end
|
||||||
|
|
||||||
|
assert rom_offset == compressed_rom_size
|
||||||
|
# Pad the compressed rom with the pattern matching the baseroms
|
||||||
|
for i in range(compressed_rom_size, compressed_rom_size_padded):
|
||||||
|
compressed_rom_data[i] = i % 256
|
||||||
|
|
||||||
|
# Write the new dmadata
|
||||||
|
dmadata_offset = dmadata_offset_start
|
||||||
|
for dma_entry in compressed_rom_dma_entries:
|
||||||
|
assert dmadata_offset + DmaEntry.SIZE_BYTES <= dmadata_offset_end
|
||||||
|
|
||||||
|
dma_entry.to_bin(compressed_rom_data[dmadata_offset:])
|
||||||
|
|
||||||
|
dmadata_offset += DmaEntry.SIZE_BYTES
|
||||||
|
|
||||||
|
return compressed_rom_data
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--in",
|
||||||
|
dest="in_rom",
|
||||||
|
required=True,
|
||||||
|
help="path to an uncompressed rom to be compressed",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--out",
|
||||||
|
dest="out_rom",
|
||||||
|
required=True,
|
||||||
|
help="path of the compressed rom to write out",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dma-range",
|
||||||
|
dest="dma_range",
|
||||||
|
required=True,
|
||||||
|
help=(
|
||||||
|
"The dmadata location in the rom, in format"
|
||||||
|
" 'start_inclusive-end_exclusive' and using hexadecimal offsets"
|
||||||
|
" (e.g. '0x12f70-0x19030')."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--compress",
|
||||||
|
dest="compress_ranges",
|
||||||
|
required=True,
|
||||||
|
help=(
|
||||||
|
"The indices in the dmadata of the entries to be compressed,"
|
||||||
|
" where 0 is the first entry."
|
||||||
|
" It is a comma-separated list of individual indices and inclusive ranges."
|
||||||
|
" e.g. '0-1,3,5,6-9' is all indices from 0 to 9 (included) except 2 and 4."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--threads",
|
||||||
|
dest="n_threads",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help="how many cores to use for parallel compression",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
in_rom_p = Path(args.in_rom)
|
||||||
|
if not in_rom_p.exists():
|
||||||
|
parser.error(f"Input rom file {in_rom_p} doesn't exist.")
|
||||||
|
|
||||||
|
out_rom_p = Path(args.out_rom)
|
||||||
|
|
||||||
|
dma_range_str: str = args.dma_range
|
||||||
|
dma_range_ends_str = dma_range_str.split("-")
|
||||||
|
assert len(dma_range_ends_str) == 2, dma_range_str
|
||||||
|
dmadata_offset_start, dmadata_offset_end = (
|
||||||
|
int(v_str, 16) for v_str in dma_range_ends_str
|
||||||
|
)
|
||||||
|
assert dmadata_offset_start < dmadata_offset_end, dma_range_str
|
||||||
|
|
||||||
|
compress_ranges_str: str = args.compress_ranges
|
||||||
|
compress_entries_indices = set()
|
||||||
|
for compress_range_str in compress_ranges_str.split(","):
|
||||||
|
compress_range_ends_str = compress_range_str.split("-")
|
||||||
|
assert len(compress_range_ends_str) <= 2, (
|
||||||
|
compress_range_ends_str,
|
||||||
|
compress_range_str,
|
||||||
|
compress_ranges_str,
|
||||||
|
)
|
||||||
|
compress_range_ends = [int(v_str) for v_str in compress_range_ends_str]
|
||||||
|
if len(compress_range_ends) == 1:
|
||||||
|
compress_entries_indices.add(compress_range_ends[0])
|
||||||
|
else:
|
||||||
|
assert len(compress_range_ends) == 2
|
||||||
|
compress_range_first, compress_range_last = compress_range_ends
|
||||||
|
compress_entries_indices.update(
|
||||||
|
range(compress_range_first, compress_range_last + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
n_threads = args.n_threads
|
||||||
|
|
||||||
|
in_rom_data = in_rom_p.read_bytes()
|
||||||
|
out_rom_data = compress_rom(
|
||||||
|
memoryview(in_rom_data),
|
||||||
|
dmadata_offset_start,
|
||||||
|
dmadata_offset_end,
|
||||||
|
compress_entries_indices,
|
||||||
|
n_threads,
|
||||||
|
)
|
||||||
|
out_rom_p.write_bytes(out_rom_data)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -496,7 +496,7 @@ def main():
|
||||||
|
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
cs_data = None
|
cs_data = None
|
||||||
with open(script_dir + "/../baserom/" + file_result.name, "rb") as ovl_file:
|
with open(script_dir + "/../baseroms/gc-eu-mq-dbg/segments/" + file_result.name, "rb") as ovl_file:
|
||||||
ovl_file.seek(file_result.offset)
|
ovl_file.seek(file_result.offset)
|
||||||
cs_data = [i[0] for i in struct.iter_unpack(">I", bytearray(ovl_file.read()))]
|
cs_data = [i[0] for i in struct.iter_unpack(">I", bytearray(ovl_file.read()))]
|
||||||
if cs_data is not None:
|
if cs_data is not None:
|
||||||
|
|
273
tools/decompress_baserom.py
Executable file
273
tools/decompress_baserom.py
Executable file
|
@ -0,0 +1,273 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: © 2024 ZeldaRET
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
import struct
|
||||||
|
from pathlib import Path
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
import crunch64
|
||||||
|
import ipl3checksum
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
|
||||||
|
def decompress_zlib(data: bytes) -> bytes:
|
||||||
|
decomp = zlib.decompressobj(-zlib.MAX_WBITS)
|
||||||
|
output = bytearray()
|
||||||
|
output.extend(decomp.decompress(data))
|
||||||
|
while decomp.unconsumed_tail:
|
||||||
|
output.extend(decomp.decompress(decomp.unconsumed_tail))
|
||||||
|
output.extend(decomp.flush())
|
||||||
|
return bytes(output)
|
||||||
|
|
||||||
|
|
||||||
|
def decompress(data: bytes, is_zlib_compressed: bool) -> bytes:
|
||||||
|
if is_zlib_compressed:
|
||||||
|
return decompress_zlib(data)
|
||||||
|
return crunch64.yaz0.decompress(data)
|
||||||
|
|
||||||
|
|
||||||
|
FILE_TABLE_OFFSET = {
|
||||||
|
"gc-eu-mq-dbg": 0x12F70,
|
||||||
|
}
|
||||||
|
|
||||||
|
VERSIONS_MD5S = {
|
||||||
|
"gc-eu-mq-dbg": "f0b7f35375f9cc8ca1b2d59d78e35405",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def round_up(n, shift):
|
||||||
|
mod = 1 << shift
|
||||||
|
return (n + mod - 1) >> shift << shift
|
||||||
|
|
||||||
|
|
||||||
|
def as_word_list(b) -> list[int]:
|
||||||
|
return [i[0] for i in struct.iter_unpack(">I", b)]
|
||||||
|
|
||||||
|
|
||||||
|
def read_dmadata_entry(file_content: bytearray, addr: int) -> list[int]:
|
||||||
|
return as_word_list(file_content[addr : addr + 0x10])
|
||||||
|
|
||||||
|
|
||||||
|
def read_dmadata(file_content: bytearray, start) -> list[list[int]]:
|
||||||
|
dmadata = []
|
||||||
|
addr = start
|
||||||
|
entry = read_dmadata_entry(file_content, addr)
|
||||||
|
i = 0
|
||||||
|
while any([e != 0 for e in entry]):
|
||||||
|
dmadata.append(entry)
|
||||||
|
addr += 0x10
|
||||||
|
i += 1
|
||||||
|
entry = read_dmadata_entry(file_content, addr)
|
||||||
|
return dmadata
|
||||||
|
|
||||||
|
|
||||||
|
def update_crc(decompressed: io.BytesIO) -> io.BytesIO:
|
||||||
|
print("Recalculating crc...")
|
||||||
|
calculated_checksum = ipl3checksum.CICKind.CIC_X105.calculateChecksum(
|
||||||
|
bytes(decompressed.getbuffer())
|
||||||
|
)
|
||||||
|
new_crc = struct.pack(f">II", calculated_checksum[0], calculated_checksum[1])
|
||||||
|
|
||||||
|
decompressed.seek(0x10)
|
||||||
|
decompressed.write(new_crc)
|
||||||
|
return decompressed
|
||||||
|
|
||||||
|
|
||||||
|
def decompress_rom(
|
||||||
|
file_content: bytearray, dmadata_addr: int, dmadata: list[list[int]], version: str
|
||||||
|
) -> bytearray:
|
||||||
|
rom_segments = {} # vrom start : data s.t. len(data) == vrom_end - vrom_start
|
||||||
|
new_dmadata = bytearray() # new dmadata: {vrom start , vrom end , vrom start , 0}
|
||||||
|
|
||||||
|
decompressed = io.BytesIO(b"")
|
||||||
|
|
||||||
|
for v_start, v_end, p_start, p_end in dmadata:
|
||||||
|
if p_start == 0xFFFFFFFF and p_end == 0xFFFFFFFF:
|
||||||
|
new_dmadata.extend(struct.pack(">IIII", v_start, v_end, p_start, p_end))
|
||||||
|
continue
|
||||||
|
if p_end == 0: # uncompressed
|
||||||
|
rom_segments.update(
|
||||||
|
{v_start: file_content[p_start : p_start + v_end - v_start]}
|
||||||
|
)
|
||||||
|
else: # compressed
|
||||||
|
rom_segments.update(
|
||||||
|
{
|
||||||
|
v_start: decompress(
|
||||||
|
file_content[p_start:p_end], version in {"ique-cn", "ique-zh"}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
new_dmadata.extend(struct.pack(">IIII", v_start, v_end, v_start, 0))
|
||||||
|
|
||||||
|
# write rom segments to vaddrs
|
||||||
|
for vrom_st, data in rom_segments.items():
|
||||||
|
decompressed.seek(vrom_st)
|
||||||
|
decompressed.write(data)
|
||||||
|
# write new dmadata
|
||||||
|
decompressed.seek(dmadata_addr)
|
||||||
|
decompressed.write(new_dmadata)
|
||||||
|
# pad to size
|
||||||
|
padding_end = round_up(dmadata[-1][1], 14)
|
||||||
|
decompressed.seek(padding_end - 1)
|
||||||
|
decompressed.write(bytearray([0]))
|
||||||
|
# re-calculate crc
|
||||||
|
return bytearray(update_crc(decompressed).getbuffer())
|
||||||
|
|
||||||
|
|
||||||
|
def get_str_hash(byte_array):
|
||||||
|
return str(hashlib.md5(byte_array).hexdigest())
|
||||||
|
|
||||||
|
|
||||||
|
def check_existing_rom(rom_path: Path, correct_str_hash: str):
|
||||||
|
# If the baserom exists and is correct, we don't need to change anything
|
||||||
|
if rom_path.exists():
|
||||||
|
if get_str_hash(rom_path.read_bytes()) == correct_str_hash:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def word_swap(file_content: bytearray) -> bytearray:
|
||||||
|
words = str(int(len(file_content) / 4))
|
||||||
|
little_byte_format = "<" + words + "I"
|
||||||
|
big_byte_format = ">" + words + "I"
|
||||||
|
tmp = struct.unpack_from(little_byte_format, file_content, 0)
|
||||||
|
struct.pack_into(big_byte_format, file_content, 0, *tmp)
|
||||||
|
return file_content
|
||||||
|
|
||||||
|
|
||||||
|
def byte_swap(file_content: bytearray) -> bytearray:
|
||||||
|
halfwords = str(int(len(file_content) / 2))
|
||||||
|
little_byte_format = "<" + halfwords + "H"
|
||||||
|
big_byte_format = ">" + halfwords + "H"
|
||||||
|
tmp = struct.unpack_from(little_byte_format, file_content, 0)
|
||||||
|
struct.pack_into(big_byte_format, file_content, 0, *tmp)
|
||||||
|
return file_content
|
||||||
|
|
||||||
|
|
||||||
|
def per_version_fixes(file_content: bytearray, version: str) -> bytearray:
|
||||||
|
if version == "gc-eu-mq-dbg":
|
||||||
|
# Strip the overdump
|
||||||
|
print("Stripping overdump...")
|
||||||
|
file_content = file_content[0:0x3600000]
|
||||||
|
|
||||||
|
# Patch the header
|
||||||
|
print("Patching header...")
|
||||||
|
file_content[0x3E] = 0x50
|
||||||
|
return file_content
|
||||||
|
|
||||||
|
|
||||||
|
def pad_rom(file_content: bytearray, dmadata: list[list[int]]) -> bytearray:
|
||||||
|
padding_start = round_up(dmadata[-1][1], 12)
|
||||||
|
padding_end = round_up(dmadata[-1][1], 14)
|
||||||
|
print(f"Padding from {padding_start:X} to {padding_end:X}...")
|
||||||
|
for i in range(padding_start, padding_end):
|
||||||
|
file_content[i] = 0xFF
|
||||||
|
return file_content
|
||||||
|
|
||||||
|
# Determine if we have a ROM file
|
||||||
|
ROM_FILE_EXTENSIONS = ["z64", "n64", "v64"]
|
||||||
|
|
||||||
|
def find_baserom(version: str) -> Path | None:
|
||||||
|
for rom_file_ext_lower in ROM_FILE_EXTENSIONS:
|
||||||
|
for rom_file_ext in (rom_file_ext_lower, rom_file_ext_lower.upper()):
|
||||||
|
rom_file_name_candidate = Path(f"baseroms/{version}/baserom.{rom_file_ext}")
|
||||||
|
if rom_file_name_candidate.exists():
|
||||||
|
return rom_file_name_candidate
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
description = "Convert a rom that uses dmadata to an uncompressed one."
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description=description)
|
||||||
|
parser.add_argument("version", help="Version of the game to decompress.", choices=list(VERSIONS_MD5S.keys()))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
version = args.version
|
||||||
|
|
||||||
|
uncompressed_path = Path(f"baseroms/{version}/baserom-decompressed.z64")
|
||||||
|
|
||||||
|
file_table_offset = FILE_TABLE_OFFSET[version]
|
||||||
|
correct_str_hash = VERSIONS_MD5S[version]
|
||||||
|
|
||||||
|
if check_existing_rom(uncompressed_path, correct_str_hash):
|
||||||
|
print("Found valid baserom - exiting early")
|
||||||
|
return
|
||||||
|
|
||||||
|
rom_file_name = find_baserom(version)
|
||||||
|
|
||||||
|
if rom_file_name is None:
|
||||||
|
path_list = [
|
||||||
|
f"baseroms/{version}/baserom.{rom_file_ext}" for rom_file_ext in ROM_FILE_EXTENSIONS
|
||||||
|
]
|
||||||
|
print(f"Error: Could not find {','.join(path_list)}.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Read in the original ROM
|
||||||
|
print(f"File '{rom_file_name}' found.")
|
||||||
|
|
||||||
|
file_content = bytearray(rom_file_name.read_bytes())
|
||||||
|
|
||||||
|
# Check if ROM needs to be byte/word swapped
|
||||||
|
# Little-endian
|
||||||
|
if file_content[0] == 0x40:
|
||||||
|
# Word Swap ROM
|
||||||
|
print("ROM needs to be word swapped...")
|
||||||
|
file_content = word_swap(file_content)
|
||||||
|
print("Word swapping done.")
|
||||||
|
|
||||||
|
# Byte-swapped
|
||||||
|
elif file_content[0] == 0x37:
|
||||||
|
# Byte Swap ROM
|
||||||
|
print("ROM needs to be byte swapped...")
|
||||||
|
file_content = byte_swap(file_content)
|
||||||
|
print("Byte swapping done.")
|
||||||
|
|
||||||
|
file_content = per_version_fixes(file_content, version)
|
||||||
|
|
||||||
|
dmadata = read_dmadata(file_content, file_table_offset)
|
||||||
|
# Decompress
|
||||||
|
if any(
|
||||||
|
[
|
||||||
|
b != 0
|
||||||
|
for b in file_content[
|
||||||
|
file_table_offset + 0xAC : file_table_offset + 0xAC + 0x4
|
||||||
|
]
|
||||||
|
]
|
||||||
|
):
|
||||||
|
print("Decompressing rom...")
|
||||||
|
file_content = decompress_rom(file_content, file_table_offset, dmadata, version)
|
||||||
|
|
||||||
|
file_content = pad_rom(file_content, dmadata)
|
||||||
|
|
||||||
|
# Check to see if the ROM is a "vanilla" ROM
|
||||||
|
str_hash = get_str_hash(file_content)
|
||||||
|
if str_hash != correct_str_hash:
|
||||||
|
print(
|
||||||
|
f"Error: Expected a hash of {correct_str_hash} but got {str_hash}. The baserom has probably been tampered, find a new one"
|
||||||
|
)
|
||||||
|
|
||||||
|
if version == "gc-eu-mq-dbg":
|
||||||
|
if str_hash == "32fe2770c0f9b1a9cd2a4d449348c1cb":
|
||||||
|
print(
|
||||||
|
"The provided baserom is a rom which has been edited with ZeldaEdit and is not suitable for use with decomp. Find a new one."
|
||||||
|
)
|
||||||
|
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Write out our new ROM
|
||||||
|
print(f"Writing new ROM {uncompressed_path}.")
|
||||||
|
uncompressed_path.write_bytes(file_content)
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -166,7 +166,7 @@ item_ids = {
|
||||||
|
|
||||||
def disas_elfmsgs(start):
|
def disas_elfmsgs(start):
|
||||||
baserom = None
|
baserom = None
|
||||||
with open("baserom.z64", "rb") as infile:
|
with open("baseroms/gc-eu-mq-dbg/baserom-decompressed.z64", "rb") as infile:
|
||||||
baserom = bytearray(infile.read())
|
baserom = bytearray(infile.read())
|
||||||
|
|
||||||
branches = []
|
branches = []
|
||||||
|
|
16
tools/dmadata_range.sh
Executable file
16
tools/dmadata_range.sh
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
NM=$1
|
||||||
|
ELF=$2
|
||||||
|
|
||||||
|
# dmadata_syms contents look like:
|
||||||
|
# ffffffff80015850 T _dmadataSegmentTextStart
|
||||||
|
# 00000000 A _dmadataSegmentTextSize
|
||||||
|
# 00011a40 A _dmadataSegmentRomStart
|
||||||
|
# ffffffff8001b920 T _dmadataSegmentRoDataEnd
|
||||||
|
dmadata_syms=`$NM $ELF --no-sort --radix=x --format=bsd | grep dmadata`
|
||||||
|
|
||||||
|
_dmadataSegmentRomStart=`echo "$dmadata_syms" | grep '\b_dmadataSegmentRomStart\b' | cut -d' ' -f1`
|
||||||
|
_dmadataSegmentRomEnd=` echo "$dmadata_syms" | grep '\b_dmadataSegmentRomEnd\b' | cut -d' ' -f1`
|
||||||
|
|
||||||
|
echo 0x"$_dmadataSegmentRomStart"-0x"$_dmadataSegmentRomEnd"
|
|
@ -20,22 +20,53 @@ static void write_dmadata_table(FILE *fout)
|
||||||
fprintf(fout, "DEFINE_DMA_ENTRY(%s, \"%s\")\n", g_segments[i].name, g_segments[i].name);
|
fprintf(fout, "DEFINE_DMA_ENTRY(%s, \"%s\")\n", g_segments[i].name, g_segments[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void write_compress_ranges(FILE *fout)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
bool continue_list = false;
|
||||||
|
int stride_first = -1;
|
||||||
|
|
||||||
|
for (i = 0; i < g_segmentsCount; i++) {
|
||||||
|
if (g_segments[i].compress) {
|
||||||
|
if (stride_first == -1)
|
||||||
|
stride_first = i;
|
||||||
|
}
|
||||||
|
if (!g_segments[i].compress || i == g_segmentsCount - 1) {
|
||||||
|
if (stride_first != -1) {
|
||||||
|
int stride_last = i - 1;
|
||||||
|
if (continue_list) {
|
||||||
|
fprintf(fout, ",");
|
||||||
|
}
|
||||||
|
if (stride_first == stride_last) {
|
||||||
|
fprintf(fout, "%d", stride_first);
|
||||||
|
} else {
|
||||||
|
fprintf(fout, "%d-%d", stride_first, stride_last);
|
||||||
|
}
|
||||||
|
continue_list = true;
|
||||||
|
stride_first = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void usage(const char *execname)
|
static void usage(const char *execname)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "zelda64 dmadata generation tool v0.01\n"
|
fprintf(stderr, "zelda64 dmadata generation tool v0.01\n"
|
||||||
"usage: %s SPEC_FILE DMADATA_TABLE\n"
|
"usage: %s SPEC_FILE DMADATA_TABLE COMPRESS_RANGES\n"
|
||||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||||
"DMADATA_TABLE filename of output dmadata table header\n",
|
"DMADATA_TABLE filename of output dmadata table header\n",
|
||||||
|
"COMPRESS_RANGES filename to write which files are compressed (e.g. 0-5,7,10-20)\n",
|
||||||
execname);
|
execname);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
FILE *dmaout;
|
FILE *dmaout;
|
||||||
|
FILE *compress_ranges_out;
|
||||||
void *spec;
|
void *spec;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
if (argc != 3)
|
if (argc != 4)
|
||||||
{
|
{
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -49,6 +80,13 @@ int main(int argc, char **argv)
|
||||||
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
||||||
write_dmadata_table(dmaout);
|
write_dmadata_table(dmaout);
|
||||||
fclose(dmaout);
|
fclose(dmaout);
|
||||||
|
|
||||||
|
compress_ranges_out = fopen(argv[3], "w");
|
||||||
|
if (compress_ranges_out == NULL)
|
||||||
|
util_fatal_error("failed to open file '%s' for writing", argv[3]);
|
||||||
|
write_compress_ranges(compress_ranges_out);
|
||||||
|
fclose(compress_ranges_out);
|
||||||
|
|
||||||
free_rom_spec(g_segments, g_segmentsCount);
|
free_rom_spec(g_segments, g_segmentsCount);
|
||||||
free(spec);
|
free(spec);
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ def read_tables():
|
||||||
global staff_message_entry_table
|
global staff_message_entry_table
|
||||||
|
|
||||||
baserom = None
|
baserom = None
|
||||||
with open("baserom.z64","rb") as infile:
|
with open("baseroms/gc-eu-mq-dbg/baserom-decompressed.z64","rb") as infile:
|
||||||
baserom = infile.read()
|
baserom = infile.read()
|
||||||
|
|
||||||
nes_message_entry_table = as_message_table_entry(baserom[nes_message_entry_table_addr:ger_message_entry_table_addr])
|
nes_message_entry_table = as_message_table_entry(baserom[nes_message_entry_table_addr:ger_message_entry_table_addr])
|
||||||
|
@ -326,7 +326,7 @@ def dump_all_text():
|
||||||
nes_offset = segmented_to_physical(entry[3])
|
nes_offset = segmented_to_physical(entry[3])
|
||||||
nes_length = next_entry[3] - entry[3]
|
nes_length = next_entry[3] - entry[3]
|
||||||
nes_text = ""
|
nes_text = ""
|
||||||
with open("baserom/nes_message_data_static","rb") as infile:
|
with open("baseroms/gc-eu-mq-dbg/segments/nes_message_data_static","rb") as infile:
|
||||||
infile.seek(nes_offset)
|
infile.seek(nes_offset)
|
||||||
nes_text = fixup_message(decode(infile.read(nes_length), entry[1]).replace("\x00","",-1))
|
nes_text = fixup_message(decode(infile.read(nes_length), entry[1]).replace("\x00","",-1))
|
||||||
|
|
||||||
|
@ -337,13 +337,13 @@ def dump_all_text():
|
||||||
next_entry = combined_message_entry_table[i+2]
|
next_entry = combined_message_entry_table[i+2]
|
||||||
ger_offset = segmented_to_physical(entry[4])
|
ger_offset = segmented_to_physical(entry[4])
|
||||||
ger_length = next_entry[4] - entry[4]
|
ger_length = next_entry[4] - entry[4]
|
||||||
with open("baserom/ger_message_data_static","rb") as infile:
|
with open("baseroms/gc-eu-mq-dbg/segments/ger_message_data_static","rb") as infile:
|
||||||
infile.seek(ger_offset)
|
infile.seek(ger_offset)
|
||||||
ger_text = fixup_message(decode(infile.read(ger_length), entry[1]).replace("\x00","",-1))
|
ger_text = fixup_message(decode(infile.read(ger_length), entry[1]).replace("\x00","",-1))
|
||||||
|
|
||||||
fra_offset = segmented_to_physical(entry[5])
|
fra_offset = segmented_to_physical(entry[5])
|
||||||
fra_length = next_entry[5] - entry[5]
|
fra_length = next_entry[5] - entry[5]
|
||||||
with open("baserom/fra_message_data_static","rb") as infile:
|
with open("baseroms/gc-eu-mq-dbg/segments/fra_message_data_static","rb") as infile:
|
||||||
infile.seek(fra_offset)
|
infile.seek(fra_offset)
|
||||||
fra_text = fixup_message(decode(infile.read(fra_length), entry[1]).replace("\x00","",-1))
|
fra_text = fixup_message(decode(infile.read(fra_length), entry[1]).replace("\x00","",-1))
|
||||||
|
|
||||||
|
@ -352,7 +352,7 @@ def dump_all_text():
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
def dump_staff_text():
|
def dump_staff_text():
|
||||||
staff_message_data_static_size = path.getsize("baserom/staff_message_data_static")
|
staff_message_data_static_size = path.getsize("baseroms/gc-eu-mq-dbg/segments/staff_message_data_static")
|
||||||
# text id, ypos, type, staff
|
# text id, ypos, type, staff
|
||||||
messages = []
|
messages = []
|
||||||
for i,entry in enumerate(staff_message_entry_table,0):
|
for i,entry in enumerate(staff_message_entry_table,0):
|
||||||
|
@ -361,7 +361,7 @@ def dump_staff_text():
|
||||||
staff_offset = segmented_to_physical(entry[3])
|
staff_offset = segmented_to_physical(entry[3])
|
||||||
# hacky way to ensure the staff message entry table is read all the way to the end
|
# hacky way to ensure the staff message entry table is read all the way to the end
|
||||||
staff_length = (staff_message_data_static_size if entry[0] == 0x052F else segmented_to_physical(next_entry[3])) - segmented_to_physical(entry[3])
|
staff_length = (staff_message_data_static_size if entry[0] == 0x052F else segmented_to_physical(next_entry[3])) - segmented_to_physical(entry[3])
|
||||||
with open("baserom/staff_message_data_static","rb") as infile:
|
with open("baseroms/gc-eu-mq-dbg/segments/staff_message_data_static","rb") as infile:
|
||||||
infile.seek(staff_offset)
|
infile.seek(staff_offset)
|
||||||
messages.append((entry[0], entry[1], entry[2], fixup_message(decode(infile.read(staff_length), entry[1]).replace("\x00","",-1))))
|
messages.append((entry[0], entry[1], entry[2], fixup_message(decode(infile.read(staff_length), entry[1]).replace("\x00","",-1))))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -387,7 +387,7 @@ def GetColliderInit(address, type, num, path):
|
||||||
else:
|
else:
|
||||||
return("ItemInit type must specify number of elements")
|
return("ItemInit type must specify number of elements")
|
||||||
|
|
||||||
ovlFile = open(path + "/baserom/" + fileResult.name, "rb")
|
ovlFile = open(path + "/baseroms/gc-eu-mq-dbg/segments/" + fileResult.name, "rb")
|
||||||
ovlData = bytearray(ovlFile.read())
|
ovlData = bytearray(ovlFile.read())
|
||||||
ovlFile.close()
|
ovlFile.close()
|
||||||
|
|
||||||
|
@ -470,7 +470,7 @@ print(GetColliderInitFull(args.address, args.type, args.num, repo))
|
||||||
|
|
||||||
# script_dir = os.path.dirname(os.path.realpath(__file__))
|
# script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
# ovlFile = open(script_dir + "/../../baserom/" + fileResult.name, "rb")
|
# ovlFile = open(script_dir + "/../../baseroms/" + fileResult.name, "rb")
|
||||||
# ovlData = bytearray(ovlFile.read())
|
# ovlData = bytearray(ovlFile.read())
|
||||||
# ovlFile.close()
|
# ovlFile.close()
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ def get_damage_bytes(address, repo):
|
||||||
|
|
||||||
print(file_result)
|
print(file_result)
|
||||||
|
|
||||||
with open(repo + os.sep + "baserom" + os.sep + file_result.name, "rb") as ovl_file:
|
with open(repo + os.sep + "baseroms/gc-eu-mq-dbg/segments" + os.sep + file_result.name, "rb") as ovl_file:
|
||||||
ovl_data = bytearray(ovl_file.read())
|
ovl_data = bytearray(ovl_file.read())
|
||||||
|
|
||||||
damage_data = ovl_data[file_result.offset:file_result.offset+0x20]
|
damage_data = ovl_data[file_result.offset:file_result.offset+0x20]
|
||||||
|
|
|
@ -72,7 +72,7 @@ repo = scriptDir + os.sep + ".." + os.sep + ".."
|
||||||
|
|
||||||
|
|
||||||
kaleido_scope_data = []
|
kaleido_scope_data = []
|
||||||
with open(repo + "/baserom/ovl_kaleido_scope", "rb") as file:
|
with open(repo + "/baseroms/gc-eu-mq-dbg/segments/ovl_kaleido_scope", "rb") as file:
|
||||||
kaleido_scope_data = bytearray(file.read())
|
kaleido_scope_data = bytearray(file.read())
|
||||||
|
|
||||||
scenemaps = []
|
scenemaps = []
|
||||||
|
|
|
@ -97,7 +97,7 @@ repo = scriptDir + os.sep + ".." + os.sep + ".."
|
||||||
|
|
||||||
|
|
||||||
map_mark_data = []
|
map_mark_data = []
|
||||||
with open(repo + "/baserom/ovl_map_mark_data", "rb") as file:
|
with open(repo + "/baseroms/gc-eu-mq-dbg/segments/ovl_map_mark_data", "rb") as file:
|
||||||
map_mark_data = bytearray(file.read())
|
map_mark_data = bytearray(file.read())
|
||||||
|
|
||||||
scenemaps = []
|
scenemaps = []
|
||||||
|
|
|
@ -127,6 +127,7 @@ static const char *const stmtNames[] =
|
||||||
[STMT_after] = "after",
|
[STMT_after] = "after",
|
||||||
[STMT_align] = "align",
|
[STMT_align] = "align",
|
||||||
[STMT_beginseg] = "beginseg",
|
[STMT_beginseg] = "beginseg",
|
||||||
|
[STMT_compress] = "compress",
|
||||||
[STMT_endseg] = "endseg",
|
[STMT_endseg] = "endseg",
|
||||||
[STMT_entry] = "entry",
|
[STMT_entry] = "entry",
|
||||||
[STMT_flags] = "flags",
|
[STMT_flags] = "flags",
|
||||||
|
@ -216,10 +217,13 @@ bool parse_segment_statement(struct Segment *currSeg, STMTId stmt, char* args, i
|
||||||
currSeg->includes[currSeg->includesCount - 1].linkerPadding = 0;
|
currSeg->includes[currSeg->includesCount - 1].linkerPadding = 0;
|
||||||
currSeg->includes[currSeg->includesCount - 1].dataWithRodata = (stmt == STMT_include_data_with_rodata);
|
currSeg->includes[currSeg->includesCount - 1].dataWithRodata = (stmt == STMT_include_data_with_rodata);
|
||||||
break;
|
break;
|
||||||
case STMT_increment:
|
case STMT_increment:
|
||||||
if (!parse_number(args, &currSeg->increment))
|
if (!parse_number(args, &currSeg->increment))
|
||||||
util_fatal_error("line %i: expected number after 'increment'", lineNum);
|
util_fatal_error("line %i: expected number after 'increment'", lineNum);
|
||||||
break;
|
break;
|
||||||
|
case STMT_compress:
|
||||||
|
currSeg->compress = true;
|
||||||
|
break;
|
||||||
case STMT_pad_text:
|
case STMT_pad_text:
|
||||||
currSeg->includes[currSeg->includesCount - 1].linkerPadding += 0x10;
|
currSeg->includes[currSeg->includesCount - 1].linkerPadding += 0x10;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -9,6 +9,7 @@ typedef enum {
|
||||||
STMT_after,
|
STMT_after,
|
||||||
STMT_align,
|
STMT_align,
|
||||||
STMT_beginseg,
|
STMT_beginseg,
|
||||||
|
STMT_compress,
|
||||||
STMT_endseg,
|
STMT_endseg,
|
||||||
STMT_entry,
|
STMT_entry,
|
||||||
STMT_flags,
|
STMT_flags,
|
||||||
|
@ -48,6 +49,7 @@ typedef struct Segment {
|
||||||
uint32_t number;
|
uint32_t number;
|
||||||
struct Include* includes;
|
struct Include* includes;
|
||||||
int includesCount;
|
int includesCount;
|
||||||
|
bool compress;
|
||||||
} Segment;
|
} Segment;
|
||||||
|
|
||||||
void parse_rom_spec(char* spec, struct Segment** segments, int* segment_count);
|
void parse_rom_spec(char* spec, struct Segment** segments, int* segment_count);
|
||||||
|
|
Loading…
Reference in a new issue