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 = &section->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;