Prevent the game from caching downloaded WIP mods (#69)

* don't tmp or cache wip mods

* update
This commit is contained in:
PeachyPeach 2024-06-23 17:54:07 +02:00 committed by GitHub
parent 69cb215219
commit c4214ed2da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 351 additions and 56 deletions

View file

@ -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;

View file

@ -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();

164
src/pc/fs/fmem.c Normal file
View file

@ -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;
}

18
src/pc/fs/fmem.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef FMEM_H
#define FMEM_H
#include <stdio.h>
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

View file

@ -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);

View file

@ -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;
}

View file

@ -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) {

View file

@ -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 <windows.h>
@ -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;
}
}

View file

@ -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;

View file

@ -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;
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;