diff --git a/Makefile b/Makefile index 56404185..f8078859 100644 --- a/Makefile +++ b/Makefile @@ -42,8 +42,8 @@ TEXTURE_FIX ?= 0 EXT_OPTIONS_MENU ?= 1 # Disable text-based save-files by default TEXTSAVES ?= 0 -# Load textures from external PNG files -EXTERNAL_TEXTURES ?= 0 +# Load resources from external files +EXTERNAL_DATA ?= 0 # Various workarounds for weird toolchains @@ -274,7 +274,7 @@ else ifeq ($(VERSION),sh) OPT_FLAGS := -O2 else - OPT_FLAGS := -g + OPT_FLAGS := -O2 endif endif @@ -555,9 +555,9 @@ ifeq ($(LEGACY_GL),1) endif # Load external textures -ifeq ($(EXTERNAL_TEXTURES),1) - CC_CHECK += -DEXTERNAL_TEXTURES - CFLAGS += -DEXTERNAL_TEXTURES +ifeq ($(EXTERNAL_DATA),1) + CC_CHECK += -DEXTERNAL_DATA + CFLAGS += -DEXTERNAL_DATA # tell skyconv to write names instead of actual texture data and save the split tiles so we can use them later SKYCONV_ARGS := --store-names --write-tiles "$(BUILD_DIR)/textures/skybox_tiles" endif @@ -617,15 +617,20 @@ ZEROTERM = $(PYTHON) $(TOOLS_DIR)/zeroterm.py all: $(EXE) -ifeq ($(EXTERNAL_TEXTURES),1) +ifeq ($(EXTERNAL_DATA),1) # depend on resources as well all: res # prepares the resource folder for external data res: $(EXE) @mkdir -p $(BUILD_DIR)/res + @mkdir -p $(BUILD_DIR)/res/sound @cp -r -f textures/ $(BUILD_DIR)/res/ @cp -r -f $(BUILD_DIR)/textures/skybox_tiles/ $(BUILD_DIR)/res/textures/ + @cp -f $(SOUND_BIN_DIR)/sound_data.ctl $(BUILD_DIR)/res/sound/ + @cp -f $(SOUND_BIN_DIR)/sound_data.tbl $(BUILD_DIR)/res/sound/ + @cp -f $(SOUND_BIN_DIR)/sequences.bin $(BUILD_DIR)/res/sound/ + @cp -f $(SOUND_BIN_DIR)/bank_sets $(BUILD_DIR)/res/sound/ @find actors -name \*.png -exec cp --parents {} $(BUILD_DIR)/res/ \; @find levels -name \*.png -exec cp --parents {} $(BUILD_DIR)/res/ \; endif @@ -724,7 +729,7 @@ endif ################################################################ # RGBA32, RGBA16, IA16, IA8, IA4, IA1, I8, I4 -ifeq ($(EXTERNAL_TEXTURES),1) +ifeq ($(EXTERNAL_DATA),1) $(BUILD_DIR)/%: %.png $(ZEROTERM) "$(patsubst %.png,%,$^)" > $@ else @@ -736,7 +741,7 @@ $(BUILD_DIR)/%.inc.c: $(BUILD_DIR)/% %.png hexdump -v -e '1/1 "0x%X,"' $< > $@ echo >> $@ -ifeq ($(EXTERNAL_TEXTURES),0) +ifeq ($(EXTERNAL_DATA),0) # Color Index CI8 $(BUILD_DIR)/%.ci8: %.ci8.png $(N64GRAPHICS_CI) -i $@ -g $< -f ci8 @@ -787,6 +792,18 @@ $(SOUND_BIN_DIR)/%.m64: $(SOUND_BIN_DIR)/%.o $(SOUND_BIN_DIR)/%.o: $(SOUND_BIN_DIR)/%.s $(AS) $(ASFLAGS) -o $@ $< +ifeq ($(EXTERNAL_DATA),1) + +$(SOUND_BIN_DIR)/sound_data.ctl.c: $(SOUND_BIN_DIR)/sound_data.ctl + echo "unsigned char gSoundDataADSR[] = \"sound/sound_data.ctl\";" > $@ +$(SOUND_BIN_DIR)/sound_data.tbl.c: $(SOUND_BIN_DIR)/sound_data.tbl + echo "unsigned char gSoundDataRaw[] = \"sound/sound_data.tbl\";" > $@ +$(SOUND_BIN_DIR)/sequences.bin.c: $(SOUND_BIN_DIR)/sequences.bin + echo "unsigned char gMusicData[] = \"sound/sequences.bin\";" > $@ +$(SOUND_BIN_DIR)/bank_sets.c: $(SOUND_BIN_DIR)/bank_sets + echo "unsigned char gBankSetsData[] = \"sound/bank_sets\";" > $@ + +else $(SOUND_BIN_DIR)/sound_data.ctl.c: $(SOUND_BIN_DIR)/sound_data.ctl echo "unsigned char gSoundDataADSR[] = {" > $@ @@ -808,6 +825,8 @@ $(SOUND_BIN_DIR)/bank_sets.c: $(SOUND_BIN_DIR)/bank_sets hexdump -v -e '1/1 "0x%X,"' $< >> $@ echo "};" >> $@ +endif + $(BUILD_DIR)/levels/scripts.o: $(BUILD_DIR)/include/level_headers.h $(BUILD_DIR)/include/level_headers.h: levels/level_headers.h.in diff --git a/src/audio/load.c b/src/audio/load.c index 146863c1..affc6081 100644 --- a/src/audio/load.c +++ b/src/audio/load.c @@ -6,6 +6,8 @@ #include "data.h" #include "seqplayer.h" +#include "../pc/platform.h" + #define ALIGN16(val) (((val) + 0xF) & ~0xF) struct SharedDma { @@ -868,6 +870,24 @@ void load_sequence_internal(u32 player, u32 seqId, s32 loadAsync) { seqPlayer->scriptState.pc = sequenceData; } +#ifdef EXTERNAL_DATA +# define LOAD_DATA(x) load_sound_res((const char *)x) +# include +# include +static inline void *load_sound_res(const char *path) { + void *data = sys_load_res(path); + if (!data) { + fprintf(stderr, "could not load sound data from '%s'\n", path); + abort(); + } + // FIXME: figure out where it is safe to free this shit + // can't free it immediately after in audio_init() + return data; +} +#else +# define LOAD_DATA(x) x +#endif + void audio_init() { #ifdef VERSION_EU UNUSED s8 pad[16]; @@ -966,7 +986,7 @@ void audio_init() { // Load header for sequence data (assets/music_data.sbk.s) gSeqFileHeader = (ALSeqFile *) buf; - data = gMusicData; + data = LOAD_DATA(gMusicData); audio_dma_copy_immediate((uintptr_t) data, gSeqFileHeader, 0x10); gSequenceCount = gSeqFileHeader->seqCount; #ifdef VERSION_EU @@ -981,7 +1001,7 @@ void audio_init() { // Load header for CTL (assets/sound_data.ctl.s, i.e. ADSR) gAlCtlHeader = (ALSeqFile *) buf; - data = gSoundDataADSR; + data = LOAD_DATA(gSoundDataADSR); audio_dma_copy_immediate((uintptr_t) data, gAlCtlHeader, 0x10); size = gAlCtlHeader->seqCount * sizeof(ALSeqData) + 4; size = ALIGN16(size); @@ -996,14 +1016,16 @@ void audio_init() { size = gAlTbl->seqCount * sizeof(ALSeqData) + 4; size = ALIGN16(size); gAlTbl = soundAlloc(&gAudioInitPool, size); - audio_dma_copy_immediate((uintptr_t) gSoundDataRaw, gAlTbl, size); - alSeqFileNew(gAlTbl, gSoundDataRaw); + + data = LOAD_DATA(gSoundDataRaw); + audio_dma_copy_immediate((uintptr_t) data, gAlTbl, size); + alSeqFileNew(gAlTbl, data); // Load bank sets for each sequence (assets/bank_sets.s) + data = LOAD_DATA(gBankSetsData); gAlBankSets = soundAlloc(&gAudioInitPool, 0x100); - audio_dma_copy_immediate((uintptr_t) gBankSetsData, gAlBankSets, 0x100); + audio_dma_copy_immediate((uintptr_t) data, gAlBankSets, 0x100); init_sequence_players(); gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; } - diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index 9711ec7e..7c28c293 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -128,7 +128,7 @@ u8 gMenuHoldKeyIndex = 0; u8 gMenuHoldKeyTimer = 0; s32 gDialogResponse = 0; -#if !defined(EXTERNAL_TEXTURES) && (defined(VERSION_JP) || defined(VERSION_SH) || defined(VERSION_EU)) +#if !defined(EXTERNAL_DATA) && (defined(VERSION_JP) || defined(VERSION_SH) || defined(VERSION_EU)) #ifdef VERSION_EU #define CHCACHE_BUFLEN (8 * 8) // EU only converts 8x8 #else @@ -242,7 +242,7 @@ static inline void alloc_ia8_text_from_i1(u8 *out, u16 *in, s16 width, s16 heigh } static inline u8 *convert_ia8_char(u8 c, u16 *tex, s16 w, s16 h) { -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA return (u8 *)tex; // the data's just a name #else if (!tex) return NULL; @@ -303,7 +303,7 @@ static void alloc_ia4_tex_from_i1(u8 *out, u8 *in, s16 width, s16 height) { } static u8 *convert_ia4_char(u8 c, u8 *tex, s16 w, s16 h) { -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA return tex; // the data's just a name #else if (!tex) return NULL; diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 9c9038a4..f404773a 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -67,7 +67,7 @@ unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID }; unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID }; unsigned int configStickDeadzone = 16; // 16*DEADZONE_STEP=4960 (the original default deadzone) -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA bool configPrecacheRes = false; #endif #ifdef BETTERCAMERA @@ -109,7 +109,7 @@ static const struct ConfigOption options[] = { {.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft}, {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight}, {.name = "stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &configStickDeadzone}, - #ifdef EXTERNAL_TEXTURES + #ifdef EXTERNAL_DATA {.name = "precache", .type = CONFIG_TYPE_BOOL, .boolValue = &configPrecacheRes}, #endif #ifdef BETTERCAMERA diff --git a/src/pc/configfile.h b/src/pc/configfile.h index d6087612..3802d07b 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -36,7 +36,7 @@ extern unsigned int configKeyStickDown[]; extern unsigned int configKeyStickLeft[]; extern unsigned int configKeyStickRight[]; extern unsigned int configStickDeadzone; -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA extern bool configPrecacheRes; #endif #ifdef BETTERCAMERA diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index 6bd2fca4..1e86ec5a 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -6,7 +6,7 @@ #include #include -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA #define STB_IMAGE_IMPLEMENTATION #include #endif @@ -47,7 +47,7 @@ #define MAX_LIGHTS 2 #define MAX_VERTICES 64 -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA # define MAX_CACHED_TEXTURES 4096 // for preloading purposes # define HASH_SHIFT 0 #else @@ -173,7 +173,7 @@ static size_t buf_vbo_num_tris; static struct GfxWindowManagerAPI *gfx_wapi; static struct GfxRenderingAPI *gfx_rapi; -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA static inline size_t string_hash(const uint8_t *str) { size_t h = 0; for (const uint8_t *p = str; *p; p++) @@ -275,9 +275,9 @@ static struct ColorCombiner *gfx_lookup_or_create_color_combiner(uint32_t cc_id) } static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, const uint8_t *orig_addr, uint32_t fmt, uint32_t siz) { - #ifdef EXTERNAL_TEXTURES // hash and compare the data (i.e. the texture name) itself + #ifdef EXTERNAL_DATA // hash and compare the data (i.e. the texture name) itself size_t hash = string_hash(orig_addr); - #define CMPADDR(x, y) (x && !sys_strcasecmp(x, y)) + #define CMPADDR(x, y) (x && !sys_strcasecmp((const char *)x, (const char *)y)) #else // hash and compare the address size_t hash = (uintptr_t)orig_addr; #define CMPADDR(x, y) x == y @@ -318,7 +318,7 @@ static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, co #undef CMPADDR } -#ifndef EXTERNAL_TEXTURES +#ifndef EXTERNAL_DATA static void import_texture_rgba32(int tile) { uint32_t width = rdp.texture_tile.line_size_bytes / 2; @@ -492,7 +492,7 @@ static void import_texture_ci8(int tile) { gfx_rapi->upload_texture(rgba32_buf, width, height); } -#else // EXTERNAL_TEXTURES +#else // EXTERNAL_DATA // this is taken straight from n64graphics static bool texname_to_texformat(const char *name, u8 *fmt, u8 *siz) { @@ -595,7 +595,7 @@ static inline void load_texture(const char *name) { stbi_image_free(data); // don't need this anymore } -#endif // EXTERNAL_TEXTURES +#endif // EXTERNAL_DATA static void import_texture(int tile) { uint8_t fmt = rdp.texture_tile.fmt; @@ -611,7 +611,7 @@ static void import_texture(int tile) { return; } -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA // the "texture data" is actually a C string with the path to our texture in it // load it from an external image in our data path load_texture((const char*)rdp.loaded_texture[tile].addr); @@ -1761,7 +1761,7 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi) { for (size_t i = 0; i < sizeof(precomp_shaders) / sizeof(uint32_t); i++) { gfx_lookup_or_create_shader_program(precomp_shaders[i]); } - #ifdef EXTERNAL_TEXTURES + #ifdef EXTERNAL_DATA // preload all textures if needed if (configPrecacheRes) { printf("Precaching textures from `%s`\n", sys_data_path()); diff --git a/src/pc/platform.c b/src/pc/platform.c index 00cfa289..80c0eb74 100644 --- a/src/pc/platform.c +++ b/src/pc/platform.c @@ -86,6 +86,29 @@ bool sys_dir_walk(const char *base, walk_fn_t walk, const bool recur) { return ret; } +void *sys_load_res(const char *name) { + char path[SYS_MAX_PATH] = { 0 }; + snprintf(path, sizeof(path), "%s/%s", sys_data_path(), name); + + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + void *buf = malloc(size); + if (!buf) { + fclose(f); + return NULL; + } + + fread(buf, 1, size, f); + fclose(f); + + return buf; +} + bool sys_mkdir(const char *name) { #ifdef _WIN32 return _mkdir(name) == 0; diff --git a/src/pc/platform.h b/src/pc/platform.h index 9b498470..30b33dcc 100644 --- a/src/pc/platform.h +++ b/src/pc/platform.h @@ -19,6 +19,7 @@ int sys_strcasecmp(const char *s1, const char *s2); bool sys_mkdir(const char *name); // creates with 0777 by default bool sys_file_exists(const char *name); bool sys_dir_exists(const char *name); +void *sys_load_res(const char *name); // receives the full path // should return `true` if traversal should continue