#include "stk.h" #include "stk_log.h" #include #include #include #include typedef void (*stk_module_func)(void); extern void **stk_handles; extern stk_module_func *stk_inits; extern stk_module_func *stk_shutdowns; extern char (*stk_module_ids)[STK_MOD_ID_BUFFER]; extern size_t module_count; uint8_t stk_initialized = 0; static char stk_mod_dir[STK_PATH_MAX_OS] = "mods"; static char stk_tmp_name[STK_MOD_ID_BUFFER] = ".tmp"; static char stk_tmp_dir[STK_PATH_MAX_OS] = "mods/.tmp"; static void *watch_handle = NULL; char (*platform_directory_init_scan(const char *path, size_t *out_count))[STK_PATH_MAX]; void *platform_directory_watch_start(const char *path); void platform_directory_watch_stop(void *handle); stk_module_event_t *platform_directory_watch_check( void *handle, char (**file_list)[STK_PATH_MAX], size_t *out_count, char (*loaded_module_ids)[STK_MOD_ID_BUFFER], const size_t loaded_count); int platform_mkdir(const char *path); int platform_copy_file(const char *from, const char *to); int platform_remove_dir(const char *path); char *extract_module_id(const char *path, char *out_id); int is_mod_loaded(const char *module_id); size_t stk_module_count(void); int stk_module_load(const char *path, int index); int stk_module_load_init(const char *path, int index); void stk_module_unload(size_t index); void stk_module_unload_all(void); int stk_module_init_memory(size_t capacity); int stk_module_realloc_memory(size_t new_capacity); static void build_path(char *dest, size_t dest_size, const char *dir, const char *file) { dest[0] = '\0'; strncat(dest, dir, dest_size - 1); strncat(dest, "/", dest_size - strlen(dest) - 1); strncat(dest, file, dest_size - strlen(dest) - 1); } int stk_init(void) { char (*files)[STK_PATH_MAX] = NULL; size_t file_count, i; char full_path[STK_PATH_MAX_OS]; char tmp_path[STK_PATH_MAX_OS]; platform_mkdir(stk_tmp_dir); files = platform_directory_init_scan(stk_mod_dir, &file_count); if (file_count > 0 && stk_module_init_memory(file_count) != 0) return -1; if (!files) goto scanned; for (i = 0; i < file_count; ++i) { build_path(full_path, sizeof(full_path), stk_mod_dir, files[i]); build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, files[i]); platform_copy_file(full_path, tmp_path); stk_module_load_init(tmp_path, i); } free(files); scanned: watch_handle = platform_directory_watch_start(stk_mod_dir); stk_log(stdout, "[stk] stk v%s initialized! Loaded %lu mod%s from %s/", STK_VERSION_STRING, module_count, module_count != 1 ? "s" : "", stk_mod_dir); stk_initialized = 1; return 0; } void stk_shutdown(void) { if (watch_handle) { platform_directory_watch_stop(watch_handle); watch_handle = NULL; } stk_module_unload_all(); platform_remove_dir(stk_tmp_dir); stk_initialized = 0; stk_log(stdout, "[stk] stk shutdown"); } size_t stk_poll(void) { char (*file_list)[STK_PATH_MAX] = NULL; stk_module_event_t *events = NULL; size_t i, file_count = 0, reload_count = 0, load_count = 0, unload_count = 0, reload_index = 0, load_index = 0, unload_index = 0; int *reloaded_mod_indices = NULL, *reloaded_mod_file_indices = NULL, *unloaded_mod_indices = NULL, *loaded_mod_indices = NULL; size_t remaining_loads, new_capacity, holes_to_fill; size_t write_pos, read_pos; char full_path[STK_PATH_MAX_OS], tmp_path[STK_PATH_MAX_OS]; char mod_id[STK_MOD_ID_BUFFER]; events = platform_directory_watch_check(watch_handle, &file_list, &file_count, stk_module_ids, module_count); if (!events) goto finish_poll; for (i = 0; i < file_count; ++i) { switch (events[i]) { case STK_MOD_LOAD: ++load_count; break; case STK_MOD_RELOAD: ++reload_count; break; case STK_MOD_UNLOAD: ++unload_count; break; } } reloaded_mod_indices = malloc(reload_count * sizeof(int)); reloaded_mod_file_indices = malloc(reload_count * sizeof(int)); unloaded_mod_indices = malloc(unload_count * sizeof(int)); loaded_mod_indices = malloc(load_count * sizeof(int)); for (i = 0; i < file_count; ++i) { extract_module_id(file_list[i], mod_id); switch (events[i]) { case STK_MOD_LOAD: loaded_mod_indices[load_index++] = i; break; case STK_MOD_UNLOAD: reloaded_mod_file_indices[reload_index] = i; reloaded_mod_indices[reload_index++] = is_mod_loaded(mod_id); break; case STK_MOD_RELOAD: unloaded_mod_indices[unload_index++] = is_mod_loaded(mod_id); break; } } if (load_count > unload_count) goto handle_grow; goto begin_operations; handle_grow: remaining_loads = load_count - unload_count; new_capacity = module_count + remaining_loads; if (stk_module_realloc_memory(new_capacity) != 0) goto free_poll; begin_operations: for (i = 0; i < unload_count; ++i) stk_module_unload(unloaded_mod_indices[i]); for (i = 0; i < reload_count; ++i) stk_module_unload(reloaded_mod_indices[i]); for (i = 0; i < reload_count; ++i) { int file_idx = reloaded_mod_file_indices[i]; int mod_idx = reloaded_mod_indices[i]; build_path(full_path, sizeof(full_path), stk_mod_dir, file_list[file_idx]); build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, file_list[file_idx]); platform_copy_file(full_path, tmp_path); stk_module_load(tmp_path, mod_idx); } holes_to_fill = (load_count < unload_count) ? load_count : unload_count; for (i = 0; i < holes_to_fill; ++i) { int target_idx = unloaded_mod_indices[i]; int file_idx = loaded_mod_indices[i]; build_path(full_path, sizeof(full_path), stk_mod_dir, file_list[file_idx]); build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, file_list[file_idx]); platform_copy_file(full_path, tmp_path); stk_module_load(tmp_path, target_idx); } if (load_count > unload_count) goto append_modules; if (unload_count > load_count) goto trim_arrays; goto free_poll; append_modules: for (; i < load_count; ++i) { int file_idx = loaded_mod_indices[i]; build_path(full_path, sizeof(full_path), stk_mod_dir, file_list[file_idx]); build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, file_list[file_idx]); platform_copy_file(full_path, tmp_path); stk_module_load(tmp_path, module_count); ++module_count; } goto free_poll; trim_arrays: write_pos = unloaded_mod_indices[holes_to_fill]; for (i = holes_to_fill + 1; i < unload_count; ++i) { if (unloaded_mod_indices[i] < write_pos) write_pos = unloaded_mod_indices[i]; } for (read_pos = write_pos + 1; read_pos < module_count; ++read_pos) { if (stk_handles[read_pos] != NULL) { stk_handles[write_pos] = stk_handles[read_pos]; stk_inits[write_pos] = stk_inits[read_pos]; stk_shutdowns[write_pos] = stk_shutdowns[read_pos]; memcpy(stk_module_ids[write_pos], stk_module_ids[read_pos], STK_MOD_ID_BUFFER); ++write_pos; } } module_count = write_pos; stk_module_realloc_memory(module_count); free_poll: free(reloaded_mod_indices); free(reloaded_mod_file_indices); free(unloaded_mod_indices); free(loaded_mod_indices); free(events); free(file_list); finish_poll: return file_count; } void stk_set_mod_dir(const char *path) { if (!path || stk_initialized) return; strncpy(stk_mod_dir, path, STK_PATH_MAX_OS - 1); stk_mod_dir[STK_PATH_MAX_OS - 1] = '\0'; strncpy(stk_tmp_dir, stk_mod_dir, STK_PATH_MAX_OS - 1); stk_tmp_dir[STK_PATH_MAX_OS - 1] = '\0'; strncat(stk_tmp_dir, "/", STK_PATH_MAX_OS - strlen(stk_tmp_dir) - 1); strncat(stk_tmp_dir, stk_tmp_name, STK_PATH_MAX_OS - strlen(stk_tmp_dir) - 1); } void stk_set_tmp_dir_name(const char *name) { if (!name || stk_initialized) return; strncpy(stk_tmp_name, name, STK_MOD_ID_BUFFER - 1); stk_tmp_name[STK_MOD_ID_BUFFER - 1] = '\0'; strncpy(stk_tmp_dir, stk_mod_dir, STK_PATH_MAX_OS - 1); stk_tmp_dir[STK_PATH_MAX_OS - 1] = '\0'; strncat(stk_tmp_dir, "/", STK_PATH_MAX_OS - strlen(stk_tmp_dir) - 1); strncat(stk_tmp_dir, stk_tmp_name, STK_PATH_MAX_OS - strlen(stk_tmp_dir) - 1); }