diff --git a/data/dynos.cpp.h b/data/dynos.cpp.h index 93dbce8b..46695e27 100644 --- a/data/dynos.cpp.h +++ b/data/dynos.cpp.h @@ -9,6 +9,7 @@ extern "C" { #include "engine/math_util.h" #include "game/moving_texture.h" #include "pc/djui/djui_console.h" +#include "pc/fs/fmem.h" } #define FUNCTION_CODE (u32) 0x434E5546 @@ -95,16 +96,16 @@ public: public: static BinFile *OpenR(const char *aFilename) { - FILE *f = fopen(aFilename, "rb"); + FILE *f = f_open_r(aFilename); if (f) { - fseek(f, 0, SEEK_END); + f_seek(f, 0, SEEK_END); BinFile *_BinFile = (BinFile *) calloc(1, sizeof(BinFile)); _BinFile->mFilename = (const char *) memcpy(calloc(strlen(aFilename) + 1, 1), aFilename, strlen(aFilename)); _BinFile->mReadOnly = true; - _BinFile->Grow(ftell(f)); - rewind(f); - fread(_BinFile->mData, 1, _BinFile->mSize, f); - fclose(f); + _BinFile->Grow(f_tell(f)); + f_rewind(f); + f_read(_BinFile->mData, 1, _BinFile->mSize, f); + f_close(f); return _BinFile; } return NULL; diff --git a/data/dynos_bin_compress.cpp b/data/dynos_bin_compress.cpp index 879fc8b5..02a3546a 100644 --- a/data/dynos_bin_compress.cpp +++ b/data/dynos_bin_compress.cpp @@ -17,7 +17,7 @@ static inline void DynOS_Bin_Compress_Init() { } static inline void DynOS_Bin_Compress_Close() { - if (sFile) fclose(sFile); + if (sFile) f_close(sFile); sFile = NULL; } @@ -159,14 +159,14 @@ BinFile *DynOS_Bin_Decompress(const SysPath &aFilename) { // Open input file if (!DynOS_Bin_Compress_Check( - (sFile = fopen(aFilename.c_str(), "rb")) != NULL, + (sFile = f_open_r(aFilename.c_str())) != NULL, __FUNCTION__, aFilename.c_str(), "Cannot open file" )) return NULL; // Read magic u64 _Magic = 0; if (!DynOS_Bin_Compress_Check( - fread(&_Magic, sizeof(u64), 1, sFile) == 1, + f_read(&_Magic, sizeof(u64), 1, sFile) == 1, __FUNCTION__, aFilename.c_str(), "Cannot read magic" )) return NULL; @@ -180,20 +180,20 @@ BinFile *DynOS_Bin_Decompress(const SysPath &aFilename) { // Read expected uncompressed file size if (!DynOS_Bin_Compress_Check( - fread(&sLengthUncompressed, sizeof(u64), 1, sFile) == 1, + f_read(&sLengthUncompressed, sizeof(u64), 1, sFile) == 1, __FUNCTION__, aFilename.c_str(), "Cannot read uncompressed file size" )) return NULL; // Retrieve file length if (!DynOS_Bin_Compress_Check( - fseek(sFile, 0, SEEK_END) == 0, + f_seek(sFile, 0, SEEK_END) == 0, __FUNCTION__, aFilename.c_str(), "Cannot retrieve file length" )) return NULL; // Check file length u64 _LengthHeader = (u64) (sizeof(u64) + sizeof(u64)); if (!DynOS_Bin_Compress_Check( - (sLengthCompressed = (u64) ftell(sFile)) >= _LengthHeader, + (sLengthCompressed = (u64) f_tell(sFile)) >= _LengthHeader, __FUNCTION__, aFilename.c_str(), "Empty file" )) return NULL; @@ -201,11 +201,11 @@ BinFile *DynOS_Bin_Decompress(const SysPath &aFilename) { if (!DynOS_Bin_Compress_Check( (sBufferCompressed = (u8 *) calloc(sLengthCompressed - _LengthHeader, sizeof(u8))) != NULL, __FUNCTION__, aFilename.c_str(), "Cannot allocate memory for decompression" - )) return NULL; else fseek(sFile, _LengthHeader, SEEK_SET); + )) return NULL; else f_seek(sFile, _LengthHeader, SEEK_SET); // Read input data if (!DynOS_Bin_Compress_Check( - fread(sBufferCompressed, sizeof(u8), sLengthCompressed - _LengthHeader, sFile) == sLengthCompressed - _LengthHeader, + f_read(sBufferCompressed, sizeof(u8), sLengthCompressed - _LengthHeader, sFile) == sLengthCompressed - _LengthHeader, __FUNCTION__, aFilename.c_str(), "Cannot read compressed data" )) return NULL; else DynOS_Bin_Compress_Close(); diff --git a/src/pc/fs/fmem.c b/src/pc/fs/fmem.c new file mode 100644 index 00000000..f8650587 --- /dev/null +++ b/src/pc/fs/fmem.c @@ -0,0 +1,164 @@ +#include "fmem.h" +#include "pc/platform.h" +#include "engine/math_util.h" + +typedef struct file_t { + char filename[SYS_MAX_PATH]; + void *data; + size_t size; + size_t pos; + bool readonly; +} file_t; + +typedef struct file_node_t { + file_t file; + struct file_node_t *prev; + struct file_node_t *next; +} file_node_t; + +static file_node_t *sMemoryFiles = NULL; + +static file_t *f_get_file_from_handle(FILE *f) { + for (file_node_t *node = sMemoryFiles; node; node = node->prev) { + if (node == (void *) f) { + return &node->file; + } + } + return NULL; +} + +static file_t *f_get_file_from_name(const char *filename) { + for (file_node_t *node = sMemoryFiles; node; node = node->prev) { + if (strcmp(node->file.filename, filename) == 0) { + return &node->file; + } + } + return NULL; +} + +static file_t *f_create_file(const char *filename) { + file_node_t *node = calloc(1, sizeof(file_node_t)); + if (sMemoryFiles) { + sMemoryFiles->next = node; + node->prev = sMemoryFiles; + } + sMemoryFiles = node; + strncpy(node->file.filename, filename, sizeof(node->file.filename) - 1); + return &node->file; +} + +static void f_remove_file(file_t *file) { + file_node_t *node = (file_node_t *) file; + if (node->prev) { + node->prev->next = node->next; + } + if (node->next) { + node->next->prev = node->prev; + } + if (node == sMemoryFiles) { + sMemoryFiles = node->prev; + } + if (file->data) { + free(file->data); + } + free(node); +} + +FILE *f_open_r(const char *filename) { + file_t *file = f_get_file_from_name(filename); + if (!file) return fopen(filename, "rb"); + file->pos = 0; + file->readonly = true; + return (FILE *) file; +} + +FILE *f_open_w(const char *filename) { + file_t *file = f_create_file(filename); + return (FILE *) file; +} + +int f_close(FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return fclose(f); + return 0; +} + +void f_delete(FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return; + f_remove_file(file); +} + +size_t f_read(void *dst, size_t size, size_t count, FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return fread(dst, size, count, f); + if (file->pos >= file->size) return 0; + count = min(count, ((file->size - file->pos) / size)); + memcpy(dst, file->data + file->pos, count * size); + file->pos += count * size; + return count; +} + +size_t f_write(const void *str, size_t size, size_t count, FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return fwrite(str, size, count, f); + if (file->readonly) return 0; + size_t newsize = file->pos + size * count; + if (newsize > file->size) { + void *buffer = malloc(newsize); + if (!buffer) { + return 0; + } + if (file->data) { + memcpy(buffer, file->data, file->size); + free(file->data); + } + file->data = buffer; + file->size = newsize; + } + memcpy(file->data + file->pos, str, size * count); + file->pos += size * count; + return count; +} + +int f_seek(FILE *f, long offset, int origin) { + file_t *file = f_get_file_from_handle(f); + if (!file) return fseek(f, offset, origin); + switch (origin) { + case SEEK_SET: file->pos = offset; break; + case SEEK_CUR: file->pos += offset; break; + case SEEK_END: file->pos = file->size + offset; break; + default: return 1; + } + return 0; +} + +long f_tell(FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return ftell(f); + return file->pos; +} + +void f_rewind(FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return rewind(f); + file->pos = 0; +} + +int f_flush(FILE *f) { + file_t *file = f_get_file_from_handle(f); + if (!file) return fflush(f); + return 0; +} + +void f_shutdown() { + for (file_node_t *node = sMemoryFiles; node;) { + if (node->file.data) { + free(node->file.data); + } + file_node_t *prev = node->prev; + free(node); + node = prev; + } + sMemoryFiles = NULL; +} diff --git a/src/pc/fs/fmem.h b/src/pc/fs/fmem.h new file mode 100644 index 00000000..316b4884 --- /dev/null +++ b/src/pc/fs/fmem.h @@ -0,0 +1,18 @@ +#ifndef FMEM_H +#define FMEM_H + +#include + +FILE *f_open_r (const char *filename); +FILE *f_open_w (const char *filename); +int f_close (FILE *f); +void f_delete (FILE *f); +size_t f_read (void *dst, size_t size, size_t count, FILE *f); +size_t f_write (const void *str, size_t size, size_t count, FILE *f); +int f_seek (FILE *f, long offset, int origin); +long f_tell (FILE *f); +void f_rewind (FILE *f); +int f_flush (FILE *f); +void f_shutdown (); + +#endif diff --git a/src/pc/lua/smlua.c b/src/pc/lua/smlua.c index edcfbd51..b5703995 100644 --- a/src/pc/lua/smlua.c +++ b/src/pc/lua/smlua.c @@ -9,6 +9,7 @@ #include "pc/lua/utils/smlua_level_utils.h" #include "pc/lua/utils/smlua_anim_utils.h" #include "pc/djui/djui.h" +#include "pc/fs/fmem.h" lua_State* gLuaState = NULL; u8 gLuaInitializingScript = 0; @@ -81,28 +82,30 @@ void smlua_exec_str(const char* str) { #define LUA_BOM_19 0x4077280000000000llu static bool smlua_check_binary_header(struct ModFile *file) { - FILE *f = fopen(file->cachedPath, "rb"); + FILE *f = f_open_r(file->cachedPath); if (f) { // Read signature char signature[sizeof(LUA_SIGNATURE)] = { 0 }; - if (fread(signature, 1, sizeof(LUA_SIGNATURE) - 1, f) != sizeof(LUA_SIGNATURE) - 1) { + if (f_read(signature, 1, sizeof(LUA_SIGNATURE) - 1, f) != sizeof(LUA_SIGNATURE) - 1) { LOG_LUA("Failed to load lua script '%s': File too short.", file->cachedPath); - fclose(f); + f_close(f); + f_delete(f); return false; } // Check signature if (strcmp(signature, LUA_SIGNATURE) != 0) { - fclose(f); + f_close(f); return true; // Not a binary lua } // Read version number u8 version; - if (fread(&version, 1, 1, f) != 1) { + if (f_read(&version, 1, 1, f) != 1) { LOG_LUA("Failed to load lua script '%s': File too short.", file->cachedPath); - fclose(f); + f_close(f); + f_delete(f); return false; } @@ -110,15 +113,17 @@ static bool smlua_check_binary_header(struct ModFile *file) { u8 expectedVersion = strtoul(LUA_VERSION_MAJOR LUA_VERSION_MINOR, NULL, 16); if (version != expectedVersion) { LOG_LUA("Failed to load lua script '%s': Lua versions don't match (%X, expected %X).", file->cachedPath, version, expectedVersion); - fclose(f); + f_close(f); + f_delete(f); return false; } // Read the rest of the header u8 header[28]; - if (fread(header, 1, 28, f) != 28) { + if (f_read(header, 1, 28, f) != 28) { LOG_LUA("Failed to load lua script '%s': File too short.", file->cachedPath); - fclose(f); + f_close(f); + f_delete(f); return false; } @@ -129,12 +134,14 @@ static bool smlua_check_binary_header(struct ModFile *file) { u64 bom19 = *((u64 *) (header + 20)); if (bom11 != LUA_BOM_11) { LOG_ERROR("Failed to load lua script '%s': BOM at offset 0x11 don't match (%016llX, expected %016llX).", file->cachedPath, bom11, LUA_BOM_11); - fclose(f); + f_close(f); + f_delete(f); return false; } if (bom19 != LUA_BOM_19) { LOG_ERROR("Failed to load lua script '%s': BOM at offset 0x19 don't match (%016llX, expected %016llX).", file->cachedPath, bom19, LUA_BOM_19); - fclose(f); + f_close(f); + f_delete(f); return false; } @@ -146,33 +153,37 @@ static bool smlua_check_binary_header(struct ModFile *file) { u8 sizeOfLuaNumber = header[11]; if (sizeOfCInteger != sizeof(int)) { LOG_ERROR("Failed to load lua script '%s': sizes of C Integer don't match (%d, expected %llu).", file->cachedPath, sizeOfCInteger, (long long unsigned)sizeof(int)); - fclose(f); + f_close(f); + f_delete(f); return false; } if (sizeOfCPointer != sizeof(void *)) { // 4 for 32-bit architectures, 8 for 64-bit LOG_ERROR("Failed to load lua script '%s': sizes of C Pointer don't match (%d, expected %llu).", file->cachedPath, sizeOfCPointer, (long long unsigned)sizeof(void *)); - fclose(f); + f_close(f); + f_delete(f); return false; } if (sizeOfCFloat != sizeof(float)) { LOG_ERROR("Failed to load lua script '%s': sizes of C Float don't match (%d, expected %llu).", file->cachedPath, sizeOfCFloat, (long long unsigned)sizeof(float)); - fclose(f); + f_close(f); + f_delete(f); return false; } if (sizeOfLuaInteger != sizeof(LUA_INTEGER)) { LOG_ERROR("Failed to load lua script '%s': sizes of Lua Integer don't match (%d, expected %llu).", file->cachedPath, sizeOfLuaInteger, (long long unsigned)sizeof(LUA_INTEGER)); - fclose(f); + f_close(f); + f_delete(f); return false; } if (sizeOfLuaNumber != sizeof(LUA_NUMBER)) { LOG_ERROR("Failed to load lua script '%s': sizes of Lua Number don't match (%d, expected %llu).", file->cachedPath, sizeOfLuaNumber, (long long unsigned)sizeof(LUA_NUMBER)); - fclose(f); + f_close(f); + f_delete(f); return false; } // All's good - LOG_INFO("Loading lua script '%s'", file->cachedPath); - fclose(f); + f_close(f); return true; } LOG_LUA("Failed to load lua script '%s': File not found.", file->cachedPath); @@ -190,13 +201,40 @@ static void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteI gLuaInitializingScript = 1; LOG_INFO("Loading lua script '%s'", file->cachedPath); - if (luaL_loadfile(L, file->cachedPath) != LUA_OK) { // only run on success - LOG_LUA("Failed to load lua script '%s'.", file->cachedPath); - LOG_LUA("%s", smlua_to_string(L, lua_gettop(L))); + FILE *f = f_open_r(file->cachedPath); + if (!f) { + LOG_LUA("Failed to load lua script '%s': File not found.", file->cachedPath); gLuaInitializingScript = 0; return; } + f_seek(f, 0, SEEK_END); + size_t length = f_tell(f); + char *buffer = calloc(length + 1, 1); + if (!buffer) { + LOG_LUA("Failed to load lua script '%s': Cannot allocate buffer.", file->cachedPath); + gLuaInitializingScript = 0; + return; + } + + f_rewind(f); + if (f_read(buffer, 1, length, f) < length) { + LOG_LUA("Failed to load lua script '%s': Unexpected early end of file.", file->cachedPath); + gLuaInitializingScript = 0; + return; + } + f_close(f); + f_delete(f); + + if (luaL_loadstring(L, buffer) != LUA_OK) { // only run on success + LOG_LUA("Failed to load lua script '%s'.", file->cachedPath); + LOG_LUA("%s", smlua_to_string(L, lua_gettop(L))); + gLuaInitializingScript = 0; + free(buffer); + return; + } + free(buffer); + // check if this is the first time this mod has been loaded lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); bool firstInit = (lua_type(L, -1) == LUA_TNIL); diff --git a/src/pc/lua/utils/smlua_audio_utils.c b/src/pc/lua/utils/smlua_audio_utils.c index 6b3ec9ee..d6e9c578 100644 --- a/src/pc/lua/utils/smlua_audio_utils.c +++ b/src/pc/lua/utils/smlua_audio_utils.c @@ -16,6 +16,7 @@ #include "pc/utils/misc.h" #include "pc/debuglog.h" #include "pc/pc_main.h" +#include "pc/fs/fmem.h" #include "audio/external.h" struct AudioOverride { @@ -81,22 +82,24 @@ bool smlua_audio_utils_override(u8 sequenceId, s32* bankId, void** seqData) { static u8* buffer = NULL; static long int length = 0; - FILE* fp = fopen(override->filename, "rb"); + FILE* fp = f_open_r(override->filename); if (!fp) { return false; } - fseek(fp, 0L, SEEK_END); - length = ftell(fp); + f_seek(fp, 0L, SEEK_END); + length = f_tell(fp); buffer = malloc(length+1); if (buffer == NULL) { LOG_ERROR("Failed to malloc m64 sound file"); - fclose(fp); + f_close(fp); + f_delete(fp); return false; } - fseek(fp, 0L, SEEK_SET); - fread(buffer, length, 1, fp); + f_seek(fp, 0L, SEEK_SET); + f_read(buffer, length, 1, fp); - fclose(fp); + f_close(fp); + f_delete(fp); // cache override->loaded = true; @@ -251,12 +254,53 @@ struct ModAudio* audio_load_internal(const char* filename, bool isStream) { audio->file = modFile; // load audio - ma_result result = ma_sound_init_from_file( - &gModAudioEngine, modFile->cachedPath, + FILE *f = f_open_r(modFile->cachedPath); + if (!f) { + LOG_ERROR("failed to load audio file '%s': file not found", filename); + return NULL; + } + + f_seek(f, 0, SEEK_END); + u32 size = f_tell(f); + f_rewind(f); + void *buffer = calloc(size, 1); + if (!buffer) { + f_close(f); + f_delete(f); + LOG_ERROR("failed to load audio file '%s': cannot allocate buffer of size: %d", filename, size); + return NULL; + } + + // read the audio buffer + if (f_read(buffer, 1, size, f) < size) { + free(buffer); + f_close(f); + f_delete(f); + LOG_ERROR("failed to load audio file '%s': cannot read audio buffer of size: %d", filename, size); + return NULL; + } + f_close(f); + f_delete(f); + + // decode the audio buffer + // note: buffer and decoder are not freed after a successful call, because ma_decoder_init_memory() does not make copies of them + ma_decoder *decoder = calloc(1, sizeof(ma_decoder)); + ma_result result = ma_decoder_init_memory(buffer, size, NULL, decoder); + if (result != MA_SUCCESS) { + free(decoder); + free(buffer); + LOG_ERROR("failed to load audio file '%s': failed to decode raw audio: %d", filename, result); + return NULL; + } + + result = ma_sound_init_from_data_source( + &gModAudioEngine, decoder, isStream ? MA_SOUND_STREAM_FLAGS : MA_SOUND_SAMPLE_FLAGS, - NULL, NULL, &audio->sound + NULL, &audio->sound ); if (result != MA_SUCCESS) { + free(decoder); + free(buffer); LOG_ERROR("failed to load audio file '%s': %d", filename, result); return NULL; } diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c index 6056e580..86a8044a 100644 --- a/src/pc/mods/mod.c +++ b/src/pc/mods/mod.c @@ -6,6 +6,7 @@ #include "pc/utils/misc.h" #include "pc/utils/md5.h" #include "pc/debuglog.h" +#include "pc/fs/fmem.h" size_t mod_get_lua_size(struct Mod* mod) { if (!mod) { return 0; } @@ -171,7 +172,8 @@ void mod_clear(struct Mod* mod) { for (int j = 0; j < mod->fileCount; j++) { struct ModFile* file = &mod->files[j]; if (file->fp != NULL) { - fclose(file->fp); + f_close(file->fp); + f_delete(file->fp); file->fp = NULL; } if (file->cachedPath != NULL) { diff --git a/src/pc/mods/mods.c b/src/pc/mods/mods.c index 8ee0e993..e2f6834e 100644 --- a/src/pc/mods/mods.c +++ b/src/pc/mods/mods.c @@ -5,6 +5,7 @@ #include "data/dynos.c.h" #include "pc/debuglog.h" #include "pc/loading.h" +#include "pc/fs/fmem.h" #if defined(_WIN32) || defined(_WIN64) #include @@ -322,7 +323,8 @@ void mods_clear(struct Mods* mods) { for (int j = 0; j < mod->fileCount; j++) { struct ModFile* file = &mod->files[j]; if (file->fp != NULL) { - fclose(file->fp); + f_close(file->fp); + f_delete(file->fp); file->fp = NULL; } } diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 46848e10..81d8aa0a 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -23,6 +23,7 @@ #include "pc/debuglog.h" #include "pc/pc_main.h" #include "pc/gfx/gfx_pc.h" +#include "pc/fs/fmem.h" #include "game/camera.h" #include "game/skybox.h" #include "game/object_list_processor.h" @@ -726,6 +727,7 @@ void network_shutdown(bool sendLeaving, bool exiting, bool popup, bool reconnect save_file_load_all(TRUE); extern void save_file_set_using_backup_slot(bool usingBackupSlot); save_file_set_using_backup_slot(false); + f_shutdown(); extern s16 gMenuMode; gMenuMode = -1; diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c index 66177b13..ab5aa491 100644 --- a/src/pc/network/packets/packet_download.c +++ b/src/pc/network/packets/packet_download.c @@ -9,6 +9,7 @@ #include "pc/djui/djui_panel_join_message.h" //#define DISABLE_MODULE_LOG 1 #include "pc/debuglog.h" +#include "pc/fs/fmem.h" #define CHUNK_SIZE 800 #define OFFSET_COUNT 50 @@ -191,8 +192,8 @@ static void network_update_offset_groups(void) { for (u64 fileIndex = 0; fileIndex < mod->fileCount; fileIndex++) { struct ModFile* modFile = &mod->files[fileIndex]; if (modFile->fp == NULL) { continue; } - fflush(modFile->fp); - fclose(modFile->fp); + f_flush(modFile->fp); + f_close(modFile->fp); modFile->fp = NULL; } mod->enabled = true; @@ -315,6 +316,18 @@ after_filled:; //LOG_INFO("Sent chunk: offset %llu, length %llu", requestOffset, chunkFill); } +// Cache any mod that doesn't have "(wip)" or "[wip]" in its name (case-insensitive) +static bool should_cache_mod(struct Mod *mod) { + char *modName = sys_strdup(mod->name); + sys_strlwr(modName); + bool shouldCache = ( + !strstr(modName, "(wip)") && + !strstr(modName, "[wip]") + ); + free(modName); + return shouldCache; +} + static void open_mod_file(struct Mod* mod, struct ModFile* file) { if (file->fp != NULL) { return; @@ -326,10 +339,13 @@ static void open_mod_file(struct Mod* mod, struct ModFile* file) { return; } - mod_file_create_directories(mod, file); - file->wroteBytes = 0; - file->fp = fopen(fullPath, "wb"); + if (should_cache_mod(mod)) { + mod_file_create_directories(mod, file); + file->fp = fopen(fullPath, "wb"); + } else { + file->fp = f_open_w(fullPath); + } if (file->fp == NULL) { LOG_ERROR("unable to open for write: '%s' - '%s'", fullPath, strerror(errno)); return; @@ -430,14 +446,22 @@ after_group:; LOG_ERROR("Failed to open file for download write: %s", modFile->cachedPath); return; } - fseek(modFile->fp, fileWriteOffset, SEEK_SET); - fwrite(&chunk[chunkPour], sizeof(u8), fileWriteLength, modFile->fp); + f_seek(modFile->fp, fileWriteOffset, SEEK_SET); + f_write(&chunk[chunkPour], sizeof(u8), fileWriteLength, modFile->fp); modFile->wroteBytes += fileWriteLength; if (modFile->wroteBytes >= modFile->size) { - fflush(modFile->fp); - fclose(modFile->fp); + f_flush(modFile->fp); + f_close(modFile->fp); modFile->fp = NULL; + + // Write cachedPath here so the file doesn't end up in mod.cache + if (!should_cache_mod(mod)) { + char modFilePath[SYS_MAX_PATH] = { 0 }; + concat_path(modFilePath, mod->basePath, modFile->relativePath); + normalize_path(modFilePath); + modFile->cachedPath = strdup(modFilePath); + } } wroteBytes += fileWriteLength;