├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── TODO ├── blast.c ├── config.h ├── configs ├── blast_corps.e.code.yaml ├── blast_corps.e.yaml ├── blast_corps.u.1.0.code.yaml ├── blast_corps.u.1.0.yaml ├── blast_corps.u.1.1.code.yaml ├── blast_corps.u.1.1.yaml ├── blast_dozer.code.yaml ├── blast_dozer.yaml ├── doraemon.yaml ├── mk64.u.yaml ├── pifboot.yaml ├── sm64.e.yaml ├── sm64.j.yaml ├── sm64.shindou.yaml └── sm64.u.yaml ├── examples ├── README.md ├── behavior │ └── sm64.rotating_sign.patch ├── coin_colors │ ├── coin_colors.ips │ ├── doors_trees_coins.0x05780.ia16.png │ ├── doors_trees_coins.0x05F80.ia16.png │ ├── doors_trees_coins.0x06780.ia16.png │ └── doors_trees_coins.0x06F80.ia16.png ├── hello_c │ ├── gen │ │ ├── extended.c │ │ └── sm64.h │ └── hello_c.patch ├── hello_world │ └── sm64.hello_world.patch ├── hud_toggle │ └── sm64.hudtoggle.patch ├── skip_screens │ ├── skip_lakitu.patch │ ├── skip_mario.patch │ ├── skip_menu.patch │ ├── skip_peach.patch │ └── skip_title.patch └── texture_swap │ ├── castle_grounds_segment7.0x0EAE8.ia8.png │ ├── castle_grounds_textures.0x01000.png │ ├── font_graphics.0x122B8.ia8.png │ └── water_skybox.0x00000.skybox.png ├── f3d.c ├── f3d2obj.c ├── libblast.h ├── libmio0.c ├── libmio0.h ├── libsfx.c ├── libsfx.h ├── libsm64.c ├── libsm64.h ├── mipsdisasm.c ├── mipsdisasm.h ├── n64cksum.c ├── n64graphics.c ├── n64graphics.h ├── n64split.c ├── n64split.collision.mtl.h ├── n64split.makefile.h ├── release ├── licenses │ ├── capstone.LICENSE │ ├── gnu_make.LICENSE │ ├── libyaml.LICENSE │ └── n64split.LICENSE ├── n64split.README.txt ├── sm64extend.README.txt └── split.bat ├── sm64compress.c ├── sm64extend.c ├── sm64geo.c ├── sm64walk.c ├── strutils.c ├── strutils.h ├── tests └── example1 │ ├── Makefile │ ├── example1.asm │ └── textures │ ├── heart.0.ia16.png │ └── heart.0.rgba16.png ├── tools ├── Makefile ├── dma2config.py ├── hudtable.c ├── jalfind.c ├── match_signatures.c ├── mk64karts.c ├── montage.c ├── n64ci.c ├── sm64collision.c └── sm64text.c ├── utils.c ├── utils.h └── yamlconfig.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.obj 4 | 5 | # Precompiled Headers 6 | *.gch 7 | *.pch 8 | 9 | # Libraries 10 | *.lib 11 | *.a 12 | 13 | # Shared objects (inc. Windows DLLs) 14 | *.dll 15 | *.so 16 | *.so.* 17 | *.dylib 18 | 19 | # Executables 20 | *.exe 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/stb"] 2 | path = ext/stb 3 | url = https://github.com/nothings/stb.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(sm64tools C) 4 | 5 | if (MSVC) 6 | # Always static linking on windows 7 | add_definitions(/MT) 8 | else () 9 | add_definitions(-Wextra) 10 | endif () 11 | 12 | set(CMAKE_C_FLAGS "${GCC_EXTRA_CFLAGS}") 13 | set(CMAKE_EXE_LINKER_FLAGS "${GCC_EXTRA_LDFLAGS}") 14 | 15 | include_directories(${CMAKE_SOURCE_DIR}/ext) 16 | include_directories("${PROJECT_SOURCE_DIR}/external/include") 17 | link_directories("${PROJECT_SOURCE_DIR}/external/lib") 18 | 19 | add_library(sm64 STATIC libmio0.c libsm64.c utils.c) 20 | 21 | add_executable(sm64extend sm64extend.c) 22 | target_link_libraries(sm64extend sm64) 23 | 24 | add_executable(sm64compress sm64compress.c) 25 | target_link_libraries(sm64compress sm64) 26 | 27 | add_executable(sm64walk sm64walk.c) 28 | target_link_libraries(sm64walk sm64) 29 | 30 | add_executable(f3d f3d.c utils.c) 31 | 32 | add_executable(f3d2obj blast.c f3d2obj.c n64graphics.c utils.c) 33 | target_link_libraries(f3d2obj png z) 34 | 35 | add_executable(sm64geo sm64geo.c utils.c) 36 | 37 | add_executable(mio0 libmio0.c) 38 | set_target_properties(mio0 PROPERTIES COMPILE_DEFINITIONS "MIO0_STANDALONE") 39 | 40 | add_executable(mipsdisasm mipsdisasm.c utils.c yamlconfig.c) 41 | set_target_properties(mipsdisasm PROPERTIES COMPILE_DEFINITIONS "MIPSDISASM_STANDALONE") 42 | target_link_libraries(mipsdisasm capstone yaml) 43 | 44 | add_executable(n64cksum n64cksum.c) 45 | target_link_libraries(n64cksum sm64) 46 | 47 | add_executable(n64graphics n64graphics.c utils.c) 48 | set_target_properties(n64graphics PROPERTIES COMPILE_DEFINITIONS "N64GRAPHICS_STANDALONE") 49 | target_link_libraries(n64graphics png z) 50 | 51 | add_executable(n64split blast.c libsfx.c mipsdisasm.c n64split.c n64graphics.c strutils.c yamlconfig.c) 52 | target_link_libraries(n64split sm64 capstone yaml z) 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Q 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ################ Target Executable and Sources ############### 2 | 3 | SM64_LIB := libsm64.a 4 | COMPRESS_TARGET := sm64compress 5 | CKSUM_TARGET := n64cksum 6 | DISASM_TARGET := mipsdisasm 7 | EXTEND_TARGET := sm64extend 8 | F3D_TARGET := f3d 9 | F3D2OBJ_TARGET := f3d2obj 10 | GEO_TARGET := sm64geo 11 | GRAPHICS_TARGET := n64graphics 12 | MIO0_TARGET := mio0 13 | SPLIT_TARGET := n64split 14 | WALK_TARGET := sm64walk 15 | 16 | LIB_SRC_FILES := libmio0.c \ 17 | libsm64.c \ 18 | libsfx.c \ 19 | utils.c 20 | 21 | CKSUM_SRC_FILES := n64cksum.c 22 | 23 | COMPRESS_SRC_FILES := sm64compress.c 24 | 25 | DISASM_SRC_FILES := mipsdisasm.c \ 26 | utils.c 27 | 28 | EXTEND_SRC_FILES := sm64extend.c 29 | 30 | F3D_SRC_FILES := f3d.c \ 31 | utils.c 32 | 33 | F3D2OBJ_SRC_FILES := blast.c \ 34 | f3d2obj.c \ 35 | n64graphics.c \ 36 | utils.c 37 | 38 | GEO_SRC_FILES := sm64geo.c \ 39 | utils.c 40 | 41 | GRAPHICS_SRC_FILES := n64graphics.c \ 42 | utils.c 43 | 44 | SPLIT_SRC_FILES := blast.c \ 45 | libmio0.c \ 46 | libsfx.c \ 47 | mipsdisasm.c \ 48 | n64graphics.c \ 49 | n64split.c \ 50 | strutils.c \ 51 | utils.c \ 52 | yamlconfig.c 53 | 54 | OBJ_DIR = ./obj 55 | 56 | ##################### Compiler Options ####################### 57 | 58 | WIN64_CROSS = x86_64-w64-mingw32- 59 | WIN32_CROSS = i686-w64-mingw32- 60 | #CROSS = $(WIN32_CROSS) 61 | CC = $(CROSS)gcc 62 | LD = $(CC) 63 | AR = $(CROSS)ar 64 | 65 | INCLUDES = -I./ext 66 | DEFS = 67 | # Release flags 68 | CFLAGS = -Wall -Wextra -Wno-format-overflow -O2 -ffunction-sections -fdata-sections $(INCLUDES) $(DEFS) -MMD 69 | LDFLAGS = -s -Wl,--gc-sections 70 | # Debug flags 71 | #CFLAGS = -Wall -Wextra -O0 -g $(INCLUDES) $(DEFS) -MMD 72 | #LDFLAGS = 73 | LIBS = 74 | SPLIT_LIBS = -lcapstone -lyaml -lz 75 | 76 | LIB_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(LIB_SRC_FILES:.c=.o)) 77 | CKSUM_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(CKSUM_SRC_FILES:.c=.o)) 78 | COMPRESS_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(COMPRESS_SRC_FILES:.c=.o)) 79 | EXTEND_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(EXTEND_SRC_FILES:.c=.o)) 80 | F3D_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(F3D_SRC_FILES:.c=.o)) 81 | F3D2OBJ_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(F3D2OBJ_SRC_FILES:.c=.o)) 82 | GEO_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(GEO_SRC_FILES:.c=.o)) 83 | SPLIT_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(SPLIT_SRC_FILES:.c=.o)) 84 | OBJ_FILES = $(LIB_OBJ_FILES) $(EXTEND_OBJ_FILES) $(COMPRESS_OBJ_FILES) \ 85 | $(SPLIT_OBJ_FILES) $(CKSUM_OBJ_FILES) $(F3D_OBJ_FILES) \ 86 | $(F3D2OBJ_OBJ_FILES) $(GEO_OBJ_FILES) 87 | DEP_FILES = $(OBJ_FILES:.o=.d) 88 | 89 | ######################## Targets ############################# 90 | 91 | default: all 92 | 93 | all: $(EXTEND_TARGET) $(COMPRESS_TARGET) $(MIO0_TARGET) $(CKSUM_TARGET) \ 94 | $(SPLIT_TARGET) $(F3D_TARGET) $(F3D2OBJ_TARGET) $(GRAPHICS_TARGET) \ 95 | $(DISASM_TARGET) $(GEO_TARGET) $(WALK_TARGET) 96 | 97 | $(OBJ_DIR)/%.o: %.c 98 | @[ -d $(OBJ_DIR) ] || mkdir -p $(OBJ_DIR) 99 | $(CC) $(CFLAGS) -o $@ -c $< 100 | 101 | $(SM64_LIB): $(LIB_OBJ_FILES) 102 | rm -f $@ 103 | $(AR) rcs $@ $^ 104 | 105 | $(CKSUM_TARGET): $(CKSUM_OBJ_FILES) $(SM64_LIB) 106 | $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) 107 | 108 | $(COMPRESS_TARGET): $(COMPRESS_OBJ_FILES) $(SM64_LIB) 109 | $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) 110 | 111 | $(EXTEND_TARGET): $(EXTEND_OBJ_FILES) $(SM64_LIB) 112 | $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) 113 | 114 | $(F3D_TARGET): $(F3D_OBJ_FILES) 115 | $(LD) $(LDFLAGS) -o $@ $^ 116 | 117 | $(F3D2OBJ_TARGET): $(F3D2OBJ_OBJ_FILES) 118 | $(LD) $(LDFLAGS) -o $@ $^ 119 | 120 | $(GEO_TARGET): $(GEO_OBJ_FILES) 121 | $(LD) $(LDFLAGS) -o $@ $^ 122 | 123 | $(GRAPHICS_TARGET): $(GRAPHICS_SRC_FILES) 124 | $(CC) $(CFLAGS) -DN64GRAPHICS_STANDALONE $^ $(LDFLAGS) -o $@ 125 | 126 | $(MIO0_TARGET): libmio0.c libmio0.h 127 | $(CC) $(CFLAGS) -DMIO0_STANDALONE $(LDFLAGS) -o $@ $< 128 | 129 | $(DISASM_TARGET): $(DISASM_SRC_FILES) 130 | $(CC) $(CFLAGS) -DMIPSDISASM_STANDALONE $^ $(LDFLAGS) -o $@ -lcapstone 131 | 132 | $(SPLIT_TARGET): $(SPLIT_OBJ_FILES) 133 | $(LD) $(LDFLAGS) -o $@ $^ $(SPLIT_LIBS) 134 | 135 | $(WALK_TARGET): sm64walk.c $(SM64_LIB) 136 | $(CC) $(CFLAGS) -o $@ $^ 137 | 138 | rawmips: rawmips.c utils.c 139 | $(CC) $(CFLAGS) -o $@ $^ -lcapstone 140 | 141 | clean: 142 | rm -f $(OBJ_FILES) $(DEP_FILES) $(SM64_LIB) $(MIO0_TARGET) 143 | rm -f $(CKSUM_TARGET) $(CKSUM_TARGET).exe 144 | rm -f $(COMPRESS_TARGET) $(COMPRESS_TARGET).exe 145 | rm -f $(DISASM_TARGET) $(DISASM_TARGET).exe 146 | rm -f $(EXTEND_TARGET) $(EXTEND_TARGET).exe 147 | rm -f $(F3D_TARGET) $(F3D_TARGET).exe 148 | rm -f $(F3D2OBJ_TARGET) $(F3D2OBJ_TARGET).exe 149 | rm -f $(GEO_TARGET) $(GEO_TARGET).exe 150 | rm -f $(MIO0_TARGET) $(MIO0_TARGET).exe 151 | rm -f $(GRAPHICS_TARGET) $(GRAPHICS_TARGET).exe 152 | rm -f $(SPLIT_TARGET) $(SPLIT_TARGET).exe 153 | rm -f $(WALK_TARGET) $(WALK_TARGET).exe 154 | -@[ -d $(OBJ_DIR) ] && rmdir --ignore-fail-on-non-empty $(OBJ_DIR) 155 | 156 | .PHONY: all clean default 157 | 158 | #################### Dependency Files ######################## 159 | 160 | -include $(DEP_FILES) 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sm64tools 2 | Collection of tools for manipulating the Super Mario 64 ROM 3 | 4 | ## n64split 5 | N64 ROM Splitter and Build System 6 | - splits ROM into assets: asm, textures, models, levels, behavior data 7 | - generates build files to rebuild the ROM 8 | - intelligent recursive disassembler 9 | - generic config file system to support multiple games 10 | 11 | ### Usage 12 | ```console 13 | n64split [-c CONFIG] [-k] [-m] [-o OUTPUT_DIR] [-s SCALE] [-t] [-v] [-V] ROM 14 | ``` 15 | Options: 16 | - -c CONFIG ROM configuration file (default: auto-detect) 17 | - -k keep going as much as possible after error 18 | - -m merge related instructions in to pseudoinstructions 19 | - -o OUTPUT_DIR output directory (default: {CONFIG.basename}.split) 20 | - -s SCALE amount to scale models by (default: 1024.0) 21 | - -t generate large texture for MIO0 blocks 22 | - -v verbose output 23 | - -V print version information 24 | 25 | ## sm64extend 26 | Super Mario 64 ROM Extender 27 | - accepts Z64 (BE), V64 (byte-swapped), or N64 (little-endian) ROMs as input 28 | - works with US, European, Japanese, and Shindou ROMs 29 | - decompresses all MIO0 blocks from ROM to extended area 30 | - configurable extended ROM size (default 64 MB) 31 | - configurable padding between MIO0 blocks (default 32 KB) 32 | - configurable MIO0 block alignment (default 1 byte) 33 | - changes all 0x18 level commands to 0x17 34 | - creates MIO0 headers for all 0x1A level commands 35 | - optionally fills old MIO0 blocks with 0x01 36 | - optionally dump compressed and uncompressed MIO0 data to files 37 | - updates assembly reference to MIO0 blocks 38 | - recalculates ROM header checksums 39 | 40 | ### Usage 41 | ```console 42 | sm64extend [-a ALIGNMENT] [-p PADDING] [-s SIZE] [-d] [-f] [-v] FILE [OUT_FILE] 43 | ``` 44 | Options: 45 | - -a ALIGNMENT Byte boundary to align MIO0 blocks (default = 1). 46 | - -p PADDING Padding to insert between MIO0 blocks in KB (default = 32). 47 | - -s SIZE Size of the extended ROM in MB (default: 64). 48 | - -d Dump MIO0 blocks to files in mio0 directory. 49 | - -f Fill old MIO0 blocks with 0x01. 50 | - -v verbose output. 51 | 52 | Output file: If unspecified, it is constructed by replacing input file extension with .ext.z64 53 | 54 | ### Examples 55 | 64 MB extended ROM that is bit compatible with with generated from the M64ROMExtender1.3b, after extending to 64 MB 56 | ```console 57 | sm64extend sm64.z64 58 | ``` 59 | 60 | 24 MB extended ROM that is bit compatible with the ROM generated from the M64ROMExtender1.3b 61 | ```console 62 | sm64extend -s 24 sm64.z64 63 | ``` 64 | 65 | Enable verbose messages and specify output filename: 66 | ```console 67 | sm64extend -v sm64.z64 sm64_output.ext.z64 68 | ``` 69 | 70 | Pad 64 KB between blocks, align blocks to 16-byte boundaries, fill old MIO0 blocks with 0x01: 71 | ```console 72 | sm64extend -p 64 -a 16 -f sm64.z64 73 | ``` 74 | 75 | ## sm64compress 76 | Experimental Super Mario 64 ROM alignment and compression tool 77 | - packs all MIO0 blocks together, reducing unused space 78 | - optionally compresses MIO0 blocks (and converts 0x17 commands to 0x18) 79 | - configurable MIO0 block alignment (default 16 byte) 80 | - reduces output ROM size to 4 MB boundary 81 | - updates assembly reference to MIO0 blocks 82 | - recalculates ROM header checksums 83 | 84 | ### Usage 85 | ```console 86 | sm64compress [-a ALIGNMENT] [-c] [-d] [-v] FILE [OUT_FILE] 87 | ``` 88 | Options: 89 | - -a alignment Byte boundary to align MIO0 blocks (default = 16). 90 | - -c compress all blocks using MIO0. 91 | - -d dump MIO0 blocks to files in mio0 directory. 92 | - -v verbose output. 93 | 94 | Output file: If unspecified, it is constructed by replacing input file extension with .out.z64 95 | 96 | ## Other Tools 97 | There are many other smaller tools included to help with SM64 hacking. They are: 98 | - f3d: tool to decode Fast3D display lists 99 | - mio0: standalone MIO0 compressor/decompressor 100 | - n64cksum: standalone N64 checksum generator. can either do in place or output to a new file 101 | - n64graphics: converts graphics data from PNG files into RGBA or IA N64 graphics data 102 | - mipsdisasm: standalone recursive MIPS disassembler 103 | - sm64geo: standalone SM64 geometry layout decoder 104 | 105 | ## License 106 | 107 | MIT License. Copyright 2015 queueRAM. 108 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 0.4: 2 | *add batch files to patch examples 3 | *fix 'make clean' not deleting sm64.map 4 | *fill in sm64.j asm and RAM/ROM mappings 5 | *add option to specify output dir 6 | *name 'gen' dir based on config name 7 | *add TYPE_M64 to support M64 music data and find references 8 | *add GNU patch for windows 9 | *update libcapstone 10 | *add some mk64 asm 11 | 12 | 0.5: 13 | rip models and collision? 14 | improve sw %lo() detection 15 | scan procedure at a time to find instruction pairs to collapse 16 | - lui/addiu, lui/ori, lui/sw (possibly .set at or at least %hi/%lo), slt/bne 17 | add TYPE_TEXTURE and support arbitrary types within MIO0 (and other compressed?) blocks 18 | add standalone sm64level, sm64behavior tools? 19 | add 0x39 level references to table at EC7E0 20 | add way to include jump tables or define data types of tables 21 | - autodetect through JR? 22 | add RAM labels for tables 23 | add VS compiler flags to reduce build size 24 | add document or script release 25 | - bump version in changed tools 26 | - update release/n64split.README 27 | - update README with any changes to usage 28 | - create new build-vM.m.b 29 | - cd build-vM.m.b 30 | - cmake -DCMAKE_BUILD_TYPE=Release .. 31 | - call command line tool to build all projects? 32 | - create new n64split-vM.m.b-win32 33 | - copy licenses/ 34 | - copy configs/ 35 | - copy release/split.bat 36 | - copy release/n64split.README.txt README.txt 37 | - copy build-vM.m.b/Release/ tools/ 38 | - delete libsm64.lib 39 | add version, help, flags to n64graphics 40 | add ./test/ 41 | remove hacks for overlapping asm procedures 42 | create directories for mipsdisasm, libmio0 43 | merge rawmips into mipsdisasm 44 | rename mipsdisasm to mipsrdisasm - MIPS recursive disasm 45 | add parens and arguments in function name 46 | use segmented addresses for more level script callouts 47 | - move data to segmented address (possibly overlay) 48 | - add symbols for LOAD addresses 49 | add asm macros for level scripts 50 | add asm macros for geo layout 51 | - add labels for asm and other references 52 | add asm macros for behavior scripts 53 | create common label database (config lables, ROM labels, behavior labels, HW register labels, jump tables) 54 | update linker script for RAM/ROM AT addressing 55 | find way to have data labels 56 | - in config file 57 | - linker script 58 | figure out remaining cut scenes 59 | maybe: 60 | use shygoo's 0x1A level command with uncompressed data 61 | convert texture encoding to: 62 | split --> textures/A.0x0.png, A.s 63 | textures/A.0x0.png --n64graphics--> bin/textures/A.0x0.bin 64 | .include "A.s" .incbin "bin/textures/A.0x0.bin" 65 | A.bin -> A.mio0: remove this step 66 | 67 | Examples: 68 | hello world example 69 | - hook into mario function instead of butterfly behavior 70 | hello world 2 example 71 | http://www.hastebin.com/unuqiwetov <-- use this one 72 | http://www.hastebin.com/ayoqopoyeq 73 | switch to Green Stars method of HUD toggle 74 | - still need show star status 75 | look into what importer does for bounds extending 76 | import custom levels 77 | look into m64 music importing 78 | add new .text section at 0x80400000 and setup DMAs for it 79 | 80 | Other: 81 | add f3d parsing? 82 | see what else the level importer changed 83 | figure out where music and samples are located and split out 84 | switch emulator to cen64 85 | see what data is after geo layout after main_level_scripts 86 | improve mio0 compressor 87 | check capstone disassembly of COP instructions 88 | -> appears to do some COP2, need to validate assembly again 89 | -> does not do RSP, need to implement in custom callback for data 90 | - probably also requires switching to callback for each instruction 91 | http://sprunge.us/VYYI?gas 92 | 93 | figure out what these functions are for: 94 | JALs: 95 | // recursive, only called by self 802C9AD8 96 | Looking for 802C9AD8 0C0B26B6 97 | 084AFC: 0C0B26B6 98 | 084B28: 0C0B26B6 99 | 100 | // recursive, only called by self 8017E430 101 | Looking for 8017E430 0C05F90C 102 | 22E96C: 0C05F90C 103 | 104 | // recursive, only called by self 8017F350 105 | Looking for 8017F350 0C05FCD4 106 | 22F96C: 0C05FCD4 107 | 108 | // recursive, only called by self 8018837C 109 | Looking for 8018837C 0C0620DF 110 | 238968: 0C0620DF 111 | 112 | Looking for 8017C810 0C05F204 113 | 257A28: 0C05F204 114 | 115 | Looking for 8017EF9C 0C05FBE7 116 | 257B88: 0C05FBE7 117 | 118 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_ 2 | #define CONFIG_H_ 3 | 4 | typedef enum 5 | { 6 | TYPE_INVALID, 7 | TYPE_ASM, 8 | TYPE_BIN, 9 | TYPE_BLAST, 10 | TYPE_GZIP, 11 | TYPE_HEADER, 12 | TYPE_INSTRUMENT_SET, 13 | TYPE_M64, 14 | TYPE_SFX_CTL, 15 | TYPE_SFX_TBL, 16 | TYPE_MIO0, 17 | TYPE_PTR, 18 | // F3D display lists and related 19 | TYPE_F3D_DL, 20 | TYPE_F3D_LIGHT, 21 | TYPE_F3D_VERTEX, 22 | // Textures 23 | TYPE_TEX_CI, 24 | TYPE_TEX_I, 25 | TYPE_TEX_IA, 26 | TYPE_TEX_RGBA, 27 | TYPE_TEX_SKYBOX, 28 | // SM64 specific types 29 | TYPE_SM64_GEO, 30 | TYPE_SM64_BEHAVIOR, 31 | TYPE_SM64_COLLISION, 32 | TYPE_SM64_LEVEL, 33 | } section_type; 34 | 35 | typedef struct _label 36 | { 37 | unsigned int ram_addr; 38 | char name[128]; 39 | } label; 40 | 41 | typedef struct _texture 42 | { 43 | unsigned int offset; 44 | unsigned int palette; // only for CI textures 45 | unsigned short width; 46 | unsigned short height; 47 | unsigned short depth; 48 | section_type format; 49 | } texture; 50 | 51 | typedef struct _split_section 52 | { 53 | char label[128]; 54 | unsigned int start; 55 | unsigned int end; 56 | unsigned int vaddr; 57 | section_type type; 58 | 59 | int subtype; 60 | 61 | // texture specific data 62 | texture tex; 63 | 64 | struct _split_section *children; 65 | int child_count; 66 | } split_section; 67 | 68 | typedef struct _rom_config 69 | { 70 | char name[128]; 71 | char basename[128]; 72 | 73 | unsigned int checksum1; 74 | unsigned int checksum2; 75 | 76 | split_section *sections; 77 | int section_count; 78 | 79 | label *labels; 80 | int label_count; 81 | } rom_config; 82 | 83 | int config_parse_file(const char *filename, rom_config *config); 84 | void config_print(const rom_config *config); 85 | int config_validate(const rom_config *config, unsigned int max_len); 86 | void config_free(rom_config *config); 87 | 88 | section_type config_str2section(const char *type_name); 89 | const char *config_section2str(section_type section); 90 | 91 | // get version of underlying config library 92 | const char *config_get_version(void); 93 | 94 | #endif // CONFIG_H_ 95 | -------------------------------------------------------------------------------- /configs/blast_corps.e.code.yaml: -------------------------------------------------------------------------------- 1 | # ROM splitter configuration file 2 | name: "Blast Corps (E) hd_code_text.raw" 3 | 4 | # checksums from ROM header offsets 0x10 and 0x14 5 | # used for auto configuration detection 6 | checksum1: 0x3C0E00FF 7 | checksum2: 0x35CEB000 8 | 9 | # base filename used for outputs (please, no spaces) 10 | basename: "blast_corps.e.code" 11 | 12 | # ranges to split the ROM into 13 | # types: 14 | # asm - MIPS assembly block. Symbol names are in 'labels' list below 15 | ranges: 16 | # start, end, type, label 17 | - [0x000000, 0x0A4410, "asm", "main", 0x802447C0] 18 | 19 | # Labels for functions or data memory addresses 20 | # All label addresses are RAM addresses 21 | # Order does not matter 22 | labels: 23 | - [0x802447C0, "MainJump"] 24 | - [0x80244870, "Thread1"] 25 | - [0x80244930, "Thread3"] 26 | - [0x80257594, "LoadLevel"] 27 | - [0x80269258, "Thread4"] 28 | - [0x80271640, "LoadLevelRdus"] 29 | - [0x802729F8, "Thread5"] 30 | - [0x8028D1E4, "InitiateDma"] 31 | - [0x8028DEB0, "LoadLevelAmmo"] 32 | - [0x8028F1E0, "LoadLevelTnt"] 33 | - [0x80291AD0, "LoadLevelSquareBlocks"] 34 | - [0x802A440C, "LoadLevelTodo74"] 35 | - [0x802A4590, "LoadLevelTodo2C"] 36 | - [0x802A46C4, "LoadLevelBuildings"] 37 | - [0x802A5520, "CopyBuildingTableToRam"] 38 | - [0x802A5408, "InflateBuildingData"] 39 | - [0x802A55C4, "LoadLevelTodo60"] 40 | - [0x802A56D8, "LoadLevelTodo4C"] 41 | - [0x802A5978, "LoadLevelTodo78"] 42 | - [0x802A5E7C, "LoadLevelVehicles"] 43 | - [0x802A59AC, "LoadLevelMissileCarrier"] 44 | - [0x802A66C4, "LoadLevelCollisionXZ"] 45 | - [0x802A6768, "LoadLevelTodo70"] 46 | - [0x802A680C, "LoadLevelTodo64"] 47 | - [0x802A68F0, "LoadLevelTrainPlatform"] 48 | - [0x802A6DD4, "LoadLevelTerrain"] 49 | - [0x802A6E54, "AlignTo8Bytes"] 50 | - [0x802A7E80, "LoadLevelTodo40"] 51 | - [0x802A7F74, "LoadLevelTodo44"] 52 | - [0x802A814C, "DecodeTexture"] 53 | - [0x802A82C8, "DecodeTexture6"] 54 | - [0x802A839C, "DecodeTexture3"] 55 | - [0x802A8450, "DecodeTexture1"] 56 | - [0x802A8500, "DecodeTexture2"] 57 | - [0x802A85CC, "DecodeTexture4"] 58 | - [0x802A86A4, "DecodeTexture5"] 59 | - [0x802A8780, "DecodeTexture0"] 60 | - [0x802C6A78, "GzipInflate"] 61 | - [0x802D1410, "LoadLevelCommPoint"] 62 | - [0x802D6990, "osInitialize"] 63 | - [0x802D6BC0, "osEPiRawReadIo"] 64 | - [0x802D6C20, "osCreateThread"] 65 | - [0x802D6D70, "osStartThread"] 66 | - [0x802D6ED0, "osCreatePiManager"] 67 | - [0x802D7050, "osSetThreadPri"] 68 | - [0x802D7130, "osSendMesg"] 69 | - [0x802D7280, "osRecvMesg"] 70 | - [0x802D73C0, "osViBlack"] 71 | - [0x802D7430, "osGetTime"] 72 | - [0x802D74C0, "__ull_rshift"] 73 | - [0x802D74EC, "__ull_rem"] 74 | - [0x802D7528, "__ull_div"] 75 | - [0x802D7564, "__ll_lshift"] 76 | - [0x802D7590, "__ll_rem"] 77 | - [0x802D75CC, "__ll_div"] 78 | - [0x802D7628, "__ll_mul"] 79 | - [0x802D7658, "__ull_divremi"] 80 | - [0x802D76B8, "__ll_mod"] 81 | - [0x802D7754, "__ll_rshift"] 82 | - [0x802D7790, "osVirtualToPhysical"] 83 | - [0x802D7810, "osWriteBackDCache"] 84 | - [0x802D7A40, "guOrthoF"] 85 | - [0x802D7B94, "guFrustum"] 86 | - [0x802D7C00, "guPerspectiveF"] 87 | - [0x802D7E30, "guPerspective"] 88 | - [0x802D8290, "guMtxF2L"] 89 | - [0x802D8390, "guMtxIdentF"] 90 | - [0x802D8500, "guRotateRPYF"] 91 | - [0x802D8554, "guRotateRPY"] 92 | - [0x802D8A60, "sqrtf"] 93 | - [0x802D8DA0, "sinf"] 94 | - [0x802D8F60, "osCreateMesgQueue"] 95 | - [0x802D8FD0, "osViSetSpecialFeatures"] 96 | - [0x802D9190, "osWriteBackDCacheAll"] 97 | - [0x802D91C0, "osInvalDCache"] 98 | - [0x802D9270, "osDestroyThread"] 99 | - [0x802D9370, "cosf"] 100 | - [0x802D78F0, "proutSprintf"] 101 | - [0x802D795C, "sprintf"] 102 | - [0x802D7890, "osSetIntMask"] 103 | - [0x802DADD4, "alBnkfNew"] 104 | - [0x802DAED8, "alSetFileNew"] 105 | - [0x802DC190, "osGetCount"] 106 | - [0x802DC240, "__osViInit"] 107 | - [0x802DC380, "osAiSetFrequency"] 108 | - [0x802DC4E0, "osAiSetNextBuffer"] 109 | - [0x802DC590, "osAiGetLength"] 110 | - [0x802DCC70, "osPiStartDma"] 111 | - [0x802DCF90, "osCreateViManager"] 112 | - [0x802DD114, "__osViDevMgrMain"] 113 | - [0x802DD2F0, "osViSetMode"] 114 | - [0x802DD360, "osSetEventMsg"] 115 | - [0x802DD3D0, "osViSetEventMsg"] 116 | - [0x802DD840, "osViSwapBuffer"] 117 | - [0x802DD890, "osSetTimer"] 118 | - [0x802DD9F0, "osSpTaskDunno"] 119 | - [0x802DDB0C, "osSpTaskLoad"] 120 | - [0x802DDC6C, "osSpTaskStartGo"] 121 | - [0x802DDCAC, "osSpTaskYield"] 122 | - [0x802DDD90, "osContInit"] 123 | - [0x802DDF8C, "__osContGetInitData"] 124 | - [0x802DE05C, "__osPackRequestData"] 125 | - [0x802DE1C0, "osContStartReadData"] 126 | - [0x802DE284, "osContGetReadData"] 127 | - [0x802DE420, "osInvalCache"] 128 | - [0x802DE4A0, "bzero"] 129 | - [0x802DE690, "__osGetSR"] 130 | - [0x802DE6A0, "__osSetFpcCsr"] 131 | - [0x802DE6B0, "__osSiRawReadIo"] 132 | - [0x802DE700, "__osSiRawWriteIo"] 133 | - [0x802DE750, "__osExceptionPreamble"] 134 | - [0x802DE760, "__osExceptionHandler"] 135 | - [0x802DED90, "__osEnqueueAndYield"] 136 | - [0x802DEE20, "__osEnqueueThread"] 137 | - [0x802DEE68, "__osPopThread"] 138 | - [0x802DEE78, "__osDispatchThread"] 139 | - [0x802DEFB8, "__osCleanupThread"] 140 | - [0x802DF020, "__osDisableInt"] 141 | - [0x802DF040, "__osRestoreInt"] 142 | - [0x802DF0A0, "__osPiCreateAccessQueue"] 143 | - [0x802DF0F0, "__osPiGetAccess"] 144 | - [0x802DF134, "__osPiRelAccess"] 145 | - [0x802DF160, "osGetThreadPri"] 146 | - [0x802DF180, "osPiRawStartDma"] 147 | - [0x802DF260, "__osDevMgrMain"] 148 | - [0x802DF3E0, "__osTimerServicesInit"] 149 | - [0x802DF46C, "__osTimerInterrupt"] 150 | - [0x802DF5E4, "__osSetTimerIntr"] 151 | - [0x802DF658, "__osInsertTimer"] 152 | - [0x802DF7E0, "__osProbeTLB"] 153 | - [0x802DF8A0, "_Printf"] 154 | - [0x802E0A30, "memcpy"] 155 | - [0x802E28F0, "__osAiDeviceBusy"] 156 | - [0x802E42B0, "osJamMesg"] 157 | - [0x802E4440, "__osSpGetStatus"] 158 | - [0x802DD440, "bcopy"] 159 | - [0x802E4450, "__osSpSetStatus"] 160 | - [0x802E4460, "__osSpSetPc"] 161 | - [0x802E44A0, "__osSpRawStartDma"] 162 | - [0x802E4530, "__osSpDeviceBusy"] 163 | - [0x802E4560, "__osSiRawStartDma"] 164 | - [0x802E4610, "__osSiCreateAccessQueue"] 165 | - [0x802E4660, "__osSiGetAccess"] 166 | - [0x802E46A4, "__osSiRelAccess"] 167 | - [0x802E46D0, "__osSiDeviceBusy"] 168 | - [0x802E4B80, "__osSyncPutChars"] 169 | - [0x802E4CB0, "__osSetCompare"] 170 | - [0x802E7000, "__osAtomicDec"] 171 | 172 | -------------------------------------------------------------------------------- /configs/blast_corps.e.yaml: -------------------------------------------------------------------------------- 1 | # ROM splitter configuration file 2 | name: "Blast Corps (E)" 3 | 4 | # checksums from ROM header offsets 0x10 and 0x14 5 | # used for auto configuration detection 6 | checksum1: 0x7C64E6DB 7 | checksum2: 0x55B924DB 8 | 9 | # base filename used for outputs (please, no spaces) 10 | basename: "blast_corps.e" 11 | 12 | # ranges to split the ROM into 13 | # types: 14 | # asm - MIPS assembly block. Symbol names are in 'labels' list below 15 | # behavior - behavior script 16 | # bin - raw binary, usually data 17 | # blast - Blast Corps compressed blocks 18 | # gzip - gzip compressed blocks 19 | # header - ROM header block 20 | # instrset - instrument set 21 | # level - level commands 22 | # m64 - M64 music sequence bank 23 | # mio0 - MIO0 compressed data block. may have texture breakdown 24 | # ptr - RAM address or ROM offset pointer 25 | # 26 | # textures types: 27 | # rgba - 32/16 bit RGBA (5-5-5-1/8-8-8-8) 28 | # ia - 16/8/4/1-bit greyscale 29 | # skybox - grid of 32x32 16-bit RGBA 30 | ranges: 31 | # start, end, type, label 32 | - [0x000000, 0x000040, "header", "header"] 33 | - [0x000040, 0x001000, "bin", "boot"] 34 | - [0x001000, 0x004A3C, "asm", "main", 0x8021ED00] 35 | # hd_code_text is copied to 0x80000400 and then inflated to 0x802447C0 by proc_80220998 36 | - [0x788120, 0x7D845C, "gzip", "hd_code_text.raw"] 37 | - [0x7D845C, 0x7E4940, "gzip", "hd_code_data.raw"]: 38 | # TODO: many of these are just guesses 39 | - [0x10E30, "tex.rgba", 32, 16, 16] 40 | - [0x11230, "tex.rgba", 32, 16, 16] 41 | - [0x12180, "tex.rgba", 32, 32, 30] 42 | - [0x142E0, "tex.rgba", 16, 32, 32] 43 | - [0x16B68, "tex.ia", 16, 32, 32] 44 | - [0x7E4940, 0x7F75A4, "gzip", "hd_front_end_text.raw"] 45 | - [0x7F75A4, 0x7FB7C0, "gzip", "hd_front_end_data.raw"] 46 | 47 | # Labels for functions or data memory addresses 48 | # All label addresses are RAM addresses 49 | # Order does not matter 50 | labels: 51 | - [0x8021ED00, "EntryPoint"] 52 | - [0x80220730, "Main"] 53 | - [0x80220BA0, "bzero"] 54 | - [0x80220C40, "osInitialize"] 55 | - [0x80220E70, "DmaCopy"] 56 | - [0x80220F50, "GetDmaStatus"] 57 | - [0x80220F70, "__osGetSR"] 58 | - [0x80220F80, "__osSetFpcCsr"] 59 | - [0x80220F90, "__osSiRawReadIo"] 60 | - [0x80220FE0, "__osSiRawWriteIo"] 61 | - [0x802218A0, "osWriteBackDCache"] 62 | - [0x80221920, "osInvalCache"] 63 | - [0x80221A00, "osEPiRawReadIo"] 64 | - [0x80221AC8, "__ull_div"] 65 | - [0x80221BC8, "__ll_mul"] 66 | - [0x80221DE0, "__osSiDeviceBusy"] 67 | - [0x802447C0, "MainJump"] 68 | 69 | -------------------------------------------------------------------------------- /configs/blast_corps.u.1.0.code.yaml: -------------------------------------------------------------------------------- 1 | # ROM splitter configuration file 2 | name: "Blast Corps (U) (V1.0) hd_code_text.raw" 3 | 4 | # checksums from ROM header offsets 0x10 and 0x14 5 | # used for auto configuration detection 6 | checksum1: 0x3C0E00FF 7 | checksum2: 0x35CEB000 8 | 9 | # base filename used for outputs (please, no spaces) 10 | basename: "blast_corps.u.1.0.hd_code_text" 11 | 12 | # ranges to split the ROM into 13 | # types: 14 | # asm - MIPS assembly block. Symbol names are in 'labels' list below 15 | ranges: 16 | # start, end, type, label 17 | - [0x000000, 0x0A4360, "asm", 0x802447C0] 18 | 19 | # Labels for functions or data memory addresses 20 | # All label addresses are RAM addresses 21 | # Order does not matter 22 | labels: 23 | - [0x802447C0, "MainJump"] 24 | - [0x80244870, "Thread1"] 25 | - [0x80244930, "Thread3"] 26 | - [0x8025615C, "LoadLevel"] 27 | - [0x80267A9C, "Thread4"] 28 | - [0x8026FBB0, "LoadLevelRdus"] 29 | - [0x80270F7C, "Thread5"] 30 | - [0x8028B4C4, "InitiateDma"] 31 | - [0x8028C190, "LoadLevelAmmo"] 32 | - [0x8028D4C0, "LoadLevelTnt"] 33 | - [0x8028FDA0, "LoadLevelSquareBlocks"] 34 | - [0x802A1A9C, "LoadLevelTodo74"] 35 | - [0x802A1C20, "LoadLevelTodo2C"] 36 | - [0x802A1D54, "LoadLevelBuildings"] 37 | - [0x802A2BB0, "CopyBuildingTableToRam"] 38 | - [0x802A2A98, "InflateBuildingData"] 39 | - [0x802A2C54, "LoadLevelTodo60"] 40 | - [0x802A2D68, "LoadLevelTodo4C"] 41 | - [0x802A3000, "LoadLevelTodo78"] 42 | - [0x802A3504, "LoadLevelVehicles"] 43 | - [0x802A3034, "LoadLevelMissileCarrier"] 44 | - [0x802A3D4C, "LoadLevelCollisionXZ"] 45 | - [0x802A3DF0, "LoadLevelTodo70"] 46 | - [0x802A3E94, "LoadLevelTodo64"] 47 | - [0x802A3F78, "LoadLevelTrainPlatform"] 48 | - [0x802A445C, "LoadLevelTerrain"] 49 | - [0x802A44DC, "AlignTo8Bytes"] 50 | - [0x802A5500, "LoadLevelTodo40"] 51 | - [0x802A55F4, "LoadLevelTodo44"] 52 | - [0x802A57CC, "DecodeTexture"] 53 | - [0x802A5948, "DecodeTexture6"] 54 | - [0x802A5A1C, "DecodeTexture3"] 55 | - [0x802A5AD0, "DecodeTexture1"] 56 | - [0x802A5B80, "DecodeTexture2"] 57 | - [0x802A5C4C, "DecodeTexture4"] 58 | - [0x802A5D24, "DecodeTexture5"] 59 | - [0x802A5E00, "DecodeTexture0"] 60 | - [0x802C4058, "GzipInflate"] 61 | - [0x802CE9F0, "LoadLevelCommPoint"] 62 | - [0x802D3F70, "osInitialize"] 63 | - [0x802D41A0, "osEPiRawReadIo"] 64 | - [0x802D4200, "osCreateThread"] 65 | - [0x802D4350, "osStartThread"] 66 | - [0x802D44B0, "osCreatePiManager"] 67 | - [0x802D4630, "osSetThreadPri"] 68 | - [0x802D4710, "osSendMesg"] 69 | - [0x802D4860, "osRecvMesg"] 70 | - [0x802D49A0, "osViBlack"] 71 | - [0x802D4A10, "osGetTime"] 72 | - [0x802D4AA0, "__ull_rshift"] 73 | - [0x802D4ACC, "__ull_rem"] 74 | - [0x802D4B08, "__ull_div"] 75 | - [0x802D4B44, "__ll_lshift"] 76 | - [0x802D4B70, "__ll_rem"] 77 | - [0x802D4BAC, "__ll_div"] 78 | - [0x802D4C08, "__ll_mul"] 79 | - [0x802D4C38, "__ull_divremi"] 80 | - [0x802D4C98, "__ll_mod"] 81 | - [0x802D4D34, "__ll_rshift"] 82 | - [0x802D4D70, "osVirtualToPhysical"] 83 | - [0x802D4DF0, "osWriteBackDCache"] 84 | - [0x802D4F10, "guOrthoF"] 85 | - [0x802D5064, "guFrustum"] 86 | - [0x802D50D0, "guPerspectiveF"] 87 | - [0x802D5300, "guPerspective"] 88 | - [0x802D5760, "guMtxF2L"] 89 | - [0x802D5860, "guMtxIdentF"] 90 | - [0x802D59D0, "guRotateRPYF"] 91 | - [0x802D5A24, "guRotateRPY"] 92 | - [0x802D5F30, "sqrtf"] 93 | - [0x802D6270, "sinf"] 94 | - [0x802D6430, "osCreateMesgQueue"] 95 | - [0x802D64A0, "osViSetSpecialFeatures"] 96 | - [0x802D6660, "osWriteBackDCacheAll"] 97 | - [0x802D6690, "osInvalDCache"] 98 | - [0x802D6740, "osDestroyThread"] 99 | - [0x802D6840, "cosf"] 100 | - [0x802D69B0, "proutSprintf"] 101 | - [0x802D6A1C, "sprintf"] 102 | - [0x802D75B0, "osSetIntMask"] 103 | - [0x802D83B4, "alBnkfNew"] 104 | - [0x802D84B8, "alSetFileNew"] 105 | - [0x802D9770, "osGetCount"] 106 | - [0x802D9820, "__osViInit"] 107 | - [0x802D9950, "osAiSetFrequency"] 108 | - [0x802D9AB0, "osAiSetNextBuffer"] 109 | - [0x802D9B60, "osAiGetLength"] 110 | - [0x802DA240, "osPiStartDma"] 111 | - [0x802DA560, "osCreateViManager"] 112 | - [0x802DA6E4, "__osViDevMgrMain"] 113 | - [0x802DA8C0, "osViSetMode"] 114 | - [0x802DA930, "osSetEventMsg"] 115 | - [0x802DA9A0, "osViSetEventMsg"] 116 | - [0x802DAAA0, "osViSwapBuffer"] 117 | - [0x802DAAF0, "osSetTimer"] 118 | - [0x802DAC50, "osSpTaskDunno"] 119 | - [0x802DAD6C, "osSpTaskLoad"] 120 | - [0x802DAECC, "osSpTaskStartGo"] 121 | - [0x802DAF10, "osSpTaskYield"] 122 | - [0x802DAFF0, "osContInit"] 123 | - [0x802DB1EC, "__osContGetInitData"] 124 | - [0x802DB2BC, "__osPackRequestData"] 125 | - [0x802DB420, "osContStartReadData"] 126 | - [0x802DB4E4, "osContGetReadData"] 127 | - [0x802DB680, "osInvalCache"] 128 | - [0x802DB700, "bzero"] 129 | - [0x802DB8F0, "__osGetSR"] 130 | - [0x802DB900, "__osSetFpcCsr"] 131 | - [0x802DB910, "__osSiRawReadIo"] 132 | - [0x802DB960, "__osSiRawWriteIo"] 133 | - [0x802DB9B0, "__osExceptionPreamble"] 134 | - [0x802DB9C0, "__osExceptionHandler"] 135 | - [0x802DBFF0, "__osEnqueueAndYield"] 136 | - [0x802DC080, "__osEnqueueThread"] 137 | - [0x802DC0C8, "__osPopThread"] 138 | - [0x802DC0D8, "__osDispatchThread"] 139 | - [0x802DC218, "__osCleanupThread"] 140 | - [0x802DC280, "__osDisableInt"] 141 | - [0x802DC2A0, "__osRestoreInt"] 142 | - [0x802DC300, "__osPiCreateAccessQueue"] 143 | - [0x802DC350, "__osPiGetAccess"] 144 | - [0x802DC394, "__osPiRelAccess"] 145 | - [0x802DC3C0, "osGetThreadPri"] 146 | - [0x802DC3E0, "osPiRawStartDma"] 147 | - [0x802DC4C0, "__osDevMgrMain"] 148 | - [0x802DC640, "__osTimerServicesInit"] 149 | - [0x802DC6CC, "__osTimerInterrupt"] 150 | - [0x802DC844, "__osSetTimerIntr"] 151 | - [0x802DC8B8, "__osInsertTimer"] 152 | - [0x802DCA40, "__osProbeTLB"] 153 | - [0x802DCB00, "_Printf"] 154 | - [0x802DDC90, "memcpy"] 155 | - [0x802DFB50, "__osAiDeviceBusy"] 156 | - [0x802E20F0, "osJamMesg"] 157 | - [0x802E2280, "__osSpGetStatus"] 158 | - [0x802E2290, "bcopy"] 159 | - [0x802E25A0, "__osSpSetStatus"] 160 | - [0x802E25B0, "__osSpSetPc"] 161 | - [0x802E25F0, "__osSpRawStartDma"] 162 | - [0x802E2680, "__osSpDeviceBusy"] 163 | - [0x802E26B0, "__osSiRawStartDma"] 164 | - [0x802E2760, "__osSiCreateAccessQueue"] 165 | - [0x802E27B0, "__osSiGetAccess"] 166 | - [0x802E27F4, "__osSiRelAccess"] 167 | - [0x802E2820, "__osSiDeviceBusy"] 168 | - [0x802E2CD0, "__osSyncPutChars"] 169 | - [0x802E2E00, "__osSetCompare"] 170 | - [0x802E5150, "__osAtomicDec"] 171 | -------------------------------------------------------------------------------- /configs/blast_corps.u.1.1.code.yaml: -------------------------------------------------------------------------------- 1 | # ROM splitter configuration file 2 | name: "Blast Corps (U) (V1.1) hd_code_text.raw" 3 | 4 | # checksums from ROM header offsets 0x10 and 0x14 5 | # used for auto configuration detection 6 | checksum1: 0x3C0E00FF 7 | checksum2: 0x35CEB000 8 | 9 | # base filename used for outputs (please, no spaces) 10 | basename: "blast_corps.u.1.1.hd_code_text" 11 | 12 | # ranges to split the ROM into 13 | # types: 14 | # asm - MIPS assembly block. Symbol names are in 'labels' list below 15 | ranges: 16 | # start, end, type, label 17 | - [0x000000, 0x0A4410, "asm", "main", 0x802447C0] 18 | 19 | # Labels for functions or data memory addresses 20 | # All label addresses are RAM addresses 21 | # Order does not matter 22 | labels: 23 | - [0x802447C0, "MainJump"] 24 | - [0x80244870, "Thread1"] 25 | - [0x80244930, "Thread3"] 26 | - [0x8025615C, "LoadLevel"] 27 | - [0x80267A9C, "Thread4"] 28 | - [0x8026FBB0, "LoadLevelRdus"] 29 | - [0x80270F7C, "Thread5"] 30 | - [0x8028B4C4, "InitiateDma"] 31 | - [0x8028C190, "LoadLevelAmmo"] 32 | - [0x8028D4C0, "LoadLevelTnt"] 33 | - [0x8028FDA0, "LoadLevelSquareBlocks"] 34 | - [0x802A1A9C, "LoadLevelTodo74"] 35 | - [0x802A1C20, "LoadLevelTodo2C"] 36 | - [0x802A1D54, "LoadLevelBuildings"] 37 | - [0x802A2BB0, "CopyBuildingTableToRam"] 38 | - [0x802A2A98, "InflateBuildingData"] 39 | - [0x802A2C54, "LoadLevelTodo60"] 40 | - [0x802A2D68, "LoadLevelTodo4C"] 41 | - [0x802A3008, "LoadLevelTodo78"] 42 | - [0x802A350C, "LoadLevelVehicles"] 43 | - [0x802A303C, "LoadLevelMissileCarrier"] 44 | - [0x802A3D54, "LoadLevelCollisionXZ"] 45 | - [0x802A3DF8, "LoadLevelTodo70"] 46 | - [0x802A3E9C, "LoadLevelTodo64"] 47 | - [0x802A3F80, "LoadLevelTrainPlatform"] 48 | - [0x802A4464, "LoadLevelTerrain"] 49 | - [0x802A44E4, "AlignTo8Bytes"] 50 | - [0x802A5510, "LoadLevelTodo40"] 51 | - [0x802A5604, "LoadLevelTodo44"] 52 | - [0x802A57DC, "DecodeTexture"] 53 | - [0x802A5958, "DecodeTexture6"] 54 | - [0x802A5A2C, "DecodeTexture3"] 55 | - [0x802A5AE0, "DecodeTexture1"] 56 | - [0x802A5B90, "DecodeTexture2"] 57 | - [0x802A5C5C, "DecodeTexture4"] 58 | - [0x802A5D34, "DecodeTexture5"] 59 | - [0x802A5E10, "DecodeTexture0"] 60 | - [0x802C4108, "GzipInflate"] 61 | - [0x802CEAA0, "LoadLevelCommPoint"] 62 | - [0x802D4020, "osInitialize"] 63 | - [0x802D4250, "osEPiRawReadIo"] 64 | - [0x802D42B0, "osCreateThread"] 65 | - [0x802D4400, "osStartThread"] 66 | - [0x802D4560, "osCreatePiManager"] 67 | - [0x802D46E0, "osSetThreadPri"] 68 | - [0x802D47C0, "osSendMesg"] 69 | - [0x802D4910, "osRecvMesg"] 70 | - [0x802D4A50, "osViBlack"] 71 | - [0x802D4AC0, "osGetTime"] 72 | - [0x802D4B50, "__ull_rshift"] 73 | - [0x802D4B7C, "__ull_rem"] 74 | - [0x802D4BB8, "__ull_div"] 75 | - [0x802D4BF4, "__ll_lshift"] 76 | - [0x802D4C20, "__ll_rem"] 77 | - [0x802D4C5C, "__ll_div"] 78 | - [0x802D4CB8, "__ll_mul"] 79 | - [0x802D4CE8, "__ull_divremi"] 80 | - [0x802D4D48, "__ll_mod"] 81 | - [0x802D4DE4, "__ll_rshift"] 82 | - [0x802D4E20, "osVirtualToPhysical"] 83 | - [0x802D4EA0, "osWriteBackDCache"] 84 | - [0x802D4FC0, "guOrthoF"] 85 | - [0x802D5114, "guFrustum"] 86 | - [0x802D5180, "guPerspectiveF"] 87 | - [0x802D53B0, "guPerspective"] 88 | - [0x802D5810, "guMtxF2L"] 89 | - [0x802D5910, "guMtxIdentF"] 90 | - [0x802D5A80, "guRotateRPYF"] 91 | - [0x802D5AD4, "guRotateRPY"] 92 | - [0x802D5FE0, "sqrtf"] 93 | - [0x802D6320, "sinf"] 94 | - [0x802D64E0, "osCreateMesgQueue"] 95 | - [0x802D6550, "osViSetSpecialFeatures"] 96 | - [0x802D6710, "osWriteBackDCacheAll"] 97 | - [0x802D6740, "osInvalDCache"] 98 | - [0x802D67F0, "osDestroyThread"] 99 | - [0x802D68F0, "cosf"] 100 | - [0x802D6A60, "proutSprintf"] 101 | - [0x802D6ACC, "sprintf"] 102 | - [0x802D7660, "osSetIntMask"] 103 | - [0x802D8464, "alBnkfNew"] 104 | - [0x802D8568, "alSetFileNew"] 105 | - [0x802D9820, "osGetCount"] 106 | - [0x802D98D0, "__osViInit"] 107 | - [0x802D9A00, "osAiSetFrequency"] 108 | - [0x802D9B60, "osAiSetNextBuffer"] 109 | - [0x802D9C10, "osAiGetLength"] 110 | - [0x802DA2F0, "osPiStartDma"] 111 | - [0x802DA610, "osCreateViManager"] 112 | - [0x802DA794, "__osViDevMgrMain"] 113 | - [0x802DA970, "osViSetMode"] 114 | - [0x802DA9E0, "osSetEventMsg"] 115 | - [0x802DAA50, "osViSetEventMsg"] 116 | - [0x802DAB50, "osViSwapBuffer"] 117 | - [0x802DABA0, "osSetTimer"] 118 | - [0x802DAD00, "osSpTaskDunno"] 119 | - [0x802DAE1C, "osSpTaskLoad"] 120 | - [0x802DAF7C, "osSpTaskStartGo"] 121 | - [0x802DAFC0, "osSpTaskYield"] 122 | - [0x802DB0A0, "osContInit"] 123 | - [0x802DB29C, "__osContGetInitData"] 124 | - [0x802DB36C, "__osPackRequestData"] 125 | - [0x802DB4D0, "osContStartReadData"] 126 | - [0x802DB594, "osContGetReadData"] 127 | - [0x802DB730, "osInvalCache"] 128 | - [0x802DB7B0, "bzero"] 129 | - [0x802DB9A0, "__osGetSR"] 130 | - [0x802DB9B0, "__osSetFpcCsr"] 131 | - [0x802DB9C0, "__osSiRawReadIo"] 132 | - [0x802DBA10, "__osSiRawWriteIo"] 133 | - [0x802DBA60, "__osExceptionPreamble"] 134 | - [0x802DBA70, "__osExceptionHandler"] 135 | - [0x802DC0A0, "__osEnqueueAndYield"] 136 | - [0x802DC130, "__osEnqueueThread"] 137 | - [0x802DC178, "__osPopThread"] 138 | - [0x802DC188, "__osDispatchThread"] 139 | - [0x802DC2C8, "__osCleanupThread"] 140 | - [0x802DC330, "__osDisableInt"] 141 | - [0x802DC350, "__osRestoreInt"] 142 | - [0x802DC3B0, "__osPiCreateAccessQueue"] 143 | - [0x802DC400, "__osPiGetAccess"] 144 | - [0x802DC444, "__osPiRelAccess"] 145 | - [0x802DC470, "osGetThreadPri"] 146 | - [0x802DC490, "osPiRawStartDma"] 147 | - [0x802DC570, "__osDevMgrMain"] 148 | - [0x802DC6F0, "__osTimerServicesInit"] 149 | - [0x802DC77C, "__osTimerInterrupt"] 150 | - [0x802DC8F4, "__osSetTimerIntr"] 151 | - [0x802DC968, "__osInsertTimer"] 152 | - [0x802DCAF0, "__osProbeTLB"] 153 | - [0x802DCBB0, "_Printf"] 154 | - [0x802DDD40, "memcpy"] 155 | - [0x802DFC00, "__osAiDeviceBusy"] 156 | - [0x802E21A0, "osJamMesg"] 157 | - [0x802E2330, "__osSpGetStatus"] 158 | - [0x802E2340, "bcopy"] 159 | - [0x802E2650, "__osSpSetStatus"] 160 | - [0x802E2660, "__osSpSetPc"] 161 | - [0x802E26A0, "__osSpRawStartDma"] 162 | - [0x802E2730, "__osSpDeviceBusy"] 163 | - [0x802E2760, "__osSiRawStartDma"] 164 | - [0x802E2810, "__osSiCreateAccessQueue"] 165 | - [0x802E2860, "__osSiGetAccess"] 166 | - [0x802E28A4, "__osSiRelAccess"] 167 | - [0x802E28D0, "__osSiDeviceBusy"] 168 | - [0x802E2D80, "__osSyncPutChars"] 169 | - [0x802E2EB0, "__osSetCompare"] 170 | - [0x802E5200, "__osAtomicDec"] 171 | -------------------------------------------------------------------------------- /configs/blast_dozer.code.yaml: -------------------------------------------------------------------------------- 1 | # ROM splitter configuration file 2 | name: "Blast Dozer (J) hd_code_text.raw" 3 | 4 | # checksums from ROM header offsets 0x10 and 0x14 5 | # used for auto configuration detection 6 | checksum1: 0x3C0E00FF 7 | checksum2: 0x35CEB000 8 | 9 | # base filename used for outputs (please, no spaces) 10 | basename: "blast_dozer.hd_code_text" 11 | 12 | # ranges to split the ROM into 13 | # types: 14 | # asm - MIPS assembly block. Symbol names are in 'labels' list below 15 | ranges: 16 | # start, end, type, label 17 | - [0x000000, 0x0A4360, "asm", "main", 0x802447C0] 18 | 19 | # Labels for functions or data memory addresses 20 | # All label addresses are RAM addresses 21 | # Order does not matter 22 | labels: 23 | - [0x802447C0, "MainJump"] 24 | - [0x80244870, "Thread1"] 25 | - [0x80244930, "Thread3"] 26 | - [0x8025615C, "LoadLevel"] 27 | - [0x80267C5C, "Thread4"] 28 | - [0x8026FEB0, "LoadLevelRdus"] 29 | - [0x8027127C, "Thread5"] 30 | - [0x8028B824, "InitiateDma"] 31 | - [0x8028C4F0, "LoadLevelAmmo"] 32 | - [0x8028D820, "LoadLevelTnt"] 33 | - [0x80290100, "LoadLevelSquareBlocks"] 34 | - [0x802A1E0C, "LoadLevelTodo74"] 35 | - [0x802A1F90, "LoadLevelTodo2C"] 36 | - [0x802A20C4, "LoadLevelBuildings"] 37 | - [0x802A2F20, "CopyBuildingTableToRam"] 38 | - [0x802A2E08, "InflateBuildingData"] 39 | - [0x802A2FC4, "LoadLevelTodo60"] 40 | - [0x802A30D8, "LoadLevelTodo4C"] 41 | - [0x802A3378, "LoadLevelTodo78"] 42 | - [0x802A387C, "LoadLevelVehicles"] 43 | - [0x802A33AC, "LoadLevelMissileCarrier"] 44 | - [0x802A40C4, "LoadLevelCollisionXZ"] 45 | - [0x802A4168, "LoadLevelTodo70"] 46 | - [0x802A420C, "LoadLevelTodo64"] 47 | - [0x802A42F0, "LoadLevelTrainPlatform"] 48 | - [0x802A47D4, "LoadLevelTerrain"] 49 | - [0x802A4854, "AlignTo8Bytes"] 50 | - [0x802A5880, "LoadLevelTodo40"] 51 | - [0x802A5974, "LoadLevelTodo44"] 52 | - [0x802A5B4C, "DecodeTexture"] 53 | - [0x802A5CC8, "DecodeTexture6"] 54 | - [0x802A5D9C, "DecodeTexture3"] 55 | - [0x802A5E50, "DecodeTexture1"] 56 | - [0x802A5F00, "DecodeTexture2"] 57 | - [0x802A5FCC, "DecodeTexture4"] 58 | - [0x802A60A4, "DecodeTexture5"] 59 | - [0x802A6180, "DecodeTexture0"] 60 | - [0x802C44D8, "GzipInflate"] 61 | - [0x802CEE70, "LoadLevelCommPoint"] 62 | - [0x802D43F0, "osInitialize"] 63 | - [0x802D4620, "osEPiRawReadIo"] 64 | - [0x802D4680, "osCreateThread"] 65 | - [0x802D47D0, "osStartThread"] 66 | - [0x802D4930, "osCreatePiManager"] 67 | - [0x802D4AB0, "osSetThreadPri"] 68 | - [0x802D4B90, "osSendMesg"] 69 | - [0x802D4CE0, "osRecvMesg"] 70 | - [0x802D4E20, "osViBlack"] 71 | - [0x802D4E90, "osGetTime"] 72 | - [0x802D4F20, "__ull_rshift"] 73 | - [0x802D4F4C, "__ull_rem"] 74 | - [0x802D4F88, "__ull_div"] 75 | - [0x802D4FC4, "__ll_lshift"] 76 | - [0x802D4FF0, "__ll_rem"] 77 | - [0x802D502C, "__ll_div"] 78 | - [0x802D5088, "__ll_mul"] 79 | - [0x802D50B8, "__ull_divremi"] 80 | - [0x802D5118, "__ll_mod"] 81 | - [0x802D51B4, "__ll_rshift"] 82 | - [0x802D51F0, "osVirtualToPhysical"] 83 | - [0x802D5270, "osWriteBackDCache"] 84 | - [0x802D5390, "guOrthoF"] 85 | - [0x802D54E4, "guFrustum"] 86 | - [0x802D5550, "guPerspectiveF"] 87 | - [0x802D5780, "guPerspective"] 88 | - [0x802D5BE0, "guMtxF2L"] 89 | - [0x802D5CE0, "guMtxIdentF"] 90 | - [0x802D5E50, "guRotateRPYF"] 91 | - [0x802D5EA4, "guRotateRPY"] 92 | - [0x802D63B0, "sqrtf"] 93 | - [0x802D66F0, "sinf"] 94 | - [0x802D68B0, "osCreateMesgQueue"] 95 | - [0x802D6920, "osViSetSpecialFeatures"] 96 | - [0x802D6AE0, "osWriteBackDCacheAll"] 97 | - [0x802D6B10, "osInvalDCache"] 98 | - [0x802D6BC0, "osDestroyThread"] 99 | - [0x802D6CC0, "cosf"] 100 | - [0x802D6E30, "proutSprintf"] 101 | - [0x802D6E9C, "sprintf"] 102 | - [0x802D7A30, "osSetIntMask"] 103 | - [0x802D8834, "alBnkfNew"] 104 | - [0x802D8938, "alSetFileNew"] 105 | - [0x802D9BF0, "osGetCount"] 106 | - [0x802D9CA0, "__osViInit"] 107 | - [0x802D9DD0, "osAiSetFrequency"] 108 | - [0x802D9F30, "osAiSetNextBuffer"] 109 | - [0x802D9FE0, "osAiGetLength"] 110 | - [0x802DA6C0, "osPiStartDma"] 111 | - [0x802DA9E0, "osCreateViManager"] 112 | - [0x802DAB64, "__osViDevMgrMain"] 113 | - [0x802DAD40, "osViSetMode"] 114 | - [0x802DADB0, "osSetEventMsg"] 115 | - [0x802DAE20, "osViSetEventMsg"] 116 | - [0x802DAF20, "osViSwapBuffer"] 117 | - [0x802DAF70, "osSetTimer"] 118 | - [0x802DB0D0, "osSpTaskDunno"] 119 | - [0x802DB1EC, "osSpTaskLoad"] 120 | - [0x802DB34C, "osSpTaskStartGo"] 121 | - [0x802DB390, "osSpTaskYield"] 122 | - [0x802DB470, "osContInit"] 123 | - [0x802DB66C, "__osContGetInitData"] 124 | - [0x802DB73C, "__osPackRequestData"] 125 | - [0x802DB8A0, "osContStartReadData"] 126 | - [0x802DB964, "osContGetReadData"] 127 | - [0x802DBB00, "osInvalCache"] 128 | - [0x802DBB80, "bzero"] 129 | # [0x802DBD60, "__osSetSR"] 130 | - [0x802DBD70, "__osGetSR"] 131 | - [0x802DBD80, "__osSetFpcCsr"] 132 | - [0x802DBD90, "__osSiRawReadIo"] 133 | - [0x802DBDE0, "__osSiRawWriteIo"] 134 | - [0x802DBE30, "__osExceptionPreamble"] 135 | - [0x802DBE40, "__osExceptionHandler"] 136 | - [0x802DC470, "__osEnqueueAndYield"] 137 | - [0x802DC500, "__osEnqueueThread"] 138 | - [0x802DC548, "__osPopThread"] 139 | - [0x802DC558, "__osDispatchThread"] 140 | - [0x802DC698, "__osCleanupThread", 0x802DC6A0] 141 | - [0x802DC700, "__osDisableInt"] 142 | - [0x802DC720, "__osRestoreInt"] 143 | - [0x802DC780, "__osPiCreateAccessQueue"] 144 | - [0x802DC7D0, "__osPiGetAccess"] 145 | - [0x802DC814, "__osPiRelAccess"] 146 | - [0x802DC840, "osGetThreadPri"] 147 | - [0x802DC860, "osPiRawStartDma"] 148 | - [0x802DC940, "__osDevMgrMain"] 149 | - [0x802DCAC0, "__osTimerServicesInit"] 150 | - [0x802DCB4C, "__osTimerInterrupt"] 151 | - [0x802DCCC4, "__osSetTimerIntr"] 152 | - [0x802DCD38, "__osInsertTimer"] 153 | - [0x802DCEC0, "__osProbeTLB"] 154 | - [0x802DCF80, "_Printf"] 155 | - [0x802DE110, "memcpy"] 156 | - [0x802DFFD0, "__osAiDeviceBusy"] 157 | - [0x802E2570, "osJamMesg"] 158 | - [0x802E2700, "__osSpGetStatus"] 159 | - [0x802E2710, "bcopy"] 160 | - [0x802E2A20, "__osSpSetStatus"] 161 | - [0x802E2A30, "__osSpSetPc"] 162 | - [0x802E2A70, "__osSpRawStartDma"] 163 | - [0x802E2B00, "__osSpDeviceBusy"] 164 | - [0x802E2B30, "__osSiRawStartDma"] 165 | - [0x802E2BE0, "__osSiCreateAccessQueue"] 166 | - [0x802E2C30, "__osSiGetAccess"] 167 | - [0x802E2C74, "__osSiRelAccess"] 168 | - [0x802E2CA0, "__osSiDeviceBusy"] 169 | - [0x802E3150, "__osSyncPutChars"] 170 | - [0x802E3280, "__osSetCompare"] 171 | - [0x802E55D0, "__osAtomicDec"] 172 | -------------------------------------------------------------------------------- /configs/doraemon.yaml: -------------------------------------------------------------------------------- 1 | name: "Doraemon - Mittsu no Seireiseki (J)" 2 | 3 | checksum1: 0xbff7b1c2 4 | checksum2: 0xaebf148e 5 | 6 | basename: "doraemon" 7 | 8 | ranges: 9 | - [0x000000, 0x000040, "header", "header"] 10 | - [0x000040, 0x001000, "bin", "boot"] 11 | - [0x001000, 0x0A0754, "asm", "main", 0x80000400] 12 | # TODO: RSP ucode 13 | # - [0x0A0830, 0x0A1030, "bin", "rsp.audio"] # 0x8009FC30 [0x0800] 14 | # - [0x0A3040, 0x0A4040, "bin", "rsp.graphics"] # 0x800A2440 [0x1000] 15 | 16 | labels: 17 | - [0x80000400, "EntryPoint"] 18 | - [0x80000450, "Main"] 19 | - [0x80001BF8, "ReadControllerData"] 20 | - [0x80001C38, "InitControllers"] 21 | - [0x800004C0, "Thread1_Idle"] 22 | - [0x80006D94, "Thread3"] 23 | - [0x80000BC0, "Thread4"] 24 | - [0x80000550, "Thread10"] 25 | - [0x80001EF0, "Thread20"] 26 | - [0x800016D0, "Thread30"] 27 | - [0x80002470, "Thread44_1"] 28 | - [0x80002944, "Thread44_2"] 29 | - [0x80002AF4, "Thread44_3"] 30 | - [0x8008BAC0, "osContInit"] 31 | - [0x8008AF70, "osGetTime"] 32 | - [0x80091950, "osGetCount"] 33 | - [0x80092068, "__ull_div"] 34 | - [0x80092000, "__ull_rshift"] 35 | - [0x8009202C, "__ull_rem"] 36 | - [0x800920A4, "__ll_lshift"] 37 | - [0x800920D0, "__ll_rem"] 38 | - [0x8009210C, "__ll_div"] 39 | - [0x80092168, "__ll_mul"] 40 | - [0x80092198, "__ull_divremi"] 41 | - [0x800921F8, "__ll_mod"] 42 | - [0x80092294, "__ll_rshift"] 43 | - [0x8008A840, "osCreateMesgQueue"] 44 | - [0x80095000, "osSetTimer"] 45 | - [0x8008AE30, "osRecvMesg"] 46 | - [0x8008B040, "osSendMesg"] 47 | - [0x8008BD88, "__osPackRequestData"] 48 | - [0x80098A70, "__osSiRawStartDma"] 49 | - [0x8008BCB8, "__osContGetInitData"] 50 | - [0x800989B0, "__osSiCreateAccessQueue"] 51 | - [0x8008BE80, "osEepromProbe"] 52 | - [0x800950E0, "osEepromRead"] 53 | - [0x80089E50, "osEepromLongRead"] 54 | - [0x80094B20, "osEepromWrite"] 55 | - [0x80089D10, "osEepromLongWrite"] 56 | - [0x80094CD0, "EepromWriteSerialize"] 57 | - [0x80091B40, "guMtxIdentF"] 58 | - [0x80091CB0, "guScaleF"] 59 | - [0x80091D04, "guScale"] 60 | - [0x80091A40, "guMtxF2L"] 61 | - [0x8008C820, "guPerspectiveF"] 62 | - [0x8008CA50, "guPerspective"] 63 | - [0x80091D50, "guRotateRPYF"] 64 | - [0x80091EE4, "guRotateRPY"] 65 | - [0x8008C4E0, "sinf"] 66 | - [0x8008C6A0, "cosf"] 67 | - [0x80098A00, "__osSiGetAccess"] 68 | - [0x80094DDC, "__osEepStatus"] 69 | - [0x80098A44, "__osSiRelAccess"] 70 | - [0x80089F90, "osInitialize"] 71 | - [0x800953F0, "__osGetSR"] 72 | - [0x80095400, "__osSetFpcCsr"] 73 | - [0x80095410, "__osSiRawReadIo"] 74 | - [0x80095460, "__osSiRawWriteIo"] 75 | - [0x8009D8E0, "__osSiDeviceBusy"] 76 | - [0x800954B0, "__osExceptionPreamble"] 77 | - [0x800954C0, "__osExceptionHandler"] 78 | - [0x80091890, "osWriteBackDCache"] 79 | - [0x80095DB0, "osInvalCache"] 80 | - [0x8008DD60, "osInvalDCache"] 81 | - [0x8008B320, "osWriteBackDCacheAll"] 82 | - [0x80095E90, "osEPiRawReadIo"] 83 | - [0x8008DBB0, "osPiStartDma"] 84 | - [0x8009CF90, "osJamMesg"] 85 | - [0x800984C0, "memcpy"] 86 | - [0x800984EC, "strlen"] 87 | - [0x80098514, "strchr"] 88 | - [0x80095EF0, "bzero"] 89 | - [0x80098560, "bcopy"] 90 | - [0x8008C810, "sqrtf"] 91 | - [0x8008B250, "sprintf"] 92 | - [0x8008B274, "proutSprintf"] 93 | - [0x80097E70, "_Printf"] 94 | - [0x8008D390, "osVirtualToPhysical"] 95 | - [0x8008B6E0, "osContStartReadData"] 96 | - [0x8008B7A4, "osContGetReadData"] 97 | - [0x80095ACC, "__osEnqueueAndYield"] 98 | - [0x80095C14, "__osPopThread"] 99 | - [0x80095BCC, "__osEnqueueThread"] 100 | - [0x80095C24, "__osDispatchThread"] 101 | - [0x80096860, "__osDisableInt"] 102 | - [0x80096880, "__osRestoreInt"] 103 | - [0x8008A230, "osCreateThread"] 104 | - [0x8008A5B0, "osDestroyThread"] 105 | - [0x80095DA0, "__osCleanupThread"] 106 | - [0x8008A870, "osCreateViManager"] 107 | - [0x8008A9F8, "__osViDevMgrMain"] 108 | - [0x80096FBC, "__osTimerInterrupt"] 109 | - [0x8009DB00, "__osSetCompare"] 110 | - [0x80097134, "__osSetTimerIntr"] 111 | - [0x800971A8, "__osInsertTimer"] 112 | - [0x80096F30, "__osTimerServicesInit"] 113 | - [0x8008ABD0, "osViSetMode"] 114 | - [0x8008AC40, "osViBlack"] 115 | - [0x800916D0, "osViSetSpecialFeatures"] 116 | - [0x8008B2D0, "osViSwapBuffer"] 117 | - [0x8008ACB0, "osSetEventMsg"] 118 | - [0x8008AD20, "osViSetEventMsg"] 119 | - [0x800969A0, "osGetThreadPri"] 120 | - [0x8008A4D0, "osSetThreadPri"] 121 | - [0x80097330, "__osViInit"] 122 | - [0x8008A380, "osStartThread"] 123 | - [0x800968E0, "__osPiCreateAccessQueue"] 124 | - [0x80096930, "__osPiGetAccess"] 125 | - [0x80096974, "__osPiRelAccess"] 126 | - [0x800969C0, "osPiRawStartDma"] 127 | - [0x8008A6B0, "osCreatePiManager"] 128 | - [0x80098870, "__osSpSetStatus"] 129 | - [0x800977F0, "__osSpGetStatus"] 130 | - [0x8008B46C, "osSpTaskLoad"] 131 | - [0x8008B5CC, "osSpTaskStartGo"] 132 | - [0x80098950, "__osSpDeviceBusy"] 133 | - [0x8008B6C0, "osSpTaskYield"] 134 | - [0x8008B350, "TaskVirtualToPhysical"] 135 | - [0x80098880, "__osSpSetPc"] 136 | - [0x800988C0, "__osSpRawStartDma"] 137 | - [0x8008D170, "osAiSetFrequency"] 138 | - [0x8008D4C0, "osAiGetLength"] 139 | - [0x8008D410, "osAiSetNextBuffer"] 140 | - [0x8009A950, "__osAiDeviceBusy"] 141 | - [0x8009A890, "__osProbeTLB"] 142 | -------------------------------------------------------------------------------- /configs/pifboot.yaml: -------------------------------------------------------------------------------- 1 | # ROM splitter configuration file name: "N64 PIF IPL NTSC" # checksums from ROM header offsets 0x10 and 0x14 # used for auto configuration detection 2 | checksum1: 0x40898000 3 | checksum2: 0x3C08A404 4 | 5 | # base filename used for outputs (please, no spaces) 6 | basename: "pif.ntsc" 7 | 8 | # ranges to split the ROM into 9 | # types: 10 | # asm - MIPS assembly block. Symbol names are in 'labels' list below 11 | ranges: 12 | # start, end, type, label 13 | - [0x000000, 0x0000D4, "asm", "rom", 0x1FC00000] 14 | - [0x0000D4, 0x000800, "asm", "ram", 0xA4000000] 15 | 16 | # Labels for functions or data memory addresses 17 | # All label addresses are RAM addresses 18 | # Order does not matter 19 | labels: 20 | - [0x1FC00000, "Reset"] 21 | - [0xA4000000, "Main"] 22 | - [0xA4000550, "Multiply"] 23 | - [0xA4000184, "proc_A4000184"] 24 | 25 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Example modifications 2 | 3 | This directory contains examples for replacing textures, patching assembly 4 | source, and updating binaries. The steps for applying the examples to a split 5 | ROM are given below. Most examples include a batch file to automate the process. 6 | 7 | ## Hello World 8 | Print Hello World to the screen by hooking into a behavior and overriding an 9 | unused function. Kills a butterfly. 10 | 11 | 1. patch -p0 < sm64.hello\_world.patch 12 | 13 | Thanks to Jedi for implementing this example and Kaze for his ASM Tutorial: 14 | http://www.smwcentral.net/?p=viewthread&t=68900 15 | 16 | ## Texture swap 17 | 18 | ### Castle grounds 19 | Adds a little color to the castle exterior texture in castle grounds (RGBA). 20 | 21 | 1. copy texture\_swap/castle\_grounds\_textures.0x01000.png to sm64.split/textures/ 22 | 23 | ### Peach's signature 24 | Replace Peach's signature (IA8) with someone else's. 25 | 26 | 1. copy texture\_swap/castle\_grounds\_segment7.0x0EAE8.ia8.png to sm64.split/textures/ 27 | 28 | ### Transition 29 | Swap star transition with mushroom. 30 | 31 | 1. copy texture\_swap/font\_graphics.0x122B8.ia8.png to sm64.split/textures/ 32 | 33 | ### Skybox 34 | Replace water skybox with night sky from Lylat System. 35 | 36 | 1. copy texture\_swap/water\_skybox.0x00000.skybox.png to sm64.split/textures/ 37 | 38 | ## HUD toggle 39 | Source patch for R-button toggle show HUD ASM 40 | 41 | 1. patch -p0 < sm64.hudtoggle.patch 42 | 43 | Thanks to Skelux and Kaze for each of their HUD toggling implementations: 44 | https://sites.google.com/site/supermario64starroad/home/sm64-documents/skelux 45 | http://smwc.me/1208284 46 | 47 | ## Coin colors 48 | Coin vertex colors and texture replacement. 49 | 50 | 1. copy the png files from coin\_colors/ to sm64.split/textures/ 51 | 2. Apply coin\_colors.ips IPS patch to sm64.split/bin/doors\_trees\_coins.bin 52 | 53 | Thanks to cpuHacka101 for details on this 54 | http://www.smwcentral.net/?p=viewthread&t=58544 55 | 56 | ## Behavior 57 | Source patch for modifying the sign posts behavior to rotate about the Y-axis 58 | 59 | 1. patch -p0 < sm64.rotating\_sign.patch 60 | 61 | ## Skip Screens 62 | Skip title, Mario, menu screens at startup or Peach and Lakitu intros. 63 | 64 | 1. patch -p0 < skip\_mario.patch 65 | 2. patch -p0 < skip\_title.patch 66 | 3. patch -p0 < skip\_menu.patch 67 | 4. patch -p0 < skip\_peach.patch 68 | 5. patch -p0 < skip\_lakitu.patch 69 | -------------------------------------------------------------------------------- /examples/behavior/sm64.rotating_sign.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/behavior_data.s 2 | +++ sm64.split/behavior_data.s 3 | @@ -2799,7 +2799,7 @@ 4 | .word 0x08000000 5 | .word 0x10050000 6 | .word 0x0C000000, ProcessCollision 7 | -.word 0x102B0000 8 | +.word 0x0F130100 9 | .word 0x09000000 10 | beh_sign_on_wall: # 3324 11 | .word 0x00090000 12 | -------------------------------------------------------------------------------- /examples/coin_colors/coin_colors.ips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/coin_colors/coin_colors.ips -------------------------------------------------------------------------------- /examples/coin_colors/doors_trees_coins.0x05780.ia16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/coin_colors/doors_trees_coins.0x05780.ia16.png -------------------------------------------------------------------------------- /examples/coin_colors/doors_trees_coins.0x05F80.ia16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/coin_colors/doors_trees_coins.0x05F80.ia16.png -------------------------------------------------------------------------------- /examples/coin_colors/doors_trees_coins.0x06780.ia16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/coin_colors/doors_trees_coins.0x06780.ia16.png -------------------------------------------------------------------------------- /examples/coin_colors/doors_trees_coins.0x06F80.ia16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/coin_colors/doors_trees_coins.0x06F80.ia16.png -------------------------------------------------------------------------------- /examples/hello_c/gen/extended.c: -------------------------------------------------------------------------------- 1 | extern void PrintXY(unsigned int x, unsigned int y, const char *str); 2 | 3 | const char HelloString[] = "- Hello World -"; 4 | unsigned int x = 1; 5 | unsigned int y = 1; 6 | char xdir = 0, ydir = 0; 7 | 8 | #define MAX_X 0x100 9 | #define MAX_Y 0xB0 10 | 11 | void BehHelloWorld(void) 12 | { 13 | PrintXY(x, y, HelloString); 14 | x += xdir ? -1 : 1; 15 | y += ydir ? -1 : 1; 16 | if (x == 0 || x == MAX_X) xdir = !xdir; 17 | if (y == 0 || y == MAX_Y) ydir = !ydir; 18 | } 19 | -------------------------------------------------------------------------------- /examples/hello_c/gen/sm64.h: -------------------------------------------------------------------------------- 1 | #ifndef SM64_H 2 | #define SM64_H 3 | 4 | #include 5 | 6 | // handful of useful routines to call from C 7 | 8 | // standard libary 9 | void bcopy(const void *src, void *dest, size_t n); 10 | void bzero(void *s, size_t n); 11 | 12 | // math 13 | float sinf(float); 14 | float cosf(float); 15 | float sqrtf(float); 16 | 17 | // SM64 18 | void PrintInt(unsigned int x, unsigned int y, const char *format, unsigned int value); 19 | void PrintStr(unsigned int x, unsigned int y, const char *str); 20 | void PrintXY(unsigned int x, unsigned int y, const char *str); 21 | 22 | #endif // SM64_H 23 | -------------------------------------------------------------------------------- /examples/hello_c/hello_c.patch: -------------------------------------------------------------------------------- 1 | diff -Naur gen.orig/behavior_data.s gen/behavior_data.s 2 | --- gen.orig/behavior_data.s 2015-08-05 13:07:51.819231111 -0500 3 | +++ gen/behavior_data.s 2015-08-05 13:13:20.057826973 -0500 4 | @@ -4692,3 +4692,9 @@ 5 | .word 0x0C000000, BehIntroSceneLoop 6 | .word 0x09000000 7 | .word 0x00000000 8 | + 9 | +beh_hello_world: 10 | +.word 0x08000000 11 | +.word 0x0C000000, BehHelloWorld 12 | +.word 0x09000000 13 | +.word 0x00000000 14 | diff -Naur gen.orig/extended.c gen/extended.c 15 | --- gen.orig/extended.c 1969-12-31 18:00:00.000000000 -0600 16 | +++ gen/extended.c 2015-08-06 16:31:17.162926557 -0500 17 | @@ -0,0 +1,18 @@ 18 | +#include "sm64.h" 19 | + 20 | +const char HelloString[] = "- Hello World -"; 21 | +unsigned int x = 1; 22 | +unsigned int y = 1; 23 | +char xdir = 0, ydir = 0; 24 | + 25 | +#define MAX_X 0x100 26 | +#define MAX_Y 0xB0 27 | + 28 | +void BehHelloWorld(void) 29 | +{ 30 | + PrintXY(x, y, HelloString); 31 | + x += xdir ? -1 : 1; 32 | + y += ydir ? -1 : 1; 33 | + if (x == 0 || x == MAX_X) xdir = !xdir; 34 | + if (y == 0 || y == MAX_Y) ydir = !ydir; 35 | +} 36 | diff -Naur gen.orig/levels/castle_grounds_level.s gen/levels/castle_grounds_level.s 37 | --- gen.orig/levels/castle_grounds_level.s 2015-08-05 13:07:52.329223754 -0500 38 | +++ gen/levels/castle_grounds_level.s 2015-08-05 13:22:20.880011503 -0500 39 | @@ -67,7 +67,7 @@ 40 | .word 0x24181FBB, 0x16F10307, 0xEA0A0000, 0x00000000, 0x00000000, beh_butterfly 41 | .word 0x24181FBB, 0x15610307, 0xEB360000, 0x00000000, 0x00000000, beh_butterfly 42 | .word 0x24181FBB, 0xFA200146, 0x0C7C0000, 0x00000000, 0x00000000, beh_butterfly 43 | -.word 0x24181FBB, 0xFB4C0146, 0x0CE00000, 0x00000000, 0x00000000, beh_butterfly 44 | +.word 0x24181FBB, 0xFB4C0146, 0x0CE00000, 0x00000000, 0x00000000, beh_hello_world 45 | .word 0x24181F55, 0x00000C66, 0xEA070000, 0x00000000, 0x00000000, beh_yoshi 46 | .word 0x07040000 47 | .word 0x1B040000 48 | diff -Naur gen.orig/sm64.h gen/sm64.h 49 | --- gen.orig/sm64.h 1969-12-31 18:00:00.000000000 -0600 50 | +++ gen/sm64.h 2015-08-06 16:31:11.946334171 -0500 51 | @@ -0,0 +1,22 @@ 52 | +#ifndef SM64_H 53 | +#define SM64_H 54 | + 55 | +#include 56 | + 57 | +// handful of useful routines to call from C 58 | + 59 | +// standard libary 60 | +void bcopy(const void *src, void *dest, size_t n); 61 | +void bzero(void *s, size_t n); 62 | + 63 | +// math 64 | +float sinf(float); 65 | +float cosf(float); 66 | +float sqrtf(float); 67 | + 68 | +// SM64 69 | +void PrintInt(unsigned int x, unsigned int y, const char *format, unsigned int value); 70 | +void PrintStr(unsigned int x, unsigned int y, const char *str); 71 | +void PrintXY(unsigned int x, unsigned int y, const char *str); 72 | + 73 | +#endif // SM64_H 74 | diff -Naur gen.orig/sm64.ld gen/sm64.ld 75 | --- gen.orig/sm64.ld 2015-08-05 13:07:52.405889314 -0500 76 | +++ gen/sm64.ld 2015-08-05 13:25:47.643687263 -0500 77 | @@ -3,6 +3,7 @@ 78 | 79 | OUTPUT_FORMAT ("elf32-bigmips", "elf32-bigmips", "elf32-littlemips") 80 | OUTPUT_ARCH (mips) 81 | +ENTRY(EntryPoint) 82 | 83 | SECTIONS 84 | { 85 | @@ -16,8 +17,8 @@ 86 | .rodata 0x800000 : { 87 | FILL (0x01) /* fill unused with 0x01 */ 88 | * (.mio0); 89 | - * (.rodata); 90 | - * (.data); 91 | + build/sm64.o (.rodata); 92 | + build/sm64.o (.data); 93 | * (.MIPS.abiflags); 94 | . = ALIGN(0x10); 95 | } 96 | @@ -26,13 +27,32 @@ 97 | .behavior 0x13000000 : AT( LOADADDR(.rodata) + SIZEOF(.rodata) ) { 98 | FILL (0x01) /* fill unused with 0x01 */ 99 | * (.behavior); 100 | + . = ALIGN(0x10); 101 | behavior_length = . - 0x13000000; 102 | - /* default 4MB data (12MB ROM) */ 103 | - . = 0x400000 - SIZEOF(.rodata); 104 | } 105 | __load_behavior_data = LOADADDR(.behavior); 106 | __load_behavior_data_end = LOADADDR(.behavior) + behavior_length; 107 | 108 | + /* Create new text section at 0x80400000 loaded immediately after behaviors */ 109 | + .text80400000 0x80400000 : AT(__load_behavior_data_end) { 110 | + __run_text80400000 = .; 111 | + * (.text80400000); /* use this for assembly sections */ 112 | + build/extended.o (.text); 113 | + build/extended.o (.rodata); 114 | + build/extended.o (.rodata.*); 115 | + build/extended.o (.data); 116 | + build/extended.o (.sdata); 117 | + build/extended.o (.bss); 118 | + build/extended.o (.sbss); 119 | + . = ALIGN(0x10); 120 | + __load_text80400000_len = . - 0x80400000; 121 | + /* default 4MB data (12MB ROM) */ 122 | + . = 0x400000 - SIZEOF(.rodata) - SIZEOF(.behavior); 123 | + } 124 | + /* Create two symbols for the start and end addresses. */ 125 | + __load_text80400000 = LOADADDR(.text80400000); 126 | + __load_text80400000_end = LOADADDR(.text80400000) + __load_text80400000_len; 127 | + 128 | /* (0x8016F000, 0x801B99DF, 0x7FF4FB40), // 21F4C0-269EA0 [4A9E0] */ 129 | .text8016F000 0x8016F000 : AT(0x21F4C0) { 130 | * (.text8016F000); 131 | diff -Naur gen.orig/sm64.s gen/sm64.s 132 | --- gen.orig/sm64.s 2015-08-05 13:07:52.405889314 -0500 133 | +++ gen/sm64.s 2015-08-05 13:12:31.271864732 -0500 134 | @@ -38,30 +38,39 @@ 135 | .LEntryPoint_10: # 80246010 136 | addi $t1, $t1, -8 137 | sw $zero, ($t0) 138 | - sw $zero, %lo(0x80340004)($t0) # $zero, 4($t0) 139 | + sw $zero, 4($t0) 140 | bnez $t1, .LEntryPoint_10 141 | addi $t0, $t0, 8 142 | - lui $t2, 0x8024 143 | - lui $sp, 0x8020 144 | - addiu $t2, $t2, 0x6df8 145 | - jr $t2 146 | - addiu $sp, $sp, 0x600 147 | -# end EntryPoint 148 | - 149 | -# alignment 150 | -.word 0x00000000, 0x00000000, 0x00000000, 0x00000000 151 | -.word 0x00000000, 0x00000000 152 | - 153 | -Unknown80246050: # begin 80246050 (001050) 154 | - addiu $sp, $sp, -8 155 | - lw $t6, 0x8032d5ec # lui $t6, 0x8033/lw $t6, -0x2a14($t6) 156 | - lhu $t7, 0x12($t6) 157 | - beqz $t7, .LUnknown80246050_110 158 | - nop 159 | - lh $t8, 0x8032d5bc # lui $t8, 0x8033/lh $t8, -0x2a44($t8) 160 | - lw $t1, 0x8032d5ec # lui $t1, 0x8033/lw $t1, -0x2a14($t1) 161 | - lui $t0, 0x8033 162 | - sll $t9, $t8, 1 163 | + 164 | +# intialize stack so we can call some routines 165 | + la $sp, 0x80200600 166 | + 167 | + jal osWriteBackDCacheAll 168 | + nop 169 | + 170 | + li $a0, 0x0 171 | + la $a1, __load_text80400000 172 | + la $a2, __run_text80400000 173 | + li $a3, __load_text80400000_len 174 | + jal osPiRawStartDma 175 | + nop 176 | + 177 | +.LUntilDmaDone: 178 | + lw $t0, 0xA4600010 179 | + andi $t0, $t0, 0x1 180 | + bne $t0, $zero, .LUntilDmaDone 181 | + nop 182 | + 183 | +# TODO: do these need different args? 184 | + jal osInvalCache 185 | + nop 186 | + jal osInvalDCache 187 | + nop 188 | +# this replaces the lui $t2, 0x8024/addiu $t2, $t2, 0x6df8/jr $t2 189 | + j Main 190 | + nop 191 | + 192 | +# leftover from Unknown80246050: 193 | addu $t0, $t0, $t9 194 | lhu $t0, -0x2a64($t0) 195 | lhu $t2, 0x12($t1) 196 | --- Makefile.orig 2015-08-05 12:49:18.333734339 -0500 197 | +++ Makefile 2015-08-05 12:48:52.617442852 -0500 198 | @@ -44,7 +44,7 @@ 199 | all: $(TARGET).gen.z64 200 | 201 | clean: 202 | - rm -f $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).o $(BUILD_DIR)/$(TARGET).bin $(TARGET).v64 203 | + rm -f $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/extended.o $(BUILD_DIR)/$(TARGET).o $(BUILD_DIR)/$(TARGET).bin $(TARGET).v64 204 | 205 | $(MIO0_DIR)/%.mio0: $(MIO0_DIR)/%.bin 206 | $(MIO0TOOL) $< $@ 207 | @@ -58,8 +58,8 @@ 208 | $(BUILD_DIR)/%.o: gen/%.c Makefile.as | $(BUILD_DIR) 209 | $(CC) $(CFLAGS) -o $@ $< 210 | 211 | -$(BUILD_DIR)/$(TARGET).elf: $(BUILD_DIR)/$(TARGET).o $(LD_SCRIPT) 212 | - $(LD) $(LDFLAGS) -o $@ $< $(LIBS) 213 | +$(BUILD_DIR)/$(TARGET).elf: $(BUILD_DIR)/$(TARGET).o $(BUILD_DIR)/extended.o $(LD_SCRIPT) 214 | + $(LD) $(LDFLAGS) -o $@ $< $(BUILD_DIR)/extended.o $(LIBS) 215 | 216 | $(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf 217 | $(OBJCOPY) $< $@ -O binary 218 | -------------------------------------------------------------------------------- /examples/hello_world/sm64.hello_world.patch: -------------------------------------------------------------------------------- 1 | diff -aur sm64.split.orig/behavior_data.s sm64.split/behavior_data.s 2 | --- sm64.split.orig/behavior_data.s 3 | +++ sm64.split/behavior_data.s 4 | @@ -4663,3 +4663,8 @@ 5 | .word 0x0C000000, BehIntroSceneLoop 6 | .word 0x09000000 7 | .word 0x00000000 8 | +beh_hello_world: 9 | +.word 0x08000000 10 | +.word 0x0C000000, BehHelloWorld 11 | +.word 0x09000000 12 | +.word 0x00000000 13 | diff -aur sm64.split.orig/levels/castle_grounds_level.s sm64.split/levels/castle_grounds_level.s 14 | --- sm64.split.orig/levels/castle_grounds_level.s 15 | +++ sm64.split/levels/castle_grounds_level.s 16 | @@ -56,7 +56,7 @@ 17 | .word 0x24181F37, 0xF1050D14, 0xE6090000, 0x00000000, 0x00000000, beh_castle_flag_waving 18 | .word 0x24181F37, 0x0EFB0D14, 0xE6090000, 0x00000000, 0x00000000, beh_castle_flag_waving 19 | .word 0x07040000 20 | -.word 0x24181FBB, 0xEE640196, 0x11300000, 0x00000000, 0x00000000, beh_butterfly 21 | +.word 0x24181FBB, 0xEE640196, 0x11300000, 0x00000000, 0x00000000, beh_hello_world 22 | .word 0x24181FBB, 0xEEC80196, 0x11940000, 0x00000000, 0x00000000, beh_butterfly 23 | .word 0x24181FBB, 0xED9C0196, 0x10040000, 0x00000000, 0x00000000, beh_butterfly 24 | .word 0x24181FBB, 0xE88D01D9, 0xF5C30000, 0x00000000, 0x00000000, beh_butterfly 25 | Only in sm64.split/levels: castle_grounds_level.s.orig 26 | diff -aur sm64.split.orig/sm64.s sm64.split/sm64.s 27 | --- sm64.split.orig/sm64.s 28 | +++ sm64.split/sm64.s 29 | @@ -141124,49 +141124,30 @@ 30 | addiu $sp, $sp, 8 31 | # end proc_802C9A3C 32 | 33 | -UnknownRecursive802C9AD8: # begin 802C9AD8 (084AD8) 34 | - addiu $sp, $sp, -0x28 35 | +# hello world behavior function 36 | +BehHelloWorld: # begin 802C9AD8 (084AD8) 37 | + # increase stack and store return address 38 | + addiu $sp, $sp, -0x18 39 | sw $ra, 0x14($sp) 40 | - sw $a0, 0x28($sp) 41 | - lw $t6, 0x28($sp) 42 | - sw $t6, 0x1c($sp) 43 | - lw $t7, 0x28($sp) 44 | - lw $t8, 0x10($t7) 45 | - beqz $t8, .LUnknownRecursive802C9AD8_34 46 | - sw $t8, 0x24($sp) 47 | - jal UnknownRecursive802C9AD8 48 | - lw $a0, 0x24($sp) 49 | - b .LUnknownRecursive802C9AD8_3C 50 | - nop 51 | -.LUnknownRecursive802C9AD8_34: # 802C9B0C 52 | - jal proc_802CA028 53 | - lw $a0, 0x28($sp) 54 | -.LUnknownRecursive802C9AD8_3C: # 802C9B14 55 | - lw $t9, 0x28($sp) 56 | - lw $t1, 0x1c($sp) 57 | - lw $t0, 8($t9) 58 | - bne $t0, $t1, .LUnknownRecursive802C9AD8_78 59 | - sw $t0, 0x20($sp) 60 | -.LUnknownRecursive802C9AD8_50: # 802C9B28 61 | - jal UnknownRecursive802C9AD8 62 | - lw $a0, 0x20($sp) 63 | - lw $t2, 0x20($sp) 64 | - lw $t3, 8($t2) 65 | - sw $t3, 0x28($sp) 66 | - lw $t4, 0x28($sp) 67 | - lw $t6, 0x1c($sp) 68 | - lw $t5, 8($t4) 69 | - beq $t5, $t6, .LUnknownRecursive802C9AD8_50 70 | - sw $t5, 0x20($sp) 71 | -.LUnknownRecursive802C9AD8_78: # 802C9B50 72 | - b .LUnknownRecursive802C9AD8_80 73 | - nop 74 | -.LUnknownRecursive802C9AD8_80: # 802C9B58 75 | + 76 | + la $a2, hello_string # message location, lui/addiu 77 | + li $a0, 0xA0 # X position 78 | + li $a1, 0xA8 # Y position 79 | + jal PrintXY 80 | + nop 81 | + 82 | + # restore return address and jump back 83 | lw $ra, 0x14($sp) 84 | - addiu $sp, $sp, 0x28 85 | + addiu $sp, $sp, 0x18 86 | jr $ra 87 | nop 88 | -# end UnknownRecursive802C9AD8 89 | +# end BehHelloWorld 90 | + 91 | +# placing the string here makes sure it's copied to RAM 92 | +hello_string: 93 | +.string "- Hello World -" 94 | + 95 | +.space 0x50, 0x0 # keep alignment the same 96 | 97 | proc_802C9B68: # begin 802C9B68 (084B68) 98 | addiu $sp, $sp, -0x18 99 | -------------------------------------------------------------------------------- /examples/hud_toggle/sm64.hudtoggle.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/sm64.s 2 | +++ sm64.split/sm64.s 3 | @@ -69951,11 +69951,11 @@ 4 | lw $t6, 0x40($sp) 5 | lui $at, 0x8034 6 | sw $t6, %lo(0x8033CBD0)($at) # $t6, -0x3430($at) 7 | - jal ChangeCameraStatus 8 | + jal HandleToggleHud 9 | lw $a0, 0x40($sp) 10 | lw $t7, 0x40($sp) 11 | lbu $t8, 0x30($t7) 12 | - bnez $t8, .Lproc_802868F8_98 13 | + b .Lproc_802868F8_98 # disable lakitu camera 14 | nop 15 | jal proc_80288624 16 | move $a0, $zero 17 | @@ -72380,73 +72380,37 @@ 18 | jr $ra 19 | nop 20 | 21 | -ChangeCameraStatus: # begin 80288E68 (043E68) 22 | - addiu $sp, $sp, -0x20 23 | +HandleToggleHud: 24 | + addiu $sp, $sp, -0x18 25 | sw $ra, 0x14($sp) 26 | - sw $a0, 0x20($sp) 27 | - sh $zero, 0x1e($sp) 28 | - lw $t6, 0x20($sp) 29 | - lbu $t7, 0x30($t6) 30 | - bnez $t7, .LChangeCameraStatus_4C 31 | - nop 32 | - lw $t8, 0x8032d5e4 # lui $t8, 0x8033/lw $t8, -0x2a1c($t8) 33 | - lhu $t9, 0x10($t8) 34 | - andi $t0, $t9, 0x10 35 | - beqz $t0, .LChangeCameraStatus_5C 36 | - nop 37 | - jal proc_80288624 38 | - move $a0, $zero 39 | - addiu $at, $zero, 2 40 | - bne $v0, $at, .LChangeCameraStatus_5C 41 | - nop 42 | -.LChangeCameraStatus_4C: # 80288EB4 43 | - lh $t1, 0x1e($sp) 44 | - ori $t2, $t1, 4 45 | - b .LChangeCameraStatus_8C 46 | - sh $t2, 0x1e($sp) 47 | -.LChangeCameraStatus_5C: # 80288EC4 48 | - jal proc_80288718 49 | - move $a0, $zero 50 | - addiu $at, $zero, 1 51 | - bne $v0, $at, .LChangeCameraStatus_80 52 | - nop 53 | - lh $t3, 0x1e($sp) 54 | - ori $t4, $t3, 1 55 | - b .LChangeCameraStatus_8C 56 | - sh $t4, 0x1e($sp) 57 | -.LChangeCameraStatus_80: # 80288EE8 58 | - lh $t5, 0x1e($sp) 59 | - ori $t6, $t5, 2 60 | - sh $t6, 0x1e($sp) 61 | -.LChangeCameraStatus_8C: # 80288EF4 62 | - lh $t7, 0x8033c848 # lui $t7, 0x8034/lh $t7, -0x37b8($t7) 63 | - andi $t8, $t7, 2 64 | - beqz $t8, .LChangeCameraStatus_AC 65 | - nop 66 | - lh $t9, 0x1e($sp) 67 | - ori $t0, $t9, 8 68 | - sh $t0, 0x1e($sp) 69 | -.LChangeCameraStatus_AC: # 80288F14 70 | - lh $t1, 0x8033c848 # lui $t1, 0x8034/lh $t1, -0x37b8($t1) 71 | - andi $t2, $t1, 0x2000 72 | - beqz $t2, .LChangeCameraStatus_CC 73 | - nop 74 | - lh $t3, 0x1e($sp) 75 | - ori $t4, $t3, 0x10 76 | - sh $t4, 0x1e($sp) 77 | -.LChangeCameraStatus_CC: # 80288F34 78 | - jal SetCameraStatus 79 | - lh $a0, 0x1e($sp) 80 | - b .LChangeCameraStatus_E4 81 | - lh $v0, 0x1e($sp) 82 | - b .LChangeCameraStatus_E4 83 | - nop 84 | -.LChangeCameraStatus_E4: # 80288F4C 85 | + lh $s1, 0x8033AFA0 # lui $s1, 0x8034 / lh $s1, -0x5060($s1) 86 | + lh $t0, 0x8032B1A0 # lui $t0, 0x8034 / lh $t0, -0x4E60($t0) 87 | + andi $s1, $s1, 0x10 88 | + beq $zero, $s1, .L_End # if R no being pressed, skip 89 | + nop 90 | + beq $t0, $s1, .L_PrintTimer # if being toggled on, play sound 91 | + nop 92 | + lui $a0, 0x701A 93 | + la $a1, 0x803331F0 # lui $a1, 0x8033 / addiu $a1, $a1, 0x31F0 94 | + jal SetSound # 8031EB00 95 | + ori $a0, $a0, 0x81 96 | +.L_PrintTimer: 97 | + addiu $a0, $zero, 0x102 98 | + lh $a3, 0x8033B226 # lui $a3, 0x8034 / lh $a3, -0x4DDA($a3) 99 | + beq $zero, $a3, .L_End 100 | + nop 101 | + la $a2, 0x803383A0 # lui $a2, 0x8034 / addiu $a2, $a2, 0x7C60 102 | + jal PrintInt # 802D62D8 103 | + li $a1, 0x20 104 | +.L_End: 105 | + lui $at, 0x8033 106 | + sh $s1, %lo(0x8032B1A0)($at) 107 | lw $ra, 0x14($sp) 108 | - addiu $sp, $sp, 0x20 109 | + addiu $sp, $sp, 0x18 110 | jr $ra 111 | nop 112 | -# end ChangeCameraStatus 113 | +# keep same size 114 | +.space 0x78, 0x00 115 | 116 | proc_80288F5C: # begin 80288F5C (043F5C) 117 | addiu $sp, $sp, -0x98 118 | @@ -168911,10 +168875,9 @@ 119 | addiu $sp, $sp, -0x20 120 | sw $ra, 0x14($sp) 121 | lh $t6, 0x8033b26a # lui $t6, 0x8034/lh $t6, -0x4d96($t6) 122 | + bnez $t6, .LRenderHud_40 123 | sh $t6, 0x1e($sp) 124 | - lh $t7, 0x1e($sp) 125 | - bnez $t7, .LRenderHud_40 126 | - nop 127 | + 128 | lui $at, 0x8033 129 | sb $zero, %lo(0x803325F0)($at) # $zero, 0x25f0($at) 130 | addiu $t8, $zero, 8 131 | @@ -168937,6 +168900,9 @@ 132 | jal RenderHudCannonReticle 133 | nop 134 | .LRenderHud_74: # 802E3DA0 135 | + lh $t1, 0x8033AFA0 # lui $t1, 0x8034 / lh $t1, -0x5060($t1) 136 | + andi $t1, $t1, 0x10 137 | + beq $t1, $zero, .LRenderHud_BC 138 | lh $t2, 0x1e($sp) 139 | andi $t3, $t2, 1 140 | beqz $t3, .LRenderHud_8C 141 | @@ -168971,7 +168937,7 @@ 142 | nop 143 | jal RenderHudHp 144 | nop 145 | - jal ShowCameraStatus 146 | + nop 147 | nop 148 | .LRenderHud_F4: # 802E3E20 149 | lh $t2, 0x1e($sp) 150 | @@ -168981,9 +168947,6 @@ 151 | jal RenderHudTimer 152 | nop 153 | .LRenderHud_10C: # 802E3E38 154 | - b .LRenderHud_114 155 | - nop 156 | -.LRenderHud_114: # 802E3E40 157 | lw $ra, 0x14($sp) 158 | addiu $sp, $sp, 0x20 159 | jr $ra 160 | -------------------------------------------------------------------------------- /examples/skip_screens/skip_lakitu.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/sm64.s 2 | +++ sm64.split/sm64.s 3 | @@ -6115,7 +6115,7 @@ 4 | lh $a0, 0x8032ddf4 # lui $a0, 0x8033/lh $a0, -0x220c($a0) 5 | jal proc_80279E44 6 | addiu $a0, $a0, -1 7 | - sltiu $s0, $v0, 1 8 | + li $s0, 0 9 | lui $at, 0x8034 10 | sb $s0, %lo(0x8033B26E)($at) # $s0, -0x4d92($at) 11 | lw $t6, 0x2c($sp) 12 | -------------------------------------------------------------------------------- /examples/skip_screens/skip_mario.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/levels/game_over_level.s 2 | +++ sm64.split/levels/game_over_level.s 3 | @@ -28,9 +28,9 @@ 4 | .word 0x19040002 5 | .word 0x170C0013, __load_behavior_data, __load_behavior_data_end 6 | .word 0x1A0C000A, mario_bg, mario_bg_end 7 | -.word 0x1D040000 8 | -.word 0x1F080100, 0x1400035C 9 | -.word 0x20040000 10 | + 11 | +.word 0x01100014, game_over_level, game_over_level_end, 0x1400020C 12 | + 13 | .word 0x1E040000 14 | .word 0x03040002 15 | .word 0x34040000 16 | @@ -48,9 +48,9 @@ 17 | .word 0x19040003 18 | .word 0x170C0013, __load_behavior_data, __load_behavior_data_end 19 | .word 0x1A0C000A, mario_bg, mario_bg_end 20 | -.word 0x1D040000 21 | -.word 0x1F080100, 0x140003B8 22 | -.word 0x20040000 23 | + 24 | +.word 0x01100014, game_over_level, game_over_level_end, 0x1400020C 25 | + 26 | .word 0x1E040000 27 | .word 0x03040002 28 | .word 0x34040000 29 | -------------------------------------------------------------------------------- /examples/skip_screens/skip_menu.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/levels/game_over_level.s 2 | +++ sm64.split/levels/game_over_level.s 3 | @@ -84,7 +84,7 @@ 4 | .word 0x1C040000 5 | .word 0x03040002 6 | .word 0x13040010 7 | -.word 0x01100014, main_menu_level, main_menu_level_end, 0x14000000 8 | +.word 0x01100015, main_level_scripts, main_level_scripts_end, 0x15000000 9 | .word 0x33080110, 0xFFFFFF00 10 | .word 0x03040010 11 | .word 0x1C040000 12 | -------------------------------------------------------------------------------- /examples/skip_screens/skip_peach.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/sm64.s 2 | +++ sm64.split/sm64.s 3 | @@ -5996,7 +5996,7 @@ 4 | lh $a0, 0x8032ddf4 # lui $a0, 0x8033/lh $a0, -0x220c($a0) 5 | jal proc_80279E44 6 | addiu $a0, $a0, -1 7 | - beqz $v0, .Lproc_8024BA8C_170 8 | + nop 9 | nop 10 | lw $a0, 0x8032d93c # lui $a0, 0x8033/lw $a0, -0x26c4($a0) 11 | li $a1, 0x0C400201 # lui $a1, 0xc40/ori $a1, $a1, 0x201 12 | -------------------------------------------------------------------------------- /examples/skip_screens/skip_title.patch: -------------------------------------------------------------------------------- 1 | --- sm64.split.orig/levels/game_over_level.s 2 | +++ sm64.split/levels/game_over_level.s 3 | @@ -9,9 +9,9 @@ 4 | .word 0x16100000, 0x8016F000, 0x0021F4C0, 0x00269EA0 5 | .word 0x170C0013, __load_behavior_data, __load_behavior_data_end 6 | .word 0x180C0007, wood_trademark, wood_trademark_end 7 | -.word 0x1D040000 8 | -.word 0x1F080100, 0x140002D0 9 | -.word 0x20040000 10 | + 11 | +.word 0x01100014, game_over_level, game_over_level_end, 0x14000078 12 | + 13 | .word 0x1E040000 14 | .word 0x29040100 15 | .word 0x11080000, LevelProc_8016F5B0 # 8016F5B0 16 | -------------------------------------------------------------------------------- /examples/texture_swap/castle_grounds_segment7.0x0EAE8.ia8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/texture_swap/castle_grounds_segment7.0x0EAE8.ia8.png -------------------------------------------------------------------------------- /examples/texture_swap/castle_grounds_textures.0x01000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/texture_swap/castle_grounds_textures.0x01000.png -------------------------------------------------------------------------------- /examples/texture_swap/font_graphics.0x122B8.ia8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/texture_swap/font_graphics.0x122B8.ia8.png -------------------------------------------------------------------------------- /examples/texture_swap/water_skybox.0x00000.skybox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/examples/texture_swap/water_skybox.0x00000.skybox.png -------------------------------------------------------------------------------- /f3d.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "utils.h" 6 | 7 | #define F3D_VERSION "0.2" 8 | 9 | #define F3D_MOVEMEM 0x03 10 | #define F3D_VTX 0x04 11 | #define F3D_DL 0x06 12 | #define F3D_QUAD 0xB5 13 | #define F3D_CLRGEOMODE 0xB6 14 | #define F3D_SETGEOMODE 0xB7 15 | #define F3D_ENDDL 0xB8 16 | #define F3D_TEXTURE 0xBB 17 | #define F3D_TRI1 0xBF 18 | #define G_SETTILESIZE 0xF2 19 | #define G_LOADBLOCK 0xF3 20 | #define G_SETTILE 0xF5 21 | #define G_SETFOGCOLOR 0xF8 22 | #define G_SETENVCOLOR 0xFB 23 | #define G_SETCOMBINE 0xFC 24 | #define G_SETTIMG 0xFD 25 | 26 | typedef struct 27 | { 28 | char *in_filename; 29 | char *out_filename; 30 | unsigned int offset; 31 | unsigned int length; 32 | } arg_config; 33 | 34 | static arg_config default_config = 35 | { 36 | NULL, 37 | NULL, 38 | 0, 39 | 0 40 | }; 41 | 42 | static void get_mode_string(unsigned char *data, char *description) 43 | { 44 | unsigned int val = read_u32_be(&data[4]); 45 | switch (val) { 46 | case 0x00022000: sprintf(description, "vertex RGB, no culling"); break; 47 | case 0x00020000: sprintf(description, "vertex RGB, culling"); break; 48 | case 0x00000000: sprintf(description, "no vertex RGB, culling"); break; 49 | case 0x00002200: sprintf(description, "no vertex RGB, no culling"); break; 50 | default: sprintf(description, "unknown"); break; 51 | } 52 | } 53 | 54 | static void print_f3d(FILE *fout, unsigned char *data) 55 | { 56 | char description[64]; 57 | unsigned char offset; 58 | unsigned int address; 59 | unsigned int val; 60 | // default description 61 | description[0] = '\0'; 62 | switch (data[0]) { 63 | case F3D_MOVEMEM: 64 | switch (data[1]) { 65 | case 0x86: sprintf(description, "light"); break; 66 | case 0x88: sprintf(description, "dark "); break; 67 | } 68 | address = read_u32_be(&data[4]); 69 | fprintf(fout, "%14s %s %08X", "F3D_MOVEMEM", description, address); 70 | break; 71 | case F3D_VTX: 72 | offset = data[1]; 73 | val = read_u16_be(&data[2]); 74 | address = read_u32_be(&data[4]); 75 | fprintf(fout, "%14s %02X %04X (%d) %08X", "F3D_VTX", offset, val, val/0x10, address); 76 | break; 77 | case F3D_DL: 78 | address = read_u32_be(&data[4]); 79 | fprintf(fout, "%14s %08X", "F3D_DL", address); 80 | break; 81 | case F3D_QUAD: 82 | { 83 | unsigned char vertex[6]; 84 | vertex[0] = data[1] / 0x0A; 85 | vertex[1] = data[2] / 0x0A; 86 | vertex[2] = data[3] / 0x0A; 87 | // data[6] unused 88 | vertex[3] = data[5] / 0x0A; 89 | vertex[4] = data[6] / 0x0A; 90 | vertex[5] = data[7] / 0x0A; 91 | fprintf(fout, "%14s %3d %3d %3d %3d %3d %3d", "F3D_QUAD", 92 | vertex[0], vertex[1], vertex[2], 93 | vertex[3], vertex[4], vertex[5]); 94 | break; 95 | } 96 | case F3D_CLRGEOMODE: 97 | get_mode_string(data, description); 98 | fprintf(fout, "%14s %s", "F3D_CLRGEOMODE", description); 99 | break; 100 | case F3D_SETGEOMODE: 101 | get_mode_string(data, description); 102 | fprintf(fout, "%14s %s", "F3D_SETGEOMODE", description); 103 | break; 104 | case F3D_ENDDL: 105 | fprintf(fout, "%14s %s", "F3D_ENDL", description); 106 | break; 107 | case F3D_TEXTURE: 108 | switch (data[3]) { 109 | case 0x00: 110 | val = read_u32_be(&data[4]); 111 | if (val == 0xFFFFFFFF) { 112 | sprintf(description, "end, reset scale to 0"); 113 | } 114 | break; 115 | case 0x01: 116 | val = read_u32_be(&data[4]); 117 | if (val == 0xFFFFFFFF) { 118 | sprintf(description, "start, set scale to 1"); 119 | } else if (val == 0x0F8007C0) { 120 | sprintf(description, "start environment mapping"); 121 | } 122 | break; 123 | } 124 | fprintf(fout, "%14s %s", "F3D_TEXTURE", description); 125 | break; 126 | case F3D_TRI1: 127 | { 128 | unsigned char vertex[3]; 129 | vertex[0] = data[5] / 0x0A; 130 | vertex[1] = data[6] / 0x0A; 131 | vertex[2] = data[7] / 0x0A; 132 | fprintf(fout, "%14s %3d %3d %3d", "F3D_TRI1", vertex[0], vertex[1], vertex[2]); 133 | break; 134 | } 135 | case G_SETTILESIZE: 136 | { 137 | unsigned short width, height; 138 | width = (((data[5] << 8) | (data[6] & 0xF0)) >> 6) + 1; 139 | height = (((data[6] & 0x0F) << 8 | data[7]) >> 2) + 1; 140 | fprintf(fout, "%14s %2d %2d", "G_SETTILESIZE", width, height); 141 | break; 142 | } 143 | case G_LOADBLOCK: 144 | { 145 | unsigned w0 = read_u32_be(data); 146 | unsigned w1 = read_u32_be(&data[4]); 147 | unsigned uls = (w0 >> 12) & 0x3FF; 148 | unsigned ult = w0 & 0x3FF; 149 | unsigned lrs = (w1 >> 12) & 0x3FF; 150 | unsigned dxt = w1 & 0x3FF; 151 | fprintf(fout, "%14s %03X %03X %03X %u", "G_LOADBLOCK", uls, ult, lrs, dxt); 152 | break; 153 | } 154 | case G_SETTILE: 155 | { 156 | const char * fmt_table[] = 157 | { 158 | "RGBA", "YUV", "CI", "IA", "I" 159 | }; 160 | unsigned format = (data[1] >> 5) & 0x7; // bits 21-23 161 | unsigned size = (data[1] >> 3) & 0x3; // bits 19-20 162 | unsigned depth = 0; 163 | switch (size) { 164 | case 0: depth = 4; break; 165 | case 1: depth = 8; break; 166 | case 2: depth = 16; break; 167 | case 3: depth = 32; break; 168 | default: ERROR("Unknown depth: %d\n", size); 169 | } 170 | if (format < DIM(fmt_table)) { 171 | sprintf(description, "%s %d", fmt_table[format], depth); 172 | } 173 | fprintf(fout, "%14s %s", "G_SETTILE", description); 174 | break; 175 | } 176 | case G_SETFOGCOLOR: 177 | fprintf(fout, "%14s %3d, %3d, %3d, %3d", "G_SETFOGCOLOR", data[4], data[5], data[6], data[7]); 178 | break; 179 | case G_SETENVCOLOR: 180 | fprintf(fout, "%14s %3d, %3d, %3d, %3d", "G_SETENVCOLOR", data[4], data[5], data[6], data[7]); 181 | break; 182 | case G_SETCOMBINE: 183 | { 184 | struct {unsigned char data[7]; char *description;} table[] = 185 | { 186 | {{0x12, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x38}, "solid RGBA"}, 187 | {{0x12, 0x18, 0x24, 0xFF, 0x33, 0xFF, 0xFF}, "alpha RGBA"}, 188 | }; 189 | unsigned i; 190 | for (i = 0; i < DIM(table); i++) { 191 | if (!memcmp(table[i].data, &data[1], 7)) { 192 | strcpy(description, table[i].description); 193 | } 194 | } 195 | fprintf(fout, "%14s %s", "G_SETCOMBINE", description); 196 | break; 197 | } 198 | case G_SETTIMG: 199 | address = read_u32_be(&data[4]); 200 | fprintf(fout, "%14s %08X", "G_SETTIMG", address); 201 | break; 202 | default: 203 | fprintf(fout, "%14s %s", "Unknown", description); 204 | break; 205 | } 206 | } 207 | 208 | static void print_usage(void) 209 | { 210 | ERROR("Usage: f3d [-l LENGTH] [-o OFFSET] FILE\n" 211 | "\n" 212 | "f3d v" F3D_VERSION ": N64 Fast3D display list decoder\n" 213 | "\n" 214 | "Optional arguments:\n" 215 | " -l LENGTH length of data to decode in bytes (default: length of file)\n" 216 | " -o OFFSET starting offset in FILE (default: 0)\n" 217 | "\n" 218 | "File arguments:\n" 219 | " FILE input file\n" 220 | " [OUTPUT] output file (default: stdout)\n"); 221 | exit(1); 222 | } 223 | 224 | // parse command line arguments 225 | static void parse_arguments(int argc, char *argv[], arg_config *config) 226 | { 227 | int i; 228 | int file_count = 0; 229 | if (argc < 2) { 230 | print_usage(); 231 | exit(1); 232 | } 233 | for (i = 1; i < argc; i++) { 234 | if (argv[i][0] == '-') { 235 | switch (argv[i][1]) { 236 | case 'l': 237 | if (++i >= argc) { 238 | print_usage(); 239 | } 240 | config->length = strtoul(argv[i], NULL, 0); 241 | break; 242 | case 'o': 243 | if (++i >= argc) { 244 | print_usage(); 245 | } 246 | config->offset = strtoul(argv[i], NULL, 0); 247 | break; 248 | default: 249 | print_usage(); 250 | break; 251 | } 252 | } else { 253 | switch (file_count) { 254 | case 0: 255 | config->in_filename = argv[i]; 256 | break; 257 | case 1: 258 | config->out_filename = argv[i]; 259 | break; 260 | default: // too many 261 | print_usage(); 262 | break; 263 | } 264 | file_count++; 265 | } 266 | } 267 | if (file_count < 1) { 268 | print_usage(); 269 | } 270 | } 271 | 272 | int main(int argc, char *argv[]) 273 | { 274 | arg_config config; 275 | FILE *fout; 276 | unsigned char *data; 277 | long size; 278 | unsigned int i; 279 | int stop_on_enddl = 0; 280 | 281 | // get configuration from arguments 282 | config = default_config; 283 | parse_arguments(argc, argv, &config); 284 | if (config.out_filename == NULL) { 285 | fout = stdout; 286 | } else { 287 | fout = fopen(config.out_filename, "w"); 288 | if (fout == NULL) { 289 | perror("Error opening output file"); 290 | return EXIT_FAILURE; 291 | } 292 | } 293 | 294 | // operation 295 | size = read_file(config.in_filename, &data); 296 | if (size < 0) { 297 | perror("Error opening input file"); 298 | return EXIT_FAILURE; 299 | } 300 | if (config.length == 0) { 301 | config.length = size - config.offset; 302 | stop_on_enddl = 1; 303 | } 304 | if (config.offset >= (unsigned int)size) { 305 | ERROR("Error: offset greater than file size (%X > %X)\n", 306 | config.offset, (unsigned int)size); 307 | return EXIT_FAILURE; 308 | } 309 | if (config.offset + config.length > (unsigned int)size) { 310 | ERROR("Warning: length goes beyond file size (%X > %X), truncating\n", 311 | config.offset + config.length, (unsigned int)size); 312 | config.length = size - config.offset; 313 | } 314 | 315 | for (i = config.offset; i < config.offset + config.length; i += 8) { 316 | fprintf(fout, "%05X: %08X %08X", i, read_u32_be(&data[i]), read_u32_be(&data[i+4])); 317 | print_f3d(fout, &data[i]); 318 | fprintf(fout, "\n"); 319 | if (stop_on_enddl && F3D_ENDDL == data[i]) { 320 | break; 321 | } 322 | } 323 | 324 | free(data); 325 | if (fout != stdout) { 326 | fclose(fout); 327 | } 328 | 329 | return 0; 330 | } 331 | -------------------------------------------------------------------------------- /libblast.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBBLAST_H_ 2 | #define LIBBLAST_H_ 3 | 4 | // 802A5E10 (061650) 5 | // just a memcpy from a0 to a3 6 | int decode_block0(unsigned char *in, int length, unsigned char *out); 7 | 8 | // 802A5AE0 (061320) 9 | int decode_block1(unsigned char *in, int length, unsigned char *out); 10 | 11 | // 802A5B90 (0613D0) 12 | int decode_block2(unsigned char *in, int length, unsigned char *out); 13 | 14 | // 802A5A2C (06126C) 15 | int decode_block3(unsigned char *in, int length, unsigned char *out); 16 | 17 | // 802A5C5C (06149C) 18 | int decode_block4(unsigned char *in, int length, unsigned char *out, unsigned char *lut); 19 | 20 | // 802A5D34 (061574) 21 | int decode_block5(unsigned char *in, int length, unsigned char *out, unsigned char *lut); 22 | 23 | // 802A5958 (061198) 24 | int decode_block6(unsigned char *in, int length, unsigned char *out); 25 | 26 | // decode Blast Corps compressed data of given type 27 | // in_filename - input file name of compressed data 28 | // type - type of compression: 0-6 29 | // out_filename - output file name of uncompressed data 30 | // lut - lookup table to use for types 4 and 5 31 | // returns 0 on success, non-0 otherwise 32 | int blast_decode_file(char *in_filename, int type, char *out_filename, unsigned char *lut); 33 | 34 | #endif // LIBBLAST_H_ 35 | -------------------------------------------------------------------------------- /libmio0.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBMIO0_H_ 2 | #define LIBMIO0_H_ 3 | 4 | // defines 5 | 6 | #define MIO0_HEADER_LENGTH 16 7 | 8 | // typedefs 9 | 10 | typedef struct 11 | { 12 | unsigned int dest_size; 13 | unsigned int comp_offset; 14 | unsigned int uncomp_offset; 15 | } mio0_header_t; 16 | 17 | // function prototypes 18 | 19 | // decode MIO0 header 20 | // returns 1 if valid header, 0 otherwise 21 | int mio0_decode_header(const unsigned char *buf, mio0_header_t *head); 22 | 23 | // encode MIO0 header from struct 24 | void mio0_encode_header(unsigned char *buf, const mio0_header_t *head); 25 | 26 | // decode MIO0 data in memory 27 | // in: buffer containing MIO0 data 28 | // out: buffer for output data 29 | // end: output offset of the last byte decoded from in (set to NULL if unwanted) 30 | // returns bytes extracted to 'out' or negative value on failure 31 | int mio0_decode(const unsigned char *in, unsigned char *out, unsigned int *end); 32 | 33 | // encode MIO0 data in memory 34 | // in: buffer containing raw data 35 | // out: buffer for MIO0 data 36 | // returns size of compressed data in 'out' including MIO0 header 37 | int mio0_encode(const unsigned char *in, unsigned int length, unsigned char *out); 38 | 39 | // decode an entire MIO0 block at an offset from file to output file 40 | // in_file: input filename 41 | // offset: offset to start decoding from in_file 42 | // out_file: output filename 43 | int mio0_decode_file(const char *in_file, unsigned long offset, const char *out_file); 44 | 45 | // encode an entire file 46 | // in_file: input filename containing raw data to be encoded 47 | // out_file: output filename to write MIO0 compressed data to 48 | int mio0_encode_file(const char *in_file, const char *out_file); 49 | 50 | #endif // LIBMIO0_H_ 51 | -------------------------------------------------------------------------------- /libsfx.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSFX_H_ 2 | #define LIBSFX_H_ 3 | 4 | // defines 5 | 6 | // typedefs 7 | 8 | typedef struct { 9 | unsigned int order; 10 | unsigned int predictor_count; 11 | unsigned *data; 12 | } predictor_data; 13 | 14 | typedef struct { 15 | unsigned int start; 16 | unsigned int end; 17 | unsigned int count; 18 | unsigned int unknown; 19 | unsigned *state; 20 | } loop_data; 21 | 22 | typedef struct { 23 | unsigned int unknown_1; 24 | unsigned int sound_offset; 25 | loop_data *loop; 26 | predictor_data *predictor; 27 | unsigned int sound_length; 28 | unsigned int unknown_2; 29 | unsigned int unknown_3; 30 | unsigned int unknown_4; 31 | } wave_table; 32 | 33 | typedef struct { 34 | unsigned char unknown_1; 35 | unsigned char pan; 36 | unsigned unknown_2; 37 | wave_table *wav; 38 | float key_base; 39 | unsigned *adrs; 40 | } percussion; 41 | 42 | typedef struct { 43 | percussion *items; 44 | } percussion_table; 45 | 46 | typedef struct { 47 | unsigned int unknown; 48 | unsigned *adrs; 49 | wave_table *wav_prev; 50 | float key_base_prev; 51 | wave_table *wav; 52 | float key_base; 53 | wave_table *wav_sec; 54 | float key_base_sec; 55 | } sound; 56 | 57 | typedef struct { 58 | unsigned int instrument_count; 59 | unsigned int percussion_count; 60 | unsigned int unknown_1; 61 | unsigned int unknown_2; 62 | percussion_table percussions; 63 | sound *sounds; 64 | } sound_bank; 65 | 66 | typedef struct { 67 | unsigned unknown; 68 | unsigned bank_count; 69 | sound_bank *banks; 70 | } sound_bank_header; 71 | 72 | typedef struct { 73 | unsigned unknown; 74 | unsigned data_count; 75 | unsigned char **data; 76 | } sound_data_header; 77 | 78 | // function prototypes 79 | 80 | //NEEDS COMMENTS!!! 81 | 82 | // initialize the key table for vadpcm decoding 83 | void sfx_initialize_key_table(); 84 | 85 | // read the sound bank table 86 | // data: buffer containing sound bank data 87 | // data_offset: offset in data where the sound bank begins 88 | // returns a sound_data_header which contains info about all the sounds stored in the rom 89 | sound_bank_header read_sound_bank(unsigned char *data, unsigned int data_offset); 90 | 91 | // read the sound data table 92 | // data: buffer containing sound data 93 | // data_offset: offset in data where the sound data begins 94 | // returns a sound_data_header which contains the raw, encoded sound data 95 | sound_data_header read_sound_data(unsigned char *data, unsigned int data_offset); 96 | 97 | // create a .wav file from provided encoded sound data 98 | // sound_dir: directory to store the .wav file in 99 | // wav_name: name for the new .wav file 100 | // wav: the sound information that's stored in the sound_bank_header 101 | // key_base: not entirely sure what this is, but it's converted & stored in the new .wav file 102 | // snd_data: buffer containing the raw, encoded sound data 103 | // sampling_rate: sample rate for the sound data (higher sampling rate = wav file speeds up) 104 | // returns 1 if the .wav file was created, 0 if not 105 | int extract_raw_sound(char *sound_dir, char *wav_name, wave_table *wav, float key_base, unsigned char *snd_data, unsigned long sampling_rate); 106 | 107 | #endif // LIBMIO0_H_ 108 | -------------------------------------------------------------------------------- /libsm64.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSM64_H_ 2 | #define LIBSM64_H_ 3 | 4 | #define MIO0_DIR "mio0files" 5 | 6 | // typedefs 7 | typedef enum 8 | { 9 | ROM_INVALID, // not valid SM64 ROM 10 | ROM_SM64_BS, // SM64 byte-swapped (BADC) 11 | ROM_SM64_BE, // SM64 big-endian (ABCD) 12 | ROM_SM64_LE, // SM64 little-endian 13 | ROM_SM64_BE_EXT, // SM64 big-endian, extended 14 | } rom_type; 15 | 16 | typedef enum 17 | { 18 | VERSION_UNKNOWN, 19 | VERSION_SM64_U, 20 | VERSION_SM64_E, 21 | VERSION_SM64_J, 22 | VERSION_SM64_SHINDOU, 23 | VERSION_SM64_IQUE, 24 | } rom_version; 25 | 26 | typedef struct 27 | { 28 | char *in_filename; 29 | char *ext_filename; 30 | unsigned int ext_size; 31 | unsigned int padding; 32 | unsigned int alignment; 33 | char fill; 34 | char dump; 35 | } sm64_config; 36 | 37 | // determine ROM type based on data 38 | // buf: buffer containing raw SM64 ROM file data 39 | // length: length of 'buf' 40 | // returns SM64 ROM type or invalid 41 | rom_type sm64_rom_type(unsigned char *buf, unsigned int length); 42 | 43 | // determine SM64 ROM type based on cksum data 44 | // buf: buffer containing raw SM64 ROM file data 45 | // returns SM64 ROM version or unknown 46 | rom_version sm64_rom_version(unsigned char *buf); 47 | 48 | // find and decompress all MIO0 blocks 49 | // config: configuration to determine alignment, padding and size 50 | // in_buf: buffer containing entire contents of SM64 data in big endian 51 | // length: length of in_buf 52 | // out_buf: buffer containing extended SM64 53 | void sm64_decompress_mio0(const sm64_config *config, 54 | unsigned char *in_buf, 55 | unsigned int in_length, 56 | unsigned char *out_buf); 57 | 58 | // update N64 header checksums 59 | // buf: buffer containing ROM data 60 | // checksums are written into the buffer 61 | void sm64_update_checksums(unsigned char *buf); 62 | 63 | #endif // LIBSM64_H_ 64 | -------------------------------------------------------------------------------- /mipsdisasm.h: -------------------------------------------------------------------------------- 1 | #ifndef MIPSDISASM_H_ 2 | #define MIPSDISASM_H_ 3 | 4 | // typedefs 5 | typedef struct _disasm_state disasm_state; 6 | 7 | typedef enum 8 | { 9 | ASM_GAS, // GNU as 10 | ASM_ARMIPS, // armips 11 | } asm_syntax; 12 | 13 | // allocate and initialize disassembler state to be passed into disassembler routines 14 | // syntax: assembler syntax to use 15 | // merge_pseudo: if true, attempt to link pseudo instructions 16 | // returns disassembler state 17 | disasm_state *disasm_state_init(asm_syntax syntax, int merge_pseudo); 18 | 19 | // free disassembler state allocated during pass1 20 | // state: disassembler state returned from disasm_state_alloc() or mipsdisasm_pass1() 21 | void disasm_state_free(disasm_state *state); 22 | 23 | // add a label to the disassembler state 24 | // state: disassembler state returned from disasm_state_alloc() or mipsdisasm_pass1() 25 | // name: string name of label (if NULL, generated based on vaddr) 26 | // vaddr: virtual address of label 27 | void disasm_label_add(disasm_state *state, const char *name, unsigned int vaddr); 28 | 29 | // lookup a global label from the disassembler state 30 | // state: disassembler state returned from disasm_state_alloc() or mipsdisasm_pass1() 31 | // vaddr: virtual address of label 32 | // name: string to write label to 33 | // returns 1 if found, 0 otherwise 34 | int disasm_label_lookup(const disasm_state *state, unsigned int vaddr, char *name); 35 | 36 | // first pass of disassembler - collects procedures called and sorts them 37 | // data: buffer containing raw MIPS assembly 38 | // offset: buffer offset to start at 39 | // length: length to disassemble starting at 'offset' 40 | // vaddr: virtual address of first byte 41 | // syntax: assembler syntax to use 42 | // state: disassembler state. if NULL, is allocated, returned at end 43 | void mipsdisasm_pass1(unsigned char *data, unsigned int offset, unsigned int length, unsigned int vaddr, disasm_state *state); 44 | 45 | // disassemble a region of code, output to file stream 46 | // out: stream to output data to 47 | // state: disassembler state from pass1 48 | // offset: starting offset to match in disassembler state 49 | void mipsdisasm_pass2(FILE *out, disasm_state *state, unsigned int offset); 50 | 51 | // get version string of raw disassembler 52 | const char *disasm_get_version(void); 53 | 54 | #endif // MIPSDISASM_H_ 55 | -------------------------------------------------------------------------------- /n64cksum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "libsm64.h" 5 | #include "utils.h" 6 | 7 | #define N64CKSUM_VERSION "0.1" 8 | 9 | static void print_usage(void) 10 | { 11 | ERROR("Usage: n64cksum ROM [ROM_OUT]\n" 12 | "\n" 13 | "n64cksum v" N64CKSUM_VERSION ": N64 ROM checksum calculator\n" 14 | "\n" 15 | "File arguments:\n" 16 | " ROM input ROM file\n" 17 | " ROM_OUT output ROM file (default: overwrites input ROM)\n"); 18 | } 19 | 20 | int main(int argc, char *argv[]) 21 | { 22 | unsigned char *rom_data; 23 | char *file_in; 24 | char *file_out; 25 | long length; 26 | long write_length; 27 | if (argc < 2) { 28 | print_usage(); 29 | return EXIT_FAILURE; 30 | } 31 | 32 | file_in = argv[1]; 33 | if (argc > 2) { 34 | file_out = argv[2]; 35 | } else { 36 | file_out = argv[1]; 37 | } 38 | 39 | length = read_file(file_in, &rom_data); 40 | if (length < 0) { 41 | ERROR("Error reading input file \"%s\"\n", file_in); 42 | return EXIT_FAILURE; 43 | } 44 | 45 | sm64_update_checksums(rom_data); 46 | 47 | write_length = write_file(file_out, rom_data, length); 48 | 49 | free(rom_data); 50 | 51 | if (write_length != length) { 52 | ERROR("Error writing to output file \"%s\"\n", file_out); 53 | return EXIT_FAILURE; 54 | } 55 | 56 | return EXIT_SUCCESS; 57 | } 58 | -------------------------------------------------------------------------------- /n64graphics.h: -------------------------------------------------------------------------------- 1 | #ifndef N64GRAPHICS_H_ 2 | #define N64GRAPHICS_H_ 3 | 4 | #include 5 | 6 | // intermediate formats 7 | typedef struct _rgba 8 | { 9 | uint8_t red; 10 | uint8_t green; 11 | uint8_t blue; 12 | uint8_t alpha; 13 | } rgba; 14 | 15 | typedef struct _ia 16 | { 17 | uint8_t intensity; 18 | uint8_t alpha; 19 | } ia; 20 | 21 | // CI palette 22 | typedef struct 23 | { 24 | uint16_t data[256]; 25 | int max; // max number of entries 26 | int used; // number of entries used 27 | } palette_t; 28 | 29 | //--------------------------------------------------------- 30 | // N64 RGBA/IA/I/CI -> intermediate RGBA/IA 31 | //--------------------------------------------------------- 32 | 33 | // N64 raw RGBA16/RGBA32 -> intermediate RGBA 34 | rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth); 35 | 36 | // N64 raw IA1/IA4/IA8/IA16 -> intermediate IA 37 | ia *raw2ia(const uint8_t *raw, int width, int height, int depth); 38 | 39 | // N64 raw I4/I8 -> intermediate IA 40 | ia *raw2i(const uint8_t *raw, int width, int height, int depth); 41 | 42 | //--------------------------------------------------------- 43 | // intermediate RGBA/IA -> N64 RGBA/IA/I/CI 44 | // returns length written to 'raw' used or -1 on error 45 | //--------------------------------------------------------- 46 | 47 | // intermediate RGBA -> N64 raw RGBA16/RGBA32 48 | int rgba2raw(uint8_t *raw, const rgba *img, int width, int height, int depth); 49 | 50 | // intermediate IA -> N64 raw IA1/IA4/IA8/IA16 51 | int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth); 52 | 53 | // intermediate IA -> N64 raw I4/I8 54 | int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth); 55 | 56 | 57 | //--------------------------------------------------------- 58 | // N64 CI <-> N64 RGBA16/IA16 59 | //--------------------------------------------------------- 60 | 61 | // N64 CI raw data and palette to raw data (either RGBA16 or IA16) 62 | uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int height, int ci_depth); 63 | 64 | // convert from raw (RGBA16 or IA16) format to CI + palette 65 | int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth); 66 | 67 | 68 | //--------------------------------------------------------- 69 | // intermediate RGBA/IA -> PNG 70 | //--------------------------------------------------------- 71 | 72 | // intermediate RGBA write to PNG file 73 | int rgba2png(const char *png_filename, const rgba *img, int width, int height); 74 | 75 | // intermediate IA write to grayscale PNG file 76 | int ia2png(const char *png_filename, const ia *img, int width, int height); 77 | 78 | 79 | //--------------------------------------------------------- 80 | // PNG -> intermediate RGBA/IA 81 | //--------------------------------------------------------- 82 | 83 | // PNG file -> intermediate RGBA 84 | rgba *png2rgba(const char *png_filename, int *width, int *height); 85 | 86 | // PNG file -> intermediate IA 87 | ia *png2ia(const char *png_filename, int *width, int *height); 88 | 89 | 90 | //--------------------------------------------------------- 91 | // version 92 | //--------------------------------------------------------- 93 | 94 | // get version of underlying graphics reading library 95 | const char *n64graphics_get_read_version(void); 96 | 97 | // get version of underlying graphics writing library 98 | const char *n64graphics_get_write_version(void); 99 | 100 | #endif // N64GRAPHICS_H_ 101 | -------------------------------------------------------------------------------- /n64split.collision.mtl.h: -------------------------------------------------------------------------------- 1 | static const char collision_mtl_data[] = 2 | "# collision model materials\n" 3 | "#\n" 4 | "# Ka: ambient reflectivity\n" 5 | "# Kd: diffuse reflectivity\n" 6 | "# illum: illuminaiton model\n" 7 | "# * 1: Color on and Ambient on\n" 8 | "# * 2: Highlight on\n" 9 | "# * 3: Reflection on and Ray trace on\n" 10 | "# * 4: Transparency: Glass on\n" 11 | "# Reflection: Ray trace on\n" 12 | "# * 5: Reflection: Fresnel on and Ray trace on\n" 13 | "# * 6: Transparency: Refraction on\n" 14 | "# Reflection: Fresnel off and Ray trace on\n" 15 | "# * 7: Transparency: Refraction on\n" 16 | "# Reflection: Fresnel on and Ray trace on\n" 17 | "# * 8: Reflection on and Ray trace off\n" 18 | "# * 9: Transparency: Glass on\n" 19 | "# Reflection: Ray trace off\n" 20 | "# * 10: Casts shadows onto invisible surfaces\n" 21 | "\n" 22 | "newmtl 0D\n" 23 | "Ka 1 0.6 0.78\n" 24 | "Kd 1 0.6 0.78\n" 25 | "illum 1\n" 26 | "\n" 27 | "newmtl climbable\n" 28 | "Ka 0.75 0.5 0\n" 29 | "Kd 0.75 0.5 0\n" 30 | "illum 1\n" 31 | "\n" 32 | "newmtl deathfloor\n" 33 | "Ka 0 0 0\n" 34 | "Kd 0 0 0\n" 35 | "illum 1\n" 36 | "\n" 37 | "newmtl fence\n" 38 | "Ka 0.2 0.2 0.2\n" 39 | "Kd 0.2 0.2 0.2\n" 40 | "illum 1\n" 41 | "\n" 42 | "newmtl flat\n" 43 | "Ka 0 0.5 0.5\n" 44 | "Kd 0 0.5 0.5\n" 45 | "illum 1\n" 46 | "\n" 47 | "newmtl grass\n" 48 | "Ka 0 0.9 0\n" 49 | "Kd 0 0.9 0\n" 50 | "illum 1\n" 51 | "\n" 52 | "newmtl hang\n" 53 | "Ka 0.9 0 0\n" 54 | "Kd 0.9 0 0\n" 55 | "illum 1\n" 56 | "\n" 57 | "newmtl icy\n" 58 | "Ka 0.2 0.2 0.9\n" 59 | "Kd 0.2 0.2 0.9\n" 60 | "illum 1\n" 61 | "\n" 62 | "newmtl lethal_lava\n" 63 | "Ka 1 0 0\n" 64 | "Kd 1 0 0\n" 65 | "illum 1\n" 66 | "\n" 67 | "newmtl normal\n" 68 | "Ka 0.98 0.98 0\n" 69 | "Kd 0.98 0.98 0\n" 70 | "illum 1\n" 71 | "\n" 72 | "newmtl pool_warp\n" 73 | "Ka 0.1 0.1 0.1\n" 74 | "Kd 0.1 0.1 0.1\n" 75 | "illum 1\n" 76 | "\n" 77 | "newmtl slippery\n" 78 | "Ka 0.1 0.1 1.0\n" 79 | "Kd 0.1 0.1 1.0\n" 80 | "illum 1\n" 81 | "\n" 82 | "newmtl snowy\n" 83 | "Ka 0.9 0.9 0.9\n" 84 | "Kd 0.9 0.9 0.9\n" 85 | "illum 1\n" 86 | "\n" 87 | "newmtl snowy2\n" 88 | "Ka 1 1 1\n" 89 | "Kd 1 1 1\n" 90 | "illum 1\n" 91 | "\n" 92 | "newmtl unclimbable\n" 93 | "Ka 1.0 1.0 1.0\n" 94 | "Kd 1.0 1.0 1.0\n" 95 | "illum 1\n" 96 | "\n" 97 | "newmtl very_slippery\n" 98 | "Ka 0 0 1.0\n" 99 | "Kd 0 0 1.0\n" 100 | "illum 1\n" 101 | "\n" 102 | "newmtl wall\n" 103 | "Ka 0.5 0.5 0.5\n" 104 | "Kd 0.5 0.5 0.5\n" 105 | "illum 1\n" 106 | "\n" 107 | "newmtl water_currents\n" 108 | "Ka 0.5 0.5 1.0\n" 109 | "Kd 0.5 0.5 1.0\n" 110 | "illum 1\n" 111 | "\n" 112 | ; 113 | -------------------------------------------------------------------------------- /n64split.makefile.h: -------------------------------------------------------------------------------- 1 | static const char makefile_data[] = 2 | "# Makefile to rebuild SM64 split image\n" 3 | "\n" 4 | "################ Target Executable and Sources ###############\n" 5 | "\n" 6 | "# BUILD_DIR is location where all build artifacts are placed\n" 7 | "BUILD_DIR = build\n" 8 | "\n" 9 | "##################### Compiler Options #######################\n" 10 | "CROSS = mips64-elf-\n" 11 | "AS = $(CROSS)as\n" 12 | "CC = $(CROSS)gcc\n" 13 | "LD = $(CROSS)ld\n" 14 | "OBJDUMP = $(CROSS)objdump\n" 15 | "OBJCOPY = $(CROSS)objcopy\n" 16 | "\n" 17 | "ASFLAGS = -mtune=vr4300 -march=vr4300\n" 18 | "CFLAGS = -Wall -O2 -mtune=vr4300 -march=vr4300 -G 0 -c\n" 19 | "LDFLAGS = -T $(LD_SCRIPT) -Map $(BUILD_DIR)/sm64.map\n" 20 | "\n" 21 | "####################### Other Tools #########################\n" 22 | "\n" 23 | "# N64 tools\n" 24 | "TOOLS_DIR = ../tools\n" 25 | "MIO0TOOL = $(TOOLS_DIR)/mio0\n" 26 | "N64CKSUM = $(TOOLS_DIR)/n64cksum\n" 27 | "N64GRAPHICS = $(TOOLS_DIR)/n64graphics\n" 28 | "EMULATOR = mupen64plus\n" 29 | "EMU_FLAGS = --noosd\n" 30 | "LOADER = loader64\n" 31 | "LOADER_FLAGS = -vwf\n" 32 | "\n" 33 | "FixPath = $(subst /,\\,$1)\n" 34 | "\n" 35 | "######################## Targets #############################\n" 36 | "\n" 37 | "default: all\n" 38 | "\n" 39 | "# file dependencies generated by splitter\n" 40 | "MAKEFILE_SPLIT = Makefile.split\n" 41 | "include $(MAKEFILE_SPLIT)\n" 42 | "\n" 43 | "all: $(TARGET).z64\n" 44 | "\n" 45 | "clean:\n" 46 | "\tdel /Q $(call FixPath,$(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).o $(BUILD_DIR)/$(TARGET).bin $(BUILD_DIR)/$(TARGET).map $(TARGET).z64)\n" 47 | "\n" 48 | "$(MIO0_DIR)/%%.mio0: $(MIO0_DIR)/%%.bin\n" 49 | "\t$(MIO0TOOL) $< $@\n" 50 | "\n" 51 | "$(BUILD_DIR):\n" 52 | "\tmkdir $(BUILD_DIR)\n" 53 | "\n" 54 | "$(BUILD_DIR)/$(TARGET).o: $(TARGET).s Makefile $(MAKEFILE_SPLIT) $(MIO0_FILES) $(LEVEL_FILES) $(MUSIC_FILES) | $(BUILD_DIR)\n" 55 | "\t$(AS) $(ASFLAGS) -o $@ $<\n" 56 | "\n" 57 | "$(BUILD_DIR)/%%.o: %%.c Makefile.as | $(BUILD_DIR)\n" 58 | "\t$(CC) $(CFLAGS) -o $@ $<\n" 59 | "\n" 60 | "$(BUILD_DIR)/$(TARGET).elf: $(BUILD_DIR)/$(TARGET).o $(LD_SCRIPT)\n" 61 | "\t$(LD) $(LDFLAGS) -o $@ $< $(LIBS)\n" 62 | "\n" 63 | "$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf\n" 64 | "\t$(OBJCOPY) $< $@ -O binary\n" 65 | "\n" 66 | "# final z64 updates checksum\n" 67 | "$(TARGET).z64: $(BUILD_DIR)/$(TARGET).bin\n" 68 | "\t$(N64CKSUM) $< $@\n" 69 | "\n" 70 | "$(BUILD_DIR)/$(TARGET).hex: $(TARGET).z64\n" 71 | "\txxd $< > $@\n" 72 | "\n" 73 | "$(BUILD_DIR)/$(TARGET).objdump: $(BUILD_DIR)/$(TARGET).elf\n" 74 | "\t$(OBJDUMP) -D $< > $@\n" 75 | "\n" 76 | "test: $(TARGET).z64\n" 77 | "\t$(EMULATOR) $(EMU_FLAGS) $<\n" 78 | "\n" 79 | "load: $(TARGET).z64\n" 80 | "\t$(LOADER) $(LOADER_FLAGS) $<\n" 81 | "\n" 82 | ".PHONY: all clean default diff test\n" 83 | ; 84 | -------------------------------------------------------------------------------- /release/licenses/capstone.LICENSE: -------------------------------------------------------------------------------- 1 | This is the software license for Capstone disassembly framework. 2 | Capstone has been designed & implemented by Nguyen Anh Quynh 3 | 4 | See http://www.capstone-engine.org for further information. 5 | 6 | Copyright (c) 2013, COSEINC. 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | * Neither the name of the developer(s) nor the names of its 18 | contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /release/licenses/libyaml.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006 Kirill Simonov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /release/licenses/n64split.LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Q 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /release/n64split.README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/release/n64split.README.txt -------------------------------------------------------------------------------- /release/sm64extend.README.txt: -------------------------------------------------------------------------------- 1 | ## sm64extend: Super Mario 64 ROM Extender 2 | - accepts Z64 (BE), V64 (byte-swapped), or N64 (little-endian) ROMs as input 3 | - works with US, European, Japanese, Shindou, and iQue ROMs 4 | - decompresses all MIO0 blocks from ROM to extended area 5 | - configurable extended ROM size (default 64 MB) 6 | - configurable padding between MIO0 blocks (default 32 KB) 7 | - configurable MIO0 block alignment (default 1 byte) 8 | - changes all 0x18 level commands to 0x17 9 | - creates MIO0 headers for all 0x1A level commands 10 | - optionally dump compressed and uncompressed MIO0 data to files 11 | - optionally fills old MIO0 blocks with 0x01 12 | - updates assembly reference to MIO0 blocks 13 | - recalculates ROM header checksums 14 | 15 | ### Usage 16 | ``` 17 | sm64extend [-a ALIGNMENT] [-p PADDING] [-s SIZE] [-d] [-f] [-v] FILE [OUT_FILE] 18 | ``` 19 | Options: 20 | -a ALIGNMENT Byte boundary to align MIO0 blocks (default = 1). 21 | -p PADDING Padding to insert between MIO0 blocks in KB (default = 32). 22 | -s SIZE Size of the extended ROM in MB (default: 64). 23 | -d dump MIO0 blocks to files in 'mio0files' directory 24 | -f Fill old MIO0 blocks with 0x01. 25 | -v Verbose output. 26 | 27 | File arguments: 28 | FILE input ROM file 29 | OUT_FILE output ROM file (default: replaces FILE extension with .ext.z64) 30 | 31 | ### Examples 32 | 64 MB extended ROM that is bit compatible with with generated from the M64ROMExtender1.3b, after extending to 64 MB 33 | ``` 34 | sm64extend sm64.z64 35 | ``` 36 | 37 | 24 MB extended ROM that is bit compatible with the ROM generated from the M64ROMExtender1.3b 38 | ``` 39 | sm64extend -s 24 sm64.z64 40 | ``` 41 | 42 | Enable verbose messages and specify output filename: 43 | ``` 44 | sm64extend -v sm64.z64 sm64_output.ext.z64 45 | ``` 46 | 47 | Pad 64 KB between blocks, align blocks to 16-byte boundaries, fill old MIO0 blocks with 0x01: 48 | ``` 49 | sm64extend -p 64 -a 16 -f sm64.z64 sm64_output.ext.z64 50 | ``` 51 | ### Changelog: 52 | v0.3.2: add support for SM64 iQue ROM 53 | v0.3.1: add little-endian ROM support and error checking 54 | - add support for .n64 little-endian ROMs 55 | - add further validation on SM64 ROM version 56 | - restrict range of output size to between 16 and 64MB 57 | v0.3: add support for (E) and Shindou ROMs 58 | - increase detection of level script and asm references 59 | - detect optimized ASM references 60 | - statically link win32 build to drop MSVCR120.dll dependency 61 | v0.2.1: add dump option and ROM validity checks 62 | - add dump (-d) command line option 63 | - add more ROM validity checks on input file 64 | v0.2: more options, error checking, and allow extending SM64 (J) 65 | - add fill (-f) command line option 66 | - add more error checking up front and while extending 67 | - dynamically find ASM references to MIO0 blocks to support SM64 (J) ROM 68 | v0.1: Initial release 69 | - supports extending SM64 (U) ROM 70 | -------------------------------------------------------------------------------- /release/split.bat: -------------------------------------------------------------------------------- 1 | :: change to batch file's directory so tools and config file can be referenced 2 | pushd %~dp0 3 | :: split input ROM using verbose mode and let it autodetect the config file 4 | tools\n64split.exe -v %1 5 | popd 6 | :: wait for user 7 | pause 8 | -------------------------------------------------------------------------------- /sm64extend.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "libsm64.h" 6 | #include "utils.h" 7 | 8 | #define SM64EXTEND_VERSION "0.3.2" 9 | 10 | // default configuration 11 | static const sm64_config default_config = 12 | { 13 | NULL, // input filename 14 | NULL, // extended filename 15 | 64, // extended size 16 | 32, // MIO0 padding 17 | 1, // MIO0 alignment 18 | 0, // fill old MIO0 blocks 19 | 0, // dump MIO0 blocks to files 20 | }; 21 | 22 | static void print_usage(void) 23 | { 24 | ERROR("Usage: sm64extend [-a ALIGNMENT] [-p PADDING] [-s SIZE] [-d] [-f] [-v] FILE [OUT_FILE]\n" 25 | "\n" 26 | "sm64extend v" SM64EXTEND_VERSION ": Super Mario 64 ROM extender\n" 27 | "Supports (E), (J), (U), Shindou, and iQue ROMs in .n64, .v64, or .z64 formats\n" 28 | "\n" 29 | "Optional arguments:\n" 30 | " -a ALIGNMENT byte boundary to align MIO0 blocks (default: %d)\n" 31 | " -p PADDING padding to insert between MIO0 blocks in KB (default: %d)\n" 32 | " -s SIZE size of the extended ROM in MB (default: %d)\n" 33 | " -d dump MIO0 blocks to files in 'mio0files' directory\n" 34 | " -f fill old MIO0 blocks with 0x01\n" 35 | " -v verbose progress output\n" 36 | "\n" 37 | "File arguments:\n" 38 | " FILE input ROM file\n" 39 | " OUT_FILE output ROM file (default: replaces FILE extension with .ext.z64)\n", 40 | default_config.alignment, default_config.padding, default_config.ext_size); 41 | exit(EXIT_FAILURE); 42 | } 43 | 44 | // parse command line arguments 45 | static void parse_arguments(int argc, char *argv[], sm64_config *config) 46 | { 47 | int i; 48 | int file_count = 0; 49 | if (argc < 2) { 50 | print_usage(); 51 | } 52 | for (i = 1; i < argc; i++) { 53 | if (argv[i][0] == '-') { 54 | switch (argv[i][1]) { 55 | case 'a': 56 | if (++i >= argc) { 57 | print_usage(); 58 | } 59 | config->alignment = strtoul(argv[i], NULL, 0); 60 | break; 61 | case 'd': 62 | config->dump = 1; 63 | break; 64 | case 'f': 65 | config->fill = 1; 66 | break; 67 | case 'p': 68 | if (++i >= argc) { 69 | print_usage(); 70 | } 71 | config->padding = strtoul(argv[i], NULL, 0); 72 | break; 73 | case 's': 74 | if (++i >= argc) { 75 | print_usage(); 76 | } 77 | config->ext_size = strtoul(argv[i], NULL, 0); 78 | break; 79 | case 'v': 80 | g_verbosity = 1; 81 | break; 82 | default: 83 | print_usage(); 84 | break; 85 | } 86 | } else { 87 | switch (file_count) { 88 | case 0: 89 | config->in_filename = argv[i]; 90 | break; 91 | case 1: 92 | config->ext_filename = argv[i]; 93 | break; 94 | default: // too many 95 | print_usage(); 96 | break; 97 | } 98 | file_count++; 99 | } 100 | } 101 | if (file_count < 1) { 102 | print_usage(); 103 | } 104 | } 105 | 106 | int main(int argc, char *argv[]) 107 | { 108 | char ext_filename[FILENAME_MAX]; 109 | sm64_config config; 110 | unsigned char *in_buf = NULL; 111 | unsigned char *out_buf = NULL; 112 | long in_size; 113 | long bytes_written; 114 | rom_type rtype; 115 | rom_version rversion; 116 | 117 | // get configuration from arguments 118 | config = default_config; 119 | parse_arguments(argc, argv, &config); 120 | if (config.ext_filename == NULL) { 121 | config.ext_filename = ext_filename; 122 | generate_filename(config.in_filename, config.ext_filename, "ext.z64"); 123 | } 124 | 125 | // validate arguments 126 | if (config.ext_size < 16 || config.ext_size > 64) { 127 | ERROR("Error: Extended size must be between 16 and 64 MB\n"); 128 | exit(EXIT_FAILURE); 129 | } 130 | if (!is_power2(config.alignment)) { 131 | ERROR("Error: Alignment must be power of 2\n"); 132 | exit(EXIT_FAILURE); 133 | } 134 | 135 | // convert sizes to bytes 136 | config.ext_size *= MB; 137 | config.padding *= KB; 138 | 139 | // generate MIO0 directory 140 | if (config.dump) { 141 | make_dir(MIO0_DIR); 142 | } 143 | 144 | // read input file into memory 145 | in_size = read_file(config.in_filename, &in_buf); 146 | if (in_size <= 0) { 147 | ERROR("Error reading input file \"%s\"\n", config.in_filename); 148 | exit(EXIT_FAILURE); 149 | } 150 | 151 | // confirm valid SM64 152 | rtype = sm64_rom_type(in_buf, in_size); 153 | switch (rtype) { 154 | case ROM_INVALID: 155 | ERROR("This does not appear to be a valid SM64 ROM\n"); 156 | exit(EXIT_FAILURE); 157 | break; 158 | case ROM_SM64_BS: 159 | INFO("Converting ROM from byte-swapped to big-endian\n"); 160 | swap_bytes(in_buf, in_size); 161 | break; 162 | case ROM_SM64_BE: 163 | break; 164 | case ROM_SM64_LE: 165 | INFO("Converting ROM from little to big-endian\n"); 166 | reverse_endian(in_buf, in_size); 167 | break; 168 | case ROM_SM64_BE_EXT: 169 | ERROR("This ROM is already extended!\n"); 170 | exit(EXIT_FAILURE); 171 | break; 172 | } 173 | 174 | rversion = sm64_rom_version(in_buf); 175 | if (rversion == VERSION_UNKNOWN) { 176 | ERROR("Unknown SM64 ROM version\n"); 177 | exit(EXIT_FAILURE); 178 | } 179 | 180 | // allocate output memory 181 | out_buf = malloc(config.ext_size); 182 | 183 | // copy file from input to output 184 | memcpy(out_buf, in_buf, in_size); 185 | 186 | // fill new space with 0x01 187 | memset(&out_buf[in_size], 0x01, config.ext_size - in_size); 188 | 189 | // decode SM64 MIO0 files and adjust pointers 190 | sm64_decompress_mio0(&config, in_buf, in_size, out_buf); 191 | 192 | // update N64 header CRC 193 | sm64_update_checksums(out_buf); 194 | 195 | // write to output file 196 | bytes_written = write_file(config.ext_filename, out_buf, config.ext_size); 197 | if (bytes_written < (long)config.ext_size) { 198 | ERROR("Error writing bytes to output file \"%s\"\n", config.ext_filename); 199 | exit(EXIT_FAILURE); 200 | } 201 | 202 | return EXIT_SUCCESS; 203 | } 204 | -------------------------------------------------------------------------------- /sm64geo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "utils.h" 6 | 7 | #define SM64GEO_VERSION "0.1" 8 | 9 | typedef struct 10 | { 11 | char *in_filename; 12 | char *out_filename; 13 | unsigned int offset; 14 | unsigned int length; 15 | } arg_config; 16 | 17 | static arg_config default_config = 18 | { 19 | NULL, 20 | NULL, 21 | 0, 22 | 0 23 | }; 24 | 25 | static void print_spaces(FILE *fp, int count) 26 | { 27 | int i; 28 | for (i = 0; i < count; i++) { 29 | fputc(' ', fp); 30 | } 31 | } 32 | 33 | void print_geo(FILE *out, unsigned char *data, unsigned int offset, unsigned int length) 34 | { 35 | unsigned int a = offset; 36 | int indent = 0; 37 | int i; 38 | while (a < offset + length) { 39 | switch (data[a]) { 40 | case 0x00: // end 41 | case 0x01: 42 | case 0x03: 43 | case 0x04: 44 | case 0x05: 45 | case 0x09: 46 | case 0x0B: 47 | case 0x0C: 48 | case 0x17: 49 | case 0x20: 50 | i = 4; 51 | break; 52 | case 0x02: 53 | case 0x0D: 54 | case 0x0E: 55 | case 0x12: 56 | case 0x14: 57 | case 0x15: 58 | case 0x16: 59 | case 0x18: 60 | case 0x19: 61 | i = 8; 62 | break; 63 | case 0x08: 64 | case 0x13: 65 | case 0x1C: 66 | i = 12; 67 | break; 68 | case 0x10: 69 | i = 16; 70 | break; 71 | case 0x0F: 72 | i = 20; 73 | break; 74 | case 0x0A: 75 | i = 8; 76 | if (data[a+1]) { 77 | i += 4; 78 | } 79 | break; 80 | case 0x11: 81 | case 0x1D: 82 | i = 8; 83 | if (data[a+1] & 0x80) { 84 | i += 4; 85 | } 86 | break; 87 | default: 88 | i = 4; 89 | ERROR("WHY? %06X %2X\n", a, data[a]); 90 | } 91 | if (data[a] == 0x05 && indent > 1) { 92 | indent -= 2; 93 | } 94 | if (data[a] == 0x01) { 95 | indent = 0; 96 | } 97 | fprintf(out, "%4X: ", a); 98 | print_spaces(out, indent); 99 | fprintf(out, "[ "); 100 | fprint_hex(out, &data[a], i); 101 | fprintf(out, "]\n"); 102 | if (data[a] == 0x04) { 103 | indent += 2; 104 | } 105 | a += i; 106 | } 107 | } 108 | 109 | static void print_usage(void) 110 | { 111 | ERROR("Usage: sm64geo [-l LENGTH] [-o OFFSET] FILE\n" 112 | "\n" 113 | "sm64geo v" SM64GEO_VERSION ": Super Mario 64 geometry layout decoder\n" 114 | "\n" 115 | "Optional arguments:\n" 116 | " -l LENGTH length of data to decode in bytes (default: length of file)\n" 117 | " -o OFFSET starting offset in FILE (default: 0)\n" 118 | "\n" 119 | "File arguments:\n" 120 | " FILE input file\n" 121 | " [OUTPUT] output file (default: stdout)\n"); 122 | exit(1); 123 | } 124 | 125 | // parse command line arguments 126 | static void parse_arguments(int argc, char *argv[], arg_config *config) 127 | { 128 | int i; 129 | int file_count = 0; 130 | if (argc < 2) { 131 | print_usage(); 132 | exit(1); 133 | } 134 | for (i = 1; i < argc; i++) { 135 | if (argv[i][0] == '-') { 136 | switch (argv[i][1]) { 137 | case 'l': 138 | if (++i >= argc) { 139 | print_usage(); 140 | } 141 | config->length = strtoul(argv[i], NULL, 0); 142 | break; 143 | case 'o': 144 | if (++i >= argc) { 145 | print_usage(); 146 | } 147 | config->offset = strtoul(argv[i], NULL, 0); 148 | break; 149 | default: 150 | print_usage(); 151 | break; 152 | } 153 | } else { 154 | switch (file_count) { 155 | case 0: 156 | config->in_filename = argv[i]; 157 | break; 158 | case 1: 159 | config->out_filename = argv[i]; 160 | break; 161 | default: // too many 162 | print_usage(); 163 | break; 164 | } 165 | file_count++; 166 | } 167 | } 168 | if (file_count < 1) { 169 | print_usage(); 170 | } 171 | } 172 | 173 | int main(int argc, char *argv[]) 174 | { 175 | arg_config config; 176 | FILE *fout; 177 | unsigned char *data; 178 | long size; 179 | 180 | // get configuration from arguments 181 | config = default_config; 182 | parse_arguments(argc, argv, &config); 183 | if (config.out_filename == NULL) { 184 | fout = stdout; 185 | } else { 186 | fout = fopen(config.out_filename, "w"); 187 | if (fout == NULL) { 188 | perror("Error opening output file"); 189 | return EXIT_FAILURE; 190 | } 191 | } 192 | 193 | // operation 194 | size = read_file(config.in_filename, &data); 195 | if (size < 0) { 196 | perror("Error opening input file"); 197 | return EXIT_FAILURE; 198 | } 199 | if (config.length == 0) { 200 | config.length = size - config.offset; 201 | } 202 | if (config.offset >= (unsigned int)size) { 203 | ERROR("Error: offset greater than file size (%X > %X)\n", 204 | config.offset, (unsigned int)size); 205 | return EXIT_FAILURE; 206 | } 207 | if (config.offset + config.length > (unsigned int)size) { 208 | ERROR("Warning: length goes beyond file size (%X > %X), truncating\n", 209 | config.offset + config.length, (unsigned int)size); 210 | config.length = size - config.offset; 211 | } 212 | print_geo(fout, data, config.offset, config.length); 213 | free(data); 214 | 215 | if (fout != stdout) { 216 | fclose(fout); 217 | } 218 | 219 | return EXIT_SUCCESS; 220 | } 221 | -------------------------------------------------------------------------------- /sm64walk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "libsm64.h" 6 | #include "utils.h" 7 | 8 | #define SM64WALK_VERSION "0.1" 9 | 10 | static void print_usage(void) 11 | { 12 | ERROR("Usage: sm64walk [-o OFFSET] [-r REGION] [-v] FILE\n" 13 | "\n" 14 | "sm64walk v" SM64WALK_VERSION ": Super Mario 64 script walker\n" 15 | "\n" 16 | "Optional arguments:\n" 17 | " -o OFFSET start decoding level scripts at OFFSET (default: auto-detect)\n" 18 | " -r REGION region to use. valid: Europe, US, JP, Shindou\n" 19 | " -v verbose progress output\n" 20 | "\n" 21 | "File arguments:\n" 22 | " FILE input ROM file\n"); 23 | exit(EXIT_FAILURE); 24 | } 25 | 26 | // parse command line arguments 27 | static void parse_arguments(int argc, char *argv[], unsigned *offset, char *region, char *in_filename) 28 | { 29 | int i; 30 | int file_count = 0; 31 | if (argc < 2) { 32 | print_usage(); 33 | } 34 | for (i = 1; i < argc; i++) { 35 | if (argv[i][0] == '-') { 36 | switch (argv[i][1]) { 37 | case 'o': 38 | if (++i >= argc) { 39 | print_usage(); 40 | } 41 | *offset = strtoul(argv[i], NULL, 0); 42 | break; 43 | case 'r': 44 | if (++i >= argc) { 45 | print_usage(); 46 | } 47 | *region = argv[i][0]; 48 | break; 49 | case 'v': 50 | g_verbosity = 1; 51 | break; 52 | default: 53 | print_usage(); 54 | break; 55 | } 56 | } else { 57 | switch (file_count) { 58 | case 0: 59 | strcpy(in_filename, argv[i]); 60 | break; 61 | default: // too many 62 | print_usage(); 63 | break; 64 | } 65 | file_count++; 66 | } 67 | } 68 | if (file_count < 1) { 69 | print_usage(); 70 | } 71 | } 72 | 73 | typedef struct 74 | { 75 | unsigned start; 76 | unsigned end; 77 | } level_t; 78 | 79 | static void add_level(level_t levels[], unsigned *lcount, unsigned start, unsigned end) 80 | { 81 | unsigned i; 82 | INFO("Adding level %06X - %06X\n", start, end); 83 | for (i = 0; i < *lcount; i++) { 84 | if (levels[i].start == start) { 85 | return; 86 | } 87 | } 88 | levels[*lcount].start = start; 89 | levels[*lcount].end = end; 90 | (*lcount)++; 91 | } 92 | 93 | static void decode_level(unsigned char *data, level_t levels[], unsigned int l, unsigned *lcount) 94 | { 95 | unsigned int ptr_start; 96 | unsigned int ptr_end; 97 | unsigned int dst; 98 | unsigned int a; 99 | int i; 100 | 101 | printf("Decoding level script %X\n", levels[l].start); 102 | 103 | a = levels[l].start; 104 | // length = 0 ends level script 105 | while (a < levels[l].end && data[a+1] != 0) { 106 | printf("%06X [%03X] ", a, a - levels[l].start); 107 | switch (data[a]) { 108 | case 0x00: printf("LoadJump0"); break; // load and jump from ROM into a RAM segment 109 | case 0x01: printf("LoadJump1"); break; // load and jump from ROM into a RAM segment 110 | case 0x02: printf("EndLevel "); break; // end of level layout data 111 | case 0x03: printf("Delay03 "); break; // delay frames 112 | case 0x04: printf("Delay04 "); break; // delay frames and signal end 113 | case 0x05: printf("JumpSeg "); break; // jump to level script at segmented address 114 | case 0x06: printf("PushJump "); break; // push script stack and jump to segmented address 115 | case 0x07: printf("PopScript"); break; // pop script stack, return to prev 0x06 or 0x0C 116 | case 0x08: printf("Push16 "); break; // push script stack and 16-bit value 117 | case 0x09: printf("Pop16 "); break; // pop script stack and 16-bit value 118 | case 0x0A: printf("PushNull "); break; // push script stack and 32-bit 0x00000000 119 | case 0x0B: printf("CondPop "); break; // conditional stack pop 120 | case 0x0C: printf("CondJump "); break; // conditional jump to segmented address 121 | case 0x0D: printf("CondPush "); break; // conditional stack push 122 | case 0x0E: printf("CondSkip "); break; // conditional skip over following 0x0F and 0x10 commands 123 | case 0x0F: printf("SkipNext "); break; // skip over following 0x10 commands 124 | case 0x10: printf("NoOp "); break; // no operation 125 | case 0x11: printf("AccumAsm1"); break; // set accumulator from ASM function 126 | case 0x12: printf("AccumAsm2"); break; // actively set accumulator from ASM function 127 | case 0x13: printf("SetAccum "); break; // set accumulator to constant value 128 | case 0x14: printf("PushPool "); break; // push pool state 129 | case 0x15: printf("PopPool "); break; // pop pool state 130 | case 0x16: printf("LoadASM "); break; // load ASM into RAM 131 | case 0x17: printf("ROM->Seg "); break; // copy uncompressed data from ROM to a RAM segment 132 | case 0x18: printf("MIO0->Seg"); break; // decompress MIO0 data from ROM and copy it into a RAM segment 133 | case 0x19: printf("MarioFace"); break; // create Mario face for demo screen 134 | case 0x1A: printf("MIO0Textr"); break; // decompress MIO0 data from ROM and copy it into a RAM segment (for texture only segments?) 135 | case 0x1B: printf("StartLoad"); break; // start RAM loading sequence (before 17, 18, 1A) 136 | case 0x1D: printf("EndLoad "); break; // end RAM loading sequence (after 17, 18, 1A) 137 | case 0x1F: printf("StartArea"); break; // start of an area 138 | case 0x20: printf("EndArea "); break; // end of an area 139 | case 0x21: printf("LoadPoly "); break; // load polygon data without geo layout 140 | case 0x22: printf("LdPolyGeo"); break; // load polygon data with geo layout 141 | case 0x24: printf("PlaceObj "); break; // place object in level with behavior 142 | case 0x25: printf("LoadMario"); break; // load mario object with behavior 143 | case 0x26: printf("ConctWarp"); break; // connect warps 144 | case 0x27: printf("PaintWarp"); break; // level warps for paintings 145 | case 0x28: printf("Transport"); break; // transport Mario to an area 146 | case 0x2B: printf("MarioStrt"); break; // Mario's default position 147 | case 0x2E: printf("Collision"); break; // load collision data 148 | case 0x2F: printf("RendrArea"); break; // decide which area of level geo to render 149 | case 0x31: printf("Terrain "); break; // set default terrain type 150 | case 0x33: printf("FadeColor"); break; // fade/overlay screen with color 151 | case 0x34: printf("Blackout "); break; // blackout screen 152 | case 0x36: printf("Music36 "); break; // set music 153 | case 0x37: printf("Music37 "); break; // set music 154 | case 0x39: printf("MulObject"); break; // multiple objects from main level segment 155 | case 0x3B: printf("JetStream"); break; // define jet streams that repulse / pull Mario 156 | case 0x3C: printf("GetPut "); break; // get/put remote value 157 | default: printf(" "); break; 158 | } 159 | printf(" %02X %02X %02X%02X ", data[a], data[a+1], data[a+2], data[a+3]); 160 | switch (data[a]) { 161 | case 0x00: // load and jump from ROM into a RAM segment 162 | case 0x01: // load and jump from ROM into a RAM segment 163 | ptr_start = read_u32_be(&data[a+4]); 164 | ptr_end = read_u32_be(&data[a+8]); 165 | printf("%08X %08X %08X\n", ptr_start, ptr_end, read_u32_be(&data[a+0xc])); 166 | add_level(levels, lcount, ptr_start, ptr_end); 167 | break; 168 | case 0x17: // copy uncompressed data from ROM to a RAM segment 169 | case 0x18: // decompress MIO0 data from ROM and copy it into a RAM segment 170 | case 0x1A: // decompress MIO0 data from ROM and copy it into a RAM segment (for texture only segments?) 171 | ptr_start = read_u32_be(&data[a+4]); 172 | ptr_end = read_u32_be(&data[a+8]); 173 | printf("%08X %08X\n", ptr_start, ptr_end); 174 | break; 175 | case 0x11: // call function 176 | case 0x12: // call function 177 | ptr_start = read_u32_be(&data[a+0x4]); 178 | printf("%08X\n", ptr_start); 179 | break; 180 | case 0x16: // load ASM into RAM 181 | dst = read_u32_be(&data[a+0x4]); 182 | ptr_start = read_u32_be(&data[a+0x8]); 183 | ptr_end = read_u32_be(&data[a+0xc]); 184 | printf("%08X %08X %08X\n", dst, ptr_start, ptr_end); 185 | break; 186 | case 0x25: // load mario object with behavior 187 | case 0x24: // load object with behavior 188 | printf("%08X", read_u32_be(&data[a])); 189 | for (i = 4; i < data[a+1]-4; i+=4) { 190 | printf(" %08X", read_u32_be(&data[a+i])); 191 | } 192 | dst = read_u32_be(&data[a+i]); 193 | printf(" %08X\n", dst); 194 | break; 195 | default: 196 | for (i = 4; i < data[a+1]; i+=4) { 197 | printf("%08X ", read_u32_be(&data[a+i])); 198 | } 199 | printf("\n"); 200 | break; 201 | } 202 | a += data[a+1]; 203 | } 204 | printf("Done %X\n\n", levels[l].start); 205 | } 206 | 207 | char detectRegion(unsigned char *data) 208 | { 209 | unsigned checksum = read_u32_be(&data[0x10]); 210 | // add main entry level script 211 | switch (checksum) { 212 | case 0xA03CF036: return 'E'; 213 | case 0x4EAA3D0E: return 'J'; 214 | case 0xD6FBA4A8: return 'S'; // Shindou Edition (J) 215 | case 0x635A2BFF: return 'U'; 216 | default: 217 | ERROR("Unknown ROM checksum: 0x%08X\n", checksum); 218 | exit(1); 219 | } 220 | } 221 | 222 | unsigned getRegionOffset(char region) 223 | { 224 | switch (region) { 225 | case 'E': return 0xDE160; 226 | case 'J': return 0x1076A0; 227 | case 'S': return 0xE42C0; 228 | case 'U': return 0x108A10; 229 | default: 230 | ERROR("Unknown region: '%c'\n", region); 231 | exit(1); 232 | } 233 | return 0; 234 | } 235 | 236 | static void walk_scripts(unsigned char *data, unsigned offset) 237 | { 238 | level_t levelscripts[100]; 239 | unsigned lcount = 0; 240 | unsigned l = 0; 241 | levelscripts[0].start = offset; 242 | levelscripts[0].end = offset + 0x30; 243 | lcount++; 244 | while (l < lcount) { 245 | decode_level(data, levelscripts, l, &lcount); 246 | l++; 247 | } 248 | } 249 | 250 | int main(int argc, char *argv[]) 251 | { 252 | char in_filename[FILENAME_MAX]; 253 | unsigned char *in_buf = NULL; 254 | unsigned offset = 0xFFFFFFFF; 255 | long in_size; 256 | int rom_type; 257 | char region = 0; 258 | 259 | // get configuration from arguments 260 | parse_arguments(argc, argv, &offset, ®ion, in_filename); 261 | 262 | // read input file into memory 263 | in_size = read_file(in_filename, &in_buf); 264 | if (in_size <= 0) { 265 | ERROR("Error reading input file \"%s\"\n", in_filename); 266 | exit(EXIT_FAILURE); 267 | } 268 | 269 | // confirm valid SM64 270 | rom_type = sm64_rom_type(in_buf, in_size); 271 | if (rom_type < 0) { 272 | ERROR("This does not appear to be a valid SM64 ROM\n"); 273 | exit(EXIT_FAILURE); 274 | } else if (rom_type == 1) { 275 | // byte-swapped BADC format, swap to big-endian ABCD format for processing 276 | INFO("Byte-swapping ROM\n"); 277 | swap_bytes(in_buf, in_size); 278 | } 279 | 280 | if (offset == 0xFFFFFFFF) { 281 | if (region == 0) { 282 | region = detectRegion(in_buf); 283 | } 284 | offset = getRegionOffset(region); 285 | } 286 | 287 | // walk those scripts 288 | walk_scripts(in_buf, offset); 289 | 290 | // cleanup 291 | free(in_buf); 292 | 293 | return EXIT_SUCCESS; 294 | } 295 | -------------------------------------------------------------------------------- /strutils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "strutils.h" 7 | 8 | // buffer for local sprintf 9 | static char tmpbuf[4096]; 10 | 11 | void strbuf_alloc(strbuf *sbuf, size_t allocate) 12 | { 13 | // some sane default allocation 14 | if (allocate <= 0) { 15 | allocate = 512; 16 | } 17 | sbuf->buf = malloc(allocate); 18 | sbuf->allocated = allocate; 19 | sbuf->index = 0; 20 | } 21 | 22 | void strbuf_sprintf(strbuf *sbuf, const char *format, ...) 23 | { 24 | va_list args; 25 | 26 | va_start(args, format); 27 | int len = vsprintf(tmpbuf, format, args); 28 | va_end(args); 29 | 30 | while (sbuf->allocated <= sbuf->index + len) { 31 | sbuf->allocated *= 2; 32 | sbuf->buf = realloc(sbuf->buf, sbuf->allocated); 33 | } 34 | memcpy(&sbuf->buf[sbuf->index], tmpbuf, len + 1); 35 | sbuf->index += len; 36 | } 37 | 38 | void strbuf_free(strbuf *sbuf) 39 | { 40 | if (sbuf->buf) { 41 | free(sbuf->buf); 42 | sbuf->allocated = 0; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /strutils.h: -------------------------------------------------------------------------------- 1 | #ifndef STRUTILS_H 2 | #define STRUTILS_H 3 | 4 | typedef struct 5 | { 6 | char *buf; 7 | size_t allocated; 8 | size_t index; 9 | } strbuf; 10 | 11 | void strbuf_alloc(strbuf *sbuf, size_t allocate); 12 | 13 | // sprintf and strcat to end of buffer 14 | void strbuf_sprintf(strbuf *sbuf, const char *format, ...); 15 | 16 | void strbuf_free(strbuf *sbuf); 17 | 18 | #endif /* STRUTILS_H */ 19 | -------------------------------------------------------------------------------- /tests/example1/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = example1.bin 2 | 3 | BIN_DIR = ./bin 4 | 5 | AS = armips 6 | MIO0 = ../../mio0 7 | N64GRAPHICS = ../../n64graphics 8 | 9 | # targets 10 | 11 | default: $(TARGET) 12 | 13 | example1.bin: example1.asm bin/texture_block.bin.gz bin/texture_block.mio0 bin/texture_ia16.bin bin/texture_rgba16.bin 14 | $(AS) $< 15 | 16 | $(BIN_DIR): 17 | mkdir -p $(BIN_DIR) 18 | 19 | # graphics conversion 20 | $(BIN_DIR)/texture_rgba16.bin: textures/heart.0.rgba16.png $(BIN_DIR) 21 | $(N64GRAPHICS) -f rgba16 -g $< -i $@ 22 | 23 | $(BIN_DIR)/texture_ia16.bin: textures/heart.0.ia16.png $(BIN_DIR) 24 | $(N64GRAPHICS) -f ia16 -g $< -i $@ 25 | 26 | # concatenate texture files to form texture_block 27 | $(BIN_DIR)/texture_block.bin: bin/texture_rgba16.bin bin/texture_ia16.bin 28 | cat $^ > $@ 29 | 30 | $(BIN_DIR)/texture_block.bin.gz: bin/texture_block.bin 31 | @[ -d $(BIN_DIR) ] || mkdir -p $(BIN_DIR) 32 | gzip -c $< > $@ 33 | 34 | $(BIN_DIR)/texture_block.mio0: bin/texture_block.bin 35 | @[ -d $(BIN_DIR) ] || mkdir -p $(BIN_DIR) 36 | $(MIO0) -c $< $@ 37 | 38 | clean: 39 | rm -rf $(TARGET) $(BIN_DIR) 40 | 41 | .PHONY: clean default 42 | -------------------------------------------------------------------------------- /tests/example1/example1.asm: -------------------------------------------------------------------------------- 1 | .n64 2 | .create "example1.bin", 0x0 3 | 4 | //------------------------------------------------- 5 | // N64 header 6 | //------------------------------------------------- 7 | .word 0x80371240 // PI BSD Domain 1 register 8 | .word 0x0000000F // clock rate setting 9 | .word EntryPoint // entry point 10 | .word 0x00001444 // release 11 | .word 0x635A2BFF // checksum1 12 | .word 0x8B022326 // checksum2 13 | .word 0x00000000 // unknown 14 | .word 0x00000000 // unknown" 15 | .ascii "TEST BINARY ROM " // ROM name: 20 bytes 16 | .word 0x00000000 // unknown 17 | .word 0x0000004E // cartridge 18 | .ascii "TS" // cartridge ID 19 | .ascii "E" // country 20 | .byte 0x00 // version 21 | 22 | //------------------------------------------------- 23 | // begin asm section 24 | //------------------------------------------------- 25 | .definelabel stack_start, 0x80400000 26 | 27 | .headersize 0x80100000 - orga() 28 | 29 | // .text 30 | EntryPoint: 31 | lw sp, stack_start 32 | jal main 33 | nop 34 | j EntryPoint 35 | nop 36 | 37 | main: 38 | addiu sp, sp, -0x18 39 | sw ra, 0x14(sp) 40 | la t1, data0 41 | la t2, data1 42 | beq t1, t2, @@done 43 | nop 44 | @@loop: 45 | lw t3, 0(t1) 46 | addiu t1, t1, 4 47 | bne t1, t2, @@loop 48 | nop 49 | @@done: 50 | lw ra, 0x14(sp) 51 | jr ra 52 | addiu sp, sp, 0x18 53 | 54 | // .data 55 | .align 0x10 56 | data0: 57 | .dw 0x00000000, 0x11111111, 0x22222222, 0x33333333 58 | data1: 59 | .dw 0x44444444, 0x55555555, 0x66666666, 0x77777777 60 | data_ptrs: 61 | .dw data0, data1 62 | texture_ptrs: 63 | .dw texture_rgba, texture_ia 64 | file_ptrs: 65 | .dw block_mio0_start, block_mio0_end, block_gz_start, block_gz_end 66 | 67 | // put textures in its own section 68 | .align 0x10 69 | .headersize 0x04000000 - orga() 70 | 71 | texture_rgba: 72 | .incbin "bin/texture_rgba16.bin" 73 | texture_ia: 74 | .incbin "bin/texture_ia16.bin" 75 | 76 | .dw 0xcafeface 77 | 78 | // binary blocks use absolute 79 | .headersize 0x10 80 | .align 16 81 | 82 | //------------------------------------------------- 83 | // MIO0 block 84 | //------------------------------------------------- 85 | block_mio0_start: 86 | .incbin "bin/texture_block.mio0" 87 | block_mio0_end: 88 | 89 | .dw 0xdeadbeef 90 | 91 | //------------------------------------------------- 92 | // gzip block 93 | //------------------------------------------------- 94 | .align 16 95 | block_gz_start: 96 | .incbin "bin/texture_block.bin.gz" 97 | block_gz_end: 98 | 99 | .close 100 | -------------------------------------------------------------------------------- /tests/example1/textures/heart.0.ia16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/tests/example1/textures/heart.0.ia16.png -------------------------------------------------------------------------------- /tests/example1/textures/heart.0.rgba16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queueRAM/sm64tools/81de9e5a8f0fa96686a16441d5b9f25742f4d17d/tests/example1/textures/heart.0.rgba16.png -------------------------------------------------------------------------------- /tools/Makefile: -------------------------------------------------------------------------------- 1 | ################ Target Executable and Sources ############### 2 | 3 | TARGET := montage 4 | 5 | SRC_FILES := montage.c \ 6 | ../yamlconfig.c \ 7 | ../utils.c 8 | 9 | ##################### Compiler Options ####################### 10 | 11 | WIN64_CROSS = x86_64-w64-mingw32- 12 | WIN32_CROSS = i686-w64-mingw32- 13 | #CROSS = $(WIN32_CROSS) 14 | CC = $(CROSS)gcc 15 | LD = $(CC) 16 | 17 | INCLUDES = 18 | DEFS = 19 | CFLAGS = -Wall -Wextra -O2 -ffunction-sections -fdata-sections $(INCLUDES) $(DEFS) 20 | 21 | LDFLAGS = -s -Wl,--gc-sections 22 | LIBS = -lconfig 23 | 24 | ######################## Targets ############################# 25 | 26 | default: all 27 | 28 | all: $(TARGET) matchsigs sm64collision sm64walk 29 | 30 | $(TARGET): $(SRC_FILES) 31 | $(CC) $(CFLAGS) -o $@ $^ $(LIBS) 32 | 33 | matchsigs: match_signatures.c ../utils.c 34 | $(CC) $(CFLAGS) -o $@ $^ -lcapstone 35 | 36 | sm64collision: sm64collision.c ../utils.c 37 | $(CC) $(CFLAGS) -o $@ $^ 38 | 39 | sm64text: sm64text.c 40 | $(CC) $(CFLAGS) -o $@ $^ 41 | 42 | clean: 43 | rm -f $(TARGET) 44 | 45 | .PHONY: all clean default 46 | 47 | -------------------------------------------------------------------------------- /tools/dma2config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | import csv 5 | import sys 6 | 7 | def eprint(*args, **kwargs): 8 | print(*args, file=sys.stderr, **kwargs) 9 | sys.stderr.flush() 10 | 11 | # find most recent DMA that matches JAL 12 | def FindLatestDma(dmas, jal): 13 | jal_mask = jal & 0x0FFFFFFF 14 | for dma in reversed(dmas): 15 | if jal_mask >= dma.dest and jal_mask < dma.dest + dma.length: 16 | return dma 17 | return None 18 | 19 | # DMA class stores information about DMA from ROM to RDRAM 20 | class DMA(): 21 | def __init__(self, source, dest, length): 22 | self.source = source 23 | self.dest = dest 24 | self.length = length 25 | self.jals = [] 26 | 27 | # ROM class stores detected ROM information 28 | class ROM(): 29 | def __init__(self, rom_id, regions, description, cksum1, cksum2): 30 | self.rom_id = rom_id 31 | self.regions = regions 32 | self.description = description 33 | self.cksum1 = cksum1 34 | self.cksum2 = cksum2 35 | 36 | rom = ROM("rom", "", "noname", 0x0, 0x0) 37 | dmas = [] 38 | jals = [] 39 | 40 | # command line input checking 41 | if len(sys.argv) < 2: 42 | eprint("Usage: dma2config.py ") 43 | sys.exit(1) 44 | 45 | # read in log file as CSV and parse rows 46 | eprint("Parsing '{}'...".format(sys.argv[1])) 47 | with open(sys.argv[1], 'rb') as csvfile: 48 | spamreader = csv.reader(csvfile, delimiter=',') 49 | for row in spamreader: 50 | if row[0] == 'ROM': 51 | rom = ROM(row[1], row[2], row[3], int(row[4], 16), int(row[5], 16)) 52 | eprint("ROM info: {}".format(row)) 53 | elif row[0] == 'DMA': 54 | source = int(row[1], 16) 55 | if not any(x.source == source for x in dmas): 56 | dmas.append(DMA(source, int(row[2], 16), int(row[3], 16))) 57 | elif row[0] == 'JAL' or row[0] == 'JALR': 58 | jal = int(row[1], 16) 59 | jals.append(jal) 60 | dma = FindLatestDma(dmas, jal) 61 | if dma is None: 62 | eprint("Error no DMA for JAL %08X" % jal) 63 | else: 64 | dma.jals.append(jal) 65 | 66 | # sort arrays 67 | eprint("Sorting arrays...") 68 | dmas.sort(key=lambda x: x.dest) 69 | jals.sort() 70 | 71 | # merge consecutive DMA regions 72 | eprint("Merging DMA regions...") 73 | dmas_new = [] 74 | for dma in dmas: 75 | merged = False 76 | for n in dmas_new: 77 | if dma.source == n.source + n.length and dma.dest == n.dest + n.length: 78 | n.length += dma.length 79 | merged = True 80 | elif n.source == dma.source + dma.length and n.dest == dma.dest + dma.length: 81 | n.source = dma.source 82 | n.length += dma.length 83 | merged = True 84 | if not merged: 85 | dmas_new.append(dma) 86 | dmas = dmas_new 87 | 88 | # copy of DMAs, sorted by ROM 89 | dmas_rom = sorted(dmas, key=lambda x: x.source) 90 | 91 | # output n64split config file 92 | eprint("Generating config file...") 93 | print("// n64split configuration file") 94 | print("// generated by dma2config") 95 | print("name = \"%s\";" % rom.description) 96 | print("") 97 | 98 | print("// checksums from ROM header offsets 0x10 and 0x14") 99 | print("// used for auto configuration detection") 100 | print("checksum1 = 0x%08X;" % rom.cksum1) 101 | print("checksum2 = 0x%08X;" % rom.cksum2) 102 | print("") 103 | 104 | print("// base filename used for outputs (please, no spaces)") 105 | print("basename = \"%s\";" % rom.rom_id) 106 | print("") 107 | 108 | print("// memory map from KSEG0 RAM addresses to ROM offsets") 109 | print("// these were decoded from DMA accesses") 110 | print("memory =") 111 | print("(") 112 | print(" // start end ram-to-rom") 113 | last_dma = None 114 | for dma in dmas: 115 | if dma.jals: 116 | start = 0x80000000 + dma.dest 117 | end = start + dma.length 118 | ram2rom = start - dma.source 119 | if last_dma is not None: 120 | print(", // 0x%06X-0x%06X 0x%06X" % (last_dma.source, last_dma.source + last_dma.length, last_dma.length)) 121 | sys.stdout.write(" (0x%08X, 0x%08X, 0x%08X)" % (start, end, ram2rom)) 122 | last_dma = dma 123 | # last DMA gets no comma 124 | if last_dma is not None: 125 | print(" // 0x%06X-0x%06X 0x%06X" % (last_dma.source, last_dma.source + last_dma.length, last_dma.length)) 126 | 127 | print(");") 128 | print("") 129 | print("// ranges to split the ROM into") 130 | print("// types:") 131 | print("// asm - MIPS assembly block. Symbol names are in 'labels' list below") 132 | print("// behavior - behavior script") 133 | print("// bin - raw binary, usually data") 134 | print("// header - ROM header block") 135 | print("// instrset - instrument set") 136 | print("// level - level commands") 137 | print("// m64 - M64 music sequence bank") 138 | print("// mio0 - MIO0 compressed data block. may have texture breakdown") 139 | print("// ptr - RAM address or ROM offset pointer") 140 | print("//") 141 | print("// textures types:") 142 | print("// rgba - 16-bit RGBA (5-5-5-1)") 143 | print("// ia - 16/8/4/1-bit greyscale") 144 | print("// skybox - grid of 32x32 16-bit RGBA") 145 | print("ranges =") 146 | print("(") 147 | print(" // start, end, type, label") 148 | first = True 149 | for dma in dmas_rom: 150 | if not first: 151 | print(",") 152 | else: 153 | first = False 154 | sys.stdout.write(" (0x%06X, 0x%06X, \"%s\")" % (dma.source, dma.source + dma.length, "asm" if dma.jals else "bin" )) 155 | print(); 156 | print(");") 157 | print("") 158 | 159 | print("// Labels for functions or data memory addresses") 160 | print("// All label addresses are RAM addresses") 161 | print("// Order does not matter") 162 | print("labels =") 163 | print("(") 164 | first = True 165 | for jal in jals: 166 | if not first: 167 | print(",") 168 | else: 169 | first = False 170 | sys.stdout.write(" (0x%08X, \"proc_%08X\")" % (jal, jal)) 171 | print(); 172 | print(");") 173 | print("") 174 | -------------------------------------------------------------------------------- /tools/hudtable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../utils.h" 6 | 7 | #define NUM_COLS 4 8 | #define NUM_ROWS (0x80/NUM_COLS) 9 | 10 | char lookupchar(int hex) 11 | { 12 | if (hex >= ' ' && hex <= '~') { 13 | return (char)hex; 14 | } 15 | return ' '; 16 | } 17 | 18 | int convertchar(int ascii) 19 | { 20 | if (ascii >= 0x41 && ascii < 0x5B) { 21 | return ascii - 0x37; 22 | } else if (ascii >= 0x61 && ascii < 0x7B) { 23 | return ascii - 0x57; 24 | } else if (ascii >= 0x30 && ascii < 0x3A) { 25 | return ascii - 0x30; 26 | } else { 27 | switch (ascii) { 28 | case 0x20: return -1; break; 29 | case 0x21: return 0x24; break; 30 | case 0x23: return 0x25; break; 31 | case 0x3F: return 0x26; break; 32 | case 0x26: return 0x27; break; 33 | case 0x25: return 0x28; break; 34 | case 0x2A: return 0x32; break; 35 | case 0x2B: return 0x33; break; 36 | case 0x2C: return 0x34; break; 37 | case 0x2D: return 0x35; break; 38 | case 0x2E: return 0x36; break; 39 | case 0x2F: return 0x37; break; 40 | } 41 | } 42 | return -1; 43 | } 44 | 45 | int main(int argc, char *argv[]) 46 | { 47 | unsigned char *data; 48 | long len = read_file("sm64.split/bin/font_graphics.bin", &data); 49 | unsigned int r, c, hexchar; 50 | int convchar; 51 | unsigned int seg; 52 | unsigned int offset; 53 | char printchar; 54 | char printstr[8]; 55 | 56 | for (c = 0; c < NUM_COLS - 1; c++) { 57 | printf("^ Hex ^ A ^ Index ^ Seg Addr ^ T "); 58 | } 59 | printf("^\n"); 60 | for (r = 0; r < 0x80/NUM_COLS; r++) { 61 | for (c = 1; c < NUM_COLS; c++) { 62 | hexchar = r + NUM_ROWS*c; 63 | convchar = convertchar(hexchar); 64 | if (convchar == -1) { 65 | seg = 0; 66 | } else { 67 | seg = read_u32_be(&data[0x7700 + 4*convchar]); 68 | } 69 | offset = 0xFFFFFF & seg; 70 | printchar = lookupchar(hexchar); 71 | if (printchar == '|' || printchar == '^') { 72 | sprintf(printstr, "%%%%%c%%%%", printchar); 73 | } else { 74 | sprintf(printstr, "%c", printchar); 75 | } 76 | printf("| %02X | %s | ", hexchar, printstr); 77 | if (seg == 0) { 78 | if (convchar == -1) { 79 | printf(" - | - | "); 80 | } else { 81 | printf(" %02X | %08X | ", convchar, seg); 82 | } 83 | } else { 84 | printf(" %02X | %08X | {{http://queueram.com/n64/sm64/textures/textures/font_graphics.0x%05X.png}} ", convchar, seg, offset); 85 | } 86 | } 87 | printf("|\n"); 88 | } 89 | free(data); 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /tools/jalfind.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define JALFIND_VERSION "0.1" 5 | 6 | #define read_u32_be(buf) (unsigned int)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3])) 7 | 8 | static void print_usage(void) 9 | { 10 | fprintf(stderr, 11 | "jalfind
[ADDRESS...]\n" 12 | "\n" 13 | "jalfind v" JALFIND_VERSION ": search for MIPS JAL, LUI/ADDIU, and direct references in a file\n" 14 | "\n" 15 | "Arguments:\n" 16 | " FILE input ROM file\n" 17 | " ADDRESS address to find references to (assumes hex)\n"); 18 | } 19 | 20 | int read_file(const char *file_name, unsigned char **data) 21 | { 22 | FILE *in; 23 | unsigned char *in_buf = NULL; 24 | size_t file_size; 25 | size_t bytes_read; 26 | 27 | in = fopen(file_name, "rb"); 28 | if (in == NULL) { 29 | return -1; 30 | } 31 | 32 | // determine file size 33 | fseek(in, 0, SEEK_END); 34 | file_size = ftell(in); 35 | fseek(in, 0, SEEK_SET); 36 | // allocate buffer and read entire file in 37 | in_buf = malloc(file_size); 38 | bytes_read = fread(in_buf, 1, file_size, in); 39 | if (bytes_read != file_size) { 40 | return -2; 41 | } 42 | 43 | fclose(in); 44 | *data = in_buf; 45 | return (int)bytes_read; 46 | } 47 | 48 | #define OPCODE_MASK 0xFC000000 49 | #define OPCODE_ADDIU 0x24000000 50 | #define OPCODE_JAL 0x0C000000 51 | #define OPCODE_LUI 0x3C000000 52 | #define OPCODE_ORI 0x34000000 53 | #define IMM_MASK 0x0000FFFF 54 | #define RS_MASK 0x03E00000 55 | #define RT_MASK 0x001F0000 56 | 57 | static const char * const regs[] = 58 | { 59 | "zero", 60 | "at", 61 | "v0", 62 | "v1", 63 | "a0", 64 | "a1", 65 | "a2", 66 | "a3", 67 | "t0", 68 | "t1", 69 | "t2", 70 | "t3", 71 | "t4", 72 | "t5", 73 | "t6", 74 | "t7", 75 | "s0", 76 | "s1", 77 | "s2", 78 | "s3", 79 | "s4", 80 | "s5", 81 | "s6", 82 | "s7", 83 | "t8", 84 | "t9", 85 | "k0", 86 | "k1", 87 | "gp", 88 | "sp", 89 | "s8", 90 | "ra", 91 | }; 92 | 93 | void find_reference(unsigned char *data, int len, unsigned int addr) 94 | { 95 | unsigned int jal = OPCODE_JAL | ((0x0FFFFFFF & addr) >> 2); 96 | unsigned int lui_imm = addr >> 16; 97 | unsigned int addiu_imm = addr & 0xFFFF; 98 | int i, j; 99 | 100 | if (addr & 0x8000) { 101 | lui_imm++; 102 | } 103 | 104 | printf("--> Looking for %08X/%08X\n", addr, jal); 105 | for (i = 0; i < len; i += 4) { 106 | unsigned int ival = read_u32_be(&data[i]); 107 | 108 | // look for direct address 109 | if (addr == ival) { 110 | printf("%06X: %08X\n", i, ival); 111 | 112 | // find direct JAL 113 | } else if (jal == ival) { 114 | printf("%06X: %08X JAL 0x%08X\n", i, ival, addr); 115 | 116 | // look for LUI/ADDIU pairs that match the address 117 | } else if (((ival & OPCODE_MASK) == OPCODE_LUI) && ((ival & IMM_MASK) == lui_imm)) { 118 | unsigned int lui_rt = (ival & RT_MASK) >> 16; 119 | // look up to 32 instructions 120 | for (j = i + 4; j < len && j < i + 32*4; j += 4) { 121 | unsigned int jval = read_u32_be(&data[j]); 122 | if ((jval & OPCODE_MASK) == OPCODE_ADDIU) { 123 | unsigned int addiu_rs = (jval & RS_MASK) >> 21; 124 | unsigned int addiu_rt = (jval & RT_MASK) >> 16; 125 | if (addiu_rs == lui_rt) { 126 | if ((jval & IMM_MASK) == addiu_imm) { 127 | printf("%06X: %08X LUI %s, 0x%04X // %%hi(0x%08X)\n" 128 | "%06X: %08X ADDIU %s, %s, 0x%04X // %%lo(0x%08X)\n", 129 | i, ival, regs[lui_rt], lui_imm, addr, 130 | j, jval, regs[addiu_rt], regs[addiu_rs], addiu_imm, addr); 131 | } 132 | break; // stop looking for a matching ADDIU 133 | } 134 | } 135 | } 136 | } 137 | } 138 | } 139 | 140 | int main(int argc, char *argv[]) 141 | { 142 | unsigned char *data; 143 | char *fname; 144 | unsigned int addr; 145 | int len; 146 | int i; 147 | 148 | if (argc < 3) { 149 | print_usage(); 150 | return 1; 151 | } 152 | 153 | fname = argv[1]; 154 | len = read_file(fname, &data); 155 | if (len > 0) { 156 | for (i = 2; i < argc; i++) { 157 | addr = strtoul(argv[i], NULL, 16); 158 | find_reference(data, len, addr); 159 | } 160 | } else { 161 | fprintf(stderr, "Error opening/reading \"%s\"\n", fname); 162 | return 1; 163 | } 164 | 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /tools/mk64karts.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../libmio0.h" 6 | #include "../n64graphics.h" 7 | #include "../utils.h" 8 | 9 | #define MK64KARTS_VERSION "0.2" 10 | 11 | // first and last MIO0 blocks for kart textures 12 | #define MIO0_FIRST 0x145470 13 | #define MIO0_LAST 0x619730 14 | 15 | #define WIDTH 64 16 | #define HEIGHT 64 17 | 18 | typedef struct 19 | { 20 | char png_dir[FILENAME_MAX]; 21 | char rom_filename[FILENAME_MAX]; 22 | int wheel; 23 | } arg_config; 24 | 25 | // default configuration 26 | static const arg_config default_args = 27 | { 28 | "karts", // PNG directory 29 | "", // ROM filename 30 | 0, // wheel animation index 31 | }; 32 | 33 | // start of wheel palette data, kart palette is +0x24200 34 | static unsigned palette_groups[] = 35 | { 36 | 0x1BBE6C, // Luigi 37 | 0x24E6A0, // Mario 38 | 0x2E5BC8, // Yoshi 39 | 0x379D38, // Peach 40 | 0x41705C, // Wario 41 | 0x4A7AC8, // Toad 42 | 0x559AE4, // DK 43 | 0x619EF8, // Bowser 44 | }; 45 | 46 | static void print_usage(void) 47 | { 48 | ERROR("Usage: mk64karts [-d PNG_DIR] [-w WHEEL] [-v] MK64_ROM\n" 49 | "\n" 50 | "mk64karts v" MK64KARTS_VERSION ": MK64 kart texture dumper\n" 51 | "\n" 52 | "Optional arguments:\n" 53 | " -d PNG_DIR output directory for PNG textures (default: \"%s\")\n" 54 | " -w WHEEL wheel animation index [0-3] (default: %d)\n" 55 | " -v verbose output\n" 56 | "\n" 57 | "File arguments:\n" 58 | " MK64_ROM input MK64 ROM file\n", 59 | default_args.png_dir, default_args.wheel); 60 | exit(1); 61 | } 62 | 63 | // parse command line arguments 64 | static void parse_arguments(int argc, char *argv[], arg_config *config) 65 | { 66 | int i; 67 | int file_count = 0; 68 | if (argc < 2) { 69 | print_usage(); 70 | exit(1); 71 | } 72 | for (i = 1; i < argc; i++) { 73 | if (argv[i][0] == '-') { 74 | switch (argv[i][1]) { 75 | case 'd': 76 | if (++i >= argc) { 77 | print_usage(); 78 | } 79 | strcpy(config->png_dir, argv[i]); 80 | break; 81 | case 'w': 82 | if (++i >= argc) { 83 | print_usage(); 84 | } 85 | config->wheel = strtol(argv[i], NULL, 0); 86 | if (config->wheel < 0 || config->wheel > 3) { 87 | print_usage(); 88 | } 89 | break; 90 | case 'v': 91 | g_verbosity = 1; 92 | break; 93 | default: 94 | print_usage(); 95 | break; 96 | } 97 | } else { 98 | if (file_count == 0) { 99 | strcpy(config->rom_filename, argv[i]); 100 | } else { 101 | // too many 102 | print_usage(); 103 | } 104 | file_count++; 105 | } 106 | } 107 | if (file_count < 1) { 108 | print_usage(); 109 | } 110 | } 111 | 112 | int main(int argc, char *argv[]) 113 | { 114 | char pngfilename[FILENAME_MAX]; 115 | arg_config args; 116 | char *palette; 117 | unsigned char *rom; 118 | unsigned char *inflated; 119 | rgba *imgr; 120 | int i; 121 | int kart; 122 | int cur_count; 123 | 124 | args = default_args; 125 | parse_arguments(argc, argv, &args); 126 | INFO("Arguments: \"%s\" \"%s\" %d\n", args.rom_filename, args.png_dir, args.wheel); 127 | 128 | INFO("Loading \"%s\"\n", args.rom_filename); 129 | long rom_len = read_file(args.rom_filename, &rom); 130 | 131 | if (rom_len <= 0) { 132 | ERROR("Error reading in \"%s\"\n", args.rom_filename); 133 | exit(1); 134 | } 135 | INFO("Loaded 0x%lX bytes from \"%s\"\n", rom_len, args.rom_filename); 136 | 137 | // ensure PNG directory exists 138 | make_dir(args.png_dir); 139 | 140 | // prepare palette - wheels are from index 0xC0-0xFF (offset 0x180-0x1FF) 141 | kart = -1; 142 | palette = malloc(2*256); 143 | inflated = malloc(2*WIDTH*HEIGHT); 144 | cur_count = 0; 145 | for (i = MIO0_FIRST; i <= MIO0_LAST; i += 4) { 146 | if (!memcmp(&rom[i], "MIO0", 4)) { 147 | unsigned int end; 148 | if (kart < 0 || i > palette_groups[kart]) { 149 | kart++; 150 | cur_count = 0; 151 | memcpy(palette, &rom[palette_groups[kart] + 0x24200], 0x180); 152 | } 153 | unsigned wheel_offset = palette_groups[kart] + 0x80*(4*cur_count + args.wheel); 154 | memcpy(&palette[0x180], &rom[wheel_offset], 0x80); 155 | INFO("Inflating MIO0 block 0x%X\n", i); 156 | int len = mio0_decode(&rom[i], inflated, &end); 157 | if (len != WIDTH*HEIGHT) { 158 | ERROR("%X: %X > %X\n", i, len, WIDTH*HEIGHT); 159 | exit(1); 160 | } 161 | sprintf(pngfilename, "%s/%X.png", args.png_dir, i); 162 | INFO("Converting 0x%X bytes from raw CI to RGBA\n", len); 163 | imgr = rawci2rgba(inflated, palette, WIDTH, HEIGHT, 16); 164 | INFO("Writing out PNG \"%s\"\n", pngfilename); 165 | rgba2png(imgr, WIDTH, HEIGHT, pngfilename); 166 | 167 | i += end - 4; 168 | i &= ~(0x3); // ensure still aligned 169 | cur_count++; 170 | } 171 | } 172 | 173 | free(inflated); 174 | free(palette); 175 | 176 | return 0; 177 | } 178 | -------------------------------------------------------------------------------- /tools/montage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../config.h" 7 | #include "../utils.h" 8 | 9 | #define DEFAULT_CONFIG "../configs/sm64.u.config" 10 | 11 | typedef struct 12 | { 13 | unsigned int rom; 14 | unsigned int ext; 15 | } sm64_offset; 16 | 17 | // extended ROM mapping based on output from sm64extend 18 | static const sm64_offset mapping[] = 19 | { 20 | {0x00108A40, 0x00803156}, // 0x00800000}, // 0081BB64 as raw data with a MIO0 header 21 | {0x00114750, 0x00823B64}, // 00858EDC as raw data 22 | {0x0012A7E0, 0x00860EDC}, // 0087623C as raw data 23 | {0x00132C60, 0x0087E23C}, // 008843BC as raw data 24 | {0x00134D20, 0x0088C3BC}, // 0089D45C as raw data 25 | {0x0013B910, 0x008A545C}, // 008B918C as raw data 26 | {0x00145E90, 0x008C118C}, // 008D57DC as raw data 27 | {0x001521D0, 0x008DD7DC}, // 008F3894 as raw data 28 | {0x00160670, 0x008FB894}, // 009089C4 as raw data 29 | {0x00165A50, 0x009109C4}, // 00913E8C as raw data 30 | {0x00166C60, 0x0091BE8C}, // 0092C004 as raw data 31 | {0x0016D870, 0x00934004}, // 00958204 as raw data 32 | {0x00180BB0, 0x00960204}, // 009770C4 as raw data 33 | {0x00188440, 0x0097F0C4}, // 009E1FD4 as raw data 34 | {0x001B9CC0, 0x009E9FD4}, // 00A01934 as raw data 35 | {0x001C4230, 0x00A09934}, // 00A2EABC as raw data 36 | {0x001D8310, 0x00A36ABC}, // 00A4E934 as raw data 37 | {0x001E51F0, 0x00A56934}, // 00A5C7AC as raw data 38 | {0x001E7EE0, 0x00A647AC}, // 00A7981C as raw data 39 | {0x001F2200, 0x00A8181C}, // 00AAA40C as raw data 40 | {0x00201410, 0x00AB240C}, // 00AE5714 as raw data 41 | {0x0026A3A0, 0x00AED714}, // 00AFA054 as raw data 42 | {0x0026F420, 0x00B02054}, // 00B085FC as raw data 43 | {0x002708C0, 0x00B112CD}, // 0x00B105FC}, // 00B178B5 as raw data with a MIO0 header 44 | {0x002A65B0, 0x00B1F8B5}, // 00B2D715 as raw data 45 | {0x002AC6B0, 0x00B35715}, // 00B55855 as raw data 46 | {0x002B8F10, 0x00B5D855}, // 00B7D995 as raw data 47 | {0x002C73D0, 0x00B85995}, // 00B9A2D5 as raw data 48 | {0x002D0040, 0x00BA22D5}, // 00BBAC15 as raw data 49 | {0x002D64F0, 0x00BC2C15}, // 00BE2D55 as raw data 50 | {0x002E7880, 0x00BEAD55}, // 00C0AE95 as raw data 51 | {0x002F14E0, 0x00C12E95}, // 00C32FD5 as raw data 52 | {0x002FB1B0, 0x00C3AFD5}, // 00C4F915 as raw data 53 | {0x00301CD0, 0x00C57915}, // 00C77A55 as raw data 54 | {0x0030CEC0, 0x00C7FA55}, // 00C9FB95 as raw data 55 | {0x0031E1D0, 0x00CA93A9}, // 0x00CA7B95}, // 00CB53A9 as raw data with a MIO0 header 56 | {0x00326E40, 0x00CBECBD}, // 0x00CBD3A9}, // 00CCB4BD as raw data with a MIO0 header 57 | {0x0032D070, 0x00CD4BD1}, // 0x00CD34BD}, // 00CE03D1 as raw data with a MIO0 header 58 | {0x00334B30, 0x00CE9CE5}, // 0x00CE83D1}, // 00CF64E5 as raw data with a MIO0 header 59 | {0x0033D710, 0x00CFF5F9}, // 0x00CFE4E5}, // 00D07DF9 as raw data with a MIO0 header 60 | {0x00341140, 0x00D1120D}, // 0x00D0FDF9}, // 00D1B20D as raw data with a MIO0 header 61 | {0x00347A50, 0x00D24B21}, // 0x00D2320D}, // 00D31321 as raw data with a MIO0 header 62 | {0x0034E760, 0x00D3A4B5}, // 0x00D39321}, // 00D430B5 as raw data with a MIO0 header 63 | {0x00351960, 0x00D4C9C9}, // 0x00D4B0B5}, // 00D591C9 as raw data with a MIO0 header 64 | {0x00357350, 0x00D629DD}, // 0x00D611C9}, // 00D6E9DD as raw data with a MIO0 header 65 | {0x0035ED10, 0x00D78271}, // 0x00D769DD}, // 00D84671 as raw data with a MIO0 header 66 | {0x00365980, 0x00D8DF85}, // 0x00D8C671}, // 00D9A785 as raw data with a MIO0 header 67 | {0x0036F530, 0x00DA2785}, // 00DA951D as raw data 68 | {0x00371C40, 0x00DB151D}, // 00DD8361 as raw data 69 | {0x00383950, 0x00DE0361}, // 00E03B07 as raw data 70 | {0x00396340, 0x00E0BB07}, // 00E84C1F as raw data 71 | {0x003D0DC0, 0x00E8CC1F}, // 00EB8587 as raw data 72 | {0x003E76B0, 0x00EC0587}, // 00EE8E37 as raw data 73 | {0x003FC2B0, 0x00EF0E37}, // 00F025F9 as raw data 74 | {0x00405FB0, 0x00F0A5F9}, // 00F1A081 as raw data 75 | {0x0040ED70, 0x00F22081}, // 00F3A809 as raw data 76 | {0x0041A760, 0x00F42809}, // 00F53BB5 as raw data 77 | {0x004246D0, 0x00F5BBB5}, // 00F69F71 as raw data 78 | {0x0042CF20, 0x00F71F71}, // 00F88991 as raw data 79 | {0x00437870, 0x00F90991}, // 00FBF807 as raw data 80 | {0x0044ABC0, 0x00FC7807}, // 00FD907F as raw data 81 | {0x00454E00, 0x00FE107F}, // 00FF0EAF as raw data 82 | {0x0045C600, 0x00FF8EAF}, // 01003B77 as raw data 83 | {0x004614D0, 0x0100BB77}, // 0102177F as raw data 84 | {0x0046B090, 0x0102977F}, // 0102CAAF as raw data 85 | {0x0046C3A0, 0x01034AAF}, // 010502A3 as raw data 86 | {0x004784A0, 0x010582A3}, // 01080B73 as raw data 87 | {0x0048D930, 0x01088B73}, // 01098883 as raw data 88 | {0x00496090, 0x010A0883}, // 010B269B as raw data 89 | {0x0049E710, 0x010BA69B}, // 010E19EB as raw data 90 | {0x004AC570, 0x010E99EB}, // 010F0867 as raw data 91 | {0x004AF930, 0x010F8867}, // 01109903 as raw data 92 | {0x004B80D0, 0x01111903}, // 0111D8AB as raw data 93 | {0x004BEC30, 0x011258AB}, // 0112E271 as raw data 94 | {0x004C2920, 0x01136271}, // 01138D39 as raw data 95 | {0x004C4320, 0x01140D39}, // 011544E7 as raw data 96 | {0x004CDBD0, 0x0115C4E7}, // 0115E087 as raw data 97 | {0x004CEC00, 0x01166087}, // 0116B143 as raw data 98 | {0x004D1910, 0x01173143}, // 011A35B7 as raw data 99 | }; 100 | 101 | static unsigned int map(unsigned int rom) 102 | { 103 | unsigned i; 104 | for (i = 0; i < DIM(mapping); i++) { 105 | if (mapping[i].rom == rom) { 106 | return mapping[i].ext; 107 | } 108 | } 109 | return 0; 110 | } 111 | 112 | int main(int argc, char *argv[]) 113 | { 114 | rom_config config; 115 | FILE *out; 116 | int i; 117 | unsigned int w, h; 118 | unsigned int offset; 119 | 120 | // load defaults and parse arguments 121 | out = stdout; 122 | 123 | // parse config file 124 | INFO("Parsing config file '%s'\n", DEFAULT_CONFIG); 125 | if (parse_config_file(DEFAULT_CONFIG, &config)) { 126 | ERROR("Error parsing config file '%s'\n", DEFAULT_CONFIG); 127 | return 1; 128 | } 129 | 130 | fprintf(out, "mkdir -p montages\n\n"); 131 | 132 | for (i = 0; i < config.section_count; i++) { 133 | split_section *sec = &config.sections[i]; 134 | if (sec->type == TYPE_MIO0) { 135 | if (sec->extra) { 136 | texture *texts = sec->extra; 137 | int t; 138 | fprintf(out, "montage \\\n"); 139 | for (t = 0; t < sec->extra_len; t++) { 140 | char name[256]; 141 | char format[8]; 142 | w = texts[t].width; 143 | h = texts[t].height; 144 | offset = texts[t].offset; 145 | switch (texts[t].format) { 146 | case FORMAT_IA: 147 | sprintf(name, "%s.0x%05X.ia%d.png", sec->label, offset, texts[t].depth); 148 | sprintf(format, "ia%d", texts[t].depth); 149 | break; 150 | case FORMAT_RGBA: 151 | sprintf(name, "%s.0x%05X.png", sec->label, offset); 152 | sprintf(format, "rgba"); 153 | break; 154 | case FORMAT_SKYBOX: 155 | sprintf(name, "%s.0x%05X.skybox.png", sec->label, offset); 156 | sprintf(format, "sky"); 157 | break; 158 | default: 159 | continue; 160 | } 161 | fprintf(out, " -label '%05X\\n%s\\n%dx%d' ../sm64.split/textures/%s \\\n", offset, format, w, h, name); 162 | } 163 | int x = MIN(10, sec->extra_len); 164 | int y = MAX(1, (sec->extra_len + 9) / 10); 165 | fprintf(out, " -tile %dx%d -background none -geometry '64x64+2+2>' montages/%s.png\n\n", 166 | x, y, sec->label); 167 | fprintf(out, "pngcrush -ow montages/%s.png\n\n", sec->label); 168 | } 169 | } 170 | } 171 | 172 | fprintf(out, "cat << EOF > index.html\n"); 173 | fprintf(out, "\n" 174 | "\n" 175 | "%s Textures\n", config.name); 176 | fprintf(out, 177 | "\n"); 236 | fprintf(out, "\n"); 237 | fprintf(out, "
\n"); 238 | fprintf(out, "\n"); 239 | fprintf(out, "\n"); 240 | for (i = 0; i < config.section_count; i++) { 241 | split_section *sec = &config.sections[i]; 242 | if (sec->type == TYPE_MIO0) { 243 | if (sec->extra) { 244 | fprintf(out, "\n", 245 | sec->start, map(sec->start), sec->label); 246 | } 247 | } 248 | } 249 | fprintf(out, "
ROM MIO0Extended ROMTextures and offset in block
%X%X
\n"); 250 | fprintf(out, "
\n"); 251 | fprintf(out, "\n"); 252 | fprintf(out, "\n"); 253 | fprintf(out, "EOF\n"); 254 | 255 | return 0; 256 | } 257 | 258 | -------------------------------------------------------------------------------- /tools/n64ci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../n64graphics.h" 6 | #include "../utils.h" 7 | 8 | #define N64CI_VERSION "0.1" 9 | 10 | #define SCALE_8_5(VAL_) (((VAL_) * 0x1F) / 0xFF) 11 | 12 | typedef struct 13 | { 14 | char pal_filename[FILENAME_MAX]; 15 | unsigned pal_entries; 16 | char **input_files; 17 | unsigned input_count; 18 | } arg_config; 19 | 20 | typedef struct 21 | { 22 | rgba *data; 23 | unsigned char *ci; 24 | int width; 25 | int height; 26 | } image_t; 27 | 28 | typedef struct 29 | { 30 | unsigned short data[256]; 31 | unsigned max; // max number of entries 32 | unsigned used; 33 | } palette_t; 34 | 35 | // default configuration 36 | static const arg_config default_args = 37 | { 38 | "palette.bin", // output palette filename 39 | 256, // number of palette entries 40 | NULL, // array of input file names 41 | 0 // count of input files 42 | }; 43 | 44 | // find index of palette color 45 | // return -1 if not found 46 | static int pal_find_color(palette_t *pal, unsigned short col) 47 | { 48 | unsigned i; 49 | for (i = 0; i < pal->used; i++) { 50 | if (pal->data[i] == col) { 51 | return i; 52 | } 53 | } 54 | return -1; 55 | } 56 | 57 | // find color in palette, or add if not there 58 | // returns palette index entered or -1 if palette full 59 | static int pal_add_color(palette_t *pal, rgba *col) 60 | { 61 | unsigned short rgba16; 62 | char r, g, b, a; 63 | int idx; 64 | r = SCALE_8_5(col->red); 65 | g = SCALE_8_5(col->green); 66 | b = SCALE_8_5(col->blue); 67 | a = col->alpha ? 0x1 : 0x0; 68 | rgba16 = (((r << 3) | (g >> 2)) << 8) | ((g & 0x3) << 6) | (b << 1) | a; 69 | idx = pal_find_color(pal, rgba16); 70 | if (idx < 0) { 71 | if (pal->used == pal->max) { 72 | ERROR("Error: trying to use more than %d\n", pal->max); 73 | } else { 74 | idx = pal->used; 75 | pal->data[pal->used] = rgba16; 76 | pal->used++; 77 | } 78 | } 79 | return idx; 80 | } 81 | 82 | static void print_usage(void) 83 | { 84 | ERROR("Usage: n64ci [-e PAL_ENTIRES] [-p PAL_FILE] [-v] [PNG images]\n" 85 | "\n" 86 | "n64ci v" N64CI_VERSION ": N64 CI image encoder\n" 87 | "\n" 88 | "Optional arguments:\n" 89 | " -e PAL_ENTRIES number of palette entires (default: %d)\n" 90 | " -p PAL_FILE output palette file (default: \"%s\")\n" 91 | " -v verbose progress output\n" 92 | "\n" 93 | "File arguments:\n" 94 | " PNG images input PNG images to encode\n", 95 | default_args.pal_entries, default_args.pal_filename); 96 | exit(1); 97 | } 98 | 99 | // parse command line arguments 100 | static void parse_arguments(int argc, char *argv[], arg_config *config) 101 | { 102 | int i; 103 | // allocate max input files 104 | config->input_files = malloc(sizeof(config->input_files) * argc); 105 | config->input_count = 0; 106 | if (argc < 2) { 107 | print_usage(); 108 | exit(1); 109 | } 110 | for (i = 1; i < argc; i++) { 111 | if (argv[i][0] == '-') { 112 | switch (argv[i][1]) { 113 | case 'e': 114 | if (++i >= argc) { 115 | print_usage(); 116 | } 117 | config->pal_entries = strtoul(argv[i], NULL, 0); 118 | break; 119 | case 'p': 120 | if (++i >= argc) { 121 | print_usage(); 122 | } 123 | strcpy(config->pal_filename, argv[i]); 124 | break; 125 | case 'v': 126 | g_verbosity = 1; 127 | break; 128 | default: 129 | print_usage(); 130 | break; 131 | } 132 | } else { 133 | // assume input filename 134 | config->input_files[config->input_count] = argv[i]; 135 | config->input_count++; 136 | } 137 | } 138 | if (config->input_count < 1) { 139 | print_usage(); 140 | } 141 | } 142 | 143 | int main(int argc, char *argv[]) 144 | { 145 | char bin_filename[FILENAME_MAX]; 146 | unsigned char *palette_bin; 147 | arg_config config; 148 | palette_t palette; 149 | image_t *images; 150 | unsigned pal_length; 151 | unsigned i; 152 | int x, y; 153 | 154 | config = default_args; 155 | parse_arguments(argc, argv, &config); 156 | INFO("Arguments: \"%s\" %d %d\n", config.pal_filename, config.pal_entries, config.input_count); 157 | 158 | images = malloc(sizeof(*images) * config.input_count); 159 | 160 | // load all images 161 | for (i = 0; i < config.input_count; i++) { 162 | images[i].data = pngfile2rgba(config.input_files[i], &images[i].width, &images[i].height); 163 | } 164 | 165 | // assign colors to palette 166 | palette.max = config.pal_entries; 167 | palette.used = 0; 168 | for (i = 0; i < config.input_count; i++) { 169 | images[i].ci = malloc(sizeof(*images[i].ci) * images[i].width * images[i].height); 170 | for (x = 0; x < images[i].width; x++) { 171 | for (y = 0; y < images[i].height; y++) { 172 | unsigned img_idx = y * images[i].width + x; 173 | int pal_idx = pal_add_color(&palette, &images[i].data[img_idx]); 174 | if (pal_idx < 0) { 175 | ERROR("Error adding color @ (%d, %d): %d (used: %u)\n", x, y, pal_idx, palette.used); 176 | exit(1); 177 | } else { 178 | images[i].ci[img_idx] = (unsigned char)pal_idx; 179 | } 180 | } 181 | } 182 | } 183 | 184 | // output bin files 185 | for (i = 0; i < config.input_count; i++) { 186 | generate_filename(config.input_files[i], bin_filename, "bin"); 187 | write_file(bin_filename, images[i].ci, images[i].width * images[i].height); 188 | } 189 | 190 | // output palette file 191 | pal_length = config.pal_entries*2; 192 | // unused entries set to 0xFFFF 193 | palette_bin = malloc(pal_length); 194 | memset(palette_bin, 0xFF, pal_length); 195 | for (i = 0; i < palette.used; i++) { 196 | write_u16_be(&palette_bin[i*2], palette.data[i]); 197 | } 198 | write_file(config.pal_filename, palette_bin, pal_length); 199 | 200 | ERROR("Used: %d\n", palette.used); 201 | 202 | free(config.input_files); 203 | free(palette_bin); 204 | free(images); 205 | 206 | return 0; 207 | } 208 | -------------------------------------------------------------------------------- /tools/sm64collision.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../utils.h" 6 | 7 | #define SM64COLLISION_VERSION "0.1" 8 | 9 | typedef struct 10 | { 11 | char *in_filename; 12 | char *out_filename; 13 | char *name; 14 | unsigned offset; 15 | unsigned scale; 16 | short x; 17 | short y; 18 | short z; 19 | } arg_config; 20 | 21 | typedef struct 22 | { 23 | short x, y, z; 24 | } vertex; 25 | 26 | typedef struct 27 | { 28 | unsigned vidx[3]; 29 | } triangle; 30 | 31 | typedef struct 32 | { 33 | vertex *verts; 34 | unsigned vcount; 35 | triangle *tris; 36 | unsigned tcount; 37 | } collision; 38 | 39 | // default configuration 40 | static const arg_config default_config = 41 | { 42 | NULL, // input filename 43 | NULL, // output filename 44 | "collision", // model name 45 | 0xF3A0, // offset for castle grounds 46 | 4096, // scale for OBJ export 47 | 0, // shift X 48 | 0, // shift Y 49 | 0, // shift Z 50 | }; 51 | 52 | static void print_usage(void) 53 | { 54 | ERROR("Usage: sm64collision [-o OFFSET] [-n NAME] [-s SCALE] [-x X] [-y Y] [-z Z] [-v] FILE [OUT_FILE]\n" 55 | "\n" 56 | "sm64collision v" SM64COLLISION_VERSION ": Super Mario 64 collision decoder\n" 57 | "\n" 58 | "Optional arguments:\n" 59 | " -o OFFSET offset to pull collision data from in bytes (default: 0x%X)\n" 60 | " -n NAME name specified in obj model (default: %s)\n" 61 | " -s SCALE scale obj data by this amount (default: %d)\n" 62 | " -x X amount to shift X values (default: %d)\n" 63 | " -y Y amount to shift Y values (default: %d)\n" 64 | " -z Z amount to shift Z values (default: %d)\n" 65 | " -v verbose progress output\n" 66 | "\n" 67 | "File arguments:\n" 68 | " FILE input binary file\n" 69 | " OUT_FILE output OBJ file (default: no obj output)\n", 70 | default_config.offset, default_config.name, default_config.scale, 71 | default_config.x, default_config.y, default_config.z); 72 | exit(EXIT_FAILURE); 73 | } 74 | 75 | // parse command line arguments 76 | static void parse_arguments(int argc, char *argv[], arg_config *args) 77 | { 78 | int i; 79 | int file_count = 0; 80 | if (argc < 2) { 81 | print_usage(); 82 | } 83 | for (i = 1; i < argc; i++) { 84 | if (argv[i][0] == '-') { 85 | switch (argv[i][1]) { 86 | case 'o': 87 | if (++i >= argc) { 88 | print_usage(); 89 | } 90 | args->offset = strtoul(argv[i], NULL, 0); 91 | break; 92 | case 'n': 93 | if (++i >= argc) { 94 | print_usage(); 95 | } 96 | args->name = argv[i]; 97 | break; 98 | case 's': 99 | if (++i >= argc) { 100 | print_usage(); 101 | } 102 | args->scale = strtol(argv[i], NULL, 0); 103 | break; 104 | case 'x': 105 | if (++i >= argc) { 106 | print_usage(); 107 | } 108 | args->x = strtol(argv[i], NULL, 0); 109 | break; 110 | case 'y': 111 | if (++i >= argc) { 112 | print_usage(); 113 | } 114 | args->y = strtol(argv[i], NULL, 0); 115 | break; 116 | case 'z': 117 | if (++i >= argc) { 118 | print_usage(); 119 | } 120 | args->z = strtol(argv[i], NULL, 0); 121 | break; 122 | case 'v': 123 | g_verbosity = 1; 124 | break; 125 | default: 126 | print_usage(); 127 | break; 128 | } 129 | } else { 130 | switch (file_count) { 131 | case 0: 132 | args->in_filename = argv[i]; 133 | break; 134 | case 1: 135 | args->out_filename = argv[i]; 136 | break; 137 | default: // too many 138 | print_usage(); 139 | break; 140 | } 141 | file_count++; 142 | } 143 | } 144 | if (file_count < 1) { 145 | print_usage(); 146 | } 147 | } 148 | 149 | void decode_collision(unsigned char *data, collision *col) 150 | { 151 | unsigned vcount; 152 | unsigned tcount; 153 | unsigned cur_tcount; 154 | unsigned offset; 155 | unsigned terrain; 156 | unsigned v_per_t; 157 | unsigned processing; 158 | unsigned i; 159 | if (data[0] != 0x00 || data[1] != 0x40) { 160 | ERROR("Unknown data: %08X\n", read_u32_be(data)); 161 | return; 162 | } 163 | vcount = read_u16_be(&data[2]); 164 | INFO("Loading %u vertices\n", vcount); 165 | col->verts = malloc(vcount * sizeof(*col->verts)); 166 | // load vetices 167 | offset = 4; 168 | for (i = 0; i < vcount; i++) { 169 | col->verts[i].x = read_s16_be(&data[offset + i*6]); 170 | col->verts[i].y = read_s16_be(&data[offset + i*6+2]); 171 | col->verts[i].z = read_s16_be(&data[offset + i*6+4]); 172 | } 173 | offset += vcount*6; 174 | tcount = 0; 175 | col->tris = NULL; 176 | processing = 1; 177 | while (processing) { 178 | terrain = read_u16_be(&data[offset]); 179 | cur_tcount = read_u16_be(&data[offset+2]); 180 | if (terrain == 0x41 || terrain > 0xFF) { 181 | ERROR("terrain: %X, tcount: %X\n", terrain, cur_tcount); 182 | processing = 0; 183 | break; 184 | } 185 | switch (terrain) { 186 | case 0x0E: 187 | case 0x2C: 188 | case 0x24: 189 | case 0x25: 190 | case 0x27: 191 | case 0x2D: 192 | v_per_t = 4; 193 | break; 194 | default: 195 | v_per_t = 3; 196 | break; 197 | } 198 | INFO("Loading %u triangles of terrain %X\n", cur_tcount, terrain); 199 | col->tris = realloc(col->tris, (tcount + cur_tcount) * sizeof(*col->tris)); 200 | // load triangles 201 | offset += 4; 202 | for (i = 0; i < cur_tcount; i++) { 203 | col->tris[tcount+i].vidx[0] = read_u16_be(&data[offset + i*v_per_t*2]); 204 | col->tris[tcount+i].vidx[1] = read_u16_be(&data[offset + i*v_per_t*2+2]); 205 | col->tris[tcount+i].vidx[2] = read_u16_be(&data[offset + i*v_per_t*2+4]); 206 | } 207 | tcount += cur_tcount; 208 | offset += cur_tcount*v_per_t*2; 209 | } 210 | col->vcount = vcount; 211 | col->tcount = tcount; 212 | } 213 | 214 | static void generate_obj(char *filename, char *name, collision *col, unsigned scale) 215 | { 216 | FILE *out; 217 | unsigned i; 218 | 219 | out = fopen(filename, "w"); 220 | if (out == NULL) { 221 | ERROR("Error opening %s\n", filename); 222 | exit(EXIT_FAILURE); 223 | } 224 | 225 | fprintf(out, "g %s\n", name); 226 | 227 | for (i = 0; i < col->vcount; i++) { 228 | float x, y, z; 229 | x = (float)col->verts[i].x / (float)scale; 230 | y = (float)col->verts[i].y / (float)scale; 231 | z = (float)col->verts[i].z / (float)scale; 232 | fprintf(out, "v %f %f %f\n", x, y, z); 233 | } 234 | 235 | for (i = 0; i < col->tcount; i++) { 236 | fprintf(out, "f %d %d %d\n", col->tris[i].vidx[0]+1, col->tris[i].vidx[1]+1, col->tris[i].vidx[2]+1); 237 | } 238 | 239 | fclose(out); 240 | } 241 | 242 | int main(int argc, char *argv[]) 243 | { 244 | arg_config config; 245 | collision col; 246 | unsigned char *in_buf = NULL; 247 | long in_size; 248 | unsigned i; 249 | 250 | // get configuration from arguments 251 | config = default_config; 252 | parse_arguments(argc, argv, &config); 253 | 254 | // read input file into memory 255 | in_size = read_file(config.in_filename, &in_buf); 256 | if (in_size <= 0) { 257 | ERROR("Error reading input file \"%s\"\n", config.in_filename); 258 | exit(EXIT_FAILURE); 259 | } 260 | 261 | // decode collision vertices and triangles 262 | decode_collision(&in_buf[config.offset], &col); 263 | 264 | ERROR("Read: %d vertices, %d triangles\n", col.vcount, col.tcount); 265 | // output obj 266 | if (config.out_filename != NULL) { 267 | INFO("Generating OBJ file \"%s\"\n", config.out_filename); 268 | generate_obj(config.out_filename, config.name, &col, config.scale); 269 | } 270 | 271 | // if shifting values 272 | if (config.x || config.y || config.z) { 273 | INFO("Shifting vertices by: {%d %d %d}\n", config.x, config.y, config.z); 274 | for (i = 0; i < col.vcount; i++) { 275 | col.verts[i].x += config.x; 276 | col.verts[i].y += config.y; 277 | col.verts[i].z += config.z; 278 | } 279 | for (i = 0; i < col.vcount; i++) { 280 | write_u16_be(&in_buf[config.offset+4+i*6], col.verts[i].x); 281 | write_u16_be(&in_buf[config.offset+4+i*6+2], col.verts[i].y); 282 | write_u16_be(&in_buf[config.offset+4+i*6+4], col.verts[i].z); 283 | } 284 | write_file(config.in_filename, in_buf, in_size); 285 | } 286 | 287 | // cleanup 288 | free(in_buf); 289 | 290 | return EXIT_SUCCESS; 291 | } 292 | -------------------------------------------------------------------------------- /tools/sm64text.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define SM64TEXT_VERSION "0.2" 9 | 10 | #define COUNT_OF(ARR) (sizeof(ARR)/sizeof(ARR[0])) 11 | 12 | typedef struct { 13 | enum {REGION_U, REGION_J} reg; 14 | enum {CONVERT_BYTES, CONVERT_TEXT, DUMP_TABLE} conv; 15 | union { 16 | uint8_t *bytes; 17 | char *text; 18 | }; 19 | size_t length; 20 | } config; 21 | 22 | static const config default_config = { 23 | .reg = REGION_J, 24 | .conv = CONVERT_BYTES, 25 | .bytes = NULL, 26 | .length = 0, 27 | }; 28 | 29 | typedef struct { 30 | uint8_t bytes[2]; 31 | size_t blen; 32 | char *text; 33 | } mapping; 34 | 35 | const uint8_t sm64_string_terminator = 0xFF; 36 | 37 | const mapping j_mapping[] = { 38 | {{0x00}, 1, "0"}, 39 | {{0x01}, 1, "1"}, 40 | {{0x02}, 1, "2"}, 41 | {{0x03}, 1, "3"}, 42 | {{0x04}, 1, "4"}, 43 | {{0x05}, 1, "5"}, 44 | {{0x06}, 1, "6"}, 45 | {{0x07}, 1, "7"}, 46 | {{0x08}, 1, "8"}, 47 | {{0x09}, 1, "9"}, 48 | {{0x0A}, 1, "A"}, 49 | {{0x0B}, 1, "B"}, 50 | {{0x0C}, 1, "C"}, 51 | {{0x0D}, 1, "D"}, 52 | {{0x0E}, 1, "E"}, 53 | {{0x0F}, 1, "F"}, 54 | {{0x10}, 1, "G"}, 55 | {{0x11}, 1, "H"}, 56 | {{0x12}, 1, "I"}, 57 | {{0x13}, 1, "J"}, 58 | {{0x14}, 1, "K"}, 59 | {{0x15}, 1, "L"}, 60 | {{0x16}, 1, "M"}, 61 | {{0x17}, 1, "N"}, 62 | {{0x18}, 1, "O"}, 63 | {{0x19}, 1, "P"}, 64 | {{0x1A}, 1, "Q"}, 65 | {{0x1B}, 1, "R"}, 66 | {{0x1C}, 1, "S"}, 67 | {{0x1D}, 1, "T"}, 68 | {{0x1E}, 1, "U"}, 69 | {{0x1F}, 1, "V"}, 70 | {{0x20}, 1, "W"}, 71 | {{0x21}, 1, "X"}, 72 | {{0x22}, 1, "Y"}, 73 | {{0x23}, 1, "Z"}, 74 | {{0x40}, 1, "あ"}, 75 | {{0x41}, 1, "い"}, 76 | {{0x42}, 1, "う"}, 77 | {{0x43}, 1, "え"}, 78 | {{0x44}, 1, "お"}, 79 | {{0x45}, 1, "か"}, 80 | {{0x46}, 1, "き"}, 81 | {{0x47}, 1, "く"}, 82 | {{0x48}, 1, "け"}, 83 | {{0x49}, 1, "こ"}, 84 | {{0x4A}, 1, "さ"}, 85 | {{0x4B}, 1, "し"}, 86 | {{0x4C}, 1, "す"}, 87 | {{0x4D}, 1, "せ"}, 88 | {{0x4E}, 1, "そ"}, 89 | {{0x4F}, 1, "た"}, 90 | {{0x50}, 1, "ち"}, 91 | {{0x51}, 1, "つ"}, 92 | {{0x52}, 1, "て"}, 93 | {{0x53}, 1, "と"}, 94 | {{0x54}, 1, "な"}, 95 | {{0x55}, 1, "に"}, 96 | {{0x56}, 1, "ぬ"}, 97 | {{0x57}, 1, "ね"}, 98 | {{0x58}, 1, "の"}, 99 | {{0x59}, 1, "は"}, 100 | {{0x5A}, 1, "ひ"}, 101 | {{0x5B}, 1, "ふ"}, 102 | {{0x5C}, 1, "へ"}, 103 | {{0x5D}, 1, "ほ"}, 104 | {{0x5E}, 1, "ま"}, 105 | {{0x5F}, 1, "み"}, 106 | {{0x60}, 1, "む"}, 107 | {{0x61}, 1, "め"}, 108 | {{0x62}, 1, "も"}, 109 | {{0x63}, 1, "や"}, 110 | {{0x64}, 1, "ゆ"}, 111 | {{0x65}, 1, "よ"}, 112 | {{0x66}, 1, "ら"}, 113 | {{0x67}, 1, "り"}, 114 | {{0x68}, 1, "る"}, 115 | {{0x69}, 1, "れ"}, 116 | {{0x6A}, 1, "ろ"}, 117 | {{0x6B}, 1, "わ"}, 118 | {{0x6C}, 1, "を"}, 119 | {{0x6D}, 1, "ん"}, 120 | {{0x6E}, 1, "。"}, 121 | {{0x6F}, 1, ","}, 122 | {{0x70}, 1, "ア"}, 123 | {{0x71}, 1, "イ"}, 124 | {{0x72}, 1, "ウ"}, 125 | {{0x73}, 1, "エ"}, 126 | {{0x74}, 1, "オ"}, 127 | {{0x75}, 1, "カ"}, 128 | {{0x76}, 1, "キ"}, 129 | {{0x77}, 1, "ク"}, 130 | {{0x78}, 1, "ケ"}, 131 | {{0x79}, 1, "コ"}, 132 | {{0x7A}, 1, "サ"}, 133 | {{0x7B}, 1, "シ"}, 134 | {{0x7C}, 1, "ス"}, 135 | {{0x7D}, 1, "セ"}, 136 | {{0x7E}, 1, "ソ"}, 137 | {{0x7F}, 1, "タ"}, 138 | {{0x80}, 1, "チ"}, 139 | {{0x81}, 1, "ツ"}, 140 | {{0x82}, 1, "テ"}, 141 | {{0x83}, 1, "ト"}, 142 | {{0x84}, 1, "ナ"}, 143 | {{0x85}, 1, "ニ"}, 144 | {{0x86}, 1, "ヌ"}, 145 | {{0x87}, 1, "ネ"}, 146 | {{0x88}, 1, "ノ"}, 147 | {{0x89}, 1, "ハ"}, 148 | {{0x8A}, 1, "ヒ"}, 149 | {{0x8B}, 1, "フ"}, 150 | {{0x8C}, 1, "ヘ"}, 151 | {{0x8D}, 1, "ホ"}, 152 | {{0x8E}, 1, "マ"}, 153 | {{0x8F}, 1, "ミ"}, 154 | {{0x90}, 1, "ム"}, 155 | {{0x91}, 1, "メ"}, 156 | {{0x92}, 1, "モ"}, 157 | {{0x93}, 1, "ヤ"}, 158 | {{0x94}, 1, "ユ"}, 159 | {{0x95}, 1, "ヨ"}, 160 | {{0x96}, 1, "ラ"}, 161 | {{0x97}, 1, "リ"}, 162 | {{0x98}, 1, "ル"}, 163 | {{0x99}, 1, "レ"}, 164 | {{0x9A}, 1, "ロ"}, 165 | {{0x9B}, 1, "ワ"}, 166 | {{0x9D}, 1, "ン"}, 167 | {{0x9E}, 1, " "}, 168 | {{0x9F}, 1, "ー"}, 169 | {{0xA0}, 1, "ぇ"}, 170 | {{0xA1}, 1, "っ"}, 171 | {{0xA2}, 1, "ゃ"}, 172 | {{0xA3}, 1, "ゅ"}, 173 | {{0xA4}, 1, "ょ"}, 174 | {{0xA5}, 1, "ぁ"}, 175 | {{0xA6}, 1, "ぃ"}, 176 | {{0xA7}, 1, "ぅ"}, 177 | {{0xA8}, 1, "ぉ"}, 178 | {{0xD0}, 1, "ェ"}, 179 | {{0xD1}, 1, "ッ"}, 180 | {{0xD2}, 1, "ャ"}, 181 | {{0xD3}, 1, "ュ"}, 182 | {{0xD4}, 1, "ョ"}, 183 | {{0xD5}, 1, "ァ"}, 184 | {{0xD6}, 1, "ィ"}, 185 | {{0xD7}, 1, "ゥ"}, 186 | {{0xD8}, 1, "ォ"}, 187 | {{0xE0}, 1, "@"}, 188 | {{0xE1}, 1, "("}, 189 | {{0xE2}, 1, ")("}, 190 | {{0xE3}, 1, ")"}, 191 | {{0xE4}, 1, "<->"}, 192 | {{0xF2}, 1, "!"}, 193 | {{0xF3}, 1, "%"}, 194 | {{0xF4}, 1, "?"}, 195 | {{0xF5}, 1, "『"}, 196 | {{0xF6}, 1, "』"}, 197 | {{0xF7}, 1, "~"}, 198 | {{0xF8}, 1, "…"}, 199 | {{0xF9}, 1, "$"}, 200 | {{0xFA}, 1, "*"}, 201 | {{0xFB}, 1, "x"}, 202 | {{0xFC}, 1, "・"}, 203 | {{0xFD}, 1, "#"}, 204 | {{0xFE}, 1, "\n"}, 205 | {{0xF0, 0x45}, 2, "が"}, 206 | {{0xF0, 0x46}, 2, "ぎ"}, 207 | {{0xF0, 0x47}, 2, "ぐ"}, 208 | {{0xF0, 0x48}, 2, "げ"}, 209 | {{0xF0, 0x49}, 2, "ご"}, 210 | {{0xF0, 0x4A}, 2, "ざ"}, 211 | {{0xF0, 0x4B}, 2, "じ"}, 212 | {{0xF0, 0x4C}, 2, "ず"}, 213 | {{0xF0, 0x4D}, 2, "ぜ"}, 214 | {{0xF0, 0x4E}, 2, "ぞ"}, 215 | {{0xF0, 0x4F}, 2, "だ"}, 216 | {{0xF0, 0x50}, 2, "ぢ"}, 217 | {{0xF0, 0x51}, 2, "づ"}, 218 | {{0xF0, 0x52}, 2, "で"}, 219 | {{0xF0, 0x53}, 2, "ど"}, 220 | {{0xF0, 0x59}, 2, "ば"}, 221 | {{0xF0, 0x5A}, 2, "び"}, 222 | {{0xF0, 0x5B}, 2, "ぶ"}, 223 | {{0xF0, 0x5C}, 2, "べ"}, 224 | {{0xF0, 0x5D}, 2, "ぼ"}, 225 | {{0xF0, 0x75}, 2, "ガ"}, 226 | {{0xF0, 0x76}, 2, "ギ"}, 227 | {{0xF0, 0x77}, 2, "グ"}, 228 | {{0xF0, 0x78}, 2, "ゲ"}, 229 | {{0xF0, 0x79}, 2, "ゴ"}, 230 | {{0xF0, 0x7A}, 2, "ザ"}, 231 | {{0xF0, 0x7B}, 2, "ジ"}, 232 | {{0xF0, 0x7C}, 2, "ズ"}, 233 | {{0xF0, 0x7D}, 2, "ゼ"}, 234 | {{0xF0, 0x7E}, 2, "ゾ"}, 235 | {{0xF0, 0x7F}, 2, "ダ"}, 236 | {{0xF0, 0x80}, 2, "ヂ"}, 237 | {{0xF0, 0x81}, 2, "ヅ"}, 238 | {{0xF0, 0x82}, 2, "デ"}, 239 | {{0xF0, 0x83}, 2, "ド"}, 240 | {{0xF0, 0x89}, 2, "バ"}, 241 | {{0xF0, 0x8A}, 2, "ビ"}, 242 | {{0xF0, 0x8B}, 2, "ブ"}, 243 | {{0xF0, 0x8C}, 2, "ベ"}, 244 | {{0xF0, 0x8D}, 2, "ボ"}, 245 | {{0xF1, 0x59}, 2, "ぱ"}, 246 | {{0xF1, 0x5A}, 2, "ぴ"}, 247 | {{0xF1, 0x5B}, 2, "ぷ"}, 248 | {{0xF1, 0x5C}, 2, "ぺ"}, 249 | {{0xF1, 0x5D}, 2, "ぽ"}, 250 | {{0xF1, 0x89}, 2, "パ"}, 251 | {{0xF1, 0x8A}, 2, "ピ"}, 252 | {{0xF1, 0x8B}, 2, "プ"}, 253 | {{0xF1, 0x8C}, 2, "ペ"}, 254 | {{0xF1, 0x8D}, 2, "ポ"}, 255 | }; 256 | 257 | const mapping us_mapping[] = { 258 | {{0x00}, 1, "0"}, 259 | {{0x01}, 1, "1"}, 260 | {{0x02}, 1, "2"}, 261 | {{0x03}, 1, "3"}, 262 | {{0x04}, 1, "4"}, 263 | {{0x05}, 1, "5"}, 264 | {{0x06}, 1, "6"}, 265 | {{0x07}, 1, "7"}, 266 | {{0x08}, 1, "8"}, 267 | {{0x09}, 1, "9"}, 268 | {{0x0A}, 1, "A"}, 269 | {{0x0B}, 1, "B"}, 270 | {{0x0C}, 1, "C"}, 271 | {{0x0D}, 1, "D"}, 272 | {{0x0E}, 1, "E"}, 273 | {{0x0F}, 1, "F"}, 274 | {{0x10}, 1, "G"}, 275 | {{0x11}, 1, "H"}, 276 | {{0x12}, 1, "I"}, 277 | {{0x13}, 1, "J"}, 278 | {{0x14}, 1, "K"}, 279 | {{0x15}, 1, "L"}, 280 | {{0x16}, 1, "M"}, 281 | {{0x17}, 1, "N"}, 282 | {{0x18}, 1, "O"}, 283 | {{0x19}, 1, "P"}, 284 | {{0x1A}, 1, "Q"}, 285 | {{0x1B}, 1, "R"}, 286 | {{0x1C}, 1, "S"}, 287 | {{0x1D}, 1, "T"}, 288 | {{0x1E}, 1, "U"}, 289 | {{0x1F}, 1, "V"}, 290 | {{0x20}, 1, "W"}, 291 | {{0x21}, 1, "X"}, 292 | {{0x22}, 1, "Y"}, 293 | {{0x23}, 1, "Z"}, 294 | {{0x24}, 1, "a"}, 295 | {{0x25}, 1, "b"}, 296 | {{0x26}, 1, "c"}, 297 | {{0x27}, 1, "d"}, 298 | {{0x28}, 1, "e"}, 299 | {{0x29}, 1, "f"}, 300 | {{0x2A}, 1, "g"}, 301 | {{0x2B}, 1, "h"}, 302 | {{0x2C}, 1, "i"}, 303 | {{0x2D}, 1, "j"}, 304 | {{0x2E}, 1, "k"}, 305 | {{0x2F}, 1, "l"}, 306 | {{0x30}, 1, "m"}, 307 | {{0x31}, 1, "n"}, 308 | {{0x32}, 1, "o"}, 309 | {{0x33}, 1, "p"}, 310 | {{0x34}, 1, "q"}, 311 | {{0x35}, 1, "r"}, 312 | {{0x36}, 1, "s"}, 313 | {{0x37}, 1, "t"}, 314 | {{0x38}, 1, "u"}, 315 | {{0x39}, 1, "v"}, 316 | {{0x3A}, 1, "w"}, 317 | {{0x3B}, 1, "x"}, 318 | {{0x3C}, 1, "y"}, 319 | {{0x3D}, 1, "z"}, 320 | {{0x3E}, 1, "'"}, 321 | {{0x3F}, 1, "."}, 322 | {{0x50}, 1, "^"}, // up 323 | {{0x51}, 1, "|"}, // down 324 | {{0x52}, 1, "<"}, // left 325 | {{0x53}, 1, ">"}, // right 326 | {{0x54}, 1, "[A]"}, 327 | {{0x55}, 1, "[B]"}, 328 | {{0x56}, 1, "[C]"}, 329 | {{0x57}, 1, "[Z]"}, 330 | {{0x58}, 1, "[R]"}, 331 | {{0x6F}, 1, ","}, 332 | {{0xD0}, 1, "/"}, 333 | {{0xD1}, 1, "the"}, 334 | {{0xD2}, 1, "you"}, 335 | {{0x9E}, 1, " "}, 336 | {{0x9F}, 1, "-"}, 337 | {{0xE1}, 1, "("}, 338 | {{0xE2}, 1, "\""}, 339 | {{0xE3}, 1, ")"}, 340 | {{0xE4}, 1, "+"}, 341 | {{0xE5}, 1, "&"}, 342 | {{0xE6}, 1, ":"}, 343 | {{0xF2}, 1, "!"}, 344 | {{0xF4}, 1, "?"}, 345 | {{0xF5}, 1, "{"}, // opening " 346 | {{0xF6}, 1, "}"}, // closing " 347 | {{0xF7}, 1, "~"}, 348 | {{0xF9}, 1, "$"}, 349 | {{0xFA}, 1, "*"}, // filled star 350 | {{0xFB}, 1, "[x]"}, 351 | {{0xFC}, 1, ";"}, 352 | {{0xFD}, 1, "#"}, // open star 353 | {{0xFE}, 1, "\n"}, 354 | }; 355 | 356 | static void print_usage(void) 357 | { 358 | printf("Usage: sm64text [-b] [-t] [-r REGION] [-d] INPUT\n" 359 | "\n" 360 | "sm64text v" SM64TEXT_VERSION ": SM64 dialog text encoder/decoder\n" 361 | "\n" 362 | "Optional arguments:\n" 363 | " -b INPUT is bytes to convert to utf8 text\n" 364 | " -t INPUT is utf8 text convert to SM64 dialog encoded bytes (appends 0xFF end of string)\n" 365 | " -r REGION region to use: U or J (default: J)\n" 366 | " -d dump table to format suitable for armips\n" 367 | "Required arguments:\n" 368 | " INPUT either text string (if using -t) or hex byte pairs (if using -b)\n" 369 | "\n" 370 | "Examples:\n" 371 | " $ sm64text -r u 2B 28 2F 2F 32\n" 372 | " hello\n" 373 | " $ sm64text -r u -t hello\n" 374 | " 0x2B, 0x28, 0x2F, 0x2F, 0x32, 0xFF\n"); 375 | } 376 | 377 | static int is_hex(char val) 378 | { 379 | return ((val >= '0' && val <= '9') || (val >= 'A' && val <= 'F') || (val >= 'a' && val <= 'f')); 380 | } 381 | 382 | static size_t parse_bytes(uint8_t *bytes, const char *hex) 383 | { 384 | size_t count = 0; 385 | int i = 0; 386 | while (hex[i]) { 387 | if (is_hex(hex[i]) && is_hex(hex[i+1])) { 388 | const char val[3] = {hex[i], hex[i+1], '\0'}; 389 | bytes[count++] = strtoul(val, NULL, 16); 390 | i += 2; 391 | } else { 392 | i++; 393 | } 394 | } 395 | return count; 396 | } 397 | 398 | static void parse_config(config *conf, int argc, char *argv[]) 399 | { 400 | int c; 401 | while ((c = getopt(argc, argv, "bdtr:")) != -1) { 402 | switch (c) { 403 | case 'b': 404 | conf->conv = CONVERT_BYTES; 405 | break; 406 | case 'd': 407 | conf->conv = DUMP_TABLE; 408 | break; 409 | case 't': 410 | conf->conv = CONVERT_TEXT; 411 | break; 412 | case 'r': 413 | switch (tolower(optarg[0])) { 414 | case 'j': conf->reg = REGION_J; break; 415 | case 'u': conf->reg = REGION_U; break; 416 | default: print_usage(); exit(1); break; 417 | } 418 | break; 419 | case '?': 420 | default: 421 | print_usage(); 422 | exit(1); 423 | } 424 | } 425 | 426 | switch (conf->conv) { 427 | case CONVERT_BYTES: 428 | if (optind < argc) { 429 | size_t allocation = 1024; 430 | uint8_t *bytes = malloc(allocation); 431 | size_t count = 0; 432 | for (int i = optind; i < argc; i++) { 433 | count += parse_bytes(&bytes[count], argv[i]); 434 | if (count >= allocation) { 435 | allocation *= 2; 436 | bytes = realloc(bytes, allocation); 437 | } 438 | } 439 | conf->bytes = bytes; 440 | conf->length = count; 441 | } 442 | break; 443 | case CONVERT_TEXT: 444 | if (optind < argc) { 445 | conf->text = argv[optind]; 446 | } 447 | break; 448 | default: 449 | break; 450 | } 451 | } 452 | 453 | // find longest byte pattern in mapping table that matches 'bytes' 454 | int lookup_longest_bytes(const mapping *map, size_t map_count, const uint8_t *bytes) 455 | { 456 | int best_idx = -1; 457 | for (size_t i = 0; i < map_count; i++) { 458 | if (!memcmp(bytes, map[i].bytes, map[i].blen)) { 459 | if (best_idx < 0 || map[i].blen > map[best_idx].blen) { 460 | best_idx = i; 461 | } 462 | } 463 | } 464 | return best_idx; 465 | } 466 | 467 | // decode bytes to text for given mapping table 468 | void print_text(const mapping *map, size_t map_count, uint8_t *bytes, int length) 469 | { 470 | int i = 0; 471 | while (i < length && bytes[i] != sm64_string_terminator) { 472 | int best = lookup_longest_bytes(map, map_count, &bytes[i]); 473 | if (best < 0) { 474 | fprintf(stderr, "Error: couldn't find %02X\n", bytes[i]); 475 | exit(1); 476 | } else { 477 | printf("%s", map[best].text); 478 | i += map[best].blen; 479 | } 480 | } 481 | } 482 | 483 | // find longest matching text in mapping tables that matches 'text' 484 | int lookup_longest_text(const mapping *map, size_t map_count, const char *text) 485 | { 486 | int best_idx = -1; 487 | size_t best_len = 0; 488 | for (size_t i = 0; i < map_count; i++) { 489 | size_t len = strlen(map[i].text); 490 | if (!strncmp(text, map[i].text, len)) { 491 | if (best_idx < 0 || len > best_len) { 492 | best_idx = i; 493 | best_len = len; 494 | } 495 | } 496 | } 497 | return best_idx; 498 | } 499 | 500 | void print_bytes(const mapping *map, size_t map_count, const char *text) 501 | { 502 | size_t i = 0; 503 | int first = 1; 504 | size_t len = strlen(text); 505 | while (i < len) { 506 | int best = lookup_longest_text(map, map_count, &text[i]); 507 | if (best < 0) { 508 | fprintf(stderr, "Error: couldn't find %c\n", text[i]); 509 | exit(1); 510 | } else { 511 | for (size_t b = 0; b < map[best].blen; b++) { 512 | if (!first) printf(", "); 513 | printf("0x%02X", map[best].bytes[b]); 514 | first = 0; 515 | } 516 | i += strlen(map[best].text); 517 | } 518 | } 519 | printf(", 0x%02X\n", sm64_string_terminator); 520 | } 521 | 522 | static void dump_table(const mapping *map, size_t map_count) 523 | { 524 | for (size_t i = 0; i < map_count; i++) { 525 | for (size_t b = 0; b < map[i].blen; b++) { 526 | printf("%02X", map[i].bytes[b]); 527 | } 528 | printf("="); 529 | switch (map[i].text[0]) { 530 | case '\n': printf("\\n"); break; 531 | case '\r': printf("\\r"); break; 532 | default: printf("%s", map[i].text); break; 533 | } 534 | printf("\n"); 535 | } 536 | printf("/%02X\n", sm64_string_terminator); 537 | } 538 | 539 | int main(int argc, char *argv[]) 540 | { 541 | config conf = default_config; 542 | const mapping *map = us_mapping; 543 | size_t map_count = COUNT_OF(us_mapping); 544 | 545 | parse_config(&conf, argc, argv); 546 | if (conf.conv != DUMP_TABLE && !conf.bytes) { 547 | print_usage(); 548 | return 1; 549 | } 550 | 551 | // select mapping table 552 | switch (conf.reg) { 553 | case REGION_U: 554 | map = us_mapping; 555 | map_count = COUNT_OF(us_mapping); 556 | break; 557 | case REGION_J: 558 | map = j_mapping; 559 | map_count = COUNT_OF(j_mapping); 560 | break; 561 | } 562 | 563 | // run conversion 564 | switch (conf.conv) { 565 | case CONVERT_BYTES: 566 | print_text(map, map_count, conf.bytes, conf.length); 567 | printf("\n"); 568 | break; 569 | case CONVERT_TEXT: 570 | print_bytes(map, map_count, conf.text); 571 | break; 572 | case DUMP_TABLE: 573 | dump_table(map, map_count); 574 | break; 575 | } 576 | 577 | return 0; 578 | } 579 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #if defined(_MSC_VER) || defined(__MINGW32__) 9 | #include 10 | #include 11 | #else 12 | #include 13 | #include 14 | #endif 15 | 16 | #include "utils.h" 17 | 18 | // global verbosity setting 19 | int g_verbosity = 0; 20 | 21 | int read_s16_be(unsigned char *buf) 22 | { 23 | unsigned tmp = read_u16_be(buf); 24 | int ret; 25 | if (tmp > 0x7FFF) { 26 | ret = -((int)0x10000 - (int)tmp); 27 | } else { 28 | ret = (int)tmp; 29 | } 30 | return ret; 31 | } 32 | 33 | float read_f32_be(unsigned char *buf) 34 | { 35 | union {uint32_t i; float f;} ret; 36 | ret.i = read_u32_be(buf); 37 | return ret.f; 38 | } 39 | 40 | int is_power2(unsigned int val) 41 | { 42 | while (((val & 1) == 0) && (val > 1)) { 43 | val >>= 1; 44 | } 45 | return (val == 1); 46 | } 47 | 48 | void fprint_hex(FILE *fp, const unsigned char *buf, int length) 49 | { 50 | int i; 51 | for (i = 0; i < length; i++) { 52 | fprint_byte(fp, buf[i]); 53 | fputc(' ', fp); 54 | } 55 | } 56 | 57 | void fprint_hex_source(FILE *fp, const unsigned char *buf, int length) 58 | { 59 | int i; 60 | for (i = 0; i < length; i++) { 61 | if (i > 0) fputs(", ", fp); 62 | fputs("0x", fp); 63 | fprint_byte(fp, buf[i]); 64 | } 65 | } 66 | 67 | void print_hex(const unsigned char *buf, int length) 68 | { 69 | fprint_hex(stdout, buf, length); 70 | } 71 | 72 | void swap_bytes(unsigned char *data, long length) 73 | { 74 | long i; 75 | unsigned char tmp; 76 | for (i = 0; i < length; i += 2) { 77 | tmp = data[i]; 78 | data[i] = data[i+1]; 79 | data[i+1] = tmp; 80 | } 81 | } 82 | 83 | void reverse_endian(unsigned char *data, long length) 84 | { 85 | long i; 86 | unsigned char tmp; 87 | for (i = 0; i < length; i += 4) { 88 | tmp = data[i]; 89 | data[i] = data[i+3]; 90 | data[i+3] = tmp; 91 | tmp = data[i+1]; 92 | data[i+1] = data[i+2]; 93 | data[i+2] = tmp; 94 | } 95 | } 96 | 97 | long filesize(const char *filename) 98 | { 99 | struct stat st; 100 | 101 | if (stat(filename, &st) == 0) { 102 | return st.st_size; 103 | } 104 | 105 | return -1; 106 | } 107 | 108 | void touch_file(const char *filename) 109 | { 110 | int fd; 111 | //fd = open(filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666); 112 | fd = open(filename, O_WRONLY|O_CREAT, 0666); 113 | if (fd >= 0) { 114 | utime(filename, NULL); 115 | close(fd); 116 | } 117 | } 118 | 119 | long read_file(const char *file_name, unsigned char **data) 120 | { 121 | FILE *in; 122 | unsigned char *in_buf = NULL; 123 | long file_size; 124 | long bytes_read; 125 | in = fopen(file_name, "rb"); 126 | if (in == NULL) { 127 | return -1; 128 | } 129 | 130 | // allocate buffer to read from offset to end of file 131 | fseek(in, 0, SEEK_END); 132 | file_size = ftell(in); 133 | 134 | // sanity check 135 | if (file_size > 256*MB) { 136 | return -2; 137 | } 138 | 139 | in_buf = malloc(file_size); 140 | fseek(in, 0, SEEK_SET); 141 | 142 | // read bytes 143 | bytes_read = fread(in_buf, 1, file_size, in); 144 | if (bytes_read != file_size) { 145 | return -3; 146 | } 147 | 148 | fclose(in); 149 | *data = in_buf; 150 | return bytes_read; 151 | } 152 | 153 | long write_file(const char *file_name, unsigned char *data, long length) 154 | { 155 | FILE *out; 156 | long bytes_written; 157 | // open output file 158 | out = fopen(file_name, "wb"); 159 | if (out == NULL) { 160 | perror(file_name); 161 | return -1; 162 | } 163 | bytes_written = fwrite(data, 1, length, out); 164 | fclose(out); 165 | return bytes_written; 166 | } 167 | 168 | void generate_filename(const char *in_name, char *out_name, char *extension) 169 | { 170 | char tmp_name[FILENAME_MAX]; 171 | int len; 172 | int i; 173 | strcpy(tmp_name, in_name); 174 | len = strlen(tmp_name); 175 | for (i = len - 1; i > 0; i--) { 176 | if (tmp_name[i] == '.') { 177 | break; 178 | } 179 | } 180 | if (i <= 0) { 181 | i = len; 182 | } 183 | tmp_name[i] = '\0'; 184 | sprintf(out_name, "%s.%s", tmp_name, extension); 185 | } 186 | 187 | char *basename(const char *name) 188 | { 189 | const char *base = name; 190 | while (*name) { 191 | if (*name++ == '/') { 192 | base = name; 193 | } 194 | } 195 | return (char *)base; 196 | } 197 | 198 | void make_dir(const char *dir_name) 199 | { 200 | struct stat st = {0}; 201 | if (stat(dir_name, &st) == -1) { 202 | mkdir(dir_name, 0755); 203 | } 204 | } 205 | 206 | long copy_file(const char *src_name, const char *dst_name) 207 | { 208 | unsigned char *buf; 209 | long bytes_written; 210 | long bytes_read; 211 | 212 | bytes_read = read_file(src_name, &buf); 213 | 214 | if (bytes_read > 0) { 215 | bytes_written = write_file(dst_name, buf, bytes_read); 216 | if (bytes_written != bytes_read) { 217 | bytes_read = -1; 218 | } 219 | free(buf); 220 | } 221 | 222 | return bytes_read; 223 | } 224 | 225 | void dir_list_ext(const char *dir, const char *extension, dir_list *list) 226 | { 227 | char *pool; 228 | char *pool_ptr; 229 | struct dirent *entry; 230 | DIR *dfd; 231 | int idx; 232 | 233 | dfd = opendir(dir); 234 | if (dfd == NULL) { 235 | ERROR("Can't open '%s'\n", dir); 236 | exit(1); 237 | } 238 | 239 | pool = malloc(FILENAME_MAX * MAX_DIR_FILES); 240 | pool_ptr = pool; 241 | 242 | idx = 0; 243 | while ((entry = readdir(dfd)) != NULL && idx < MAX_DIR_FILES) { 244 | if (!extension || str_ends_with(entry->d_name, extension)) { 245 | sprintf(pool_ptr, "%s/%s", dir, entry->d_name); 246 | list->files[idx] = pool_ptr; 247 | pool_ptr += strlen(pool_ptr) + 1; 248 | idx++; 249 | } 250 | } 251 | list->count = idx; 252 | 253 | closedir(dfd); 254 | } 255 | 256 | void dir_list_free(dir_list *list) 257 | { 258 | // assume first entry in array is allocated 259 | if (list->files[0]) { 260 | free(list->files[0]); 261 | list->files[0] = NULL; 262 | } 263 | } 264 | 265 | int str_ends_with(const char *str, const char *suffix) 266 | { 267 | if (!str || !suffix) { 268 | return 0; 269 | } 270 | size_t len_str = strlen(str); 271 | size_t len_suffix = strlen(suffix); 272 | if (len_suffix > len_str) { 273 | return 0; 274 | } 275 | return (0 == strncmp(str + len_str - len_suffix, suffix, len_suffix)); 276 | } 277 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H_ 2 | #define UTILS_H_ 3 | 4 | #include 5 | 6 | // defines 7 | 8 | // printing size_t varies by compiler 9 | #if defined(_MSC_VER) || defined(__MINGW32__) 10 | #define SIZE_T_FORMAT "%Iu" 11 | #else 12 | #define SIZE_T_FORMAT "%zu" 13 | #endif 14 | 15 | #define KB 1024 16 | #define MB (1024 * KB) 17 | 18 | // number of elements in statically declared array 19 | #define DIM(S_ARR_) (sizeof(S_ARR_) / sizeof(S_ARR_[0])) 20 | 21 | #define MIN(A_, B_) ((A_) < (B_) ? (A_) : (B_)) 22 | #define MAX(A_, B_) ((A_) > (B_) ? (A_) : (B_)) 23 | 24 | // align value to N-byte boundary 25 | #define ALIGN(VAL_, ALIGNMENT_) (((VAL_) + ((ALIGNMENT_) - 1)) & ~((ALIGNMENT_) - 1)) 26 | 27 | // read/write u32/16 big/little endian 28 | #define read_u32_be(buf) (unsigned int)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3])) 29 | #define read_u32_le(buf) (unsigned int)(((buf)[1] << 24) + ((buf)[0] << 16) + ((buf)[3] << 8) + ((buf)[2])) 30 | #define write_u32_be(buf, val) do { \ 31 | (buf)[0] = ((val) >> 24) & 0xFF; \ 32 | (buf)[1] = ((val) >> 16) & 0xFF; \ 33 | (buf)[2] = ((val) >> 8) & 0xFF; \ 34 | (buf)[3] = (val) & 0xFF; \ 35 | } while(0) 36 | #define read_u16_be(buf) (((buf)[0] << 8) + ((buf)[1])) 37 | #define write_u16_be(buf, val) do { \ 38 | (buf)[0] = ((val) >> 8) & 0xFF; \ 39 | (buf)[1] = ((val)) & 0xFF; \ 40 | } while(0) 41 | 42 | // print nibbles and bytes 43 | #define fprint_nibble(FP, NIB_) fputc((NIB_) < 10 ? ('0' + (NIB_)) : ('A' + (NIB_) - 0xA), FP) 44 | #define fprint_byte(FP, BYTE_) do { \ 45 | fprint_nibble(FP, (BYTE_) >> 4); \ 46 | fprint_nibble(FP, (BYTE_) & 0x0F); \ 47 | } while(0) 48 | #define print_nibble(NIB_) fprint_nibble(stdout, NIB_) 49 | #define print_byte(BYTE_) fprint_byte(stdout, BYTE_) 50 | 51 | // Windows compatibility 52 | #if defined(_MSC_VER) || defined(__MINGW32__) 53 | #include 54 | #define mkdir(DIR_, PERM_) _mkdir(DIR_) 55 | #ifndef strcasecmp 56 | #define strcasecmp(A, B) stricmp(A, B) 57 | #endif 58 | #endif 59 | 60 | // typedefs 61 | 62 | #define MAX_DIR_FILES 128 63 | typedef struct 64 | { 65 | char *files[MAX_DIR_FILES]; 66 | int count; 67 | } dir_list; 68 | 69 | // global verbosity setting 70 | extern int g_verbosity; 71 | 72 | #define ERROR(...) fprintf(stderr, __VA_ARGS__) 73 | #define INFO(...) if (g_verbosity) printf(__VA_ARGS__) 74 | #define INFO_HEX(...) if (g_verbosity) print_hex(__VA_ARGS__) 75 | 76 | // functions 77 | 78 | // convert two bytes in big-endian to signed int 79 | int read_s16_be(unsigned char *buf); 80 | 81 | // convert four bytes in big-endian to float 82 | float read_f32_be(unsigned char *buf); 83 | 84 | // determine if value is power of 2 85 | // returns 1 if val is power of 2, 0 otherwise 86 | int is_power2(unsigned int val); 87 | 88 | // print buffer as hex bytes 89 | // fp: file pointer 90 | // buf: buffer to read bytes from 91 | // length: length of buffer to print 92 | void fprint_hex(FILE *fp, const unsigned char *buf, int length); 93 | void fprint_hex_source(FILE *fp, const unsigned char *buf, int length); 94 | void print_hex(const unsigned char *buf, int length); 95 | 96 | // perform byteswapping to convert from v64 to z64 ordering 97 | void swap_bytes(unsigned char *data, long length); 98 | 99 | // reverse endian to convert from n64 to z64 ordering 100 | void reverse_endian(unsigned char *data, long length); 101 | 102 | // get size of file without opening it; 103 | // returns file size or negative on error 104 | long filesize(const char *file_name); 105 | 106 | // update file timestamp to now, creating it if it doesn't exist 107 | void touch_file(const char *filename); 108 | 109 | // read entire contents of file into buffer 110 | // returns file size or negative on error 111 | long read_file(const char *file_name, unsigned char **data); 112 | 113 | // write buffer to file 114 | // returns number of bytes written out or -1 on failure 115 | long write_file(const char *file_name, unsigned char *data, long length); 116 | 117 | // generate an output file name from input name by replacing file extension 118 | // in_name: input file name 119 | // out_name: buffer to write output name in 120 | // extension: new file extension to use 121 | void generate_filename(const char *in_name, char *out_name, char *extension); 122 | 123 | // extract base filename from file path 124 | // name: path to file 125 | // returns just the file name after the last '/' 126 | char *basename(const char *name); 127 | 128 | // make a directory if it doesn't exist 129 | // dir_name: name of the directory 130 | void make_dir(const char *dir_name); 131 | 132 | // copy a file from src_name to dst_name. will not make directories 133 | // src_name: source file name 134 | // dst_name: destination file name 135 | long copy_file(const char *src_name, const char *dst_name); 136 | 137 | // list a directory, optionally filtering files by extension 138 | // dir: directory to list files in 139 | // extension: extension to filter files by (NULL if no filtering) 140 | // list: output list and count 141 | void dir_list_ext(const char *dir, const char *extension, dir_list *list); 142 | 143 | // free associated date from a directory list 144 | // list: directory list filled in by dir_list_ext() call 145 | void dir_list_free(dir_list *list); 146 | 147 | // determine if a string ends with another string 148 | // str: string to check if ends with 'suffix' 149 | // suffix: string to see if 'str' ends with 150 | // returns 1 if 'str' ends with 'suffix' 151 | int str_ends_with(const char *str, const char *suffix); 152 | 153 | #endif // UTILS_H_ 154 | --------------------------------------------------------------------------------