25static int s_screen_entry_count = 0;
27static void str_to_lower(
char *str)
29 for (
int i = 0; str[i] !=
'\0'; i++)
31 str[i] = (char)tolower((
unsigned char)str[i]);
35static bool has_msb_extension(
const char *name)
37 size_t len = strlen(name);
43 const char *ext = name + (len - 4);
44 return (tolower((
unsigned char)ext[0]) ==
'.' &&
45 tolower((
unsigned char)ext[1]) ==
'm' &&
46 tolower((
unsigned char)ext[2]) ==
's' &&
47 tolower((
unsigned char)ext[3]) ==
'b');
51static bool screen_mesh_exists(
const char *msb_file)
53 char mesh_path[MAX_PATH];
54 snprintf(mesh_path,
sizeof(mesh_path),
"mesh\\%s", msb_file);
57 uiAPI.SFStringConstructor_char(&path, mesh_path);
59 uiAPI.SFStringDestructor(&path);
73static bool accept_screen_entry(
const ScreenEntry *entry)
81 if (!has_msb_extension(entry->
msb_file))
84 "screens.json: \"%s\" maps to \"%s\", which is not a .msb mesh",
89 for (
int i = 0; i < s_screen_entry_count; i++)
91 if (strcmp(s_screen_entries[i].map_name, entry->
map_name) == 0)
94 "screens.json: map \"%s\" is listed twice, keeping \"%s\"",
95 entry->
map_name, s_screen_entries[i].msb_file);
100 if (!screen_mesh_exists(entry->
msb_file))
103 "screens.json: map \"%s\" needs \"%s\", which is missing from mesh\\",
108 s_screen_entries[s_screen_entry_count] = *entry;
109 s_screen_entry_count++;
115 char version_tag_buffer[128];
116 snprintf(version_tag_buffer,
sizeof(version_tag_buffer),
"%d.%d.%d-%s",
121 "Muddykat, UnSchtalch",
122 "Swaps in custom loading screens listed by the sfsf\\screens.json file.");
126 s_screen_entry_count = 0;
128 char currentDir[MAX_PATH];
129 GetCurrentDirectory(MAX_PATH, currentDir);
132 snprintf(path,
sizeof(path),
"%s\\sfsf\\screens.json", currentDir);
135 int parsed_count = 0;
140 log_info(
"| - No screens.json found at %s (custom loading screens disabled)", path);
145 for (
int i = 0; i < parsed_count; i++)
147 if (!accept_screen_entry(&parsed[i]))
153 log_info(
"| - %d loading screen(s) registered, %d rejected", s_screen_entry_count, rejected);
158 if (map_name == NULL || s_screen_entry_count == 0)
164 size_t len = strlen(map_name);
165 if (len >=
sizeof(lowered))
167 len =
sizeof(lowered) - 1;
169 memcpy(lowered, map_name, len);
171 str_to_lower(lowered);
174 const char *last_separator = strrchr(lowered,
'\\');
175 char *filename = (
char *)(last_separator ? last_separator + 1 : lowered);
177 char *dot = strrchr(filename,
'.');
186 const char *underscore = strchr(filename,
'_');
187 const char *extracted = (underscore != NULL) ? underscore + 1 : filename;
189 if (extracted[0] ==
'\0')
194 for (
int i = 0; i < s_screen_entry_count; i++)
196 if (strcmp(s_screen_entries[i].map_name, extracted) == 0)
198 return s_screen_entries[i].msb_file;
checkFileExists_ptr checkFileExists
void clear_mod_errors(SFMod *mod)
Empties a mod's error buffer.
void register_mod_for_listing(SFMod *mod, ModType type)
Adds a mod to the list rendered by the in-game mod list screen.
void report_mod_error(SFMod *mod, const char *format,...)
Logs an error, appends it to the mod's error buffer and counts it.
#define SCREEN_MAP_NAME_LEN
bool parse_screens_json_file(const char *path, ScreenEntry *out_entries, int max_entries, int *out_count)
Reads a screens.json: a single object of map_name : msb_file pairs.
const char * find_screen_for_map(const char *map_name)
Finds the loading screen mesh for a map, or NULL for the default.
#define MAX_SCREEN_ENTRIES
void initialize_screens_module()
Loads and validates sfsf\screens.json.
void log_debug(DebugLevel level, const char *format,...)
void log_info(const char *format,...)
SFMod * createModInfo(const char *mod_id, const char *mod_version, const char *mod_author, const char *mod_description)
#define SPELLFRAMEWORK_TAG
#define SPELLFRAMEWORK_VERSION_PATCH
#define SPELLFRAMEWORK_VERSION_MAJOR
#define SPELLFRAMEWORK_VERSION_MINOR
One "map name -> loading screen mesh" pair.