mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-21 11:35:11 +00:00
Low-hanging fruit mod loading optimizations (#493)
* Low-hanging fruit mod loading optimizations * Fix DynOS fread spam and don't use a linked list for mod cache (still O(n^2), but much faster)
This commit is contained in:
parent
15eb8dd8fc
commit
693a77078d
6 changed files with 89 additions and 70 deletions
|
@ -36,14 +36,18 @@ char *DynOS_Read_Buffer(FILE* aFile, GfxData* aGfxData) {
|
||||||
aGfxData->mModelIdentifier = (u32) _Length;
|
aGfxData->mModelIdentifier = (u32) _Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove comments
|
char *_OrigFileBuffer = New<char>(_Length + 1);
|
||||||
|
char *pOrigFileBuffer = _OrigFileBuffer;
|
||||||
rewind(aFile);
|
rewind(aFile);
|
||||||
|
_OrigFileBuffer[fread(_OrigFileBuffer, 1, _Length, aFile)] = 0;
|
||||||
|
|
||||||
|
// Remove comments
|
||||||
char *_FileBuffer = New<char>(_Length + 1);
|
char *_FileBuffer = New<char>(_Length + 1);
|
||||||
char *pFileBuffer = _FileBuffer;
|
char *pFileBuffer = _FileBuffer;
|
||||||
char _Previous = 0;
|
char _Previous = 0;
|
||||||
char _Current = 0;
|
char _Current = 0;
|
||||||
s32 _CommentType = 0;
|
s32 _CommentType = 0;
|
||||||
while (fread(&_Current, 1, 1, aFile)) {
|
while ((_Current = *pOrigFileBuffer++)) {
|
||||||
if (_CommentType == COMMENT_NONE) {
|
if (_CommentType == COMMENT_NONE) {
|
||||||
if (_Current == '/') {
|
if (_Current == '/') {
|
||||||
_CommentType = COMMENT_START;
|
_CommentType = COMMENT_START;
|
||||||
|
@ -79,6 +83,7 @@ char *DynOS_Read_Buffer(FILE* aFile, GfxData* aGfxData) {
|
||||||
_Previous = _Current;
|
_Previous = _Current;
|
||||||
}
|
}
|
||||||
*(pFileBuffer++) = 0;
|
*(pFileBuffer++) = 0;
|
||||||
|
Delete(_OrigFileBuffer);
|
||||||
|
|
||||||
// Remove ifdef blocks
|
// Remove ifdef blocks
|
||||||
// Doesn't support nested blocks
|
// Doesn't support nested blocks
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
|
#include <fileapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
@ -292,18 +293,32 @@ bool fs_sys_filename_is_portable(char const *filename) {
|
||||||
/* these operate on the real file system */
|
/* these operate on the real file system */
|
||||||
|
|
||||||
bool fs_sys_path_exists(const char *name) {
|
bool fs_sys_path_exists(const char *name) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
|
||||||
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
return (stat(name, &st) == 0);
|
return (stat(name, &st) == 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs_sys_file_exists(const char *name) {
|
bool fs_sys_file_exists(const char *name) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD attribs = GetFileAttributesA(name);
|
||||||
|
return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
return (stat(name, &st) == 0 && S_ISREG(st.st_mode));
|
return (stat(name, &st) == 0 && S_ISREG(st.st_mode));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs_sys_dir_exists(const char *name) {
|
bool fs_sys_dir_exists(const char *name) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD attribs = GetFileAttributesA(name);
|
||||||
|
return attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
return (stat(name, &st) == 0 && S_ISDIR(st.st_mode));
|
return (stat(name, &st) == 0 && S_ISDIR(st.st_mode));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs_sys_dir_is_empty(const char *name) {
|
bool fs_sys_dir_is_empty(const char *name) {
|
||||||
|
|
|
@ -204,18 +204,22 @@ void mod_clear(struct Mod* mod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod->fileCount = 0;
|
mod->fileCount = 0;
|
||||||
|
mod->fileCapacity = 0;
|
||||||
mod->size = 0;
|
mod->size = 0;
|
||||||
free(mod);
|
free(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
|
static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
|
||||||
// actual allocation
|
// actual allocation
|
||||||
u16 fileIndex = mod->fileCount++;
|
if (mod->fileCount == mod->fileCapacity) {
|
||||||
mod->files = realloc(mod->files, sizeof(struct ModFile) * mod->fileCount);
|
mod->fileCapacity = (mod->fileCapacity == 0) ? 16 : (mod->fileCapacity * 2);
|
||||||
if (mod->files == NULL) {
|
mod->files = realloc(mod->files, sizeof(struct ModFile) * mod->fileCapacity);
|
||||||
LOG_ERROR("Failed to allocate file: '%s'", relativePath);
|
if (mod->files == NULL) {
|
||||||
return NULL;
|
LOG_ERROR("Failed to allocate file: '%s'", relativePath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
u16 fileIndex = mod->fileCount++;
|
||||||
|
|
||||||
// clear memory
|
// clear memory
|
||||||
struct ModFile* file = &mod->files[fileIndex];
|
struct ModFile* file = &mod->files[fileIndex];
|
||||||
|
|
|
@ -30,6 +30,7 @@ struct Mod {
|
||||||
struct ModFile* files;
|
struct ModFile* files;
|
||||||
s32 index;
|
s32 index;
|
||||||
u16 fileCount;
|
u16 fileCount;
|
||||||
|
u16 fileCapacity;
|
||||||
bool isDirectory;
|
bool isDirectory;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool selectable;
|
bool selectable;
|
||||||
|
|
|
@ -15,25 +15,26 @@
|
||||||
#define MOD_CACHE_VERSION 7
|
#define MOD_CACHE_VERSION 7
|
||||||
#define MD5_BUFFER_SIZE 1024
|
#define MD5_BUFFER_SIZE 1024
|
||||||
|
|
||||||
struct ModCacheEntry* sModCacheHead = NULL;
|
static struct ModCacheEntry* sModCacheEntries = NULL;
|
||||||
|
static size_t sModCacheLength = 0;
|
||||||
|
static size_t sModLengthCapacity = 0;
|
||||||
|
|
||||||
static void mod_cache_remove_node(struct ModCacheEntry* node, struct ModCacheEntry* parent) {
|
static void mod_cache_remove_node(struct ModCacheEntry* node) {
|
||||||
if (node == NULL) { return; }
|
|
||||||
if (node == sModCacheHead) { sModCacheHead = node->next; }
|
|
||||||
if (parent != NULL) { parent->next = node->next; }
|
|
||||||
//LOG_INFO("Removing node: %s", node->path);
|
|
||||||
if (node->path) {
|
if (node->path) {
|
||||||
free(node->path);
|
free(node->path);
|
||||||
node->path = NULL;
|
node->path = NULL;
|
||||||
}
|
}
|
||||||
free(node);
|
if (node != &sModCacheEntries[sModCacheLength - 1])
|
||||||
|
memcpy(node, &sModCacheEntries[sModCacheLength - 1], sizeof(struct ModCacheEntry));
|
||||||
|
sModCacheLength--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mod_cache_shutdown(void) {
|
void mod_cache_shutdown(void) {
|
||||||
LOG_INFO("Shutting down mod cache.");
|
LOG_INFO("Shutting down mod cache.");
|
||||||
while (sModCacheHead) {
|
sModCacheLength = 0;
|
||||||
mod_cache_remove_node(sModCacheHead, NULL);
|
sModLengthCapacity = 0;
|
||||||
}
|
free(sModCacheEntries);
|
||||||
|
sModCacheEntries = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mod_cache_md5(const char* inPath, u8* outDataPath) {
|
void mod_cache_md5(const char* inPath, u8* outDataPath) {
|
||||||
|
@ -77,6 +78,15 @@ void mod_cache_md5(const char* inPath, u8* outDataPath) {
|
||||||
MD5_Final(outDataPath, &ctx);
|
MD5_Final(outDataPath, &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 mod_cache_fnv1a(const char* str) {
|
||||||
|
u64 hash = 0xCBF29CE484222325;
|
||||||
|
while (*str) {
|
||||||
|
hash *= 0x100000001B3;
|
||||||
|
hash ^= *str++;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
static bool mod_cache_is_valid(struct ModCacheEntry* node) {
|
static bool mod_cache_is_valid(struct ModCacheEntry* node) {
|
||||||
if (node == NULL || node->path == NULL || strlen(node->path) == 0) {
|
if (node == NULL || node->path == NULL || strlen(node->path) == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -88,42 +98,37 @@ static bool mod_cache_is_valid(struct ModCacheEntry* node) {
|
||||||
|
|
||||||
struct ModCacheEntry* mod_cache_get_from_hash(u8* dataHash) {
|
struct ModCacheEntry* mod_cache_get_from_hash(u8* dataHash) {
|
||||||
if (dataHash == NULL) { return NULL; }
|
if (dataHash == NULL) { return NULL; }
|
||||||
struct ModCacheEntry* node = sModCacheHead;
|
for (size_t i = 0; i < sModCacheLength;) {
|
||||||
struct ModCacheEntry* prev = NULL;
|
struct ModCacheEntry* node = &sModCacheEntries[i];
|
||||||
while (node != NULL) {
|
|
||||||
struct ModCacheEntry* next = node->next;
|
|
||||||
if (!memcmp(node->dataHash, dataHash, 16)) {
|
if (!memcmp(node->dataHash, dataHash, 16)) {
|
||||||
if (mod_cache_is_valid(node)) {
|
if (mod_cache_is_valid(node)) {
|
||||||
return node;
|
return node;
|
||||||
} else {
|
} else {
|
||||||
mod_cache_remove_node(node, prev);
|
mod_cache_remove_node(node);
|
||||||
node = prev;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev = node;
|
i++;
|
||||||
node = next;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ModCacheEntry* mod_cache_get_from_path(const char* path, bool validate) {
|
struct ModCacheEntry* mod_cache_get_from_path(const char* path, bool validate) {
|
||||||
if (path == NULL || strlen(path) == 0) { return NULL; }
|
if (path == NULL || strlen(path) == 0) { return NULL; }
|
||||||
struct ModCacheEntry* node = sModCacheHead;
|
u64 pathHash = mod_cache_fnv1a(path);
|
||||||
struct ModCacheEntry* prev = NULL;
|
for (size_t i = 0; i < sModCacheLength;) {
|
||||||
while (node != NULL) {
|
struct ModCacheEntry* node = &sModCacheEntries[i];
|
||||||
struct ModCacheEntry* next = node->next;
|
if (node->pathHash == pathHash && !strcmp(node->path, path)) {
|
||||||
if (!strcmp(node->path, path)) {
|
|
||||||
if (!validate) {
|
if (!validate) {
|
||||||
return node;
|
return node;
|
||||||
} else if (mod_cache_is_valid(node)) {
|
} else if (mod_cache_is_valid(node)) {
|
||||||
return node;
|
return node;
|
||||||
} else {
|
} else {
|
||||||
mod_cache_remove_node(node, prev);
|
mod_cache_remove_node(node);
|
||||||
node = prev;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev = node;
|
i++;
|
||||||
node = next;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +148,7 @@ void mod_cache_add_internal(u8* dataHash, u64 lastLoaded, char* inPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
normalize_path((char*)path);
|
normalize_path((char*)path);
|
||||||
|
u64 pathHash = mod_cache_fnv1a(path);
|
||||||
|
|
||||||
bool foundNonZero = false;
|
bool foundNonZero = false;
|
||||||
for (u8 i = 0; i < 16; i++) {
|
for (u8 i = 0; i < 16; i++) {
|
||||||
|
@ -157,43 +163,34 @@ void mod_cache_add_internal(u8* dataHash, u64 lastLoaded, char* inPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ModCacheEntry* node = calloc(1, sizeof(struct ModCacheEntry));
|
if (sModCacheEntries == NULL) {
|
||||||
memcpy(node->dataHash, dataHash, sizeof(u8) * 16);
|
sModLengthCapacity = 16;
|
||||||
if (lastLoaded == 0) { lastLoaded = clock(); }
|
sModCacheLength = 0;
|
||||||
node->lastLoaded = lastLoaded;
|
sModCacheEntries = calloc(sModLengthCapacity, sizeof(struct ModCacheEntry));
|
||||||
node->path = (char*)path;
|
} else if (sModCacheLength == sModLengthCapacity) {
|
||||||
node->next = NULL;
|
sModLengthCapacity *= 2;
|
||||||
|
sModCacheEntries = realloc(sModCacheEntries, sizeof(struct ModCacheEntry) * sModLengthCapacity);
|
||||||
if (sModCacheHead == NULL) {
|
|
||||||
sModCacheHead = node;
|
|
||||||
LOG_INFO("Added head: %s", node->path);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ModCacheEntry* n = sModCacheHead;
|
struct ModCacheEntry node = {};
|
||||||
struct ModCacheEntry* prev = NULL;
|
memcpy(node.dataHash, dataHash, sizeof(u8) * 16);
|
||||||
while (n != NULL) {
|
if (lastLoaded == 0) { lastLoaded = clock(); }
|
||||||
struct ModCacheEntry* next = n->next;
|
node.lastLoaded = lastLoaded;
|
||||||
|
node.path = (char*)path;
|
||||||
|
node.pathHash = pathHash;
|
||||||
|
|
||||||
// found end of list, add it
|
for (size_t i = 0; i < sModCacheLength;) {
|
||||||
if (next == NULL) {
|
struct ModCacheEntry* n = &sModCacheEntries[i];
|
||||||
LOG_INFO("Added node: %s", node->path);
|
|
||||||
if (n != node) { n->next = node; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// found old hash, remove it
|
// found old hash, remove it
|
||||||
if (!strcmp(n->path, path)) {
|
if (n->pathHash == pathHash && !strcmp(n->path, path)) {
|
||||||
LOG_INFO("Removing old node: %s", node->path);
|
LOG_INFO("Removing old node: %s", node->path);
|
||||||
mod_cache_remove_node(n, prev);
|
mod_cache_remove_node(n);
|
||||||
} else {
|
} else {
|
||||||
prev = n;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = next;
|
|
||||||
}
|
}
|
||||||
|
memcpy(&sModCacheEntries[sModCacheLength++], &node, sizeof(node));
|
||||||
LOG_ERROR("Did not add node for some reason?");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mod_cache_add(struct Mod* mod, struct ModFile* file, bool useFilePath) {
|
void mod_cache_add(struct Mod* mod, struct ModFile* file, bool useFilePath) {
|
||||||
|
@ -329,19 +326,16 @@ void mod_cache_save(void) {
|
||||||
u8 t = *gBehaviorOffset != 0;
|
u8 t = *gBehaviorOffset != 0;
|
||||||
fwrite(&t, sizeof(u8), 1, fp);
|
fwrite(&t, sizeof(u8), 1, fp);
|
||||||
|
|
||||||
struct ModCacheEntry* node = sModCacheHead;
|
for (size_t i = 0; i < sModCacheLength; i++) {
|
||||||
while (node != NULL) {
|
struct ModCacheEntry* node = &sModCacheEntries[i];
|
||||||
struct ModCacheEntry* next = node->next;
|
if (node->path == NULL) { continue; }
|
||||||
if (node->path == NULL) { goto iterate; }
|
|
||||||
u16 pathLen = strlen(node->path);
|
u16 pathLen = strlen(node->path);
|
||||||
if (pathLen == 0) { goto iterate; }
|
if (pathLen == 0) { continue; }
|
||||||
|
|
||||||
fwrite(node->dataHash, sizeof(u8), 16, fp);
|
fwrite(node->dataHash, sizeof(u8), 16, fp);
|
||||||
fwrite(&node->lastLoaded, sizeof(u64), 1, fp);
|
fwrite(&node->lastLoaded, sizeof(u64), 1, fp);
|
||||||
fwrite(&pathLen, sizeof(u16), 1, fp);
|
fwrite(&pathLen, sizeof(u16), 1, fp);
|
||||||
fwrite(node->path, sizeof(u8), pathLen + 1, fp);
|
fwrite(node->path, sizeof(u8), pathLen + 1, fp);
|
||||||
iterate:
|
|
||||||
node = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct ModCacheEntry {
|
||||||
u8 dataHash[16];
|
u8 dataHash[16];
|
||||||
u64 lastLoaded;
|
u64 lastLoaded;
|
||||||
char* path;
|
char* path;
|
||||||
struct ModCacheEntry* next;
|
u64 pathHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mod_cache_md5(const char* inPath, u8* outDataPath);
|
void mod_cache_md5(const char* inPath, u8* outDataPath);
|
||||||
|
|
Loading…
Reference in a new issue