diff --git a/src/platform.h b/src/platform.h index 2a21894..e755b4c 100644 --- a/src/platform.h +++ b/src/platform.h @@ -10,6 +10,10 @@ bool platform_get_fullscreen(); void platform_set_fullscreen(bool fullscreen); void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len)); +uint8_t *platform_load_asset(const char *name, uint32_t *bytes_read); +uint8_t *platform_load_userdata(const char *name, uint32_t *bytes_read); +uint32_t platform_store_userdata(const char *name, void *bytes, int32_t len); + #if defined(RENDERER_SOFTWARE) rgba_t *platform_get_screenbuffer(int32_t *pitch); #endif diff --git a/src/platform_sdl.c b/src/platform_sdl.c index a702e5a..6a07968 100755 --- a/src/platform_sdl.c +++ b/src/platform_sdl.c @@ -3,6 +3,8 @@ #include "platform.h" #include "input.h" #include "system.h" +#include "utils.h" +#include "mem.h" static uint64_t perf_freq = 0; static bool wants_to_exit = false; @@ -10,6 +12,9 @@ static SDL_Window *window; static SDL_AudioDeviceID audio_device; static SDL_GameController *gamepad; static void (*audio_callback)(float *buffer, uint32_t len) = NULL; +static char *path_assets = NULL; +static char *path_userdata = NULL; +static char *temp_path = NULL; uint8_t platform_sdl_gamepad_map[] = { @@ -223,6 +228,28 @@ void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len)) { } +uint8_t *platform_load_asset(const char *name, uint32_t *bytes_read) { + char *path = strcat(strcpy(temp_path, path_assets), name); + return file_load(path, bytes_read); +} + +uint8_t *platform_load_userdata(const char *name, uint32_t *bytes_read) { + char *path = strcat(strcpy(temp_path, path_userdata), name); + if (!file_exists(path)) { + *bytes_read = 0; + return NULL; + } + return file_load(path, bytes_read); +} + +uint32_t platform_store_userdata(const char *name, void *bytes, int32_t len) { + char *path = strcat(strcpy(temp_path, path_userdata), name); + return file_store(path, bytes, len); +} + + + + #if defined(RENDERER_GL) // ---------------------------------------------------- #define PLATFORM_WINDOW_FLAGS SDL_WINDOW_OPENGL SDL_GLContext platform_gl; @@ -317,7 +344,37 @@ void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len)) { int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); - int gcdb_res = SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); + // Figure out the absolute asset and userdata paths. These may either be + // supplied at build time through -DPATH_ASSETS=.. and -DPATH_USERDATA=.. + // or received at runtime from SDL. Note that SDL may return NULL for these. + // We fall back to the current directory (i.e. just "") in this case. + + #ifdef PATH_ASSETS + path_assets = TOSTRING(PATH_ASSETS); + #else + path_assets = SDL_GetBasePath(); + if (path_assets == NULL) { + path_assets = ""; + } + #endif + + #ifdef PATH_USERDATA + path_userdata = TOSTRING(PATH_USERDATA); + #else + path_userdata = SDL_GetPrefPath("phoboslab", "wipeout"); + if (path_userdata == NULL) { + path_userdata = ""; + } + #endif + + // Reserve some space for concatenating the asset and userdata paths with + // local filenames. + temp_path = mem_bump(max(strlen(path_assets), strlen(path_userdata)) + 64); + + // Load gamecontrollerdb.txt if present. + // FIXME: Should this load from userdata instead? + char *gcdb_path = strcat(strcpy(temp_path, path_assets), "gamecontrollerdb.txt"); + int gcdb_res = SDL_GameControllerAddMappingsFromFile(gcdb_path); if (gcdb_res < 0) { printf("Failed to load gamecontrollerdb.txt\n"); } @@ -325,6 +382,12 @@ int main(int argc, char *argv[]) { printf("load gamecontrollerdb.txt\n"); } + + + gamepad = platform_find_gamepad(); + + perf_freq = SDL_GetPerformanceFrequency(); + audio_device = SDL_OpenAudioDevice(NULL, 0, &(SDL_AudioSpec){ .freq = 44100, .format = AUDIO_F32, @@ -333,10 +396,6 @@ int main(int argc, char *argv[]) { .callback = platform_audio_callback }, NULL, 0); - gamepad = platform_find_gamepad(); - - perf_freq = SDL_GetPerformanceFrequency(); - window = SDL_CreateWindow( SYSTEM_WINDOW_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, @@ -357,6 +416,9 @@ int main(int argc, char *argv[]) { system_cleanup(); platform_video_cleanup(); + SDL_free(path_assets); + SDL_free(path_userdata); + SDL_CloseAudioDevice(audio_device); SDL_Quit(); return 0; diff --git a/src/platform_sokol.c b/src/platform_sokol.c index 709fb43..f6ca41c 100644 --- a/src/platform_sokol.c +++ b/src/platform_sokol.c @@ -1,5 +1,7 @@ #include "platform.h" #include "system.h" +#include "utils.h" +#include "mem.h" #if defined(RENDERER_GL) #ifdef __EMSCRIPTEN__ @@ -17,6 +19,22 @@ #include "libs/sokol_app.h" #include "input.h" +// FIXME: we should figure out the actual path where the executabe resides, +// instead of just assuming it's in the pwd +#ifdef PATH_ASSETS + static const char *path_assets = TOSTRING(PATH_ASSETS); +#else + static const char *path_assets = ""; +#endif + +#ifdef PATH_USERDATA + static const char *path_userdata = TOSTRING(PATH_USERDATA); +#else + static const char *path_userdata = ""; +#endif + +static char *temp_path; + static const uint8_t keyboard_map[] = { [SAPP_KEYCODE_SPACE] = INPUT_KEY_SPACE, [SAPP_KEYCODE_APOSTROPHE] = INPUT_KEY_APOSTROPHE, @@ -246,7 +264,28 @@ void platform_set_audio_mix_cb(void (*cb)(float *buffer, uint32_t len)) { audio_callback = cb; } +uint8_t *platform_load_asset(const char *name, uint32_t *bytes_read) { + char *path = strcat(strcpy(temp_path, path_assets), name); + return file_load(path, bytes_read); +} + +uint8_t *platform_load_userdata(const char *name, uint32_t *bytes_read) { + char *path = strcat(strcpy(temp_path, path_userdata), name); + if (!file_exists(path)) { + *bytes_read = 0; + return NULL; + } + return file_load(path, bytes_read); +} + +uint32_t platform_store_userdata(const char *name, void *bytes, int32_t len) { + char *path = strcat(strcpy(temp_path, path_userdata), name); + return file_store(path, bytes, len); +} + sapp_desc sokol_main(int argc, char* argv[]) { + temp_path = mem_bump(max(strlen(path_assets), strlen(path_userdata)) + 64); + stm_setup(); saudio_setup(&(saudio_desc){ diff --git a/src/utils.c b/src/utils.c index b6eeb4b..596e757 100644 --- a/src/utils.c +++ b/src/utils.c @@ -13,12 +13,12 @@ char *get_path(const char *dir, const char *file) { } -bool file_exists(char *path) { +bool file_exists(const char *path) { struct stat s; return (stat(path, &s) == 0); } -uint8_t *file_load(char *path, uint32_t *bytes_read) { +uint8_t *file_load(const char *path, uint32_t *bytes_read) { FILE *f = fopen(path, "rb"); error_if(!f, "Could not open file for reading: %s", path); @@ -43,7 +43,7 @@ uint8_t *file_load(char *path, uint32_t *bytes_read) { return bytes; } -uint32_t file_store(char *path, void *bytes, int32_t len) { +uint32_t file_store(const char *path, void *bytes, int32_t len) { FILE *f = fopen(path, "wb"); error_if(!f, "Could not open file for writing: %s", path); diff --git a/src/utils.h b/src/utils.h index d8a838d..861ef54 100644 --- a/src/utils.h +++ b/src/utils.h @@ -71,9 +71,9 @@ bool str_starts_with(const char *haystack, const char *needle); float rand_float(float min, float max); int32_t rand_int(int32_t min, int32_t max); -bool file_exists(char *path); -uint8_t *file_load(char *path, uint32_t *bytes_read); -uint32_t file_store(char *path, void *bytes, int32_t len); +bool file_exists(const char *path); +uint8_t *file_load(const char *path, uint32_t *bytes_read); +uint32_t file_store(const char *path, void *bytes, int32_t len); #define sort(LIST, LEN, COMPARE_FUNC) \ diff --git a/src/wipeout/game.c b/src/wipeout/game.c index 4066c16..5fa8b72 100755 --- a/src/wipeout/game.c +++ b/src/wipeout/game.c @@ -502,13 +502,17 @@ static int global_textures_len = 0; static void *global_mem_mark = 0; void game_init() { - if (file_exists("save.dat")) { - uint32_t size; - save_t *save_file = (save_t *)file_load("save.dat", &size); + + uint32_t size; + save_t *save_file = (save_t *)platform_load_userdata("save.dat", &size); + if (save_file) { if (size == sizeof(save_t) && save_file->magic == SAVE_DATA_MAGIC) { printf("load save data success\n"); memcpy(&save, save_file, sizeof(save_t)); } + else { + printf("unexpected size/magic for save data"); + } mem_temp_free(save_file); } @@ -631,7 +635,7 @@ void game_update() { // FIXME: use a text based format? // FIXME: this should probably run async somewhere save.is_dirty = false; - file_store("save.dat", &save, sizeof(save_t)); + platform_store_userdata("save.dat", &save, sizeof(save_t)); printf("wrote save.dat\n"); } diff --git a/src/wipeout/image.c b/src/wipeout/image.c index 99be1f5..2b890e2 100755 --- a/src/wipeout/image.c +++ b/src/wipeout/image.c @@ -1,6 +1,7 @@ #include "../types.h" #include "../mem.h" #include "../utils.h" +#include "../platform.h" #include "object.h" #include "track.h" @@ -229,7 +230,7 @@ void lzss_decompress(uint8_t *in_data, uint8_t *out_data) { cmp_t *image_load_compressed(char *name) { printf("load cmp %s\n", name); uint32_t compressed_size; - uint8_t *compressed_bytes = file_load(name, &compressed_size); + uint8_t *compressed_bytes = platform_load_asset(name, &compressed_size); uint32_t p = 0; int32_t decompressed_size = 0; @@ -263,7 +264,7 @@ cmp_t *image_load_compressed(char *name) { uint16_t image_get_texture(char *name) { printf("load: %s\n", name); uint32_t size; - uint8_t *bytes = file_load(name, &size); + uint8_t *bytes = platform_load_asset(name, &size); image_t *image = image_load_from_bytes(bytes, false); uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels); mem_temp_free(image); @@ -275,7 +276,7 @@ uint16_t image_get_texture(char *name) { uint16_t image_get_texture_semi_trans(char *name) { printf("load: %s\n", name); uint32_t size; - uint8_t *bytes = file_load(name, &size); + uint8_t *bytes = platform_load_asset(name, &size); image_t *image = image_load_from_bytes(bytes, true); uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels); mem_temp_free(image); diff --git a/src/wipeout/object.c b/src/wipeout/object.c index e5f690b..f175221 100755 --- a/src/wipeout/object.c +++ b/src/wipeout/object.c @@ -2,6 +2,7 @@ #include "../mem.h" #include "../render.h" #include "../utils.h" +#include "../platform.h" #include "object.h" #include "track.h" @@ -26,7 +27,7 @@ static rgba_t int32_to_rgba(uint32_t v) { Object *objects_load(char *name, texture_list_t tl) { uint32_t length = 0; - uint8_t *bytes = file_load(name, &length); + uint8_t *bytes = platform_load_asset(name, &length); if (!bytes) { die("Failed to load file %s\n", name); } diff --git a/src/wipeout/sfx.c b/src/wipeout/sfx.c index 50eed0c..8c3deea 100644 --- a/src/wipeout/sfx.c +++ b/src/wipeout/sfx.c @@ -67,7 +67,7 @@ void sfx_load() { // 16 byte blocks: 2 byte header, 14 bytes with 2x4bit samples each uint32_t vb_size; - uint8_t *vb = file_load("wipeout/sound/wipeout.vb", &vb_size); + uint8_t *vb = platform_load_asset("wipeout/sound/wipeout.vb", &vb_size); uint32_t num_samples = (vb_size / 16) * 28; int16_t *sample_buffer = mem_bump(num_samples * sizeof(int16_t)); diff --git a/src/wipeout/track.c b/src/wipeout/track.c index 4466478..cd612cd 100755 --- a/src/wipeout/track.c +++ b/src/wipeout/track.c @@ -2,6 +2,7 @@ #include "../utils.h" #include "../render.h" #include "../system.h" +#include "../platform.h" #include "object.h" #include "track.h" @@ -87,7 +88,7 @@ void track_load(const char *base_path) { ttf_t *track_load_tile_format(char *ttf_name) { uint32_t ttf_size; - uint8_t *ttf_bytes = file_load(ttf_name, &ttf_size); + uint8_t *ttf_bytes = platform_load_asset(ttf_name, &ttf_size); uint32_t p = 0; uint32_t num_tiles = ttf_size / 42; @@ -123,7 +124,7 @@ bool track_collect_pickups(track_face_t *face) { vec3_t *track_load_vertices(char *file_name) { uint32_t size; - uint8_t *bytes = file_load(file_name, &size); + uint8_t *bytes = platform_load_asset(file_name, &size); g.track.vertex_count = size / 16; // VECTOR_SIZE vec3_t *vertices = mem_temp_alloc(sizeof(vec3_t) * g.track.vertex_count); @@ -147,7 +148,7 @@ static const vec2_t track_uv[2][4] = { void track_load_faces(char *file_name, vec3_t *vertices) { uint32_t size; - uint8_t *bytes = file_load(file_name, &size); + uint8_t *bytes = platform_load_asset(file_name, &size); g.track.face_count = size / 20; // TRACK_FACE_DATA_SIZE g.track.faces = mem_bump(sizeof(track_face_t) * g.track.face_count); @@ -196,7 +197,7 @@ void track_load_faces(char *file_name, vec3_t *vertices) { void track_load_sections(char *file_name) { uint32_t size; - uint8_t *bytes = file_load(file_name, &size); + uint8_t *bytes = platform_load_asset(file_name, &size); g.track.section_count = size / 156; // SECTION_DATA_SIZE g.track.sections = mem_bump(sizeof(section_t) * g.track.section_count);