diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py index 73b9ed176..4ab580d9e 100644 --- a/autogen/convert_constants.py +++ b/autogen/convert_constants.py @@ -234,7 +234,7 @@ def build_files(processed_files): def build_to_c(built_files): txt = '' - with open(get_path(in_filename), 'r') as f: + with open(get_path(in_filename), 'r', newline='\n') as f: txt = f.read() txt += '\n' + built_files @@ -327,7 +327,7 @@ def def_constant(processed_constant): def build_to_def(processed_files): s = '-- AUTOGENERATED FOR CODE EDITORS --\n\n' - with open(get_path(in_filename), 'r') as f: + with open(get_path(in_filename), 'r', newline='\n') as f: s += f.read() s += '\n' @@ -347,15 +347,15 @@ def main(): built_c = build_to_c(built_files) - with open(get_path(out_filename), 'w') as out: + with open(get_path(out_filename), 'w', newline='\n') as out: out.write(built_c) doc = doc_files(processed_files) - with open(get_path(out_filename_docs), 'w') as out: + with open(get_path(out_filename_docs), 'w', newline='\n') as out: out.write(doc) defs = build_to_def(processed_files) - with open(get_path(out_filename_defs), 'w') as out: + with open(get_path(out_filename_defs), 'w', newline='\n') as out: out.write(defs) global totalConstants diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index b0a14bdbd..6f52f5353 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -52,6 +52,7 @@ in_files = [ "src/game/object_list_processor.h", "src/game/behavior_actions.h", "src/game/mario_misc.h", + "src/pc/mods/mod_storage.h" "src/pc/utils/misc.h", "src/game/level_update.h" ] @@ -820,7 +821,7 @@ def doc_files(processed_files): buffer = buffer.replace('$[FUNCTION_NAV_HERE', function_nav) - with open(get_path(out_filename_docs % page_name), 'w') as out: + with open(get_path(out_filename_docs % page_name), 'w', newline='\n') as out: out.write(buffer) ############################################################################ @@ -869,7 +870,7 @@ def def_files(processed_files): for def_pointer in def_pointers: s += '--- @class %s\n' % def_pointer - with open(get_path(out_filename_defs), 'w') as out: + with open(get_path(out_filename_defs), 'w', newline='\n') as out: out.write(s) ############################################################################ @@ -888,7 +889,7 @@ def main(): .replace("$[BINDS]", built_binds) \ .replace("$[INCLUDES]", built_includes) - with open(filename, 'w') as out: + with open(filename, 'w', newline='\n') as out: out.write(gen) print('REJECTS:\n%s' % rejects) diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py index bb15a278d..397c150ca 100644 --- a/autogen/convert_structs.py +++ b/autogen/convert_structs.py @@ -424,7 +424,7 @@ def doc_structs(structs): continue s += doc_struct(struct) + '\n' - with open(get_path(out_filename_docs), 'w') as out: + with open(get_path(out_filename_docs), 'w', newline='\n') as out: out.write(s) ############################################################################ @@ -472,7 +472,7 @@ def def_structs(structs): for def_pointer in def_pointers: s += '--- @class %s\n' % def_pointer - with open(get_path(out_filename_defs), 'w') as out: + with open(get_path(out_filename_defs), 'w', newline='\n') as out: out.write(s) ############################################################################ @@ -494,11 +494,11 @@ def build_files(): built_include = build_includes() out_c_filename = get_path(out_filename_c) - with open(out_c_filename, 'w') as out: + with open(out_c_filename, 'w', newline='\n') as out: out.write(c_template.replace("$[BODY]", built_body).replace('$[INCLUDES]', built_include)) out_h_filename = get_path(out_filename_h) - with open(out_h_filename, 'w') as out: + with open(out_h_filename, 'w', newline='\n') as out: out.write(h_template.replace("$[BODY]", built_enum)) doc_structs(parsed) diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index d3c5c6fd6..c671421e8 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -5211,6 +5211,19 @@ function vec3f_set_dist_and_angle(from, to, dist, pitch, yaw) -- ... end +--- @param key string +--- @return string +function mod_storage_load(key) + -- ... +end + +--- @param key string +--- @param value string +--- @return boolean +function mod_storage_save(key, value) + -- ... +end + --- @return nil function update_all_mario_stars() -- ... @@ -5276,6 +5289,12 @@ function network_player_set_description(np, description, r, g, b, a) -- ... end +--- @param localIndex integer +--- @return string +function network_discord_id_from_local_index(localIndex) + -- ... +end + --- @param localIndex integer --- @return string function network_get_player_text_color_string(localIndex) diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index ff2bc0339..ae9b6faeb 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -7095,25 +7095,10 @@
---- -# functions from misc.h
-## [update_all_mario_stars](#update_all_mario_stars) - -### Lua Example -`update_all_mario_stars()` - -### Parameters -- None - -### Returns -- None - -### C Prototype -`void update_all_mario_stars(void);` [:arrow_up_small:](#) @@ -7301,6 +7286,26 @@
+## [network_discord_id_from_local_index](#network_discord_id_from_local_index) + +### Lua Example +`local stringValue = network_discord_id_from_local_index(localIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| localIndex | `integer` | + +### Returns +- `string` + +### C Prototype +`char* network_discord_id_from_local_index(u8 localIndex);` + +[:arrow_up_small:](#) + +
+ ## [network_get_player_text_color_string](#network_get_player_text_color_string) ### Lua Example diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 2bf3e70a1..3a8ced33f 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1012,8 +1012,6 @@
-- misc.h - - [update_all_mario_stars](functions-3.md#update_all_mario_stars)
@@ -1030,6 +1028,7 @@
- network_utils.h + - [network_discord_id_from_local_index](functions-3.md#network_discord_id_from_local_index) - [network_get_player_text_color_string](functions-3.md#network_get_player_text_color_string) - [network_global_index_from_local](functions-3.md#network_global_index_from_local) - [network_is_moderator](functions-3.md#network_is_moderator) diff --git a/src/game/save_file.c b/src/game/save_file.c index 0480eedff..a4435b96f 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -11,7 +11,6 @@ #include "course_table.h" #include "rumble_init.h" #include "macros.h" -#include "pc/ini.h" #include "pc/network/network.h" #include "pc/lua/utils/smlua_level_utils.h" #include "pc/utils/misc.h" diff --git a/src/pc/configini.c b/src/pc/configini.c new file mode 100644 index 000000000..7ef01907b --- /dev/null +++ b/src/pc/configini.c @@ -0,0 +1,1223 @@ +/* + libconfigini - an ini formatted configuration parser library + Copyright (C) 2013-present Taner YILMAZ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "configini.h" +#include "queue.h" + + +#define COMMENT_CHARS "#" /* default comment chars */ +#define KEYVAL_SEP '=' /* default key-val seperator character */ +#define STR_TRUE "1" /* default string valu of true */ +#define STR_FALSE "0" /* default string valu of false */ + +#define CONFIG_INIT_MAGIC 0x12F0ED1 + +#define UNUSED __attribute__((unused)) + +/** + * \brief Configuration key-value + */ +typedef struct ConfigKeyValue +{ + char *key; + char *value; + TAILQ_ENTRY(ConfigKeyValue) next; +} ConfigKeyValue; + +/** + * \brief Configuration section + */ +typedef struct ConfigSection +{ + char *name; + int numofkv; + TAILQ_HEAD(, ConfigKeyValue) kv_list; + TAILQ_ENTRY(ConfigSection) next; +} ConfigSection; + +/** + * \brief Configuration handle + */ +struct Config +{ + char *comment_chars; + char keyval_sep; + char *true_str; + char *false_str; + int initnum; + int numofsect; + TAILQ_HEAD(, ConfigSection) sect_list; +}; + + + + +static int StrSafeCopy(char *dst, const char *src, int size) +{ + char *d = dst; + const char *s = src; + int n = size; + + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + if (n == 0) { + if (size != 0) + *d = '\0'; + while (*s++) + ; + } + + return (s - src - 1); +} + +static bool StrIsTypeOfTrue(const char *s) +{ + if ( !strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "1") ) + return true; + return false; +} + +static bool StrIsTypeOfFalse(const char *s) +{ + if ( !strcasecmp(s, "false") || !strcasecmp(s, "no") || !strcasecmp(s, "0") ) + return true; + return false; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +const char *ConfigRetToString(ConfigRet ret) +{ + switch(ret) { + case CONFIG_OK: return "OK"; + case CONFIG_ERR_FILE: return "File IO error"; + case CONFIG_ERR_NO_SECTION: return "No section"; + case CONFIG_ERR_NO_KEY: return "No key"; + case CONFIG_ERR_MEMALLOC: return "Memory allocation failed"; + case CONFIG_ERR_INVALID_PARAM: return "Invalid parameter"; + case CONFIG_ERR_INVALID_VALUE: return "Invalid value"; + case CONFIG_ERR_PARSING: return "Parse error"; + default: return NULL; + } +} + +/** + * \brief ConfigSetCommentCharset() sets comment characters + * + * \param cfg config handle + * \param comment_ch charaters to consider as comments + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigSetCommentCharset(Config *cfg, const char *comment_ch) +{ + char *p; + + if (!cfg || !comment_ch) + return CONFIG_ERR_INVALID_PARAM; + + if ((p = strdup(comment_ch)) == NULL) + return CONFIG_ERR_MEMALLOC; + + if (cfg->comment_chars) + free(cfg->comment_chars); + cfg->comment_chars = p; + + return CONFIG_OK; +} + +/** + * \brief ConfigSetCommentCharset() sets comment characters + * + * \param cfg config handle + * \param ch charater to consider as key-value seperator + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigSetKeyValSepChar(Config *cfg, char ch) +{ + if (!cfg) + return CONFIG_ERR_INVALID_PARAM; + + cfg->keyval_sep = ch; + + return CONFIG_OK; +} + +/** + * \brief ConfigSetCommentCharset() sets comment characters + * + * \param cfg config handle + * \param true_str string value of boolean true (must be one of these: "true", "yes", "1") + * \param false_str string value of boolean false (must be one of these: "false", "no", "0") + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigSetBoolString(Config *cfg, const char *true_str, const char *false_str) +{ + char *t, *f; + + if ( !cfg || + !true_str || !*true_str || !StrIsTypeOfTrue(true_str) || + !false_str || !*false_str || !StrIsTypeOfFalse(false_str) ) + return CONFIG_ERR_INVALID_PARAM; + + if ((t = strdup(true_str)) == NULL) + return CONFIG_ERR_MEMALLOC; + + if ((f = strdup(false_str)) == NULL) { + free(t); + return CONFIG_ERR_MEMALLOC; + } + + if (cfg->true_str) + free(cfg->true_str); + if (cfg->false_str) + free(cfg->false_str); + + cfg->true_str = t; + cfg->false_str = f; + + return CONFIG_OK; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** + * \brief ConfigGetSection() gets the requested section + * + * \param cfg config handle to search in + * \param section section name to search for + * \param sect pointer to ConfigSection* searched for to save + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +static ConfigRet ConfigGetSection(const Config *cfg, const char *section, ConfigSection **sect) +{ + if (!cfg || !sect) + return CONFIG_ERR_INVALID_PARAM; + + TAILQ_FOREACH(*sect, &cfg->sect_list, next) { + if ( (section && (*sect)->name && !strcmp((*sect)->name, section)) || + (!section && !(*sect)->name) ) { + return CONFIG_OK; + } + } + + return CONFIG_ERR_NO_SECTION; +} + +/** + * \brief Checks whether section exists + * + * \param cfg config handle to search in + * \param section section name to search for + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +bool ConfigHasSection(const Config *cfg, const char *section) +{ + ConfigSection *sect = NULL; + + return ( (ConfigGetSection(cfg, section, §) == CONFIG_OK) ? true : false ); +} + +/** + * \brief ConfigGetKeyValue() gets the ConfigKeyValue * + * + * \param cfg config handle + * \param sect section to search in + * \param key key to search for + * \param kv pointer to ConfigKeyValue* searched for to save + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +static ConfigRet ConfigGetKeyValue(UNUSED const Config *cfg, ConfigSection *sect, const char *key, + ConfigKeyValue **kv) +{ + if (!sect || !key || !kv) + return CONFIG_ERR_INVALID_PARAM; + + TAILQ_FOREACH(*kv, §->kv_list, next) { + if (!strcmp((*kv)->key, key)) + return CONFIG_OK; + } + + return CONFIG_ERR_NO_KEY; +} + +/** + * \brief ConfigGetSectionCount() gets number of sections + * + * \param cfg config handle to search in + * + * \return Returns number of sections on success, -1 on failure. + */ +int ConfigGetSectionCount(const Config *cfg) +{ + if (!cfg) + return -1; + + return (TAILQ_FIRST(&cfg->sect_list)->numofkv > 0 ? cfg->numofsect : cfg->numofsect - 1); +} + +/** + * \brief ConfigGetKeyCount() gets number of keys + * + * \param cfg config handle to search in + * \param section section name to search for + * + * \return Returns number of keys on success, -1 on failure. + */ +int ConfigGetKeyCount(const Config *cfg, const char *section) +{ + ConfigSection *sect = NULL; + + if (!cfg) + return -1; + + if (ConfigGetSection(cfg, section, §) != CONFIG_OK) + return -1; + + return sect->numofkv; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** + * \brief ConfigReadString() reads a string value from the cfg. + * If any error occurs default value is copied to 'value' buffer and + * returns reason. If key is optional and does not exists in config, + * the 'value' is default value and return is CONFIG_ERR_NO_KEY + * + * \param cfg config handle + * \param section section to search in + * \param key key to search for + * \param value value to save in + * \param size value buffer size + * \param dfl_value default value to copy back if any error occurs + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadString(const Config *cfg, const char *section, const char *key, + char *value, int size, const char *dfl_value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + + if (!cfg || !key || !value || (size < 1)) + return CONFIG_ERR_INVALID_PARAM; + + *value = '\0'; + + if ( ((ret = ConfigGetSection(cfg, section, §)) != CONFIG_OK) || + ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) != CONFIG_OK) ) { + if (dfl_value) + StrSafeCopy(value, dfl_value, size); + return ret; + } + + StrSafeCopy(value, kv->value, size); + + return CONFIG_OK; +} + +/** + * \brief ConfigReadInt() reads an integer value from the cfg. + * If any error occurs default value is copied to 'value' buffer and + * returns reason. If key is optional and does not exists in config, + * the 'value' is default value and return is CONFIG_ERR_NO_KEY + * + * \param cfg config handle + * \param section section to search in + * \param key key to search for + * \param value value to save in + * \param dfl_value default value to copy back if any error occurs + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadInt(const Config *cfg, const char *section, const char *key, + int *value, int dfl_value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + char *p = NULL; + + if (!cfg || !key || !value) + return CONFIG_ERR_INVALID_PARAM; + + *value = dfl_value; + + if ( ((ret = ConfigGetSection(cfg, section, §)) != CONFIG_OK) || + ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) != CONFIG_OK) ) { + return ret; + } + + *value = (int) strtol(kv->value, &p, 10); + if (*p || (errno == ERANGE)) + return CONFIG_ERR_INVALID_VALUE; + + return CONFIG_OK; +} + +/** + * \brief ConfigReadUnsignedInt() reads an unsigned integer value from the cfg. + * If any error occurs default value is copied to 'value' buffer and + * returns reason. If key is optional and does not exists in config, + * the 'value' is default value and return is CONFIG_ERR_NO_KEY + * + * \param cfg config handle + * \param section section to search in + * \param key key to search for + * \param value value to save in + * \param dfl_value default value to copy back if any error occurs + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadUnsignedInt(const Config *cfg, const char *section, const char *key, + unsigned int *value, unsigned int dfl_value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + char *p = NULL; + + if (!cfg || !key || !value) + return CONFIG_ERR_INVALID_PARAM; + + *value = dfl_value; + + if ( ((ret = ConfigGetSection(cfg, section, §)) != CONFIG_OK) || + ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) != CONFIG_OK) ) { + return ret; + } + + *value = (unsigned int) strtoul(kv->value, &p, 10); + if (*p || (errno == ERANGE)) + return CONFIG_ERR_INVALID_VALUE; + + return CONFIG_OK; +} + +/** + * \brief ConfigReadFloat() reads a float value from the cfg. + * If any error occurs default value is copied to 'value' buffer and + * returns reason. If key is optional and does not exists in config, + * the 'value' is default value and return is CONFIG_ERR_NO_KEY + * + * \param cfg config handle + * \param section section to search in + * \param key key to search for + * \param value value to save in + * \param dfl_value default value to copy back if any error occurs + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadFloat(const Config *cfg, const char *section, const char *key, + float *value, float dfl_value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + char *p = NULL; + + if (!cfg || !key || !value) + return CONFIG_ERR_INVALID_PARAM; + + *value = dfl_value; + + if ( ((ret = ConfigGetSection(cfg, section, §)) != CONFIG_OK) || + ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) != CONFIG_OK) ) { + return ret; + } + + *value = strtof(kv->value, &p); + if (*p || (errno == ERANGE)) + return CONFIG_ERR_INVALID_VALUE; + + return CONFIG_OK; +} + +/** + * \brief ConfigReadDouble() reads a double value from the cfg. + * If any error occurs default value is copied to 'value' buffer and + * returns reason. If key is optional and does not exists in config, + * the 'value' is default value and return is CONFIG_ERR_NO_KEY + * + * \param cfg config handle + * \param section section to search in + * \param key key to search for + * \param value value to save in + * \param dfl_value default value to copy back if any error occurs + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadDouble(const Config *cfg, const char *section, const char *key, + double *value, double dfl_value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + char *p = NULL; + + if (!cfg || !key || !value) + return CONFIG_ERR_INVALID_PARAM; + + *value = dfl_value; + + if ( ((ret = ConfigGetSection(cfg, section, §)) != CONFIG_OK) || + ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) != CONFIG_OK) ) { + return ret; + } + + *value = strtod(kv->value, &p); + if (*p || (errno == ERANGE)) + return CONFIG_ERR_INVALID_VALUE; + + return CONFIG_OK; +} + +/** + * \brief ConfigReadBool() reads a boolean value from the cfg. + * If any error occurs default value is copied to 'value' buffer and + * returns reason. If key is optional and does not exists in config, + * the 'value' is default value and return is CONFIG_ERR_NO_KEY + * + * \param cfg config handle + * \param section section to search in + * \param key key to search for + * \param value value to save in + * \param dfl_value default value to copy back if any error occurs + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadBool(const Config *cfg, const char *section, const char *key, + bool *value, bool dfl_value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + + if (!cfg || !key || !value) + return CONFIG_ERR_INVALID_PARAM; + + *value = dfl_value; + + if ( ((ret = ConfigGetSection(cfg, section, §)) != CONFIG_OK) || + ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) != CONFIG_OK) ) { + return ret; + } + + if (StrIsTypeOfTrue(kv->value)) + *value = true; + else if (StrIsTypeOfFalse(kv->value)) + *value = false; + else + return CONFIG_ERR_INVALID_VALUE; + + return CONFIG_OK; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** + * \brief ConfigAddSection() creates a section in the cfg + * + * \param cfg config handle + * \param section section to add + * \param sect pointer to added ConfigSection* or NULL if not needed + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +static ConfigRet ConfigAddSection(Config *cfg, const char *section, ConfigSection **sect) +{ + ConfigSection *_sect = NULL; + ConfigRet ret = CONFIG_OK; + + if (!cfg) + return CONFIG_ERR_INVALID_PARAM; + + if (!sect) + sect = &_sect; + + if ((ret = ConfigGetSection(cfg, section, sect)) != CONFIG_ERR_NO_SECTION) + return ret; + + *sect = calloc(1, sizeof(ConfigSection)); + if (*sect == NULL) + return CONFIG_ERR_MEMALLOC; + + if (section) { + if (((*sect)->name = strdup(section)) == NULL) { + free(*sect); + return CONFIG_ERR_MEMALLOC; + } + } + + TAILQ_INIT(&(*sect)->kv_list); + TAILQ_INSERT_TAIL(&cfg->sect_list, *sect, next); + ++(cfg->numofsect); + + return CONFIG_OK; +} + +/** + * \brief ConfigAddString() adds the key with string value to the cfg + * + * \param cfg config handle + * \param section section to add in + * \param key key to save as + * \param value value to save as + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigAddString(Config *cfg, const char *section, const char *key, const char *value) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + const char *p = NULL; + const char *q = NULL; + + if (!cfg || !key || !value) + return CONFIG_ERR_INVALID_PARAM; + + if ((ret = ConfigAddSection(cfg, section, §)) != CONFIG_OK) + return ret; + + switch (ret = ConfigGetKeyValue(cfg, sect, key, &kv)) { + case CONFIG_OK: + if (kv->value) { + free(kv->value); + kv->value = NULL; + } + break; + + case CONFIG_ERR_NO_KEY: + if ((kv = calloc(1, sizeof(ConfigKeyValue))) == NULL) + return CONFIG_ERR_MEMALLOC; + if ((kv->key = strdup(key)) == NULL) { + free(kv); + return CONFIG_ERR_MEMALLOC; + } + TAILQ_INSERT_TAIL(§->kv_list, kv, next); + ++(sect->numofkv); + break; + + default: + return ret; + } + + for (p = value; *p && isspace(*p); ++p) + ; + for (q = p; *q && (*q != '\r') && (*q != '\n') && !strchr(cfg->comment_chars, *q); ++q) + ; + while (*q && (q > p) && isspace(*(q - 1))) + --q; + + kv->value = (char *) malloc(q - p + 1); + if (kv->value == NULL) { + TAILQ_REMOVE(§->kv_list, kv, next); + --(sect->numofkv); + free(kv->key); + free(kv); + return CONFIG_ERR_MEMALLOC; + } + memcpy(kv->value, p, q - p); + kv->value[q - p] = '\0'; + + return CONFIG_OK; +} + +/** + * \brief ConfigAddInt() adds the key with integer value to the cfg + * + * \param cfg config handle + * \param section section to add in + * \param key key to save as + * \param value value to save as + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigAddInt(Config *cfg, const char *section, const char *key, int value) +{ + char buf[64]; + + snprintf(buf, sizeof(buf), "%d", value); + + return ConfigAddString(cfg, section, key, buf); +} + +/** + * \brief ConfigAddUnsignedInt() adds the key with unsigned integer value to the cfg + * + * \param cfg config handle + * \param section section to add in + * \param key key to save as + * \param value value to save as + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigAddUnsignedInt(Config *cfg, const char *section, const char *key, unsigned int value) +{ + char buf[64]; + + snprintf(buf, sizeof(buf), "%u", value); + + return ConfigAddString(cfg, section, key, buf); +} + +/** + * \brief ConfigAddFloat() adds the key with float value to the cfg + * + * \param cfg config handle + * \param section section to add in + * \param key key to save as + * \param value value to save as + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigAddFloat(Config * cfg, const char *section, const char *key, float value) +{ + char buf[64]; + + snprintf(buf, sizeof(buf), "%f", value); + + return ConfigAddString(cfg, section, key, buf); +} + +/** + * \brief ConfigAddDouble() adds the key with double value to the cfg + * + * \param cfg config handle + * \param section section to add in + * \param key key to save as + * \param value value to save as + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigAddDouble(Config *cfg, const char *section, const char *key, double value) +{ + char buf[64]; + + snprintf(buf, sizeof(buf), "%f", value); + + return ConfigAddString(cfg, section, key, buf); +} + +/** + * \brief ConfigAddBool() adds the key with blooean value to the cfg + * + * \param cfg config handle + * \param section section to add in + * \param key key to save as + * \param value value to save as + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigAddBool(Config * cfg, const char *section, const char *key, bool value) +{ + return ConfigAddString(cfg, section, key, value ? cfg->true_str : cfg->false_str); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +static void _ConfigRemoveKey(ConfigSection *sect, ConfigKeyValue *kv) +{ + TAILQ_REMOVE(§->kv_list, kv, next); + --(sect->numofkv); + + if (kv->key) + free(kv->key); + if (kv->value) + free(kv->value); + free(kv); +} + +/** + * \brief ConfigRemoveKey() removes the key which exists under section from the cfg + * + * \param cfg config handle + * \param section section to seach in + * \param key key to remove + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigRemoveKey(Config *cfg, const char *section, const char *key) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + ConfigRet ret = CONFIG_OK; + + if (!cfg || !key) + return CONFIG_ERR_INVALID_PARAM; + + if ((ret = ConfigGetSection(cfg, section, §)) == CONFIG_OK) { + if ((ret = ConfigGetKeyValue(cfg, sect, key, &kv)) == CONFIG_OK) + _ConfigRemoveKey(sect, kv); + } + + return ret; +} + +static void _ConfigRemoveSection(Config *cfg, ConfigSection *sect) +{ + ConfigKeyValue *kv, *t_kv; + + if (!cfg || !sect) + return; + + TAILQ_REMOVE(&cfg->sect_list, sect, next); + --(cfg->numofsect); + + TAILQ_FOREACH_SAFE(kv, §->kv_list, next, t_kv) { + _ConfigRemoveKey(sect, kv); + } + + if (sect->name) + free(sect->name); + free(sect); +} + +/** + * \brief ConfigRemoveSection() removes section from the cfgfile + * + * \param cfg config handle + * \param section section to remove + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigRemoveSection(Config *cfg, const char *section) +{ + ConfigSection *sect = NULL; + ConfigRet ret = CONFIG_OK; + + if (!cfg) + return CONFIG_ERR_INVALID_PARAM; + + if ((ret = ConfigGetSection(cfg, section, §)) == CONFIG_OK) + _ConfigRemoveSection(cfg, sect); + + return ret; +} + +/** + * \brief ConfigNew() creates a cfg handle with + * default section which has no section name + * + * \return Config* handle on success, NULL on failure + */ +Config *ConfigNew() +{ + Config *cfg = NULL; + + cfg = calloc(1, sizeof(Config)); + if (cfg == NULL) + return NULL; + + TAILQ_INIT(&cfg->sect_list); + + /* add default section */ + if (ConfigAddSection(cfg, CONFIG_SECTION_FLAT, NULL) != CONFIG_OK) { + free(cfg); + return NULL; + } + + cfg->comment_chars = strdup(COMMENT_CHARS); + cfg->keyval_sep = KEYVAL_SEP; + cfg->true_str = strdup(STR_TRUE); + cfg->false_str = strdup(STR_FALSE); + cfg->initnum = CONFIG_INIT_MAGIC; + + return cfg; +} + +/** + * \brief ConfigFree() frees the memory for the cfg handle + * + * \param cfg config handle + */ +void ConfigFree(Config *cfg) +{ + ConfigSection *sect, *t_sect; + + if (cfg == NULL) + return; + + TAILQ_FOREACH_SAFE(sect, &cfg->sect_list, next, t_sect) { + _ConfigRemoveSection(cfg, sect); + } + + if (cfg->comment_chars) free(cfg->comment_chars); + if (cfg->true_str) free(cfg->true_str); + if (cfg->false_str) free(cfg->false_str); + + free(cfg); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** + * \brief Gets section name on the buffer p + * + * \param cfg config handle + * \param p read buffer + * \param section pointer address to section + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +static ConfigRet GetSectName(Config *cfg, char *p, char **section) +{ + char *q, *r; + + if (!cfg || !p || !*p || !section) + return CONFIG_ERR_INVALID_PARAM; + + *section = NULL; + + /* get section name */ + while (*p && isspace(*p)) + ++p; + + if (*p != '[') + return CONFIG_ERR_PARSING; + + ++p; + while (*p && isspace(*p)) + ++p; + + for (q = p; + *q && (*q != '\r') && (*q != '\n') && (*q != ']') && !strchr(cfg->comment_chars, *q); + ++q) + ; + + if (*q != ']') + return CONFIG_ERR_PARSING; + + r = q + 1; + + while (*q && (q > p) && isspace(*(q - 1))) + --q; + + if (q == p) /* section has no name */ + return CONFIG_ERR_PARSING; + + *q = '\0'; + *section = p; + + /* check rest of section */ + while (*r && isspace(*r)) + ++r; + + /* there are unrecognized trailing data */ + if (*r && !strchr(cfg->comment_chars, *r)) + return CONFIG_ERR_PARSING; + + return CONFIG_OK; +} + +/** + * \brief Gets key and value on the buffer p + * + * \param cfg config handle + * \param p read buffer + * \param key pointer address to key + * \param val pointer address to value + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +static ConfigRet GetKeyVal(Config *cfg, char *p, char **key, char **val) +{ + char *q, *v; + + if (!cfg || !p || !*p || !key || !val) + return CONFIG_ERR_INVALID_PARAM; + + *key = *val = NULL; + + /* get key */ + while (*p && isspace(*p)) + ++p; + + for (q = p; + *q && (*q != '\r') && (*q != '\n') && (*q != cfg->keyval_sep) && !strchr(cfg->comment_chars, *q); + ++q) + ; + + if (*q != cfg->keyval_sep) + return CONFIG_ERR_PARSING; + + v = q + 1; + + while (*q && (q > p) && isspace(*(q - 1))) + --q; + + if (q == p) /* no key name */ + return CONFIG_ERR_PARSING; + + *q = '\0'; + *key = p; + + /* get value */ + while (*v && isspace(*v)) + ++v; + + for (q = v; + *q && (*q != '\r') && (*q != '\n') && !strchr(cfg->comment_chars, *q); + ++q) + ; + + while (*q && (q > v) && isspace(*(q - 1))) + --q; + + if (q == v) /* no value */ + return CONFIG_ERR_INVALID_VALUE; + + *q = '\0'; + *val = v; + + return CONFIG_OK; +} + +/** + * \brief ConfigRead() reads the stream and populates the entire content to cfg handle + * + * \param fp FILE handle to read + * \param cfg pointer to config handle. + * If not NULL a handle created with ConfigNew() must be given. + * If cfg is NULL a new one is created and saved to cfg. + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigRead(FILE *fp, Config **cfg) +{ + ConfigSection *sect = NULL; + char *p = NULL; + char *section = NULL; + char *key = NULL; + char *val = NULL; + char buf[4096]; + Config *_cfg = NULL; + bool newcfg = false; + ConfigRet ret = CONFIG_OK; + + if ( !fp || !cfg || (*cfg && ((*cfg)->initnum != CONFIG_INIT_MAGIC)) ) + return CONFIG_ERR_INVALID_PARAM; + + if (*cfg == NULL) { + _cfg = ConfigNew(); + if (_cfg == NULL) + return CONFIG_ERR_MEMALLOC; + *cfg = _cfg; + newcfg = true; + } + else + _cfg = *cfg; + + while (!feof(fp)) { + if (fgets(buf, sizeof(buf), fp) == NULL) + continue; + + for (p = buf; *p && isspace(*p) ; ++p) + ; + if (!*p || strchr(_cfg->comment_chars, *p)) + continue; + + if (*p == '[') { + if ((ret = GetSectName(_cfg, p, §ion)) != CONFIG_OK) + goto error; + + if ((ret = ConfigAddSection(_cfg, section, §)) != CONFIG_OK) + goto error; + } + else { + if ((ret = GetKeyVal(_cfg, p, &key, &val)) != CONFIG_OK) + goto error; + + if ((ret = ConfigAddString(_cfg, sect->name, key, val)) != CONFIG_OK) + goto error; + } + } + + return CONFIG_OK; + +error: + if (newcfg) { + ConfigFree(_cfg); + *cfg = NULL; + } + + return ret; +} + +/** + * \brief ConfigReadFile() opens and reads the file and populates the + * entire content to cfg handle + * + * \param filename name of file to open and load + * \param cfg pointer to config handle. + * If not NULL a handle created with ConfigNew() must be given. + * If cfg is NULL a new one is created and saved to cfg. + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigReadFile(const char *filename, Config **cfg) +{ + FILE *fp = NULL; + ConfigRet ret = CONFIG_OK; + + if ( !filename || !cfg || (*cfg && ((*cfg)->initnum != CONFIG_INIT_MAGIC)) ) + return CONFIG_ERR_INVALID_PARAM; + + if ((fp = fopen(filename, "r")) == NULL) + return CONFIG_ERR_FILE; + + ret = ConfigRead(fp, cfg); + + fclose(fp); + + return ret; +} + +/** + * \brief ConfigPrint() prints all cfg content to the stream + * + * \param cfg config handle + * \param stream stream to print + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigPrint(const Config *cfg, FILE *stream) +{ + ConfigSection *sect = NULL; + ConfigKeyValue *kv = NULL; + + if (!cfg || !stream) + return CONFIG_ERR_INVALID_PARAM; + + TAILQ_FOREACH(sect, &cfg->sect_list, next) { + if (sect->name) + fprintf(stream, "[%s]\n", sect->name); + + TAILQ_FOREACH(kv, §->kv_list, next) + fprintf(stream, "%s=%s\n", kv->key, kv->value); + + fprintf(stream, "\n"); + } + + return CONFIG_OK; +} + +/** + * \brief ConfigPrintToFile() prints (saves) all cfg content to the file + * + * \param cfg config handle + * \param filename filename to save in + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigPrintToFile(const Config *cfg, char *filename) +{ + FILE *fp = NULL; + ConfigRet ret = CONFIG_OK; + + if (!cfg || !filename) + return CONFIG_ERR_INVALID_PARAM; + + if ((fp = fopen(filename, "wb")) == NULL) + return CONFIG_ERR_FILE; + + ret = ConfigPrint(cfg, fp); + + fclose(fp); + + return ret; +} + +/** + * \brief ConfigPrintSettings() prints settings to the stream + * + * \param cfg config handle + * \param stream stream to print + * + * \return Returns CONFIG_RET_OK as success, otherwise is an error. + */ +ConfigRet ConfigPrintSettings(const Config *cfg, FILE *stream) +{ + if (!cfg || !stream) + return CONFIG_ERR_INVALID_PARAM; + + fprintf(stream, "\n"); + fprintf(stream, "Configuration settings: \n"); + fprintf(stream, " Comment characters : %s\n", cfg->comment_chars); + fprintf(stream, " Key-Value seperator: %c\n", cfg->keyval_sep); + fprintf(stream, " True-False strings : %s-%s\n", cfg->true_str, cfg->false_str); + fprintf(stream, "\n"); + + return CONFIG_OK; +} + diff --git a/src/pc/configini.h b/src/pc/configini.h new file mode 100644 index 000000000..e46aa4554 --- /dev/null +++ b/src/pc/configini.h @@ -0,0 +1,110 @@ +/* + libconfigini - an ini formatted configuration parser library + Copyright (C) 2013-present Taner YILMAZ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONFIGINI_H_ +#define CONFIGINI_H_ + +#include +#include + + +typedef struct Config Config; + + +#define CONFIG_SECTION_FLAT NULL /* config is flat data (has no section) */ + + +/** + * \brief Return types + */ +typedef enum +{ + CONFIG_OK, /* ok (no error) */ + CONFIG_ERR_FILE, /* file io error (file not exists, cannot open file, ...) */ + CONFIG_ERR_NO_SECTION, /* section does not exist */ + CONFIG_ERR_NO_KEY, /* key does not exist */ + CONFIG_ERR_MEMALLOC, /* memory allocation failed */ + CONFIG_ERR_INVALID_PARAM, /* invalid parametrs (as NULL) */ + CONFIG_ERR_INVALID_VALUE, /* value of key is invalid (inconsistent data, empty data) */ + CONFIG_ERR_PARSING, /* parsing error of data (does not fit to config format) */ +} ConfigRet; + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +Config* ConfigNew (void); +void ConfigFree (Config *cfg); + +const char *ConfigRetToString (ConfigRet ret); + +ConfigRet ConfigRead (FILE *fp, Config **cfg); +ConfigRet ConfigReadFile (const char *filename, Config **cfg); + +ConfigRet ConfigPrint (const Config *cfg, FILE *stream); +ConfigRet ConfigPrintToFile (const Config *cfg, char *filename); +ConfigRet ConfigPrintSettings (const Config *cfg, FILE *stream); + +int ConfigGetSectionCount (const Config *cfg); +int ConfigGetKeyCount (const Config *cfg, const char *sect); + +ConfigRet ConfigSetCommentCharset(Config *cfg, const char *comment_ch); +ConfigRet ConfigSetKeyValSepChar (Config *cfg, char ch); +ConfigRet ConfigSetBoolString (Config *cfg, const char *true_str, const char *false_str); + +ConfigRet ConfigReadString (const Config *cfg, const char *sect, const char *key, char * val, int size, const char * dfl_val); +ConfigRet ConfigReadInt (const Config *cfg, const char *sect, const char *key, int * val, int dfl_val); +ConfigRet ConfigReadUnsignedInt (const Config *cfg, const char *sect, const char *key, unsigned int *val, unsigned int dfl_val); +ConfigRet ConfigReadFloat (const Config *cfg, const char *sect, const char *key, float * val, float dfl_val); +ConfigRet ConfigReadDouble (const Config *cfg, const char *sect, const char *key, double * val, double dfl_val); +ConfigRet ConfigReadBool (const Config *cfg, const char *sect, const char *key, bool * val, bool dfl_val); + +ConfigRet ConfigAddString (Config *cfg, const char *sect, const char *key, const char *val); +ConfigRet ConfigAddInt (Config *cfg, const char *sect, const char *key, int val); +ConfigRet ConfigAddUnsignedInt (Config *cfg, const char *sect, const char *key, unsigned int val); +ConfigRet ConfigAddFloat (Config *cfg, const char *sect, const char *key, float val); +ConfigRet ConfigAddDouble (Config *cfg, const char *sect, const char *key, double val); +ConfigRet ConfigAddBool (Config *cfg, const char *sect, const char *key, bool val); + +bool ConfigHasSection (const Config *cfg, const char *sect); + +ConfigRet ConfigRemoveSection (Config *cfg, const char *sect); +ConfigRet ConfigRemoveKey (Config *cfg, const char *sect, const char *key); + + +#ifdef __cplusplus +} +#endif + + +#endif /* CONFIGINI_H_ */ diff --git a/src/pc/djui/djui_panel_player.c b/src/pc/djui/djui_panel_player.c index 729b56b04..b9569e060 100644 --- a/src/pc/djui/djui_panel_player.c +++ b/src/pc/djui/djui_panel_player.c @@ -113,7 +113,7 @@ void djui_panel_player_edit_palette_destroy(struct DjuiBase* caller) { } static void djui_panel_player_edit_palette_create(struct DjuiBase* caller) { - char* sPartStrings[PLAYER_PART_MAX] = { "Pants", "Shirt", "Gloves", "Shoes", "Hair", "Skin" }; + char* sPartStrings[PLAYER_PART_MAX] = { "Overalls", "Shirt", "Gloves", "Shoes", "Hair", "Skin" }; f32 bodyHeight = 32 * 5 + 64 * 1 + 16 * 5; diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index a1db9a5d1..2fb7ea86d 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -33,6 +33,7 @@ #include "src/game/mario_misc.h" #include "src/pc/utils/misc.h" #include "src/game/level_update.h" +#include "src/pc/mods/mod_storage.h" //////////////////////// @@ -11718,15 +11719,6 @@ int smlua_func_vec3s_to_vec3f(lua_State* L) { } */ - //////////// - // misc.h // -//////////// - -int smlua_func_update_all_mario_stars(UNUSED lua_State* L) { - if(!smlua_functions_valid_param_count(L, 0)) { return 0; } - - - update_all_mario_stars(); return 1; } @@ -11867,6 +11859,17 @@ int smlua_func_network_player_set_description(lua_State* L) { // network_utils.h // ///////////////////// +int smlua_func_network_discord_id_from_local_index(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + u8 localIndex = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1 for function 'network_discord_id_from_local_index'"); return 0; } + + lua_pushstring(L, network_discord_id_from_local_index(localIndex)); + + return 1; +} + int smlua_func_network_get_player_text_color_string(lua_State* L) { if(!smlua_functions_valid_param_count(L, 1)) { return 0; } @@ -19202,8 +19205,6 @@ void smlua_bind_functions_autogen(void) { //smlua_bind_function(L, "vec3s_sum", smlua_func_vec3s_sum); <--- UNIMPLEMENTED //smlua_bind_function(L, "vec3s_to_vec3f", smlua_func_vec3s_to_vec3f); <--- UNIMPLEMENTED - // misc.h - smlua_bind_function(L, "update_all_mario_stars", smlua_func_update_all_mario_stars); // network_player.h smlua_bind_function(L, "get_network_player_from_area", smlua_func_get_network_player_from_area); @@ -19216,6 +19217,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "network_player_set_description", smlua_func_network_player_set_description); // network_utils.h + smlua_bind_function(L, "network_discord_id_from_local_index", smlua_func_network_discord_id_from_local_index); smlua_bind_function(L, "network_get_player_text_color_string", smlua_func_network_get_player_text_color_string); smlua_bind_function(L, "network_global_index_from_local", smlua_func_network_global_index_from_local); smlua_bind_function(L, "network_is_moderator", smlua_func_network_is_moderator); diff --git a/src/pc/mods/mod_storage.c b/src/pc/mods/mod_storage.c new file mode 100644 index 000000000..6ade649b3 --- /dev/null +++ b/src/pc/mods/mod_storage.c @@ -0,0 +1,165 @@ +#include "mod_storage.h" + +#include +#include "pc/platform.h" +#include "pc/configini.h" // for writing +#include "pc/ini.h" // for parsing +#include "pc/lua/smlua.h" +#include "pc/mods/mods_utils.h" +#include "pc/debuglog.h" + +void strdelete(char string[], char substr[]) { + // i is used to loop through the string + u16 i = 0; + + // store the lengths of the string and substr + u16 string_length = strlen(string); + u16 substr_length = strlen(substr); + + // loop through starting at the first index + while (i < string_length) { + // if we find the substr at the current index, delete it + if (strstr(&string[i], substr) == &string[i]) { + // determine the string's new length after removing the substr occurrence + string_length -= substr_length; + // shift forward the remaining characters in the string after the substr + // occurrence by the length of substr, effectively removing it! + for (u16 j = i; j < string_length; j++) { + string[j] = string[j + substr_length]; + } + } else { + i++; + } + } + + string[i] = '\0'; +} + +bool char_valid(char* buffer) { + if (buffer[0] == '\0') { return false; } + while (*buffer != '\0') { + if ((*buffer >= 'a' && *buffer <= 'z') || (*buffer >= 'A' && *buffer <= 'Z') || (*buffer >= '0' && *buffer <= '9') || *buffer == '_' || *buffer == '.') { + buffer++; + continue; + } + return false; + } + return true; +} + +u32 key_count(char* filename) { + FILE *file; + file = fopen(filename, "r"); + if (file == NULL) { return 0; } + + u32 lines = 1; + char c; + do { + c = fgetc(file); + if (c == '\n') { lines++; } + } while (c != EOF); + + fclose(file); + + return lines - 4; +} + +char *mod_storage_get_filename(char* dest) { + const char *path = sys_user_path(); // get base sm64ex-coop appdata dir + snprintf(dest, SYS_MAX_PATH - 1, "%s/sav/%s", path, gLuaActiveMod->relativePath); // append sav folder + strdelete(dest, ".lua"); // delete ".lua" from sav name + strcat(dest, SAVE_EXTENSION); // append SAVE_EXTENSION + normalize_path(dest); // fix any out of place slashes +} + +bool mod_storage_save(const char *key, const char *value) { + if (strlen(key) > MAX_KEY_VALUE_LENGTH || strlen(value) > MAX_KEY_VALUE_LENGTH) { + LOG_LUA_LINE("Too long of a key and or value for mod_storage_save()"); + return false; + } + if (!char_valid((char *)key) || !char_valid((char *)value)) { + LOG_LUA_LINE("Invalid key and or value passed to mod_storage_save()"); + return false; + } + + FILE *file; + Config *cfg = NULL; + char *filename; + filename = (char *)malloc((SYS_MAX_PATH - 1) * sizeof(char)); + mod_storage_get_filename(filename); + + // ensure savPath exists + char savPath[SYS_MAX_PATH] = { 0 }; + if (snprintf(savPath, SYS_MAX_PATH - 1, "%s", fs_get_write_path(SAVE_DIRECTORY)) < 0) { + LOG_ERROR("Failed to concat sav path"); + free(filename); + return false; + } + if (!fs_sys_dir_exists(savPath)) { fs_sys_mkdir(savPath); } + + bool exists = path_exists(filename); + file = fopen(filename, exists ? "r+" : "w"); + cfg = ConfigNew(); + if (exists) { + if (ConfigReadFile(filename, &cfg) != CONFIG_OK) { + ConfigFree(cfg); + fclose(file); + free(filename); + return false; + } + if (key_count(filename) > MAX_KEYS) { + LOG_LUA_LINE("Tried to save more than MAX_KEYS with mod_storage_save()"); + ConfigFree(cfg); + fclose(file); + free(filename); + return false; + } + } + + ConfigRemoveKey(cfg, "storage", key); + ConfigAddString(cfg, "storage", key, value); + + ConfigPrint(cfg, file); + ConfigFree(cfg); + fclose(file); + free(filename); + + return true; +} + +const char *mod_storage_load(const char *key) { + if (strlen(key) > MAX_KEY_VALUE_LENGTH) { + LOG_LUA_LINE("Too long of a key for mod_storage_load()"); + return NULL; + } + if (!char_valid((char *)key)) { + LOG_LUA_LINE("Invalid key passed to mod_storage_save()"); + return NULL; + } + + char *filename; + filename = (char *)malloc((SYS_MAX_PATH - 1) * sizeof(char)); + mod_storage_get_filename(filename); + static char value[MAX_KEY_VALUE_LENGTH]; + ini_t *storage; + + if (!path_exists(filename)) { + free(filename); + return NULL; + } + + storage = ini_load(filename); + if (storage == NULL) { + ini_free(storage); + free(filename); + return NULL; + } + snprintf(value, MAX_KEY_VALUE_LENGTH, "%s", ini_get(storage, "storage", key)); + + ini_free(storage); + free(filename); + + if (strstr(value, "(null)") != NULL) { return NULL; } + + return value; +} diff --git a/src/pc/mods/mod_storage.h b/src/pc/mods/mod_storage.h new file mode 100644 index 000000000..feddb7f9f --- /dev/null +++ b/src/pc/mods/mod_storage.h @@ -0,0 +1,14 @@ +#ifndef MOD_STORAGE_H +#define MOD_STORAGE_H + +#include "mod.h" + +#define MAX_KEYS 255 +#define MAX_KEY_VALUE_LENGTH 64 +#define SAVE_DIRECTORY "sav" +#define SAVE_EXTENSION ".sav" + +bool mod_storage_save(const char *key, const char *value); +const char *mod_storage_load(const char *key); + +#endif diff --git a/src/pc/network/network_utils.c b/src/pc/network/network_utils.c index 90562dcb8..0dce3266f 100644 --- a/src/pc/network/network_utils.c +++ b/src/pc/network/network_utils.c @@ -1,5 +1,6 @@ #include #include "network_utils.h" +#include "discord/discord.h" #include "game/mario_misc.h" u8 network_global_index_from_local(u8 localIndex) { @@ -22,6 +23,11 @@ u8 network_local_index_from_global(u8 globalIndex) { return globalIndex + ((globalIndex < gNetworkPlayerLocal->globalIndex) ? 1 : 0); } +char* network_discord_id_from_local_index(u8 localIndex) { + if (gNetworkSystem == &gNetworkSystemDiscord) { return gNetworkSystem->get_id_str(localIndex); } + return NULL; +} + bool network_is_server(void) { return gNetworkType == NT_SERVER; } diff --git a/src/pc/network/network_utils.h b/src/pc/network/network_utils.h index de7de04de..f1eabed97 100644 --- a/src/pc/network/network_utils.h +++ b/src/pc/network/network_utils.h @@ -7,6 +7,8 @@ u8 network_global_index_from_local(u8 localIndex); u8 network_local_index_from_global(u8 globalIndex); +char* network_discord_id_from_local_index(u8 localIndex); + bool network_is_server(void); bool network_is_moderator(void); diff --git a/src/pc/queue.h b/src/pc/queue.h new file mode 100644 index 000000000..a82af55c5 --- /dev/null +++ b/src/pc/queue.h @@ -0,0 +1,694 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: head/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } , +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, struct type, field.le_next)) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */