diff --git a/Makefile b/Makefile index 56404185..49b60620 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 @@ -60,8 +60,20 @@ NON_MATCHING := 1 GRUCODE := f3dex2e WINDOWS_BUILD ?= 0 -ifeq ($(TARGET_WEB),0) +# Attempt to detect OS + ifeq ($(OS),Windows_NT) + HOST_OS ?= Windows +else + HOST_OS ?= $(shell uname -s 2>/dev/null || echo Unknown) + # some weird MINGW/Cygwin env that doesn't define $OS + ifneq (,$(findstring MINGW,HOST_OS)) + HOST_OS := Windows + endif +endif + +ifeq ($(TARGET_WEB),0) +ifeq ($(HOST_OS),Windows) WINDOWS_BUILD := 1 endif endif @@ -274,7 +286,7 @@ else ifeq ($(VERSION),sh) OPT_FLAGS := -O2 else - OPT_FLAGS := -g + OPT_FLAGS := -O2 endif endif @@ -555,9 +567,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,17 +629,30 @@ ZEROTERM = $(PYTHON) $(TOOLS_DIR)/zeroterm.py all: $(EXE) -ifeq ($(EXTERNAL_TEXTURES),1) +ifeq ($(EXTERNAL_DATA),1) + +# thank you apple very cool +ifeq ($(HOST_OS),Darwin) + CP := gcp +else + CP := cp +endif + # depend on resources as well all: res # prepares the resource folder for external data res: $(EXE) - @mkdir -p $(BUILD_DIR)/res - @cp -r -f textures/ $(BUILD_DIR)/res/ - @cp -r -f $(BUILD_DIR)/textures/skybox_tiles/ $(BUILD_DIR)/res/textures/ - @find actors -name \*.png -exec cp --parents {} $(BUILD_DIR)/res/ \; - @find levels -name \*.png -exec cp --parents {} $(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 clean: @@ -724,7 +749,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 +761,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 +812,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 +845,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/SAVE_FORMAT.MD b/SAVE_FORMAT.MD index c11d9194..5f44a011 100644 --- a/SAVE_FORMAT.MD +++ b/SAVE_FORMAT.MD @@ -34,14 +34,14 @@ This section contains two flags for menu. | Flag | Value | Description | |---|---|---| -| coin_score_age | ?? | Each save file has a 2 bit "age" for each course. The higher this value, the older the high score is. This is used for tie-breaking when displaying on the high score screen -| sound_mode | ?? | Sound mode for the game: stereo, mono, or headset +| coin_score_age | 0, 1, 2, 3 | Each save file has a 2 bit "age" for each course. The higher this value, the older the high score is. This is used for tie-breaking when displaying on the high score screen +| sound_mode | stereo, mono, headset | Sound mode for the game Example: ``` [menu] -coin_score_age = ?? -sound_mode = ?? +coin_score_age = 0 +sound_mode = stereo ``` ___ ## Flags Section - [flags] @@ -145,14 +145,14 @@ ___ This section contains information about where Mario lost his cap and who take it. | Flag | Value | Description | |---|---|---| -| type | ground, klepto, ukiki, mrblizzard | The one who or what took the cap from Mario. -| level | ?? | Specifies the course where the cap is located. -| area | ?? | Specifies the area in the course. +| type | ground, klepto, ukiki, mrblizzard | The one who or what took the cap from Mario. Default flag is **"ground"** +| level | ssl, sl, ttm, none | Specifies the course where the cap is located. Default flag is **"none"**. +| area | 1, 2 | Specifies the area in the course. Example: ``` [cap] -type = onground -level = 5 -area = 0 +type = ground +level = ssl +area = 1 ``` \ No newline at end of file diff --git a/doxygen/logo.png b/doxygen/logo.png index f01a8654..0980cc0c 100644 Binary files a/doxygen/logo.png and b/doxygen/logo.png differ 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/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c index f652ef47..ed1d767e 100644 --- a/src/game/behaviors/fish.inc.c +++ b/src/game/behaviors/fish.inc.c @@ -62,11 +62,13 @@ void fish_act_spawn(void) { * Y coordinate is greater than 2000.0f then spawn another fish. */ void fish_act_respawn(void) { +#ifndef NODRAWINGDISTANCE if (gCurrLevelNum != LEVEL_SA) { if (gMarioObject->oPosY - o->oPosY > 2000.0f) { o->oAction = FISH_ACT_RESPAWN; } } +#endif } /** 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/game/save_file.c b/src/game/save_file.c index 1b460bb8..a8db910b 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -1,5 +1,4 @@ #include -#include #include "sm64.h" #include "game_init.h" #include "main.h" diff --git a/src/game/text_save.inc.h b/src/game/text_save.inc.h index b84ab1b2..a5a8f54f 100644 --- a/src/game/text_save.inc.h +++ b/src/game/text_save.inc.h @@ -1,4 +1,8 @@ #include +#include +#include +#include "course_table.h" +#include "pc/ini.h" #define FILENAME_FORMAT "save_file_%d.sav" #define NUM_COURSES 15 @@ -6,7 +10,7 @@ #define NUM_FLAGS 21 #define NUM_CAP_ON 4 -/* Flag key */ +/* Flag keys */ const char *sav_flags[NUM_FLAGS] = { "file_exists", "wing_cap", "metal_cap", "vanish_cap", "key_1", "key_2", "basement_door", "upstairs_door", "ddd_moved_back", "moat_drained", @@ -31,6 +35,11 @@ const char *cap_on_types[NUM_CAP_ON] = { "ground", "klepto", "ukiki", "mrblizzard" }; +/* Sound modes */ +const char *sound_modes[3] = { + "stereo", "mono", "headset" +}; + /* Get current timestamp */ static void get_timestamp(char* buffer) { time_t timer; @@ -75,7 +84,7 @@ static s32 write_text_save(s32 fileIndex) { struct MainMenuSaveData *menudata; char filename[32] = { 0 }; char value[32] = { 0 }; - u32 i, flags, coins, stars, starFlags; + u32 i, bit, flags, coins, stars, starFlags; /* Define savefile's name */ if (sprintf(filename, FILENAME_FORMAT, fileIndex) < 0) @@ -84,6 +93,8 @@ static s32 write_text_save(s32 fileIndex) { file = fopen(filename, "wt"); if (file == NULL) return -1; + else + printf("Updating savefile in '%s'\n", filename); /* Write header */ fprintf(file, "# Super Mario 64 save file\n"); @@ -98,8 +109,22 @@ static s32 write_text_save(s32 fileIndex) { menudata = &gSaveBuffer.menuData[0]; fprintf(file, "\n[menu]\n"); fprintf(file, "coin_score_age = %d\n", menudata->coinScoreAges[fileIndex]); - fprintf(file, "sound_mode = %u\n", menudata->soundMode); - + + /* Sound mode */ + if (menudata->soundMode == 0) { + fprintf(file, "sound_mode = %s\n", sound_modes[0]); // stereo + } + else if (menudata->soundMode == 3) { + fprintf(file, "sound_mode = %s\n", sound_modes[1]); // mono + } + else if (menudata->soundMode == 1) { + fprintf(file, "sound_mode = %s\n", sound_modes[2]); // headset + } + else { + printf("Undefined sound mode!"); + return -1; + } + /* Write all flags */ fprintf(file, "\n[flags]\n"); for (i = 1; i < NUM_FLAGS; i++) { @@ -139,7 +164,8 @@ static s32 write_text_save(s32 fileIndex) { fprintf(file, "\n[cap]\n"); for (i = 0; i < NUM_CAP_ON; i++) { flags = save_file_get_flags(); // Read all flags - flags = (flags & (1 << (i+16))); // Get `cap` flags + bit = (1 << (i+16)); // Determine current flag + flags = (flags & bit); // Get `cap` flag flags = (flags) ? 1 : 0; // Determine if bit is set or not if (flags) { fprintf(file, "type = %s\n", cap_on_types[i]); @@ -149,7 +175,20 @@ static s32 write_text_save(s32 fileIndex) { /* Write in what course and area Mario losted its cap, and cap's position */ savedata = &gSaveBuffer.files[fileIndex][0]; - fprintf(file, "level = %d\n", savedata->capLevel); + switch(savedata->capLevel) { + case COURSE_SSL: + fprintf(file, "level = %s\n", "ssl"); + break; + case COURSE_SL: + fprintf(file, "level = %s\n", "sl"); + break; + case COURSE_TTM: + fprintf(file, "level = %s\n", "ttm"); + break; + default: + fprintf(file, "level = %s\n", "none"); + break; + } fprintf(file, "area = %d\n", savedata->capArea); fclose(file); @@ -166,8 +205,7 @@ static s32 read_text_save(s32 fileIndex) { ini_t *savedata; u32 i, flag, coins, stars, starFlags; - u32 capLevel, capArea; - Vec3s capPos; + u32 capArea; /* Define savefile's name */ if (sprintf(filename, FILENAME_FORMAT, fileIndex) < 0) @@ -178,15 +216,30 @@ static s32 read_text_save(s32 fileIndex) { if (savedata == NULL) { return -1; } else { - /* Good, file exists for gSaveBuffer */ - gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS; + printf("Loading savefile from '%s'\n", filename); } /* Read coin score age for selected file and sound mode */ ini_sget(savedata, "menu", "coin_score_age", "%d", &gSaveBuffer.menuData[0].coinScoreAges[fileIndex]); - ini_sget(savedata, "menu", "sound_mode", "%u", - &gSaveBuffer.menuData[0].soundMode); // Can override 4 times! + + value = ini_get(savedata, "menu", "sound_mode"); + if (value) { + if (strcmp(value, sound_modes[0]) == 0) { + gSaveBuffer.menuData[0].soundMode = 0; // stereo + } + else if (strcmp(value, sound_modes[1]) == 0) { + gSaveBuffer.menuData[0].soundMode = 3; // mono + } + else if (strcmp(value, sound_modes[2]) == 0) { + gSaveBuffer.menuData[0].soundMode = 1; // headset + } + } + else { + printf("Invalid 'menu:sound_mode' flag!\n"); + return -1; + } + /* Parse main flags */ for (i = 1; i < NUM_FLAGS; i++) { @@ -245,14 +298,48 @@ static s32 read_text_save(s32 fileIndex) { } } - /* ... also it's position, area and level */ - sscanf(ini_get(savedata, "cap", "level"), "%d", &capLevel); - sscanf(ini_get(savedata, "cap", "area"), "%d", &capArea); + /* ... it's level ... */ + value = ini_get(savedata, "cap", "level"); + if (value) { + if (strcmp(value, "ssl") == 0) { + gSaveBuffer.files[fileIndex][0].capLevel = 8; // ssl + } + else if (strcmp(value, "sl") == 0) { + gSaveBuffer.files[fileIndex][0].capLevel = 10; // sl + } + else if (strcmp(value, "ttm") == 0) { + gSaveBuffer.files[fileIndex][0].capLevel = 12; // ttm + } + else if (strcmp(value, "none") == 0) { + gSaveBuffer.files[fileIndex][0].capLevel = 0; + } + else { + printf("Invalid 'cap:level' flag!\n"); + return -1; + } + } - gSaveBuffer.files[fileIndex][0].capLevel = capLevel; - gSaveBuffer.files[fileIndex][0].capArea = capArea; + /* ... and it's area */ + value = ini_get(savedata, "cap", "area"); + if (value) { + sscanf(value, "%d", &capArea); + if (capArea > 1 && capArea < 2) { + printf("Invalid 'cap:area' flag: %d!\n", capArea); + return -1; + } + else { + gSaveBuffer.files[fileIndex][0].capArea = capArea; + } + } + + /* Good, file exists for gSaveBuffer */ + gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS; + /* Make a backup */ + bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1], + sizeof(gSaveBuffer.files[fileIndex][1])); + /* Cleaning up after ourselves */ ini_free(savedata); - return 1; + return 0; } diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 528ef1da..79deadb6 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -68,8 +68,8 @@ 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) unsigned int configRumbleStrength = 50; -#ifdef EXTERNAL_TEXTURES -bool configPrecacheRes = false; +#ifdef EXTERNAL_DATA +bool configPrecacheRes = true; #endif #ifdef BETTERCAMERA // BetterCamera settings @@ -111,7 +111,7 @@ static const struct ConfigOption options[] = { {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight}, {.name = "stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &configStickDeadzone}, {.name = "rumble_strength", .type = CONFIG_TYPE_UINT, .uintValue = &configRumbleStrength}, - #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 6dce17fb..850d05b3 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -37,7 +37,7 @@ extern unsigned int configKeyStickLeft[]; extern unsigned int configKeyStickRight[]; extern unsigned int configStickDeadzone; extern unsigned int configRumbleStrength; -#ifdef EXTERNAL_TEXTURES +#ifdef EXTERNAL_DATA extern bool configPrecacheRes; #endif #ifdef BETTERCAMERA diff --git a/src/pc/controller/controller_api.h b/src/pc/controller/controller_api.h index 74db6552..6e55c846 100644 --- a/src/pc/controller/controller_api.h +++ b/src/pc/controller/controller_api.h @@ -1,10 +1,18 @@ #ifndef CONTROLLER_API #define CONTROLLER_API -#define DEADZONE_STEP 310 // original deadzone is 4960 +#define DEADZONE_STEP 310 // original deadzone is 4960 #define VK_INVALID 0xFFFF #define VK_SIZE 0x1000 +// fake buttons for binding the stick directions +#define STICK_UP 0x80000 +#define STICK_DOWN 0x40000 +#define STICK_LEFT 0x10000 +#define STICK_RIGHT 0x20000 +#define STICK_XMASK 0x30000 +#define STICK_YMASK 0xc0000 + #include struct ControllerAPI { diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c index 9fb4b635..b38352dd 100644 --- a/src/pc/controller/controller_keyboard.c +++ b/src/pc/controller/controller_keyboard.c @@ -61,10 +61,10 @@ static void keyboard_bindkeys(void) { bzero(keyboard_mapping, sizeof(keyboard_mapping)); num_keybinds = 0; - keyboard_add_binds(0x80000, configKeyStickUp); - keyboard_add_binds(0x10000, configKeyStickLeft); - keyboard_add_binds(0x40000, configKeyStickDown); - keyboard_add_binds(0x20000, configKeyStickRight); + keyboard_add_binds(STICK_UP, configKeyStickUp); + keyboard_add_binds(STICK_LEFT, configKeyStickLeft); + keyboard_add_binds(STICK_DOWN, configKeyStickDown); + keyboard_add_binds(STICK_RIGHT, configKeyStickRight); keyboard_add_binds(A_BUTTON, configKeyA); keyboard_add_binds(B_BUTTON, configKeyB); keyboard_add_binds(Z_TRIG, configKeyZ); @@ -87,18 +87,16 @@ static void keyboard_init(void) { static void keyboard_read(OSContPad *pad) { pad->button |= keyboard_buttons_down; - if ((keyboard_buttons_down & 0x30000) == 0x10000) { + const u32 xstick = keyboard_buttons_down & STICK_XMASK; + const u32 ystick = keyboard_buttons_down & STICK_YMASK; + if (xstick == STICK_LEFT) pad->stick_x = -128; - } - if ((keyboard_buttons_down & 0x30000) == 0x20000) { + else if (xstick == STICK_RIGHT) pad->stick_x = 127; - } - if ((keyboard_buttons_down & 0xc0000) == 0x40000) { + if (ystick == STICK_DOWN) pad->stick_y = -128; - } - if ((keyboard_buttons_down & 0xc0000) == 0x80000) { + else if (ystick == STICK_UP) pad->stick_y = 127; - } } static u32 keyboard_rawkey(void) { diff --git a/src/pc/controller/controller_sdl.c b/src/pc/controller/controller_sdl.c index 15384a37..683eefbb 100644 --- a/src/pc/controller/controller_sdl.c +++ b/src/pc/controller/controller_sdl.c @@ -13,6 +13,7 @@ #include "controller_api.h" #include "controller_sdl.h" #include "../configfile.h" +#include "../platform.h" #include "game/level_update.h" @@ -71,6 +72,10 @@ static void controller_sdl_bind(void) { controller_add_binds(A_BUTTON, configKeyA); controller_add_binds(B_BUTTON, configKeyB); controller_add_binds(Z_TRIG, configKeyZ); + controller_add_binds(STICK_UP, configKeyStickUp); + controller_add_binds(STICK_LEFT, configKeyStickLeft); + controller_add_binds(STICK_DOWN, configKeyStickDown); + controller_add_binds(STICK_RIGHT, configKeyStickRight); controller_add_binds(U_CBUTTONS, configKeyCUp); controller_add_binds(L_CBUTTONS, configKeyCLeft); controller_add_binds(D_CBUTTONS, configKeyCDown); @@ -86,6 +91,17 @@ static void controller_sdl_init(void) { return; } + // try loading an external gamecontroller mapping file + char gcpath[SYS_MAX_PATH]; + snprintf(gcpath, sizeof(gcpath), "%s/gamecontrollerdb.txt", sys_save_path()); + int nummaps = SDL_GameControllerAddMappingsFromFile(gcpath); + if (nummaps < 0) { + snprintf(gcpath, sizeof(gcpath), "%s/gamecontrollerdb.txt", sys_data_path()); + nummaps = SDL_GameControllerAddMappingsFromFile(gcpath); + } + if (nummaps >= 0) + printf("loaded %d controller mappings from '%s'\n", nummaps, gcpath); + #ifdef BETTERCAMERA if (newcam_mouse == 1) SDL_SetRelativeMouseMode(SDL_TRUE); @@ -117,7 +133,6 @@ static void controller_sdl_read(OSContPad *pad) { // remember buttons that changed from 0 to 1 last_mouse = (mouse_buttons ^ mouse) & mouse; mouse_buttons = mouse; - #endif SDL_GameControllerUpdate(); @@ -148,9 +163,23 @@ static void controller_sdl_read(OSContPad *pad) { if (pressed) last_joybutton = i; } + u32 buttons_down = 0; for (u32 i = 0; i < num_joy_binds; ++i) if (joy_buttons[joy_binds[i][0]]) - pad->button |= joy_binds[i][1]; + buttons_down |= joy_binds[i][1]; + + pad->button |= buttons_down; + + const u32 xstick = buttons_down & STICK_XMASK; + const u32 ystick = buttons_down & STICK_YMASK; + if (xstick == STICK_LEFT) + pad->stick_x = -128; + else if (xstick == STICK_RIGHT) + pad->stick_x = 127; + if (ystick == STICK_DOWN) + pad->stick_y = -128; + else if (ystick == STICK_UP) + pad->stick_y = 127; int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX); int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY); @@ -225,7 +254,7 @@ static void controller_sdl_shutdown(void) { u32 controller_rumble_init(void) { if (SDL_HapticRumbleSupported(sdl_haptic) != SDL_TRUE) { - printf("Controller does not support haptics! %s\n", SDL_GetError()); + // printf("Controller does not support haptics! %s\n", SDL_GetError()); return 1; } if (SDL_HapticRumbleInit(sdl_haptic) != 0) { @@ -235,6 +264,7 @@ u32 controller_rumble_init(void) { return 0; } + s32 controller_rumble_play(f32 strength, u32 length) { if (SDL_HapticRumblePlay(sdl_haptic, strength, length) != 0) { printf("Unable to start rumble! %s\n", SDL_GetError()); 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