More progress on mod table rewrite

This commit is contained in:
MysterD 2022-03-14 19:30:02 -07:00
parent 0a3d0b2033
commit 0983474429
9 changed files with 248 additions and 45 deletions

View file

@ -13,7 +13,7 @@
#include "gfx/gfx_window_manager_api.h" #include "gfx/gfx_window_manager_api.h"
#include "controller/controller_api.h" #include "controller/controller_api.h"
#include "fs/fs.h" #include "fs/fs.h"
#include "pc/mod_list.h" #include "pc/mods/mods.h"
#include "pc/network/ban_list.h" #include "pc/network/ban_list.h"
#include "pc/crash_handler.h" #include "pc/crash_handler.h"
@ -217,21 +217,27 @@ static const struct ConfigOption options[] = {
// FunctionConfigOption functions // FunctionConfigOption functions
static void enable_mod_read(char** tokens, UNUSED int numTokens) { static void enable_mod_read(char** tokens, UNUSED int numTokens) {
for (unsigned int i = 0; i < gModTableLocal.entryCount; i++) { char combined[256] = { 0 };
struct ModListEntry* entry = &gModTableLocal.entries[i]; for (int i = 1; i < numTokens; i++) {
if (!strcmp(tokens[1], entry->name)) { if (i != 1) { strncat(combined, " ", 255); }
entry->enabled = true; strncat(combined, tokens[i], 255);
}
for (unsigned int i = 0; i < gLocalMods.entryCount; i++) {
struct Mod* mod = gLocalMods.entries[i];
if (!strcmp(combined, mod->relativePath)) {
mod->enabled = true;
break; break;
} }
} }
} }
static void enable_mod_write(FILE* file) { static void enable_mod_write(FILE* file) {
for (unsigned int i = 0; i < gModTableLocal.entryCount; i++) { for (unsigned int i = 0; i < gLocalMods.entryCount; i++) {
struct ModListEntry* entry = &gModTableLocal.entries[i]; struct Mod* mod = gLocalMods.entries[i];
if (entry == NULL) { continue; } if (mod == NULL) { continue; }
if (!entry->enabled) { continue; } if (!mod->enabled) { continue; }
fprintf(file, "%s %s\n", "enable-mod:", entry->name); fprintf(file, "%s %s\n", "enable-mod:", mod->relativePath);
} }
} }

View file

@ -5,7 +5,8 @@
#include "pc/utils/misc.h" #include "pc/utils/misc.h"
#include "pc/configfile.h" #include "pc/configfile.h"
#include "pc/cheats.h" #include "pc/cheats.h"
#include "pc/mod_list.h" #include "pc/mods/mods.h"
#include "pc/mods/mods_utils.h"
static struct DjuiFlowLayout* sModLayout = NULL; static struct DjuiFlowLayout* sModLayout = NULL;
static struct DjuiThreePanel* sDescriptionPanel = NULL; static struct DjuiThreePanel* sDescriptionPanel = NULL;
@ -43,10 +44,11 @@ static void djui_panel_host_mods_description_create() {
static void djui_mod_checkbox_on_hover(struct DjuiBase* base) { static void djui_mod_checkbox_on_hover(struct DjuiBase* base) {
char* description = ""; char* description = "";
if (base->tag >= 0 && base->tag < gModTableLocal.entryCount) { if (base->tag >= 0 && base->tag < gLocalMods.entryCount) {
char* d = gModTableLocal.entries[base->tag].description; struct Mod* mod = gLocalMods.entries[base->tag];
char* d = mod->description;
if (d != NULL) { if (d != NULL) {
description = gModTableLocal.entries[base->tag].description; description = mod->description;
} }
} }
djui_text_set_text(sTooltip, description); djui_text_set_text(sTooltip, description);
@ -57,15 +59,15 @@ static void djui_mod_checkbox_on_hover_end(UNUSED struct DjuiBase* base) {
} }
static void djui_mod_checkbox_on_value_change(UNUSED struct DjuiBase* base) { static void djui_mod_checkbox_on_value_change(UNUSED struct DjuiBase* base) {
mod_list_update_selectable(); mods_update_selectable();
u16 index = 0; u16 index = 0;
struct DjuiBaseChild* node = sModLayout->base.child; struct DjuiBaseChild* node = sModLayout->base.child;
while (node != NULL) { while (node != NULL) {
if (index >= gModTableLocal.entryCount) { break; } if (index >= gLocalMods.entryCount) { break; }
struct ModListEntry* entry = &gModTableLocal.entries[index]; struct Mod* mod = gLocalMods.entries[index];
djui_base_set_enabled(node->base, entry->selectable); djui_base_set_enabled(node->base, mod->selectable);
// iterate // iterate
index++; index++;
@ -86,7 +88,7 @@ static void djui_panel_host_mods_destroy(struct DjuiBase* base) {
void djui_panel_host_mods_create(struct DjuiBase* caller) { void djui_panel_host_mods_create(struct DjuiBase* caller) {
f32 bodyHeight = (416) + 64 * 1 + 16 * 1; f32 bodyHeight = (416) + 64 * 1 + 16 * 1;
mod_list_update_selectable(); mods_update_selectable();
struct DjuiBase* defaultBase = NULL; struct DjuiBase* defaultBase = NULL;
struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\M\\#1be700\\O\\#00b3ff\\D\\#ffef00\\S"); struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\M\\#1be700\\O\\#00b3ff\\D\\#ffef00\\S");
@ -95,13 +97,13 @@ void djui_panel_host_mods_create(struct DjuiBase* caller) {
struct DjuiPaginated* paginated = djui_paginated_create(&body->base, 8); struct DjuiPaginated* paginated = djui_paginated_create(&body->base, 8);
sModLayout = paginated->layout; sModLayout = paginated->layout;
struct DjuiBase* layoutBase = &paginated->layout->base; struct DjuiBase* layoutBase = &paginated->layout->base;
for (int i = 0; i < gModTableLocal.entryCount; i++) { for (int i = 0; i < gLocalMods.entryCount; i++) {
struct ModListEntry* entry = &gModTableLocal.entries[i]; struct Mod* mod = gLocalMods.entries[i];
struct DjuiCheckbox* checkbox = djui_checkbox_create(layoutBase, entry->displayName ? entry->displayName : entry->name, &entry->enabled); struct DjuiCheckbox* checkbox = djui_checkbox_create(layoutBase, mod->name, &mod->enabled);
checkbox->base.tag = i; checkbox->base.tag = i;
djui_base_set_size_type(&checkbox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size_type(&checkbox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox->base, 1.0f, 32); djui_base_set_size(&checkbox->base, 1.0f, 32);
djui_base_set_enabled(&checkbox->base, entry->selectable); djui_base_set_enabled(&checkbox->base, mod->selectable);
djui_interactable_hook_hover(&checkbox->base, djui_mod_checkbox_on_hover, djui_mod_checkbox_on_hover_end); djui_interactable_hook_hover(&checkbox->base, djui_mod_checkbox_on_hover, djui_mod_checkbox_on_hover_end);
djui_interactable_hook_value_change(&checkbox->base, djui_mod_checkbox_on_value_change); djui_interactable_hook_value_change(&checkbox->base, djui_mod_checkbox_on_value_change);
if (i == 0) { defaultBase = &checkbox->base; } if (i == 0) { defaultBase = &checkbox->base; }

View file

@ -7,7 +7,10 @@
void mod_clear(struct Mod* mod) { void mod_clear(struct Mod* mod) {
for (int j = 0; j < mod->fileCount; j++) { for (int j = 0; j < mod->fileCount; j++) {
struct ModFile* file = &mod->files[j]; struct ModFile* file = &mod->files[j];
file = file; if (file->fp != NULL) {
fclose(file->fp);
file->fp = NULL;
}
} }
if (mod->name != NULL) { if (mod->name != NULL) {
@ -30,6 +33,8 @@ void mod_clear(struct Mod* mod) {
} }
mod->fileCount = 0; mod->fileCount = 0;
mod->size = 0;
free(mod);
} }
static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) { static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
@ -51,6 +56,28 @@ static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
return NULL; return NULL;
} }
// figure out full path
char fullPath[SYS_MAX_PATH] = { 0 };
if (!mod_file_full_path(fullPath, mod, file)) {
LOG_ERROR("Failed to concat path: '%s' + '%s'", mod->basePath, relativePath);
return NULL;
}
// open file
FILE* f = fopen(fullPath, "rb");
if (f == NULL) {
LOG_ERROR("Failed to open '%s'", fullPath);
return NULL;
}
// get size
fseek(f, 0, SEEK_END);
file->size = ftell(f);
mod->size += file->size;
// close file
fclose(f);
return file; return file;
} }
@ -112,7 +139,7 @@ static bool mod_load_files(struct Mod* mod, char* modName, char* fullPath) {
while ((dir = readdir(d)) != NULL) { while ((dir = readdir(d)) != NULL) {
// sanity check / fill path[] // sanity check / fill path[]
if (!directory_sanity_check(dir, actorsPath, path)) { continue; } if (!directory_sanity_check(dir, actorsPath, path)) { continue; }
if (snprintf(relativePath, SYS_MAX_PATH - 1, "%s/actors/%s", modName, dir->d_name) < 0) { if (snprintf(relativePath, SYS_MAX_PATH - 1, "actors/%s", dir->d_name) < 0) {
LOG_ERROR("Could not concat actor path!"); LOG_ERROR("Could not concat actor path!");
return false; return false;
} }
@ -229,23 +256,28 @@ bool mod_load(struct Mods* mods, char* basePath, char* modName) {
} }
// make sure mod is unique // make sure mod is unique
for (int i = 0; i < mods->modCount; i++) { for (int i = 0; i < mods->entryCount; i++) {
struct Mod* compareMod = &mods->entries[i]; struct Mod* compareMod = mods->entries[i];
if (!strcmp(compareMod->relativePath, modName)) { if (!strcmp(compareMod->relativePath, modName)) {
return true; return true;
} }
} }
// allocate mod // allocate mod
u16 modIndex = mods->modCount++; u16 modIndex = mods->entryCount++;
mods->entries = realloc(mods->entries, sizeof(struct Mod) * mods->modCount); mods->entries = realloc(mods->entries, sizeof(struct Mod*) * mods->entryCount);
if (mods->entries == NULL) { if (mods->entries == NULL) {
LOG_ERROR("Failed to allocate entries!"); LOG_ERROR("Failed to allocate entries!");
mods_clear(mods); mods_clear(mods);
return false; return false;
} }
struct Mod* mod = &mods->entries[modIndex]; mods->entries[modIndex] = calloc(1, sizeof(struct Mod));
memset(mod, 0, sizeof(struct Mod)); struct Mod* mod = mods->entries[modIndex];
if (mod == NULL) {
LOG_ERROR("Failed to allocate mod!");
mods_clear(mods);
return false;
}
// set paths // set paths
char* cpyPath = isDirectory ? fullPath : basePath; char* cpyPath = isDirectory ? fullPath : basePath;

View file

@ -9,6 +9,8 @@ struct Mods;
struct ModFile { struct ModFile {
char relativePath[SYS_MAX_PATH]; char relativePath[SYS_MAX_PATH];
size_t size;
FILE* fp;
}; };
struct Mod { struct Mod {
@ -21,6 +23,8 @@ struct Mod {
u16 fileCount; u16 fileCount;
bool isDirectory; bool isDirectory;
bool enabled; bool enabled;
bool selectable;
size_t size;
}; };
void mod_clear(struct Mod* mod); void mod_clear(struct Mod* mod);

View file

@ -5,19 +5,55 @@
#define MOD_DIRECTORY "mods" #define MOD_DIRECTORY "mods"
static struct Mods gLocalMods = { 0 }; struct Mods gLocalMods = { 0 };
struct Mods gRemoteMods = { 0 };
struct Mods gActiveMods = { 0 };
void mods_clear(struct Mods* mods) { void mods_activate(struct Mods* mods) {
for (int i = 0; i < mods->modCount; i ++) { mods_clear(&gActiveMods);
struct Mod* mod = &mods->entries[i];
mod_clear(mod); // count enabled
u16 enabledCount = 0;
for (int i = 0; i < mods->entryCount; i++) {
struct Mod* mod = mods->entries[i];
if (mod->enabled) { enabledCount++; }
} }
if (mods->entries != NULL) { // allocate
free(mods->entries); gActiveMods.entries = calloc(enabledCount, sizeof(struct Mod*));
mods->entries = NULL; if (gActiveMods.entries == NULL) {
LOG_ERROR("Failed to allocate active mods table!");
return;
}
// copy enabled entries
for (int i = 0; i < mods->entryCount; i++) {
struct Mod* mod = mods->entries[i];
if (mod->enabled) {
gActiveMods.entries[gActiveMods.entryCount++] = mod;
}
}
// open file pointers
for (int i = 0; i < gActiveMods.entryCount; i++) {
struct Mod* mod = gActiveMods.entries[i];
for (int j = 0; j < mod->fileCount; j++) {
struct ModFile* file = &mod->files[j];
char fullPath[SYS_MAX_PATH] = { 0 };
if (!mod_file_full_path(fullPath, mod, file)) {
LOG_ERROR("Failed to concat path: '%s' + '%s'", mod->basePath, relativePath);
continue;
}
file->fp = fopen(fullPath, "rb");
if (file->fp == NULL) {
LOG_ERROR("Failed to open file '%s'", fullPath);
continue;
}
}
} }
mods->modCount = 0;
} }
static void mods_load(struct Mods* mods, char* modsBasePath) { static void mods_load(struct Mods* mods, char* modsBasePath) {
@ -78,8 +114,50 @@ void mods_init(void) {
// load mods // load mods
if (hasUserPath) { mods_load(&gLocalMods, userModPath); } if (hasUserPath) { mods_load(&gLocalMods, userModPath); }
mods_load(&gLocalMods, "./" MOD_DIRECTORY); mods_load(&gLocalMods, "./" MOD_DIRECTORY);
// calculate total size
gLocalMods.size = 0;
for (int i = 0; i < gLocalMods.entryCount; i++) {
struct Mod* mod = gLocalMods.entries[i];
gLocalMods.size += mod->size;
}
}
void mods_clear(struct Mods* mods) {
if (mods == &gActiveMods) {
// don't clear the mods of gActiveMods since they're a copy
// just close all file pointers
for (int i = 0; i < mods->entryCount; i ++) {
struct Mod* mod = mods->entries[i];
for (int j = 0; j < mod->fileCount; j++) {
struct ModFile* file = &mod->files[j];
if (file->fp != NULL) {
fclose(file->fp);
file->fp = NULL;
}
}
}
} else {
// clear mods of gLocalMods and gRemoteMods
for (int i = 0; i < mods->entryCount; i ++) {
struct Mod* mod = mods->entries[i];
mod_clear(mod);
}
}
// cleanup entries
if (mods->entries != NULL) {
free(mods->entries);
mods->entries = NULL;
}
// cleanup params
mods->entryCount = 0;
mods->size = 0;
} }
void mods_shutdown(void) { void mods_shutdown(void) {
mods_clear(&gRemoteMods);
mods_clear(&gActiveMods);
mods_clear(&gLocalMods); mods_clear(&gLocalMods);
} }

View file

@ -6,11 +6,18 @@
#include "src/pc/platform.h" #include "src/pc/platform.h"
#include "mod.h" #include "mod.h"
#define MAX_MOD_SIZE (2 * 1048576) // 2MB
struct Mods { struct Mods {
struct Mod* entries; struct Mod** entries;
u16 modCount; u16 entryCount;
size_t size;
}; };
extern struct Mods gLocalMods;
extern struct Mods gRemoteMods;
extern struct Mods gActiveMods;
void mods_clear(struct Mods* mods); void mods_clear(struct Mods* mods);
void mods_init(void); void mods_init(void);
void mods_shutdown(void); void mods_shutdown(void);

View file

@ -1,8 +1,78 @@
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "mods.h"
#include "mods_utils.h" #include "mods_utils.h"
#include "pc/debuglog.h" #include "pc/debuglog.h"
void mods_size_enforce(void) {
for (int i = 0; i < gLocalMods.entryCount; i++) {
struct Mod* mod = gLocalMods.entries[i];
if (mod->size >= MAX_MOD_SIZE) {
mod->enabled = false;
mod->selectable = false;
}
}
}
static bool mods_incompatible_match(struct Mod* a, struct Mod* b) {
if (a->incompatible == NULL || b->incompatible == NULL) {
return false;
}
if (strlen(a->incompatible) == 0 || strlen(b->incompatible) == 0) {
return false;
}
char* ai = a->incompatible;
char* bi = b->incompatible;
char* atoken = NULL;
char* btoken = NULL;
char* arest = NULL;
char* brest = NULL;
for (atoken = strtok_r(ai, " ", &arest); atoken != NULL; atoken = strtok_r(NULL, " ", &arest)) {
for (btoken = strtok_r(bi, " ", &brest); btoken != NULL; btoken = strtok_r(NULL, " ", &brest)) {
if (!strcmp(atoken, btoken)) {
return true;
}
}
}
return false;
}
void mods_update_selectable(void) {
// reset selectable value
for (int i = 0; i < gLocalMods.entryCount; i++) {
struct Mod* mod = gLocalMods.entries[i];
mod->selectable = true;
}
// figure out which ones to deselect
for (int i = 0; i < gLocalMods.entryCount; i++) {
struct Mod* mod = gLocalMods.entries[i];
if (mod->enabled) { continue; }
for (int j = 0; j < gLocalMods.entryCount; j++) {
if (j == i) { continue; }
struct Mod* mod2 = gLocalMods.entries[j];
if (!mod2->enabled) { continue; }
if (mods_incompatible_match(mod, mod2)) {
mod->selectable = false;
break;
}
}
}
mods_size_enforce();
}
bool mod_file_full_path(char* destination, struct Mod* mod, struct ModFile* modFile) {
return concat_path(destination, mod->basePath, modFile->relativePath);
}
//////////////////////////////////////////////////////////////////////////////////////////
bool str_ends_with(char* string, char* suffix) { bool str_ends_with(char* string, char* suffix) {
if (string == NULL || suffix == NULL) { return false; } if (string == NULL || suffix == NULL) { return false; }
@ -14,6 +84,7 @@ bool str_ends_with(char* string, char* suffix) {
return !strcmp(&string[stringLength - suffixLength], suffix); return !strcmp(&string[stringLength - suffixLength], suffix);
} }
//////////////////////////////////////////////////////////////////////////////////////////
char* extract_lua_field(char* fieldName, char* buffer) { char* extract_lua_field(char* fieldName, char* buffer) {
size_t length = strlen(fieldName); size_t length = strlen(fieldName);
@ -25,6 +96,8 @@ char* extract_lua_field(char* fieldName, char* buffer) {
return NULL; return NULL;
} }
//////////////////////////////////////////////////////////////////////////////////////////
bool path_exists(char* path) { bool path_exists(char* path) {
struct stat sb = { 0 }; struct stat sb = { 0 };
return (stat(path, &sb) == 0); return (stat(path, &sb) == 0);

View file

@ -5,6 +5,10 @@
#include <types.h> #include <types.h>
#include "src/pc/platform.h" #include "src/pc/platform.h"
void mods_size_enforce(void);
void mods_update_selectable(void);
bool mod_file_full_path(char* destination, struct Mod* mod, struct ModFile* modFile);
bool str_ends_with(char* string, char* suffix); bool str_ends_with(char* string, char* suffix);
char* extract_lua_field(char* fieldName, char* buffer); char* extract_lua_field(char* fieldName, char* buffer);

View file

@ -49,7 +49,6 @@
#include "pc/network/network_player.h" #include "pc/network/network_player.h"
#include "pc/djui/djui.h" #include "pc/djui/djui.h"
#include "pc/mod_list.h"
#include "pc/mods/mods.h" #include "pc/mods/mods.h"
OSMesg D_80339BEC; OSMesg D_80339BEC;
@ -171,7 +170,6 @@ void game_deinit(void) {
gfx_shutdown(); gfx_shutdown();
network_shutdown(true); network_shutdown(true);
smlua_shutdown(); smlua_shutdown();
mod_list_shutdown();
mods_shutdown(); mods_shutdown();
inited = false; inited = false;
} }
@ -222,7 +220,6 @@ void main_func(void) {
const char *userpath = gCLIOpts.SavePath[0] ? gCLIOpts.SavePath : sys_user_path(); const char *userpath = gCLIOpts.SavePath[0] ? gCLIOpts.SavePath : sys_user_path();
fs_init(sys_ropaths, gamedir, userpath); fs_init(sys_ropaths, gamedir, userpath);
mod_list_init();
mods_init(); mods_init();
configfile_load(configfile_name()); configfile_load(configfile_name());
if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; } if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; }