shithub: zelda3

Download patch

ref: 3ba33373eca644b7c4b8347fe7d43deb2676b7fb
parent: c42071bc01e0ef703e29fba26166ecb90b231324
author: Snesrev <[email protected]>
date: Fri Sep 23 20:25:31 EDT 2022

Put all assets into zelda3_assets.dat instead of .h files

 - Now the .exe no longer depends on the ROM
 - Github could now build the exe

--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@
 /snes/*.o
 /msu/alttp_msu-*.pcm
 /tmp/
+/tables/zelda3_assets.dat
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,6 @@
 ROM:=tables/zelda3.sfc
 SRCS:=$(wildcard *.c snes/*.c)
 OBJS:=$(SRCS:%.c=%.o)
-GEN:=$(shell grep -hor tables/generated.*.h --include \*.c .)
 PYTHON:=/usr/bin/env python3
 CFLAGS:=$(if $(CFLAGS),$(CFLAGS),-O2)
 
@@ -12,13 +11,13 @@
 .PHONY: all clean clean_obj clean_gen
 
 all: $(TARGET_EXEC)
-$(TARGET_EXEC): tables/generated_dialogue.h $(OBJS)
+$(TARGET_EXEC): tables/zelda3_assets.dat $(OBJS)
 	$(CC) $(OBJS) -o $@ $(LDFLAGS)
-$(GEN): tables/dialogue.txt
+tables/zelda3_assets.dat: tables/dialogue.txt
 	cd tables; $(PYTHON) compile_resources.py ../$(ROM)
 tables/dialogue.txt:
 	cd tables; $(PYTHON) extract_resources.py ../$(ROM)
-%.o : %.c tables/generated_dialogue.h
+%.o : %.c tables/zelda3_assets.dat
 	$(CC) -c $(CFLAGS) $< -o $@
 
 clean: clean_obj clean_gen
@@ -25,4 +24,4 @@
 clean_obj:
 	$(RM) $(OBJS) $(TARGET_EXEC)
 clean_gen:
-	$(RM) $(GEN)
+	$(RM) tables/zelda3_assets.dat
--- a/ancilla.c
+++ b/ancilla.c
@@ -9,8 +9,8 @@
 #include "player.h"
 #include "misc.h"
 #include "dungeon.h"
-#include "tables/generated_ancilla.h"
 #include "sprite_main.h"
+#include "assets.h"
 
 static const uint8 kAncilla_Pflags[68] = {
   0,    8,  0xc, 0x10, 0x10,    4, 0x10, 0x18,    8,    8,    8,    0, 0x14, 0, 0x10, 0x28,
--- /dev/null
+++ b/assets.h
@@ -1,0 +1,339 @@
+#pragma once
+#include "types.h"
+
+enum {
+  kNumberOfAssets = 165
+};
+extern const uint8 *g_asset_ptrs[kNumberOfAssets];
+extern uint32 g_asset_sizes[kNumberOfAssets];
+#define kSoundBank_intro ((uint8*)g_asset_ptrs[0])
+#define kSoundBank_intro_SIZE (g_asset_sizes[0])
+#define kSoundBank_indoor ((uint8*)g_asset_ptrs[1])
+#define kSoundBank_indoor_SIZE (g_asset_sizes[1])
+#define kSoundBank_ending ((uint8*)g_asset_ptrs[2])
+#define kSoundBank_ending_SIZE (g_asset_sizes[2])
+#define kDungeonRoom ((uint8*)g_asset_ptrs[3])
+#define kDungeonRoom_SIZE (g_asset_sizes[3])
+#define kDungeonRoomOffs ((uint16*)g_asset_ptrs[4])
+#define kDungeonRoomOffs_SIZE (g_asset_sizes[4])
+#define kDungeonRoomDoorOffs ((uint16*)g_asset_ptrs[5])
+#define kDungeonRoomDoorOffs_SIZE (g_asset_sizes[5])
+#define kDungeonRoomHeaders ((uint8*)g_asset_ptrs[6])
+#define kDungeonRoomHeaders_SIZE (g_asset_sizes[6])
+#define kDungeonRoomHeadersOffs ((uint16*)g_asset_ptrs[7])
+#define kDungeonRoomHeadersOffs_SIZE (g_asset_sizes[7])
+#define kDungeonRoomChests ((uint8*)g_asset_ptrs[8])
+#define kDungeonRoomChests_SIZE (g_asset_sizes[8])
+#define kDungeonRoomTeleMsg ((uint16*)g_asset_ptrs[9])
+#define kDungeonRoomTeleMsg_SIZE (g_asset_sizes[9])
+#define kDungeonPitsHurtPlayer ((uint16*)g_asset_ptrs[10])
+#define kDungeonPitsHurtPlayer_SIZE (g_asset_sizes[10])
+#define kEntranceData_rooms ((uint16*)g_asset_ptrs[11])
+#define kEntranceData_rooms_SIZE (g_asset_sizes[11])
+#define kEntranceData_relativeCoords ((uint8*)g_asset_ptrs[12])
+#define kEntranceData_relativeCoords_SIZE (g_asset_sizes[12])
+#define kEntranceData_scrollX ((uint16*)g_asset_ptrs[13])
+#define kEntranceData_scrollX_SIZE (g_asset_sizes[13])
+#define kEntranceData_scrollY ((uint16*)g_asset_ptrs[14])
+#define kEntranceData_scrollY_SIZE (g_asset_sizes[14])
+#define kEntranceData_playerX ((uint16*)g_asset_ptrs[15])
+#define kEntranceData_playerX_SIZE (g_asset_sizes[15])
+#define kEntranceData_playerY ((uint16*)g_asset_ptrs[16])
+#define kEntranceData_playerY_SIZE (g_asset_sizes[16])
+#define kEntranceData_cameraX ((uint16*)g_asset_ptrs[17])
+#define kEntranceData_cameraX_SIZE (g_asset_sizes[17])
+#define kEntranceData_cameraY ((uint16*)g_asset_ptrs[18])
+#define kEntranceData_cameraY_SIZE (g_asset_sizes[18])
+#define kEntranceData_blockset ((uint8*)g_asset_ptrs[19])
+#define kEntranceData_blockset_SIZE (g_asset_sizes[19])
+#define kEntranceData_floor ((int8*)g_asset_ptrs[20])
+#define kEntranceData_floor_SIZE (g_asset_sizes[20])
+#define kEntranceData_palace ((int8*)g_asset_ptrs[21])
+#define kEntranceData_palace_SIZE (g_asset_sizes[21])
+#define kEntranceData_doorwayOrientation ((uint8*)g_asset_ptrs[22])
+#define kEntranceData_doorwayOrientation_SIZE (g_asset_sizes[22])
+#define kEntranceData_startingBg ((uint8*)g_asset_ptrs[23])
+#define kEntranceData_startingBg_SIZE (g_asset_sizes[23])
+#define kEntranceData_quadrant1 ((uint8*)g_asset_ptrs[24])
+#define kEntranceData_quadrant1_SIZE (g_asset_sizes[24])
+#define kEntranceData_quadrant2 ((uint8*)g_asset_ptrs[25])
+#define kEntranceData_quadrant2_SIZE (g_asset_sizes[25])
+#define kEntranceData_doorSettings ((uint16*)g_asset_ptrs[26])
+#define kEntranceData_doorSettings_SIZE (g_asset_sizes[26])
+#define kEntranceData_musicTrack ((uint8*)g_asset_ptrs[27])
+#define kEntranceData_musicTrack_SIZE (g_asset_sizes[27])
+#define kStartingPoint_rooms ((uint16*)g_asset_ptrs[28])
+#define kStartingPoint_rooms_SIZE (g_asset_sizes[28])
+#define kStartingPoint_relativeCoords ((uint8*)g_asset_ptrs[29])
+#define kStartingPoint_relativeCoords_SIZE (g_asset_sizes[29])
+#define kStartingPoint_scrollX ((uint16*)g_asset_ptrs[30])
+#define kStartingPoint_scrollX_SIZE (g_asset_sizes[30])
+#define kStartingPoint_scrollY ((uint16*)g_asset_ptrs[31])
+#define kStartingPoint_scrollY_SIZE (g_asset_sizes[31])
+#define kStartingPoint_playerX ((uint16*)g_asset_ptrs[32])
+#define kStartingPoint_playerX_SIZE (g_asset_sizes[32])
+#define kStartingPoint_playerY ((uint16*)g_asset_ptrs[33])
+#define kStartingPoint_playerY_SIZE (g_asset_sizes[33])
+#define kStartingPoint_cameraX ((uint16*)g_asset_ptrs[34])
+#define kStartingPoint_cameraX_SIZE (g_asset_sizes[34])
+#define kStartingPoint_cameraY ((uint16*)g_asset_ptrs[35])
+#define kStartingPoint_cameraY_SIZE (g_asset_sizes[35])
+#define kStartingPoint_blockset ((uint8*)g_asset_ptrs[36])
+#define kStartingPoint_blockset_SIZE (g_asset_sizes[36])
+#define kStartingPoint_floor ((int8*)g_asset_ptrs[37])
+#define kStartingPoint_floor_SIZE (g_asset_sizes[37])
+#define kStartingPoint_palace ((int8*)g_asset_ptrs[38])
+#define kStartingPoint_palace_SIZE (g_asset_sizes[38])
+#define kStartingPoint_doorwayOrientation ((uint8*)g_asset_ptrs[39])
+#define kStartingPoint_doorwayOrientation_SIZE (g_asset_sizes[39])
+#define kStartingPoint_startingBg ((uint8*)g_asset_ptrs[40])
+#define kStartingPoint_startingBg_SIZE (g_asset_sizes[40])
+#define kStartingPoint_quadrant1 ((uint8*)g_asset_ptrs[41])
+#define kStartingPoint_quadrant1_SIZE (g_asset_sizes[41])
+#define kStartingPoint_quadrant2 ((uint8*)g_asset_ptrs[42])
+#define kStartingPoint_quadrant2_SIZE (g_asset_sizes[42])
+#define kStartingPoint_doorSettings ((uint16*)g_asset_ptrs[43])
+#define kStartingPoint_doorSettings_SIZE (g_asset_sizes[43])
+#define kStartingPoint_entrance ((uint8*)g_asset_ptrs[44])
+#define kStartingPoint_entrance_SIZE (g_asset_sizes[44])
+#define kStartingPoint_musicTrack ((uint8*)g_asset_ptrs[45])
+#define kStartingPoint_musicTrack_SIZE (g_asset_sizes[45])
+#define kDungeonRoomDefault ((uint8*)g_asset_ptrs[46])
+#define kDungeonRoomDefault_SIZE (g_asset_sizes[46])
+#define kDungeonRoomDefaultOffs ((uint16*)g_asset_ptrs[47])
+#define kDungeonRoomDefaultOffs_SIZE (g_asset_sizes[47])
+#define kDungeonRoomOverlay ((uint8*)g_asset_ptrs[48])
+#define kDungeonRoomOverlay_SIZE (g_asset_sizes[48])
+#define kDungeonRoomOverlayOffs ((uint16*)g_asset_ptrs[49])
+#define kDungeonRoomOverlayOffs_SIZE (g_asset_sizes[49])
+#define kDungeonSecrets ((uint8*)g_asset_ptrs[50])
+#define kDungeonSecrets_SIZE (g_asset_sizes[50])
+#define kDungAttrsForTile_Offs ((uint16*)g_asset_ptrs[51])
+#define kDungAttrsForTile_Offs_SIZE (g_asset_sizes[51])
+#define kDungAttrsForTile ((uint8*)g_asset_ptrs[52])
+#define kDungAttrsForTile_SIZE (g_asset_sizes[52])
+#define kMovableBlockDataInit ((uint16*)g_asset_ptrs[53])
+#define kMovableBlockDataInit_SIZE (g_asset_sizes[53])
+#define kTorchDataInit ((uint16*)g_asset_ptrs[54])
+#define kTorchDataInit_SIZE (g_asset_sizes[54])
+#define kTorchDataJunk ((uint16*)g_asset_ptrs[55])
+#define kTorchDataJunk_SIZE (g_asset_sizes[55])
+#define kEnemyDamageData ((uint8*)g_asset_ptrs[56])
+#define kEnemyDamageData_SIZE (g_asset_sizes[56])
+#define kLinkGraphics ((uint8*)g_asset_ptrs[57])
+#define kLinkGraphics_SIZE (g_asset_sizes[57])
+#define kDungeonSprites ((uint8*)g_asset_ptrs[58])
+#define kDungeonSprites_SIZE (g_asset_sizes[58])
+#define kDungeonSpriteOffs ((uint16*)g_asset_ptrs[59])
+#define kDungeonSpriteOffs_SIZE (g_asset_sizes[59])
+#define kMap32ToMap16_0 ((uint8*)g_asset_ptrs[60])
+#define kMap32ToMap16_0_SIZE (g_asset_sizes[60])
+#define kMap32ToMap16_1 ((uint8*)g_asset_ptrs[61])
+#define kMap32ToMap16_1_SIZE (g_asset_sizes[61])
+#define kMap32ToMap16_2 ((uint8*)g_asset_ptrs[62])
+#define kMap32ToMap16_2_SIZE (g_asset_sizes[62])
+#define kMap32ToMap16_3 ((uint8*)g_asset_ptrs[63])
+#define kMap32ToMap16_3_SIZE (g_asset_sizes[63])
+#define kDialogueOffs ((uint16*)g_asset_ptrs[64])
+#define kDialogueOffs_SIZE (g_asset_sizes[64])
+#define kDialogueText ((uint8*)g_asset_ptrs[65])
+#define kDialogueText_SIZE (g_asset_sizes[65])
+#define kSprGfx ((uint8*)g_asset_ptrs[66])
+#define kSprGfx_SIZE (g_asset_sizes[66])
+#define kBgGfx ((uint8*)g_asset_ptrs[67])
+#define kBgGfx_SIZE (g_asset_sizes[67])
+#define kOverworldMapGfx ((uint8*)g_asset_ptrs[68])
+#define kOverworldMapGfx_SIZE (g_asset_sizes[68])
+#define kLightOverworldTilemap ((uint8*)g_asset_ptrs[69])
+#define kLightOverworldTilemap_SIZE (g_asset_sizes[69])
+#define kDarkOverworldTilemap ((uint8*)g_asset_ptrs[70])
+#define kDarkOverworldTilemap_SIZE (g_asset_sizes[70])
+#define kPredefinedTileData ((uint16*)g_asset_ptrs[71])
+#define kPredefinedTileData_SIZE (g_asset_sizes[71])
+#define kFontData ((uint16*)g_asset_ptrs[72])
+#define kFontData_SIZE (g_asset_sizes[72])
+#define kMap16ToMap8 ((uint16*)g_asset_ptrs[73])
+#define kMap16ToMap8_SIZE (g_asset_sizes[73])
+#define kGeneratedWishPondItem ((uint8*)g_asset_ptrs[74])
+#define kGeneratedWishPondItem_SIZE (g_asset_sizes[74])
+#define kGeneratedBombosArr ((uint8*)g_asset_ptrs[75])
+#define kGeneratedBombosArr_SIZE (g_asset_sizes[75])
+#define kGeneratedEndSequence15 ((uint8*)g_asset_ptrs[76])
+#define kGeneratedEndSequence15_SIZE (g_asset_sizes[76])
+#define kEnding_Credits_Text ((uint8*)g_asset_ptrs[77])
+#define kEnding_Credits_Text_SIZE (g_asset_sizes[77])
+#define kEnding_Credits_Offs ((uint16*)g_asset_ptrs[78])
+#define kEnding_Credits_Offs_SIZE (g_asset_sizes[78])
+#define kEnding_MapData ((uint16*)g_asset_ptrs[79])
+#define kEnding_MapData_SIZE (g_asset_sizes[79])
+#define kEnding0_Offs ((uint16*)g_asset_ptrs[80])
+#define kEnding0_Offs_SIZE (g_asset_sizes[80])
+#define kEnding0_Data ((uint8*)g_asset_ptrs[81])
+#define kEnding0_Data_SIZE (g_asset_sizes[81])
+#define kPalette_DungBgMain ((uint16*)g_asset_ptrs[82])
+#define kPalette_DungBgMain_SIZE (g_asset_sizes[82])
+#define kPalette_MainSpr ((uint16*)g_asset_ptrs[83])
+#define kPalette_MainSpr_SIZE (g_asset_sizes[83])
+#define kPalette_ArmorAndGloves ((uint16*)g_asset_ptrs[84])
+#define kPalette_ArmorAndGloves_SIZE (g_asset_sizes[84])
+#define kPalette_Sword ((uint16*)g_asset_ptrs[85])
+#define kPalette_Sword_SIZE (g_asset_sizes[85])
+#define kPalette_Shield ((uint16*)g_asset_ptrs[86])
+#define kPalette_Shield_SIZE (g_asset_sizes[86])
+#define kPalette_SpriteAux3 ((uint16*)g_asset_ptrs[87])
+#define kPalette_SpriteAux3_SIZE (g_asset_sizes[87])
+#define kPalette_MiscSprite_Indoors ((uint16*)g_asset_ptrs[88])
+#define kPalette_MiscSprite_Indoors_SIZE (g_asset_sizes[88])
+#define kPalette_SpriteAux1 ((uint16*)g_asset_ptrs[89])
+#define kPalette_SpriteAux1_SIZE (g_asset_sizes[89])
+#define kPalette_OverworldBgMain ((uint16*)g_asset_ptrs[90])
+#define kPalette_OverworldBgMain_SIZE (g_asset_sizes[90])
+#define kPalette_OverworldBgAux12 ((uint16*)g_asset_ptrs[91])
+#define kPalette_OverworldBgAux12_SIZE (g_asset_sizes[91])
+#define kPalette_OverworldBgAux3 ((uint16*)g_asset_ptrs[92])
+#define kPalette_OverworldBgAux3_SIZE (g_asset_sizes[92])
+#define kPalette_PalaceMapBg ((uint16*)g_asset_ptrs[93])
+#define kPalette_PalaceMapBg_SIZE (g_asset_sizes[93])
+#define kPalette_PalaceMapSpr ((uint16*)g_asset_ptrs[94])
+#define kPalette_PalaceMapSpr_SIZE (g_asset_sizes[94])
+#define kHudPalData ((uint16*)g_asset_ptrs[95])
+#define kHudPalData_SIZE (g_asset_sizes[95])
+#define kOverworldMapPaletteData ((uint16*)g_asset_ptrs[96])
+#define kOverworldMapPaletteData_SIZE (g_asset_sizes[96])
+#define kDungMap_FloorLayout ((uint8*)g_asset_ptrs[97])
+#define kDungMap_FloorLayout_SIZE (g_asset_sizes[97])
+#define kDungMap_Tiles ((uint8*)g_asset_ptrs[98])
+#define kDungMap_Tiles_SIZE (g_asset_sizes[98])
+#define kBgTilemap_0 ((uint8*)g_asset_ptrs[99])
+#define kBgTilemap_0_SIZE (g_asset_sizes[99])
+#define kBgTilemap_1 ((uint8*)g_asset_ptrs[100])
+#define kBgTilemap_1_SIZE (g_asset_sizes[100])
+#define kBgTilemap_2 ((uint8*)g_asset_ptrs[101])
+#define kBgTilemap_2_SIZE (g_asset_sizes[101])
+#define kBgTilemap_3 ((uint8*)g_asset_ptrs[102])
+#define kBgTilemap_3_SIZE (g_asset_sizes[102])
+#define kBgTilemap_4 ((uint8*)g_asset_ptrs[103])
+#define kBgTilemap_4_SIZE (g_asset_sizes[103])
+#define kBgTilemap_5 ((uint8*)g_asset_ptrs[104])
+#define kBgTilemap_5_SIZE (g_asset_sizes[104])
+#define kOverworld_Hibytes_Comp ((uint8*)g_asset_ptrs[105])
+#define kOverworld_Hibytes_Comp_SIZE (g_asset_sizes[105])
+#define kOverworld_Lobytes_Comp ((uint8*)g_asset_ptrs[106])
+#define kOverworld_Lobytes_Comp_SIZE (g_asset_sizes[106])
+#define kOverworldMapIsSmall ((uint8*)g_asset_ptrs[107])
+#define kOverworldMapIsSmall_SIZE (g_asset_sizes[107])
+#define kOverworldAuxTileThemeIndexes ((uint8*)g_asset_ptrs[108])
+#define kOverworldAuxTileThemeIndexes_SIZE (g_asset_sizes[108])
+#define kOverworldBgPalettes ((uint8*)g_asset_ptrs[109])
+#define kOverworldBgPalettes_SIZE (g_asset_sizes[109])
+#define kOverworld_SignText ((uint16*)g_asset_ptrs[110])
+#define kOverworld_SignText_SIZE (g_asset_sizes[110])
+#define kOwMusicSets ((uint8*)g_asset_ptrs[111])
+#define kOwMusicSets_SIZE (g_asset_sizes[111])
+#define kOwMusicSets2 ((uint8*)g_asset_ptrs[112])
+#define kOwMusicSets2_SIZE (g_asset_sizes[112])
+#define kBirdTravel_ScreenIndex ((uint16*)g_asset_ptrs[113])
+#define kBirdTravel_ScreenIndex_SIZE (g_asset_sizes[113])
+#define kBirdTravel_Map16LoadSrcOff ((uint16*)g_asset_ptrs[114])
+#define kBirdTravel_Map16LoadSrcOff_SIZE (g_asset_sizes[114])
+#define kBirdTravel_ScrollX ((uint16*)g_asset_ptrs[115])
+#define kBirdTravel_ScrollX_SIZE (g_asset_sizes[115])
+#define kBirdTravel_ScrollY ((uint16*)g_asset_ptrs[116])
+#define kBirdTravel_ScrollY_SIZE (g_asset_sizes[116])
+#define kBirdTravel_LinkXCoord ((uint16*)g_asset_ptrs[117])
+#define kBirdTravel_LinkXCoord_SIZE (g_asset_sizes[117])
+#define kBirdTravel_LinkYCoord ((uint16*)g_asset_ptrs[118])
+#define kBirdTravel_LinkYCoord_SIZE (g_asset_sizes[118])
+#define kBirdTravel_CameraXScroll ((uint16*)g_asset_ptrs[119])
+#define kBirdTravel_CameraXScroll_SIZE (g_asset_sizes[119])
+#define kBirdTravel_CameraYScroll ((uint16*)g_asset_ptrs[120])
+#define kBirdTravel_CameraYScroll_SIZE (g_asset_sizes[120])
+#define kBirdTravel_Unk1 ((int8*)g_asset_ptrs[121])
+#define kBirdTravel_Unk1_SIZE (g_asset_sizes[121])
+#define kBirdTravel_Unk3 ((int8*)g_asset_ptrs[122])
+#define kBirdTravel_Unk3_SIZE (g_asset_sizes[122])
+#define kWhirlpoolAreas ((uint16*)g_asset_ptrs[123])
+#define kWhirlpoolAreas_SIZE (g_asset_sizes[123])
+#define kOverworld_Entrance_Area ((uint16*)g_asset_ptrs[124])
+#define kOverworld_Entrance_Area_SIZE (g_asset_sizes[124])
+#define kOverworld_Entrance_Pos ((uint16*)g_asset_ptrs[125])
+#define kOverworld_Entrance_Pos_SIZE (g_asset_sizes[125])
+#define kOverworld_Entrance_Id ((uint8*)g_asset_ptrs[126])
+#define kOverworld_Entrance_Id_SIZE (g_asset_sizes[126])
+#define kFallHole_Area ((uint16*)g_asset_ptrs[127])
+#define kFallHole_Area_SIZE (g_asset_sizes[127])
+#define kFallHole_Pos ((uint16*)g_asset_ptrs[128])
+#define kFallHole_Pos_SIZE (g_asset_sizes[128])
+#define kFallHole_Entrances ((uint8*)g_asset_ptrs[129])
+#define kFallHole_Entrances_SIZE (g_asset_sizes[129])
+#define kExitData_ScreenIndex ((uint8*)g_asset_ptrs[130])
+#define kExitData_ScreenIndex_SIZE (g_asset_sizes[130])
+#define kExitDataRooms ((uint16*)g_asset_ptrs[131])
+#define kExitDataRooms_SIZE (g_asset_sizes[131])
+#define kExitData_Map16LoadSrcOff ((uint16*)g_asset_ptrs[132])
+#define kExitData_Map16LoadSrcOff_SIZE (g_asset_sizes[132])
+#define kExitData_ScrollX ((uint16*)g_asset_ptrs[133])
+#define kExitData_ScrollX_SIZE (g_asset_sizes[133])
+#define kExitData_ScrollY ((uint16*)g_asset_ptrs[134])
+#define kExitData_ScrollY_SIZE (g_asset_sizes[134])
+#define kExitData_XCoord ((uint16*)g_asset_ptrs[135])
+#define kExitData_XCoord_SIZE (g_asset_sizes[135])
+#define kExitData_YCoord ((uint16*)g_asset_ptrs[136])
+#define kExitData_YCoord_SIZE (g_asset_sizes[136])
+#define kExitData_CameraXScroll ((uint16*)g_asset_ptrs[137])
+#define kExitData_CameraXScroll_SIZE (g_asset_sizes[137])
+#define kExitData_CameraYScroll ((uint16*)g_asset_ptrs[138])
+#define kExitData_CameraYScroll_SIZE (g_asset_sizes[138])
+#define kExitData_NormalDoor ((uint16*)g_asset_ptrs[139])
+#define kExitData_NormalDoor_SIZE (g_asset_sizes[139])
+#define kExitData_FancyDoor ((uint16*)g_asset_ptrs[140])
+#define kExitData_FancyDoor_SIZE (g_asset_sizes[140])
+#define kExitData_Unk1 ((int8*)g_asset_ptrs[141])
+#define kExitData_Unk1_SIZE (g_asset_sizes[141])
+#define kExitData_Unk3 ((int8*)g_asset_ptrs[142])
+#define kExitData_Unk3_SIZE (g_asset_sizes[142])
+#define kSpExit_Top ((uint16*)g_asset_ptrs[143])
+#define kSpExit_Top_SIZE (g_asset_sizes[143])
+#define kSpExit_Bottom ((uint16*)g_asset_ptrs[144])
+#define kSpExit_Bottom_SIZE (g_asset_sizes[144])
+#define kSpExit_Left ((uint16*)g_asset_ptrs[145])
+#define kSpExit_Left_SIZE (g_asset_sizes[145])
+#define kSpExit_Right ((uint16*)g_asset_ptrs[146])
+#define kSpExit_Right_SIZE (g_asset_sizes[146])
+#define kSpExit_Tab4 ((int16*)g_asset_ptrs[147])
+#define kSpExit_Tab4_SIZE (g_asset_sizes[147])
+#define kSpExit_Tab5 ((int16*)g_asset_ptrs[148])
+#define kSpExit_Tab5_SIZE (g_asset_sizes[148])
+#define kSpExit_Tab6 ((int16*)g_asset_ptrs[149])
+#define kSpExit_Tab6_SIZE (g_asset_sizes[149])
+#define kSpExit_Tab7 ((int16*)g_asset_ptrs[150])
+#define kSpExit_Tab7_SIZE (g_asset_sizes[150])
+#define kSpExit_LeftEdgeOfMap ((uint16*)g_asset_ptrs[151])
+#define kSpExit_LeftEdgeOfMap_SIZE (g_asset_sizes[151])
+#define kSpExit_Dir ((uint8*)g_asset_ptrs[152])
+#define kSpExit_Dir_SIZE (g_asset_sizes[152])
+#define kSpExit_SprGfx ((uint8*)g_asset_ptrs[153])
+#define kSpExit_SprGfx_SIZE (g_asset_sizes[153])
+#define kSpExit_AuxGfx ((uint8*)g_asset_ptrs[154])
+#define kSpExit_AuxGfx_SIZE (g_asset_sizes[154])
+#define kSpExit_PalBg ((uint8*)g_asset_ptrs[155])
+#define kSpExit_PalBg_SIZE (g_asset_sizes[155])
+#define kSpExit_PalSpr ((uint8*)g_asset_ptrs[156])
+#define kSpExit_PalSpr_SIZE (g_asset_sizes[156])
+#define kOverworldSecrets_Offs ((uint16*)g_asset_ptrs[157])
+#define kOverworldSecrets_Offs_SIZE (g_asset_sizes[157])
+#define kOverworldSecrets ((uint8*)g_asset_ptrs[158])
+#define kOverworldSecrets_SIZE (g_asset_sizes[158])
+#define kOverworldSpriteOffs ((uint16*)g_asset_ptrs[159])
+#define kOverworldSpriteOffs_SIZE (g_asset_sizes[159])
+#define kOverworldSprites ((uint8*)g_asset_ptrs[160])
+#define kOverworldSprites_SIZE (g_asset_sizes[160])
+#define kOverworldSpriteGfx ((uint8*)g_asset_ptrs[161])
+#define kOverworldSpriteGfx_SIZE (g_asset_sizes[161])
+#define kOverworldSpritePalettes ((uint8*)g_asset_ptrs[162])
+#define kOverworldSpritePalettes_SIZE (g_asset_sizes[162])
+#define kMap8DataToTileAttr ((uint8*)g_asset_ptrs[163])
+#define kMap8DataToTileAttr_SIZE (g_asset_sizes[163])
+#define kSomeTileAttr ((uint8*)g_asset_ptrs[164])
+#define kSomeTileAttr_SIZE (g_asset_sizes[164])
+#define kAssets_Sig 90, 101, 108, 100, 97, 51, 95, 118, 48, 32, 32, 32, 32, 32, 10, 0, 140, 60, 238, 107, 183, 109, 113, 156, 115, 98, 147, 236, 79, 160, 95, 71, 26, 185, 82, 158, 43, 82, 22, 60, 62, 92, 250, 152, 23, 230, 97, 235
--- a/dungeon.c
+++ b/dungeon.c
@@ -12,9 +12,9 @@
 #include "player.h"
 #include "misc.h"
 #include "player_oam.h"
-#include "tables/generated_dungeon_rooms.h"
 #include "tagalong.h"
 #include "messaging.h"
+#include "assets.h"
 
 // todo: move to config
 static const uint16 kBossRooms[] = {
@@ -2383,7 +2383,7 @@
 }
 
 bool Dungeon_IsPitThatHurtsPlayer() {
-  for (int i = countof(kDungeonPitsHurtPlayer) - 1; i >= 0; i--) {
+  for (int i = kDungeonPitsHurtPlayer_SIZE / 2 - 1; i >= 0; i--) {
     if (kDungeonPitsHurtPlayer[i] == dungeon_room_index)
       return true;
   }
@@ -5727,7 +5727,7 @@
     const uint8 *chest_data;
     int i;
     chest_data = kDungeonRoomChests;
-    for (i = 0; i < countof(kDungeonRoomChests); i += 3, chest_data += 3) {
+    for (i = 0; i < kDungeonRoomChests_SIZE; i += 3, chest_data += 3) {
       chest_room = *(uint16 *)chest_data;
       if ((chest_room & 0x7fff) == dungeon_room_index && --chest_idx < 0) {
         data = chest_data[2];
@@ -8415,10 +8415,10 @@
   player_oam_x_offset = player_oam_y_offset = 0x80;
   link_direction_mask_a = link_direction_mask_b = 0xf;
   BYTE(link_z_coord) = link_actual_vel_z = 0xff;
-  memcpy(movable_block_datas, kMovableBlockDataInit, sizeof(kMovableBlockDataInit));
+  memcpy(movable_block_datas, kMovableBlockDataInit, kMovableBlockDataInit_SIZE);
   memcpy(&movable_block_datas[99], kTorchDataInit, 116); // junk
-  memcpy(dung_torch_data, kTorchDataInit, sizeof(kTorchDataInit));
-  memcpy(&dung_torch_data[144], kTorchDataJunk, sizeof(kTorchDataJunk));
+  memcpy(dung_torch_data, kTorchDataInit, kTorchDataInit_SIZE);
+  memcpy(&dung_torch_data[144], kTorchDataJunk, kTorchDataJunk_SIZE);
 
   memset(memorized_tile_addr, 0, 0x100);
   memset(pots_revealed_in_room, 0, 0x280);
--- a/ending.c
+++ b/ending.c
@@ -10,10 +10,10 @@
 #include "misc.h"
 #include "messaging.h"
 #include "player_oam.h"
-#include "tables/generated_ending.h"
 #include "sprite_main.h"
 #include "ancilla.h"
 #include "hud.h"
+#include "assets.h"
 
 static const uint16 kPolyhedralPalette[8] = { 0, 0x14d, 0x1b0, 0x1f3, 0x256, 0x279, 0x2fd, 0x35f };
 
--- a/load_gfx.c
+++ b/load_gfx.c
@@ -4,10 +4,8 @@
 #include "overworld.h"
 #include "load_gfx.h"
 #include "player.h"
-#include "tables/generated_images.h"
-#include "tables/generated_font.h"
-#include "tables/generated_palettes.h"
 #include "sprite.h"
+#include "assets.h"
 
 static const uint16 kGlovesColor[2] = {0x52f6, 0x376};
 static const uint8 kGraphics_IncrementalVramUpload_Dst[16] = {0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f};
@@ -329,7 +327,7 @@
   return kFontData;
 }
 static const uint8 *GetCompSpritePtr(int i) {
-  return kSprGfx[i];
+  return kSprGfx + *(uint32 *)(kSprGfx + i * 4);
 }
 
 void ApplyPaletteFilter_bounce() {
@@ -1014,11 +1012,12 @@
 }
 
 int Decomp_spr(uint8 *dst, int gfx) {  // 80e772
-  return Decompress(dst, kSprGfx[gfx]);
+  return Decompress(dst, GetCompSpritePtr(gfx));
 }
 
 int Decomp_bg(uint8 *dst, int gfx) {  // 80e78f
-  return Decompress(dst, kBgGfx[gfx]);
+  const uint8 *p = kBgGfx + *(uint32 *)(kBgGfx + gfx * 4);
+  return Decompress(dst, p);
 }
 
 int Decompress(uint8 *dst, const uint8 *src) {  // 80e79e
--- a/main.c
+++ b/main.c
@@ -20,6 +20,7 @@
 
 #include "zelda_rtl.h"
 #include "config.h"
+#include "assets.h"
 
 extern Dsp *GetDspForRendering();
 extern Snes *g_snes;
@@ -40,8 +41,8 @@
 static void HandleGamepadInput(int button, bool pressed);
 static void HandleGamepadAxisInput(int gamepad_id, int axis, int value);
 static void OpenOneGamepad(int i);
+static void LoadAssets();
 
-
 enum {
   kDefaultFullscreen = 0,
   kDefaultWindowScale = 2,
@@ -176,6 +177,7 @@
   SwitchDirectory();
   ParseConfigFile();
   AfterConfigParse();
+  LoadAssets();
 
   ZeldaInitialize();
   g_zenv.ppu->extraLeftRight = UintMin(g_config.extended_aspect_ratio, kPpuExtraLeftRight);
@@ -694,4 +696,33 @@
   return result;
 }
 
+const uint8 *g_asset_ptrs[kNumberOfAssets];
+uint32 g_asset_sizes[kNumberOfAssets];
+
+static void LoadAssets() {
+  size_t length = 0;
+  uint8 *data = ReadFile("tables/zelda3_assets.dat", &length);
+  if (!data)
+    data = ReadFile("zelda3_assets.dat", &length);
+  if (!data) Die("Failed to read zelda3_assets.dat");
+
+  static const char kAssetsSig[] = { kAssets_Sig };
+
+  if (length < 16 + 32 + 32 + 8 + kNumberOfAssets * 4 ||
+      memcmp(data, kAssetsSig, 48) != 0 ||
+      *(uint32*)(data + 80) != kNumberOfAssets)
+    Die("Invalid assets file");
+
+  uint32 offset = 88 + kNumberOfAssets * 4 + *(uint32 *)(data + 84);
+
+  for (size_t i = 0; i < kNumberOfAssets; i++) {
+    uint32 size = *(uint32 *)(data + 88 + i * 4);
+    offset = (offset + 3) & ~3;
+    if ((uint64)offset + size > length)
+      Die("Assets file corruption");
+    g_asset_sizes[i] = size;
+    g_asset_ptrs[i] = data + offset;
+    offset += size;
+  }
+}
 
--- a/messaging.c
+++ b/messaging.c
@@ -15,10 +15,9 @@
 #include "player_oam.h"
 #include "attract.h"
 #include "nmi.h"
-#include "tables/generated_dialogue.h"
-#include "tables/generated_overworld_map.h"
-#include "tables/generated_dungeon_map.h"
+#include "assets.h"
 
+
 static const int8 kDungMap_Tab0[14] = {-1, -1, -1, -1, -1, 2, 0, 10, 4, 8, -1, 6, 12, 14};
 static const uint16 kDungMap_Tab1[8] = {0x2108, 0x2109, 0x2109, 0x210a, 0x210b, 0x210c, 0x210d, 0x211d};
 static const uint16 kDungMap_Tab2[8] = {0x2118, 0x2119, 0xa109, 0x211a, 0x211b, 0x211c, 0x2118, 0xa11d};
@@ -235,24 +234,6 @@
   &Text_Render,
   &RenderText_PostDeathSaveOptions,
 };
-static const uint16 kOverworldMapPaletteData[256] = {
-  0, 0x94b, 0x1563, 0x1203, 0x2995, 0x5bdf, 0x2191, 0x2e37, 0x7c1f, 0x6f37, 0x7359, 0x777a, 0x7b9b, 0x7fbd, 0, 0,
-  0, 0x100, 0, 0, 0x7b9b, 0x11b6, 0x1a9b, 0x5fff, 0x2995, 0x6e94, 0x76d6, 0x7f39, 0x7f7b, 0x7fbd, 0, 0,
-  0, 0x100, 0x1d74, 0x67f9, 0x1ee9, 0x338e, 0x6144, 0x7e6a, 0xa44, 0x7c1f, 0x6144, 0x22eb, 0x3dca, 0x5ed2, 0x7fda, 0x316a,
-  0, 0x100, 0x14cc, 0x1910, 0x2995, 0x3e3a, 0x1963, 0x15e3, 0x25f5, 0x2e37, 0x15e3, 0x22eb, 0x6144, 0x7e33, 0x5d99, 0x771d,
-  0, 0xcec, 0x22eb, 0x2fb1, 0x1d70, 0x2e37, 0x25f5, 0x3e77, 0x473a, 0x6144, 0x7e6a, 0x15e3, 0x2e0b, 0x5354, 0x7fff, 0x16a6,
-  0, 0x100, 0x15c5, 0x16a6, 0x1ee9, 0x2f4d, 0x25f5, 0x3e77, 0x473a, 0x5354, 0x15e3, 0x22eb, 0x2918, 0x4a1f, 0x3f7f, 0x7c1f,
-  0, 0x100, 0x1563, 0x1203, 0x1ee9, 0x2fb0, 0x1d70, 0x2e37, 0x473a, 0x6144, 0x15e3, 0x22eb, 0x1d70, 0x2e37, 0x4f3f, 0x7fbd,
-  0, 0, 0, 0, 0, 0, 0, 0x25f5, 0x316a, 0x5ed2, 0x7fff, 0x15e3, 0x473a, 0x2918, 0x771d, 0,
-  0, 0x18c6, 0x948, 0x118a, 0x25cf, 0x57bf, 0x1971, 0x2a18, 0x7c1f, 0x52d8, 0x5af9, 0x5f1a, 0x633b, 0x6b5c, 0, 0,
-  0, 0x18c6, 5, 0x45fc, 0x633b, 0x1dce, 0x3694, 0x4718, 0x25cf, 0x1d40, 0x34ea, 0x616f, 0x771b, 0x26d6, 0x2b18, 0x2f5a,
-  0, 0x18c6, 0x2571, 0x63da, 0x2a32, 0x3a94, 0x1d40, 0x2580, 0x7c1f, 0x7c1f, 0xcc0, 0x1ecc, 0x3135, 0x1dce, 0x4718, 0x3694,
-  0, 0x18c6, 0x14e7, 0x216c, 0x25d0, 0x3a75, 0x2169, 0x2e0e, 0x21d6, 0x2a18, 0x1971, 0x2a32, 0x1d40, 0x2580, 0x597a, 0x72fe,
-  0, 0x18c6, 0x2a32, 0x3a94, 0x2171, 0x3238, 0x29f6, 0x4278, 0x4edb, 0x1d40, 0x35cd, 0x15ab, 0x198e, 0x3254, 0x731f, 0x1ed4,
-  0, 0x18c6, 0x16a, 0x21ce, 0x2a32, 0x3a94, 0x29f6, 0x4278, 0x4edb, 0x1d40, 0x1971, 0x2a32, 0x496c, 0x5a10, 0x3b5f, 0x7c1f,
-  0, 0x18c6, 0x948, 0x118a, 0x222e, 0x32f2, 0x1951, 0x2a18, 0x431b, 0x1d40, 0x1971, 0x2a32, 0x21d4, 0x2a18, 0x4b1f, 0x7b9d,
-  0, 0x7c1f, 0x7c1f, 0x7c1f, 0x7c1f, 0x2e31, 0xe4, 0x2169, 0x2e0e, 0x42f1, 0x7c1f, 0x7c1f, 0x7c1f, 0x4a1d, 0x4e3f, 0x5a5f,
-};
 static const uint8 kOverworldMap_tab1[333] = {
   0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf,
   0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd8, 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, 0xcf, 0xce,
@@ -354,11 +335,12 @@
 static const uint8 kLocationMenuStartPos[3] = {0, 1, 6};
 static void RunInterface();
 const uint8 *GetDungmapFloorLayout() {
-  return kDungMap_FloorLayout[cur_palace_index_x2 >> 1];
+  return kDungMap_FloorLayout + *(uint32 *)(kDungMap_FloorLayout + (cur_palace_index_x2 >> 1) * 4);
 }
 
 uint8 GetOtherDungmapInfo(int count) {
-  return kDungMap_Tiles[cur_palace_index_x2 >> 1][count];
+  const uint8 *p = kDungMap_Tiles + *(uint32 *)(kDungMap_Tiles + (cur_palace_index_x2 >> 1) * 4);
+  return p[count];
 }
 
 void DungMap_4() {
--- a/misc.c
+++ b/misc.c
@@ -15,8 +15,7 @@
 #include "ending.h"
 #include "attract.h"
 #include "snes/snes_regs.h"
-#include "tables/generated_predefined_tiles.h"
-#include "tables/generated_sound_banks.h"
+#include "assets.h"
 
 static void KillAgahnim_LoadMusic();
 static void KillAghanim_Init();
--- a/nmi.c
+++ b/nmi.c
@@ -4,8 +4,7 @@
 #include "messaging.h"
 #include "snes/snes_regs.h"
 #include "snes/ppu.h"
-#include "tables/generated_bg_tilemaps.h"
-#include "tables/generated_link_graphics.h"
+#include "assets.h"
 
 static const uint8 kNmiVramAddrs[] = {
   0, 0, 4, 8, 12, 8, 12, 0, 4, 0, 8, 4, 12, 4, 12, 0,
--- a/overworld.c
+++ b/overworld.c
@@ -10,11 +10,7 @@
 #include "messaging.h"
 #include "player_oam.h"
 #include "snes/snes_regs.h"
-#include "tables/generated_map32_to_map16.h"
-#include "tables/generated_map16_to_map8.h"
-#include "tables/generated_overworld_tables.h"
-#include "tables/generated_overworld.h"
-#include "tables/generated_enemy_damage_data.h"
+#include "assets.h"
 
 const uint16 kOverworld_OffsetBaseX[64] = {
   0,     0, 0x400, 0x600, 0x600, 0xa00, 0xa00, 0xe00,
@@ -2021,7 +2017,7 @@
 }
 
 void FindPartnerWhirlpoolExit() {  // 82ed08
-  int j = FindInWordArray(kWhirlpoolAreas, overworld_screen_index, countof(kWhirlpoolAreas));
+  int j = FindInWordArray(kWhirlpoolAreas, overworld_screen_index, kWhirlpoolAreas_SIZE / 2);
   if (j >= 0) {
     num_memorized_tiles = 0;
     Overworld_LoadBirdTravelPos(j + 9);
@@ -2450,13 +2446,24 @@
   Overworld_DecompressAndDrawOneQuadrant((uint16 *)&g_ram[0x3040], si + 9);
 }
 
+static const uint8 *GetOverworldHibytes(int i) {
+  return kOverworld_Hibytes_Comp + *(uint32 *)(kOverworld_Hibytes_Comp + i * 4);
+}
+
+static const uint8 *GetOverworldLobytes(int i) {
+  return kOverworld_Lobytes_Comp + *(uint32 *)(kOverworld_Lobytes_Comp + i * 4);
+}
+
+
 void Overworld_DecompressAndDrawOneQuadrant(uint16 *dst, int screen) {  // 82f595
   int rv;
-  rv = Decompress_bank02(&g_ram[0x14400], kOverworld_Hibytes_Comp[screen]);
+
+
+  rv = Decompress_bank02(&g_ram[0x14400], GetOverworldHibytes(screen));
   for (int i = 0; i < 256; i++)
     g_ram[0x14001 + i * 2] = g_ram[0x14400 + i];
 
-  rv = Decompress_bank02(&g_ram[0x14400], kOverworld_Lobytes_Comp[screen]);
+  rv = Decompress_bank02(&g_ram[0x14400], GetOverworldLobytes(screen));
   for (int i = 0; i < 256; i++)
     g_ram[0x14000 + i * 2] = g_ram[0x14400 + i];
 
@@ -2480,6 +2487,7 @@
     int x = (a >> 1) + (a >> 2);
     const uint8 *ov;
     ov = kMap32ToMap16_0 + x;
+
     map16_decode_0[0] = ov[0];
     map16_decode_0[2] = ov[1];
     map16_decode_0[4] = ov[2];
@@ -2587,7 +2595,7 @@
 
 void DecompressEnemyDamageSubclasses() {  // 82fe71
   uint8 *tmp = &g_ram[0x14000];
-  memcpy(tmp, kEnemyDamageData, sizeof(kEnemyDamageData));
+  memcpy(tmp, kEnemyDamageData, kEnemyDamageData_SIZE);
   for (int i = 0; i < 0x1000; i += 2) {
     uint8 t = *tmp++;
     enemy_damage_data[i + 0] = t >> 4;
--- a/sprite.c
+++ b/sprite.c
@@ -11,9 +11,8 @@
 #include "misc.h"
 #include "overlord.h"
 #include "tile_detect.h"
-#include "tables/generated_dungeon_sprites.h"
 #include "sprite_main.h"
-
+#include "assets.h"
 static const uint16 kOamGetBufferPos_Tab0[6] = {0x171, 0x201, 0x31, 0xc1, 0x141, 0x1d1};
 static const uint16 kOamGetBufferPos_Tab1[48] = {
    0x30,  0x50,  0x80,  0xb0,  0xe0, 0x110, 0x140, 0x170, 0x1d0, 0x1d4, 0x1dc, 0x1e0, 0x1e4, 0x1ec, 0x1f0, 0x1f8,
--- a/tables/compile_music.py
+++ b/tables/compile_music.py
@@ -424,7 +424,7 @@
   r.extend([0, 0])
   return r
 
-def print_song(song, f):
+def print_song(song):
   global types_for_name
   types_for_name = {}
 
@@ -438,10 +438,12 @@
 
   r = produce_loadable_seq(serializer)
 
-  util.print_int_array('kSoundBank_%s' % song, r, 'uint8', False, 32, file = f)
+  result = 'kSoundBank_%s' % song, r
 
   if not compare_with_orig(serializer, song):
     raise Exception('compare failed')
+
+  return result
 
 if __name__ == "__main__":
   if len(sys.argv) == 1:
--- a/tables/compile_resources.py
+++ b/tables/compile_resources.py
@@ -5,6 +5,7 @@
 import yaml
 import tables
 import compile_music
+import array, hashlib, struct
 
 print_int_array = util.print_int_array
 
@@ -15,9 +16,27 @@
   
 def invert_dict(xs):
   return {s:i for i,s in xs.items()}
-    
+
+assets = {}
+
+
+def add_asset_uint8(name, data):
+  assert name not in assets
+  assets[name] = ('uint8', bytes(array.array('B', data)))
+
+def add_asset_uint16(name, data):
+  assert name not in assets
+  assets[name] = ('uint16', bytes(array.array('H', data)))
+
+def add_asset_int8(name, data):
+  assert name not in assets
+  assets[name] = ('int8', bytes(array.array('b', data)))
+
+def add_asset_int16(name, data):
+  assert name not in assets
+  assets[name] = ('int16', bytes(array.array('h', data)))
+
 def print_map32_to_map16():
-  f = open(PATH+'generated_map32_to_map16.h', 'w')
   tab = {}
   for line in open(PATH + 'map32_to_map16.txt'):
     line = line.strip()
@@ -39,14 +58,13 @@
     for j in range(4):
       res[j].extend(pack(tab[i][j], tab[i+1][j], tab[i+2][j], tab[i+3][j]))
 
-  print_int_array('kMap32ToMap16_0', res[0], 'uint8', True, 16, file = f)
-  print_int_array('kMap32ToMap16_1', res[1], 'uint8', True, 16, file = f)
-  print_int_array('kMap32ToMap16_2', res[2], 'uint8', True, 16, file = f)
-  print_int_array('kMap32ToMap16_3', res[3], 'uint8', True, 16, file = f)
-  f.close()
+  add_asset_uint8('kMap32ToMap16_0', res[0])
+  add_asset_uint8('kMap32ToMap16_1', res[1])
+  add_asset_uint8('kMap32ToMap16_2', res[2])
+  add_asset_uint8('kMap32ToMap16_3', res[3])
+
   
 def print_dialogue():
-  f = open(PATH+'generated_dialogue.h', 'w')
   new_r = []
   offs = []
   for line in open(PATH + 'dialogue.txt'):
@@ -57,9 +75,8 @@
     r = text_compression.compress_string(b)
     new_r.extend(r)
 
-  print_int_array('kDialogueOffs', offs, 'uint16', True, 16, file = f)   
-  print_int_array('kDialogueText', new_r, 'uint8', True, 16, file = f)
-  f.close()
+  add_asset_uint16('kDialogueOffs', offs)
+  add_asset_uint8('kDialogueText', new_r)
 
 ROM = util.LoadedRom(sys.argv[1] if len(sys.argv) >= 2 else None)
 
@@ -110,94 +127,72 @@
   rr.append(0xff)
   return rr
   
+def pack_u32_arrays(arr):
+  all_offs, offs = [], len(arr) * 4
+  for i in range(len(arr)):
+    all_offs.append(offs)
+    offs += len(arr[i])
+  return b''.join([struct.pack('I', i) for i in all_offs] + arr)
+
 def print_images():
-  rall = []
-  f = open(PATH+'generated_images.h', 'w')
+  lengths = b''
+  all = []
   for i in range(12):
-    r = ROM.get_bytes(kCompSpritePtrs[i], 0x600)
-    rall.append('kSprGfx_%d' % i)
-    print_int_array('kSprGfx_%d' % i, r, 'uint8', True, 64, file = f)
-
+    all.append(bytes(ROM.get_bytes(kCompSpritePtrs[i], 0x600)))
   for i in range(12, 108):
     decomp, comp_len = util.decomp(kCompSpritePtrs[i], ROM.get_byte, False, True)
-    r = ROM.get_bytes(kCompSpritePtrs[i], comp_len)
+    all.append(bytes(ROM.get_bytes(kCompSpritePtrs[i], comp_len)))
+  add_asset_uint8('kSprGfx', pack_u32_arrays(all))
 
-    #print('%d: %d -> %d' % (i, len(compress_store(decomp)), comp_len))
-    rall.append('kSprGfx_%d' % i)
-    print_int_array('kSprGfx_%d' % i, r, 'uint8', True, 64, file = f)
-
-  print_int_array('kSprGfx', rall, 'uint8 *const', None, 8, file = f)
-
-  rall = []
+  all = []
   for i in range(len(kCompBgPtrs)):
     decomp, comp_len = util.decomp(kCompBgPtrs[i], ROM.get_byte, False, True)
-    r = ROM.get_bytes(kCompBgPtrs[i], comp_len)
+    all.append(bytes(ROM.get_bytes(kCompBgPtrs[i], comp_len)))
+  add_asset_uint8('kBgGfx', pack_u32_arrays(all))
 
-    #print('%d: %d -> %d' % (i, len(compress_store(decomp)), comp_len))
-    rall.append('kBgGfx_%d' % i)
-    print_int_array('kBgGfx_%d' % i, r, 'uint8', True, 64, file = f)
-    
-  print_int_array('kBgGfx', rall, 'uint8 *const', None, 8, file = f)
 
-  f.close()
 
-
 def print_misc():
-  f = open(PATH+'generated_overworld_map.h', 'w')
-  print_int_array('kOverworldMapGfx', ROM.get_bytes(0x18c000, 0x4000), 'uint8', True, 64, file = f)
-  print_int_array('kLightOverworldTilemap', ROM.get_bytes(0xac727, 4096), 'uint8', True, 64, file = f)
-  print_int_array('kDarkOverworldTilemap', ROM.get_bytes(0xaD727, 1024), 'uint8', True, 64, file = f)
-  f.close()
+  add_asset_uint8('kOverworldMapGfx', ROM.get_bytes(0x18c000, 0x4000))
+  add_asset_uint8('kLightOverworldTilemap', ROM.get_bytes(0xac727, 4096))
+  add_asset_uint8('kDarkOverworldTilemap', ROM.get_bytes(0xaD727, 1024))
 
-  f = open(PATH+'generated_predefined_tiles.h', 'w')
-  print_int_array('kPredefinedTileData', ROM.get_words(0x9B52, 6438), 'uint16', False, 16, file = f)
-  f.close()
+  add_asset_uint16('kPredefinedTileData', ROM.get_words(0x9B52, 6438))
 
-  f = open(PATH+'generated_font.h', 'w')
-  print_int_array('kFontData', ROM.get_words(0xe8000, 2048), 'uint16', False, 16, file = f)
-  f.close()
+  add_asset_uint16('kFontData', ROM.get_words(0xe8000, 2048))
 
-  f = open(PATH+'generated_map16_to_map8.h', 'w')
-  print_int_array('kMap16ToMap8', ROM.get_words(0x8f8000, 3752 * 4), 'uint16', False, 16, file = f)
-  f.close()
+  add_asset_uint16('kMap16ToMap8', ROM.get_words(0x8f8000, 3752 * 4))
 
-  f = open(PATH+'generated_ancilla.h', 'w')
-  print_int_array('kGeneratedWishPondItem', ROM.get_bytes(0x888450, 256), 'uint8', False, 16, file = f)
-  print_int_array('kGeneratedBombosArr', ROM.get_bytes(0x8890FC, 256), 'uint8', False, 16, file = f)
-  f.close()
+  add_asset_uint8('kGeneratedWishPondItem', ROM.get_bytes(0x888450, 256))
+  add_asset_uint8('kGeneratedBombosArr', ROM.get_bytes(0x8890FC, 256))
 
-  f = open(PATH+'generated_ending.h', 'w')
-  print_int_array('kGeneratedEndSequence15', ROM.get_bytes(0x8ead25, 256), 'uint8', False, 16, file = f)
-  print_int_array('kEnding_Credits_Text', ROM.get_bytes(0x8EB178, 1989), 'uint8', False, 16, file = f)
-  print_int_array('kEnding_Credits_Offs', ROM.get_words(0x8EB93d, 394), 'uint16', False, 16, file = f)
-  print_int_array('kEnding_MapData', ROM.get_words(0x8EB038, 160), 'uint16', False, 16, file = f)
-  print_int_array('kEnding0_Offs', ROM.get_words(0x8EC2E1, 17), 'uint16', False, 16, file = f)
-  print_int_array('kEnding0_Data', ROM.get_bytes(0x8EBF4C, 917), 'uint8', False, 16, file = f)
-  f.close()
+  add_asset_uint8('kGeneratedEndSequence15', ROM.get_bytes(0x8ead25, 256))
+  add_asset_uint8('kEnding_Credits_Text', ROM.get_bytes(0x8EB178, 1989))
+  add_asset_uint16('kEnding_Credits_Offs', ROM.get_words(0x8EB93d, 394))
+  add_asset_uint16('kEnding_MapData', ROM.get_words(0x8EB038, 160))
+  add_asset_uint16('kEnding0_Offs', ROM.get_words(0x8EC2E1, 17))
+  add_asset_uint8('kEnding0_Data', ROM.get_bytes(0x8EBF4C, 917))
 
-  f = open(PATH+'generated_palettes.h', 'w')
-  print_int_array('kPalette_DungBgMain', ROM.get_words(0x9BD734, 1800), 'uint16', False, 15, file = f)
-  print_int_array('kPalette_MainSpr', ROM.get_words(0x9BD218, 120), 'uint16', False, 15, file = f)
+  add_asset_uint16('kPalette_DungBgMain', ROM.get_words(0x9BD734, 1800))
+  add_asset_uint16('kPalette_MainSpr', ROM.get_words(0x9BD218, 120))
 
-  print_int_array('kPalette_ArmorAndGloves', ROM.get_words(0x9BD308, 75), 'uint16', False, 15, file = f)
-  print_int_array('kPalette_Sword', ROM.get_words(0x9BD630, 12), 'uint16', False, 3, file = f)
-  print_int_array('kPalette_Shield', ROM.get_words(0x9BD648, 12), 'uint16', False, 4, file = f)
+  add_asset_uint16('kPalette_ArmorAndGloves', ROM.get_words(0x9BD308, 75))
+  add_asset_uint16('kPalette_Sword', ROM.get_words(0x9BD630, 12))
+  add_asset_uint16('kPalette_Shield', ROM.get_words(0x9BD648, 12))
 
-  print_int_array('kPalette_SpriteAux3', ROM.get_words(0x9BD39E, 84), 'uint16', False, 7, file = f)
-  print_int_array('kPalette_MiscSprite_Indoors', ROM.get_words(0x9BD446, 77), 'uint16', False, 7, file = f)
-  print_int_array('kPalette_SpriteAux1', ROM.get_words(0x9BD4E0, 168), 'uint16', False, 7, file = f)
+  add_asset_uint16('kPalette_SpriteAux3', ROM.get_words(0x9BD39E, 84))
+  add_asset_uint16('kPalette_MiscSprite_Indoors', ROM.get_words(0x9BD446, 77))
+  add_asset_uint16('kPalette_SpriteAux1', ROM.get_words(0x9BD4E0, 168))
 
-  print_int_array('kPalette_OverworldBgMain', ROM.get_words(0x9BE6C8, 210), 'uint16', False, 7, file = f)
-  print_int_array('kPalette_OverworldBgAux12', ROM.get_words(0x9BE86C, 420), 'uint16', False, 21, file = f)
-  print_int_array('kPalette_OverworldBgAux3', ROM.get_words(0x9BE604, 98), 'uint16', False, 7, file = f)
-  print_int_array('kPalette_PalaceMapBg', ROM.get_words(0x9BE544, 96), 'uint16', False,16, file = f)
-  print_int_array('kPalette_PalaceMapSpr', ROM.get_words(0x9BD70A, 21), 'uint16', False,7, file = f)
-  print_int_array('kHudPalData', ROM.get_words(0x9BD660, 64), 'uint16', False, 16, file = f)
+  add_asset_uint16('kPalette_OverworldBgMain', ROM.get_words(0x9BE6C8, 210))
+  add_asset_uint16('kPalette_OverworldBgAux12', ROM.get_words(0x9BE86C, 420))
+  add_asset_uint16('kPalette_OverworldBgAux3', ROM.get_words(0x9BE604, 98))
+  add_asset_uint16('kPalette_PalaceMapBg', ROM.get_words(0x9BE544, 96))
+  add_asset_uint16('kPalette_PalaceMapSpr', ROM.get_words(0x9BD70A, 21))
+  add_asset_uint16('kHudPalData', ROM.get_words(0x9BD660, 64))
 
-  print_int_array('kOverworldMapPaletteData', ROM.get_words(0x8ADB27, 256), 'uint16', False, 16, file = f)
+  add_asset_uint16('kOverworldMapPaletteData', ROM.get_words(0x8ADB27, 256))
 
-  f.close()
-
 g_overworld_yaml_cache = {}
 def load_overworld_yaml(room):
   if room not in g_overworld_yaml_cache:
@@ -206,26 +201,20 @@
     
 
 def print_overworld():
-  f = open(PATH+'generated_overworld.h', 'w')
-
   r = []
   for i in range(160):
     addr = ROM.get_24(0x82F94D + i * 3)
     decomp, comp_len = util.decomp(addr, ROM.get_byte, True, True)
-    r.append('kOverworld_Hibytes_Comp_%d' % i)
-    print_int_array('kOverworld_Hibytes_Comp_%d' % i, ROM.get_bytes(addr, comp_len), 'uint8', True, 64, file = f)
-  print_int_array('kOverworld_Hibytes_Comp', r, 'uint8 *const', None, 8, file = f)
+    r.append(bytes(ROM.get_bytes(addr, comp_len)))
+  add_asset_uint8('kOverworld_Hibytes_Comp', pack_u32_arrays(r))
 
   r = []
   for i in range(160):
     addr = ROM.get_24(0x82FB2D + i * 3)
     decomp, comp_len = util.decomp(addr, ROM.get_byte, True, True)
-    r.append('kOverworld_Lobytes_Comp_%d' % i)
-    print_int_array('kOverworld_Lobytes_Comp_%d' % i, ROM.get_bytes(addr, comp_len), 'uint8', True, 64, file = f)
-  print_int_array('kOverworld_Lobytes_Comp', r, 'uint8 *const', None, 8, file = f)
+    r.append(bytes(ROM.get_bytes(addr, comp_len)))
+  add_asset_uint8('kOverworld_Lobytes_Comp', pack_u32_arrays(r))
 
-  f.close()
-
 def is_area_head(i):
   return i >= 128 or ROM.get_byte(0x82A5EC + (i & 63)) == (i & 63)
 
@@ -236,11 +225,20 @@
     t = [initializer] * size
     self.arrs.append((type, name, t, items_per_line))
     setattr(self, name, t)
-  def write(self, f):
+  def write(self):
     for type, name, arr, items_per_line in self.arrs:
       for i, j in enumerate(arr):
         assert isinstance(j, int), (name, i, j, arr)
-      print_int_array(name, arr, type, True, items_per_line, file = f)
+      if type == 'uint8':
+        add_asset_uint8(name, arr)
+      elif type == 'uint16':
+        add_asset_uint16(name, arr)
+      elif type == 'int8':
+        add_asset_int8(name, arr)
+      elif type == 'int16':
+        add_asset_int16(name, arr)
+      else:
+        assert 0, type
     
 def print_overworld_tables():
   A = OutArrays()
@@ -446,37 +444,27 @@
   do_sprite_range(0, 64, 'Sprites.SecondPart', [2], 2)
   do_sprite_range(64, 144, 'Sprites', [1, 2], 3)
 
-  f = open(PATH+'generated_overworld_tables.h', 'w')
-  A.write(f)
-  print_int_array('kMap8DataToTileAttr', ROM.get_bytes(0x8E9459, 512), 'uint8', False, 16, file = f)
-  print_int_array('kSomeTileAttr', ROM.get_bytes(0x9bf110, 3824), 'uint8', False, 16, file = f)
-  f.close()  
+  A.write()
+  add_asset_uint8('kMap8DataToTileAttr', ROM.get_bytes(0x8E9459, 512))
+  add_asset_uint8('kSomeTileAttr', ROM.get_bytes(0x9bf110, 3824))
 
 
 def print_dungeon_map():
-  f = open(PATH+'generated_dungeon_map.h', 'w')
-
   r, r2 = [], []
-  name = 'kDungMap_FloorLayout'
-  name2 = 'kDungMap_Tiles'
   for i in range(14):
     kSizes = [75, 125, 50, 75, 175, 75, 50, 75, 50, 200, 150, 75, 100, 200]
     addr = 0xa0000 + ROM.get_word(0x8AF605 + i * 2)
-    r.append('%s_%d' % (name, i))
-    bytes = ROM.get_bytes(addr, kSizes[i])
-    nonzero_bytes = len(bytes) - bytes.count(0xf)
-    print_int_array(r[-1], bytes, 'uint8', False, 25, file = f)
+    b = ROM.get_bytes(addr, kSizes[i])
+    nonzero_bytes = len(b) - b.count(0xf)
+    r.append(bytes(b))
     addr = 0xa0000 + ROM.get_word(0x8AFBE4 + i * 2)
-    r2.append('%s_%d' % (name2, i))
-    bytes = ROM.get_bytes(addr, nonzero_bytes)
-    print_int_array(r2[-1], bytes, 'uint8', False, 25, file = f)
+    b = ROM.get_bytes(addr, nonzero_bytes)
+    r2.append(bytes(b))
     
-  print_int_array(name, r, 'uint8 *const', None, 4, file = f)
-  print_int_array(name2, r2, 'uint8 *const', None, 4, file = f)
-  f.close()
+  add_asset_uint8('kDungMap_FloorLayout', pack_u32_arrays(r))
+  add_asset_uint8('kDungMap_Tiles', pack_u32_arrays(r2))
 
 
-
 g_dungeon_yaml_cache = {}
 def load_dungeon_yaml(room):
   if room not in g_dungeon_yaml_cache:
@@ -516,12 +504,10 @@
         data.extend(kDropTypesToCode[s[-1]])
         
     data.append(0xff)
-  f = open(PATH+'generated_dungeon_sprites.h', 'w')
-  print_int_array('kDungeonSprites', data, 'uint8', True, 16, file = f)
-  print_int_array('kDungeonSpriteOffs', offsets, 'uint16', True, 16, file=f)
-  f.close()
+  add_asset_uint8('kDungeonSprites', data)
+  add_asset_uint16('kDungeonSpriteOffs', offsets)
 
-def print_dungeon_secrets_to_file(f):
+def print_dungeon_secrets():
   result = [None] * 640
   for i in range(320):
     y = load_dungeon_yaml(i)
@@ -541,7 +527,7 @@
       l = (len(result) - 2)
       result[i*2+0] = l & 0xff
       result[i*2+1] = l >> 8
-  print_int_array('kDungeonSecrets', result, 'uint8', True, 16, file = f)
+  add_asset_uint8('kDungeonSecrets', result)
 
 def append_scan_bytes(big, little):
   for n in range(len(little), -1, -1):
@@ -651,7 +637,7 @@
     if starting_points[i] == None:
       raise Exception('Starting point %d not defined' % i)
 
-  def print_entrance_info(f, entrances, prefix):
+  def print_entrance_info(entrances, prefix):
     def get_rc(a):
       rep, room, quads, xy = a.get('repair_scroll_bounds'), a['room'], a['quadrants'], a['player_xy']
       if rep == None: rep = (0, 0, 0, 0, 0, 0, 0, 0)
@@ -676,42 +662,41 @@
       if a[0] == 'none_0xffff': return 65535
       return ['wooden', 'bombable'].index(a[0]) << 15 | a[1] << 1 | a[2] << 7
             
-    print_int_array(prefix+'rooms', [a['room'] for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'relativeCoords', flatten([get_rc(a) for a in entrances]), 'uint8', True, 16, file = f)
+    add_asset_uint16(prefix+'rooms', [a['room'] for a in entrances])
+    add_asset_uint8(prefix+'relativeCoords', flatten([get_rc(a) for a in entrances]))
     def get_base_x(a): return ((a['room'] & 0x00f) << 9)
     def get_base_y(a): return ((a['room'] & 0x1f0) << 5)
-    print_int_array(prefix+'scrollX', [a['scroll_xy'][0] + get_base_x(a) for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'scrollY', [a['scroll_xy'][1] + get_base_y(a) for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'playerX', [a['player_xy'][0] + get_base_x(a) for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'playerY', [a['player_xy'][1] + get_base_y(a) for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'cameraX', [a['camera_xy'][0] for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'cameraY', [a['camera_xy'][1] for a in entrances], 'uint16', True, 16, file = f)
-    print_int_array(prefix+'blockset', [a['blockset'] for a in entrances], 'uint8', True, 16, file = f)
-    print_int_array(prefix+'floor', [a['floor'] for a in entrances], 'int8', True, 16, file = f)
-    print_int_array(prefix+'palace', [get_palace(a['palace']) for a in entrances], 'int8', True, 16, file = f)
-    print_int_array(prefix+'doorwayOrientation', [a['doorway_orientation'] for a in entrances], 'uint8', True, 16, file = f)
-    print_int_array(prefix+'startingBg', [a['plane'] + a['ladder_level'] * 16 for a in entrances], 'uint8', True, 16, file = f)
-    print_int_array(prefix+'quadrant1', [get_quadrant1(a['quadrants']) for a in entrances], 'uint8', True, 16, file = f)
-    print_int_array(prefix+'quadrant2', [get_quadrant2(a['quadrants']) for a in entrances], 'uint8', True, 16, file = f)
-    print_int_array(prefix+'doorSettings', [get_exit_door(a['house_exit_door']) for a in entrances], 'uint16', True, 16, file = f)
+    add_asset_uint16(prefix+'scrollX', [a['scroll_xy'][0] + get_base_x(a) for a in entrances])
+    add_asset_uint16(prefix+'scrollY', [a['scroll_xy'][1] + get_base_y(a) for a in entrances])
+    add_asset_uint16(prefix+'playerX', [a['player_xy'][0] + get_base_x(a) for a in entrances])
+    add_asset_uint16(prefix+'playerY', [a['player_xy'][1] + get_base_y(a) for a in entrances])
+    add_asset_uint16(prefix+'cameraX', [a['camera_xy'][0] for a in entrances])
+    add_asset_uint16(prefix+'cameraY', [a['camera_xy'][1] for a in entrances])
+    add_asset_uint8(prefix+'blockset', [a['blockset'] for a in entrances])
+    add_asset_int8(prefix+'floor', [a['floor'] for a in entrances])
+    add_asset_int8(prefix+'palace', [get_palace(a['palace']) for a in entrances])
+    add_asset_uint8(prefix+'doorwayOrientation', [a['doorway_orientation'] for a in entrances])
+    add_asset_uint8(prefix+'startingBg', [a['plane'] + a['ladder_level'] * 16 for a in entrances])
+    add_asset_uint8(prefix+'quadrant1', [get_quadrant1(a['quadrants']) for a in entrances])
+    add_asset_uint8(prefix+'quadrant2', [get_quadrant2(a['quadrants']) for a in entrances])
+    add_asset_uint16(prefix+'doorSettings', [get_exit_door(a['house_exit_door']) for a in entrances])
     if prefix == 'kStartingPoint_':
-      print_int_array(prefix+'entrance', [a['associated_entrance_index'] for a in entrances], 'uint8', True, 16, file = f)
+      add_asset_uint8(prefix+'entrance', [a['associated_entrance_index'] for a in entrances])
     m = invert_dict(tables.kMusicNames)
-    print_int_array(prefix+'musicTrack', [m[a['music']] for a in entrances], 'uint8', True, 16, file = f)
+    add_asset_uint8(prefix+'musicTrack', [m[a['music']] for a in entrances])
     
 
-  f = open(PATH+'generated_dungeon_rooms.h', 'w')
-  print_int_array('kDungeonRoom', data, 'uint8', True, 16, file = f)
-  print_int_array('kDungeonRoomOffs', offsets, 'uint16', True, 16, file = f)
-  print_int_array('kDungeonRoomDoorOffs', door_offsets, 'uint16', True, 16, file = f)
-  print_int_array('kDungeonRoomHeaders', room_headers, 'uint8', True, 16, file = f)
-  print_int_array('kDungeonRoomHeadersOffs', header_offsets, 'uint16', True, 16, file = f)
-  print_int_array('kDungeonRoomChests', chests, 'uint8', True, 16, file = f)
-  print_int_array('kDungeonRoomTeleMsg', sign_texts, 'uint16', True, 16, file = f)
-  print_int_array('kDungeonPitsHurtPlayer', pits_hurt_player, 'uint16', True, 16, file = f)
+  add_asset_uint8('kDungeonRoom', data)
+  add_asset_uint16('kDungeonRoomOffs', offsets)
+  add_asset_uint16('kDungeonRoomDoorOffs', door_offsets)
+  add_asset_uint8('kDungeonRoomHeaders', room_headers)
+  add_asset_uint16('kDungeonRoomHeadersOffs', header_offsets)
+  add_asset_uint8('kDungeonRoomChests', chests)
+  add_asset_uint16('kDungeonRoomTeleMsg', sign_texts)
+  add_asset_uint16('kDungeonPitsHurtPlayer', pits_hurt_player)
   
-  print_entrance_info(f, entrances, 'kEntranceData_')
-  print_entrance_info(f, starting_points, 'kStartingPoint_')
+  print_entrance_info(entrances, 'kEntranceData_')
+  print_entrance_info(starting_points, 'kStartingPoint_')
 
   data = []
   offsets = [0] * 8
@@ -719,8 +704,8 @@
   for i in range(len(offsets)):
     offsets[i] = len(data)
     print_layer(default_yaml['Default%d' % i], None)
-  print_int_array('kDungeonRoomDefault', data, 'uint8', True, 16, file = f)
-  print_int_array('kDungeonRoomDefaultOffs', offsets, 'uint16', True, 16, file = f)
+  add_asset_uint8('kDungeonRoomDefault', data)
+  add_asset_uint16('kDungeonRoomDefaultOffs', offsets)
 
   data = []
   offsets = [0] * 19
@@ -728,30 +713,25 @@
   for i in range(len(offsets)):
     offsets[i] = len(data)
     print_layer(overlay_yaml['Overlay%d' % i], None)
-  print_int_array('kDungeonRoomOverlay', data, 'uint8', True, 16, file = f)
-  print_int_array('kDungeonRoomOverlayOffs', offsets, 'uint16', True, 16, file = f)
+  add_asset_uint8('kDungeonRoomOverlay', data)
+  add_asset_uint16('kDungeonRoomOverlayOffs', offsets)
 
-  print_dungeon_secrets_to_file(f)
+  print_dungeon_secrets()
 
-  print_int_array('kDungAttrsForTile_Offs', ROM.get_words(0x8e9000, 21), 'uint16', False, 16, file = f)
-  print_int_array('kDungAttrsForTile', ROM.get_bytes(0x8e902a, 1024), 'uint8', False, 16, file = f)
+  add_asset_uint16('kDungAttrsForTile_Offs', ROM.get_words(0x8e9000, 21))
+  add_asset_uint8('kDungAttrsForTile', ROM.get_bytes(0x8e902a, 1024))
 
-  print_int_array('kMovableBlockDataInit', ROM.get_words(0x84f1de, 198), 'uint16', False, 16, file = f)
-  print_int_array('kTorchDataInit', ROM.get_words(0x84F36A, 144), 'uint16', False, 16, file = f)
-  print_int_array('kTorchDataJunk', ROM.get_words(0x84F48a, 48), 'uint16', False, 16, file = f)
+  add_asset_uint16('kMovableBlockDataInit', ROM.get_words(0x84f1de, 198))
+  add_asset_uint16('kTorchDataInit', ROM.get_words(0x84F36A, 144))
+  add_asset_uint16('kTorchDataJunk', ROM.get_words(0x84F48a, 48))
 
-  f.close()
 
-
  
 def print_enemy_damage_data():
-  f = open(PATH+'generated_enemy_damage_data.h', 'w')
   decomp, comp_len = util.decomp(0x83e800, ROM.get_byte, True, True)
-  print_int_array('kEnemyDamageData', decomp, 'uint8', False, 32, file = f)
-  f.close()
+  add_asset_uint8('kEnemyDamageData', decomp)
 
 def print_tilemaps():
-  f = open(PATH+'generated_bg_tilemaps.h', 'w')
   kSrcs = [0xcdd6d, 0xce7bf, 0xce2a8, 0xce63c, 0xce456, 0xeda9c]
   def decode_one(p):
     p_org = p
@@ -763,11 +743,9 @@
     return p - p_org + 1
   for i,s in enumerate(kSrcs):
     l = decode_one(s)
-    print_int_array('kBgTilemap_%d' % i, ROM.get_bytes(s, l), 'uint8', False, 32, file = f)
-  f.close()
+    add_asset_uint8('kBgTilemap_%d' % i, ROM.get_bytes(s, l))
 
 def print_link_graphics():
-  f = open(PATH+'generated_link_graphics.h', 'w')
   image = Image.open(PATH+'linksprite.png')
   data = image.tobytes()
   def encode_4bit_sprite(data, offset, pitch):
@@ -784,18 +762,14 @@
   for y in range(56):
     for x in range(16):
       b += encode_4bit_sprite(data, y * 128 * 8 + x * 8, 128)
-  print_int_array('kLinkGraphics', b, 'uint8', False, 32, file = f)
-  #print(list(encode_4bit_sprite(data, 0, 128)))
-  f.close()
+  add_asset_uint8('kLinkGraphics', b)
 
 def print_sound_banks():
-  f = open(PATH+'generated_sound_banks.h', 'w')
   for song in ['intro', 'indoor', 'ending']:
-    compile_music.print_song(song, f)
-  f.close()
+    name, data = compile_music.print_song(song)
+    add_asset_uint8(name, data)
 
 
-
 def print_all():
   print_sound_banks()
   print_dungeon_rooms()
@@ -812,4 +786,46 @@
   print_overworld_tables()
 
 print_all()
+
+
+def write_assets_to_file(print_header = False):
+  key_sig = b''
+  all_data = []
+  if print_header:
+    print('''#pragma once
+#include "types.h"
+
+enum {
+  kNumberOfAssets = %d
+};
+extern const uint8 *g_asset_ptrs[kNumberOfAssets];
+extern uint32 g_asset_sizes[kNumberOfAssets];''' % len(assets))
+
+  for i, (k, (tp, data)) in enumerate(assets.items()):
+    if print_header:
+      print('#define %s ((%s*)g_asset_ptrs[%d])' % (k, tp, i))
+      print('#define %s_SIZE (g_asset_sizes[%d])' % (k, i))
+    key_sig += k.encode('utf8') + b'\0'
+    all_data.append(data)
+
+  assets_sig = b'Zelda3_v0     \n\0' + hashlib.sha256(key_sig).digest()
+
+  if print_header:
+    print('#define kAssets_Sig %s' % ", ".join((str(a) for a in assets_sig)))
+
+  hdr = assets_sig + b'\x00' * 32 + struct.pack('II', len(all_data), len(key_sig))
+
+  encoded_sizes = array.array('I', [len(i) for i in all_data])
+
+  file_data = hdr + encoded_sizes + key_sig
+
+  for v in all_data:
+    while len(file_data) & 3:
+      file_data += b'\0'
+    file_data += v
+
+  open('zelda3_assets.dat', 'wb').write(file_data)
+
+write_assets_to_file(False)
+
 
--- a/zelda3.vcxproj
+++ b/zelda3.vcxproj
@@ -226,6 +226,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="ancilla.h" />
+    <ClInclude Include="assets.h" />
     <ClInclude Include="attract.h" />
     <ClInclude Include="config.h" />
     <ClInclude Include="dungeon.h" />
--- a/zelda3.vcxproj.filters
+++ b/zelda3.vcxproj.filters
@@ -246,6 +246,9 @@
     <ClInclude Include="platform\win32\volume_control.h">
       <Filter>Platform</Filter>
     </ClInclude>
+    <ClInclude Include="assets.h">
+      <Filter>Zelda</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
@@ -260,4 +263,4 @@
       <Filter>Resource Files</Filter>
     </ResourceCompile>
   </ItemGroup>
-</Project>
+</Project>
\ No newline at end of file