Started rewriting the mod table

This commit is contained in:
MysterD 2022-03-14 18:18:14 -07:00
parent d2522cc955
commit 290cc8f531
8 changed files with 535 additions and 1 deletions

View File

@ -455,7 +455,7 @@ SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels
BIN_DIRS := bin bin/$(VERSION)
# PC files
SRC_DIRS += src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/network src/pc/network/packets src/pc/network/socket src/pc/utils src/pc/djui src/pc/lua src/pc/lua/utils
SRC_DIRS += src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/mods src/pc/network src/pc/network/packets src/pc/network/socket src/pc/utils src/pc/djui src/pc/lua src/pc/lua/utils
#ifeq ($(DISCORDRPC),1)
# SRC_DIRS += src/pc/discord

290
src/pc/mods/mod.c Normal file
View File

@ -0,0 +1,290 @@
#include "mod.h"
#include "mods.h"
#include "mods_utils.h"
#include "pc/utils/misc.h"
#include "pc/debuglog.h"
void mod_clear(struct Mod* mod) {
for (int j = 0; j < mod->fileCount; j++) {
struct ModFile* file = &mod->files[j];
file = file;
}
if (mod->name != NULL) {
free(mod->name);
mod->name = NULL;
}
if (mod->incompatible != NULL) {
free(mod->incompatible);
mod->incompatible = NULL;
}
if (mod->description != NULL) {
free(mod->description);
mod->description = NULL;
}
if (mod->files != NULL) {
free(mod->files);
}
mod->fileCount = 0;
}
static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
// actual allocation
u16 fileIndex = mod->fileCount++;
mod->files = realloc(mod->files, sizeof(struct ModFile) * mod->fileCount);
if (mod->files == NULL) {
LOG_ERROR("Failed to allocate file: '%s'", relativePath);
return NULL;
}
// clear memory
struct ModFile* file = &mod->files[fileIndex];
memset(file, 0, sizeof(struct ModFile));
// set relative path
if (snprintf(file->relativePath, SYS_MAX_PATH - 1, "%s", relativePath) < 0) {
LOG_ERROR("Failed to remember relative path '%s'", relativePath);
return NULL;
}
return file;
}
static bool mod_load_files(struct Mod* mod, char* modName, char* fullPath) {
// read single lua file
if (!mod->isDirectory) {
return (mod_allocate_file(mod, modName) != NULL);
}
// deal with mod directory
{
// open mod directory
struct dirent* dir = NULL;
DIR* d = opendir(fullPath);
if (!d) {
LOG_ERROR("Could not open directory '%s'", fullPath);
return false;
}
// iterate mod directory
char path[SYS_MAX_PATH] = { 0 };
while ((dir = readdir(d)) != NULL) {
// sanity check / fill path[]
if (!directory_sanity_check(dir, fullPath, path)) { continue; }
// only consider lua files
if (!str_ends_with(path, ".lua")) {
continue;
}
// allocate file
struct ModFile* file = mod_allocate_file(mod, dir->d_name);
if (file == NULL) { return false; }
}
closedir(d);
}
// deal with actors directory
{
// concat actors directory
char actorsPath[SYS_MAX_PATH] = { 0 };
if (!concat_path(actorsPath, fullPath, "actors")) {
LOG_ERROR("Could not concat directory '%s' + '%s'", fullPath, "actors");
return false;
}
// open actors directory
struct dirent* dir = NULL;
DIR* d = opendir(actorsPath);
if (!d) {
LOG_ERROR("Could not open directory '%s'", actorsPath);
return false;
}
// iterate mod directory
char path[SYS_MAX_PATH] = { 0 };
char relativePath[SYS_MAX_PATH] = { 0 };
while ((dir = readdir(d)) != NULL) {
// sanity check / fill path[]
if (!directory_sanity_check(dir, actorsPath, path)) { continue; }
if (snprintf(relativePath, SYS_MAX_PATH - 1, "%s/actors/%s", modName, dir->d_name) < 0) {
LOG_ERROR("Could not concat actor path!");
return false;
}
// only consider bin files
if (!str_ends_with(path, ".bin")) {
continue;
}
// allocate file
struct ModFile* file = mod_allocate_file(mod, relativePath);
if (file == NULL) { return false; }
}
closedir(d);
}
return true;
}
static void mod_extract_fields(struct Mod* mod) {
// get full path
char path[SYS_MAX_PATH] = { 0 };
char* relativePath = NULL;
if (mod->isDirectory) {
for (int i = 0; i < mod->fileCount; i++) {
struct ModFile* file = &mod->files[i];
if (!strcmp(file->relativePath, "main.lua")) {
relativePath = file->relativePath;
}
}
} else {
relativePath = mod->files[0].relativePath;
}
if (relativePath == NULL || !concat_path(path, mod->basePath, relativePath)) {
LOG_ERROR("Failed to find main lua file.");
return;
}
// open file
FILE* f = fopen(path, "rb");
if (f == NULL) {
LOG_ERROR("Failed to open '%s'", path);
return;
}
fseek(f, 0, SEEK_SET);
// default to null
mod->name = NULL;
mod->incompatible = NULL;
mod->description = NULL;
// read line-by-line
char buffer[512] = { 0 };
while (!feof(f)) {
file_get_line(buffer, 512, f);
// no longer in header
if (buffer[0] != '-' || buffer[1] != '-') {
return;
}
// extract the field
char* extracted = NULL;
if (mod->name == NULL && (extracted = extract_lua_field("-- name:", buffer))) {
mod->name = calloc(33, sizeof(char));
if (snprintf(mod->name, 32, "%s", extracted) < 0) {
LOG_INFO("Truncated mod name field '%s'", mod->name);
}
} else if (mod->incompatible == NULL && (extracted = extract_lua_field("-- incompatible:", buffer))) {
mod->incompatible = calloc(257, sizeof(char));
if (snprintf(mod->incompatible, 256, "%s", extracted) < 0) {
LOG_INFO("Truncated mod incompatible field '%s'", mod->incompatible);
}
} else if (mod->description == NULL && (extracted = extract_lua_field("-- description:", buffer))) {
mod->description = calloc(513, sizeof(char));
if (snprintf(mod->description, 512, "%s", extracted) < 0) {
LOG_INFO("Truncated mod description field '%s'", mod->description);
}
}
}
// close file
fclose(f);
}
bool mod_load(struct Mods* mods, char* basePath, char* modName) {
bool valid = false;
char fullPath[SYS_MAX_PATH] = { 0 };
if (!concat_path(fullPath, basePath, modName)) {
LOG_ERROR("Failed to concat path '%s' + '%s'", basePath, modName);
return true;
}
bool isDirectory = is_directory(fullPath);
// make sure mod is valid
if (str_ends_with(modName, ".lua")) {
valid = true;
} else if (is_directory(fullPath)) {
char tmpPath[SYS_MAX_PATH] = { 0 };
if (!concat_path(tmpPath, fullPath, "main.lua")) {
LOG_ERROR("Failed to concat path '%s' + '%s'", fullPath, "main.lua");
return true;
}
valid = path_exists(tmpPath);
}
if (!valid) {
LOG_ERROR("Found invalid mod '%s'", fullPath);
return true;
}
// make sure mod is unique
for (int i = 0; i < mods->modCount; i++) {
struct Mod* compareMod = &mods->entries[i];
if (!strcmp(compareMod->relativePath, modName)) {
return true;
}
}
// allocate mod
u16 modIndex = mods->modCount++;
mods->entries = realloc(mods->entries, sizeof(struct Mod) * mods->modCount);
if (mods->entries == NULL) {
LOG_ERROR("Failed to allocate entries!");
mods_clear(mods);
return false;
}
struct Mod* mod = &mods->entries[modIndex];
memset(mod, 0, sizeof(struct Mod));
// set paths
char* cpyPath = isDirectory ? fullPath : basePath;
if (snprintf(mod->basePath, SYS_MAX_PATH - 1, "%s", cpyPath) < 0) {
LOG_ERROR("Failed to remember mod path '%s'!", cpyPath);
mods_clear(mods);
return false;
}
if (snprintf(mod->relativePath, SYS_MAX_PATH - 1, "%s", modName) < 0) {
LOG_ERROR("Failed to remember mod path '%s'!", modName);
mods_clear(mods);
return false;
}
// set directory
mod->isDirectory = isDirectory;
// read files
if (!mod_load_files(mod, modName, fullPath)) {
LOG_ERROR("Failed to load mod files for '%s'", modName);
return false;
}
// extract fields
mod_extract_fields(mod);
// set name
if (mod->name == NULL) {
mod->name = strdup(modName);
}
// print
LOG_INFO(" %s", mod->name);
if (isDirectory) {
for (int i = 0; i < mod->fileCount; i++) {
struct ModFile* file = &mod->files[i];
LOG_INFO(" - %s", file->relativePath);
}
}
return true;
}

29
src/pc/mods/mod.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef MOD_H
#define MOD_H
#include "PR/ultratypes.h"
#include <types.h>
#include "src/pc/platform.h"
struct Mods;
struct ModFile {
char relativePath[SYS_MAX_PATH];
};
struct Mod {
char* name;
char* incompatible;
char* description;
char relativePath[SYS_MAX_PATH];
char basePath[SYS_MAX_PATH];
struct ModFile* files;
u16 fileCount;
bool isDirectory;
bool enabled;
};
void mod_clear(struct Mod* mod);
bool mod_load(struct Mods* mods, char* basePath, char* modName);
#endif

85
src/pc/mods/mods.c Normal file
View File

@ -0,0 +1,85 @@
#include <unistd.h>
#include "mods.h"
#include "mods_utils.h"
#include "pc/debuglog.h"
#define MOD_DIRECTORY "mods"
static struct Mods gLocalMods = { 0 };
void mods_clear(struct Mods* mods) {
for (int i = 0; i < mods->modCount; i ++) {
struct Mod* mod = &mods->entries[i];
mod_clear(mod);
}
if (mods->entries != NULL) {
free(mods->entries);
mods->entries = NULL;
}
mods->modCount = 0;
}
static void mods_load(struct Mods* mods, char* modsBasePath) {
// sanity check
if (modsBasePath == NULL) {
LOG_ERROR("Trying to load from NULL path!");
return;
}
// make the path normal
normalize_path(modsBasePath);
// check for existence
if (!is_directory(modsBasePath)) {
LOG_ERROR("Could not find directory '%s'", modsBasePath);
}
LOG_INFO("Loading mods in '%s':", modsBasePath);
// open directory
struct dirent* dir = NULL;
DIR* d = opendir(modsBasePath);
if (!d) {
LOG_ERROR("Could not open directory '%s'", modsBasePath);
return;
}
// iterate
char path[SYS_MAX_PATH] = { 0 };
while ((dir = readdir(d)) != NULL) {
// sanity check / fill path[]
if (!directory_sanity_check(dir, modsBasePath, path)) { continue; }
// load the mod
if (!mod_load(mods, modsBasePath, dir->d_name)) {
break;
}
}
closedir(d);
}
void mods_init(void) {
// figure out user path
bool hasUserPath = true;
char userModPath[SYS_MAX_PATH] = { 0 };
if (snprintf(userModPath, SYS_MAX_PATH - 1, "%s", fs_get_write_path(MOD_DIRECTORY)) < 0) {
hasUserPath = false;
}
if (!fs_sys_dir_exists(userModPath)) {
hasUserPath = fs_sys_mkdir(userModPath);
}
// clear mods
mods_clear(&gLocalMods);
// load mods
if (hasUserPath) { mods_load(&gLocalMods, userModPath); }
mods_load(&gLocalMods, "./" MOD_DIRECTORY);
}
void mods_shutdown(void) {
mods_clear(&gLocalMods);
}

18
src/pc/mods/mods.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef MODS_H
#define MODS_H
#include "PR/ultratypes.h"
#include <types.h>
#include "src/pc/platform.h"
#include "mod.h"
struct Mods {
struct Mod* entries;
u16 modCount;
};
void mods_clear(struct Mods* mods);
void mods_init(void);
void mods_shutdown(void);
#endif

90
src/pc/mods/mods_utils.c Normal file
View File

@ -0,0 +1,90 @@
#include <unistd.h>
#include <sys/stat.h>
#include "mods_utils.h"
#include "pc/debuglog.h"
bool str_ends_with(char* string, char* suffix) {
if (string == NULL || suffix == NULL) { return false; }
size_t stringLength = strlen(string);
size_t suffixLength = strlen(suffix);
if (suffixLength > stringLength) { return false; }
return !strcmp(&string[stringLength - suffixLength], suffix);
}
char* extract_lua_field(char* fieldName, char* buffer) {
size_t length = strlen(fieldName);
if (strncmp(fieldName, buffer, length) == 0) {
char* s = &buffer[length];
while (*s == ' ' || *s == '\t') { s++; }
return s;
}
return NULL;
}
bool path_exists(char* path) {
struct stat sb = { 0 };
return (stat(path, &sb) == 0);
}
bool is_directory(char* path) {
struct stat sb = { 0 };
return (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode));
}
void normalize_path(char* path) {
// replace slashes
char* p = path;
while (*p) {
#if defined(_WIN32)
if (*p == '/') { *p = '\\'; }
#else
if (*p == '\\') { *p = '/'; }
#endif
p++;
}
}
bool concat_path(char* destination, char* path, char* fname) {
return (snprintf(destination, SYS_MAX_PATH - 1, "%s/%s", path, fname) >= 0);
}
char* path_basename(char* path) {
char* base = path;
while (*path != '\0') {
if (*(path + 1) != '\0') {
if (*path == '\\' || *path == '/') {
base = path + 1;
}
}
path++;
}
return base;
}
bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath) {
// skip anything that contains \ or /
if (strchr(dir->d_name, '/') != NULL) { return false; }
if (strchr(dir->d_name, '\\') != NULL) { return false; }
// skip anything that starts with .
if (dir->d_name == NULL || dir->d_name[0] == '.') { return false; }
// build path
if (!concat_path(outPath, dirPath, dir->d_name)) {
LOG_ERROR("Failed to concat path '%s' + '%s'", dirPath, dir->d_name);
return false;
}
normalize_path(outPath);
// sanity check
if (!path_exists(outPath)) {
LOG_ERROR("Path doesn't exist: '%s'", outPath);
return false;
}
return true;
}

19
src/pc/mods/mods_utils.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef MODS_UTILS_H
#define MODS_UTILS_H
#include "PR/ultratypes.h"
#include <types.h>
#include "src/pc/platform.h"
bool str_ends_with(char* string, char* suffix);
char* extract_lua_field(char* fieldName, char* buffer);
bool path_exists(char* path);
bool is_directory(char* path);
void normalize_path(char* path);
bool concat_path(char* destination, char* path, char* fname);
char* path_basename(char* path);
bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath);
#endif

View File

@ -50,6 +50,7 @@
#include "pc/djui/djui.h"
#include "pc/mod_list.h"
#include "pc/mods/mods.h"
OSMesg D_80339BEC;
OSMesgQueue gSIEventMesgQueue;
@ -171,6 +172,7 @@ void game_deinit(void) {
network_shutdown(true);
smlua_shutdown();
mod_list_shutdown();
mods_shutdown();
inited = false;
}
@ -221,6 +223,7 @@ void main_func(void) {
fs_init(sys_ropaths, gamedir, userpath);
mod_list_init();
mods_init();
configfile_load(configfile_name());
if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; }
if (configPlayerPalette >= PALETTE_MAX) { configPlayerPalette = 0; }