mirror of https://github.com/PrimeDecomp/prime.git
Use decomp-toolkit
This commit is contained in:
parent
ef221d5c96
commit
607753a08f
232
Makefile
232
Makefile
|
@ -1,232 +0,0 @@
|
||||||
ifneq ($(findstring MINGW,$(shell uname)),)
|
|
||||||
WINDOWS := 1
|
|
||||||
endif
|
|
||||||
ifneq ($(findstring MSYS,$(shell uname)),)
|
|
||||||
WINDOWS := 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
# If 0, tells the console to chill out. (Quiets the make process.)
|
|
||||||
VERBOSE ?= 0
|
|
||||||
|
|
||||||
# If GENERATE_MAP set to 1, tells LDFLAGS to generate a mapfile, which makes linking take several minutes.
|
|
||||||
GENERATE_MAP ?= 0
|
|
||||||
|
|
||||||
# Enable non-matching code & various fixes
|
|
||||||
NONMATCHING ?= 0
|
|
||||||
|
|
||||||
ifeq ($(VERBOSE),0)
|
|
||||||
QUIET := @
|
|
||||||
endif
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Files
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
NAME := mp1
|
|
||||||
VERSION ?= 0
|
|
||||||
|
|
||||||
VERSION_NUM := $(VERSION)
|
|
||||||
ifeq ($(VERSION),kor)
|
|
||||||
VERSION_NUM := 2
|
|
||||||
endif
|
|
||||||
|
|
||||||
BUILD_DIR := build/$(NAME).$(VERSION)
|
|
||||||
|
|
||||||
# Inputs
|
|
||||||
S_FILES := $(wildcard asm/*.s)
|
|
||||||
C_FILES := $(wildcard src/*.c)
|
|
||||||
CPP_FILES := $(wildcard src/*.cpp)
|
|
||||||
CPP_FILES += $(wildcard src/*.cp)
|
|
||||||
LDSCRIPT := $(BUILD_DIR)/ldscript.lcf
|
|
||||||
|
|
||||||
# Outputs
|
|
||||||
DOL := $(BUILD_DIR)/main.dol
|
|
||||||
ELF := $(DOL:.dol=.elf)
|
|
||||||
MAP := $(BUILD_DIR)/MetroidPrime.MAP
|
|
||||||
|
|
||||||
|
|
||||||
ifeq ($(GENERATE_MAP),1)
|
|
||||||
MAPGEN := -map $(MAP)
|
|
||||||
endif
|
|
||||||
|
|
||||||
include obj_files.mk
|
|
||||||
|
|
||||||
O_FILES := $(METROTRK_FILES) \
|
|
||||||
$(METROIDPRIME) $(WORLDFORMAT) $(WEAPONS) $(METARENDER) $(GUISYS) $(COLLISION) \
|
|
||||||
$(KYOTO_1) $(ZLIB_FILES) $(KYOTO_2) $(AI_FILES) \
|
|
||||||
$(AR_FILES) $(BASE_FILES) $(DB_FILES) $(DSP_FILES) $(DVD_FILES) $(GX_FILES) $(MTX_FILES) \
|
|
||||||
$(OS_FILES) $(PAD_FILES) $(VI_FILES) $(MSL_PPCEABI_BARE_H) $(MSL_COMMON_MATH) $(MUSYX_FILES) \
|
|
||||||
$(DTK_FILES) $(CARD_FILES) $(SI_FILES) $(EXI_FILES) $(THP_FILES) \
|
|
||||||
$(GBA_FILES)
|
|
||||||
DEPENDS := $(O_FILES:.o=.d)
|
|
||||||
# If a specific .o file is passed as a target, also process its deps
|
|
||||||
DEPENDS += $(MAKECMDGOALS:.o=.d)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Tools
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
MWCC_VERSION := 1.3.2
|
|
||||||
MWLD_VERSION := 1.3.2
|
|
||||||
|
|
||||||
# Programs
|
|
||||||
export WINEDEBUG ?= -all
|
|
||||||
ifeq ($(WINDOWS),1)
|
|
||||||
WINE :=
|
|
||||||
AS := $(DEVKITPPC)/bin/powerpc-eabi-as.exe
|
|
||||||
CPP := $(DEVKITPPC)/bin/powerpc-eabi-cpp.exe -P
|
|
||||||
PYTHON := py
|
|
||||||
SHA1SUM := sha1sum
|
|
||||||
else
|
|
||||||
WIBO := $(shell command -v wibo 2> /dev/null)
|
|
||||||
ifdef WIBO
|
|
||||||
WINE ?= wibo
|
|
||||||
else
|
|
||||||
WINE ?= wine
|
|
||||||
endif
|
|
||||||
DEVKITPPC ?= /opt/devkitpro/devkitPPC
|
|
||||||
DEPENDS := $(DEPENDS:.d=.d.unix)
|
|
||||||
AS := $(DEVKITPPC)/bin/powerpc-eabi-as
|
|
||||||
CPP := $(DEVKITPPC)/bin/powerpc-eabi-cpp -P
|
|
||||||
PYTHON := python3
|
|
||||||
SHA1SUM := shasum -a 1
|
|
||||||
endif
|
|
||||||
CC = $(WINE) tools/mwcc_compiler/$(MWCC_VERSION)/mwcceppc.exe
|
|
||||||
LD := $(WINE) tools/mwcc_compiler/$(MWLD_VERSION)/mwldeppc.exe
|
|
||||||
ELF2DOL := tools/elf2dol
|
|
||||||
METROIDBUILDINFO := tools/metroidbuildinfo
|
|
||||||
|
|
||||||
TRANSFORM_DEP := tools/transform-dep.py
|
|
||||||
FRANK := tools/franklite.py
|
|
||||||
|
|
||||||
# Options
|
|
||||||
INCLUDES := -i include/ -i libc/
|
|
||||||
ASM_INCLUDES := -I include/
|
|
||||||
|
|
||||||
# DotKuribo/llvm-project
|
|
||||||
CLANG_CC ?= clang-kuribo
|
|
||||||
CLANG_CFLAGS := --target=ppc32-kuribo -mcpu=750 -nostdlib -fno-exceptions -fno-rtti -O3 -Wall -Wno-trigraphs -Wno-inline-new-delete -Wno-unused-private-field -fpermissive -std=gnu++11 $(ASM_INCLUDES)
|
|
||||||
|
|
||||||
ASFLAGS := -mgekko $(ASM_INCLUDES) --defsym version=$(VERSION_NUM)
|
|
||||||
ifeq ($(VERBOSE),1)
|
|
||||||
# this set of LDFLAGS outputs warnings.
|
|
||||||
LDFLAGS := $(MAPGEN) -fp fmadd -nodefaults
|
|
||||||
endif
|
|
||||||
ifeq ($(VERBOSE),0)
|
|
||||||
# this set of LDFLAGS generates no warnings.
|
|
||||||
LDFLAGS := $(MAPGEN) -fp fmadd -nodefaults -w off
|
|
||||||
endif
|
|
||||||
DEFINES = -DPRIME1 -DVERSION=$(VERSION_NUM) -DNONMATCHING=$(NONMATCHING)
|
|
||||||
CFLAGS_BASE = -proc gekko -nodefaults -Cpp_exceptions off -RTTI off -fp hard -fp_contract on -O4,p -maxerrors 1 -enum int -inline auto -str reuse -nosyspath -MMD $(DEFINES) $(INCLUDES)
|
|
||||||
CFLAGS = $(CFLAGS_BASE) -use_lmw_stmw on -str reuse,pool,readonly -gccinc -inline deferred,noauto -common on
|
|
||||||
CFLAGS_RUNTIME = $(CFLAGS_BASE) -use_lmw_stmw on -str reuse,pool,readonly -gccinc -inline deferred,auto
|
|
||||||
CFLAGS_MUSYX = $(CFLAGS_BASE) -fp hard -fp_contract off -str reuse,pool,readonly
|
|
||||||
|
|
||||||
ifeq ($(VERBOSE),0)
|
|
||||||
# this set of ASFLAGS generates no warnings.
|
|
||||||
ASFLAGS += -W
|
|
||||||
endif
|
|
||||||
|
|
||||||
$(METROTRK_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(METROTRK_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(BASE_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(BASE_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(AI_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(AI_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(OS_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(OS_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(CARD_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(CARD_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(DVD_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(DVD_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(DSP_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(DSP_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(MUSYX_FILES): CFLAGS := $(CFLAGS_MUSYX)
|
|
||||||
$(ZLIB_FILES): CFLAGS := $(CFLAGS_RUNTIME)
|
|
||||||
$(MSL_PPCEABI_BARE_H): CFLAGS := $(CFLAGS_RUNTIME)
|
|
||||||
$(MSL_COMMON_MATH): CFLAGS := $(CFLAGS_RUNTIME)
|
|
||||||
$(PAD_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(PAD_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(DTK_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(DTK_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(SI_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(SI_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(DB_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(DB_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
$(GBA_FILES): MWCC_VERSION := 1.2.5
|
|
||||||
$(GBA_FILES): CFLAGS := $(CFLAGS_BASE)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Recipes
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
### Default target ###
|
|
||||||
|
|
||||||
default: all
|
|
||||||
|
|
||||||
all: $(DOL)
|
|
||||||
|
|
||||||
.PHONY: tools
|
|
||||||
|
|
||||||
$(LDSCRIPT): ldscript.lcf
|
|
||||||
$(QUIET) $(CPP) -MMD -MP -MT $@ -MF $@.d -I include/ -I . -DBUILD_DIR=$(BUILD_DIR) -o $@ $<
|
|
||||||
|
|
||||||
$(DOL): $(ELF) | tools
|
|
||||||
$(QUIET) $(ELF2DOL) $< $@
|
|
||||||
$(METROIDBUILDINFO) $@ buildstrings/$(NAME).$(VERSION).build
|
|
||||||
$(QUIET) $(SHA1SUM) -c sha1/$(NAME).$(VERSION).sha1
|
|
||||||
ifneq ($(findstring -map,$(LDFLAGS)),)
|
|
||||||
$(QUIET) $(PYTHON) tools/calcprogress.py $(DOL) $(MAP)
|
|
||||||
endif
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) -r $(BUILD_DIR)
|
|
||||||
$(MAKE) -C tools clean
|
|
||||||
tools:
|
|
||||||
$(MAKE) -C tools
|
|
||||||
|
|
||||||
# ELF creation makefile instructions
|
|
||||||
$(ELF): $(O_FILES) $(LDSCRIPT)
|
|
||||||
@echo Linking ELF $@
|
|
||||||
$(QUIET) @echo $(O_FILES) > $(BUILD_DIR)/o_files
|
|
||||||
$(QUIET) $(LD) $(LDFLAGS) -o $@ -lcf $(LDSCRIPT) @$(BUILD_DIR)/o_files
|
|
||||||
|
|
||||||
%.d.unix: %.d $(TRANSFORM_DEP)
|
|
||||||
@echo Processing $<
|
|
||||||
$(QUIET) $(PYTHON) $(TRANSFORM_DEP) $< $@
|
|
||||||
|
|
||||||
-include $(DEPENDS)
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.s
|
|
||||||
@echo "Assembling" $<
|
|
||||||
$(QUIET) mkdir -p $(dir $@)
|
|
||||||
$(QUIET) $(AS) $(ASFLAGS) -o $@ $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.clang.o: %.cpp
|
|
||||||
@echo "Clang " $<
|
|
||||||
$(QUIET) mkdir -p $(dir $@)
|
|
||||||
$(QUIET) $(CLANG_CC) $(CLANG_CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.ep.o: $(BUILD_DIR)/%.o
|
|
||||||
@echo Frank is fixing $<
|
|
||||||
$(QUIET) mkdir -p $(dir $@)
|
|
||||||
$(QUIET) $(PYTHON) $(FRANK) $< $@
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c
|
|
||||||
@echo "Compiling " $<
|
|
||||||
$(QUIET) mkdir -p $(dir $@)
|
|
||||||
$(QUIET) $(CC) $(CFLAGS) -c -o $(dir $@) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.cp
|
|
||||||
@echo "Compiling " $<
|
|
||||||
$(QUIET) mkdir -p $(dir $@)
|
|
||||||
$(QUIET) $(CC) $(CFLAGS) -c -o $(dir $@) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.cpp
|
|
||||||
@echo "Compiling " $<
|
|
||||||
$(QUIET) mkdir -p $(dir $@)
|
|
||||||
$(QUIET) $(CC) $(CFLAGS) -c -o $(dir $@) $<
|
|
||||||
|
|
||||||
### Debug Print ###
|
|
||||||
|
|
||||||
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
|
|
27
README.md
27
README.md
|
@ -23,9 +23,10 @@ Dependencies
|
||||||
|
|
||||||
Windows:
|
Windows:
|
||||||
--------
|
--------
|
||||||
|
- Install [ninja](https://github.com/ninja-build/ninja/releases) and add it to `%PATH%`.
|
||||||
- Install [devkitPro](https://github.com/devkitPro/installer/releases/latest) with GameCube development package.
|
- Install [devkitPro](https://github.com/devkitPro/installer/releases/latest) with GameCube development package.
|
||||||
- Open `C:\devkitPro\msys2\msys2.exe`
|
- Open `C:\devkitPro\msys2\msys2.exe`
|
||||||
- Run the following:
|
- Install GameCube development packages:
|
||||||
```
|
```
|
||||||
pacman -Sy --noconfirm --needed msys2-keyring
|
pacman -Sy --noconfirm --needed msys2-keyring
|
||||||
pacman -Su --noconfirm --needed gcc git gamecube-dev
|
pacman -Su --noconfirm --needed gcc git gamecube-dev
|
||||||
|
@ -33,25 +34,28 @@ Windows:
|
||||||
|
|
||||||
macOS:
|
macOS:
|
||||||
------
|
------
|
||||||
- Install wine:
|
- Install [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages):
|
||||||
```
|
```
|
||||||
brew tap gcenx/wine
|
brew install ninja
|
||||||
brew install wine-crossover
|
```
|
||||||
sudo xattr -r -d com.apple.quarantine "/Applications/Wine Crossover.app"
|
- Install [wine-crossover](https://github.com/Gcenx/homebrew-wine):
|
||||||
|
```
|
||||||
|
brew install --cask --no-quarantine gcenx/wine/wine-crossover
|
||||||
```
|
```
|
||||||
- Install [devkitPro](https://github.com/devkitPro/pacman/releases/latest).
|
- Install [devkitPro](https://github.com/devkitPro/pacman/releases/latest).
|
||||||
- Run the following:
|
- Install GameCube development packages:
|
||||||
```
|
```
|
||||||
sudo dkp-pacman -Syu --noconfirm --needed gamecube-dev
|
sudo dkp-pacman -Syu --noconfirm --needed gamecube-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Linux:
|
Linux:
|
||||||
------
|
------
|
||||||
|
- Install [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages).
|
||||||
- Install wine from your package manager.
|
- Install wine from your package manager.
|
||||||
- Faster alternative: [WiBo](https://github.com/decompals/WiBo), a minimal 32-bit Windows binary wrapper.
|
- Faster alternative: [WiBo](https://github.com/decompals/WiBo), a minimal 32-bit Windows binary wrapper.
|
||||||
Ensure the binary is in `PATH`.
|
Ensure the binary is in `PATH`.
|
||||||
- Install [devkitPro](https://devkitpro.org/wiki/devkitPro_pacman).
|
- Install [devkitPro](https://devkitpro.org/wiki/devkitPro_pacman).
|
||||||
- Run the following:
|
- Install GameCube development packages:
|
||||||
```
|
```
|
||||||
sudo dkp-pacman -Syu --noconfirm --needed gamecube-dev
|
sudo dkp-pacman -Syu --noconfirm --needed gamecube-dev
|
||||||
```
|
```
|
||||||
|
@ -64,8 +68,13 @@ Building
|
||||||
git clone https://github.com/PrimeDecomp/prime.git
|
git clone https://github.com/PrimeDecomp/prime.git
|
||||||
```
|
```
|
||||||
- Download [GC_WII_COMPILERS.zip](https://cdn.discordapp.com/attachments/727918646525165659/917185027656286218/GC_WII_COMPILERS.zip)
|
- Download [GC_WII_COMPILERS.zip](https://cdn.discordapp.com/attachments/727918646525165659/917185027656286218/GC_WII_COMPILERS.zip)
|
||||||
- Extract the contents of the `GC` directory to `tools/mwcc_compiler`
|
- Extract the _contents_ of the `GC` directory to `tools/mwcc_compiler`.
|
||||||
|
- Resulting structure should be (for example) `tools/mwcc_compiler/1.3.2/mwcceppc.exe`
|
||||||
|
- Configure:
|
||||||
|
```
|
||||||
|
python configure.py
|
||||||
|
```
|
||||||
- Build:
|
- Build:
|
||||||
```
|
```
|
||||||
make -j
|
ninja
|
||||||
```
|
```
|
||||||
|
|
745
configure.py
745
configure.py
|
@ -1,26 +1,4 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
import io
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
from shutil import which
|
|
||||||
from tools import ninja_syntax
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('--version', dest='version',
|
|
||||||
default='0', help='version to build (0, 1, kor)')
|
|
||||||
parser.add_argument('--map', dest='map', action='store_true',
|
|
||||||
help='generate map file')
|
|
||||||
parser.add_argument('--no-check', dest='check', action='store_false',
|
|
||||||
help='don\'t check hash of resulting dol')
|
|
||||||
parser.add_argument('--static-libs', dest='static_libs', action='store_true',
|
|
||||||
help='build and use static libs')
|
|
||||||
parser.add_argument('--devkitppc', dest='devkitppc', help='path to devkitPPC')
|
|
||||||
if os.name != "nt" and not "_NT-" in os.uname().sysname:
|
|
||||||
parser.add_argument('--wine', dest='wine', help='path to wine (or wibo)')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
LIBS = [
|
LIBS = [
|
||||||
{
|
{
|
||||||
"lib": "TRK_MINNOW_DOLPHIN",
|
"lib": "TRK_MINNOW_DOLPHIN",
|
||||||
|
@ -652,7 +630,7 @@ LIBS = [
|
||||||
["Kyoto/zlib/inftrees", True],
|
["Kyoto/zlib/inftrees", True],
|
||||||
["Kyoto/zlib/infutil", True],
|
["Kyoto/zlib/infutil", True],
|
||||||
["Kyoto/zlib/zutil", True],
|
["Kyoto/zlib/zutil", True],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"lib": "Kyoto_CW2",
|
"lib": "Kyoto_CW2",
|
||||||
|
@ -861,9 +839,7 @@ LIBS = [
|
||||||
"mwcc_version": "1.2.5",
|
"mwcc_version": "1.2.5",
|
||||||
"cflags": "$cflags_base",
|
"cflags": "$cflags_base",
|
||||||
"host": False,
|
"host": False,
|
||||||
"objects": [
|
"objects": ["Dolphin/vi"],
|
||||||
"Dolphin/vi"
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"lib": "MSL_C.PPCEABI.bare.H",
|
"lib": "MSL_C.PPCEABI.bare.H",
|
||||||
|
@ -1056,284 +1032,495 @@ LIBS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
# Create & link static libraries
|
if __name__ == "__main__":
|
||||||
# Disabled by default for now until we can get it working on windows/macOS
|
import os
|
||||||
ENABLE_STATIC_LIBS = args.static_libs
|
import io
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
|
||||||
# On Windows, we need this to use && in commands
|
from shutil import which
|
||||||
ALLOW_CHAIN = "cmd /c " if os.name == "nt" else ""
|
from tools import ninja_syntax
|
||||||
|
|
||||||
out = io.StringIO()
|
parser = argparse.ArgumentParser()
|
||||||
n = ninja_syntax.Writer(out)
|
parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
dest="version",
|
||||||
|
default="0",
|
||||||
|
help="version to build (0, 1, kor)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--map",
|
||||||
|
dest="map",
|
||||||
|
action="store_true",
|
||||||
|
help="generate map file",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-check",
|
||||||
|
dest="check",
|
||||||
|
action="store_false",
|
||||||
|
help="don't check hash of resulting dol",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--static-libs",
|
||||||
|
dest="static_libs",
|
||||||
|
action="store_true",
|
||||||
|
help="build and use static libs",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--devkitppc",
|
||||||
|
dest="devkitppc",
|
||||||
|
help="path to devkitPPC",
|
||||||
|
)
|
||||||
|
if os.name != "nt" and not "_NT-" in os.uname().sysname:
|
||||||
|
parser.add_argument(
|
||||||
|
"--wine",
|
||||||
|
dest="wine",
|
||||||
|
help="path to wine (or wibo)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--build-dtk",
|
||||||
|
dest="build_dtk",
|
||||||
|
help="path to decomp-toolkit source",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
n.variable("ninja_required_version", "1.3")
|
# On Windows, we need this to use && in commands
|
||||||
n.newline()
|
ALLOW_CHAIN = "cmd /c " if os.name == "nt" else ""
|
||||||
|
|
||||||
n.comment("The arguments passed to configure.py, for rerunning it.")
|
out = io.StringIO()
|
||||||
configure_args = sys.argv[1:]
|
n = ninja_syntax.Writer(out)
|
||||||
# Ignore DEVKITPPC env var on Windows
|
|
||||||
if os.name != "nt" and "DEVKITPPC" in os.environ and not args.devkitppc:
|
|
||||||
configure_args.extend(["--devkitppc", os.environ["DEVKITPPC"]])
|
|
||||||
n.variable("configure_args", configure_args)
|
|
||||||
n.newline()
|
|
||||||
|
|
||||||
###
|
n.variable("ninja_required_version", "1.3")
|
||||||
# Variables
|
n.newline()
|
||||||
###
|
|
||||||
n.variable("version", args.version.lower())
|
n.comment("The arguments passed to configure.py, for rerunning it.")
|
||||||
if args.version.lower() == 'kor':
|
configure_args = sys.argv[1:]
|
||||||
n.variable("version_num", "2")
|
# Ignore DEVKITPPC env var on Windows
|
||||||
elif args.version.isnumeric() and int(args.version) in [0, 1]:
|
if os.name != "nt" and "DEVKITPPC" in os.environ and not args.devkitppc:
|
||||||
n.variable("version_num", args.version)
|
configure_args.extend(["--devkitppc", os.environ["DEVKITPPC"]])
|
||||||
else:
|
n.variable("configure_args", configure_args)
|
||||||
sys.exit(f"Invalid version \"{args.version}\"")
|
n.newline()
|
||||||
n.variable("builddir", "build/mp1.$version")
|
|
||||||
if args.devkitppc:
|
###
|
||||||
n.variable("devkitppc", args.devkitppc)
|
# Variables
|
||||||
elif os.name == "nt":
|
###
|
||||||
n.variable("devkitppc", "C:\devkitPro\devkitPPC")
|
n.comment("Variables")
|
||||||
elif "DEVKITPPC" in os.environ:
|
n.variable("version", args.version.lower())
|
||||||
n.variable("devkitppc", os.environ["DEVKITPPC"])
|
if args.version.lower() == "kor":
|
||||||
else:
|
n.variable("version_num", "2")
|
||||||
n.variable("devkitppc", "/opt/devkitpro/devkitPPC")
|
elif args.version.isnumeric() and int(args.version) in [0, 1]:
|
||||||
n.variable("cflags_base", "-proc gekko -nodefaults -Cpp_exceptions off -RTTI off -fp hard -fp_contract on -O4,p -maxerrors 1 -enum int -inline auto -str reuse -nosyspath -MMD -DPRIME1 -DVERSION=$version_num -DNONMATCHING=0 -i include/ -i libc/")
|
n.variable("version_num", args.version)
|
||||||
n.variable("cflags_retro",
|
|
||||||
"$cflags_base -use_lmw_stmw on -str reuse,pool,readonly -gccinc -inline deferred,noauto -common on")
|
|
||||||
n.variable("cflags_runtime",
|
|
||||||
"$cflags_base -use_lmw_stmw on -str reuse,pool,readonly -gccinc -inline deferred,auto")
|
|
||||||
n.variable("cflags_musyx", "$cflags_base -str reuse,pool,readonly")
|
|
||||||
n.variable("asflags", "-mgekko -I include/ --defsym version=$version_num -W")
|
|
||||||
ldflags = "-fp fmadd -nodefaults -lcf ldscript.lcf -w off"
|
|
||||||
if args.map:
|
|
||||||
ldflags += " -map $builddir/MetroidPrime.MAP"
|
|
||||||
n.variable("ldflags", ldflags)
|
|
||||||
n.variable("mwcc_version", "1.3.2")
|
|
||||||
n.variable("python", sys.executable)
|
|
||||||
if os.name == "nt":
|
|
||||||
n.variable("sha1sum", "sha1sum")
|
|
||||||
n.variable("exe", ".exe")
|
|
||||||
else:
|
|
||||||
n.variable("sha1sum", "shasum -a 1")
|
|
||||||
if "_NT-" in os.uname().sysname:
|
|
||||||
# MSYS2
|
|
||||||
n.variable("wine", "")
|
|
||||||
elif args.wine:
|
|
||||||
n.variable("wine", args.wine + " ")
|
|
||||||
elif which("wibo") is not None:
|
|
||||||
n.variable("wine", "wibo ")
|
|
||||||
else:
|
else:
|
||||||
n.variable("wine", "wine ")
|
sys.exit(f'Invalid version "{args.version}"')
|
||||||
n.variable("exe", "")
|
n.variable("builddir", "build/mp1.$version")
|
||||||
n.newline()
|
if args.devkitppc:
|
||||||
n.variable("host_cflags", "-I include/ -Wno-trigraphs")
|
n.variable("devkitppc", args.devkitppc)
|
||||||
n.variable("host_cppflags",
|
elif os.name == "nt":
|
||||||
"-std=c++98 -I include/ -fno-exceptions -fno-rtti -D_CRT_SECURE_NO_WARNINGS -Wno-trigraphs -Wno-c++11-extensions")
|
n.variable("devkitppc", "C:\devkitPro\devkitPPC")
|
||||||
|
elif "DEVKITPPC" in os.environ:
|
||||||
###
|
n.variable("devkitppc", os.environ["DEVKITPPC"])
|
||||||
# Rules
|
|
||||||
###
|
|
||||||
n.newline()
|
|
||||||
if os.name == "nt":
|
|
||||||
n.rule(name="mwcc", command="tools\\mwcc_compiler\\$mwcc_version\\mwcceppc.exe $cflags -c $in -o $basedir",
|
|
||||||
description="MWCC $out", depfile="$basefile.d", deps="gcc")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="mwcc_frank", command=ALLOW_CHAIN+"tools\\mwcc_compiler\\$mwcc_version\\mwcceppc.exe $cflags -c $in -o $basedir && " +
|
|
||||||
"$python tools/franklite.py $out $out",
|
|
||||||
description="FRANK $out", depfile="$basefile.d", deps="gcc")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="link", command="tools\\mwcc_compiler\\$mwcc_version\\mwldeppc.exe $ldflags -o $out @$out.rsp",
|
|
||||||
description="LINK $out", rspfile="$out.rsp", rspfile_content="$in")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="as", command="$devkitppc\\bin\\powerpc-eabi-as.exe $asflags -o $out $in -MD $out.d",
|
|
||||||
description="AS $out", depfile="$out.d", deps="gcc")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="ar", command="$devkitppc\\bin\\powerpc-eabi-ar.exe crs $out $in",
|
|
||||||
description="AR $out")
|
|
||||||
n.newline()
|
|
||||||
else:
|
|
||||||
n.rule(name="mwcc", command="${wine}tools/mwcc_compiler/$mwcc_version/mwcceppc.exe $cflags -c $in -o $basedir && " +
|
|
||||||
"$python tools/transform-dep.py $basefile.d $basefile.d",
|
|
||||||
description="MWCC $out", depfile="$basefile.d", deps="gcc")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="mwcc_frank", command="${wine}tools/mwcc_compiler/$mwcc_version/mwcceppc.exe $cflags -c $in -o $basedir && " +
|
|
||||||
"$python tools/franklite.py $out $out && " +
|
|
||||||
"$python tools/transform-dep.py $basefile.d $basefile.d",
|
|
||||||
description="FRANK $out", depfile="$basefile.d", deps="gcc")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="link", command="${wine}tools/mwcc_compiler/$mwcc_version/mwldeppc.exe $ldflags -o $out @$out.rsp",
|
|
||||||
description="LINK $out", rspfile="$out.rsp", rspfile_content="$in")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="as", command="$devkitppc/bin/powerpc-eabi-as $asflags -o $out $in -MD $out.d",
|
|
||||||
description="AS $out", depfile="$out.d", deps="gcc")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="ar", command="$devkitppc/bin/powerpc-eabi-ar crs $out $in",
|
|
||||||
description="AR $out")
|
|
||||||
n.newline()
|
|
||||||
n.rule(name="host_cc", command="clang $host_cflags -c -o $out $in",
|
|
||||||
description="host_cc $out")
|
|
||||||
n.rule(name="host_cpp", command="clang++ $host_cppflags -c -o $out $in",
|
|
||||||
description="host_c++ $out")
|
|
||||||
n.newline()
|
|
||||||
|
|
||||||
###
|
|
||||||
# Build
|
|
||||||
###
|
|
||||||
all_source_files = []
|
|
||||||
all_host_source_files = []
|
|
||||||
for lib in LIBS:
|
|
||||||
inputs = []
|
|
||||||
if "lib" in lib:
|
|
||||||
lib_name = lib["lib"]
|
|
||||||
n.comment(f"{lib_name}.a")
|
|
||||||
else:
|
else:
|
||||||
n.comment("Loose files")
|
n.variable("devkitppc", "/opt/devkitpro/devkitPPC")
|
||||||
for object in lib["objects"]:
|
n.variable(
|
||||||
completed = None
|
"cflags_base",
|
||||||
add_to_all = True
|
"-proc gekko -nodefaults -Cpp_exceptions off -RTTI off -fp hard -fp_contract on -O4,p -maxerrors 1 -enum int -inline auto -str reuse -nosyspath -MMD -DPRIME1 -DVERSION=$version_num -DNONMATCHING=0 -i include/ -i libc/",
|
||||||
if type(object) is list:
|
)
|
||||||
if len(object) > 2:
|
n.variable(
|
||||||
add_to_all = object[2]
|
"cflags_retro",
|
||||||
completed = object[1]
|
"$cflags_base -use_lmw_stmw on -str reuse,pool,readonly -gccinc -inline deferred,noauto -common on",
|
||||||
object = object[0]
|
)
|
||||||
|
n.variable(
|
||||||
|
"cflags_runtime",
|
||||||
|
"$cflags_base -use_lmw_stmw on -str reuse,pool,readonly -gccinc -inline deferred,auto",
|
||||||
|
)
|
||||||
|
n.variable("cflags_musyx", "$cflags_base -str reuse,pool,readonly")
|
||||||
|
n.variable("asflags", "-mgekko -I include/ --defsym version=$version_num -W")
|
||||||
|
ldflags = "-fp fmadd -nodefaults -lcf ldscript.lcf -w off"
|
||||||
|
if args.map:
|
||||||
|
ldflags += " -map $builddir/MetroidPrime.MAP"
|
||||||
|
n.variable("ldflags", ldflags)
|
||||||
|
n.variable("mwcc_version", "1.3.2")
|
||||||
|
n.variable("python", sys.executable)
|
||||||
|
if os.name == "nt":
|
||||||
|
n.variable("exe", ".exe")
|
||||||
|
else:
|
||||||
|
if "_NT-" in os.uname().sysname:
|
||||||
|
# MSYS2
|
||||||
|
n.variable("wine", "")
|
||||||
|
elif args.wine:
|
||||||
|
n.variable("wine", args.wine + " ")
|
||||||
|
elif which("wibo") is not None:
|
||||||
|
n.variable("wine", "wibo ")
|
||||||
|
else:
|
||||||
|
n.variable("wine", "wine ")
|
||||||
|
n.variable("exe", "")
|
||||||
|
n.newline()
|
||||||
|
|
||||||
mwcc_version = lib["mwcc_version"]
|
###
|
||||||
c_file = None
|
# Rules
|
||||||
if os.path.exists(os.path.join("src", f"{object}.cpp")):
|
###
|
||||||
c_file = os.path.join("src", f"{object}.cpp")
|
if os.name == "nt":
|
||||||
elif os.path.exists(os.path.join("src", f"{object}.c")):
|
n.comment("MWCC build")
|
||||||
c_file = os.path.join("src", f"{object}.c")
|
n.rule(
|
||||||
if c_file is not None:
|
name="mwcc",
|
||||||
if completed is None:
|
command="tools\\mwcc_compiler\\$mwcc_version\\mwcceppc.exe $cflags -c $in -o $basedir",
|
||||||
print(f"Mark as incomplete: {c_file}")
|
description="MWCC $out",
|
||||||
rule = "mwcc"
|
depfile="$basefile.d",
|
||||||
if mwcc_version == "1.2.5e":
|
deps="gcc",
|
||||||
mwcc_version = "1.2.5"
|
)
|
||||||
rule = "mwcc_frank"
|
n.newline()
|
||||||
n.build(f"$builddir/src/{object}.o", rule, c_file,
|
n.comment("MWCC build with franklite")
|
||||||
|
n.rule(
|
||||||
|
name="mwcc_frank",
|
||||||
|
command=ALLOW_CHAIN
|
||||||
|
+ "tools\\mwcc_compiler\\$mwcc_version\\mwcceppc.exe $cflags -c $in -o $basedir && "
|
||||||
|
+ "$python tools/franklite.py $out $out",
|
||||||
|
description="FRANK $out",
|
||||||
|
depfile="$basefile.d",
|
||||||
|
deps="gcc",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Link ELF file")
|
||||||
|
n.rule(
|
||||||
|
name="link",
|
||||||
|
command="tools\\mwcc_compiler\\$mwcc_version\\mwldeppc.exe $ldflags -o $out @$out.rsp",
|
||||||
|
description="LINK $out",
|
||||||
|
rspfile="$out.rsp",
|
||||||
|
rspfile_content="$in",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Assemble asm")
|
||||||
|
n.rule(
|
||||||
|
name="as",
|
||||||
|
command="$devkitppc\\bin\\powerpc-eabi-as.exe $asflags -o $out $in -MD $out.d",
|
||||||
|
description="AS $out",
|
||||||
|
depfile="$out.d",
|
||||||
|
deps="gcc",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Create static library")
|
||||||
|
n.rule(
|
||||||
|
name="ar",
|
||||||
|
command="$devkitppc\\bin\\powerpc-eabi-ar.exe crs $out $in",
|
||||||
|
description="AR $out",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
else:
|
||||||
|
n.comment("MWCC build")
|
||||||
|
n.rule(
|
||||||
|
name="mwcc",
|
||||||
|
command="${wine}tools/mwcc_compiler/$mwcc_version/mwcceppc.exe $cflags -c $in -o $basedir && "
|
||||||
|
+ "$python tools/transform-dep.py $basefile.d $basefile.d",
|
||||||
|
description="MWCC $out",
|
||||||
|
depfile="$basefile.d",
|
||||||
|
deps="gcc",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("MWCC build with franklite")
|
||||||
|
n.rule(
|
||||||
|
name="mwcc_frank",
|
||||||
|
command="${wine}tools/mwcc_compiler/$mwcc_version/mwcceppc.exe $cflags -c $in -o $basedir && "
|
||||||
|
+ "$python tools/franklite.py $out $out && "
|
||||||
|
+ "$python tools/transform-dep.py $basefile.d $basefile.d",
|
||||||
|
description="FRANK $out",
|
||||||
|
depfile="$basefile.d",
|
||||||
|
deps="gcc",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Link ELF file")
|
||||||
|
n.rule(
|
||||||
|
name="link",
|
||||||
|
command="${wine}tools/mwcc_compiler/$mwcc_version/mwldeppc.exe $ldflags -o $out @$out.rsp",
|
||||||
|
description="LINK $out",
|
||||||
|
rspfile="$out.rsp",
|
||||||
|
rspfile_content="$in",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Assemble asm")
|
||||||
|
n.rule(
|
||||||
|
name="as",
|
||||||
|
command="$devkitppc/bin/powerpc-eabi-as $asflags -o $out $in -MD $out.d",
|
||||||
|
description="AS $out",
|
||||||
|
depfile="$out.d",
|
||||||
|
deps="gcc",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Create static library")
|
||||||
|
n.rule(
|
||||||
|
name="ar",
|
||||||
|
command="$devkitppc/bin/powerpc-eabi-ar crs $out $in",
|
||||||
|
description="AR $out",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
n.comment("Host build")
|
||||||
|
n.variable("host_cflags", "-I include/ -Wno-trigraphs")
|
||||||
|
n.variable(
|
||||||
|
"host_cppflags",
|
||||||
|
"-std=c++98 -I include/ -fno-exceptions -fno-rtti -D_CRT_SECURE_NO_WARNINGS -Wno-trigraphs -Wno-c++11-extensions",
|
||||||
|
)
|
||||||
|
n.rule(
|
||||||
|
name="host_cc",
|
||||||
|
command="clang $host_cflags -c -o $out $in",
|
||||||
|
description="host_cc $out",
|
||||||
|
)
|
||||||
|
n.rule(
|
||||||
|
name="host_cpp",
|
||||||
|
command="clang++ $host_cppflags -c -o $out $in",
|
||||||
|
description="host_c++ $out",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
|
###
|
||||||
|
# Rules for source files
|
||||||
|
###
|
||||||
|
n.comment("Source files")
|
||||||
|
all_source_files = []
|
||||||
|
all_host_source_files = []
|
||||||
|
for lib in LIBS:
|
||||||
|
inputs = []
|
||||||
|
if "lib" in lib:
|
||||||
|
lib_name = lib["lib"]
|
||||||
|
n.comment(f"{lib_name}.a")
|
||||||
|
else:
|
||||||
|
n.comment("Loose files")
|
||||||
|
for object in lib["objects"]:
|
||||||
|
completed = None
|
||||||
|
add_to_all = True
|
||||||
|
if type(object) is list:
|
||||||
|
if len(object) > 2:
|
||||||
|
add_to_all = object[2]
|
||||||
|
completed = object[1]
|
||||||
|
object = object[0]
|
||||||
|
|
||||||
|
mwcc_version = lib["mwcc_version"]
|
||||||
|
c_file = None
|
||||||
|
if os.path.exists(os.path.join("src", f"{object}.cpp")):
|
||||||
|
c_file = os.path.join("src", f"{object}.cpp")
|
||||||
|
elif os.path.exists(os.path.join("src", f"{object}.c")):
|
||||||
|
c_file = os.path.join("src", f"{object}.c")
|
||||||
|
if c_file is not None:
|
||||||
|
if completed is None:
|
||||||
|
print(f"Mark as incomplete: {c_file}")
|
||||||
|
rule = "mwcc"
|
||||||
|
if mwcc_version == "1.2.5e":
|
||||||
|
mwcc_version = "1.2.5"
|
||||||
|
rule = "mwcc_frank"
|
||||||
|
n.build(
|
||||||
|
outputs=f"$builddir/src/{object}.o",
|
||||||
|
rule=rule,
|
||||||
|
inputs=c_file,
|
||||||
variables={
|
variables={
|
||||||
"mwcc_version": mwcc_version,
|
"mwcc_version": mwcc_version,
|
||||||
"cflags": lib["cflags"],
|
"cflags": lib["cflags"],
|
||||||
"basedir": os.path.dirname(f"$builddir/src/{object}"),
|
"basedir": os.path.dirname(f"$builddir/src/{object}"),
|
||||||
"basefile": f"$builddir/src/{object}"
|
"basefile": f"$builddir/src/{object}",
|
||||||
})
|
},
|
||||||
if lib["host"]:
|
)
|
||||||
n.build(f"$builddir/host/{object}.o", "host_cc" if c_file.endswith(".c") else "host_cpp", c_file,
|
if lib["host"]:
|
||||||
|
n.build(
|
||||||
|
outputs=f"$builddir/host/{object}.o",
|
||||||
|
rule="host_cc" if c_file.endswith(".c") else "host_cpp",
|
||||||
|
inputs=c_file,
|
||||||
variables={
|
variables={
|
||||||
"basedir": os.path.dirname(f"$builddir/src/{object}"),
|
"basedir": os.path.dirname(f"$builddir/src/{object}"),
|
||||||
"basefile": f"$builddir/src/{object}"
|
"basefile": f"$builddir/src/{object}",
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
if add_to_all:
|
||||||
|
all_host_source_files.append(f"$builddir/host/{object}.o")
|
||||||
if add_to_all:
|
if add_to_all:
|
||||||
all_host_source_files.append(f"$builddir/host/{object}.o")
|
all_source_files.append(f"$builddir/src/{object}.o")
|
||||||
if add_to_all:
|
if os.path.exists(os.path.join("asm", f"{object}.s")):
|
||||||
all_source_files.append(f"$builddir/src/{object}.o")
|
n.build(
|
||||||
if os.path.exists(os.path.join("asm", f"{object}.s")):
|
outputs=f"$builddir/asm/{object}.o",
|
||||||
n.build(f"$builddir/asm/{object}.o", "as", f"asm/{object}.s")
|
rule="as",
|
||||||
if completed:
|
inputs=f"asm/{object}.s",
|
||||||
inputs.append(f"$builddir/src/{object}.o")
|
)
|
||||||
else:
|
|
||||||
inputs.append(f"$builddir/asm/{object}.o")
|
|
||||||
if ENABLE_STATIC_LIBS and "lib" in lib:
|
|
||||||
lib_name = lib["lib"]
|
|
||||||
n.build(f"$builddir/lib/{lib_name}.a", "ar", inputs)
|
|
||||||
n.newline()
|
|
||||||
|
|
||||||
n.comment("main.elf")
|
|
||||||
inputs = []
|
|
||||||
for lib in LIBS:
|
|
||||||
if ENABLE_STATIC_LIBS and "lib" in lib:
|
|
||||||
lib_name = lib["lib"]
|
|
||||||
inputs.append(f"$builddir/lib/{lib_name}.a")
|
|
||||||
else:
|
|
||||||
for object in lib["objects"]:
|
|
||||||
completed = False
|
|
||||||
|
|
||||||
if type(object) is list:
|
|
||||||
completed = object[1]
|
|
||||||
object = object[0]
|
|
||||||
|
|
||||||
if completed:
|
if completed:
|
||||||
inputs.append(f"$builddir/src/{object}.o")
|
inputs.append(f"$builddir/src/{object}.o")
|
||||||
else:
|
else:
|
||||||
inputs.append(f"$builddir/asm/{object}.o")
|
inputs.append(f"$builddir/asm/{object}.o")
|
||||||
if args.map:
|
if args.static_libs and "lib" in lib:
|
||||||
n.build("$builddir/main.elf", "link", inputs,
|
lib_name = lib["lib"]
|
||||||
implicit_outputs="$builddir/MetroidPrime.MAP")
|
n.build(
|
||||||
else:
|
outputs=f"$builddir/lib/{lib_name}.a",
|
||||||
n.build("$builddir/main.elf", "link", inputs)
|
rule="ar",
|
||||||
n.newline()
|
inputs=inputs,
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
###
|
###
|
||||||
# Helper rule for building all source files
|
# Link
|
||||||
###
|
###
|
||||||
n.comment("Adds a command for building all source files")
|
n.comment("Link")
|
||||||
n.build("all_source", "phony", all_source_files)
|
inputs = []
|
||||||
n.newline()
|
for lib in LIBS:
|
||||||
|
if args.static_libs and "lib" in lib:
|
||||||
|
lib_name = lib["lib"]
|
||||||
|
inputs.append(f"$builddir/lib/{lib_name}.a")
|
||||||
|
else:
|
||||||
|
for object in lib["objects"]:
|
||||||
|
completed = False
|
||||||
|
|
||||||
###
|
if type(object) is list:
|
||||||
# Helper rule for building all source files, with a host compiler
|
completed = object[1]
|
||||||
###
|
object = object[0]
|
||||||
n.comment("Adds a command for building all source files with a host compiler")
|
|
||||||
n.build("all_source_host", "phony", all_host_source_files)
|
|
||||||
n.newline()
|
|
||||||
|
|
||||||
###
|
if completed:
|
||||||
# Generate DOL
|
inputs.append(f"$builddir/src/{object}.o")
|
||||||
###
|
else:
|
||||||
n.comment("main.dol")
|
inputs.append(f"$builddir/asm/{object}.o")
|
||||||
# TODO MSVC?
|
if args.map:
|
||||||
n.rule(name="cc", command="cc -MMD -MT $out -MF $out.d $in -o $out",
|
n.build(
|
||||||
description="CC $out", depfile="$out.d", deps="gcc")
|
outputs="$builddir/main.elf",
|
||||||
n.build("build/elf2dol$exe", "cc", "tools/elf2dol.c")
|
rule="link",
|
||||||
n.build("build/metroidbuildinfo$exe", "cc", "tools/metroidbuildinfo.c")
|
inputs=inputs,
|
||||||
n.rule(name="elf2dol",
|
implicit_outputs="$builddir/MetroidPrime.MAP",
|
||||||
command=ALLOW_CHAIN+os.path.join("build", "elf2dol$exe")+" $in $out && " +
|
)
|
||||||
os.path.join("build", "metroidbuildinfo$exe") +
|
else:
|
||||||
" $out buildstrings/mp1.$version.build",
|
n.build(
|
||||||
description="DOL $out")
|
outputs="$builddir/main.elf",
|
||||||
n.build("$builddir/main.dol", "elf2dol", "$builddir/main.elf",
|
rule="link",
|
||||||
implicit=["build/elf2dol$exe", "build/metroidbuildinfo$exe"])
|
inputs=inputs,
|
||||||
n.newline()
|
)
|
||||||
|
|
||||||
###
|
|
||||||
# Check DOL hash
|
|
||||||
###
|
|
||||||
if args.check:
|
|
||||||
n.rule(name="check", command=ALLOW_CHAIN+"$sha1sum -c $in && touch $out",
|
|
||||||
description="CHECK $in")
|
|
||||||
n.build("$builddir/main.dol.ok", "check",
|
|
||||||
"sha1/mp1.$version.sha1", implicit="$builddir/main.dol")
|
|
||||||
n.newline()
|
n.newline()
|
||||||
|
|
||||||
###
|
###
|
||||||
# Progress script
|
# Helper rule for building all source files
|
||||||
###
|
###
|
||||||
if args.map:
|
n.comment("Build all source files")
|
||||||
n.rule(name="progress", command=ALLOW_CHAIN+"$python tools/calcprogress.py $in -o $out",
|
n.build(
|
||||||
description="PROGRESS $in")
|
outputs="all_source",
|
||||||
n.build("$builddir/main.dol.progress", "progress",
|
rule="phony",
|
||||||
["$builddir/main.dol", "$builddir/MetroidPrime.MAP"])
|
inputs=all_source_files,
|
||||||
|
)
|
||||||
n.newline()
|
n.newline()
|
||||||
|
|
||||||
###
|
###
|
||||||
# Regenerate on change
|
# Helper rule for building all source files, with a host compiler
|
||||||
###
|
###
|
||||||
n.comment("Regenerate build files if build script changes.")
|
n.comment("Build all source files with a host compiler")
|
||||||
n.rule(name="configure", command="$python configure.py $configure_args", generator=True)
|
n.build(
|
||||||
n.build("build.ninja", "configure", implicit=[
|
outputs="all_source_host",
|
||||||
"configure.py", "tools/ninja_syntax.py"])
|
rule="phony",
|
||||||
n.newline()
|
inputs=all_host_source_files,
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
###
|
###
|
||||||
# Default rule
|
# Tooling
|
||||||
###
|
###
|
||||||
dol_out = "$builddir/main.dol"
|
n.comment("decomp-toolkit")
|
||||||
if args.check:
|
if args.build_dtk:
|
||||||
dol_out = "$builddir/main.dol.ok"
|
n.variable("dtk", os.path.join("build", "tools", "release", "dtk$exe"))
|
||||||
if args.map:
|
n.rule(
|
||||||
n.default([dol_out, "$builddir/main.dol.progress"])
|
name="cargo",
|
||||||
else:
|
command="cargo build --release --manifest-path $in --bin $bin --target-dir $target",
|
||||||
n.default(dol_out)
|
description="CARGO $bin",
|
||||||
|
depfile="$target/release/$bin.d",
|
||||||
|
deps="gcc",
|
||||||
|
)
|
||||||
|
n.build(
|
||||||
|
outputs="$dtk",
|
||||||
|
rule="cargo",
|
||||||
|
inputs=os.path.join(args.build_dtk, "Cargo.toml"),
|
||||||
|
variables={
|
||||||
|
"bin": "dtk",
|
||||||
|
"target": os.path.join("build", "tools"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
n.variable("dtk", os.path.join("build", "tools", "dtk$exe"))
|
||||||
|
n.rule(
|
||||||
|
name="download_dtk",
|
||||||
|
command="$python tools/download_dtk.py $in $out",
|
||||||
|
description="DOWNLOAD $out",
|
||||||
|
)
|
||||||
|
n.build(
|
||||||
|
outputs="$dtk",
|
||||||
|
rule="download_dtk",
|
||||||
|
inputs="dtk_version",
|
||||||
|
implicit=["tools/download_dtk.py"],
|
||||||
|
)
|
||||||
|
n.rule(
|
||||||
|
name="elf2dol",
|
||||||
|
command=ALLOW_CHAIN
|
||||||
|
+ "$dtk elf2dol $in $out && "
|
||||||
|
+ "$dtk metroidbuildinfo $out buildstrings/mp1.$version.build",
|
||||||
|
description="DOL $out",
|
||||||
|
)
|
||||||
|
n.build(
|
||||||
|
outputs="$builddir/main.dol",
|
||||||
|
rule="elf2dol",
|
||||||
|
inputs="$builddir/main.elf",
|
||||||
|
implicit="$dtk",
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
with open("build.ninja", 'w') as f:
|
###
|
||||||
f.write(out.getvalue())
|
# Check DOL hash
|
||||||
n.close()
|
###
|
||||||
|
if args.check:
|
||||||
|
n.comment("Check DOL hash")
|
||||||
|
n.rule(
|
||||||
|
name="check",
|
||||||
|
command=ALLOW_CHAIN + "$dtk shasum -c $in && touch $out",
|
||||||
|
description="CHECK $in",
|
||||||
|
)
|
||||||
|
n.build(
|
||||||
|
outputs="$builddir/main.dol.ok",
|
||||||
|
rule="check",
|
||||||
|
inputs="sha1/mp1.$version.sha1",
|
||||||
|
implicit=["$builddir/main.dol", "$dtk"],
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
|
###
|
||||||
|
# Progress script
|
||||||
|
###
|
||||||
|
if args.map:
|
||||||
|
n.comment("Check progress")
|
||||||
|
n.rule(
|
||||||
|
name="progress",
|
||||||
|
command="$python progress.py $in -o $out",
|
||||||
|
description="PROGRESS $in",
|
||||||
|
)
|
||||||
|
n.build(
|
||||||
|
outputs="$builddir/main.dol.progress",
|
||||||
|
rule="progress",
|
||||||
|
inputs=["$builddir/main.dol", "$builddir/MetroidPrime.MAP"],
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
|
###
|
||||||
|
# Regenerate on change
|
||||||
|
###
|
||||||
|
n.comment("Reconfigure on change")
|
||||||
|
n.rule(
|
||||||
|
name="configure",
|
||||||
|
command="$python configure.py $configure_args",
|
||||||
|
generator=True,
|
||||||
|
)
|
||||||
|
n.build(
|
||||||
|
outputs="build.ninja",
|
||||||
|
rule="configure",
|
||||||
|
implicit=["configure.py", "tools/ninja_syntax.py"],
|
||||||
|
)
|
||||||
|
n.newline()
|
||||||
|
|
||||||
|
###
|
||||||
|
# Default rule
|
||||||
|
###
|
||||||
|
n.comment("Default rule")
|
||||||
|
if args.check:
|
||||||
|
dol_out = "$builddir/main.dol.ok"
|
||||||
|
else:
|
||||||
|
dol_out = "$builddir/main.dol"
|
||||||
|
if args.map:
|
||||||
|
n.default([dol_out, "$builddir/main.dol.progress"])
|
||||||
|
else:
|
||||||
|
n.default([dol_out])
|
||||||
|
|
||||||
|
with open("build.ninja", "w") as f:
|
||||||
|
f.write(out.getvalue())
|
||||||
|
n.close()
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
v0.1.0
|
|
@ -27,6 +27,8 @@ import math
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from configure import LIBS
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# #
|
# #
|
||||||
# Constants #
|
# Constants #
|
||||||
|
@ -88,13 +90,22 @@ if __name__ == "__main__":
|
||||||
parser.add_argument("-o", "--output", help="JSON output file")
|
parser.add_argument("-o", "--output", help="JSON output file")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# HACK: Check asm or src in obj_file.mk
|
# HACK: Check asm or src in configure.py
|
||||||
# to avoid counting .comm/.lcomm as decompiled
|
# to avoid counting .comm/.lcomm as decompiled
|
||||||
asm_objs = []
|
asm_objs = []
|
||||||
with open('obj_files.mk', 'r') as file:
|
for lib in LIBS:
|
||||||
for line in file:
|
for obj in lib["objects"]:
|
||||||
if "asm/" in line:
|
is_asm = False
|
||||||
asm_objs.append(line.strip().rsplit('/', 1)[-1].rstrip('\\'))
|
obj_name = None
|
||||||
|
if type(obj) is list:
|
||||||
|
obj_name = obj[0]
|
||||||
|
is_asm = not obj[1]
|
||||||
|
else:
|
||||||
|
obj_name = obj
|
||||||
|
is_asm = True
|
||||||
|
if is_asm:
|
||||||
|
name = obj_name.split('/')[-1]
|
||||||
|
asm_objs.append(f"{name}.o")
|
||||||
|
|
||||||
# Sum up DOL section sizes
|
# Sum up DOL section sizes
|
||||||
dol_handle = open(args.dol, "rb")
|
dol_handle = open(args.dol, "rb")
|
|
@ -1,15 +0,0 @@
|
||||||
CC := gcc
|
|
||||||
CFLAGS := -O3 -Wall -s
|
|
||||||
|
|
||||||
default: all
|
|
||||||
|
|
||||||
all: elf2dol metroidbuildinfo
|
|
||||||
|
|
||||||
elf2dol: elf2dol.c
|
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
|
||||||
|
|
||||||
metroidbuildinfo: metroidbuildinfo.c
|
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) elf2dol metroidbuildinfo
|
|
|
@ -8,50 +8,53 @@ script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
|
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
|
||||||
src_dir = os.path.join(root_dir, "src")
|
src_dir = os.path.join(root_dir, "src")
|
||||||
include_dirs = [
|
include_dirs = [
|
||||||
os.path.join(root_dir, "include"),
|
os.path.join(root_dir, "include"),
|
||||||
os.path.join(root_dir, "libc"),
|
os.path.join(root_dir, "libc"),
|
||||||
]
|
]
|
||||||
|
|
||||||
include_pattern = re.compile(r'^#include\s*[<"](.+?)[>"]$')
|
include_pattern = re.compile(r'^#include\s*[<"](.+?)[>"]$')
|
||||||
guard_pattern = re.compile(r'^#ifndef\s+(.*)$')
|
guard_pattern = re.compile(r"^#ifndef\s+(.*)$")
|
||||||
|
|
||||||
defines = set()
|
defines = set()
|
||||||
|
|
||||||
|
|
||||||
def import_h_file(in_file, r_path) -> str:
|
def import_h_file(in_file, r_path) -> str:
|
||||||
rel_path = os.path.join(root_dir, r_path, in_file)
|
rel_path = os.path.join(root_dir, r_path, in_file)
|
||||||
if os.path.exists(rel_path):
|
if os.path.exists(rel_path):
|
||||||
return import_c_file(rel_path)
|
return import_c_file(rel_path)
|
||||||
for include_dir in include_dirs:
|
for include_dir in include_dirs:
|
||||||
inc_path = os.path.join(include_dir, in_file)
|
inc_path = os.path.join(include_dir, in_file)
|
||||||
if os.path.exists(inc_path):
|
if os.path.exists(inc_path):
|
||||||
return import_c_file(inc_path)
|
return import_c_file(inc_path)
|
||||||
else:
|
else:
|
||||||
print("Failed to locate", in_file)
|
print("Failed to locate", in_file)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def import_c_file(in_file) -> str:
|
def import_c_file(in_file) -> str:
|
||||||
in_file = os.path.relpath(in_file, root_dir)
|
in_file = os.path.relpath(in_file, root_dir)
|
||||||
out_text = ''
|
out_text = ""
|
||||||
|
|
||||||
with open(in_file) as file:
|
with open(in_file) as file:
|
||||||
for idx, line in enumerate(file):
|
for idx, line in enumerate(file):
|
||||||
guard_match = guard_pattern.match(line.strip())
|
guard_match = guard_pattern.match(line.strip())
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
if guard_match:
|
if guard_match:
|
||||||
if guard_match[1] in defines:
|
if guard_match[1] in defines:
|
||||||
break
|
break
|
||||||
defines.add(guard_match[1])
|
defines.add(guard_match[1])
|
||||||
print("Processing file", in_file)
|
print("Processing file", in_file)
|
||||||
include_match = include_pattern.match(line.strip())
|
include_match = include_pattern.match(line.strip())
|
||||||
if include_match:
|
if include_match:
|
||||||
out_text += f"/* \"{in_file}\" line {idx} \"{include_match[1]}\" */\n"
|
out_text += f'/* "{in_file}" line {idx} "{include_match[1]}" */\n'
|
||||||
out_text += import_h_file(include_match[1], os.path.dirname(in_file))
|
out_text += import_h_file(include_match[1], os.path.dirname(in_file))
|
||||||
out_text += f"/* end \"{include_match[1]}\" */\n"
|
out_text += f'/* end "{include_match[1]}" */\n'
|
||||||
else:
|
else:
|
||||||
out_text += line
|
out_text += line
|
||||||
|
|
||||||
return out_text
|
return out_text
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="""Create a context file which can be used for decomp.me"""
|
description="""Create a context file which can be used for decomp.me"""
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
from sys import argv
|
|
||||||
|
|
||||||
def nextU32(f):
|
|
||||||
dat = f.read(4)
|
|
||||||
return int.from_bytes(dat, 'big')
|
|
||||||
|
|
||||||
with open(argv[1], 'rb') as dol:
|
|
||||||
offsets = [nextU32(dol) for i in range(18)]
|
|
||||||
addresses = [nextU32(dol) for i in range(18)]
|
|
||||||
sizes = [nextU32(dol) for i in range(18)]
|
|
||||||
|
|
||||||
target = int(argv[2], 16)
|
|
||||||
|
|
||||||
for i in range(0, 0x18):
|
|
||||||
offset = offsets[i]
|
|
||||||
size = sizes[i]
|
|
||||||
if offset <= target < offset + size:
|
|
||||||
section = i
|
|
||||||
delta = target - offset
|
|
||||||
break
|
|
||||||
|
|
||||||
print(hex(addresses[section] + delta))
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import argparse
|
||||||
|
import urllib.request
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = "https://github.com/encounter/decomp-toolkit"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("tag_file", help="file containing GitHub tag")
|
||||||
|
parser.add_argument("output", type=Path, help="output file path")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
with open(args.tag_file, "r") as f:
|
||||||
|
tag = f.readline().rstrip()
|
||||||
|
|
||||||
|
uname = os.uname()
|
||||||
|
suffix = ""
|
||||||
|
platform = uname.sysname.lower()
|
||||||
|
if platform == "darwin":
|
||||||
|
platform = "macos"
|
||||||
|
elif platform == "windows":
|
||||||
|
suffix = ".exe"
|
||||||
|
arch = uname.machine.lower()
|
||||||
|
if arch == "amd64":
|
||||||
|
arch = "x86_64"
|
||||||
|
|
||||||
|
url = f"{REPO}/releases/download/{tag}/dtk-{platform}-{arch}{suffix}"
|
||||||
|
output = args.output
|
||||||
|
# print(f"Downloading {url} to {output}")
|
||||||
|
urllib.request.urlretrieve(url, output)
|
||||||
|
|
||||||
|
st = os.stat(output)
|
||||||
|
os.chmod(output, st.st_mode | stat.S_IEXEC)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
504
tools/elf2dol.c
504
tools/elf2dol.c
|
@ -1,504 +0,0 @@
|
||||||
#include <errno.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
|
|
||||||
#ifndef MAX
|
|
||||||
//! Get the maximum of two values
|
|
||||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MIN
|
|
||||||
//! Get the minimum of two values
|
|
||||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
||||||
|
|
||||||
#define EI_NIDENT 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned char e_ident[EI_NIDENT];
|
|
||||||
uint16_t e_type;
|
|
||||||
uint16_t e_machine;
|
|
||||||
uint32_t e_version;
|
|
||||||
uint32_t e_entry;
|
|
||||||
uint32_t e_phoff;
|
|
||||||
uint32_t e_shoff;
|
|
||||||
uint32_t e_flags;
|
|
||||||
uint16_t e_ehsize;
|
|
||||||
uint16_t e_phentsize;
|
|
||||||
uint16_t e_phnum;
|
|
||||||
uint16_t e_shentsize;
|
|
||||||
uint16_t e_shnum;
|
|
||||||
uint16_t e_shstrndx;
|
|
||||||
} Elf32_Ehdr;
|
|
||||||
|
|
||||||
#define EI_CLASS 4
|
|
||||||
#define EI_DATA 5
|
|
||||||
#define EI_VERSION 6
|
|
||||||
#define EI_PAD 7
|
|
||||||
#define EI_NIDENT 16
|
|
||||||
|
|
||||||
#define ELFCLASS32 1
|
|
||||||
#define ELFDATA2MSB 2
|
|
||||||
#define EV_CURRENT 1
|
|
||||||
|
|
||||||
#define ET_EXEC 2
|
|
||||||
#define EM_PPC 20
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t p_type;
|
|
||||||
uint32_t p_offset;
|
|
||||||
uint32_t p_vaddr;
|
|
||||||
uint32_t p_paddr;
|
|
||||||
uint32_t p_filesz;
|
|
||||||
uint32_t p_memsz;
|
|
||||||
uint32_t p_flags;
|
|
||||||
uint32_t p_align;
|
|
||||||
} Elf32_Phdr;
|
|
||||||
|
|
||||||
#define PT_LOAD 1
|
|
||||||
#define PF_R 4
|
|
||||||
#define PF_W 2
|
|
||||||
#define PF_X 1
|
|
||||||
|
|
||||||
int verbosity = 0;
|
|
||||||
|
|
||||||
#if BYTE_ORDER == BIG_ENDIAN
|
|
||||||
|
|
||||||
#define swap32(x) (x)
|
|
||||||
#define swap16(x) (x)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static inline uint32_t swap32(uint32_t v) {
|
|
||||||
return (v >> 24) | ((v >> 8) & 0x0000FF00) | ((v << 8) & 0x00FF0000) |
|
|
||||||
(v << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t swap16(uint16_t v) { return (v >> 8) | (v << 8); }
|
|
||||||
|
|
||||||
#endif /* BIG_ENDIAN */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
uint32_t text_off[7];
|
|
||||||
uint32_t data_off[11];
|
|
||||||
uint32_t text_addr[7];
|
|
||||||
uint32_t data_addr[11];
|
|
||||||
uint32_t text_size[7];
|
|
||||||
uint32_t data_size[11];
|
|
||||||
uint32_t bss_addr;
|
|
||||||
uint32_t bss_size;
|
|
||||||
uint32_t entry;
|
|
||||||
uint32_t pad[7];
|
|
||||||
} DOL_hdr;
|
|
||||||
|
|
||||||
#define HAVE_BSS 1
|
|
||||||
|
|
||||||
#define MAX_TEXT_SEGMENTS 7
|
|
||||||
#define MAX_DATA_SEGMENTS 11
|
|
||||||
|
|
||||||
#define DOL_ALIGNMENT 32
|
|
||||||
|
|
||||||
#define DOL_ALIGN(x) (((x) + DOL_ALIGNMENT - 1) & ~(DOL_ALIGNMENT - 1))
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DOL_hdr header;
|
|
||||||
int text_cnt;
|
|
||||||
int data_cnt;
|
|
||||||
uint32_t text_elf_off[7];
|
|
||||||
uint32_t data_elf_off[11];
|
|
||||||
uint32_t flags;
|
|
||||||
FILE* elf;
|
|
||||||
} DOL_map;
|
|
||||||
|
|
||||||
void usage(const char* name) {
|
|
||||||
fprintf(stderr, "Usage: %s [-h] [-v] [--] elf-file dol-file\n", name);
|
|
||||||
fprintf(stderr, " Convert an ELF file to a DOL file (by segments)\n");
|
|
||||||
fprintf(stderr, " Options:\n");
|
|
||||||
fprintf(stderr, " -h Show this help\n");
|
|
||||||
fprintf(stderr, " -v Be more verbose (twice for even more)\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#define die(x) \
|
|
||||||
{ \
|
|
||||||
fprintf(stderr, x "\n"); \
|
|
||||||
exit(1); \
|
|
||||||
}
|
|
||||||
#define perrordie(x) \
|
|
||||||
{ \
|
|
||||||
perror(x); \
|
|
||||||
exit(1); \
|
|
||||||
}
|
|
||||||
|
|
||||||
void ferrordie(FILE* f, const char* str) {
|
|
||||||
if (ferror(f)) {
|
|
||||||
fprintf(stderr, "Error while ");
|
|
||||||
perrordie(str);
|
|
||||||
} else if (feof(f)) {
|
|
||||||
fprintf(stderr, "EOF while %s\n", str);
|
|
||||||
exit(1);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unknown error while %s\n", str);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_bss(DOL_map* map, uint32_t paddr, uint32_t memsz) {
|
|
||||||
if (map->flags & HAVE_BSS) {
|
|
||||||
uint32_t curr_start = swap32(map->header.bss_addr);
|
|
||||||
uint32_t curr_size = swap32(map->header.bss_size);
|
|
||||||
if (paddr < curr_start)
|
|
||||||
map->header.bss_addr = swap32(paddr);
|
|
||||||
// Total BSS size should be the end of the last bss section minus the
|
|
||||||
// start of the first bss section.
|
|
||||||
if (paddr + memsz > curr_start + curr_size)
|
|
||||||
map->header.bss_size = swap32(paddr + memsz - curr_start);
|
|
||||||
} else {
|
|
||||||
map->header.bss_addr = swap32(paddr);
|
|
||||||
map->header.bss_size = swap32(memsz);
|
|
||||||
map->flags |= HAVE_BSS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_elf_segments(DOL_map* map, const char* elf) {
|
|
||||||
int read, i;
|
|
||||||
Elf32_Ehdr ehdr;
|
|
||||||
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "Reading ELF file...\n");
|
|
||||||
|
|
||||||
map->elf = fopen(elf, "rb");
|
|
||||||
if (!map->elf)
|
|
||||||
perrordie("Could not open ELF file");
|
|
||||||
|
|
||||||
read = fread(&ehdr, sizeof(ehdr), 1, map->elf);
|
|
||||||
if (read != 1)
|
|
||||||
ferrordie(map->elf, "reading ELF header");
|
|
||||||
|
|
||||||
if (memcmp(&ehdr.e_ident[0], "\177ELF", 4))
|
|
||||||
die("Invalid ELF header");
|
|
||||||
if (ehdr.e_ident[EI_CLASS] != ELFCLASS32)
|
|
||||||
die("Invalid ELF class");
|
|
||||||
if (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
|
|
||||||
die("Invalid ELF byte order");
|
|
||||||
if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
|
|
||||||
die("Invalid ELF ident version");
|
|
||||||
if (swap32(ehdr.e_version) != EV_CURRENT)
|
|
||||||
die("Invalid ELF version");
|
|
||||||
if (swap16(ehdr.e_type) != ET_EXEC)
|
|
||||||
die("ELF is not an executable");
|
|
||||||
if (swap16(ehdr.e_machine) != EM_PPC)
|
|
||||||
die("Machine is not PowerPC");
|
|
||||||
if (!swap32(ehdr.e_entry))
|
|
||||||
die("ELF has no entrypoint");
|
|
||||||
|
|
||||||
map->header.entry = ehdr.e_entry;
|
|
||||||
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "Valid ELF header found\n");
|
|
||||||
|
|
||||||
uint16_t phnum = swap16(ehdr.e_phnum);
|
|
||||||
uint32_t phoff = swap32(ehdr.e_phoff);
|
|
||||||
Elf32_Phdr* phdrs;
|
|
||||||
|
|
||||||
if (!phnum || !phoff)
|
|
||||||
die("ELF has no program headers");
|
|
||||||
|
|
||||||
if (swap16(ehdr.e_phentsize) != sizeof(Elf32_Phdr))
|
|
||||||
die("Invalid program header entry size");
|
|
||||||
|
|
||||||
phdrs = malloc(phnum * sizeof(Elf32_Phdr));
|
|
||||||
|
|
||||||
if (fseek(map->elf, phoff, SEEK_SET) < 0)
|
|
||||||
ferrordie(map->elf, "reading ELF program headers");
|
|
||||||
read = fread(phdrs, sizeof(Elf32_Phdr), phnum, map->elf);
|
|
||||||
if (read != phnum)
|
|
||||||
ferrordie(map->elf, "reading ELF program headers");
|
|
||||||
|
|
||||||
for (i = 0; i < phnum; i++) {
|
|
||||||
if (swap32(phdrs[i].p_type) == PT_LOAD) {
|
|
||||||
uint32_t offset = swap32(phdrs[i].p_offset);
|
|
||||||
uint32_t paddr = swap32(phdrs[i].p_vaddr);
|
|
||||||
uint32_t filesz = swap32(phdrs[i].p_filesz);
|
|
||||||
uint32_t memsz = swap32(phdrs[i].p_memsz);
|
|
||||||
uint32_t flags = swap32(phdrs[i].p_flags);
|
|
||||||
if (memsz) {
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "PHDR %d: 0x%x [0x%x] -> 0x%08x [0x%x] flags 0x%x\n",
|
|
||||||
i, offset, filesz, paddr, memsz, flags);
|
|
||||||
if (flags & PF_X) {
|
|
||||||
// TEXT segment
|
|
||||||
if (!(flags & PF_R))
|
|
||||||
fprintf(stderr, "Warning: non-readable segment %d\n", i);
|
|
||||||
if (flags & PF_W)
|
|
||||||
fprintf(stderr, "Warning: writable and executable segment %d\n", i);
|
|
||||||
if (filesz > memsz) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Error: TEXT segment %d memory size (0x%x) smaller than "
|
|
||||||
"file size (0x%x)\n",
|
|
||||||
i, memsz, filesz);
|
|
||||||
exit(1);
|
|
||||||
} else if (memsz > filesz) {
|
|
||||||
add_bss(map, paddr + filesz, memsz - filesz);
|
|
||||||
}
|
|
||||||
if (map->text_cnt >= MAX_TEXT_SEGMENTS) {
|
|
||||||
die("Error: Too many TEXT segments");
|
|
||||||
}
|
|
||||||
map->header.text_addr[map->text_cnt] = swap32(paddr);
|
|
||||||
map->header.text_size[map->text_cnt] = swap32(filesz);
|
|
||||||
map->text_elf_off[map->text_cnt] = offset;
|
|
||||||
map->text_cnt++;
|
|
||||||
} else {
|
|
||||||
// DATA or BSS segment
|
|
||||||
if (!(flags & PF_R))
|
|
||||||
fprintf(stderr, "Warning: non-readable segment %d\n", i);
|
|
||||||
if (filesz == 0) {
|
|
||||||
// BSS segment
|
|
||||||
add_bss(map, paddr, memsz);
|
|
||||||
} else {
|
|
||||||
// DATA segment
|
|
||||||
if (filesz > memsz) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Error: segment %d memory size (0x%x) is smaller than "
|
|
||||||
"file size (0x%x)\n",
|
|
||||||
i, memsz, filesz);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (map->data_cnt >= MAX_DATA_SEGMENTS) {
|
|
||||||
die("Error: Too many DATA segments");
|
|
||||||
}
|
|
||||||
map->header.data_addr[map->data_cnt] = swap32(paddr);
|
|
||||||
map->header.data_size[map->data_cnt] = swap32(filesz);
|
|
||||||
map->data_elf_off[map->data_cnt] = offset;
|
|
||||||
map->data_cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (verbosity >= 1)
|
|
||||||
fprintf(stderr, "Skipping empty program header %d\n", i);
|
|
||||||
}
|
|
||||||
} else if (verbosity >= 1) {
|
|
||||||
fprintf(stderr, "Skipping program header %d of type %d\n", i,
|
|
||||||
swap32(phdrs[i].p_type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (verbosity >= 2) {
|
|
||||||
fprintf(stderr, "Segments:\n");
|
|
||||||
for (i = 0; i < map->text_cnt; i++) {
|
|
||||||
fprintf(stderr, " TEXT %d: 0x%08x [0x%x] from ELF offset 0x%x\n", i,
|
|
||||||
swap32(map->header.text_addr[i]),
|
|
||||||
swap32(map->header.text_size[i]), map->text_elf_off[i]);
|
|
||||||
}
|
|
||||||
for (i = 0; i < map->data_cnt; i++) {
|
|
||||||
fprintf(stderr, " DATA %d: 0x%08x [0x%x] from ELF offset 0x%x\n", i,
|
|
||||||
swap32(map->header.data_addr[i]),
|
|
||||||
swap32(map->header.data_size[i]), map->data_elf_off[i]);
|
|
||||||
}
|
|
||||||
if (map->flags & HAVE_BSS)
|
|
||||||
fprintf(stderr, " BSS segment: 0x%08x [0x%x]\n",
|
|
||||||
swap32(map->header.bss_addr), swap32(map->header.bss_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_dol(DOL_map* map) {
|
|
||||||
uint32_t fpos;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "Laying out DOL file...\n");
|
|
||||||
|
|
||||||
fpos = DOL_ALIGN(sizeof(DOL_hdr));
|
|
||||||
|
|
||||||
for (i = 0; i < map->text_cnt; i++) {
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, " TEXT segment %d at 0x%x\n", i, fpos);
|
|
||||||
map->header.text_off[i] = swap32(fpos);
|
|
||||||
fpos = DOL_ALIGN(fpos + swap32(map->header.text_size[i]));
|
|
||||||
}
|
|
||||||
for (i = 0; i < map->data_cnt; i++) {
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, " DATA segment %d at 0x%x\n", i, fpos);
|
|
||||||
map->header.data_off[i] = swap32(fpos);
|
|
||||||
fpos = DOL_ALIGN(fpos + swap32(map->header.data_size[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map->text_cnt == 0) {
|
|
||||||
if (verbosity >= 1)
|
|
||||||
fprintf(stderr,
|
|
||||||
"Note: adding dummy TEXT segment to work around IOS bug\n");
|
|
||||||
map->header.text_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
|
|
||||||
}
|
|
||||||
if (map->data_cnt == 0) {
|
|
||||||
if (verbosity >= 1)
|
|
||||||
fprintf(stderr,
|
|
||||||
"Note: adding dummy DATA segment to work around IOS bug\n");
|
|
||||||
map->header.data_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BLOCK (1024 * 1024)
|
|
||||||
|
|
||||||
void fcpy(FILE* dst, FILE* src, uint32_t dst_off, uint32_t src_off,
|
|
||||||
uint32_t size) {
|
|
||||||
int left = size;
|
|
||||||
int read;
|
|
||||||
int written;
|
|
||||||
int block;
|
|
||||||
void* blockbuf;
|
|
||||||
|
|
||||||
if (fseek(src, src_off, SEEK_SET) < 0)
|
|
||||||
ferrordie(src, "reading ELF segment data");
|
|
||||||
if (fseek(dst, dst_off, SEEK_SET) < 0)
|
|
||||||
ferrordie(dst, "writing DOL segment data");
|
|
||||||
|
|
||||||
blockbuf = malloc(MIN(BLOCK, left));
|
|
||||||
|
|
||||||
while (left) {
|
|
||||||
block = MIN(BLOCK, left);
|
|
||||||
read = fread(blockbuf, 1, block, src);
|
|
||||||
if (read != block) {
|
|
||||||
free(blockbuf);
|
|
||||||
ferrordie(src, "reading ELF segment data");
|
|
||||||
}
|
|
||||||
written = fwrite(blockbuf, 1, block, dst);
|
|
||||||
if (written != block) {
|
|
||||||
free(blockbuf);
|
|
||||||
ferrordie(dst, "writing DOL segment data");
|
|
||||||
}
|
|
||||||
left -= block;
|
|
||||||
}
|
|
||||||
free(blockbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fpad(FILE* dst, uint32_t dst_off, uint32_t size) {
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
if (fseek(dst, dst_off, SEEK_SET) < 0)
|
|
||||||
ferrordie(dst, "writing DOL segment data");
|
|
||||||
for (i = 0; i < size; i++)
|
|
||||||
fputc(0, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_dol(DOL_map* map, const char* dol) {
|
|
||||||
FILE* dolf;
|
|
||||||
int written;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "Writing DOL file...\n");
|
|
||||||
|
|
||||||
dolf = fopen(dol, "wb");
|
|
||||||
if (!dolf)
|
|
||||||
perrordie("Could not open DOL file");
|
|
||||||
|
|
||||||
if (verbosity >= 2) {
|
|
||||||
fprintf(stderr, "DOL header:\n");
|
|
||||||
for (i = 0; i < MAX(1, map->text_cnt); i++)
|
|
||||||
fprintf(stderr, " TEXT %d @ 0x%08x [0x%x] off 0x%x\n", i,
|
|
||||||
swap32(map->header.text_addr[i]),
|
|
||||||
swap32(map->header.text_size[i]),
|
|
||||||
swap32(map->header.text_off[i]));
|
|
||||||
for (i = 0; i < MAX(1, map->data_cnt); i++)
|
|
||||||
fprintf(stderr, " DATA %d @ 0x%08x [0x%x] off 0x%x\n", i,
|
|
||||||
swap32(map->header.data_addr[i]),
|
|
||||||
swap32(map->header.data_size[i]),
|
|
||||||
swap32(map->header.data_off[i]));
|
|
||||||
if (swap32(map->header.bss_addr) && swap32(map->header.bss_size))
|
|
||||||
fprintf(stderr, " BSS @ 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
|
|
||||||
swap32(map->header.bss_size));
|
|
||||||
fprintf(stderr, " Entry: 0x%08x\n", swap32(map->header.entry));
|
|
||||||
fprintf(stderr, "Writing DOL header...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write DOL header with aligned text and data section sizes
|
|
||||||
DOL_hdr aligned_header = map->header;
|
|
||||||
for (i = 0; i < ARRAY_COUNT(aligned_header.text_size); i++)
|
|
||||||
aligned_header.text_size[i] =
|
|
||||||
swap32(DOL_ALIGN(swap32(aligned_header.text_size[i])));
|
|
||||||
for (i = 0; i < ARRAY_COUNT(aligned_header.data_size); i++)
|
|
||||||
aligned_header.data_size[i] =
|
|
||||||
swap32(DOL_ALIGN(swap32(aligned_header.data_size[i])));
|
|
||||||
written = fwrite(&aligned_header, sizeof(DOL_hdr), 1, dolf);
|
|
||||||
if (written != 1)
|
|
||||||
ferrordie(dolf, "writing DOL header");
|
|
||||||
|
|
||||||
for (i = 0; i < map->text_cnt; i++) {
|
|
||||||
uint32_t size = swap32(map->header.text_size[i]);
|
|
||||||
uint32_t padded_size = DOL_ALIGN(size);
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "Writing TEXT segment %d...\n", i);
|
|
||||||
fcpy(dolf, map->elf, swap32(map->header.text_off[i]), map->text_elf_off[i],
|
|
||||||
size);
|
|
||||||
if (padded_size > size)
|
|
||||||
fpad(dolf, swap32(map->header.text_off[i]) + size, padded_size - size);
|
|
||||||
}
|
|
||||||
for (i = 0; i < map->data_cnt; i++) {
|
|
||||||
uint32_t size = swap32(map->header.data_size[i]);
|
|
||||||
uint32_t padded_size = DOL_ALIGN(size);
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "Writing DATA segment %d...\n", i);
|
|
||||||
fcpy(dolf, map->elf, swap32(map->header.data_off[i]), map->data_elf_off[i],
|
|
||||||
size);
|
|
||||||
if (padded_size > size)
|
|
||||||
fpad(dolf, swap32(map->header.data_off[i]) + size, padded_size - size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbosity >= 2)
|
|
||||||
fprintf(stderr, "All done!\n");
|
|
||||||
|
|
||||||
fclose(map->elf);
|
|
||||||
fclose(dolf);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
char** arg;
|
|
||||||
|
|
||||||
if (argc < 2) {
|
|
||||||
usage(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
arg = &argv[1];
|
|
||||||
argc--;
|
|
||||||
|
|
||||||
while (argc && *arg[0] == '-') {
|
|
||||||
if (!strcmp(*arg, "-h")) {
|
|
||||||
usage(argv[0]);
|
|
||||||
return 1;
|
|
||||||
} else if (!strcmp(*arg, "-v")) {
|
|
||||||
verbosity++;
|
|
||||||
} else if (!strcmp(*arg, "--")) {
|
|
||||||
arg++;
|
|
||||||
argc--;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unrecognized option %s\n", *arg);
|
|
||||||
usage(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
arg++;
|
|
||||||
argc--;
|
|
||||||
}
|
|
||||||
if (argc < 2) {
|
|
||||||
usage(argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* elf_file = arg[0];
|
|
||||||
const char* dol_file = arg[1];
|
|
||||||
|
|
||||||
DOL_map map;
|
|
||||||
|
|
||||||
memset(&map, 0, sizeof(map));
|
|
||||||
|
|
||||||
read_elf_segments(&map, elf_file);
|
|
||||||
map_dol(&map);
|
|
||||||
write_dol(&map, dol_file);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define VERSION_MAX_LEN (size_t)(35)
|
|
||||||
#define METROID_BUILD_INFO_TAG "!#$MetroidBuildInfo!#$"
|
|
||||||
|
|
||||||
void* memmem(const void* l, size_t l_len, const void* s, size_t s_len) {
|
|
||||||
register char *cur, *last;
|
|
||||||
const char* cl = (const char*)l;
|
|
||||||
const char* cs = (const char*)s;
|
|
||||||
|
|
||||||
/* we need something to compare */
|
|
||||||
if (l_len == 0 || s_len == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* "s" must be smaller or equal to "l" */
|
|
||||||
if (l_len < s_len)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* special case where s_len == 1 */
|
|
||||||
if (s_len == 1)
|
|
||||||
return memchr(l, (int)*cs, l_len);
|
|
||||||
|
|
||||||
/* the last position where its possible to find "s" in "l" */
|
|
||||||
last = (char*)cl + l_len - s_len;
|
|
||||||
|
|
||||||
for (cur = (char*)cl; cur <= last; cur++)
|
|
||||||
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
|
|
||||||
return cur;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
|
||||||
if (argc < 3) {
|
|
||||||
fprintf(stdout, "\t--- METROID BUILD INFO ---\n"
|
|
||||||
"\tWritten by Phillip \"Antidote\" Stephens\n"
|
|
||||||
"\tReleased under the MIT License\n\n"
|
|
||||||
"\tSets the MetroidBuildInfo tag value in a given binary\n"
|
|
||||||
"\tThe version string can be a maximum of 35 characters,\n"
|
|
||||||
"\texcluding null terminator\n"
|
|
||||||
"\t--------------------------\n"
|
|
||||||
|
|
||||||
);
|
|
||||||
fprintf(stdout, "Usage:\n"
|
|
||||||
"\tmetroidbuildinfo <binary> <build_file>\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Let's try to get the source binary */
|
|
||||||
FILE* source = fopen(argv[1], "rb");
|
|
||||||
if (!source) {
|
|
||||||
fprintf(stderr, "Unable to open '%s'\nPlease ensure the file exists!\n", argv[1]);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
char build_string[36] = {0};
|
|
||||||
FILE* build = fopen(argv[2], "rb");
|
|
||||||
|
|
||||||
if (!build) {
|
|
||||||
fprintf(stderr, "Unable to open '%s'\nPlease ensure the file exists!\n", argv[2]);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
size_t read_len = fread(build_string, 1, 35, build);
|
|
||||||
fclose(build);
|
|
||||||
|
|
||||||
if (read_len <= 0) {
|
|
||||||
fprintf(stderr, "Empty file %s specified for build version!\n", argv[2]);
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
build_string[strcspn(build_string, "\r\n")] = '\0';
|
|
||||||
|
|
||||||
/* Get source length */
|
|
||||||
fseek(source, 0, SEEK_END);
|
|
||||||
size_t source_len = ftell(source);
|
|
||||||
rewind(source);
|
|
||||||
void* source_buf = malloc(source_len);
|
|
||||||
if (source_buf == NULL) {
|
|
||||||
fprintf(stderr, "Unable to allocate buffer of size %zubytes!\n", source_len);
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
fread(source_buf, 1, source_len, source);
|
|
||||||
fclose(source);
|
|
||||||
|
|
||||||
/* Find the build info tag so we can stuff our version info in the binary */
|
|
||||||
void* ptr =
|
|
||||||
memmem(source_buf, source_len, METROID_BUILD_INFO_TAG, strlen(METROID_BUILD_INFO_TAG));
|
|
||||||
if (ptr == NULL) {
|
|
||||||
fprintf(stderr, "Unable to find build info tag in source!\n");
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lets actually copy over the build string */
|
|
||||||
strcpy(ptr + strlen(METROID_BUILD_INFO_TAG), build_string);
|
|
||||||
|
|
||||||
/* Now attempt to open the target file */
|
|
||||||
FILE* target = fopen(argv[1], "wb");
|
|
||||||
|
|
||||||
if (!target) {
|
|
||||||
fprintf(stderr, "Unable to open '%s'\nPlease ensure you have write permissions!\n", argv[1]);
|
|
||||||
return -7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally write the buffer to the target file */
|
|
||||||
fwrite(source_buf, 1, source_len, target);
|
|
||||||
fclose(target);
|
|
||||||
|
|
||||||
/* Don't leak */
|
|
||||||
free(source_buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue