mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 05:25:14 +00:00
Started rewriting the mod table
This commit is contained in:
parent
edf06bede0
commit
0a3d0b2033
8 changed files with 535 additions and 1 deletions
2
Makefile
2
Makefile
|
@ -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
290
src/pc/mods/mod.c
Normal 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
29
src/pc/mods/mod.h
Normal 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
85
src/pc/mods/mods.c
Normal 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
18
src/pc/mods/mods.h
Normal 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
90
src/pc/mods/mods_utils.c
Normal 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
19
src/pc/mods/mods_utils.h
Normal 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
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in a new issue