mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 13:35:12 +00:00
Mod storage, network_discord_id_from_local_index and other small changes (#200)
* Mod storage & network_discord_id_from_local_index Introducing mod storage, a key/value system for saving things like high scores or settings or anything like that to AppData\Roaming\sm64ex-coop\sav\mod-name.sav (Windows example) You can currently save only strings meaning if you want to save a number you'd need to do mod_storage_save("score", tostring(score)) and then score = tonumber(mod_storage_load("score")) To handle types like that. network_discord_id_from_local_index() returns the discord ID of a player by local index. Some misc changes I made were removing the unsed ini.h include in save_file.c and making the autogen converters output LF line endings instead of CRLF on windows. * Prevent getting id if using direct connect * Update djui_panel_player.c Why does this bother me? Co-authored-by: djoslin0 <djoslin0@users.noreply.github.com>
This commit is contained in:
parent
8e9b2c270c
commit
4b84a87c6f
16 changed files with 2281 additions and 42 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -7095,25 +7095,10 @@
|
|||
|
||||
<br />
|
||||
|
||||
---
|
||||
# functions from misc.h
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
## [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 @@
|
|||
<br />
|
||||
|
||||
|
||||
## [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:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [network_get_player_text_color_string](#network_get_player_text_color_string)
|
||||
|
||||
### Lua Example
|
||||
|
|
|
@ -1012,8 +1012,6 @@
|
|||
|
||||
<br />
|
||||
|
||||
- misc.h
|
||||
- [update_all_mario_stars](functions-3.md#update_all_mario_stars)
|
||||
|
||||
<br />
|
||||
|
||||
|
@ -1030,6 +1028,7 @@
|
|||
<br />
|
||||
|
||||
- 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)
|
||||
|
|
|
@ -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"
|
||||
|
|
1223
src/pc/configini.c
Normal file
1223
src/pc/configini.c
Normal file
File diff suppressed because it is too large
Load diff
110
src/pc/configini.h
Normal file
110
src/pc/configini.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
libconfigini - an ini formatted configuration parser library
|
||||
Copyright (C) 2013-present Taner YILMAZ <taner44@gmail.com>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
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_ */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
165
src/pc/mods/mod_storage.c
Normal file
165
src/pc/mods/mod_storage.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
#include "mod_storage.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
14
src/pc/mods/mod_storage.h
Normal file
14
src/pc/mods/mod_storage.h
Normal file
|
@ -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
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
694
src/pc/queue.h
Normal file
694
src/pc/queue.h
Normal file
|
@ -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 <sys/cdefs.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
Loading…
Reference in a new issue