diff --git a/.gitignore b/.gitignore index feb1637..bf12fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Object files *.o *.obj +*.d # Precompiled Headers *.gch @@ -18,3 +19,4 @@ # Executables *.exe +bin diff --git a/Makefile b/Makefile index 1e96976..d5e8a1e 100644 --- a/Makefile +++ b/Makefile @@ -41,17 +41,28 @@ GEO_SRC_FILES := sm64geo.c \ GRAPHICS_SRC_FILES := n64graphics.c \ utils.c +MI0_SRC_FILES := libmio0.c \ + libmio0.h + SPLIT_SRC_FILES := blast.c \ libmio0.c \ libsfx.c \ mipsdisasm.c \ n64graphics.c \ - n64split.c \ + n64split/n64split.c \ + n64split/n64split.sm64.geo.c \ + n64split/n64split.sm64.behavior.c \ + n64split/n64split.sm64.collision.c \ + n64split/n64split.sound.c \ strutils.c \ utils.c \ yamlconfig.c +WALK_SRC_FILES := sm64walk.c + OBJ_DIR = ./obj +BIN_DIR = ./bin +SPLIT_DIR = $(OBJ_DIR)/n64split ##################### Compiler Options ####################### @@ -62,7 +73,7 @@ CC = $(CROSS)gcc LD = $(CC) AR = $(CROSS)ar -INCLUDES = -I./ext +INCLUDES = -I. -I./ext DEFS = # Release flags CFLAGS = -Wall -Wextra -Wno-format-overflow -O2 -ffunction-sections -fdata-sections $(INCLUDES) $(DEFS) -MMD @@ -80,12 +91,16 @@ EXTEND_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(EXTEND_SRC_FILES:.c=.o)) F3D_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(F3D_SRC_FILES:.c=.o)) F3D2OBJ_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(F3D2OBJ_SRC_FILES:.c=.o)) GEO_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(GEO_SRC_FILES:.c=.o)) +MI0_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(MI0_SRC_FILES:.c=.o)) SPLIT_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(SPLIT_SRC_FILES:.c=.o)) -OBJ_FILES = $(LIB_OBJ_FILES) $(EXTEND_OBJ_FILES) $(COMPRESS_OBJ_FILES) \ - $(SPLIT_OBJ_FILES) $(CKSUM_OBJ_FILES) $(F3D_OBJ_FILES) \ - $(F3D2OBJ_OBJ_FILES) $(GEO_OBJ_FILES) +WALK_OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(WALK_SRC_FILES:.c=.o)) +OBJ_FILES = $(LIB_OBJ_FILES) $(CKSUM_OBJ_FILES) $(COMPRESS_OBJ_FILES) \ + $(EXTEND_OBJ_FILES) $(F3D_OBJ_FILES) $(F3D2OBJ_OBJ_FILES) \ + $(GEO_OBJ_FILES) $(MI0_OBJ_FILES) $(SPLIT_OBJ_FILES) \ + $(WALK_OBJ_FILES) DEP_FILES = $(OBJ_FILES:.o=.d) + ######################## Targets ############################# default: all @@ -96,6 +111,8 @@ all: $(EXTEND_TARGET) $(COMPRESS_TARGET) $(MIO0_TARGET) $(CKSUM_TARGET) \ $(OBJ_DIR)/%.o: %.c @[ -d $(OBJ_DIR) ] || mkdir -p $(OBJ_DIR) + @[ -d $(BIN_DIR) ] || mkdir -p $(BIN_DIR) + @[ -d $(SPLIT_DIR) ] || mkdir -p $(SPLIT_DIR) $(CC) $(CFLAGS) -o $@ -c $< $(SM64_LIB): $(LIB_OBJ_FILES) @@ -103,55 +120,57 @@ $(SM64_LIB): $(LIB_OBJ_FILES) $(AR) rcs $@ $^ $(CKSUM_TARGET): $(CKSUM_OBJ_FILES) $(SM64_LIB) - $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(LIBS) $(COMPRESS_TARGET): $(COMPRESS_OBJ_FILES) $(SM64_LIB) - $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(LIBS) $(EXTEND_TARGET): $(EXTEND_OBJ_FILES) $(SM64_LIB) - $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(LIBS) $(F3D_TARGET): $(F3D_OBJ_FILES) - $(LD) $(LDFLAGS) -o $@ $^ + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(F3D2OBJ_TARGET): $(F3D2OBJ_OBJ_FILES) - $(LD) $(LDFLAGS) -o $@ $^ + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(GEO_TARGET): $(GEO_OBJ_FILES) - $(LD) $(LDFLAGS) -o $@ $^ + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(GRAPHICS_TARGET): $(GRAPHICS_SRC_FILES) - $(CC) $(CFLAGS) -DN64GRAPHICS_STANDALONE $^ $(LDFLAGS) -o $@ + $(CC) $(CFLAGS) -DN64GRAPHICS_STANDALONE $^ $(LDFLAGS) -o $(BIN_DIR)/$@ -$(MIO0_TARGET): libmio0.c libmio0.h - $(CC) $(CFLAGS) -DMIO0_STANDALONE $(LDFLAGS) -o $@ $< +$(MIO0_TARGET): $(MI0_SRC_FILES) + $(CC) $(CFLAGS) -DMIO0_STANDALONE $(LDFLAGS) -o $(BIN_DIR)/$@ $< $(DISASM_TARGET): $(DISASM_SRC_FILES) - $(CC) $(CFLAGS) -DMIPSDISASM_STANDALONE $^ $(LDFLAGS) -o $@ -lcapstone + $(CC) $(CFLAGS) -DMIPSDISASM_STANDALONE $^ $(LDFLAGS) -o $(BIN_DIR)/$@ -lcapstone $(SPLIT_TARGET): $(SPLIT_OBJ_FILES) - $(LD) $(LDFLAGS) -o $@ $^ $(SPLIT_LIBS) + $(LD) $(LDFLAGS) -o $(BIN_DIR)/$@ $^ $(SPLIT_LIBS) -$(WALK_TARGET): sm64walk.c $(SM64_LIB) - $(CC) $(CFLAGS) -o $@ $^ +$(WALK_TARGET): $(WALK_SRC_FILES) $(SM64_LIB) + $(CC) $(CFLAGS) -o $(BIN_DIR)/$@ $^ rawmips: rawmips.c utils.c - $(CC) $(CFLAGS) -o $@ $^ -lcapstone + $(CC) $(CFLAGS) -o $(BIN_DIR)/$@ $^ -lcapstone clean: - rm -f $(OBJ_FILES) $(DEP_FILES) $(SM64_LIB) $(MIO0_TARGET) - rm -f $(CKSUM_TARGET) $(CKSUM_TARGET).exe - rm -f $(COMPRESS_TARGET) $(COMPRESS_TARGET).exe - rm -f $(DISASM_TARGET) $(DISASM_TARGET).exe - rm -f $(EXTEND_TARGET) $(EXTEND_TARGET).exe - rm -f $(F3D_TARGET) $(F3D_TARGET).exe - rm -f $(F3D2OBJ_TARGET) $(F3D2OBJ_TARGET).exe - rm -f $(GEO_TARGET) $(GEO_TARGET).exe - rm -f $(MIO0_TARGET) $(MIO0_TARGET).exe - rm -f $(GRAPHICS_TARGET) $(GRAPHICS_TARGET).exe - rm -f $(SPLIT_TARGET) $(SPLIT_TARGET).exe - rm -f $(WALK_TARGET) $(WALK_TARGET).exe + rm -f $(OBJ_FILES) $(DEP_FILES) $(SM64_LIB) $(MIO0_TARGET) $(BIN_DIR)/*.d + rm -f $(BIN_DIR)/$(CKSUM_TARGET) $(BIN_DIR)/$(CKSUM_TARGET).exe + rm -f $(BIN_DIR)/$(COMPRESS_TARGET) $(BIN_DIR)/$(COMPRESS_TARGET).exe + rm -f $(BIN_DIR)/$(DISASM_TARGET) $(BIN_DIR)/$(DISASM_TARGET).exe + rm -f $(BIN_DIR)/$(EXTEND_TARGET) $(BIN_DIR)/$(EXTEND_TARGET).exe + rm -f $(BIN_DIR)/$(F3D_TARGET) $(BIN_DIR)/$(F3D_TARGET).exe + rm -f $(BIN_DIR)/$(F3D2OBJ_TARGET) $(BIN_DIR)/$(F3D2OBJ_TARGET).exe + rm -f $(BIN_DIR)/$(GEO_TARGET) $(BIN_DIR)/$(GEO_TARGET).exe + rm -f $(BIN_DIR)/$(MIO0_TARGET) $(BIN_DIR)/$(MIO0_TARGET).exe + rm -f $(BIN_DIR)/$(GRAPHICS_TARGET) $(BIN_DIR)/$(GRAPHICS_TARGET).exe + rm -f $(BIN_DIR)/$(SPLIT_TARGET) $(BIN_DIR)/$(SPLIT_TARGET).exe + rm -f $(BIN_DIR)/$(WALK_TARGET) $(BIN_DIR)/$(WALK_TARGET).exe + -@[ -d $(SPLIT_DIR) ] && rmdir --ignore-fail-on-non-empty $(SPLIT_DIR) -@[ -d $(OBJ_DIR) ] && rmdir --ignore-fail-on-non-empty $(OBJ_DIR) + -@[ -d $(BIN_DIR) ] && rmdir --ignore-fail-on-non-empty $(BIN_DIR) .PHONY: all clean default diff --git a/README.md b/README.md index 2977e0c..f946a2f 100644 --- a/README.md +++ b/README.md @@ -105,3 +105,13 @@ There are many other smaller tools included to help with SM64 hacking. They are ## License MIT License. Copyright 2015 queueRAM. + +## Building + +### MacOSX +``` +git pull --recurse-submodules +brew install capstone +brew install libyaml +export C_INCLUDE_PATH=../sm64tools:/usr/local/Cellar/capstone/4.0.1/include/:/usr/local/Cellar/libyaml/0.2.1/include && export LIBRARY_PATH=/usr/local/opt/capstone/lib/:/usr/local/Cellar/libyaml/0.2.1/lib:/usr/local/Cellar/libpng/1.6.36/lib && make +``` diff --git a/config.h b/config.h index a4da52c..b50a68d 100644 --- a/config.h +++ b/config.h @@ -35,7 +35,7 @@ typedef enum typedef struct _label { unsigned int ram_addr; - char name[128]; + char name[2048]; } label; typedef struct _texture @@ -50,11 +50,12 @@ typedef struct _texture typedef struct _split_section { - char label[128]; + char label[512]; unsigned int start; unsigned int end; unsigned int vaddr; section_type type; + char section_name[512]; int subtype; diff --git a/ext/stb b/ext/stb index e6afb9c..2c2908f 160000 --- a/ext/stb +++ b/ext/stb @@ -1 +1 @@ -Subproject commit e6afb9cbae4064da8c3e69af3ff5c4629579c1d2 +Subproject commit 2c2908f50515dcd939f24be261c3ccbcd277bb49 diff --git a/mipsdisasm.c b/mipsdisasm.c index 067389d..3d59826 100644 --- a/mipsdisasm.c +++ b/mipsdisasm.c @@ -16,6 +16,7 @@ typedef struct { char name[60]; unsigned int vaddr; + char prefix[FILENAME_MAX]; } asm_label; typedef struct @@ -231,7 +232,7 @@ static void disassemble_block(unsigned char *data, unsigned int length, unsigned unsigned int jal_target = (unsigned int)insn[i].operands[0].imm; // create label if one does not exist if (labels_find(&state->globals, jal_target) < 0) { - char label_name[32]; + char label_name[FILENAME_MAX]; sprintf(label_name, "func_%08X", jal_target); labels_add(&state->globals, label_name, jal_target); } @@ -449,6 +450,7 @@ void mipsdisasm_pass2(FILE *out, disasm_state *state, unsigned int offset) while ( (local_idx < block->locals.count) && (vaddr > block->locals.labels[local_idx].vaddr) ) { local_idx++; } + char previous_instruction[16]; for (int i = 0; i < block->instruction_count; i++) { disasm_data *insn = &block->instructions[i]; // newline between functions @@ -465,13 +467,37 @@ void mipsdisasm_pass2(FILE *out, disasm_state *state, unsigned int offset) fprintf(out, "%s:\n", block->locals.labels[local_idx].name); local_idx++; } + // write out bytes as comment fprintf(out, "/* %06X %08X %02X%02X%02X%02X */ ", offset, vaddr, insn->bytes[0], insn->bytes[1], insn->bytes[2], insn->bytes[3]); // indent the lines after a jump or branch if (indent) { indent = 0; fputc(' ', out); } - if (insn->is_jump) { + if (strncmp(insn->mnemonic,"movf",4) == 0 || strncmp(insn->mnemonic,"lsa",4) == 0 || strncmp(insn->mnemonic,"dlsa",4) == 0 + || strncmp(insn->mnemonic,"movn",4) == 0 || strncmp(insn->mnemonic,"ext",4) == 0 || strncmp(insn->mnemonic,"movt",4) == 0 + || strncmp(insn->mnemonic,"movz",4) == 0 || strncmp(insn->mnemonic,"bbit",4) == 0 || strncmp(insn->mnemonic,"pref",4) == 0 + || strncmp(insn->mnemonic,"synci",5) == 0 || strncmp(insn->mnemonic,"ld.b",4) == 0 || strncmp(insn->mnemonic,"ori.b",4) == 0 + || strncmp(insn->mnemonic,"pause",4) == 0 || strncmp(insn->mnemonic,"rotr",4) == 0 || strncmp(insn->mnemonic,"madd",4) == 0 + || strncmp(insn->mnemonic,"nmsub",5) == 0 || strncmp(insn->mnemonic,"mz.",3) == 0 + || strncmp(insn->mnemonic,"bc0",3) == 0 || strncmp(insn->mnemonic,"dmtc",4) == 0 || strncmp(insn->mnemonic,"sync",4) == 0 + || strncmp(insn->mnemonic,"bseli",5) == 0 || strncmp(insn->mnemonic,"bnz.",4) == 0 || strncmp(insn->mnemonic,"snei",4) == 0 + || strncmp(insn->mnemonic,"cle_s.",6) == 0 || strncmp(insn->mnemonic,"bz.",3) == 0 || strncmp(insn->mnemonic,"msub.",5) == 0 + || strncmp(insn->mnemonic,"shrav.",5) == 0 || strncmp(insn->mnemonic,"din",3) == 0 || strncmp(insn->mnemonic,"cins",4) == 0 + || strncmp(insn->mnemonic,"st.",3) == 0 || strncmp(insn->mnemonic,"shra",4) == 0 || strncmp(insn->mnemonic,"dextm",5) == 0 + || strncmp(insn->mnemonic,"srl.",4) == 0 || strncmp(insn->mnemonic,"bc1",3) == 0 || strncmp(insn->mnemonic,"sra.",4) == 0 + || strncmp(insn->mnemonic,"fmul.",4) == 0 || strncmp(insn->mnemonic,"dextu",5) == 0 + ) + { + // These instructions aren't supported on the N64 but capstone didn't know that + fprintf(out, ".byte 0x%02X,0x%02X,0x%02X,0x%02X /* Because of invalid n64 opcode %s */\n", insn->bytes[0], insn->bytes[1], insn->bytes[2], insn->bytes[3], insn->mnemonic); + strcpy(insn->mnemonic, ".byte"); + } + else if (strncmp(previous_instruction,".byte",4) == 0 ) { + // fprintf(out, ".byte 0x%02X,0x%02X,0x%02X,0x%02X /* Because previous was .byte */\n", insn->bytes[0], insn->bytes[1], insn->bytes[2], insn->bytes[3]); + // strcpy(insn->mnemonic, ".byte"); + } + else if (insn->is_jump) { indent = 1; fprintf(out, "%-5s ", insn->mnemonic); if (insn->id == MIPS_INS_JAL || insn->id == MIPS_INS_BAL || insn->id == MIPS_INS_J) { @@ -638,6 +664,7 @@ void mipsdisasm_pass2(FILE *out, disasm_state *state, unsigned int offset) } vaddr += 4; offset += 4; + strcpy(previous_instruction, insn->mnemonic); } } diff --git a/n64split.c b/n64split/n64split.c similarity index 52% rename from n64split.c rename to n64split/n64split.c index f569d03..5ea81a1 100644 --- a/n64split.c +++ b/n64split/n64split.c @@ -1,43 +1,8 @@ -#include <inttypes.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stdbool.h> +#include "n64split.h" -#include <zlib.h> - -#include "config.h" -#include "libblast.h" -#include "libmio0.h" -#include "libsfx.h" -#include "mipsdisasm.h" -#include "n64graphics.h" -#include "strutils.h" -#include "utils.h" - -#define N64SPLIT_VERSION "0.4a" - -#define GLOBALS_FILE "globals.inc" -#define MACROS_FILE "macros.inc" - -typedef struct _arg_config -{ - char input_file[FILENAME_MAX]; - char config_file[FILENAME_MAX]; - char output_dir[FILENAME_MAX]; - float model_scale; - bool raw_texture; // TODO: this should be the default path once n64graphics is updated - bool large_texture; - bool large_texture_depth; - bool keep_going; - bool merge_pseudo; -} arg_config; - -typedef enum { - N64_ROM_INVALID, - N64_ROM_Z64, - N64_ROM_V64, -} n64_rom_format; +// static files +#include "n64split.makefile.h" +#include "n64split.collision.mtl.h" // default configuration static const arg_config default_args = @@ -53,10 +18,6 @@ static const arg_config default_args = .merge_pseudo = false, }; -// static files -#include "n64split.makefile.h" -#include "n64split.collision.mtl.h" - const char asm_header[] = "# %s disassembly and split file\n" "# generated by n64split v%s - N64 ROM splitter\n" @@ -68,7 +29,9 @@ const char asm_header[] = ".include \"" GLOBALS_FILE "\"\n" "\n"; -static void print_spaces(FILE *fp, int count) + + +void print_spaces(FILE *fp, int count) { int i; for (i = 0; i < count; i++) { @@ -76,7 +39,7 @@ static void print_spaces(FILE *fp, int count) } } -static n64_rom_format n64_rom_type(unsigned char *buf, unsigned int length) +n64_rom_format n64_rom_type(unsigned char *buf, unsigned int length) { const unsigned char bs[] = {0x37, 0x80, 0x40, 0x12}; // byte-swapped const unsigned char be[] = {0x80, 0x37, 0x12, 0x40}; // big-endian @@ -91,7 +54,7 @@ static n64_rom_format n64_rom_type(unsigned char *buf, unsigned int length) return N64_ROM_INVALID; } -static void gzip_decode_file(char *gzfilename, int offset, char *binfilename) +void gzip_decode_file(char *gzfilename, int offset, char *binfilename) { #define CHUNK 0x4000 FILE *file; @@ -130,7 +93,7 @@ static void gzip_decode_file(char *gzfilename, int offset, char *binfilename) fclose(fout); } -static int config_section_lookup(rom_config *config, unsigned int addr, char *label, int is_end) +int config_section_lookup(rom_config *config, unsigned int addr, char *label, int is_end) { int i; // check for ROM offsets @@ -169,339 +132,7 @@ static int config_section_lookup(rom_config *config, unsigned int addr, char *la return -1; } -static void write_behavior(FILE *out, unsigned char *data, rom_config *config, int s, disasm_state *state) -{ - char label[128]; - unsigned int a, i; - unsigned int len; - unsigned int val; - int beh_i; - split_section *sec; - split_section *beh; - sec = &config->sections[s]; - beh = sec->children; - a = sec->start; - beh_i = 0; - while (a < sec->end) { - if (beh_i < sec->child_count) { - unsigned int offset = a - sec->start; - if (offset == beh[beh_i].start) { - fprintf(out, "%s: # %04X\n", beh[beh_i].label, beh[beh_i].start); - beh_i++; - } else if (offset > beh[beh_i].start) { - ERROR("Warning: skipped behavior %04X \"%s\"\n", beh[beh_i].start, beh[beh_i].label); - beh_i++; - } - } - switch (data[a]) { - case 0x02: - case 0x04: - case 0x0C: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x23: - case 0x27: - case 0x2A: - case 0x2E: - case 0x2F: - case 0x31: - case 0x33: - case 0x36: - case 0x37: - len = 8; - break; - case 0x1C: - case 0x29: - case 0x2B: - case 0x2C: - len = 12; - break; - case 0x30: - len = 20; - break; - default: - len = 4; - break; - } - val = read_u32_be(&data[a]); - fprintf(out, ".word 0x%08X", val); - switch(data[a]) { - case 0x0C: // behavior 0x0C is a function pointer - val = read_u32_be(&data[a+4]); - disasm_label_lookup(state, val, label); - fprintf(out, ", %s\n", label); - break; - case 0x02: // jump to another behavior - case 0x04: // jump to segmented address - case 0x1C: // sub-objects - case 0x29: // sub-objects - case 0x2C: // sub-objects - for (i = 4; i < len-4; i += 4) { - val = read_u32_be(&data[a+i]); - fprintf(out, ", 0x%08X", val); - } - val = read_u32_be(&data[a+len-4]); - disasm_label_lookup(state, val, label); - fprintf(out, ", %s\n", label); - break; - default: - for (i = 4; i < len; i += 4) { - val = read_u32_be(&data[a+i]); - fprintf(out, ", 0x%08X", val); - } - fprintf(out, "\n"); - break; - } - a += len; - } -} - -typedef struct -{ - int length; - const char *macro; -} geo_command; - -static geo_command geo_table[] = -{ - /* 0x00 */ {0x08, "geo_branch_and_link"}, - /* 0x01 */ {0x04, "geo_end"}, - /* 0x02 */ {0x08, "geo_branch"}, - /* 0x03 */ {0x04, "geo_return"}, - /* 0x04 */ {0x04, "geo_open_node"}, - /* 0x05 */ {0x04, "geo_close_node"}, - /* 0x06 */ {0x04, "geo_todo_06"}, - /* 0x07 */ {0x04, "geo_update_node_flags"}, - /* 0x08 */ {0x0C, "geo_node_screen_area"}, - /* 0x09 */ {0x04, "geo_todo_09"}, - /* 0x0A */ {0x08, "geo_camera_frustum"}, // 8-12 variable - /* 0x0B */ {0x04, "geo_node_start"}, - /* 0x0C */ {0x04, "geo_zbuffer"}, - /* 0x0D */ {0x08, "geo_render_range"}, - /* 0x0E */ {0x08, "geo_switch_case"}, - /* 0x0F */ {0x14, "geo_todo_0F"}, - /* 0x10 */ {0x10, "geo_translate_rotate"}, // variable - /* 0x11 */ {0x08, "geo_todo_11"}, // variable - /* 0x12 */ {0x08, "geo_todo_12"}, // variable - /* 0x13 */ {0x0C, "geo_dl_translated"}, - /* 0x14 */ {0x08, "geo_billboard"}, - /* 0x15 */ {0x08, "geo_display_list"}, - /* 0x16 */ {0x08, "geo_shadow"}, - /* 0x17 */ {0x04, "geo_todo_17"}, - /* 0x18 */ {0x08, "geo_asm"}, - /* 0x19 */ {0x08, "geo_background"}, - /* 0x1A */ {0x08, "geo_nop_1A"}, - /* 0x1B */ {0x04, "geo_todo_1B"}, - /* 0x1C */ {0x0C, "geo_todo_1C"}, - /* 0x1D */ {0x08, "geo_scale"}, // variable - /* 0x1E */ {0x08, "geo_nop_1E"}, - /* 0x1F */ {0x10, "geo_nop_1F"}, - /* 0x20 */ {0x04, "geo_start_distance"}, -}; - -static void write_geolayout(FILE *out, unsigned char *data, unsigned int start, unsigned int end, disasm_state *state) -{ - const int INDENT_AMOUNT = 3; - const int INDENT_START = INDENT_AMOUNT; - char label[128]; - unsigned int a = start; - unsigned int tmp; - int indent; - int cmd_len; - int print_label = 1; - indent = INDENT_START; - fprintf(out, ".include \"macros.inc\"\n" - ".include \"geo_commands.inc\"\n\n" - ".section .geo, \"a\"\n\n"); - while (a < end) { - unsigned cmd = data[a]; - if (print_label) { - fprintf(out, "glabel geo_layout_X_%06X # %04X\n", a, a); - print_label = 0; - } - if ((cmd == 0x01 || cmd == 0x05) && indent > INDENT_AMOUNT) { - indent -= INDENT_AMOUNT; - } - print_spaces(out, indent); - if (cmd < DIM(geo_table)) { - if (cmd != 0x10) { // special case 0x10 since multiple pseudo - fprintf(out, "%s", geo_table[cmd].macro); - } - } else { - ERROR("Unknown geo layout command: 0x%02X\n", cmd); - } - cmd_len = geo_table[cmd].length; - switch (cmd) { - case 0x00: // 00 00 00 00 [SS SS SS SS]: branch and store - tmp = read_u32_be(&data[a+4]); - fprintf(out, " geo_layout_%08X # 0x%08X", tmp, tmp); - break; - case 0x01: // 01 00 00 00: terminate - case 0x03: // 03 00 00 00: return from branch - // no params - fprintf(out, "\n"); - indent = INDENT_START; - print_label = 1; - break; - case 0x04: // 04 00 00 00: open node - case 0x05: // 05 00 00 00: close node - case 0x0B: // 05 00 00 00: start geo layout - case 0x17: // 17 00 00 00: set up object rendering - case 0x1A: // 1A 00 00 00 00 00 00 00: no op - case 0x1E: // 1E 00 00 00 00 00 00 00: no op - case 0x1F: // 1E 00 00 00 00 00 00 00 00 00 00 00: no op - // no params - break; - case 0x02: // 02 [AA] 00 00 [SS SS SS SS] - tmp = read_u32_be(&data[a+4]); - fprintf(out, " %d, geo_layout_%08X # 0x%08X", data[a+1], tmp, tmp); - break; - case 0x08: // 08 00 00 [AA] [XX XX] [YY YY] [WW WW] [HH HH] - fprintf(out, " %d, %d, %d, %d, %d", data[a+3], - read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), - read_s16_be(&data[a+8]), read_s16_be(&data[a+10])); - break; - case 0x09: // 09 00 00 [AA] - fprintf(out, " %d", data[a+3]); - break; - case 0x0A: // 0A [AA] [BB BB] [NN NN] [FF FF] {EE EE EE EE}: set camera frustum - fprintf(out, " %d, %d, %d", read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); - if (data[a+1] > 0) { - cmd_len += 4; - disasm_label_lookup(state, read_u32_be(&data[a+8]), label); - fprintf(out, ", %s", label); - } - break; - case 0x0C: // 0C [AA] 00 00: enable/disable Z-buffer - fprintf(out, " %d", data[a+1]); - break; - case 0x0D: // 0D 00 00 00 [AA AA] [BB BB]: set render range - fprintf(out, " %d, %d", read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); - break; - case 0x0E: // 0E 00 [NN NN] [AA AA AA AA]: switch/case - fprintf(out, " %d, geo_switch_case_%08X", read_s16_be(&data[a+2]), read_u32_be(&data[a+4])); - break; - case 0x0F: // 0F 00 [TT TT] [XX XX] [YY YY] [ZZ ZZ] [UU UU] [VV VV] [WW WW] [AA AA AA AA] - fprintf(out, " %d, %d, %d, %d, %d, %d, %d", read_s16_be(&data[a+2]), - read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), read_s16_be(&data[a+8]), - read_s16_be(&data[a+10]), read_s16_be(&data[a+12]), read_s16_be(&data[a+14])); - disasm_label_lookup(state, read_u32_be(&data[a+0x10]), label); - fprintf(out, ", %s", label); - break; - case 0x10: // 10 [AA] [BB BB] [XX XX] [YY YY] [ZZ ZZ] [RX RX] [RY RY] [RZ RZ] {SS SS SS SS}: translate & rotate - { - unsigned char params = data[a+1]; - unsigned char field_type = (params & 0x70) >> 4; - unsigned char layer = params & 0xF; - switch (field_type) { - case 0: // 10 [0L] 00 00 [TX TX] [TY TY] [TZ TZ] [RX RX] [RY RY] [RZ RZ] {SS SS SS SS}: translate & rotate - fprintf(out, "geo_translate_rotate %d, %d, %d, %d, %d, %d, %d", layer, - read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), read_s16_be(&data[a+8]), - read_s16_be(&data[a+10]), read_s16_be(&data[a+12]), read_s16_be(&data[a+14])); - cmd_len = 16; - break; - case 1: // 10 [1L] [TX TX] [TY TY] [TZ TZ] {SS SS SS SS}: translate - fprintf(out, "geo_translate %d, %d, %d, %d", layer, - read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); - cmd_len = 8; - break; - case 2: // 10 [2L] [RX RX] [RY RY] [RZ RZ] {SS SS SS SS}: rotate - fprintf(out, "geo_rotate %d, %d, %d, %d", layer, - read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); - cmd_len = 8; - break; - case 3: // 10 [3L] [RY RY] {SS SS SS SS}: rotate Y - fprintf(out, "geo_rotate_y %d, %d", layer, read_s16_be(&data[a+2])); - cmd_len = 4; - break; - } - if (params & 0x80) { - tmp = read_u32_be(&data[a+cmd_len]); - fprintf(out, ", seg%X_dl_%08X", (tmp >> 24) & 0xFF, tmp); - cmd_len += 4; - } - break; - } - case 0x11: // 11 [P][L] [XX XX] [YY YY] [ZZ ZZ] {SS SS SS SS}: ? scene graph node, optional DL - case 0x12: // 12 [P][L] [XX XX] [YY YY] [ZZ ZZ] {SS SS SS SS}: ? scene graph node, optional DL - case 0x14: // 14 [P][L] [XX XX] [YY YY] [ZZ ZZ] {SS SS SS SS}: billboard model - fprintf(out, " 0x%02X, %d, %d, %d", data[a+1] & 0xF, read_s16_be(&data[a+2]), - read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); - if (data[a+1] & 0x80) { - disasm_label_lookup(state, read_u32_be(&data[a+8]), label); - fprintf(out, ", %s", label); - cmd_len += 4; - } - break; - case 0x13: // 13 [LL] [XX XX] [YY YY] [ZZ ZZ] [AA AA AA AA]: scene graph node with layer and translation - fprintf(out, " 0x%02X, %d, %d, %d", data[a+1], - read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); - tmp = read_u32_be(&data[a+8]); - if (tmp != 0x0) { - fprintf(out, ", seg%X_dl_%08X", data[a+8], tmp); - } - break; - case 0x15: // 15 [LL] 00 00 [AA AA AA AA]: load display list - fprintf(out, " 0x%02X, seg%X_dl_%08X", data[a+1], data[a+4], read_u32_be(&data[a+4])); - break; - case 0x16: // 16 00 00 [AA] 00 [BB] [CC CC]: start geo layout with shadow - fprintf(out, " 0x%02X, 0x%02X, %d", data[a+3], data[a+5], read_s16_be(&data[a+6])); - break; - case 0x18: // 18 00 [XX XX] [AA AA AA AA]: load polygons from asm - case 0x19: // 19 00 [TT TT] [AA AA AA AA]: set background/skybox - disasm_label_lookup(state, read_u32_be(&data[a+4]), label); - fprintf(out, " %d, %s", read_s16_be(&data[a+2]), label); - break; - case 0x1B: // 1B 00 [XX XX]: ?? - fprintf(out, " %d", read_s16_be(&data[a+2])); - break; - case 0x1C: // 1C [PP] [XX XX] [YY YY] [ZZ ZZ] [AA AA AA AA] - disasm_label_lookup(state, read_u32_be(&data[a+8]), label); - fprintf(out, " 0x%02X, %d, %d, %d, %s", data[a+1], read_s16_be(&data[a+2]), - read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), label); - break; - case 0x1D: // 1D [P][L] 00 00 [MM MM MM MM] {SS SS SS SS}: scale model - fprintf(out, " 0x%02X, %d", data[a+1] & 0xF, read_u32_be(&data[a+4])); - if (data[a+1] & 0x80) { - disasm_label_lookup(state, read_u32_be(&data[a+8]), label); - fprintf(out, ", %s", label); - cmd_len += 4; - } - break; - case 0x20: // 20 00 [AA AA]: start geo layout with rendering area - fprintf(out, " %d", read_s16_be(&data[a+2])); - break; - default: - ERROR("Unknown geo layout command: 0x%02X\n", cmd); - break; - } - fprintf(out, "\n"); - switch (cmd) { - case 0x04: // open_node - case 0x08: // node_screen_area - //case 0x0B: // node_start - case 0x16: // geo_shadow - case 0x20: // start_distance - indent += INDENT_AMOUNT; - default: - break; - } - if (cmd == 0x01 || cmd == 0x03) { // end or return - a += cmd_len; - cmd_len = 0; - while (a < end && 0 == read_u32_be(&data[a])) { - fprintf(out, ".word 0x0\n"); - a += 4; - } - } - a += cmd_len; - } -} - -static void write_level(FILE *out, unsigned char *data, rom_config *config, int s, disasm_state *state) +void write_level(FILE *out, unsigned char *data, rom_config *config, int s, disasm_state *state) { char start_label[128]; char end_label[128]; @@ -624,104 +255,7 @@ static void write_level(FILE *out, unsigned char *data, rom_config *config, int write_geolayout(out, &data[sec->start], a - sec->start, sec->end - sec->start, state); } -static void parse_music_sequences(FILE *out, unsigned char *data, split_section *sec, arg_config *args, strbuf *makeheader) -{ -#define MUSIC_SUBDIR "music" - typedef struct { - unsigned start; - unsigned length; - } sequence; - typedef struct { - unsigned revision; - unsigned count; - sequence *seq; - } sequence_bank; - - char music_dir[FILENAME_MAX]; - char m64_file[FILENAME_MAX]; - char m64_file_rel[FILENAME_MAX]; - char seq_name[128]; - sequence_bank seq_bank = {0}; - unsigned i; - - sprintf(music_dir, "%s/%s", args->output_dir, MUSIC_SUBDIR); - make_dir(music_dir); - - seq_bank.revision = read_u16_be(&data[sec->start]); - seq_bank.count = read_u16_be(&data[sec->start+2]); - if (seq_bank.count > 0) { - seq_bank.seq = malloc(seq_bank.count * sizeof(*seq_bank.seq)); - for (i = 0; i < seq_bank.count; i++) { - seq_bank.seq[i].start = read_u32_be(&data[sec->start+i*8+4]); - seq_bank.seq[i].length = read_u32_be(&data[sec->start+i*8+8]); - } - } - - fprintf(out, "\n# music sequence table\n"); - fprintf(out, "music_sequence_table_header:\n"); - fprintf(out, ".hword %d, (music_sequence_table_end - music_sequence_table) / 8\n", seq_bank.revision); - fprintf(out, "music_sequence_table:\n"); - for (i = 0; i < seq_bank.count; i++) { - sprintf(seq_name, "seq_%02X", i); - fprintf(out, ".word (%s - music_sequence_table_header), (%s_end - %s) # 0x%05X, 0x%04X\n", - seq_name, seq_name, seq_name, seq_bank.seq[i].start, seq_bank.seq[i].length); - } - fprintf(out, "music_sequence_table_end:\n"); - fprintf(out, "\n.align 4, 0x01\n"); - for (i = 0; i < seq_bank.count; i++) { - sprintf(seq_name, "seq_%02X", i); - fprintf(out, "\n%s:", seq_name); - - sprintf(m64_file, "%s/%s.m64", music_dir, seq_name); - write_file(m64_file, &data[sec->start + seq_bank.seq[i].start], seq_bank.seq[i].length); - - sprintf(m64_file_rel, "%s/%s.m64", MUSIC_SUBDIR, seq_name); - fprintf(out, "\n.incbin \"%s\"\n", m64_file_rel); - - // append to Makefile - strbuf_sprintf(makeheader, " \\\n$(MUSIC_DIR)/%s.m64", seq_name); - - fprintf(out, "%s_end:\n", seq_name); - } - - // free used memory - if (seq_bank.count > 0) { - free(seq_bank.seq); - } -} - -static void parse_instrument_set(FILE *out, unsigned char *data, split_section *sec) -{ - unsigned *instrument_set; - unsigned count; - unsigned i, cur; - - count = sec->child_count; - // each sequence has its own instrument set defined by offsets table - instrument_set = malloc(count * sizeof(*instrument_set)); - for (i = 0; i < count; i++) { - instrument_set[i] = read_u16_be(&data[sec->start + 2*i]); - } - fprintf(out, "\ninstrument_sets:\n"); - for (i = 0; i < count; i++) { - fprintf(out, ".hword instrument_set_%02X - instrument_sets # 0x%04X\n", i, instrument_set[i]); - } - - // output each instrument set - cur = 0; - for (i = 2*count; sec->start + i < sec->end; i++) { - unsigned char val = data[sec->start + i]; - if (instrument_set[cur] == i) { - fprintf(out, "\ninstrument_set_%02X:\n.byte 0x%02X", cur, val); - cur++; - } else { - fprintf(out, ", 0x%02X", val); - } - } - fprintf(out, "\ninstrument_sets_end:\n"); -} - -static void generate_globals(arg_config *args, rom_config *config) +void generate_globals(arg_config *args, rom_config *config) { char globalfilename[FILENAME_MAX]; FILE *fglobal; @@ -741,7 +275,7 @@ static void generate_globals(arg_config *args, rom_config *config) fclose(fglobal); } -static void generate_macros(arg_config *args) +void generate_macros(arg_config *args) { char incfilename[FILENAME_MAX]; FILE *finc; @@ -760,495 +294,8 @@ static void generate_macros(arg_config *args) fclose(finc); } -static void parse_sound_banks(FILE *out, unsigned char *data, split_section *secCtl, split_section *secTbl, arg_config *args, strbuf *makeheader) -{ -#define SOUNDS_SUBDIR "sounds" - // TODO: unused parameters - (void)out; - (void)makeheader; - - char sound_dir[FILENAME_MAX]; - char sfx_file[FILENAME_MAX]; - unsigned i, j, sound_count; - - sfx_initialize_key_table(); - - sprintf(sound_dir, "%s/%s", args->output_dir, SOUNDS_SUBDIR); - make_dir(sound_dir); - - sound_data_header sound_data = read_sound_data(data, secTbl->start); - sound_bank_header sound_banks = read_sound_bank(data, secCtl->start); - - sound_count = 0; - - for (i = 0; i < sound_banks.bank_count; i++) { - for (j = 0; j < sound_banks.banks[i].instrument_count; j++) { - if(sound_banks.banks[i].sounds[j].wav_prev != NULL) { - sprintf(sfx_file, "Bank%uSound%uPrev", i, j); - extract_raw_sound(sound_dir, sfx_file, sound_banks.banks[i].sounds[j].wav_prev, sound_banks.banks[i].sounds[j].key_base_prev, sound_data.data[i], 16000); - sound_count++; - } - if(sound_banks.banks[i].sounds[j].wav != NULL) { - sprintf(sfx_file, "Bank%uSound%u", i, j); - extract_raw_sound(sound_dir, sfx_file, sound_banks.banks[i].sounds[j].wav, sound_banks.banks[i].sounds[j].key_base, sound_data.data[i], 16000); - sound_count++; - } - if(sound_banks.banks[i].sounds[j].wav_sec != NULL) { - sprintf(sfx_file, "Bank%uSound%uSec", i, j); - extract_raw_sound(sound_dir, sfx_file, sound_banks.banks[i].sounds[j].wav_sec, sound_banks.banks[i].sounds[j].key_base_sec, sound_data.data[i], 16000); - sound_count++; - } - } - - // Todo: add percussion export here - } - - // free used memory - if (sound_banks.bank_count > 0) { - free(sound_banks.banks); - } - - INFO("Successfully exported sounds:\n"); - INFO(" # of banks: %u\n", sound_banks.bank_count); - INFO(" # of sounds: %u\n", sound_count); -} -static void generate_geo_macros(arg_config *args) -{ - char macrofilename[FILENAME_MAX]; - FILE *fmacro; - sprintf(macrofilename, "%s/geo_commands.inc", args->output_dir); - fmacro = fopen(macrofilename, "w"); - if (fmacro == NULL) { - ERROR("Error opening %s\n", macrofilename); - exit(3); - } - fprintf(fmacro, -"# geo layout macros\n" -"\n" -"# 0x00: Branch and store return address\n" -"# 0x04: scriptTarget, segment address of geo layout\n" -".macro geo_branch_and_link scriptTarget\n" -" .byte 0x00, 0x00, 0x00, 0x00\n" -" .word \\scriptTarget\n" -".endm\n" -"\n" -"# 0x01: Terminate geo layout\n" -"# 0x01-0x03: unused\n" -".macro geo_end\n" -" .byte 0x01, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x02: Branch\n" -"# 0x01: if 1, store next geo layout address on stack\n" -"# 0x02-0x03: unused\n" -"# 0x04: scriptTarget, segment address of geo layout\n" -".macro geo_branch type, scriptTarget\n" -" .byte 0x02, \\type, 0x00, 0x00\n" -" .word \\scriptTarget\n" -".endm\n" -"\n" -"# 0x03: Return from branch\n" -"# 0x01-0x03: unused\n" -".macro geo_return\n" -" .byte 0x03, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x04: Open node\n" -"# 0x01-0x03: unused\n" -".macro geo_open_node\n" -" .byte 0x04, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x05: Close node\n" -"# 0x01-0x03: unused\n" -".macro geo_close_node\n" -" .byte 0x05, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x06: TODO\n" -"# 0x01: unused\n" -"# 0x02: s16, index of some array\n" -".macro geo_todo_06 param\n" -" .byte 0x06, 0x00\n" -" .hword \\param\n" -".endm\n" -"\n" -"# 0x07: Update current scene graph node flags\n" -"# 0x01: u8 operation (0 = reset, 1 = set, 2 = clear)\n" -"# 0x02: s16 bits\n" -".macro geo_update_node_flags operation, flagBits\n" -" .byte 0x07, \\operation\n" -" .hword \\flagBits\n" -".endm\n" -"\n" -"# 0x08: Create screen area scene graph node\n" -"# 0x01: unused\n" -"# 0x02: s16 num entries (+2) to allocate\n" -"# 0x04: s16 x\n" -"# 0x06: s16 y\n" -"# 0x08: s16 width\n" -"# 0x0A: s16 height\n" -".macro geo_node_screen_area numEntries, x, y, width, height\n" -" .byte 0x08, 0x00\n" -" .hword \\numEntries\n" -" .hword \\x, \\y, \\width, \\height\n" -".endm\n" -"\n" -"# 0x09: TODO Create ? scene graph node\n" -"# 0x02: s16 ?\n" -".macro geo_todo_09 param\n" -" .byte 0x09, 0x00\n" -" .hword \\param\n" -".endm\n" -"\n" -"# 0x0A: Create camera frustum scene graph node\n" -"# 0x01: u8 if nonzero, enable function field\n" -"# 0x02: s16 field of view\n" -"# 0x04: s16 near\n" -"# 0x06: s16 far\n" -"# 0x08: [GraphNodeFunc function]\n" -".macro geo_camera_frustum fov, near, far, function=0\n" -" .byte 0x0A\n" -" .if (\\function != 0)\n" -" .byte 0x01\n" -" .else\n" -" .byte 0x00\n" -" .endif\n" -" .hword \\fov, \\near, \\far\n" -" .if (\\function != 0)\n" -" .word \\function\n" -" .endif\n" -".endm\n" -"\n" -"# 0x0B: Create a root scene graph node\n" -"# 0x01-0x03: unused\n" -".macro geo_node_start\n" -" .byte 0x0B, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x0C: Create zbuffer-toggling scene graph node\n" -"# 0x01: u8 enableZBuffer (1 = on, 0 = off)\n" -"# 0x02-0x03: unused\n" -".macro geo_zbuffer enable\n" -" .byte 0x0C, \\enable, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x0D: Create render range scene graph node\n" -"# 0x01-0x03: unused\n" -"# 0x04: s16 minDistance\n" -"# 0x06: s16 maxDistance\n" -".macro geo_render_range minDistance, maxDistance\n" -" .byte 0x0D, 0x00, 0x00, 0x00\n" -" .hword \\minDistance, \\maxDistance\n" -".endm\n" -"\n" -"# 0x0E: Create switch-case scene graph node\n" -"# 0x01: unused\n" -"# 0x02: s16 numCases\n" -"# 0x04: GraphNodeFunc caseSelectorFunc\n" -".macro geo_switch_case count, function\n" -" .byte 0x0E, 0x00\n" -" .hword \\count\n" -" .word \\function\n" -".endm\n" -"\n" -"# 0x0F: TODO Create ? scene graph node\n" -"# 0x01: unused\n" -"# 0x02: s16 ?\n" -"# 0x04: s16 unkX\n" -"# 0x06: s16 unkY\n" -"# 0x08: s16 unkZ\n" -"# 0x0A: s16 unkX_2\n" -"# 0x0C: s16 unkY_2\n" -"# 0x0E: s16 unkZ_2\n" -"# 0x10: GraphNodeFunc function\n" -".macro geo_todo_0F unknown, x1, y1, z1, x2, y2, z2, function\n" -" .byte 0x0F, 0x00\n" -" .hword \\unknown, \\x1, \\y1, \\z1, \\x2, \\y2, \\z2\n" -" .word \\function\n" -".endm\n" -"\n" -"# 0x10: Create translation & rotation scene graph node with optional display list\n" -"# Four different versions of 0x10\n" -"# cmd+0x01: u8 params\n" -"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" -"# 0b0111_0000: fieldLayout (determines how rest of data is formatted\n" -"# 0b0000_1111: drawingLayer\n" -"#\n" -"# fieldLayout = 0: Translate & Rotate\n" -"# 0x04: s16 xTranslation\n" -"# 0x06: s16 xTranslation\n" -"# 0x08: s16 xTranslation\n" -"# 0x0A: s16 xRotation\n" -"# 0x0C: s16 xRotation\n" -"# 0x0E: s16 xRotation\n" -"# 0x10: [u32 displayList: if MSbit of params set, display list segmented address]\n" -".macro geo_translate_rotate layer, tx, ty, tz, rx, ry, rz, displayList=0\n" -" .byte 0x10\n" -" .if (\\displayList != 0)\n" -" .byte 0x00 | \\layer | 0x80\n" -" .else\n" -" .byte 0x00 | \\layer\n" -" .endif\n" -" .hword 0x0000\n" -" .hword \\tx, \\ty, \\tz\n" -" .hword \\rx, \\ry, \\rz\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# fieldLayout = 1: Translate\n" -"# 0x02: s16 xTranslation\n" -"# 0x04: s16 yTranslation\n" -"# 0x06: s16 zTranslation\n" -"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" -".macro geo_translate layer, tx, ty, tz, displayList=0\n" -" .byte 0x10\n" -" .if (\\displayList != 0)\n" -" .byte 0x10 | \\layer | 0x80\n" -" .else\n" -" .byte 0x10 | \\layer\n" -" .endif\n" -" .hword \\tx, \\ty, \\tz\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# fieldLayout = 2: Rotate\n" -"# 0x02: s16 xRotation\n" -"# 0x04: s16 yRotation\n" -"# 0x06: s16 zRotation\n" -"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" -".macro geo_rotate layer, rx, ry, rz, displayList=0\n" -" .byte 0x10\n" -" .if (\\displayList != 0)\n" -" .byte 0x20 | \\layer | 0x80\n" -" .else\n" -" .byte 0x20 | \\layer\n" -" .endif\n" -" .hword \\rx, \\ry, \\rz\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# fieldLayout = 3: Rotate Y\n" -"# 0x02: s16 yRotation\n" -"# 0x04: [u32 displayList: if MSbit of params set, display list segmented address]\n" -".macro geo_rotate_y layer, ry, displayList=0\n" -" .byte 0x10\n" -" .if (\\displayList != 0)\n" -" .byte 0x30 | \\layer | 0x80\n" -" .else\n" -" .byte 0x30 | \\layer\n" -" .endif\n" -" .hword \\ry\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# 0x11: TODO Create ? scene graph node with optional display list\n" -"# 0x01: u8 params\n" -"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" -"# 0b0000_1111: drawingLayer\n" -"# 0x02: s16 unkX\n" -"# 0x04: s16 unkY\n" -"# 0x06: s16 unkZ\n" -"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" -".macro geo_todo_11 layer, ux, uy, uz, displayList=0\n" -" .byte 0x11\n" -" .if (\\displayList != 0)\n" -" .byte 0x80 | \\layer\n" -" .else\n" -" .byte 0x00\n" -" .endif\n" -" .hword \\ux, \\uy, \\uz\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# 0x12: TODO Create ? scene graph node\n" -"# 0x01: u8 params\n" -"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" -"# 0b0000_1111: drawingLayer\n" -"# 0x02: s16 unkX\n" -"# 0x04: s16 unkY\n" -"# 0x06: s16 unkZ\n" -"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" -".macro geo_todo_12 layer, ux, uy, uz, displayList=0\n" -" .byte 0x12\n" -" .if (\\displayList != 0)\n" -" .byte 0x80 | \\layer\n" -" .else\n" -" .byte 0x00\n" -" .endif\n" -" .hword \\ux, \\uy, \\uz\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# 0x13: Create display list scene graph node with translation\n" -"# 0x01: u8 drawingLayer\n" -"# 0x02: s16 xTranslation\n" -"# 0x04: s16 yTranslation\n" -"# 0x06: s16 zTranslation\n" -"# 0x08: u32 displayList: dislay list segmented address\n" -".macro geo_dl_translated layer, x, y, z, displayList=0\n" -" .byte 0x13, \\layer\n" -" .hword \\x, \\y, \\z\n" -" .word \\displayList\n" -".endm\n" -"\n" -"# 0x14: Create billboarding node with optional display list\n" -"# 0x01: u8 params\n" -"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" -"# 0b0000_1111: drawingLayer\n" -"# 0x02: s16 xTranslation\n" -"# 0x04: s16 yTranslation\n" -"# 0x06: s16 zTranslation\n" -"# 0x08: [u32 displayList: if MSbit of params is set, display list segmented address]\n" -".macro geo_billboard layer=0, tx=0, ty=0, tz=0, displayList=0\n" -" .byte 0x14\n" -" .if (\\displayList != 0)\n" -" .byte 0x80 | \\layer\n" -" .else\n" -" .byte 0x00\n" -" .endif\n" -" .hword \\tx, \\ty, \\tz\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# 0x15: Create plain display list scene graph node\n" -"# 0x01: u8 drawingLayer\n" -"# 0x02=0x03: unused\n" -"# 0x04: u32 displayList: display list segmented address\n" -".macro geo_display_list layer, displayList\n" -" .byte 0x15, \\layer, 0x00, 0x00\n" -" .word \\displayList\n" -".endm\n" -"\n" -"# 0x16: Create shadow scene graph node\n" -"# 0x01: unused\n" -"# 0x02: s16 shadowType (cast to u8)\n" -"# 0x04: s16 shadowSolidity (cast to u8)\n" -"# 0x06: s16 shadowScale\n" -".set SHADOW_CIRCLE_UNK0, 0x00\n" -".set SHADOW_CIRCLE_UNK1, 0x01\n" -".set SHADOW_CIRCLE_UNK2, 0x02 # unused shadow type\n" -".set SHADOW_SQUARE_PERMANENT, 0x0A # square shadow that never disappears\n" -".set SHADOW_SQUARE_SCALABLE, 0x0B # square shadow, shrinks with distance\n" -".set SHADOW_SQUARE_TOGGLABLE, 0x0C # square shadow, disappears with distance\n" -".set SHADOW_CIRCLE_PLAYER, 0x63 # player (Mario) shadow\n" -".set SHADOW_RECTANGLE_HARDCODED_OFFSET, 0x32 # offset of hard-coded shadows\n" -".macro geo_shadow type, solidity, scale\n" -" .byte 0x16, 0x00\n" -" .hword \\type, \\solidity, \\scale\n" -".endm\n" -"\n" -"# 0x17: TODO Create ? scene graph node\n" -"# 0x01-0x03: unused\n" -".macro geo_todo_17\n" -" .byte 0x17, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x18: Create ? scene graph node\n" -"# 0x01: unused\n" -"# 0x02: s16 parameter\n" -"# 0x04: GraphNodeFunc function\n" -".macro geo_asm param, function\n" -" .byte 0x18, 0x00\n" -" .hword \\param\n" -" .word \\function\n" -".endm\n" -"\n" -"# 0x19: Create background scene graph node\n" -"# 0x02: s16 background: background ID, or RGBA5551 color if backgroundFunc is null\n" -"# 0x04: GraphNodeFunc backgroundFunc\n" -".macro geo_background param, function=0\n" -" .byte 0x19, 0x00\n" -" .hword \\param\n" -" .word \\function\n" -".endm\n" -"\n" -"# 0x1A: No operation\n" -".macro geo_nop_1A\n" -" .byte 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x1B: TODO Create ? scene graph node\n" -"# 0x02: s16 index of array\n" -".macro geo_todo_1B param\n" -" .byte 0x1B, 0x00\n" -" .hword \\param\n" -".endm\n" -"\n" -"# 0x1C: TODO Create ? scene graph node\n" -"# 0x01: u8 unk01\n" -"# 0x02: s16 unkX\n" -"# 0x04: s16 unkY\n" -"# 0x06: s16 unkZ\n" -"# 0x08: GraphNodeFunc nodeFunc\n" -".macro geo_todo_1C param, ux, uy, uz, nodeFunc\n" -" .byte 0x1C, \\param\n" -" .hword \\ux, \\uy, \\uz\n" -" .word \\nodeFunc\n" -".endm\n" -"\n" -"# 0x1D: Create scale scene graph node with optional display list\n" -"# 0x01: u8 params\n" -"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" -"# 0b0000_1111: drawingLayer\n" -"# 0x02-0x03: unused\n" -"# 0x04: u32 scale (0x10000 = 1.0)\n" -"# 0x08: [u32 displayList: if MSbit of params is set, display list segment address]\n" -".macro geo_scale layer, scale, displayList=0\n" -" .byte 0x1D\n" -" .if (\\displayList != 0)\n" -" .byte 0x80 | \\layer\n" -" .else\n" -" .byte 0x00\n" -" .endif\n" -" .byte 0x00, 0x00\n" -" .word \\scale\n" -" .if (\\displayList != 0)\n" -" .word \\displayList\n" -" .endif\n" -".endm\n" -"\n" -"# 0x1E: No operation\n" -".macro geo_nop_1E\n" -" .byte 0x1E, 0x00, 0x00, 0x00\n" -" .byte 0x00, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x1F: No operation\n" -".macro geo_nop_1F\n" -" .byte 0x1F, 0x00, 0x00, 0x00\n" -" .byte 0x00, 0x00, 0x00, 0x00\n" -" .byte 0x00, 0x00, 0x00, 0x00\n" -" .byte 0x00, 0x00, 0x00, 0x00\n" -".endm\n" -"\n" -"# 0x20: Create render distance scene graph node (unconfirmed?)\n" -"# 0x01: unused\n" -"# 0x02: s16 renderDistance?\n" -".macro geo_start_distance renderDistance\n" -" .byte 0x20, 0x00\n" -" .hword \\renderDistance\n" -".endm\n" -"\n" - ); -} - -static void generate_ld_script(arg_config *args, rom_config *config) +void generate_ld_script(arg_config *args, rom_config *config) { char ldfilename[FILENAME_MAX]; FILE *fld; @@ -1315,164 +362,69 @@ static void generate_ld_script(arg_config *args, rom_config *config) fclose(fld); } -typedef struct -{ - unsigned type; - char *name; -} terrain_t; - -static const terrain_t terrain_table[] = -{ - {0x0000, "normal"}, - {0x0001, "lethal_lava"}, - {0x0005, "hang"}, - {0x000A, "deathfloor"}, - {0x000E, "water_currents"}, - {0x0012, "void"}, - {0x0013, "very_slippery"}, - {0x0014, "slippery"}, - {0x0015, "climbable"}, - {0x0028, "wall"}, - {0x0029, "grass"}, - {0x002A, "unclimbable"}, - {0x002C, "windy"}, - {0x002E, "icy"}, - {0x0030, "flat"}, - {0x0036, "snowy"}, - {0x0037, "snowy2"}, - {0x0076, "fence"}, - {0x007B, "vanishing_wall"}, - {0x00FD, "pool_warp"}, -}; - -char *terrain2str(unsigned type) -{ - unsigned i; - static char retval[16]; - if (0x1B <= type && type <= 0x1E) { - sprintf(retval, "switch%02X", type); - return retval; - } else if (0xA6 <= type && type <= 0xCF) { - sprintf(retval, "paintingf%02X", type); - return retval; - } else if (0xD3 <= type && type <= 0xF8) { - sprintf(retval, "paintingb%02X", type); - return retval; - } - for (i = 0; i < DIM(terrain_table); i++) { - if (terrain_table[i].type == type) { - return terrain_table[i].name; - } - } - sprintf(retval, "%02X", type); - return retval; -} - -int collision2obj(char *binfilename, unsigned int binoffset, char *objfilename, char *name, float scale) -{ - unsigned char *data; - FILE *fobj; - long in_size; - unsigned vcount; - unsigned tcount; - unsigned cur_tcount; - unsigned terrain; - unsigned v_per_t; - unsigned processing; - unsigned offset; - unsigned i; - unsigned vidx[3]; - short x, y, z; - int ret_len = 0; - - fobj = fopen(objfilename, "w"); - if (fobj == NULL) { - ERROR("Error opening \"%s\" for writing\n", objfilename); - exit(EXIT_FAILURE); +void section_sm64_geo(unsigned char *data, arg_config *args, rom_config *config, disasm_state *state, split_section *sec, char* start_label, char* outfilename, char* outfilepath, FILE *fasm, strbuf *makeheader_level) { + char geofilename[FILENAME_MAX]; + FILE *fgeo; + if (sec->label == NULL || sec->label[0] == '\0') { + sprintf(geofilename, "%s.%06X.geo.s", config->basename, sec->start); + sprintf(start_label, "L%06X", sec->start); + } else { + sprintf(geofilename, "%s.geo.s", sec->label); + strcpy(start_label, sec->label); } + sprintf(outfilename, "%s/%s", GEO_SUBDIR, geofilename); + sprintf(outfilepath, "%s/%s", args->output_dir, outfilename); - in_size = read_file(binfilename, &data); - if (in_size <= 0) { - ERROR("Error reading input file \"%s\"\n", binfilename); - exit(EXIT_FAILURE); + // decode and write level data out + fgeo = fopen(outfilepath, "w"); + if (fgeo == NULL) { + perror(outfilepath); + exit(1); } + write_geolayout(fgeo, &data[sec->start], 0, sec->end - sec->start, state); + fclose(fgeo); + + fprintf(fasm, "\n.align 4, 0x01\n"); + fprintf(fasm, ".global %s\n", start_label); + fprintf(fasm, "%s:\n", start_label); + fprintf(fasm, ".include \"%s\"\n", outfilename); + fprintf(fasm, "%s_end:\n", start_label); + // append to Makefile + strbuf_sprintf(makeheader_level, " \\\n$(GEO_DIR)/%s", geofilename); +} - offset = binoffset; - if (data[offset] != 0x00 || data[offset+1] != 0x40) { - ERROR("Unknown collision data %s.%X: %08X\n", name, offset, read_u32_be(data)); - return 0; +void write_bin_type(split_section *sec, char* outfilename, char* start_label, FILE* fasm, unsigned char *data, char* outfilepath, arg_config * args, rom_config *config) { + char* output_dir=BIN_SUBDIR; + char bin_dir[FILENAME_MAX]; + if (sec->section_name != NULL) { + output_dir=sec->section_name; } + sprintf(bin_dir, "%s/%s", args->output_dir, output_dir); + make_dir(bin_dir); - fprintf(fobj, "# collision model generated from n64split v%s\n" - "# level %s %05X\n" - "\n" - "mtllib collision.mtl\n\n", N64SPLIT_VERSION, name, binoffset); - vcount = read_u16_be(&data[offset+2]); - INFO("Loading %u vertices\n", vcount); - offset += 4; - for (i = 0; i < vcount; i++) { - x = read_s16_be(&data[offset + i*6]); - y = read_s16_be(&data[offset + i*6+2]); - z = read_s16_be(&data[offset + i*6+4]); - fprintf(fobj, "v %f %f %f\n", (float)x/scale, (float)y/scale, (float)z/scale); + if (sec->label == NULL || sec->label[0] == '\0') { + sprintf(outfilename, "%s/%s.%06X.%s", output_dir, config->basename, sec->start,sec->section_name); + } else { + sprintf(outfilename, "%s/%s.%06X.%s.%s", output_dir, config->basename, sec->start, sec->label,sec->section_name); } - offset += vcount*6; - tcount = 0; - processing = 1; - while (processing) { - terrain = read_u16_be(&data[offset]); - cur_tcount = read_u16_be(&data[offset+2]); - // 0041 indicates the end, followed by 0042 or 0043 - if (terrain == 0x41 || terrain > 0xFF) { - processing = 0; - break; - } - switch (terrain) { - case 0x0E: - case 0x2C: - case 0x24: - case 0x25: - case 0x27: - case 0x2D: - v_per_t = 4; - break; - default: - v_per_t = 3; - break; - } - fprintf(fobj, "\ng %s_%05X_%s\n", name, binoffset, terrain2str(terrain)); - fprintf(fobj, "usemtl %s\n", terrain2str(terrain)); - - INFO("Loading %u triangles of terrain %02X\n", cur_tcount, terrain); - offset += 4; - for (i = 0; i < cur_tcount; i++) { - vidx[0] = read_u16_be(&data[offset + i*v_per_t*2]); - vidx[1] = read_u16_be(&data[offset + i*v_per_t*2+2]); - vidx[2] = read_u16_be(&data[offset + i*v_per_t*2+4]); - fprintf(fobj, "f %d %d %d\n", vidx[0]+1, vidx[1]+1, vidx[2]+1); - } - tcount += cur_tcount; - offset += cur_tcount*v_per_t*2; + sprintf(outfilepath, "%s/%s", args->output_dir, outfilename); + write_file(outfilepath, &data[sec->start], sec->end - sec->start); + if (sec->label == NULL || sec->label[0] == '\0') { + sprintf(start_label, "L%06X", sec->start); + } else { + strcpy(start_label, sec->label); } - - fclose(fobj); - free(data); - - ret_len = offset - binoffset; - return ret_len; + fprintf(fasm, "%s:\n", start_label); + fprintf(fasm, ".incbin \"%s\"\n", outfilename); + fprintf(fasm, "%s_end:\n", start_label); } -static void split_file(unsigned char *data, unsigned int length, arg_config *args, rom_config *config, disasm_state *state) +void split_file(unsigned char *data, unsigned int length, arg_config *args, rom_config *config, disasm_state *state) { -#define BIN_SUBDIR "bin" -#define MIO0_SUBDIR "bin" -#define TEXTURE_SUBDIR "textures" -#define GEO_SUBDIR "geo" -#define LEVEL_SUBDIR "levels" -#define MODEL_SUBDIR "models" -#define BEHAVIOR_SUBDIR "." + char makefile_name[FILENAME_MAX]; char bin_dir[FILENAME_MAX]; + char asm_dir[FILENAME_MAX]; char mio0_dir[FILENAME_MAX]; char texture_dir[FILENAME_MAX]; char geo_dir[FILENAME_MAX]; @@ -1500,6 +452,7 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg // create directories sprintf(makefile_name, "%s/Makefile.split", args->output_dir); sprintf(bin_dir, "%s/%s", args->output_dir, BIN_SUBDIR); + sprintf(asm_dir, "%s/%s", args->output_dir, ASM_SUBDIR); sprintf(mio0_dir, "%s/%s", args->output_dir, MIO0_SUBDIR); sprintf(texture_dir, "%s/%s", args->output_dir, TEXTURE_SUBDIR); sprintf(geo_dir, "%s/%s", args->output_dir, GEO_SUBDIR); @@ -1508,6 +461,7 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg sprintf(behavior_dir, "%s/%s", args->output_dir, BEHAVIOR_SUBDIR); make_dir(args->output_dir); make_dir(bin_dir); + make_dir(asm_dir); make_dir(mio0_dir); make_dir(texture_dir); make_dir(geo_dir); @@ -1562,6 +516,7 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg group_offset += group_len; } } else { + // TODO move gap fillers into a different subdirectory sprintf(outfilename, "%s/%s.%06X.bin", BIN_SUBDIR, config->basename, prev_end); sprintf(outfilepath, "%s/%s", args->output_dir, outfilename); write_file(outfilepath, &data[prev_end], gap_len); @@ -1601,21 +556,7 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg fprintf(fasm, ".byte 0x%02X # version\n\n", data[sec->start + 0x3F]); break; case TYPE_BIN: - if (sec->label == NULL || sec->label[0] == '\0') { - sprintf(outfilename, "%s/%s.%06X.bin", BIN_SUBDIR, config->basename, sec->start); - } else { - sprintf(outfilename, "%s/%s.%06X.%s.bin", BIN_SUBDIR, config->basename, sec->start, sec->label); - } - sprintf(outfilepath, "%s/%s", args->output_dir, outfilename); - write_file(outfilepath, &data[sec->start], sec->end - sec->start); - if (sec->label == NULL || sec->label[0] == '\0') { - sprintf(start_label, "L%06X", sec->start); - } else { - strcpy(start_label, sec->label); - } - fprintf(fasm, "%s:\n", start_label); - fprintf(fasm, ".incbin \"%s\"\n", outfilename); - fprintf(fasm, "%s_end:\n", start_label); + write_bin_type(sec, outfilename, start_label, fasm, data, outfilepath, args, config); break; case TYPE_BLAST: case TYPE_MIO0: @@ -1650,8 +591,16 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg break; case TYPE_ASM: INFO("Section asm: %X-%X\n", sec->start, sec->end); - fprintf(fasm, "\n.section .text%08X, \"ax\"\n\n", sec->vaddr); - mipsdisasm_pass2(fasm, state, sec->start); + char section_asmfilename[FILENAME_MAX]; + sprintf(section_asmfilename, "%s/asm/%s.s", args->output_dir, sec->label); + // Include in main .s file + fprintf(fasm, ".include \"asm/%s.s\" \n", sec->label); + + // Open seperate .s file for this section + FILE *section_fasm = fopen(section_asmfilename, "w"); + fprintf(section_fasm, "\n.section .text%08X, \"ax\"\n\n", sec->vaddr); + mipsdisasm_pass2(section_fasm, state, sec->start); + fclose(section_fasm); break; case TYPE_SM64_LEVEL: // relocate level scripts to .mio0 area @@ -1681,8 +630,9 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg parse_instrument_set(fasm, data, sec); break; default: - ERROR("Don't know what to do with type %d\n", sec->type); - exit(1); + // printf("Treating custom file format as binary %s %s %s\n", sec->section_name, outfilepath, outfilename); + // ERROR("Don't know what to do with type %d\n", sec->type); + write_bin_type(sec, outfilename, start_label, fasm, data, outfilepath, args, config); break; } prev_end = sec->end; @@ -1709,34 +659,7 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg switch (sec->type) { case TYPE_SM64_GEO: { - char geofilename[FILENAME_MAX]; - FILE *fgeo; - if (sec->label == NULL || sec->label[0] == '\0') { - sprintf(geofilename, "%s.%06X.geo.s", config->basename, sec->start); - sprintf(start_label, "L%06X", sec->start); - } else { - sprintf(geofilename, "%s.geo.s", sec->label); - strcpy(start_label, sec->label); - } - sprintf(outfilename, "%s/%s", GEO_SUBDIR, geofilename); - sprintf(outfilepath, "%s/%s", args->output_dir, outfilename); - - // decode and write level data out - fgeo = fopen(outfilepath, "w"); - if (fgeo == NULL) { - perror(outfilepath); - exit(1); - } - write_geolayout(fgeo, &data[sec->start], 0, sec->end - sec->start, state); - fclose(fgeo); - - fprintf(fasm, "\n.align 4, 0x01\n"); - fprintf(fasm, ".global %s\n", start_label); - fprintf(fasm, "%s:\n", start_label); - fprintf(fasm, ".include \"%s\"\n", outfilename); - fprintf(fasm, "%s_end:\n", start_label); - // append to Makefile - strbuf_sprintf(&makeheader_level, " \\\n$(GEO_DIR)/%s", geofilename); + section_sm64_geo(data, args, config, state, sec, start_label, outfilename, outfilepath, fasm, &makeheader_level); break; } case TYPE_BLAST: @@ -1820,9 +743,9 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg // extract texture data if (sec->children) { unsigned int offset = 0; - unsigned int next_offset = 0; + unsigned int next_offset = 0; // TODO: add segment base to config file - const unsigned int segment_base = 0x07000000; + const unsigned int segment_base = 0x07000000; unsigned int seg_address = segment_base + offset; fprintf(fmake, "$(MIO0_DIR)/%s.bin:", start_label); INFO("Extracting textures from %s\n", start_label); @@ -1831,32 +754,32 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg texture *tex = &child->tex; w = tex->width; h = tex->height; - if (next_offset > child->start) { + if (next_offset > child->start) { ERROR("Error section overlap region %d (%X > %X)\n", t, next_offset, child->start); - exit(1); - } - if (next_offset != child->start) { - unsigned gap_len = child->start - next_offset; - INFO("Filling gap before region %d (%d bytes)\n", t, gap_len); - fprintf(binasm, "# Unknown region %06X-%06X [%X]\n", next_offset, child->start, gap_len); - while (gap_len > 0) { + exit(1); + } + if (next_offset != child->start) { + unsigned int gap_len = child->start - next_offset; + INFO("Filling gap before region %d (%d bytes)\n", t, gap_len); + fprintf(binasm, "# Unknown region %06X-%06X [%X]\n", next_offset, child->start, gap_len); + while (gap_len > 0) { int group_len = MIN(gap_len, 0x10); - fprintf(binasm, ".byte "); - fprint_hex_source(binasm, &binfilecontents[next_offset], group_len); - fprintf(binasm, "\n"); - gap_len -= group_len; - next_offset += group_len; - } + fprintf(binasm, ".byte "); + fprint_hex_source(binasm, &binfilecontents[next_offset], group_len); + fprintf(binasm, "\n"); + gap_len -= group_len; + next_offset += group_len; + } } offset = tex->offset; seg_address = segment_base + offset; - if (child->end) { - next_offset = child->end; - } else if (tex->format == TYPE_F3D_LIGHT) { - next_offset = child->start + 0x18; - } else { // assume texture - next_offset = child->start + w * h * tex->depth / 8; - } + if (child->end) { + next_offset = child->end; + } else if (tex->format == TYPE_F3D_LIGHT) { + next_offset = child->start + 0x18; + } else { // assume texture + next_offset = child->start + w * h * tex->depth / 8; + } fprintf(binasm, "\n"); switch (tex->format) { case TYPE_TEX_IA: @@ -2162,7 +1085,7 @@ static void split_file(unsigned char *data, unsigned int length, arg_config *arg generate_geo_macros(args); } -static void print_usage(void) +void print_usage(void) { ERROR("Usage: n64split [-c CONFIG] [-k] [-m] [-o OUTPUT_DIR] [-s SCALE] [-t] [-v] [-V] ROM\n" "\n" @@ -2185,7 +1108,7 @@ static void print_usage(void) exit(1); } -static void print_version(void) +void print_version(void) { ERROR("n64split v" N64SPLIT_VERSION ", using:\n" " %s\n" @@ -2196,7 +1119,7 @@ static void print_version(void) } // parse command line arguments -static void parse_arguments(int argc, char *argv[], arg_config *config) +void parse_arguments(int argc, char *argv[], arg_config *config) { int i; int file_count = 0; @@ -2263,7 +1186,8 @@ static void parse_arguments(int argc, char *argv[], arg_config *config) } } -static int detect_config_file(unsigned int c1, unsigned int c2, rom_config *config) +// Auto detect a config file based on the 2 checksums +int detect_config_file(unsigned int c1, unsigned int c2, rom_config *config) { #define CONFIGS_DIR "configs" dir_list list; @@ -2367,6 +1291,7 @@ int main(int argc, char *argv[]) unsigned int start = config.sections[i].start; unsigned int end = config.sections[i].end; unsigned int vaddr = config.sections[i].vaddr; + printf("First pass of section:%s\n", config.sections[i].label); if (end <= (unsigned int)len) { mipsdisasm_pass1(data, start, end - start, vaddr, state); } else { @@ -2389,7 +1314,7 @@ int main(int argc, char *argv[]) } } percent = (float)(100 * size) / (float)(len); - printf("Total decoded section size: %X/%lX (%.2f%%)\n", size, len, percent); + printf("Total decoded section size: %X/%lX (%.2f%%) (i.e sections that are not .bin)\n", size, len, percent); size = 0; return 0; diff --git a/n64split.collision.mtl.h b/n64split/n64split.collision.mtl.h similarity index 100% rename from n64split.collision.mtl.h rename to n64split/n64split.collision.mtl.h diff --git a/n64split/n64split.h b/n64split/n64split.h new file mode 100644 index 0000000..b810de4 --- /dev/null +++ b/n64split/n64split.h @@ -0,0 +1,133 @@ +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> + +#include <zlib.h> + +#include "config.h" +#include "libblast.h" +#include "libmio0.h" +#include "libsfx.h" +#include "mipsdisasm.h" +#include "n64graphics.h" +#include "strutils.h" +#include "utils.h" + + +//================================================================================ +// Constant Definitions +//================================================================================ + +#define N64SPLIT_VERSION "0.4a" + +#define GLOBALS_FILE "globals.inc" +#define MACROS_FILE "macros.inc" + +#define MUSIC_SUBDIR "music" +#define SOUNDS_SUBDIR "sounds" +#define BIN_SUBDIR "bin" +#define ASM_SUBDIR "asm" +#define MIO0_SUBDIR "bin" +#define TEXTURE_SUBDIR "textures" +#define GEO_SUBDIR "geo" +#define LEVEL_SUBDIR "levels" +#define MODEL_SUBDIR "models" +#define BEHAVIOR_SUBDIR "." + + +//================================================================================ +// Structure Definitions +//================================================================================ + +/* Main */ +typedef struct _arg_config +{ + char input_file[FILENAME_MAX]; + char config_file[FILENAME_MAX]; + char output_dir[FILENAME_MAX]; + float model_scale; + bool raw_texture; // TODO: this should be the default path once n64graphics is updated + bool large_texture; + bool large_texture_depth; + bool keep_going; + bool merge_pseudo; +} arg_config; + +typedef enum { + N64_ROM_INVALID, + N64_ROM_Z64, + N64_ROM_V64, +} n64_rom_format; + + +/* Collision */ +typedef struct +{ + unsigned int type; + char *name; +} terrain_t; + +extern const terrain_t terrain_table[]; + + +/* Geo */ +typedef struct +{ + int length; + const char *macro; +} geo_command; + +extern geo_command geo_table[]; + + +//================================================================================ +// Function Declarations +//================================================================================ + +/* Main */ +void print_spaces(FILE *fp, int count); +n64_rom_format n64_rom_type(unsigned char *buf, unsigned int length); +void gzip_decode_file(char *gzfilename, int offset, char *binfilename); +int config_section_lookup(rom_config *config, unsigned int addr, char *label, int is_end); +void write_level(FILE *out, unsigned char *data, rom_config *config, int s, disasm_state *state); + +void generate_globals(arg_config *args, rom_config *config); +void generate_macros(arg_config *args); +void generate_ld_script(arg_config *args, rom_config *config); + +void section_sm64_geo(unsigned char *data, arg_config *args, rom_config *config, + disasm_state *state, split_section *sec, char* start_label, + char* outfilename, char* outfilepath, FILE *fasm, strbuf *makeheader_level); + +void write_bin_type(split_section *sec, char* outfilename, char* start_label, FILE* fasm, + unsigned char *data, char* outfilepath, arg_config * args, rom_config *config); + +void split_file(unsigned char *data, unsigned int length, arg_config *args, rom_config *config, disasm_state *state); + +void print_usage(void); +void print_version(void); +void parse_arguments(int argc, char *argv[], arg_config *config); +int detect_config_file(unsigned int c1, unsigned int c2, rom_config *config); +int main(int argc, char *argv[]); + + +/* Behavior */ +void write_behavior(FILE *out, unsigned char *data, rom_config *config, int s, disasm_state *state); + + +/* Collision */ +char *terrain2str(unsigned int type); +int collision2obj(char *binfilename, unsigned int binoffset, char *objfilename, char *name, float scale); + + +/* Geo */ +void write_geolayout(FILE *out, unsigned char *data, unsigned int start, unsigned int end, disasm_state *state); +void generate_geo_macros(arg_config *args); + + +/* Sound */ +void parse_music_sequences(FILE *out, unsigned char *data, split_section *sec, arg_config *args, strbuf *makeheader); +void parse_instrument_set(FILE *out, unsigned char *data, split_section *sec); +void parse_sound_banks(FILE *out, unsigned char *data, split_section *secCtl, split_section *secTbl, arg_config *args, strbuf *makeheader); diff --git a/n64split.makefile.h b/n64split/n64split.makefile.h similarity index 97% rename from n64split.makefile.h rename to n64split/n64split.makefile.h index ce374e1..6b21790 100644 --- a/n64split.makefile.h +++ b/n64split/n64split.makefile.h @@ -7,14 +7,14 @@ static const char makefile_data[] = "BUILD_DIR = build\n" "\n" "##################### Compiler Options #######################\n" -"CROSS = mips64-elf-\n" +"CROSS = mipsel-elf-\n" "AS = $(CROSS)as\n" "CC = $(CROSS)gcc\n" "LD = $(CROSS)ld\n" "OBJDUMP = $(CROSS)objdump\n" "OBJCOPY = $(CROSS)objcopy\n" "\n" -"ASFLAGS = -mtune=vr4300 -march=vr4300\n" +"ASFLAGS = -EB -mtune=vr4300 -march=vr4300\n" "CFLAGS = -Wall -O2 -mtune=vr4300 -march=vr4300 -G 0 -c\n" "LDFLAGS = -T $(LD_SCRIPT) -Map $(BUILD_DIR)/sm64.map\n" "\n" diff --git a/n64split/n64split.sm64.behavior.c b/n64split/n64split.sm64.behavior.c new file mode 100644 index 0000000..b547dbc --- /dev/null +++ b/n64split/n64split.sm64.behavior.c @@ -0,0 +1,91 @@ +#include "n64split.h" + +void write_behavior(FILE *out, unsigned char *data, rom_config *config, int s, disasm_state *state) +{ + char label[128]; + unsigned int a, i; + unsigned int len; + unsigned int val; + int beh_i; + split_section *sec; + split_section *beh; + sec = &config->sections[s]; + beh = sec->children; + a = sec->start; + beh_i = 0; + while (a < sec->end) { + if (beh_i < sec->child_count) { + unsigned int offset = a - sec->start; + if (offset == beh[beh_i].start) { + fprintf(out, "%s: # %04X\n", beh[beh_i].label, beh[beh_i].start); + beh_i++; + } else if (offset > beh[beh_i].start) { + ERROR("Warning: skipped behavior %04X \"%s\"\n", beh[beh_i].start, beh[beh_i].label); + beh_i++; + } + } + switch (data[a]) { + case 0x02: + case 0x04: + case 0x0C: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x23: + case 0x27: + case 0x2A: + case 0x2E: + case 0x2F: + case 0x31: + case 0x33: + case 0x36: + case 0x37: + len = 8; + break; + case 0x1C: + case 0x29: + case 0x2B: + case 0x2C: + len = 12; + break; + case 0x30: + len = 20; + break; + default: + len = 4; + break; + } + val = read_u32_be(&data[a]); + fprintf(out, ".word 0x%08X", val); + switch(data[a]) { + case 0x0C: // behavior 0x0C is a function pointer + val = read_u32_be(&data[a+4]); + disasm_label_lookup(state, val, label); + fprintf(out, ", %s\n", label); + break; + case 0x02: // jump to another behavior + case 0x04: // jump to segmented address + case 0x1C: // sub-objects + case 0x29: // sub-objects + case 0x2C: // sub-objects + for (i = 4; i < len-4; i += 4) { + val = read_u32_be(&data[a+i]); + fprintf(out, ", 0x%08X", val); + } + val = read_u32_be(&data[a+len-4]); + disasm_label_lookup(state, val, label); + fprintf(out, ", %s\n", label); + break; + default: + for (i = 4; i < len; i += 4) { + val = read_u32_be(&data[a+i]); + fprintf(out, ", 0x%08X", val); + } + fprintf(out, "\n"); + break; + } + a += len; + } +} \ No newline at end of file diff --git a/n64split/n64split.sm64.collision.c b/n64split/n64split.sm64.collision.c new file mode 100644 index 0000000..7958e90 --- /dev/null +++ b/n64split/n64split.sm64.collision.c @@ -0,0 +1,144 @@ +#include "n64split.h" + +const terrain_t terrain_table[] = +{ + {0x0000, "normal"}, + {0x0001, "lethal_lava"}, + {0x0005, "hang"}, + {0x000A, "deathfloor"}, + {0x000E, "water_currents"}, + {0x0012, "void"}, + {0x0013, "very_slippery"}, + {0x0014, "slippery"}, + {0x0015, "climbable"}, + {0x0028, "wall"}, + {0x0029, "grass"}, + {0x002A, "unclimbable"}, + {0x002C, "windy"}, + {0x002E, "icy"}, + {0x0030, "flat"}, + {0x0036, "snowy"}, + {0x0037, "snowy2"}, + {0x0076, "fence"}, + {0x007B, "vanishing_wall"}, + {0x00FD, "pool_warp"}, +}; + +char *terrain2str(unsigned int type) +{ + unsigned int i; + static char retval[16]; + if (0x1B <= type && type <= 0x1E) { + sprintf(retval, "switch%02X", type); + return retval; + } else if (0xA6 <= type && type <= 0xCF) { + sprintf(retval, "paintingf%02X", type); + return retval; + } else if (0xD3 <= type && type <= 0xF8) { + sprintf(retval, "paintingb%02X", type); + return retval; + } + for (i = 0; i < DIM(terrain_table); i++) { + if (terrain_table[i].type == type) { + return terrain_table[i].name; + } + } + sprintf(retval, "%02X", type); + return retval; +} + + + +int collision2obj(char *binfilename, unsigned int binoffset, char *objfilename, char *name, float scale) +{ + unsigned char *data; + FILE *fobj; + long in_size; + unsigned int vcount; + unsigned int tcount; + unsigned int cur_tcount; + unsigned int terrain; + unsigned int v_per_t; + unsigned int processing; + unsigned int offset; + unsigned int i; + unsigned int vidx[3]; + short x, y, z; + int ret_len = 0; + + fobj = fopen(objfilename, "w"); + if (fobj == NULL) { + ERROR("Error opening \"%s\" for writing\n", objfilename); + exit(EXIT_FAILURE); + } + + in_size = read_file(binfilename, &data); + if (in_size <= 0) { + ERROR("Error reading input file \"%s\"\n", binfilename); + exit(EXIT_FAILURE); + } + + offset = binoffset; + if (data[offset] != 0x00 || data[offset+1] != 0x40) { + ERROR("Unknown collision data %s.%X: %08X\n", name, offset, read_u32_be(data)); + return 0; + } + + fprintf(fobj, "# collision model generated from n64split v%s\n" + "# level %s %05X\n" + "\n" + "mtllib collision.mtl\n\n", N64SPLIT_VERSION, name, binoffset); + vcount = read_u16_be(&data[offset+2]); + INFO("Loading %u vertices\n", vcount); + offset += 4; + for (i = 0; i < vcount; i++) { + x = read_s16_be(&data[offset + i*6]); + y = read_s16_be(&data[offset + i*6+2]); + z = read_s16_be(&data[offset + i*6+4]); + fprintf(fobj, "v %f %f %f\n", (float)x/scale, (float)y/scale, (float)z/scale); + } + offset += vcount*6; + tcount = 0; + processing = 1; + while (processing) { + terrain = read_u16_be(&data[offset]); + cur_tcount = read_u16_be(&data[offset+2]); + // 0041 indicates the end, followed by 0042 or 0043 + if (terrain == 0x41 || terrain > 0xFF) { + processing = 0; + break; + } + switch (terrain) { + case 0x0E: + case 0x2C: + case 0x24: + case 0x25: + case 0x27: + case 0x2D: + v_per_t = 4; + break; + default: + v_per_t = 3; + break; + } + fprintf(fobj, "\ng %s_%05X_%s\n", name, binoffset, terrain2str(terrain)); + fprintf(fobj, "usemtl %s\n", terrain2str(terrain)); + + INFO("Loading %u triangles of terrain %02X\n", cur_tcount, terrain); + offset += 4; + for (i = 0; i < cur_tcount; i++) { + vidx[0] = read_u16_be(&data[offset + i*v_per_t*2]); + vidx[1] = read_u16_be(&data[offset + i*v_per_t*2+2]); + vidx[2] = read_u16_be(&data[offset + i*v_per_t*2+4]); + fprintf(fobj, "f %d %d %d\n", vidx[0]+1, vidx[1]+1, vidx[2]+1); + } + tcount += cur_tcount; + offset += cur_tcount*v_per_t*2; + } + + fclose(fobj); + free(data); + + ret_len = offset - binoffset; + return ret_len; +} \ No newline at end of file diff --git a/n64split/n64split.sm64.geo.c b/n64split/n64split.sm64.geo.c new file mode 100644 index 0000000..4e9ea59 --- /dev/null +++ b/n64split/n64split.sm64.geo.c @@ -0,0 +1,672 @@ +#include "n64split.h" + +geo_command geo_table[] = +{ + /* 0x00 */ {0x08, "geo_branch_and_link"}, + /* 0x01 */ {0x04, "geo_end"}, + /* 0x02 */ {0x08, "geo_branch"}, + /* 0x03 */ {0x04, "geo_return"}, + /* 0x04 */ {0x04, "geo_open_node"}, + /* 0x05 */ {0x04, "geo_close_node"}, + /* 0x06 */ {0x04, "geo_todo_06"}, + /* 0x07 */ {0x04, "geo_update_node_flags"}, + /* 0x08 */ {0x0C, "geo_node_screen_area"}, + /* 0x09 */ {0x04, "geo_todo_09"}, + /* 0x0A */ {0x08, "geo_camera_frustum"}, // 8-12 variable + /* 0x0B */ {0x04, "geo_node_start"}, + /* 0x0C */ {0x04, "geo_zbuffer"}, + /* 0x0D */ {0x08, "geo_render_range"}, + /* 0x0E */ {0x08, "geo_switch_case"}, + /* 0x0F */ {0x14, "geo_todo_0F"}, + /* 0x10 */ {0x10, "geo_translate_rotate"}, // variable + /* 0x11 */ {0x08, "geo_todo_11"}, // variable + /* 0x12 */ {0x08, "geo_todo_12"}, // variable + /* 0x13 */ {0x0C, "geo_dl_translated"}, + /* 0x14 */ {0x08, "geo_billboard"}, + /* 0x15 */ {0x08, "geo_display_list"}, + /* 0x16 */ {0x08, "geo_shadow"}, + /* 0x17 */ {0x04, "geo_todo_17"}, + /* 0x18 */ {0x08, "geo_asm"}, + /* 0x19 */ {0x08, "geo_background"}, + /* 0x1A */ {0x08, "geo_nop_1A"}, + /* 0x1B */ {0x04, "geo_todo_1B"}, + /* 0x1C */ {0x0C, "geo_todo_1C"}, + /* 0x1D */ {0x08, "geo_scale"}, // variable + /* 0x1E */ {0x08, "geo_nop_1E"}, + /* 0x1F */ {0x10, "geo_nop_1F"}, + /* 0x20 */ {0x04, "geo_start_distance"}, +}; + +void write_geolayout(FILE *out, unsigned char *data, unsigned int start, unsigned int end, disasm_state *state) +{ + const int INDENT_AMOUNT = 3; + const int INDENT_START = INDENT_AMOUNT; + char label[128]; + unsigned int a = start; + unsigned int tmp; + int indent; + int cmd_len; + int print_label = 1; + indent = INDENT_START; + fprintf(out, ".include \"macros.inc\"\n" + ".include \"geo_commands.inc\"\n\n" + ".section .geo, \"a\"\n\n"); + while (a < end) { + unsigned int cmd = data[a]; + if (print_label) { + fprintf(out, "glabel geo_layout_X_%06X # %04X\n", a, a); + print_label = 0; + } + if ((cmd == 0x01 || cmd == 0x05) && indent > INDENT_AMOUNT) { + indent -= INDENT_AMOUNT; + } + print_spaces(out, indent); + if (cmd < DIM(geo_table)) { + if (cmd != 0x10) { // special case 0x10 since multiple pseudo + fprintf(out, "%s", geo_table[cmd].macro); + } + } else { + ERROR("Unknown geo layout command: 0x%02X\n", cmd); + } + cmd_len = geo_table[cmd].length; + switch (cmd) { + case 0x00: // 00 00 00 00 [SS SS SS SS]: branch and store + tmp = read_u32_be(&data[a+4]); + fprintf(out, " geo_layout_%08X # 0x%08X", tmp, tmp); + break; + case 0x01: // 01 00 00 00: terminate + case 0x03: // 03 00 00 00: return from branch + // no params + fprintf(out, "\n"); + indent = INDENT_START; + print_label = 1; + break; + case 0x04: // 04 00 00 00: open node + case 0x05: // 05 00 00 00: close node + case 0x0B: // 05 00 00 00: start geo layout + case 0x17: // 17 00 00 00: set up object rendering + case 0x1A: // 1A 00 00 00 00 00 00 00: no op + case 0x1E: // 1E 00 00 00 00 00 00 00: no op + case 0x1F: // 1E 00 00 00 00 00 00 00 00 00 00 00: no op + // no params + break; + case 0x02: // 02 [AA] 00 00 [SS SS SS SS] + tmp = read_u32_be(&data[a+4]); + fprintf(out, " %d, geo_layout_%08X # 0x%08X", data[a+1], tmp, tmp); + break; + case 0x08: // 08 00 00 [AA] [XX XX] [YY YY] [WW WW] [HH HH] + fprintf(out, " %d, %d, %d, %d, %d", data[a+3], + read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), + read_s16_be(&data[a+8]), read_s16_be(&data[a+10])); + break; + case 0x09: // 09 00 00 [AA] + fprintf(out, " %d", data[a+3]); + break; + case 0x0A: // 0A [AA] [BB BB] [NN NN] [FF FF] {EE EE EE EE}: set camera frustum + fprintf(out, " %d, %d, %d", read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); + if (data[a+1] > 0) { + cmd_len += 4; + disasm_label_lookup(state, read_u32_be(&data[a+8]), label); + fprintf(out, ", %s", label); + } + break; + case 0x0C: // 0C [AA] 00 00: enable/disable Z-buffer + fprintf(out, " %d", data[a+1]); + break; + case 0x0D: // 0D 00 00 00 [AA AA] [BB BB]: set render range + fprintf(out, " %d, %d", read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); + break; + case 0x0E: // 0E 00 [NN NN] [AA AA AA AA]: switch/case + fprintf(out, " %d, geo_switch_case_%08X", read_s16_be(&data[a+2]), read_u32_be(&data[a+4])); + break; + case 0x0F: // 0F 00 [TT TT] [XX XX] [YY YY] [ZZ ZZ] [UU UU] [VV VV] [WW WW] [AA AA AA AA] + fprintf(out, " %d, %d, %d, %d, %d, %d, %d", read_s16_be(&data[a+2]), + read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), read_s16_be(&data[a+8]), + read_s16_be(&data[a+10]), read_s16_be(&data[a+12]), read_s16_be(&data[a+14])); + disasm_label_lookup(state, read_u32_be(&data[a+0x10]), label); + fprintf(out, ", %s", label); + break; + case 0x10: // 10 [AA] [BB BB] [XX XX] [YY YY] [ZZ ZZ] [RX RX] [RY RY] [RZ RZ] {SS SS SS SS}: translate & rotate + { + unsigned char params = data[a+1]; + unsigned char field_type = (params & 0x70) >> 4; + unsigned char layer = params & 0xF; + switch (field_type) { + case 0: // 10 [0L] 00 00 [TX TX] [TY TY] [TZ TZ] [RX RX] [RY RY] [RZ RZ] {SS SS SS SS}: translate & rotate + fprintf(out, "geo_translate_rotate %d, %d, %d, %d, %d, %d, %d", layer, + read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), read_s16_be(&data[a+8]), + read_s16_be(&data[a+10]), read_s16_be(&data[a+12]), read_s16_be(&data[a+14])); + cmd_len = 16; + break; + case 1: // 10 [1L] [TX TX] [TY TY] [TZ TZ] {SS SS SS SS}: translate + fprintf(out, "geo_translate %d, %d, %d, %d", layer, + read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); + cmd_len = 8; + break; + case 2: // 10 [2L] [RX RX] [RY RY] [RZ RZ] {SS SS SS SS}: rotate + fprintf(out, "geo_rotate %d, %d, %d, %d", layer, + read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); + cmd_len = 8; + break; + case 3: // 10 [3L] [RY RY] {SS SS SS SS}: rotate Y + fprintf(out, "geo_rotate_y %d, %d", layer, read_s16_be(&data[a+2])); + cmd_len = 4; + break; + } + if (params & 0x80) { + tmp = read_u32_be(&data[a+cmd_len]); + fprintf(out, ", seg%X_dl_%08X", (tmp >> 24) & 0xFF, tmp); + cmd_len += 4; + } + break; + } + case 0x11: // 11 [P][L] [XX XX] [YY YY] [ZZ ZZ] {SS SS SS SS}: ? scene graph node, optional DL + case 0x12: // 12 [P][L] [XX XX] [YY YY] [ZZ ZZ] {SS SS SS SS}: ? scene graph node, optional DL + case 0x14: // 14 [P][L] [XX XX] [YY YY] [ZZ ZZ] {SS SS SS SS}: billboard model + fprintf(out, " 0x%02X, %d, %d, %d", data[a+1] & 0xF, read_s16_be(&data[a+2]), + read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); + if (data[a+1] & 0x80) { + disasm_label_lookup(state, read_u32_be(&data[a+8]), label); + fprintf(out, ", %s", label); + cmd_len += 4; + } + break; + case 0x13: // 13 [LL] [XX XX] [YY YY] [ZZ ZZ] [AA AA AA AA]: scene graph node with layer and translation + fprintf(out, " 0x%02X, %d, %d, %d", data[a+1], + read_s16_be(&data[a+2]), read_s16_be(&data[a+4]), read_s16_be(&data[a+6])); + tmp = read_u32_be(&data[a+8]); + if (tmp != 0x0) { + fprintf(out, ", seg%X_dl_%08X", data[a+8], tmp); + } + break; + case 0x15: // 15 [LL] 00 00 [AA AA AA AA]: load display list + fprintf(out, " 0x%02X, seg%X_dl_%08X", data[a+1], data[a+4], read_u32_be(&data[a+4])); + break; + case 0x16: // 16 00 00 [AA] 00 [BB] [CC CC]: start geo layout with shadow + fprintf(out, " 0x%02X, 0x%02X, %d", data[a+3], data[a+5], read_s16_be(&data[a+6])); + break; + case 0x18: // 18 00 [XX XX] [AA AA AA AA]: load polygons from asm + case 0x19: // 19 00 [TT TT] [AA AA AA AA]: set background/skybox + disasm_label_lookup(state, read_u32_be(&data[a+4]), label); + fprintf(out, " %d, %s", read_s16_be(&data[a+2]), label); + break; + case 0x1B: // 1B 00 [XX XX]: ?? + fprintf(out, " %d", read_s16_be(&data[a+2])); + break; + case 0x1C: // 1C [PP] [XX XX] [YY YY] [ZZ ZZ] [AA AA AA AA] + disasm_label_lookup(state, read_u32_be(&data[a+8]), label); + fprintf(out, " 0x%02X, %d, %d, %d, %s", data[a+1], read_s16_be(&data[a+2]), + read_s16_be(&data[a+4]), read_s16_be(&data[a+6]), label); + break; + case 0x1D: // 1D [P][L] 00 00 [MM MM MM MM] {SS SS SS SS}: scale model + fprintf(out, " 0x%02X, %d", data[a+1] & 0xF, read_u32_be(&data[a+4])); + if (data[a+1] & 0x80) { + disasm_label_lookup(state, read_u32_be(&data[a+8]), label); + fprintf(out, ", %s", label); + cmd_len += 4; + } + break; + case 0x20: // 20 00 [AA AA]: start geo layout with rendering area + fprintf(out, " %d", read_s16_be(&data[a+2])); + break; + default: + ERROR("Unknown geo layout command: 0x%02X\n", cmd); + break; + } + fprintf(out, "\n"); + switch (cmd) { + case 0x04: // open_node + case 0x08: // node_screen_area + //case 0x0B: // node_start + case 0x16: // geo_shadow + case 0x20: // start_distance + indent += INDENT_AMOUNT; + default: + break; + } + if (cmd == 0x01 || cmd == 0x03) { // end or return + a += cmd_len; + cmd_len = 0; + while (a < end && 0 == read_u32_be(&data[a])) { + fprintf(out, ".word 0x0\n"); + a += 4; + } + } + a += cmd_len; + } +} + +void generate_geo_macros(arg_config *args) +{ + char macrofilename[FILENAME_MAX]; + FILE *fmacro; + sprintf(macrofilename, "%s/geo_commands.inc", args->output_dir); + fmacro = fopen(macrofilename, "w"); + if (fmacro == NULL) { + ERROR("Error opening %s\n", macrofilename); + exit(3); + } + fprintf(fmacro, +"# geo layout macros\n" +"\n" +"# 0x00: Branch and store return address\n" +"# 0x04: scriptTarget, segment address of geo layout\n" +".macro geo_branch_and_link scriptTarget\n" +" .byte 0x00, 0x00, 0x00, 0x00\n" +" .word \\scriptTarget\n" +".endm\n" +"\n" +"# 0x01: Terminate geo layout\n" +"# 0x01-0x03: unused\n" +".macro geo_end\n" +" .byte 0x01, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x02: Branch\n" +"# 0x01: if 1, store next geo layout address on stack\n" +"# 0x02-0x03: unused\n" +"# 0x04: scriptTarget, segment address of geo layout\n" +".macro geo_branch type, scriptTarget\n" +" .byte 0x02, \\type, 0x00, 0x00\n" +" .word \\scriptTarget\n" +".endm\n" +"\n" +"# 0x03: Return from branch\n" +"# 0x01-0x03: unused\n" +".macro geo_return\n" +" .byte 0x03, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x04: Open node\n" +"# 0x01-0x03: unused\n" +".macro geo_open_node\n" +" .byte 0x04, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x05: Close node\n" +"# 0x01-0x03: unused\n" +".macro geo_close_node\n" +" .byte 0x05, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x06: TODO\n" +"# 0x01: unused\n" +"# 0x02: s16, index of some array\n" +".macro geo_todo_06 param\n" +" .byte 0x06, 0x00\n" +" .hword \\param\n" +".endm\n" +"\n" +"# 0x07: Update current scene graph node flags\n" +"# 0x01: u8 operation (0 = reset, 1 = set, 2 = clear)\n" +"# 0x02: s16 bits\n" +".macro geo_update_node_flags operation, flagBits\n" +" .byte 0x07, \\operation\n" +" .hword \\flagBits\n" +".endm\n" +"\n" +"# 0x08: Create screen area scene graph node\n" +"# 0x01: unused\n" +"# 0x02: s16 num entries (+2) to allocate\n" +"# 0x04: s16 x\n" +"# 0x06: s16 y\n" +"# 0x08: s16 width\n" +"# 0x0A: s16 height\n" +".macro geo_node_screen_area numEntries, x, y, width, height\n" +" .byte 0x08, 0x00\n" +" .hword \\numEntries\n" +" .hword \\x, \\y, \\width, \\height\n" +".endm\n" +"\n" +"# 0x09: TODO Create ? scene graph node\n" +"# 0x02: s16 ?\n" +".macro geo_todo_09 param\n" +" .byte 0x09, 0x00\n" +" .hword \\param\n" +".endm\n" +"\n" +"# 0x0A: Create camera frustum scene graph node\n" +"# 0x01: u8 if nonzero, enable function field\n" +"# 0x02: s16 field of view\n" +"# 0x04: s16 near\n" +"# 0x06: s16 far\n" +"# 0x08: [GraphNodeFunc function]\n" +".macro geo_camera_frustum fov, near, far, function=0\n" +" .byte 0x0A\n" +" .if (\\function != 0)\n" +" .byte 0x01\n" +" .else\n" +" .byte 0x00\n" +" .endif\n" +" .hword \\fov, \\near, \\far\n" +" .if (\\function != 0)\n" +" .word \\function\n" +" .endif\n" +".endm\n" +"\n" +"# 0x0B: Create a root scene graph node\n" +"# 0x01-0x03: unused\n" +".macro geo_node_start\n" +" .byte 0x0B, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x0C: Create zbuffer-toggling scene graph node\n" +"# 0x01: u8 enableZBuffer (1 = on, 0 = off)\n" +"# 0x02-0x03: unused\n" +".macro geo_zbuffer enable\n" +" .byte 0x0C, \\enable, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x0D: Create render range scene graph node\n" +"# 0x01-0x03: unused\n" +"# 0x04: s16 minDistance\n" +"# 0x06: s16 maxDistance\n" +".macro geo_render_range minDistance, maxDistance\n" +" .byte 0x0D, 0x00, 0x00, 0x00\n" +" .hword \\minDistance, \\maxDistance\n" +".endm\n" +"\n" +"# 0x0E: Create switch-case scene graph node\n" +"# 0x01: unused\n" +"# 0x02: s16 numCases\n" +"# 0x04: GraphNodeFunc caseSelectorFunc\n" +".macro geo_switch_case count, function\n" +" .byte 0x0E, 0x00\n" +" .hword \\count\n" +" .word \\function\n" +".endm\n" +"\n" +"# 0x0F: TODO Create ? scene graph node\n" +"# 0x01: unused\n" +"# 0x02: s16 ?\n" +"# 0x04: s16 unkX\n" +"# 0x06: s16 unkY\n" +"# 0x08: s16 unkZ\n" +"# 0x0A: s16 unkX_2\n" +"# 0x0C: s16 unkY_2\n" +"# 0x0E: s16 unkZ_2\n" +"# 0x10: GraphNodeFunc function\n" +".macro geo_todo_0F unknown, x1, y1, z1, x2, y2, z2, function\n" +" .byte 0x0F, 0x00\n" +" .hword \\unknown, \\x1, \\y1, \\z1, \\x2, \\y2, \\z2\n" +" .word \\function\n" +".endm\n" +"\n" +"# 0x10: Create translation & rotation scene graph node with optional display list\n" +"# Four different versions of 0x10\n" +"# cmd+0x01: u8 params\n" +"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" +"# 0b0111_0000: fieldLayout (determines how rest of data is formatted\n" +"# 0b0000_1111: drawingLayer\n" +"#\n" +"# fieldLayout = 0: Translate & Rotate\n" +"# 0x04: s16 xTranslation\n" +"# 0x06: s16 xTranslation\n" +"# 0x08: s16 xTranslation\n" +"# 0x0A: s16 xRotation\n" +"# 0x0C: s16 xRotation\n" +"# 0x0E: s16 xRotation\n" +"# 0x10: [u32 displayList: if MSbit of params set, display list segmented address]\n" +".macro geo_translate_rotate layer, tx, ty, tz, rx, ry, rz, displayList=0\n" +" .byte 0x10\n" +" .if (\\displayList != 0)\n" +" .byte 0x00 | \\layer | 0x80\n" +" .else\n" +" .byte 0x00 | \\layer\n" +" .endif\n" +" .hword 0x0000\n" +" .hword \\tx, \\ty, \\tz\n" +" .hword \\rx, \\ry, \\rz\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# fieldLayout = 1: Translate\n" +"# 0x02: s16 xTranslation\n" +"# 0x04: s16 yTranslation\n" +"# 0x06: s16 zTranslation\n" +"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" +".macro geo_translate layer, tx, ty, tz, displayList=0\n" +" .byte 0x10\n" +" .if (\\displayList != 0)\n" +" .byte 0x10 | \\layer | 0x80\n" +" .else\n" +" .byte 0x10 | \\layer\n" +" .endif\n" +" .hword \\tx, \\ty, \\tz\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# fieldLayout = 2: Rotate\n" +"# 0x02: s16 xRotation\n" +"# 0x04: s16 yRotation\n" +"# 0x06: s16 zRotation\n" +"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" +".macro geo_rotate layer, rx, ry, rz, displayList=0\n" +" .byte 0x10\n" +" .if (\\displayList != 0)\n" +" .byte 0x20 | \\layer | 0x80\n" +" .else\n" +" .byte 0x20 | \\layer\n" +" .endif\n" +" .hword \\rx, \\ry, \\rz\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# fieldLayout = 3: Rotate Y\n" +"# 0x02: s16 yRotation\n" +"# 0x04: [u32 displayList: if MSbit of params set, display list segmented address]\n" +".macro geo_rotate_y layer, ry, displayList=0\n" +" .byte 0x10\n" +" .if (\\displayList != 0)\n" +" .byte 0x30 | \\layer | 0x80\n" +" .else\n" +" .byte 0x30 | \\layer\n" +" .endif\n" +" .hword \\ry\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# 0x11: TODO Create ? scene graph node with optional display list\n" +"# 0x01: u8 params\n" +"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" +"# 0b0000_1111: drawingLayer\n" +"# 0x02: s16 unkX\n" +"# 0x04: s16 unkY\n" +"# 0x06: s16 unkZ\n" +"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" +".macro geo_todo_11 layer, ux, uy, uz, displayList=0\n" +" .byte 0x11\n" +" .if (\\displayList != 0)\n" +" .byte 0x80 | \\layer\n" +" .else\n" +" .byte 0x00\n" +" .endif\n" +" .hword \\ux, \\uy, \\uz\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# 0x12: TODO Create ? scene graph node\n" +"# 0x01: u8 params\n" +"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" +"# 0b0000_1111: drawingLayer\n" +"# 0x02: s16 unkX\n" +"# 0x04: s16 unkY\n" +"# 0x06: s16 unkZ\n" +"# 0x08: [u32 displayList: if MSbit of params set, display list segmented address]\n" +".macro geo_todo_12 layer, ux, uy, uz, displayList=0\n" +" .byte 0x12\n" +" .if (\\displayList != 0)\n" +" .byte 0x80 | \\layer\n" +" .else\n" +" .byte 0x00\n" +" .endif\n" +" .hword \\ux, \\uy, \\uz\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# 0x13: Create display list scene graph node with translation\n" +"# 0x01: u8 drawingLayer\n" +"# 0x02: s16 xTranslation\n" +"# 0x04: s16 yTranslation\n" +"# 0x06: s16 zTranslation\n" +"# 0x08: u32 displayList: dislay list segmented address\n" +".macro geo_dl_translated layer, x, y, z, displayList=0\n" +" .byte 0x13, \\layer\n" +" .hword \\x, \\y, \\z\n" +" .word \\displayList\n" +".endm\n" +"\n" +"# 0x14: Create billboarding node with optional display list\n" +"# 0x01: u8 params\n" +"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" +"# 0b0000_1111: drawingLayer\n" +"# 0x02: s16 xTranslation\n" +"# 0x04: s16 yTranslation\n" +"# 0x06: s16 zTranslation\n" +"# 0x08: [u32 displayList: if MSbit of params is set, display list segmented address]\n" +".macro geo_billboard layer=0, tx=0, ty=0, tz=0, displayList=0\n" +" .byte 0x14\n" +" .if (\\displayList != 0)\n" +" .byte 0x80 | \\layer\n" +" .else\n" +" .byte 0x00\n" +" .endif\n" +" .hword \\tx, \\ty, \\tz\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# 0x15: Create plain display list scene graph node\n" +"# 0x01: u8 drawingLayer\n" +"# 0x02=0x03: unused\n" +"# 0x04: u32 displayList: display list segmented address\n" +".macro geo_display_list layer, displayList\n" +" .byte 0x15, \\layer, 0x00, 0x00\n" +" .word \\displayList\n" +".endm\n" +"\n" +"# 0x16: Create shadow scene graph node\n" +"# 0x01: unused\n" +"# 0x02: s16 shadowType (cast to u8)\n" +"# 0x04: s16 shadowSolidity (cast to u8)\n" +"# 0x06: s16 shadowScale\n" +".set SHADOW_CIRCLE_UNK0, 0x00\n" +".set SHADOW_CIRCLE_UNK1, 0x01\n" +".set SHADOW_CIRCLE_UNK2, 0x02 # unused shadow type\n" +".set SHADOW_SQUARE_PERMANENT, 0x0A # square shadow that never disappears\n" +".set SHADOW_SQUARE_SCALABLE, 0x0B # square shadow, shrinks with distance\n" +".set SHADOW_SQUARE_TOGGLABLE, 0x0C # square shadow, disappears with distance\n" +".set SHADOW_CIRCLE_PLAYER, 0x63 # player (Mario) shadow\n" +".set SHADOW_RECTANGLE_HARDCODED_OFFSET, 0x32 # offset of hard-coded shadows\n" +".macro geo_shadow type, solidity, scale\n" +" .byte 0x16, 0x00\n" +" .hword \\type, \\solidity, \\scale\n" +".endm\n" +"\n" +"# 0x17: TODO Create ? scene graph node\n" +"# 0x01-0x03: unused\n" +".macro geo_todo_17\n" +" .byte 0x17, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x18: Create ? scene graph node\n" +"# 0x01: unused\n" +"# 0x02: s16 parameter\n" +"# 0x04: GraphNodeFunc function\n" +".macro geo_asm param, function\n" +" .byte 0x18, 0x00\n" +" .hword \\param\n" +" .word \\function\n" +".endm\n" +"\n" +"# 0x19: Create background scene graph node\n" +"# 0x02: s16 background: background ID, or RGBA5551 color if backgroundFunc is null\n" +"# 0x04: GraphNodeFunc backgroundFunc\n" +".macro geo_background param, function=0\n" +" .byte 0x19, 0x00\n" +" .hword \\param\n" +" .word \\function\n" +".endm\n" +"\n" +"# 0x1A: No operation\n" +".macro geo_nop_1A\n" +" .byte 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x1B: TODO Create ? scene graph node\n" +"# 0x02: s16 index of array\n" +".macro geo_todo_1B param\n" +" .byte 0x1B, 0x00\n" +" .hword \\param\n" +".endm\n" +"\n" +"# 0x1C: TODO Create ? scene graph node\n" +"# 0x01: u8 unk01\n" +"# 0x02: s16 unkX\n" +"# 0x04: s16 unkY\n" +"# 0x06: s16 unkZ\n" +"# 0x08: GraphNodeFunc nodeFunc\n" +".macro geo_todo_1C param, ux, uy, uz, nodeFunc\n" +" .byte 0x1C, \\param\n" +" .hword \\ux, \\uy, \\uz\n" +" .word \\nodeFunc\n" +".endm\n" +"\n" +"# 0x1D: Create scale scene graph node with optional display list\n" +"# 0x01: u8 params\n" +"# 0b1000_0000: if set, enable displayList field and drawingLayer\n" +"# 0b0000_1111: drawingLayer\n" +"# 0x02-0x03: unused\n" +"# 0x04: u32 scale (0x10000 = 1.0)\n" +"# 0x08: [u32 displayList: if MSbit of params is set, display list segment address]\n" +".macro geo_scale layer, scale, displayList=0\n" +" .byte 0x1D\n" +" .if (\\displayList != 0)\n" +" .byte 0x80 | \\layer\n" +" .else\n" +" .byte 0x00\n" +" .endif\n" +" .byte 0x00, 0x00\n" +" .word \\scale\n" +" .if (\\displayList != 0)\n" +" .word \\displayList\n" +" .endif\n" +".endm\n" +"\n" +"# 0x1E: No operation\n" +".macro geo_nop_1E\n" +" .byte 0x1E, 0x00, 0x00, 0x00\n" +" .byte 0x00, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x1F: No operation\n" +".macro geo_nop_1F\n" +" .byte 0x1F, 0x00, 0x00, 0x00\n" +" .byte 0x00, 0x00, 0x00, 0x00\n" +" .byte 0x00, 0x00, 0x00, 0x00\n" +" .byte 0x00, 0x00, 0x00, 0x00\n" +".endm\n" +"\n" +"# 0x20: Create render distance scene graph node (unconfirmed?)\n" +"# 0x01: unused\n" +"# 0x02: s16 renderDistance?\n" +".macro geo_start_distance renderDistance\n" +" .byte 0x20, 0x00\n" +" .hword \\renderDistance\n" +".endm\n" +"\n" + ); +} \ No newline at end of file diff --git a/n64split/n64split.sound.c b/n64split/n64split.sound.c new file mode 100644 index 0000000..c18d6c5 --- /dev/null +++ b/n64split/n64split.sound.c @@ -0,0 +1,151 @@ +#include "n64split.h" + +#define MUSIC_SUBDIR "music" + +void parse_music_sequences(FILE *out, unsigned char *data, split_section *sec, arg_config *args, strbuf *makeheader) +{ + typedef struct { + unsigned int start; + unsigned int length; + } sequence; + typedef struct { + unsigned int revision; + unsigned int count; + sequence *seq; + } sequence_bank; + + char music_dir[FILENAME_MAX]; + char m64_file[FILENAME_MAX]; + char m64_file_rel[FILENAME_MAX]; + char seq_name[128]; + sequence_bank seq_bank = {0}; + unsigned int i; + + sprintf(music_dir, "%s/%s", args->output_dir, MUSIC_SUBDIR); + make_dir(music_dir); + + seq_bank.revision = read_u16_be(&data[sec->start]); + seq_bank.count = read_u16_be(&data[sec->start+2]); + if (seq_bank.count > 0) { + seq_bank.seq = malloc(seq_bank.count * sizeof(*seq_bank.seq)); + for (i = 0; i < seq_bank.count; i++) { + seq_bank.seq[i].start = read_u32_be(&data[sec->start+i*8+4]); + seq_bank.seq[i].length = read_u32_be(&data[sec->start+i*8+8]); + } + } + + fprintf(out, "\n# music sequence table\n"); + fprintf(out, "music_sequence_table_header:\n"); + fprintf(out, ".hword %d, (music_sequence_table_end - music_sequence_table) / 8\n", seq_bank.revision); + fprintf(out, "music_sequence_table:\n"); + for (i = 0; i < seq_bank.count; i++) { + sprintf(seq_name, "seq_%02X", i); + fprintf(out, ".word (%s - music_sequence_table_header), (%s_end - %s) # 0x%05X, 0x%04X\n", + seq_name, seq_name, seq_name, seq_bank.seq[i].start, seq_bank.seq[i].length); + } + fprintf(out, "music_sequence_table_end:\n"); + fprintf(out, "\n.align 4, 0x01\n"); + for (i = 0; i < seq_bank.count; i++) { + sprintf(seq_name, "seq_%02X", i); + fprintf(out, "\n%s:", seq_name); + + sprintf(m64_file, "%s/%s.m64", music_dir, seq_name); + write_file(m64_file, &data[sec->start + seq_bank.seq[i].start], seq_bank.seq[i].length); + + sprintf(m64_file_rel, "%s/%s.m64", MUSIC_SUBDIR, seq_name); + fprintf(out, "\n.incbin \"%s\"\n", m64_file_rel); + + // append to Makefile + strbuf_sprintf(makeheader, " \\\n$(MUSIC_DIR)/%s.m64", seq_name); + + fprintf(out, "%s_end:\n", seq_name); + } + + // free used memory + if (seq_bank.count > 0) { + free(seq_bank.seq); + } +} + +void parse_instrument_set(FILE *out, unsigned char *data, split_section *sec) +{ + unsigned int *instrument_set; + unsigned int count; + unsigned int i, cur; + + count = sec->child_count; + // each sequence has its own instrument set defined by offsets table + instrument_set = malloc(count * sizeof(*instrument_set)); + for (i = 0; i < count; i++) { + instrument_set[i] = read_u16_be(&data[sec->start + 2*i]); + } + fprintf(out, "\ninstrument_sets:\n"); + for (i = 0; i < count; i++) { + fprintf(out, ".hword instrument_set_%02X - instrument_sets # 0x%04X\n", i, instrument_set[i]); + } + + // output each instrument set + cur = 0; + for (i = 2*count; sec->start + i < sec->end; i++) { + unsigned char val = data[sec->start + i]; + if (instrument_set[cur] == i) { + fprintf(out, "\ninstrument_set_%02X:\n.byte 0x%02X", cur, val); + cur++; + } else { + fprintf(out, ", 0x%02X", val); + } + } + fprintf(out, "\ninstrument_sets_end:\n"); +} + +void parse_sound_banks(FILE *out, unsigned char *data, split_section *secCtl, split_section *secTbl, arg_config *args, strbuf *makeheader) +{ + // TODO: unused parameters + (void)out; + (void)makeheader; + + char sound_dir[FILENAME_MAX]; + char sfx_file[FILENAME_MAX]; + unsigned int i, j, sound_count; + + sfx_initialize_key_table(); + + sprintf(sound_dir, "%s/%s", args->output_dir, SOUNDS_SUBDIR); + make_dir(sound_dir); + + sound_data_header sound_data = read_sound_data(data, secTbl->start); + sound_bank_header sound_banks = read_sound_bank(data, secCtl->start); + + sound_count = 0; + + for (i = 0; i < sound_banks.bank_count; i++) { + for (j = 0; j < sound_banks.banks[i].instrument_count; j++) { + if(sound_banks.banks[i].sounds[j].wav_prev != NULL) { + sprintf(sfx_file, "Bank%uSound%uPrev", i, j); + extract_raw_sound(sound_dir, sfx_file, sound_banks.banks[i].sounds[j].wav_prev, sound_banks.banks[i].sounds[j].key_base_prev, sound_data.data[i], 16000); + sound_count++; + } + if(sound_banks.banks[i].sounds[j].wav != NULL) { + sprintf(sfx_file, "Bank%uSound%u", i, j); + extract_raw_sound(sound_dir, sfx_file, sound_banks.banks[i].sounds[j].wav, sound_banks.banks[i].sounds[j].key_base, sound_data.data[i], 16000); + sound_count++; + } + if(sound_banks.banks[i].sounds[j].wav_sec != NULL) { + sprintf(sfx_file, "Bank%uSound%uSec", i, j); + extract_raw_sound(sound_dir, sfx_file, sound_banks.banks[i].sounds[j].wav_sec, sound_banks.banks[i].sounds[j].key_base_sec, sound_data.data[i], 16000); + sound_count++; + } + } + + // Todo: add percussion export here + } + + // free used memory + if (sound_banks.bank_count > 0) { + free(sound_banks.banks); + } + + INFO("Successfully exported sounds:\n"); + INFO(" # of banks: %u\n", sound_banks.bank_count); + INFO(" # of sounds: %u\n", sound_count); +} diff --git a/yamlconfig.c b/yamlconfig.c index 13caec4..66f7978 100644 --- a/yamlconfig.c +++ b/yamlconfig.c @@ -7,6 +7,7 @@ #include "config.h" #include "utils.h" +#define MAX_SIZE 2048 typedef struct { const char *name; @@ -41,6 +42,7 @@ static const section_entry section_table[] = { {"sm64.geo", TYPE_SM64_GEO}, {"sm64.level", TYPE_SM64_LEVEL}, }; +// TODO would be cool to extend this dynamically with plugins static inline int is_texture(section_type section) { @@ -101,7 +103,7 @@ int get_scalar_uint(unsigned int *val, yaml_node_t *node) void load_child_node(split_section *section, yaml_document_t *doc, yaml_node_t *node) { - char val[64]; + char val[MAX_SIZE]; yaml_node_item_t *i_node; yaml_node_t *next_node; texture *tex = §ion->tex; @@ -223,7 +225,7 @@ void load_section_data(split_section *section, yaml_document_t *doc, yaml_node_t void load_section(split_section *section, yaml_document_t *doc, yaml_node_t *node) { - char val[128]; + char val[MAX_SIZE]; yaml_node_item_t *i_node; yaml_node_t *next_node; size_t count = node->data.sequence.items.top - node->data.sequence.items.start; @@ -236,7 +238,10 @@ void load_section(split_section *section, yaml_document_t *doc, yaml_node_t *nod switch (i) { case 0: section->start = strtoul(val, NULL, 0); break; case 1: section->end = strtoul(val, NULL, 0); break; - case 2: section->type = config_str2section(val); break; + case 2: + section->type = config_str2section(val); + strcpy(section->section_name, val); + break; case 3: section->vaddr = strtoul(val, NULL, 0); break; } } else { @@ -277,7 +282,7 @@ void load_section(split_section *section, yaml_document_t *doc, yaml_node_t *nod } break; default: - ERROR("Error: " SIZE_T_FORMAT " - invalid section type '%s'\n", node->start_mark.line, val); + // ERROR("Error: " SIZE_T_FORMAT " - invalid section type '%s'\n", node->start_mark.line, val); return; } if (count > 3) { @@ -369,7 +374,7 @@ int load_sections_sequence(rom_config *c, yaml_document_t *doc, yaml_node_t *nod void load_label(label *lab, yaml_document_t *doc, yaml_node_t *node) { - char val[128]; + char val[MAX_SIZE]; yaml_node_item_t *i_node; yaml_node_t *next_node; if (node->type == YAML_SEQUENCE_NODE) { @@ -604,9 +609,10 @@ int config_validate(const rom_config *config, unsigned int max_len) for (i = 0; i < config->section_count; i++) { split_section *isec = &config->sections[i]; if (isec->start < last_end) { - ERROR("Error: section %d \"%s\" (%X-%X) out of order\n", - i, isec->label, isec->start, isec->end); - ret_val = -2; + // ERROR("Error: section %d \"%s\" (%X-%X) out of order\n", + // i, isec->label, isec->start, isec->end); + isec->start = last_end; + // ret_val = -2; } if (isec->end > max_len) { ERROR("Error: section %d \"%s\" (%X-%X) past end of file (%X)\n", @@ -616,7 +622,8 @@ int config_validate(const rom_config *config, unsigned int max_len) if (isec->start >= isec->end) { ERROR("Error: section %d \"%s\" (%X-%X) invalid range\n", i, isec->label, isec->start, isec->end); - ret_val = -4; + isec->end=isec->start; + //ret_val = -4; } for (j = 0; j < i; j++) { split_section *jsec = &config->sections[j]; @@ -624,7 +631,7 @@ int config_validate(const rom_config *config, unsigned int max_len) ERROR("Error: section %d \"%s\" (%X-%X) overlaps %d \"%s\" (%X-%X)\n", i, isec->label, isec->start, isec->end, j, jsec->label, jsec->start, jsec->end); - ret_val = -1; + // ret_val = -1; } } last_end = isec->end;