summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorHappy <markaizer@gmail.com>2014-08-21 04:50:46 +0800
committerHappy <markaizer@gmail.com>2014-08-21 04:50:46 +0800
commitf52e1007fe08c67003c0bc4c78231904dd3fd5cc (patch)
tree99907d827264e501774e58ab4630e41fa7103c02 /src/common
parent2410110dece79b4598c12f1c953219f1d0d1904a (diff)
parent769b1d05aa5cfa8cddfe7d21b35d5c5e4da3bbd6 (diff)
downloadhercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.gz
hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.bz2
hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.xz
hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.zip
Merge pull request #1 from HerculesWS/master
Update from original
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt170
-rw-r--r--src/common/HPM.c660
-rw-r--r--src/common/HPM.h122
-rw-r--r--src/common/HPMDataCheck.h142
-rw-r--r--src/common/HPMi.h161
-rw-r--r--src/common/Makefile.in130
-rw-r--r--src/common/atomic.h29
-rw-r--r--src/common/cbasetypes.h131
-rw-r--r--src/common/conf.c174
-rw-r--r--src/common/conf.h101
-rw-r--r--src/common/console.c361
-rw-r--r--src/common/console.h51
-rw-r--r--src/common/core.c248
-rw-r--r--src/common/core.h21
-rw-r--r--src/common/db.c386
-rw-r--r--src/common/db.h235
-rw-r--r--src/common/des.c7
-rw-r--r--src/common/des.h7
-rw-r--r--src/common/ers.c115
-rw-r--r--src/common/ers.h70
-rw-r--r--src/common/evdp.h168
-rw-r--r--src/common/evdp_epoll.c232
-rw-r--r--src/common/grfio.c72
-rw-r--r--src/common/grfio.h8
-rw-r--r--src/common/malloc.c180
-rw-r--r--src/common/malloc.h35
-rw-r--r--src/common/mapindex.c107
-rw-r--r--src/common/mapindex.h59
-rw-r--r--src/common/md5calc.c8
-rw-r--r--src/common/md5calc.h6
-rw-r--r--src/common/mempool.c568
-rw-r--r--src/common/mempool.h100
-rw-r--r--src/common/mmo.h320
-rw-r--r--src/common/mutex.c40
-rw-r--r--src/common/mutex.h79
-rw-r--r--src/common/netbuffer.c221
-rw-r--r--src/common/netbuffer.h83
-rw-r--r--src/common/network.c1061
-rw-r--r--src/common/network.h189
-rw-r--r--src/common/nullpo.c103
-rw-r--r--src/common/nullpo.h307
-rw-r--r--src/common/raconf.c584
-rw-r--r--src/common/raconf.h59
-rw-r--r--src/common/random.c31
-rw-r--r--src/common/random.h6
-rw-r--r--src/common/showmsg.c219
-rw-r--r--src/common/showmsg.h55
-rw-r--r--src/common/socket.c385
-rw-r--r--src/common/socket.h187
-rw-r--r--src/common/spinlock.h37
-rw-r--r--src/common/sql.c131
-rw-r--r--src/common/sql.h40
-rw-r--r--src/common/strlib.c176
-rw-r--r--src/common/strlib.h83
-rw-r--r--src/common/sysinfo.c1061
-rw-r--r--src/common/sysinfo.h51
-rw-r--r--src/common/thread.c141
-rw-r--r--src/common/thread.h79
-rw-r--r--src/common/timer.c176
-rw-r--r--src/common/timer.h48
-rw-r--r--src/common/utils.c224
-rw-r--r--src/common/utils.h50
-rw-r--r--src/common/winapi.h5
63 files changed, 5340 insertions, 5755 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
deleted file mode 100644
index dbc30734c..000000000
--- a/src/common/CMakeLists.txt
+++ /dev/null
@@ -1,170 +0,0 @@
-
-#
-# Create svnversion.h
-#
-message( STATUS "Creating svnversion.h" )
-if( SVNVERSION )
- file( WRITE ${CMAKE_CURRENT_BINARY_DIR}/svnversion.h
- "#ifndef SVNVERSION\n#define SVNVERSION ${SVNVERSION}\n#endif\n" )
-else()
- file( WRITE ${CMAKE_CURRENT_BINARY_DIR}/svnversion.h "" )
-endif()
-set( GLOBAL_INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "" )
-set( SVNVERSION ${SVNVERSION}
- CACHE STRING "SVN version of the source code" )
-if( INSTALL_COMPONENT_DEVELOPMENT )
- install( FILES ${CMAKE_CURRENT_BINARY_DIR}/svnversion.h
- DESTINATION "src/common"
- COMPONENT Development_base )
-endif( INSTALL_COMPONENT_DEVELOPMENT )
-message( STATUS "Creating svnversion.h - done" )
-
-
-#####################################################################
-# setup
-#
-set( COMMON_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
- CACHE PATH "common source directory" )
-mark_as_advanced( COMMON_SOURCE_DIR )
-
-set( COMMON_ALL_HEADERS
- "${CMAKE_CURRENT_BINARY_DIR}/svnversion.h"
- "${COMMON_SOURCE_DIR}/cbasetypes.h"
- "${COMMON_SOURCE_DIR}/mmo.h"
- )
-
-set( COMMON_MINI_HEADERS
- ${COMMON_ALL_HEADERS}
- "${COMMON_SOURCE_DIR}/core.h"
- "${COMMON_SOURCE_DIR}/console.h"
- "${COMMON_SOURCE_DIR}/malloc.h"
- "${COMMON_SOURCE_DIR}/showmsg.h"
- "${COMMON_SOURCE_DIR}/strlib.h"
- ${LIBCONFIG_HEADERS} # needed by showmsg.h
- CACHE INTERNAL "" )
-set( COMMON_MINI_SOURCES
- "${COMMON_SOURCE_DIR}/core.c"
- "${COMMON_SOURCE_DIR}/console.c"
- "${COMMON_SOURCE_DIR}/malloc.c"
- "${COMMON_SOURCE_DIR}/showmsg.c"
- "${COMMON_SOURCE_DIR}/strlib.c"
- ${LIBCONFIG_SOURCES} # needed by showmsg.c
- CACHE INTERNAL "" )
-set( COMMON_MINI_INCLUDE_DIRS ${LIBCONFIG_INCLUDE_DIRS} CACHE INTERNAL "" )
-set( COMMON_MINI_DEFINITIONS "-DMINICORE ${LIBCONFIG_DEFINITIONS}" CACHE INTERNAL "" )
-
-
-#
-# common_base
-#
-if( WITH_ZLIB )
-message( STATUS "Creating target common_base" )
-set( COMMON_BASE_HEADERS
- ${COMMON_ALL_HEADERS}
- "${COMMON_SOURCE_DIR}/conf.h"
- "${COMMON_SOURCE_DIR}/core.h"
- "${COMMON_SOURCE_DIR}/console.h"
- "${COMMON_SOURCE_DIR}/db.h"
- "${COMMON_SOURCE_DIR}/des.h"
- "${COMMON_SOURCE_DIR}/ers.h"
- "${COMMON_SOURCE_DIR}/grfio.h"
- "${COMMON_SOURCE_DIR}/HPM.h"
- "${COMMON_SOURCE_DIR}/HPMi.h"
- "${COMMON_SOURCE_DIR}/malloc.h"
- "${COMMON_SOURCE_DIR}/mapindex.h"
- "${COMMON_SOURCE_DIR}/md5calc.h"
- "${COMMON_SOURCE_DIR}/nullpo.h"
- "${COMMON_SOURCE_DIR}/random.h"
- "${COMMON_SOURCE_DIR}/showmsg.h"
- "${COMMON_SOURCE_DIR}/socket.h"
- "${COMMON_SOURCE_DIR}/strlib.h"
- "${COMMON_SOURCE_DIR}/timer.h"
- "${COMMON_SOURCE_DIR}/utils.h"
- "${COMMON_SOURCE_DIR}/atomic.h"
- "${COMMON_SOURCE_DIR}/spinlock.h"
- "${COMMON_SOURCE_DIR}/thread.h"
- "${COMMON_SOURCE_DIR}/mutex.h"
- "${COMMON_SOURCE_DIR}/raconf.h"
- "${COMMON_SOURCE_DIR}/mempool.h"
- ${LIBCONFIG_HEADERS} # needed by conf.h/showmsg.h
- CACHE INTERNAL "common_base headers" )
-set( COMMON_BASE_SOURCES
- "${COMMON_SOURCE_DIR}/conf.c"
- "${COMMON_SOURCE_DIR}/core.c"
- "${COMMON_SOURCE_DIR}/console.c"
- "${COMMON_SOURCE_DIR}/db.c"
- "${COMMON_SOURCE_DIR}/des.c"
- "${COMMON_SOURCE_DIR}/ers.c"
- "${COMMON_SOURCE_DIR}/grfio.c"
- "${COMMON_SOURCE_DIR}/HPM.c"
- "${COMMON_SOURCE_DIR}/malloc.c"
- "${COMMON_SOURCE_DIR}/mapindex.c"
- "${COMMON_SOURCE_DIR}/md5calc.c"
- "${COMMON_SOURCE_DIR}/nullpo.c"
- "${COMMON_SOURCE_DIR}/random.c"
- "${COMMON_SOURCE_DIR}/showmsg.c"
- "${COMMON_SOURCE_DIR}/socket.c"
- "${COMMON_SOURCE_DIR}/strlib.c"
- "${COMMON_SOURCE_DIR}/timer.c"
- "${COMMON_SOURCE_DIR}/utils.c"
- "${COMMON_SOURCE_DIR}/thread.c"
- "${COMMON_SOURCE_DIR}/mutex.c"
- "${COMMON_SOURCE_DIR}/mempool.c"
- "${COMMON_SOURCE_DIR}/raconf.c"
- ${LIBCONFIG_SOURCES} # needed by conf.c/showmsg.c
- CACHE INTERNAL "common_base sources" )
-set( COMMON_BASE_INCLUDE_DIRS
- ${LIBCONFIG_INCLUDE_DIRS}
- CACHE INTERNAL "common_base include dirs" )
-set( COMMON_BASE_DEFINITIONS
- ${LIBCONFIG_DEFINITIONS}
- CACHE INTERNAL "common_base definitions" )
-set( LIBRARIES ${GLOBAL_LIBRARIES} ${ZLIB_LIBRARIES} )
-set( INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${MT19937AR_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${COMMON_BASE_INCLUDE_DIRS} )
-set( DEFINITIONS "${GLOBAL_DEFINITIONS} ${COMMON_BASE_DEFINITIONS}" )
-set( SOURCE_FILES ${MT19937AR_HEADERS} ${MT19937AR_SOURCES} ${COMMON_BASE_HEADERS} ${COMMON_BASE_SOURCES} )
-source_group( mt19937ar FILES ${MT19937AR_HEADERS} ${MT19937AR_SOURCES} )
-source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_BASE_SOURCES} )
-add_library( common_base ${SOURCE_FILES} )
-target_link_libraries( common_base ${LIBRARIES} )
-set_target_properties( common_base PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
-include_directories( ${INCLUDE_DIRS} )
-set( HAVE_common_base ON CACHE INTERNAL "" )
-set( TARGET_LIST ${TARGET_LIST} common_base CACHE INTERNAL "" )
-message( STATUS "Creating target common_base - done" )
-else()
-message( STATUS "Skipping target common_base (requires ZLIB)" )
-unset( HAVE_common_base CACHE )
-endif()
-
-
-#
-# common_sql
-#
-if( HAVE_common_base AND WITH_MYSQL )
-message( STATUS "Creating target common_sql" )
-set( COMMON_SQL_HEADERS
- ${COMMON_ALL_HEADERS}
- "${CMAKE_CURRENT_SOURCE_DIR}/sql.h"
- CACHE INTERNAL "common_sql headers" )
-set( COMMON_SQL_SOURCES
- "${CMAKE_CURRENT_SOURCE_DIR}/sql.c"
- CACHE INTERNAL "common_sql sources" )
-set( DEPENDENCIES common_base )
-set( LIBRARIES ${GLOBAL_LIBRARIES} ${MYSQL_LIBRARIES} )
-set( INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${MYSQL_INCLUDE_DIRS} )
-set( DEFINITIONS "${GLOBAL_DEFINITIONS}" )
-set( SOURCE_FILES ${COMMON_SQL_HEADERS} ${COMMON_SQL_SOURCES} )
-source_group( common FILES ${COMMON_SQL_HEADERS} ${COMMON_SQL_SOURCES} )
-add_library( common_sql ${SOURCE_FILES} )
-add_dependencies( common_sql ${DEPENDENCIES} )
-target_link_libraries( common_sql ${LIBRARIES} ${DEPENDENCIES} )
-set_target_properties( common_sql PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
-include_directories( ${INCLUDE_DIRS} )
-set( HAVE_common_sql ON CACHE INTERNAL "" )
-set( TARGET_LIST ${TARGET_LIST} common_sql CACHE INTERNAL "" )
-message( STATUS "Creating target common_sql - done" )
-else()
-message( STATUS "Skipping target common_sql (requires common_base and MYSQL)" )
-unset( HAVE_common_sql CACHE )
-endif()
diff --git a/src/common/HPM.c b/src/common/HPM.c
index 9283360dc..f39954175 100644
--- a/src/common/HPM.c
+++ b/src/common/HPM.c
@@ -1,27 +1,36 @@
// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// See the LICENSE file
+#define HERCULES_CORE
+
+#include "../config/core.h" // CONSOLE_INPUT
+#include "HPM.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "../common/cbasetypes.h"
-#include "../common/mmo.h"
+#include "../common/conf.h"
+#include "../common/console.h"
#include "../common/core.h"
#include "../common/malloc.h"
+#include "../common/mmo.h"
#include "../common/showmsg.h"
#include "../common/socket.h"
+#include "../common/sql.h"
+#include "../common/strlib.h"
+#include "../common/sysinfo.h"
#include "../common/timer.h"
-#include "../common/conf.h"
#include "../common/utils.h"
-#include "../common/console.h"
-#include "../common/strlib.h"
-#include "../common/sql.h"
-#include "HPM.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#ifndef WIN32
-#include <unistd.h>
+# include <unistd.h>
#endif
+struct malloc_interface iMalloc_HPM;
+struct malloc_interface *HPMiMalloc;
+
void hplugin_trigger_event(enum hp_event_types type) {
unsigned int i;
for( i = 0; i < HPM->plugin_count; i++ ) {
@@ -37,7 +46,7 @@ void hplugin_export_symbol(void *var, char *name) {
HPM->symbols[HPM->symbol_count - 1]->ptr = var;
}
-void *hplugin_import_symbol(char *name) {
+void *hplugin_import_symbol(char *name, unsigned int pID) {
unsigned int i;
for( i = 0; i < HPM->symbol_count; i++ ) {
@@ -45,7 +54,7 @@ void *hplugin_import_symbol(char *name) {
return HPM->symbols[i]->ptr;
}
- ShowError("HPM:get_symbol: '"CL_WHITE"%s"CL_RESET"' not found!\n",name);
+ ShowError("HPM:get_symbol:%s: '"CL_WHITE"%s"CL_RESET"' not found!\n",HPM->pid2name(pID),name);
return NULL;
}
@@ -56,7 +65,7 @@ bool hplugin_iscompatible(char* version) {
return false;
sscanf(version, "%u.%u", &req_major, &req_minor);
-
+
return ( req_major == HPM->version[0] && req_minor <= HPM->version[1] ) ? true : false;
}
@@ -105,17 +114,20 @@ bool hplugin_populate(struct hplugin *plugin, const char *filename) {
return true;
}
-void hplugin_load(const char* filename) {
+#undef HPM_POP
+struct hplugin *hplugin_load(const char* filename) {
struct hplugin *plugin;
struct hplugin_info *info;
struct HPMi_interface **HPMi;
bool anyEvent = false;
void **import_symbol_ref;
Sql **sql_handle;
+ unsigned int *HPMDataCheckLen;
+ struct s_HPMDataCheck *HPMDataCheck;
if( HPM->exists(filename) ) {
ShowWarning("HPM:plugin_load: attempting to load duplicate '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
- return;
+ return NULL;
}
plugin = HPM->create();
@@ -123,30 +135,33 @@ void hplugin_load(const char* filename) {
if( !( plugin->dll = plugin_open(filename) ) ){
ShowWarning("HPM:plugin_load: failed to load '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
if( !( info = plugin_import(plugin->dll, "pinfo",struct hplugin_info*) ) ) {
ShowDebug("HPM:plugin_load: failed to retrieve 'plugin_info' for '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
if( !(info->type & SERVER_TYPE) ) {
HPM->unload(plugin);
- return;
+ return NULL;
}
if( !HPM->iscompatible(info->req_version) ) {
ShowWarning("HPM:plugin_load: '"CL_WHITE"%s"CL_RESET"' incompatible version '%s' -> '%s', skipping...\n", filename, info->req_version, HPM_VERSION);
HPM->unload(plugin);
- return;
+ return NULL;
}
-
+
+ plugin->info = info;
+ plugin->filename = aStrdup(filename);
+
if( !( import_symbol_ref = plugin_import(plugin->dll, "import_symbol",void **) ) ) {
ShowWarning("HPM:plugin_load: failed to retrieve 'import_symbol' for '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
*import_symbol_ref = HPM->import_symbol;
@@ -154,21 +169,21 @@ void hplugin_load(const char* filename) {
if( !( sql_handle = plugin_import(plugin->dll, "mysql_handle",Sql **) ) ) {
ShowWarning("HPM:plugin_load: failed to retrieve 'mysql_handle' for '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
- *sql_handle = HPM->import_symbol("sql_handle");
+ *sql_handle = HPM->import_symbol("sql_handle",plugin->idx);
if( !( HPMi = plugin_import(plugin->dll, "HPMi",struct HPMi_interface **) ) ) {
ShowWarning("HPM:plugin_load: failed to retrieve 'HPMi' for '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
if( !( *HPMi = plugin_import(plugin->dll, "HPMi_s",struct HPMi_interface *) ) ) {
ShowWarning("HPM:plugin_load: failed to retrieve 'HPMi_s' for '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
plugin->hpi = *HPMi;
@@ -181,25 +196,57 @@ void hplugin_load(const char* filename) {
if( ( plugin->hpi->event[HPET_READY] = plugin_import(plugin->dll, "server_online",void (*)(void)) ) )
anyEvent = true;
+ if( ( plugin->hpi->event[HPET_POST_FINAL] = plugin_import(plugin->dll, "server_post_final",void (*)(void)) ) )
+ anyEvent = true;
+
+ if( ( plugin->hpi->event[HPET_PRE_INIT] = plugin_import(plugin->dll, "server_preinit",void (*)(void)) ) )
+ anyEvent = true;
+
if( !anyEvent ) {
ShowWarning("HPM:plugin_load: no events found for '"CL_WHITE"%s"CL_RESET"', skipping...\n", filename);
HPM->unload(plugin);
- return;
+ return NULL;
}
if( !HPM->populate(plugin,filename) )
- return;
+ return NULL;
- if( SERVER_TYPE == SERVER_TYPE_MAP ) {
- plugin->hpi->addCommand = HPM->import_symbol("addCommand");
- plugin->hpi->addScript = HPM->import_symbol("addScript");
- plugin->hpi->addCPCommand = HPM->import_symbol("addCPCommand");
+ if( !( HPMDataCheckLen = plugin_import(plugin->dll, "HPMDataCheckLen", unsigned int *) ) ) {
+ ShowWarning("HPM:plugin_load: failed to retrieve 'HPMDataCheckLen' for '"CL_WHITE"%s"CL_RESET"', most likely not including HPMDataCheck.h, skipping...\n", filename);
+ HPM->unload(plugin);
+ return NULL;
}
- plugin->info = info;
- plugin->filename = aStrdup(filename);
-
- return;
+ if( !( HPMDataCheck = plugin_import(plugin->dll, "HPMDataCheck", struct s_HPMDataCheck *) ) ) {
+ ShowWarning("HPM:plugin_load: failed to retrieve 'HPMDataCheck' for '"CL_WHITE"%s"CL_RESET"', most likely not including HPMDataCheck.h, skipping...\n", filename);
+ HPM->unload(plugin);
+ return NULL;
+ }
+
+ if( HPM->DataCheck && !HPM->DataCheck(HPMDataCheck,*HPMDataCheckLen,plugin->info->name) ) {
+ ShowWarning("HPM:plugin_load: '"CL_WHITE"%s"CL_RESET"' failed DataCheck, out of sync from the core (recompile plugin), skipping...\n", filename);
+ HPM->unload(plugin);
+ return NULL;
+ }
+
+ /* id */
+ plugin->hpi->pid = plugin->idx;
+ /* core */
+ plugin->hpi->addCPCommand = HPM->import_symbol("addCPCommand",plugin->idx);
+ plugin->hpi->addPacket = HPM->import_symbol("addPacket",plugin->idx);
+ plugin->hpi->addToHPData = HPM->import_symbol("addToHPData",plugin->idx);
+ plugin->hpi->getFromHPData = HPM->import_symbol("getFromHPData",plugin->idx);
+ plugin->hpi->removeFromHPData = HPM->import_symbol("removeFromHPData",plugin->idx);
+ plugin->hpi->AddHook = HPM->import_symbol("AddHook",plugin->idx);
+ plugin->hpi->HookStop = HPM->import_symbol("HookStop",plugin->idx);
+ plugin->hpi->HookStopped = HPM->import_symbol("HookStopped",plugin->idx);
+ plugin->hpi->addArg = HPM->import_symbol("addArg",plugin->idx);
+ plugin->hpi->addConf = HPM->import_symbol("addConf",plugin->idx);
+ /* server specific */
+ if( HPM->load_sub )
+ HPM->load_sub(plugin);
+
+ return plugin;
}
void hplugin_unload(struct hplugin* plugin) {
@@ -209,7 +256,8 @@ void hplugin_unload(struct hplugin* plugin) {
aFree(plugin->filename);
if( plugin->dll )
plugin_close(plugin->dll);
-
+ /* TODO: for manual packet unload */
+ /* - Go thru known packets and unlink any belonging to the plugin being removed */
aFree(plugin);
if( !HPM->off ) {
HPM->plugins[i] = NULL;
@@ -227,53 +275,444 @@ void hplugin_unload(struct hplugin* plugin) {
}
}
-void hplugins_config_read(void) {
+void hplugins_config_read(const char * const *extra_plugins, int extra_plugins_count) {
config_t plugins_conf;
config_setting_t *plist = NULL;
const char *config_filename = "conf/plugins.conf"; // FIXME hardcoded name
+ FILE *fp;
+ int i;
+
+// uncomment once login/char support is wrapped up
+// if( !HPM->DataCheck ) {
+// ShowError("HPM:config_read: HPM->DataCheck not set! Failure\n");
+// return;
+// }
+
+ /* yes its ugly, its temporary and will be gone as soon as the new inter-server.conf is set */
+ if( (fp = fopen("conf/import/plugins.conf","r")) ) {
+ config_filename = "conf/import/plugins.conf";
+ fclose(fp);
+ }
- if (conf_read_file(&plugins_conf, config_filename))
+ if (libconfig->read_file(&plugins_conf, config_filename))
return;
if( HPM->symbol_defaults_sub )
HPM->symbol_defaults_sub();
- plist = config_lookup(&plugins_conf, "plugins_list");
+ plist = libconfig->lookup(&plugins_conf, "plugins_list");
+ for (i = 0; i < extra_plugins_count; i++) {
+ config_setting_t *entry = libconfig->setting_add(plist, NULL, CONFIG_TYPE_STRING);
+ config_setting_set_string(entry, extra_plugins[i]);
+ }
if (plist != NULL) {
- int length = config_setting_length(plist), i;
+ int length = libconfig->setting_length(plist);
char filename[60];
for(i = 0; i < length; i++) {
- snprintf(filename, 60, "plugins/%s%s", config_setting_get_string_elem(plist,i), DLL_EXT);
- HPM->load(filename);
+ if( !strcmpi(libconfig->setting_get_string_elem(plist,i),"HPMHooking") ) {//must load it first
+ struct hplugin *plugin;
+ snprintf(filename, 60, "plugins/%s%s", libconfig->setting_get_string_elem(plist,i), DLL_EXT);
+ if( ( plugin = HPM->load(filename) ) ) {
+ bool (*func)(bool *fr);
+ bool (*addhook_sub) (enum HPluginHookType type, const char *target, void *hook, unsigned int pID);
+ if( ( func = plugin_import(plugin->dll, "Hooked",bool (*)(bool *)) ) && ( addhook_sub = plugin_import(plugin->dll, "HPM_Plugin_AddHook",bool (*)(enum HPluginHookType, const char *, void *, unsigned int)) ) ) {
+ if( func(&HPM->force_return) ) {
+ HPM->hooking = true;
+ HPM->addhook_sub = addhook_sub;
+ }
+ }
+ }
+ }
+ }
+ for(i = 0; i < length; i++) {
+ if( strcmpi(libconfig->setting_get_string_elem(plist,i),"HPMHooking") ) {//now all others
+ snprintf(filename, 60, "plugins/%s%s", libconfig->setting_get_string_elem(plist,i), DLL_EXT);
+ HPM->load(filename);
+ }
}
- config_destroy(&plugins_conf);
+ libconfig->destroy(&plugins_conf);
}
if( HPM->plugin_count )
ShowStatus("HPM: There are '"CL_WHITE"%d"CL_RESET"' plugins loaded, type '"CL_WHITE"plugins"CL_RESET"' to list them\n", HPM->plugin_count);
}
+CPCMD(plugins) {
+ if( HPM->plugin_count == 0 ) {
+ ShowInfo("HPC: there are no plugins loaded\n");
+ } else {
+ unsigned int i;
+
+ ShowInfo("HPC: There are '"CL_WHITE"%d"CL_RESET"' plugins loaded\n",HPM->plugin_count);
+
+ for(i = 0; i < HPM->plugin_count; i++) {
+ ShowInfo("HPC: - '"CL_WHITE"%s"CL_RESET"' (%s)\n",HPM->plugins[i]->info->name,HPM->plugins[i]->filename);
+ }
+ }
+}
+void hplugins_grabHPData(struct HPDataOperationStorage *ret, enum HPluginDataTypes type, void *ptr) {
+ /* record address */
+ switch( type ) {
+ /* core-handled */
+ case HPDT_SESSION:
+ ret->HPDataSRCPtr = (void**)(&((struct socket_data *)ptr)->hdata);
+ ret->hdatac = &((struct socket_data *)ptr)->hdatac;
+ break;
+ /* goes to sub */
+ default:
+ if( HPM->grabHPDataSub ) {
+ if( HPM->grabHPDataSub(ret,type,ptr) )
+ return;
+ else {
+ ShowError("HPM:HPM:grabHPData failed, unknown type %d!\n",type);
+ }
+ } else
+ ShowError("HPM:grabHPData failed, type %d needs sub-handler!\n",type);
+ ret->HPDataSRCPtr = NULL;
+ ret->hdatac = NULL;
+ return;
+ }
+}
+void hplugins_addToHPData(enum HPluginDataTypes type, unsigned int pluginID, void *ptr, void *data, unsigned int index, bool autofree) {
+ struct HPluginData *HPData, **HPDataSRC;
+ struct HPDataOperationStorage action;
+ unsigned int i, max;
+
+ HPM->grabHPData(&action,type,ptr);
+
+ if( action.hdatac == NULL ) { /* woo it failed! */
+ ShowError("HPM:addToHPData:%s: failed, type %d (%u|%u)\n",HPM->pid2name(pluginID),type,pluginID,index);
+ return;
+ }
+
+ /* flag */
+ HPDataSRC = *(action.HPDataSRCPtr);
+ max = *(action.hdatac);
+
+ /* duplicate check */
+ for(i = 0; i < max; i++) {
+ if( HPDataSRC[i]->pluginID == pluginID && HPDataSRC[i]->type == index ) {
+ ShowError("HPM:addToHPData:%s: error! attempting to insert duplicate struct of id %u and index %u\n",HPM->pid2name(pluginID),pluginID,index);
+ return;
+ }
+ }
+
+ /* HPluginData is always same size, probably better to use the ERS (with reasonable chunk size e.g. 10/25/50) */
+ CREATE(HPData, struct HPluginData, 1);
+
+ /* input */
+ HPData->pluginID = pluginID;
+ HPData->type = index;
+ HPData->flag.free = autofree ? 1 : 0;
+ HPData->data = data;
+
+ /* resize */
+ *(action.hdatac) += 1;
+ RECREATE(*(action.HPDataSRCPtr),struct HPluginData *,*(action.hdatac));
+
+ /* RECREATE modified the address */
+ HPDataSRC = *(action.HPDataSRCPtr);
+ HPDataSRC[*(action.hdatac) - 1] = HPData;
+}
+
+void *hplugins_getFromHPData(enum HPluginDataTypes type, unsigned int pluginID, void *ptr, unsigned int index) {
+ struct HPDataOperationStorage action;
+ struct HPluginData **HPDataSRC;
+ unsigned int i, max;
+
+ HPM->grabHPData(&action,type,ptr);
+
+ if( action.hdatac == NULL ) { /* woo it failed! */
+ ShowError("HPM:getFromHPData:%s: failed, type %d (%u|%u)\n",HPM->pid2name(pluginID),type,pluginID,index);
+ return NULL;
+ }
+
+ /* flag */
+ HPDataSRC = *(action.HPDataSRCPtr);
+ max = *(action.hdatac);
+
+ for(i = 0; i < max; i++) {
+ if( HPDataSRC[i]->pluginID == pluginID && HPDataSRC[i]->type == index )
+ return HPDataSRC[i]->data;
+ }
+
+ return NULL;
+}
+
+void hplugins_removeFromHPData(enum HPluginDataTypes type, unsigned int pluginID, void *ptr, unsigned int index) {
+ struct HPDataOperationStorage action;
+ struct HPluginData **HPDataSRC;
+ unsigned int i, max;
+
+ HPM->grabHPData(&action,type,ptr);
+
+ if( action.hdatac == NULL ) { /* woo it failed! */
+ ShowError("HPM:removeFromHPData:%s: failed, type %d (%u|%u)\n",HPM->pid2name(pluginID),type,pluginID,index);
+ return;
+ }
+
+ /* flag */
+ HPDataSRC = *(action.HPDataSRCPtr);
+ max = *(action.hdatac);
+
+ for(i = 0; i < max; i++) {
+ if( HPDataSRC[i]->pluginID == pluginID && HPDataSRC[i]->type == index )
+ break;
+ }
+
+ if( i != max ) {
+ unsigned int cursor;
+
+ aFree(HPDataSRC[i]->data);/* when its removed we delete it regardless of autofree */
+ aFree(HPDataSRC[i]);
+ HPDataSRC[i] = NULL;
+
+ for(i = 0, cursor = 0; i < max; i++) {
+ if( HPDataSRC[i] == NULL )
+ continue;
+ if( i != cursor )
+ HPDataSRC[cursor] = HPDataSRC[i];
+ cursor++;
+ }
+ *(action.hdatac) = cursor;
+ }
+
+}
+
+bool hplugins_addpacket(unsigned short cmd, short length,void (*receive) (int fd),unsigned int point,unsigned int pluginID) {
+ struct HPluginPacket *packet;
+ unsigned int i;
+
+ if( point >= hpPHP_MAX ) {
+ ShowError("HPM->addPacket:%s: unknown point '%u' specified for packet 0x%04x (len %d)\n",HPM->pid2name(pluginID),point,cmd,length);
+ return false;
+ }
+
+ for(i = 0; i < HPM->packetsc[point]; i++) {
+ if( HPM->packets[point][i].cmd == cmd ) {
+ ShowError("HPM->addPacket:%s: can't add packet 0x%04x, already in use by '%s'!",HPM->pid2name(pluginID),cmd,HPM->pid2name(HPM->packets[point][i].pluginID));
+ return false;
+ }
+ }
+
+ RECREATE(HPM->packets[point], struct HPluginPacket, ++HPM->packetsc[point]);
+ packet = &HPM->packets[point][HPM->packetsc[point] - 1];
+
+ packet->pluginID = pluginID;
+ packet->cmd = cmd;
+ packet->len = length;
+ packet->receive = receive;
+
+ return true;
+}
+/*
+ 0 = unknown
+ 1 = OK
+ 2 = incomplete
+ */
+unsigned char hplugins_parse_packets(int fd, enum HPluginPacketHookingPoints point) {
+ unsigned int i;
+
+ for(i = 0; i < HPM->packetsc[point]; i++) {
+ if( HPM->packets[point][i].cmd == RFIFOW(fd,0) )
+ break;
+ }
+
+ if( i != HPM->packetsc[point] ) {
+ struct HPluginPacket *packet = &HPM->packets[point][i];
+ short length;
+
+ if( (length = packet->len) == -1 ) {
+ if( (length = RFIFOW(fd, 2)) > (int)RFIFOREST(fd) )
+ return 2;
+ }
+
+ packet->receive(fd);
+ RFIFOSKIP(fd, length);
+ return 1;
+ }
+
+ return 0;
+}
+
+char *hplugins_id2name (unsigned int pid) {
+ unsigned int i;
+
+ for( i = 0; i < HPM->plugin_count; i++ ) {
+ if( HPM->plugins[i]->idx == pid )
+ return HPM->plugins[i]->info->name;
+ }
+
+ return "UnknownPlugin";
+}
+char* HPM_file2ptr(const char *file) {
+ unsigned int i;
+
+ for(i = 0; i < HPM->fnamec; i++) {
+ if( HPM->fnames[i].addr == file )
+ return HPM->fnames[i].name;
+ }
+
+ i = HPM->fnamec;
+
+ /* we handle this memory outside of the server's memory manager because we need it to exist after the memory manager goes down */
+ HPM->fnames = realloc(HPM->fnames,(++HPM->fnamec)*sizeof(struct HPMFileNameCache));
+
+ HPM->fnames[i].addr = file;
+ HPM->fnames[i].name = strdup(file);
+
+ return HPM->fnames[i].name;
+}
+void* HPM_mmalloc(size_t size, const char *file, int line, const char *func) {
+ return iMalloc->malloc(size,HPM_file2ptr(file),line,func);
+}
+void* HPM_calloc(size_t num, size_t size, const char *file, int line, const char *func) {
+ return iMalloc->calloc(num,size,HPM_file2ptr(file),line,func);
+}
+void* HPM_realloc(void *p, size_t size, const char *file, int line, const char *func) {
+ return iMalloc->realloc(p,size,HPM_file2ptr(file),line,func);
+}
+void* HPM_reallocz(void *p, size_t size, const char *file, int line, const char *func) {
+ return iMalloc->reallocz(p,size,HPM_file2ptr(file),line,func);
+}
+char* HPM_astrdup(const char *p, const char *file, int line, const char *func) {
+ return iMalloc->astrdup(p,HPM_file2ptr(file),line,func);
+}
+/* TODO: add ability for tracking using pID for the upcoming runtime load/unload support. */
+bool HPM_AddHook(enum HPluginHookType type, const char *target, void *hook, unsigned int pID) {
+ if( !HPM->hooking ) {
+ ShowError("HPM:AddHook Fail! '%s' tried to hook to '%s' but HPMHooking is disabled!\n",HPM->pid2name(pID),target);
+ return false;
+ }
+ /* search if target is a known hook point within 'common' */
+ /* if not check if a sub-hooking list is available (from the server) and run it by */
+ if( HPM->addhook_sub && HPM->addhook_sub(type,target,hook,pID) )
+ return true;
+
+ ShowError("HPM:AddHook: unknown Hooking Point '%s'!\n",target);
+
+ return false;
+}
+void HPM_HookStop (const char *func, unsigned int pID) {
+ /* track? */
+ HPM->force_return = true;
+}
+bool HPM_HookStopped (void) {
+ return HPM->force_return;
+}
+/* command-line args */
+bool hpm_parse_arg(const char *arg, int *index, char *argv[], bool param) {
+ struct HPMArgData *data;
+
+ if( (data = strdb_get(HPM->arg_db,arg)) ) {
+ data->func((data->has_param && param)?argv[(*index)+1]:NULL);
+ if( data->has_param && param ) *index += 1;
+ return true;
+ }
+
+ return false;
+}
+void hpm_arg_help(void) {
+ DBIterator *iter = db_iterator(HPM->arg_db);
+ struct HPMArgData *data = NULL;
+
+ for( data = dbi_first(iter); dbi_exists(iter); data = dbi_next(iter) ) {
+ if( data->help != NULL )
+ data->help();
+ else
+ ShowInfo(" %s (%s)\t\t<no description provided>\n",data->name,HPM->pid2name(data->pluginID));
+ }
+
+ dbi_destroy(iter);
+}
+bool hpm_add_arg(unsigned int pluginID, char *name, bool has_param, void (*func) (char *param),void (*help) (void)) {
+ struct HPMArgData *data = NULL;
+
+ if( strdb_exists(HPM->arg_db, name) ) {
+ ShowError("HPM:add_arg:%s duplicate! (from %s)\n",name,HPM->pid2name(pluginID));
+ return false;
+ }
+
+ CREATE(data, struct HPMArgData, 1);
+
+ data->pluginID = pluginID;
+ data->name = aStrdup(name);
+ data->func = func;
+ data->help = help;
+ data->has_param = has_param;
+
+ strdb_put(HPM->arg_db, data->name, data);
+
+ return true;
+}
+bool hplugins_addconf(unsigned int pluginID, enum HPluginConfType type, char *name, void (*func) (const char *val)) {
+ struct HPConfListenStorage *conf;
+ unsigned int i;
+
+ if( type >= HPCT_MAX ) {
+ ShowError("HPM->addConf:%s: unknown point '%u' specified for config '%s'\n",HPM->pid2name(pluginID),type,name);
+ return false;
+ }
+
+ for(i = 0; i < HPM->confsc[type]; i++) {
+ if( !strcmpi(name,HPM->confs[type][i].key) ) {
+ ShowError("HPM->addConf:%s: duplicate '%s', already in use by '%s'!",HPM->pid2name(pluginID),name,HPM->pid2name(HPM->confs[type][i].pluginID));
+ return false;
+ }
+ }
+
+ RECREATE(HPM->confs[type], struct HPConfListenStorage, ++HPM->confsc[type]);
+ conf = &HPM->confs[type][HPM->confsc[type] - 1];
+
+ conf->pluginID = pluginID;
+ safestrncpy(conf->key, name, HPM_ADDCONF_LENGTH);
+ conf->func = func;
+
+ return true;
+}
+bool hplugins_parse_conf(const char *w1, const char *w2, enum HPluginConfType point) {
+ unsigned int i;
+
+ /* exists? */
+ for(i = 0; i < HPM->confsc[point]; i++) {
+ if( !strcmpi(w1,HPM->confs[point][i].key) )
+ break;
+ }
+
+ /* trigger and we're set! */
+ if( i != HPM->confsc[point] ) {
+ HPM->confs[point][i].func(w2);
+ return true;
+ }
+
+ return false;
+}
+
void hplugins_share_defaults(void) {
/* console */
- HPM->share(console->addCommand,"addCPCommand");
+#ifdef CONSOLE_INPUT
+ HPM->share(console->input->addCommand,"addCPCommand");
+#endif
+ /* our own */
+ HPM->share(hplugins_addpacket,"addPacket");
+ HPM->share(hplugins_addToHPData,"addToHPData");
+ HPM->share(hplugins_getFromHPData,"getFromHPData");
+ HPM->share(hplugins_removeFromHPData,"removeFromHPData");
+ HPM->share(HPM_AddHook,"AddHook");
+ HPM->share(HPM_HookStop,"HookStop");
+ HPM->share(HPM_HookStopped,"HookStopped");
+ HPM->share(hpm_add_arg,"addArg");
+ HPM->share(hplugins_addconf,"addConf");
/* core */
HPM->share(&runflag,"runflag");
HPM->share(arg_v,"arg_v");
HPM->share(&arg_c,"arg_c");
HPM->share(SERVER_NAME,"SERVER_NAME");
HPM->share(&SERVER_TYPE,"SERVER_TYPE");
- HPM->share((void*)get_svn_revision,"get_svn_revision");
- HPM->share((void*)get_git_hash,"get_git_hash");
HPM->share(DB, "DB");
- HPM->share(iMalloc, "iMalloc");
+ HPM->share(HPMiMalloc, "iMalloc");
/* socket */
- HPM->share(RFIFOSKIP,"RFIFOSKIP");
- HPM->share(WFIFOSET,"WFIFOSET");
- HPM->share(do_close,"do_close");
- HPM->share(make_connection,"make_connection");
- HPM->share(session,"session");
- HPM->share(&fd_max,"fd_max");
- HPM->share(addr_,"addr");
+ HPM->share(sockt,"sockt");
/* strlib */
HPM->share(strlib,"strlib");
HPM->share(sv,"sv");
@@ -281,41 +720,70 @@ void hplugins_share_defaults(void) {
/* sql */
HPM->share(SQL,"SQL");
/* timer */
- HPM->share(iTimer,"iTimer");
-
-}
-CPCMD(plugins) {
- if( HPM->plugin_count == 0 ) {
- ShowInfo("HPC: there are no plugins loaded\n");
- } else {
- unsigned int i;
-
- ShowInfo("HPC: There are '"CL_WHITE"%d"CL_RESET"' plugins loaded\n",HPM->plugin_count);
-
- for(i = 0; i < HPM->plugin_count; i++) {
- ShowInfo("HPC: - '"CL_WHITE"%s"CL_RESET"' (%s)\n",HPM->plugins[i]->info->name,HPM->plugins[i]->filename);
- }
- }
+ HPM->share(timer,"timer");
+ /* libconfig */
+ HPM->share(libconfig,"libconfig");
+ /* sysinfo */
+ HPM->share(sysinfo,"sysinfo");
}
+
void hpm_init(void) {
+ unsigned int i;
+
HPM->symbols = NULL;
HPM->plugins = NULL;
HPM->plugin_count = HPM->symbol_count = 0;
HPM->off = false;
- sscanf(HPM_VERSION, "%d.%d", &HPM->version[0], &HPM->version[1]);
+ memcpy(&iMalloc_HPM, iMalloc, sizeof(struct malloc_interface));
+ HPMiMalloc = &iMalloc_HPM;
+ HPMiMalloc->malloc = HPM_mmalloc;
+ HPMiMalloc->calloc = HPM_calloc;
+ HPMiMalloc->realloc = HPM_realloc;
+ HPMiMalloc->reallocz = HPM_reallocz;
+ HPMiMalloc->astrdup = HPM_astrdup;
+
+ sscanf(HPM_VERSION, "%u.%u", &HPM->version[0], &HPM->version[1]);
if( HPM->version[0] == 0 && HPM->version[1] == 0 ) {
ShowError("HPM:init:failed to retrieve HPM version!!\n");
return;
}
+
+ for(i = 0; i < hpPHP_MAX; i++) {
+ HPM->packets[i] = NULL;
+ HPM->packetsc[i] = 0;
+ }
+
+ HPM->arg_db = strdb_alloc(DB_OPT_RELEASE_DATA, 0);
+
HPM->symbol_defaults();
- console->addCommand("plugins",CPCMD_A(plugins));
-
+#ifdef CONSOLE_INPUT
+ console->input->addCommand("plugins",CPCMD_A(plugins));
+#endif
return;
}
-
+void hpm_memdown(void) {
+ unsigned int i;
+
+ /* this memory is handled outside of the server's memory manager and thus cleared after memory manager goes down */
+
+ for( i = 0; i < HPM->fnamec; i++ ) {
+ free(HPM->fnames[i].name);
+ }
+
+ if( HPM->fnames )
+ free(HPM->fnames);
+
+}
+int hpm_arg_db_clear_sub(DBKey key, DBData *data, va_list args) {
+ struct HPMArgData *a = DB->data2ptr(data);
+
+ aFree(a->name);
+
+ return 0;
+}
void hpm_final(void) {
unsigned int i;
@@ -334,12 +802,45 @@ void hpm_final(void) {
if( HPM->symbols )
aFree(HPM->symbols);
-
+
+ for( i = 0; i < hpPHP_MAX; i++ ) {
+ if( HPM->packets[i] )
+ aFree(HPM->packets[i]);
+ }
+
+ for( i = 0; i < HPCT_MAX; i++ ) {
+ if( HPM->confsc[i] )
+ aFree(HPM->confs[i]);
+ }
+
+ HPM->arg_db->destroy(HPM->arg_db,HPM->arg_db_clear_sub);
+
+ /* HPM->fnames is cleared after the memory manager goes down */
+ iMalloc->post_shutdown = hpm_memdown;
+
return;
}
void hpm_defaults(void) {
+ unsigned int i;
HPM = &HPM_s;
+ HPM->fnames = NULL;
+ HPM->fnamec = 0;
+ HPM->force_return = false;
+ HPM->hooking = false;
+ /* */
+ HPM->fnames = NULL;
+ HPM->fnamec = 0;
+ for(i = 0; i < hpPHP_MAX; i++) {
+ HPM->packets[i] = NULL;
+ HPM->packetsc[i] = 0;
+ }
+ for(i = 0; i < HPCT_MAX; i++) {
+ HPM->confs[i] = NULL;
+ HPM->confsc[i] = 0;
+ }
+ HPM->arg_db = NULL;
+ /* */
HPM->init = hpm_init;
HPM->final = hpm_final;
@@ -355,4 +856,15 @@ void hpm_defaults(void) {
HPM->config_read = hplugins_config_read;
HPM->populate = hplugin_populate;
HPM->symbol_defaults_sub = NULL;
+ HPM->pid2name = hplugins_id2name;
+ HPM->parse_packets = hplugins_parse_packets;
+ HPM->load_sub = NULL;
+ HPM->addhook_sub = NULL;
+ HPM->arg_db_clear_sub = hpm_arg_db_clear_sub;
+ HPM->parse_arg = hpm_parse_arg;
+ HPM->arg_help = hpm_arg_help;
+ HPM->grabHPData = hplugins_grabHPData;
+ HPM->grabHPDataSub = NULL;
+ HPM->parseConf = hplugins_parse_conf;
+ HPM->DataCheck = NULL;
}
diff --git a/src/common/HPM.h b/src/common/HPM.h
index 10b1f0e79..fe8d45066 100644
--- a/src/common/HPM.h
+++ b/src/common/HPM.h
@@ -1,40 +1,50 @@
// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// See the LICENSE file
-#ifndef _HPM_H_
-#define _HPM_H_
+#ifndef COMMON_HPM_H
+#define COMMON_HPM_H
+
+#ifndef HERCULES_CORE
+#error You should never include HPM.h from a plugin.
+#endif
-#include "../common/cbasetypes.h"
#include "../common/HPMi.h"
+#include "../common/cbasetypes.h"
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
- #define plugin_open(x) LoadLibraryA(x)
- #define plugin_import(x,y,z) (z)GetProcAddress(x,y)
- #define plugin_close(x) FreeLibrary(x)
+ #define plugin_open(x) LoadLibraryA(x)
+ #define plugin_import(x,y,z) (z)GetProcAddress((x),(y))
+ #define plugin_close(x) FreeLibrary(x)
- #define DLL_EXT ".dll"
- #define DLL HINSTANCE
-#else
+ #define DLL_EXT ".dll"
+ #define DLL HINSTANCE
+#else // ! WIN32
#include <dlfcn.h>
- #define plugin_open(x) dlopen(x,RTLD_NOW)
- #define plugin_import(x,y,z) (z)dlsym(x,y)
- #define plugin_close(x) dlclose(x)
+ #ifdef RTLD_DEEPBIND // Certain linux distributions require this, but it's not available everywhere
+ #define plugin_open(x) dlopen((x),RTLD_NOW|RTLD_DEEPBIND)
+ #else // ! RTLD_DEEPBIND
+ #define plugin_open(x) dlopen((x),RTLD_NOW)
+ #endif // RTLD_DEEPBIND
+ #define plugin_import(x,y,z) (z)dlsym((x),(y))
+ #define plugin_close(x) dlclose(x)
- #ifdef CYGWIN
- #define DLL_EXT ".dll"
+ #if defined CYGWIN
+ #define DLL_EXT ".dll"
+ #elif defined __DARWIN__
+ #define DLL_EXT ".dylib"
#else
- #define DLL_EXT ".so"
+ #define DLL_EXT ".so"
#endif
- #define DLL void *
+ #define DLL void *
#include <string.h> // size_t
-#endif
+#endif // WIN32
struct hplugin {
DLL dll;
@@ -49,35 +59,103 @@ struct hpm_symbol {
void *ptr;
};
+struct HPluginData {
+ unsigned int pluginID;
+ unsigned int type;
+ struct {
+ unsigned int free : 1;
+ } flag;
+ void *data;
+};
+
+struct HPluginPacket {
+ unsigned int pluginID;
+ unsigned short cmd;
+ short len;
+ void (*receive) (int fd);
+};
+
+struct HPMFileNameCache {
+ const char *addr;
+ char *name;
+};
+
+struct HPMArgData {
+ unsigned int pluginID;
+ char *name;/* e.g. "--my-arg","-v","--whatever" */
+ void (*help) (void);/* to display when --help is used */
+ void (*func) (char *param);/* NULL when no param is available */
+ bool has_param;/* because of the weird "--arg<space>param" instead of the "--arg=param" */
+};
+
+struct HPDataOperationStorage {
+ void **HPDataSRCPtr;
+ unsigned int *hdatac;
+};
+/* */
+struct HPConfListenStorage {
+ unsigned int pluginID;
+ char key[HPM_ADDCONF_LENGTH];
+ void (*func) (const char *val);
+};
+
/* Hercules Plugin Manager Interface */
struct HPM_interface {
/* vars */
unsigned int version[2];
bool off;
+ bool hooking;
+ /* hooking */
+ bool force_return;
/* data */
struct hplugin **plugins;
unsigned int plugin_count;
struct hpm_symbol **symbols;
unsigned int symbol_count;
+ /* packet hooking points */
+ struct HPluginPacket *packets[hpPHP_MAX];
+ unsigned int packetsc[hpPHP_MAX];
+ /* plugin file ptr caching */
+ struct HPMFileNameCache *fnames;
+ unsigned int fnamec;
+ /* config listen */
+ struct HPConfListenStorage *confs[HPCT_MAX];
+ unsigned int confsc[HPCT_MAX];
+ /* --command-line */
+ DBMap *arg_db;
/* funcs */
void (*init) (void);
void (*final) (void);
struct hplugin * (*create) (void);
- void (*load) (const char* filename);
+ struct hplugin * (*load) (const char* filename);
void (*unload) (struct hplugin* plugin);
bool (*exists) (const char *filename);
bool (*iscompatible) (char* version);
void (*event) (enum hp_event_types type);
- void *(*import_symbol) (char *);
+ void *(*import_symbol) (char *name, unsigned int pID);
void (*share) (void *, char *);
void (*symbol_defaults) (void);
- void (*config_read) (void);
+ void (*config_read) (const char * const *extra_plugins, int extra_plugins_count);
bool (*populate) (struct hplugin *plugin,const char *filename);
- void (*symbol_defaults_sub) (void);
+ void (*symbol_defaults_sub) (void);//TODO drop
+ char *(*pid2name) (unsigned int pid);
+ unsigned char (*parse_packets) (int fd, enum HPluginPacketHookingPoints point);
+ void (*load_sub) (struct hplugin *plugin);
+ bool (*addhook_sub) (enum HPluginHookType type, const char *target, void *hook, unsigned int pID);
+ bool (*parse_arg) (const char *arg, int* index, char *argv[], bool param);
+ void (*arg_help) (void);
+ int (*arg_db_clear_sub) (DBKey key, DBData *data, va_list args);
+ void (*grabHPData) (struct HPDataOperationStorage *ret, enum HPluginDataTypes type, void *ptr);
+ /* for server-specific HPData e.g. map_session_data */
+ bool (*grabHPDataSub) (struct HPDataOperationStorage *ret, enum HPluginDataTypes type, void *ptr);
+ /* for custom config parsing */
+ bool (*parseConf) (const char *w1, const char *w2, enum HPluginConfType point);
+ /* validates plugin data */
+ bool (*DataCheck) (struct s_HPMDataCheck *src, unsigned int size, char *name);
} HPM_s;
struct HPM_interface *HPM;
void hpm_defaults(void);
-#endif /* _HPM_H_ */
+#endif /* COMMON_HPM_H */
diff --git a/src/common/HPMDataCheck.h b/src/common/HPMDataCheck.h
new file mode 100644
index 000000000..79ec36472
--- /dev/null
+++ b/src/common/HPMDataCheck.h
@@ -0,0 +1,142 @@
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+//
+// NOTE: This file was auto-generated and should never be manually edited,
+// as it will get overwritten.
+#ifndef HPM_DATA_CHECK_H
+#define HPM_DATA_CHECK_H
+
+
+HPExport const struct s_HPMDataCheck HPMDataCheck[] = {
+ #ifdef COMMON_CONF_H
+ { "libconfig_interface", sizeof(struct libconfig_interface) },
+ #else
+ #define COMMON_CONF_H
+ #endif // COMMON_CONF_H
+ #ifdef COMMON_DB_H
+ { "DBData", sizeof(struct DBData) },
+ { "DBIterator", sizeof(struct DBIterator) },
+ { "DBMap", sizeof(struct DBMap) },
+ #else
+ #define COMMON_DB_H
+ #endif // COMMON_DB_H
+ #ifdef COMMON_DES_H
+ { "BIT64", sizeof(struct BIT64) },
+ #else
+ #define COMMON_DES_H
+ #endif // COMMON_DES_H
+ #ifdef COMMON_ERS_H
+ { "eri", sizeof(struct eri) },
+ #else
+ #define COMMON_ERS_H
+ #endif // COMMON_ERS_H
+ #ifdef COMMON_MAPINDEX_H
+ { "mapindex_interface", sizeof(struct mapindex_interface) },
+ #else
+ #define COMMON_MAPINDEX_H
+ #endif // COMMON_MAPINDEX_H
+ #ifdef COMMON_MMO_H
+ { "quest", sizeof(struct quest) },
+ #else
+ #define COMMON_MMO_H
+ #endif // COMMON_MMO_H
+ #ifdef COMMON_SOCKET_H
+ { "socket_interface", sizeof(struct socket_interface) },
+ #else
+ #define COMMON_SOCKET_H
+ #endif // COMMON_SOCKET_H
+ #ifdef COMMON_STRLIB_H
+ { "StringBuf", sizeof(struct StringBuf) },
+ { "s_svstate", sizeof(struct s_svstate) },
+ #else
+ #define COMMON_STRLIB_H
+ #endif // COMMON_STRLIB_H
+ #ifdef COMMON_SYSINFO_H
+ { "sysinfo_interface", sizeof(struct sysinfo_interface) },
+ #else
+ #define COMMON_SYSINFO_H
+ #endif // COMMON_SYSINFO_H
+ #ifdef MAP_ATCOMMAND_H
+ { "AliasInfo", sizeof(struct AliasInfo) },
+ { "atcommand_interface", sizeof(struct atcommand_interface) },
+ #else
+ #define MAP_ATCOMMAND_H
+ #endif // MAP_ATCOMMAND_H
+ #ifdef MAP_BATTLE_H
+ { "Damage", sizeof(struct Damage) },
+ { "battle_interface", sizeof(struct battle_interface) },
+ #else
+ #define MAP_BATTLE_H
+ #endif // MAP_BATTLE_H
+ #ifdef MAP_BUYINGSTORE_H
+ { "buyingstore_interface", sizeof(struct buyingstore_interface) },
+ { "s_buyingstore_item", sizeof(struct s_buyingstore_item) },
+ #else
+ #define MAP_BUYINGSTORE_H
+ #endif // MAP_BUYINGSTORE_H
+ #ifdef MAP_CHRIF_H
+ { "auth_node", sizeof(struct auth_node) },
+ #else
+ #define MAP_CHRIF_H
+ #endif // MAP_CHRIF_H
+ #ifdef MAP_CLIF_H
+ { "clif_interface", sizeof(struct clif_interface) },
+ #else
+ #define MAP_CLIF_H
+ #endif // MAP_CLIF_H
+ #ifdef MAP_ELEMENTAL_H
+ { "elemental_skill", sizeof(struct elemental_skill) },
+ #else
+ #define MAP_ELEMENTAL_H
+ #endif // MAP_ELEMENTAL_H
+ #ifdef MAP_GUILD_H
+ { "eventlist", sizeof(struct eventlist) },
+ { "guardian_data", sizeof(struct guardian_data) },
+ #else
+ #define MAP_GUILD_H
+ #endif // MAP_GUILD_H
+ #ifdef MAP_MAPREG_H
+ { "mapreg_save", sizeof(struct mapreg_save) },
+ #else
+ #define MAP_MAPREG_H
+ #endif // MAP_MAPREG_H
+ #ifdef MAP_MAP_H
+ { "map_data_other_server", sizeof(struct map_data_other_server) },
+ #else
+ #define MAP_MAP_H
+ #endif // MAP_MAP_H
+ #ifdef MAP_PACKETS_STRUCT_H
+ { "EQUIPSLOTINFO", sizeof(struct EQUIPSLOTINFO) },
+ #else
+ #define MAP_PACKETS_STRUCT_H
+ #endif // MAP_PACKETS_STRUCT_H
+ #ifdef MAP_PC_H
+ { "autotrade_vending", sizeof(struct autotrade_vending) },
+ { "item_cd", sizeof(struct item_cd) },
+ #else
+ #define MAP_PC_H
+ #endif // MAP_PC_H
+ #ifdef MAP_SCRIPT_H
+ { "Script_Config", sizeof(struct Script_Config) },
+ { "reg_db", sizeof(struct reg_db) },
+ { "script_interface", sizeof(struct script_interface) },
+ #else
+ #define MAP_SCRIPT_H
+ #endif // MAP_SCRIPT_H
+ #ifdef MAP_SEARCHSTORE_H
+ { "searchstore_interface", sizeof(struct searchstore_interface) },
+ #else
+ #define MAP_SEARCHSTORE_H
+ #endif // MAP_SEARCHSTORE_H
+ #ifdef MAP_SKILL_H
+ { "skill_cd", sizeof(struct skill_cd) },
+ { "skill_condition", sizeof(struct skill_condition) },
+ { "skill_interface", sizeof(struct skill_interface) },
+ { "skill_unit_save", sizeof(struct skill_unit_save) },
+ #else
+ #define MAP_SKILL_H
+ #endif // MAP_SKILL_H
+};
+HPExport unsigned int HPMDataCheckLen = ARRAYLENGTH(HPMDataCheck);
+
+#endif /* HPM_DATA_CHECK_H */
diff --git a/src/common/HPMi.h b/src/common/HPMi.h
index 3cdb804e0..478cfbdd9 100644
--- a/src/common/HPMi.h
+++ b/src/common/HPMi.h
@@ -1,16 +1,18 @@
// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// See the LICENSE file
-#ifndef _HPMi_H_
-#define _HPMi_H_
+#ifndef COMMON_HPMI_H
+#define COMMON_HPMI_H
#include "../common/cbasetypes.h"
-#include "../common/core.h"
#include "../common/console.h"
+#include "../common/core.h"
#include "../common/sql.h"
struct script_state;
struct AtCommandInfo;
+struct socket_data;
+struct map_session_data;
#ifdef WIN32
#define HPExport __declspec(dllexport)
@@ -18,22 +20,11 @@ struct AtCommandInfo;
#define HPExport
#endif
-#ifndef _SHOWMSG_H_
- HPExport void (*ShowMessage) (const char *, ...);
- HPExport void (*ShowStatus) (const char *, ...);
- HPExport void (*ShowSQL) (const char *, ...);
- HPExport void (*ShowInfo) (const char *, ...);
- HPExport void (*ShowNotice) (const char *, ...);
- HPExport void (*ShowWarning) (const char *, ...);
- HPExport void (*ShowDebug) (const char *, ...);
- HPExport void (*ShowError) (const char *, ...);
- HPExport void (*ShowFatalError) (const char *, ...);
-#endif
-
/* after */
#include "../common/showmsg.h"
-#define HPM_VERSION "0.1"
+#define HPM_VERSION "1.0"
+#define HPM_ADDCONF_LENGTH 40
struct hplugin_info {
char* name;
@@ -42,29 +33,155 @@ struct hplugin_info {
char* req_version;
};
-HPExport void *(*import_symbol) (char *name);
+struct s_HPMDataCheck {
+ char *name;
+ unsigned int size;
+};
+
+HPExport void *(*import_symbol) (char *name, unsigned int pID);
HPExport Sql *mysql_handle;
-#define GET_SYMBOL(n) import_symbol(n)
+#define GET_SYMBOL(n) import_symbol((n),HPMi->pid)
-#define SERVER_TYPE_ALL SERVER_TYPE_LOGIN|SERVER_TYPE_CHAR|SERVER_TYPE_MAP
+#define SERVER_TYPE_ALL (SERVER_TYPE_LOGIN|SERVER_TYPE_CHAR|SERVER_TYPE_MAP)
enum hp_event_types {
HPET_INIT,/* server starts */
HPET_FINAL,/* server is shutting down */
HPET_READY,/* server is ready (online) */
+ HPET_POST_FINAL,/* server is done shutting down */
+ HPET_PRE_INIT,/* server is about to start (used to e.g. add custom "--args" handling) */
HPET_MAX,
};
+enum HPluginPacketHookingPoints {
+ hpClif_Parse, ///< map-server (client-map)
+ hpChrif_Parse, ///< map-server (char-map)
+ hpParse_FromMap, ///< char-server (map-char)
+ hpParse_FromLogin, ///< char-server (login-char)
+ hpParse_Char, ///< char-server (client-char)
+ hpParse_FromChar, ///< login-server (char-login)
+ hpParse_Login, ///< login-server (client-login)
+ /* */
+ hpPHP_MAX,
+};
+
+enum HPluginHookType {
+ HOOK_TYPE_PRE,
+ HOOK_TYPE_POST,
+};
+
+enum HPluginDataTypes {
+ HPDT_SESSION,
+ HPDT_MSD,
+ HPDT_NPCD,
+ HPDT_MAP,
+ HPDT_INSTANCE,
+ HPDT_GUILD,
+ HPDT_PARTY,
+};
+
+/* used in macros and conf storage */
+enum HPluginConfType {
+ HPCT_BATTLE, /* battle-conf (map-server */
+ HPCT_MAX,
+};
+
+#define addHookPre(tname,hook) (HPMi->AddHook(HOOK_TYPE_PRE,(tname),(hook),HPMi->pid))
+#define addHookPost(tname,hook) (HPMi->AddHook(HOOK_TYPE_POST,(tname),(hook),HPMi->pid))
+/* need better names ;/ */
+/* will not run the original function after pre-hook processing is complete (other hooks will run) */
+#define hookStop() (HPMi->HookStop(__func__,HPMi->pid))
+#define hookStopped() (HPMi->HookStopped())
+
+#define addArg(name,param,func,help) (HPMi->addArg(HPMi->pid,(name),(param),(func),(help)))
+/* HPData handy redirects */
+/* session[] */
+#define addToSession(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_SESSION,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromSession(ptr,index) (HPMi->getFromHPData(HPDT_SESSION,HPMi->pid,(ptr),(index)))
+#define removeFromSession(ptr,index) (HPMi->removeFromHPData(HPDT_SESSION,HPMi->pid,(ptr),(index)))
+/* map_session_data */
+#define addToMSD(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_MSD,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromMSD(ptr,index) (HPMi->getFromHPData(HPDT_MSD,HPMi->pid,(ptr),(index)))
+#define removeFromMSD(ptr,index) (HPMi->removeFromHPData(HPDT_MSD,HPMi->pid,(ptr),(index)))
+/* npc_data */
+#define addToNPCD(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_NPCD,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromNPCD(ptr,index) (HPMi->getFromHPData(HPDT_NPCD,HPMi->pid,(ptr),(index)))
+#define removeFromNPCD(ptr,index) (HPMi->removeFromHPData(HPDT_NPCD,HPMi->pid,(ptr),(index)))
+/* map_data */
+#define addToMAPD(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_MAP,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromMAPD(ptr,index) (HPMi->getFromHPData(HPDT_MAP,HPMi->pid,(ptr),(index)))
+#define removeFromMAPD(ptr,index) (HPMi->removeFromHPData(HPDT_MAP,HPMi->pid,(ptr),(index)))
+/* party_data */
+#define addToPAD(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_PARTY,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromPAD(ptr,index) (HPMi->getFromHPData(HPDT_PARTY,HPMi->pid,(ptr),(index)))
+#define removeFromPAD(ptr,index) (HPMi->removeFromHPData(HPDT_PARTY,HPMi->pid,(ptr),(index)))
+/* guild */
+#define addToGLD(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_GUILD,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromGLD(ptr,index) (HPMi->getFromHPData(HPDT_GUILD,HPMi->pid,(ptr),(index)))
+#define removeFromGLD(ptr,index) (HPMi->removeFromHPData(HPDT_GUILD,HPMi->pid,(ptr),(index)))
+/* instance_data */
+#define addToINSTD(ptr,data,index,autofree) (HPMi->addToHPData(HPDT_INSTANCE,HPMi->pid,(ptr),(data),(index),(autofree)))
+#define getFromINSTD(ptr,index) (HPMi->getFromHPData(HPDT_INSTANCE,HPMi->pid,(ptr),(index)))
+#define removeFromINSTD(ptr,index) (HPMi->removeFromHPData(HPDT_INSTANCE,HPMi->pid,(ptr),(index)))
+
+/* HPMi->addCommand */
+#define addAtcommand(cname,funcname) \
+ if ( HPMi->addCommand != NULL ) { \
+ HPMi->addCommand(cname,atcommand_ ## funcname); \
+ } else { \
+ ShowWarning("HPM (%s):addAtcommand(\"%s\",%s) failed, addCommand sub is NULL!\n",pinfo.name,cname,# funcname);\
+ }
+/* HPMi->addScript */
+#define addScriptCommand(cname,scinfo,funcname) \
+ if ( HPMi->addScript != NULL ) { \
+ HPMi->addScript(cname,scinfo,buildin_ ## funcname); \
+ } else { \
+ ShowWarning("HPM (%s):addScriptCommand(\"%s\",\"%s\",%s) failed, addScript sub is NULL!\n",pinfo.name,cname,scinfo,# funcname);\
+ }
+/* HPMi->addCPCommand */
+#define addCPCommand(cname,funcname) \
+ if ( HPMi->addCPCommand != NULL ) { \
+ HPMi->addCPCommand(cname,console_parse_ ## funcname); \
+ } else { \
+ ShowWarning("HPM (%s):addCPCommand(\"%s\",%s) failed, addCPCommand sub is NULL!\n",pinfo.name,cname,# funcname);\
+ }
+/* HPMi->addPacket */
+#define addPacket(cmd,len,receive,point) HPMi->addPacket(cmd,len,receive,point,HPMi->pid)
+/* HPMi->addBattleConf */
+#define addBattleConf(bcname,funcname) HPMi->addConf(HPMi->pid,HPCT_BATTLE,bcname,funcname)
+
+/* HPMi->addPCGPermission */
+#define addGroupPermission(pcgname,maskptr) HPMi->addPCGPermission(HPMi->pid,pcgname,&maskptr)
+
/* Hercules Plugin Mananger Include Interface */
HPExport struct HPMi_interface {
+ /* */
+ unsigned int pid;
+ /* */
void (*event[HPET_MAX]) (void);
bool (*addCommand) (char *name, bool (*func)(const int fd, struct map_session_data* sd, const char* command, const char* message,struct AtCommandInfo *info));
bool (*addScript) (char *name, char *args, bool (*func)(struct script_state *st));
void (*addCPCommand) (char *name, CParseFunc func);
+ /* HPM Custom Data */
+ void (*addToHPData) (enum HPluginDataTypes type, unsigned int pluginID, void *ptr, void *data, unsigned int index, bool autofree);
+ void *(*getFromHPData) (enum HPluginDataTypes type, unsigned int pluginID, void *ptr, unsigned int index);
+ void (*removeFromHPData) (enum HPluginDataTypes type, unsigned int pluginID, void *ptr, unsigned int index);
+ /* packet */
+ bool (*addPacket) (unsigned short cmd, unsigned short length, void (*receive)(int fd), unsigned int point, unsigned int pluginID);
+ /* Hooking */
+ bool (*AddHook) (enum HPluginHookType type, const char *target, void *hook, unsigned int pID);
+ void (*HookStop) (const char *func, unsigned int pID);
+ bool (*HookStopped) (void);
+ /* program --arg/-a */
+ bool (*addArg) (unsigned int pluginID, char *name, bool has_param,void (*func) (char *param),void (*help) (void));
+ /* battle-config recv param */
+ bool (*addConf) (unsigned int pluginID, enum HPluginConfType type, char *name, void (*func) (const char *val));
+ /* pc group permission */
+ void (*addPCGPermission) (unsigned int pluginID, char *name, unsigned int *mask);
} HPMi_s;
-#ifndef _HPM_H_
- HPExport struct HPMi_interface *HPMi;
+#ifndef HERCULES_CORE
+HPExport struct HPMi_interface *HPMi;
#endif
-#endif /* _HPMi_H_ */
+#endif /* COMMON_HPMI_H */
diff --git a/src/common/Makefile.in b/src/common/Makefile.in
index 279f82e5f..5dfdd35bd 100644
--- a/src/common/Makefile.in
+++ b/src/common/Makefile.in
@@ -1,61 +1,91 @@
-
-COMMON_OBJ = obj_all/core.o obj_all/socket.o obj_all/timer.o obj_all/db.o obj_all/HPM.o \
- obj_all/nullpo.o obj_all/malloc.o obj_all/showmsg.o obj_all/strlib.o obj_all/utils.o \
- obj_all/grfio.o obj_all/mapindex.o obj_all/ers.o obj_all/md5calc.o \
- obj_all/minicore.o obj_all/minisocket.o obj_all/minimalloc.o obj_all/random.o obj_all/des.o \
- obj_all/conf.o obj_all/thread.o obj_all/mutex.o obj_all/raconf.o obj_all/mempool.o obj_all/console.o \
- obj_all/miniconsole.o
-
-COMMON_H = $(shell ls ../common/*.h)
+# Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+# See the LICENSE file
+
+# @configure_input@
+
+CONFIG_D = ../config
+CONFIG_H = $(wildcard $(CONFIG_D)/*.h) $(wildcard $(CONFIG_D)/*/*.h)
+
+LIBCONFIG_D = ../../3rdparty/libconfig
+LIBCONFIG_OBJ = $(addprefix $(LIBCONFIG_D)/, libconfig.o grammar.o scanctx.o \
+ scanner.o strbuf.o)
+LIBCONFIG_H = $(addprefix $(LIBCONFIG_D)/, libconfig.h grammar.h parsectx.h \
+ scanctx.h scanner.h strbuf.h wincompat.h)
+LIBCONFIG_INCLUDE = -I$(LIBCONFIG_D)
+
+MT19937AR_D = ../../3rdparty/mt19937ar
+MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o
+MT19937AR_H = $(MT19937AR_D)/mt19937ar.h
+MT19937AR_INCLUDE = -I$(MT19937AR_D)
+
+COMMON_SHARED_C = conf.c db.c des.c ers.c grfio.c HPM.c mapindex.c md5calc.c \
+ mutex.c nullpo.c random.c showmsg.c strlib.c sysinfo.c \
+ thread.c timer.c utils.c
+COMMON_C = $(COMMON_SHARED_C)
+COMMON_SHARED_OBJ = $(patsubst %.c,%.o,$(COMMON_SHARED_C))
+COMMON_OBJ = $(addprefix obj_all/, $(COMMON_SHARED_OBJ) \
+ console.o core.o malloc.o socket.o)
+COMMON_MINI_OBJ = $(addprefix obj_all/, $(COMMON_SHARED_OBJ) \
+ miniconsole.o minicore.o minimalloc.o minisocket.o)
+COMMON_C += console.c core.c malloc.c socket.c
+COMMON_H = atomic.h cbasetypes.h conf.h console.h core.h db.h des.h ers.h \
+ grfio.h HPM.h HPMi.h malloc.h mapindex.h md5calc.h mmo.h mutex.h \
+ nullpo.h random.h showmsg.h socket.h spinlock.h sql.h strlib.h \
+ sysinfo.h thread.h timer.h utils.h winapi.h
COMMON_SQL_OBJ = obj_sql/sql.o
COMMON_SQL_H = sql.h
-
-MT19937AR_OBJ = ../../3rdparty/mt19937ar/mt19937ar.o
-MT19937AR_H = ../../3rdparty/mt19937ar/mt19937ar.h
-MT19937AR_INCLUDE = -I../../3rdparty/mt19937ar
-
-LIBCONFIG_OBJ = ../../3rdparty/libconfig/libconfig.o ../../3rdparty/libconfig/grammar.o \
- ../../3rdparty/libconfig/scanctx.o ../../3rdparty/libconfig/scanner.o ../../3rdparty/libconfig/strbuf.o
-LIBCONFIG_H = ../../3rdparty/libconfig/libconfig.h ../../3rdparty/libconfig/grammar.h \
- ../../3rdparty/libconfig/parsectx.h ../../3rdparty/libconfig/scanctx.h ../../3rdparty/libconfig/scanner.h \
- ../../3rdparty/libconfig/strbuf.h ../../3rdparty/libconfig/wincompat.h
-LIBCONFIG_INCLUDE = -I../../3rdparty/libconfig
+COMMON_C += sql.c
+SYSINFO_INC = sysinfo.inc
HAVE_MYSQL=@HAVE_MYSQL@
ifeq ($(HAVE_MYSQL),yes)
- ALL_DEPENDS=sql
- SQL_DEPENDS=common common_sql
+ SQL_DEPENDS=common common_sql common_mini
else
SQL_DEPENDS=needs_mysql
endif
@SET_MAKE@
+CC = @CC@
+export CC
+
#####################################################################
-.PHONY : all sql clean help
+.PHONY: all sql common common_sql common_mini clean buildclean help
-all: $(ALL_DEPENDS)
+all: sql
sql: $(SQL_DEPENDS)
-clean:
+buildclean:
+ @echo " CLEAN common (build temp files)"
+ @rm -rf *.o obj_all obj_sql sysinfo.inc
+
+clean: buildclean
@echo " CLEAN common"
- @rm -rf *.o obj_all obj_sql
help:
@echo "possible targets are 'sql' 'all' 'clean' 'help'"
- @echo "'sql' - builds object files used in sql servers"
- @echo "'all' - builds all above targets"
- @echo "'clean' - cleans builds and objects"
- @echo "'help' - outputs this message"
+ @echo "'sql' - builds object files used in sql servers"
+ @echo "'all' - builds all above targets"
+ @echo "'clean', 'buildclean' - cleans builds and objects"
+ @echo "'help' - outputs this message"
#####################################################################
+Makefile: Makefile.in
+ @$(MAKE) -C ../.. src/common/Makefile
+
+$(SYSINFO_INC): $(COMMON_C) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H)
+ @echo " MAKE $@"
+ @$(MAKE) -C ../.. sysinfo
+
needs_mysql:
@echo "MySQL not found or disabled by the configure script"
@exit 1
+# object directories
+
obj_all:
@echo " MKDIR obj_all"
@-mkdir obj_all
@@ -64,35 +94,43 @@ obj_sql:
@echo " MKDIR obj_sql"
@-mkdir obj_sql
-obj_all/common.a: $(COMMON_OBJ)
+obj_all/common.a: $(COMMON_OBJ) Makefile
@echo " AR $@"
@@AR@ rcs obj_all/common.a $(COMMON_OBJ)
-obj_sql/common_sql.a: $(COMMON_SQL_OBJ)
+obj_all/common_mini.a: $(COMMON_MINI_OBJ) Makefile
+ @echo " AR $@"
+ @@AR@ rcs obj_all/common_mini.a $(COMMON_MINI_OBJ)
+
+obj_sql/common_sql.a: $(COMMON_SQL_OBJ) Makefile
@echo " AR $@"
@@AR@ rcs obj_sql/common_sql.a $(COMMON_SQL_OBJ)
-
-common: obj_all $(COMMON_OBJ) $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) obj_all/common.a
+common: $(COMMON_OBJ) $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) obj_all/common.a Makefile
-common_sql: obj_sql $(COMMON_SQL_OBJ) obj_sql/common_sql.a
+common_mini: $(COMMON_MINI_OBJ) $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) obj_all/common_mini.a Makefile
-obj_all/%.o: %.c $(COMMON_H) $(MT19937AR_H) $(LIBCONFIG_H)
- @echo " CC $<"
- @@CC@ @CFLAGS@ $(MT19937AR_INCLUDE) $(LIBCONFIG_INCLUDE) @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
+common_sql: $(COMMON_SQL_OBJ) obj_sql/common_sql.a Makefile
+
+obj_all/sysinfo.o: sysinfo.c $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) $(SYSINFO_INC) | obj_all
-obj_all/mini%.o: %.c $(COMMON_H) $(MT19937AR_H) $(LIBCONFIG_H)
+obj_all/%.o: %.c $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | $(SYSINFO_INC) obj_all
@echo " CC $<"
- @@CC@ @CFLAGS@ $(MT19937AR_INCLUDE) $(LIBCONFIG_INCLUDE) -DMINICORE @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
+ @$(CC) @CFLAGS@ @DEFS@ $(MT19937AR_INCLUDE) $(LIBCONFIG_INCLUDE) @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
-obj_sql/%.o: %.c $(COMMON_H) $(COMMON_SQL_H) $(LIBCONFIG_H)
+obj_all/mini%.o: %.c $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | $(SYSINFO_INC) obj_all
@echo " CC $<"
- @@CC@ @CFLAGS@ $(LIBCONFIG_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
+ @$(CC) @CFLAGS@ @DEFS@ $(MT19937AR_INCLUDE) $(LIBCONFIG_INCLUDE) -DMINICORE @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
+obj_sql/%.o: %.c $(COMMON_H) $(COMMON_SQL_H) $(CONFIG_H) $(LIBCONFIG_H) | $(SYSINFO_INC) obj_sql
+ @echo " CC $<"
+ @$(CC) @CFLAGS@ @DEFS@ $(LIBCONFIG_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
# missing object files
-MT19937AR_OBJ:
- @$(MAKE) -C ../../3rdparty/mt19937ar
+$(MT19937AR_OBJ):
+ @echo " MAKE $@"
+ @$(MAKE) -C $(MT19937AR_D)
-LIBCONFIG_OBJ:
- @$(MAKE) -C ../../3rdparty/libconfig
+$(LIBCONFIG_OBJ):
+ @echo " MAKE $@"
+ @$(MAKE) -C $(LIBCONFIG_D)
diff --git a/src/common/atomic.h b/src/common/atomic.h
index b1a4bda92..e73b1c464 100644
--- a/src/common/atomic.h
+++ b/src/common/atomic.h
@@ -1,26 +1,29 @@
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#ifndef _rA_ATOMIC_H_
-#define _rA_ATOMIC_H_
+#ifndef COMMON_ATOMIC_H
+#define COMMON_ATOMIC_H
-// Atomic Operations
+// Atomic Operations
// (Interlocked CompareExchange, Add .. and so on ..)
-//
+//
// Implementation varies / depends on:
// - Architecture
// - Compiler
// - Operating System
//
-// our Abstraction is fully API-Compatible to Microsofts implementation @ NT5.0+
-//
+// our Abstraction is fully API-Compatible to Microsoft's implementation @ NT5.0+
+//
#include "../common/cbasetypes.h"
#if defined(_MSC_VER)
#include "../common/winapi.h"
+// This checks if C/C++ Compiler Version is 18.00
+#if _MSC_VER < 1800
+
#if !defined(_M_X64)
-// When compiling for windows 32bit, the 8byte interlocked operations are not provided by microsoft
+// When compiling for windows 32bit, the 8byte interlocked operations are not provided by Microsoft
// (because they need at least i586 so its not generic enough.. ... )
forceinline int64 InterlockedCompareExchange64(volatile int64 *dest, int64 exch, int64 _cmp){
_asm{
@@ -33,7 +36,7 @@ forceinline int64 InterlockedCompareExchange64(volatile int64 *dest, int64 exch,
mov ecx,4[edi];
mov esi,dest;
- lock CMPXCHG8B [esi];
+ lock CMPXCHG8B [esi];
}
}
@@ -80,9 +83,13 @@ forceinline volatile int64 InterlockedExchange64(volatile int64 *target, int64 v
#endif //endif 32bit windows
+#endif //endif _msc_ver check
+
#elif defined(__GNUC__)
-#if !defined(__x86_64__) && !defined(__i386__)
+// The __sync functions are available on x86 or ARMv6+
+#if !defined(__x86_64__) && !defined(__i386__) \
+ && ( !defined(__ARM_ARCH_VERSION__) || __ARM_ARCH_VERSION__ < 6 )
#error Your Target Platfrom is not supported
#endif
@@ -136,7 +143,7 @@ static forceinline int32 InterlockedExchange(volatile int32 *target, int32 val){
}//end: InterlockedExchange()
-#endif //endif compiler decission
+#endif //endif compiler decision
-#endif
+#endif /* COMMON_ATOMIC_H */
diff --git a/src/common/cbasetypes.h b/src/common/cbasetypes.h
index bfe8bf8f8..18bc0b8cb 100644
--- a/src/common/cbasetypes.h
+++ b/src/common/cbasetypes.h
@@ -1,5 +1,5 @@
-#ifndef _CBASETYPES_H_
-#define _CBASETYPES_H_
+#ifndef COMMON_CBASETYPES_H
+#define COMMON_CBASETYPES_H
/* +--------+-----------+--------+---------+
* | ILP32 | LP64 | ILP64 | (LL)P64 |
@@ -37,11 +37,28 @@
#define CYGWIN
#endif
-// __APPLE__ is the only predefined macro on MacOS X
+// __APPLE__ is the only predefined macro on MacOS
#if defined(__APPLE__)
#define __DARWIN__
#endif
+// Standardize the ARM platform version, if available (the only values we're interested in right now are >= ARMv6)
+#if defined(__ARMV6__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
+ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) // gcc ARMv6
+#define __ARM_ARCH_VERSION__ 6
+#elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7S__) // gcc ARMv7
+#define __ARM_ARCH_VERSION__ 7
+#elif defined(_M_ARM) // MSVC
+#define __ARM_ARCH_VERSION__ _M_ARM
+#else
+#define __ARM_ARCH_VERSION__ 0
+#endif
+
+// Necessary for __NetBSD_Version__ (defined as VVRR00PP00) on NetBSD
+#ifdef __NETBSD__
+#include <sys/param.h>
+#endif // __NETBSD__
+
// 64bit OS
#if defined(_M_IA64) || defined(_M_X64) || defined(_WIN64) || defined(_LP64) || defined(_ILP64) || defined(__LP64__) || defined(__ppc64__)
#define __64BIT__
@@ -77,12 +94,6 @@
// portable printf/scanf format macros and integer definitions
// NOTE: Visual C++ uses <inttypes.h> and <stdint.h> provided in /3rdparty
//////////////////////////////////////////////////////////////////////////
-#ifdef __cplusplus
-#define __STDC_CONSTANT_MACROS
-#define __STDC_FORMAT_MACROS
-#define __STDC_LIMIT_MACROS
-#endif
-
#include <inttypes.h>
#include <stdint.h>
#include <limits.h>
@@ -92,9 +103,9 @@
// (-20 >= USHRT_MAX) returns true
#if defined(__FreeBSD__) && defined(__x86_64)
#undef UCHAR_MAX
-#define UCHAR_MAX (unsigned char)0xff
+#define UCHAR_MAX ((unsigned char)0xff)
#undef USHRT_MAX
-#define USHRT_MAX (unsigned short)0xffff
+#define USHRT_MAX ((unsigned short)0xffff)
#endif
// ILP64 isn't supported, so always 32 bits?
@@ -180,7 +191,7 @@ typedef unsigned long int ppuint32;
#if defined(WIN32) && !defined(MINGW) // does not have a signed size_t
//////////////////////////////
-#if defined(_WIN64) // naive 64bit windows platform
+#if defined(_WIN64) // native 64bit windows platform
typedef __int64 ssize_t;
#else
typedef int ssize_t;
@@ -240,6 +251,7 @@ typedef uintptr_t uintptr;
#endif
#if defined(_MSC_VER) && _MSC_VER > 1200
#define strtoull _strtoui64
+#define strtoll _strtoi64
#endif
// keyword replacement
@@ -254,20 +266,30 @@ typedef uintptr_t uintptr;
#define ra_align(n) __attribute__(( aligned(n) ))
#endif
+// Directives for the (clang) static analyzer
+#ifdef __clang__
+#define analyzer_noreturn __attribute__((analyzer_noreturn))
+#else
+#define analyzer_noreturn
+#endif
-/////////////////////////////
-// for those still not building c++
-#ifndef __cplusplus
-//////////////////////////////
// boolean types for C
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+// MSVC doesn't have stdbool.h yet as of Visual Studio 2012 (MSVC version 17.00)
+// but it will support it in Visual Studio 2013 (MSVC version 18.00)
+// http://blogs.msdn.com/b/vcblog/archive/2013/07/19/c99-library-support-in-visual-studio-2013.aspx
+// GCC and Clang are assumed to be C99 compliant
+#include <stdbool.h> // bool, true, false, __bool_true_false_are_defined
+#endif // ! defined(_MSC_VER) || _MSC_VER >= 1800
+
+#ifndef __bool_true_false_are_defined
+// If stdbool.h is not available or does not define this
typedef char bool;
#define false (1==0)
#define true (1==1)
-
-//////////////////////////////
-#endif // not __cplusplus
-//////////////////////////////
+#define __bool_true_false_are_defined
+#endif // __bool_true_false_are_defined
//////////////////////////////////////////////////////////////////////////
// macro tools
@@ -276,11 +298,13 @@ typedef char bool;
#undef swap
#endif
// hmm only ints?
-//#define swap(a,b) { int temp=a; a=b; b=temp;}
+//#define swap(a,b) { int temp=a; a=b; b=temp;}
// if using macros then something that is type independent
//#define swap(a,b) ((a == b) || ((a ^= b), (b ^= a), (a ^= b)))
// Avoid "value computed is not used" warning and generates the same assembly code
-#define swap(a,b) if (a != b) ((a ^= b), (b ^= a), (a ^= b))
+//#define swap(a,b) if (a != b) ((a ^= b), (b ^= a), (a ^= b))
+// but is vulnerable to 'if (foo) swap(bar, baz); else quux();', causing the else to nest incorrectly.
+#define swap(a,b) do { if ((a) != (b)) { (a) ^= (b); (b) ^= (a); (a) ^= (b); } } while(0)
#if 0 //to be activated soon, more tests needed on how VS works with the macro above
#ifdef WIN32
#undef swap
@@ -300,6 +324,8 @@ typedef char bool;
#endif
#endif
+#define swap_ptr(a,b) do { if ((a) != (b)) (a) = (void*)((intptr_t)(a) ^ (intptr_t)(b)); (b) = (void*)((intptr_t)(a) ^ (intptr_t)(b)); (a) = (void*)((intptr_t)(a) ^ (intptr_t)(b)); } while(0)
+
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
@@ -321,13 +347,26 @@ typedef char bool;
#endif
//////////////////////////////////////////////////////////////////////////
+// Additional printf specifiers
+#if defined(_MSC_VER)
+#define PRIS_PREFIX "I"
+#else // gcc
+#define PRIS_PREFIX "z"
+#endif
+#define PRIdS PRIS_PREFIX "d"
+#define PRIxS PRIS_PREFIX "x"
+#define PRIuS PRIS_PREFIX "u"
+#define PRIXS PRIS_PREFIX "X"
+#define PRIoS PRIS_PREFIX "o"
+
+//////////////////////////////////////////////////////////////////////////
// path separator
#if defined(WIN32)
#define PATHSEP '\\'
#define PATHSEP_STR "\\"
-#elif defined(__APPLE__)
-// FIXME Mac OS X is unix based, is this still correct?
+#elif defined(__APPLE__) && !defined(__MACH__)
+// __MACH__ indicates OS X ( http://sourceforge.net/p/predef/wiki/OperatingSystems/ )
#define PATHSEP ':'
#define PATHSEP_STR ":"
#else
@@ -336,23 +375,6 @@ typedef char bool;
#endif
//////////////////////////////////////////////////////////////////////////
-// Assert
-
-#if ! defined(Assert)
-#if defined(RELEASE)
-#define Assert(EX)
-#else
-// extern "C" {
-#include <assert.h>
-// }
-#if !defined(DEFCPP) && defined(WIN32) && !defined(MINGW)
-#include <crtdbg.h>
-#endif
-#define Assert(EX) assert(EX)
-#endif
-#endif /* ! defined(Assert) */
-
-//////////////////////////////////////////////////////////////////////////
// Has to be unsigned to avoid problems in some systems
// Problems arise when these functions expect an argument in the range [0,256[ and are fed a signed char.
#include <ctype.h>
@@ -388,7 +410,7 @@ typedef char bool;
//////////////////////////////////////////////////////////////////////////
-// Use the preprocessor to 'stringify' stuff (concert to a string).
+// Use the preprocessor to 'stringify' stuff (convert to a string).
// example:
// #define TESTE blabla
// QUOTE(TESTE) -> "TESTE"
@@ -397,26 +419,11 @@ typedef char bool;
#define EXPAND_AND_QUOTE(x) QUOTE(x)
-//////////////////////////////////////////////////////////////////////////
-// Set a pointer variable to a pointer value.
-#ifdef __cplusplus
-template <typename T1, typename T2>
-void SET_POINTER(T1*&var, T2* p)
-{
- var = static_cast<T1*>(p);
-}
-template <typename T1, typename T2>
-void SET_FUNCPOINTER(T1& var, T2 p)
-{
- char ASSERT_POINTERSIZE[sizeof(T1) == sizeof(void*) && sizeof(T2) == sizeof(void*)?1:-1];// 1 if true, -1 if false
- union{ T1 out; T2 in; } tmp;// /!\ WARNING casting a pointer to a function pointer is against the C++ standard
- tmp.in = p;
- var = tmp.out;
-}
+/* pointer size fix which fixes several gcc warnings */
+#ifdef __64BIT__
+ #define h64BPTRSIZE(y) ((intptr)(y))
#else
-#define SET_POINTER(var,p) (var) = (p)
-#define SET_FUNCPOINTER(var,p) (var) = (p)
+ #define h64BPTRSIZE(y) (y)
#endif
-
-#endif /* _CBASETYPES_H_ */
+#endif /* COMMON_CBASETYPES_H */
diff --git a/src/common/conf.c b/src/common/conf.c
index 3057bd4dc..46a034497 100644
--- a/src/common/conf.c
+++ b/src/common/conf.c
@@ -1,18 +1,25 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Portions Copyright (c) Athena Dev Teams
+
+#define HERCULES_CORE
#include "conf.h"
-#include "libconfig.h"
+
+#include "../../3rdparty/libconfig/libconfig.h"
#include "../common/showmsg.h" // ShowError
-int conf_read_file(config_t *config, const char *config_filename)
-{
- config_init(config);
- if (!config_read_file(config, config_filename)) {
+/* interface source */
+struct libconfig_interface libconfig_s;
+
+
+int conf_read_file(config_t *config, const char *config_filename) {
+ libconfig->init(config);
+ if (!libconfig->read_file_src(config, config_filename)) {
ShowError("%s:%d - %s\n", config_error_file(config),
config_error_line(config), config_error_text(config));
- config_destroy(config);
+ libconfig->destroy(config);
return 1;
}
return 0;
@@ -21,65 +28,63 @@ int conf_read_file(config_t *config, const char *config_filename)
//
// Functions to copy settings from libconfig/contrib
//
-static void config_setting_copy_simple(config_setting_t *parent, const config_setting_t *src);
-static void config_setting_copy_elem(config_setting_t *parent, const config_setting_t *src);
-static void config_setting_copy_aggregate(config_setting_t *parent, const config_setting_t *src);
-int config_setting_copy(config_setting_t *parent, const config_setting_t *src);
-
-void config_setting_copy_simple(config_setting_t *parent, const config_setting_t *src)
-{
+void config_setting_copy_simple(config_setting_t *parent, const config_setting_t *src) {
if (config_setting_is_aggregate(src)) {
- config_setting_copy_aggregate(parent, src);
+ libconfig->setting_copy_aggregate(parent, src);
}
else {
- config_setting_t *set = config_setting_add(parent, config_setting_name(src), config_setting_type(src));
-
- if (set == NULL)
+ config_setting_t *set;
+
+ if( libconfig->setting_get_member(parent, config_setting_name(src)) != NULL )
+ return;
+
+ if ((set = libconfig->setting_add(parent, config_setting_name(src), config_setting_type(src))) == NULL)
return;
if (CONFIG_TYPE_INT == config_setting_type(src)) {
- config_setting_set_int(set, config_setting_get_int(src));
- config_setting_set_format(set, src->format);
+ libconfig->setting_set_int(set, libconfig->setting_get_int(src));
+ libconfig->setting_set_format(set, src->format);
} else if (CONFIG_TYPE_INT64 == config_setting_type(src)) {
- config_setting_set_int64(set, config_setting_get_int64(src));
- config_setting_set_format(set, src->format);
+ libconfig->setting_set_int64(set, libconfig->setting_get_int64(src));
+ libconfig->setting_set_format(set, src->format);
} else if (CONFIG_TYPE_FLOAT == config_setting_type(src)) {
- config_setting_set_float(set, config_setting_get_float(src));
+ libconfig->setting_set_float(set, libconfig->setting_get_float(src));
} else if (CONFIG_TYPE_STRING == config_setting_type(src)) {
- config_setting_set_string(set, config_setting_get_string(src));
+ libconfig->setting_set_string(set, libconfig->setting_get_string(src));
} else if (CONFIG_TYPE_BOOL == config_setting_type(src)) {
- config_setting_set_bool(set, config_setting_get_bool(src));
+ libconfig->setting_set_bool(set, libconfig->setting_get_bool(src));
}
}
}
-void config_setting_copy_elem(config_setting_t *parent, const config_setting_t *src)
-{
+void config_setting_copy_elem(config_setting_t *parent, const config_setting_t *src) {
config_setting_t *set = NULL;
if (config_setting_is_aggregate(src))
- config_setting_copy_aggregate(parent, src);
+ libconfig->setting_copy_aggregate(parent, src);
else if (CONFIG_TYPE_INT == config_setting_type(src)) {
- set = config_setting_set_int_elem(parent, -1, config_setting_get_int(src));
- config_setting_set_format(set, src->format);
+ set = libconfig->setting_set_int_elem(parent, -1, libconfig->setting_get_int(src));
+ libconfig->setting_set_format(set, src->format);
} else if (CONFIG_TYPE_INT64 == config_setting_type(src)) {
- set = config_setting_set_int64_elem(parent, -1, config_setting_get_int64(src));
- config_setting_set_format(set, src->format);
+ set = libconfig->setting_set_int64_elem(parent, -1, libconfig->setting_get_int64(src));
+ libconfig->setting_set_format(set, src->format);
} else if (CONFIG_TYPE_FLOAT == config_setting_type(src)) {
- config_setting_set_float_elem(parent, -1, config_setting_get_float(src));
+ libconfig->setting_set_float_elem(parent, -1, libconfig->setting_get_float(src));
} else if (CONFIG_TYPE_STRING == config_setting_type(src)) {
- config_setting_set_string_elem(parent, -1, config_setting_get_string(src));
+ libconfig->setting_set_string_elem(parent, -1, libconfig->setting_get_string(src));
} else if (CONFIG_TYPE_BOOL == config_setting_type(src)) {
- config_setting_set_bool_elem(parent, -1, config_setting_get_bool(src));
+ libconfig->setting_set_bool_elem(parent, -1, libconfig->setting_get_bool(src));
}
}
-void config_setting_copy_aggregate(config_setting_t *parent, const config_setting_t *src)
-{
+void config_setting_copy_aggregate(config_setting_t *parent, const config_setting_t *src) {
config_setting_t *newAgg;
int i, n;
- newAgg = config_setting_add(parent, config_setting_name(src), config_setting_type(src));
+ if( libconfig->setting_get_member(parent, config_setting_name(src)) != NULL )
+ return;
+
+ newAgg = libconfig->setting_add(parent, config_setting_name(src), config_setting_type(src));
if (newAgg == NULL)
return;
@@ -88,22 +93,101 @@ void config_setting_copy_aggregate(config_setting_t *parent, const config_settin
for (i = 0; i < n; i++) {
if (config_setting_is_group(src)) {
- config_setting_copy_simple(newAgg, config_setting_get_elem(src, i));
+ libconfig->setting_copy_simple(newAgg, libconfig->setting_get_elem(src, i));
} else {
- config_setting_copy_elem(newAgg, config_setting_get_elem(src, i));
+ libconfig->setting_copy_elem(newAgg, libconfig->setting_get_elem(src, i));
}
}
}
-int config_setting_copy(config_setting_t *parent, const config_setting_t *src)
-{
+int config_setting_copy(config_setting_t *parent, const config_setting_t *src) {
+
if (!config_setting_is_group(parent) && !config_setting_is_list(parent))
return CONFIG_FALSE;
if (config_setting_is_aggregate(src)) {
- config_setting_copy_aggregate(parent, src);
+ libconfig->setting_copy_aggregate(parent, src);
} else {
- config_setting_copy_simple(parent, src);
+ libconfig->setting_copy_simple(parent, src);
}
return CONFIG_TRUE;
}
+
+void libconfig_defaults(void) {
+ libconfig = &libconfig_s;
+
+ libconfig->read = config_read;
+ libconfig->write = config_write;
+ /* */
+ libconfig->set_auto_convert = config_set_auto_convert;
+ libconfig->get_auto_convert = config_get_auto_convert;
+ /* */
+ libconfig->read_string = config_read_string;
+ libconfig->read_file_src = config_read_file;
+ libconfig->write_file = config_write_file;
+ /* */
+ libconfig->set_destructor = config_set_destructor;
+ libconfig->set_include_dir = config_set_include_dir;
+ /* */
+ libconfig->init = config_init;
+ libconfig->destroy = config_destroy;
+ /* */
+ libconfig->setting_get_int = config_setting_get_int;
+ libconfig->setting_get_int64 = config_setting_get_int64;
+ libconfig->setting_get_float = config_setting_get_float;
+ libconfig->setting_get_bool = config_setting_get_bool;
+ libconfig->setting_get_string = config_setting_get_string;
+ /* */
+ libconfig->setting_lookup_int = config_setting_lookup_int;
+ libconfig->setting_lookup_int64 = config_setting_lookup_int64;
+ libconfig->setting_lookup_float = config_setting_lookup_float;
+ libconfig->setting_lookup_bool = config_setting_lookup_bool;
+ libconfig->setting_lookup_string = config_setting_lookup_string;
+ /* */
+ libconfig->setting_set_int = config_setting_set_int;
+ libconfig->setting_set_int64 = config_setting_set_int64;
+ libconfig->setting_set_bool = config_setting_set_bool;
+ libconfig->setting_set_string = config_setting_set_string;
+ /* */
+ libconfig->setting_set_format = config_setting_set_format;
+ libconfig->setting_get_format = config_setting_get_format;
+ /* */
+ libconfig->setting_get_int_elem = config_setting_get_int_elem;
+ libconfig->setting_get_int64_elem = config_setting_get_int64_elem;
+ libconfig->setting_get_float_elem = config_setting_get_float_elem;
+ libconfig->setting_get_bool_elem = config_setting_get_bool_elem;
+ libconfig->setting_get_string_elem = config_setting_get_string_elem;
+ /* */
+ libconfig->setting_set_int_elem = config_setting_set_int_elem;
+ libconfig->setting_set_int64_elem = config_setting_set_int64_elem;
+ libconfig->setting_set_float_elem = config_setting_set_float_elem;
+ libconfig->setting_set_bool_elem = config_setting_set_bool_elem;
+ libconfig->setting_set_string_elem = config_setting_set_string_elem;
+ /* */
+ libconfig->setting_index = config_setting_index;
+ libconfig->setting_length = config_setting_length;
+ /* */
+ libconfig->setting_get_elem = config_setting_get_elem;
+ libconfig->setting_get_member = config_setting_get_member;
+ /* */
+ libconfig->setting_add = config_setting_add;
+ libconfig->setting_remove = config_setting_remove;
+ libconfig->setting_remove_elem = config_setting_remove_elem;
+ /* */
+ libconfig->setting_set_hook = config_setting_set_hook;
+ /* */
+ libconfig->lookup = config_lookup;
+ libconfig->lookup_from = config_lookup_from;
+ /* */
+ libconfig->lookup_int = config_lookup_int;
+ libconfig->lookup_int64 = config_lookup_int64;
+ libconfig->lookup_float = config_lookup_float;
+ libconfig->lookup_bool = config_lookup_bool;
+ libconfig->lookup_string = config_lookup_string;
+ /* those are custom and are from src/common/conf.c */
+ libconfig->read_file = conf_read_file;
+ libconfig->setting_copy_simple = config_setting_copy_simple;
+ libconfig->setting_copy_elem = config_setting_copy_elem;
+ libconfig->setting_copy_aggregate = config_setting_copy_aggregate;
+ libconfig->setting_copy = config_setting_copy;
+}
diff --git a/src/common/conf.h b/src/common/conf.h
index 666853ba6..c232a035c 100644
--- a/src/common/conf.h
+++ b/src/common/conf.h
@@ -1,13 +1,98 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Portions Copyright (c) Athena Dev Teams
-#ifndef _CONF_H_
-#define _CONF_H_
+#ifndef COMMON_CONF_H
+#define COMMON_CONF_H
#include "../common/cbasetypes.h"
-#include "libconfig.h"
-int conf_read_file(config_t *config, const char *config_filename);
-int config_setting_copy(config_setting_t *parent, const config_setting_t *src);
+#include "../../3rdparty/libconfig/libconfig.h"
-#endif // _CONF_H_
+/**
+ * The libconfig interface -- specially for plugins, but we enforce it throughout the core to be consistent
+ **/
+struct libconfig_interface {
+ int (*read) (config_t *config, FILE *stream);
+ void (*write) (const config_t *config, FILE *stream);
+ /* */
+ void (*set_auto_convert) (config_t *config, int flag);
+ int (*get_auto_convert) (const config_t *config);
+ /* */
+ int (*read_string) (config_t *config, const char *str);
+ int (*read_file_src) (config_t *config, const char *filename);
+ int (*write_file) (config_t *config, const char *filename);
+
+ void (*set_destructor) (config_t *config, void (*destructor)(void *));
+ void (*set_include_dir) (config_t *config, const char *include_dir);
+
+ void (*init) (config_t *config);
+ void (*destroy) (config_t *config);
+
+ int (*setting_get_int) (const config_setting_t *setting);
+ long long (*setting_get_int64) (const config_setting_t *setting);
+ double (*setting_get_float) (const config_setting_t *setting);
+
+ int (*setting_get_bool) (const config_setting_t *setting);
+
+ const char * (*setting_get_string) (const config_setting_t *setting);
+
+ int (*setting_lookup_int) (const config_setting_t *setting, const char *name, int *value);
+ int (*setting_lookup_int64) (const config_setting_t *setting, const char *name, long long *value);
+ int (*setting_lookup_float) (const config_setting_t *setting, const char *name, double *value);
+ int (*setting_lookup_bool) (const config_setting_t *setting, const char *name, int *value);
+ int (*setting_lookup_string) (const config_setting_t *setting, const char *name, const char **value);
+ int (*setting_set_int) (config_setting_t *setting ,int value);
+ int (*setting_set_int64) (config_setting_t *setting, long long value);
+ int (*setting_set_float) (config_setting_t *setting, double value);
+ int (*setting_set_bool) (config_setting_t *setting, int value);
+ int (*setting_set_string) (config_setting_t *setting, const char *value);
+
+ int (*setting_set_format) (config_setting_t *setting, short format);
+ short (*setting_get_format) (const config_setting_t *setting);
+
+ int (*setting_get_int_elem) (const config_setting_t *setting, int idx);
+ long long (*setting_get_int64_elem) (const config_setting_t *setting, int idx);
+ double (*setting_get_float_elem) (const config_setting_t *setting, int idx);
+ int (*setting_get_bool_elem) (const config_setting_t *setting, int idx);
+ const char * (*setting_get_string_elem) (const config_setting_t *setting, int idx);
+ config_setting_t * (*setting_set_int_elem) (config_setting_t *setting, int idx, int value);
+ config_setting_t * (*setting_set_int64_elem) (config_setting_t *setting, int idx, long long value);
+ config_setting_t * (*setting_set_float_elem) (config_setting_t *setting, int idx, double value);
+ config_setting_t * (*setting_set_bool_elem) (config_setting_t *setting, int idx, int value);
+ config_setting_t * (*setting_set_string_elem) (config_setting_t *setting, int idx, const char *value);
+
+ int (*setting_index) (const config_setting_t *setting);
+ int (*setting_length) (const config_setting_t *setting);
+
+ config_setting_t * (*setting_get_elem) (const config_setting_t *setting, unsigned int idx);
+ config_setting_t * (*setting_get_member) (const config_setting_t *setting, const char *name);
+
+ config_setting_t * (*setting_add) (config_setting_t *parent, const char *name, int type);
+ int (*setting_remove) (config_setting_t *parent, const char *name);
+
+ int (*setting_remove_elem) (config_setting_t *parent, unsigned int idx);
+ void (*setting_set_hook) (config_setting_t *setting, void *hook);
+
+ config_setting_t * (*lookup) (const config_t *config, const char *path);
+ config_setting_t * (*lookup_from) (config_setting_t *setting, const char *path);
+ int (*lookup_int) (const config_t *config, const char *path, int *value);
+ int (*lookup_int64) (const config_t *config, const char *path, long long *value);
+ int (*lookup_float) (const config_t *config, const char *path, double *value);
+ int (*lookup_bool) (const config_t *config, const char *path, int *value);
+ int (*lookup_string) (const config_t *config, const char *path, const char **value);
+
+ /* those are custom and are from src/common/conf.c */
+ /* Functions to copy settings from libconfig/contrib */
+ int (*read_file) (config_t *config, const char *config_filename);
+ void (*setting_copy_simple) (config_setting_t *parent, const config_setting_t *src);
+ void (*setting_copy_elem) (config_setting_t *parent, const config_setting_t *src);
+ void (*setting_copy_aggregate) (config_setting_t *parent, const config_setting_t *src);
+ int (*setting_copy) (config_setting_t *parent, const config_setting_t *src);
+};
+
+struct libconfig_interface *libconfig;
+
+void libconfig_defaults(void);
+
+#endif // COMMON_CONF_H
diff --git a/src/common/console.c b/src/common/console.c
index 08daec04e..6a82db555 100644
--- a/src/common/console.c
+++ b/src/common/console.c
@@ -2,69 +2,79 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/showmsg.h"
-#include "../common/core.h"
-#include "../config/core.h"
+#define HERCULES_CORE
+
+#include "../config/core.h" // CONSOLE_INPUT, MAX_CONSOLE_INPUT
#include "console.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../common/cbasetypes.h"
+#include "../common/core.h"
+#include "../common/showmsg.h"
+#include "../common/sysinfo.h"
+
#ifndef MINICORE
- #include "../common/ers.h"
- #include "../common/malloc.h"
- #include "../common/atomic.h"
- #include "../common/spinlock.h"
- #include "../common/thread.h"
- #include "../common/mutex.h"
- #include "../common/timer.h"
- #include "../common/strlib.h"
+# include "../common/atomic.h"
+# include "../common/ers.h"
+# include "../common/malloc.h"
+# include "../common/mutex.h"
+# include "../common/spinlock.h"
+# include "../common/sql.h"
+# include "../common/strlib.h"
+# include "../common/thread.h"
+# include "../common/timer.h"
#endif
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef _WIN32
- #include <unistd.h>
+#if !defined(WIN32)
+# include <sys/time.h>
+# include <unistd.h>
#else
- #include "../common/winapi.h" // Console close event handling
+# include "../common/winapi.h" // Console close event handling
+# ifdef CONSOLE_INPUT
+# include <conio.h> /* _kbhit() */
+# endif
#endif
+struct console_interface console_s;
#ifdef CONSOLE_INPUT
- #ifdef _WIN32
- #include <conio.h> /* _kbhit() */
- #endif
+struct console_input_interface console_input_s;
#endif
-struct console_interface console_s;
-
/*======================================
* CORE : Display title
*--------------------------------------*/
void display_title(void) {
- const char* svn = get_svn_revision();
- const char* git = get_git_hash();
+ const char *vcstype = sysinfo->vcstype();
ShowMessage("\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" Hercules Development Team presents "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" _ _ _ "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" | | | | | | "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" | |_| | ___ _ __ ___ _ _| | ___ ___ "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" | _ |/ _ \\ '__/ __| | | | |/ _ \\/ __|"CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" | | | | __/ | | (__| |_| | | __/\\__ \\"CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" \\_| |_/\\___|_| \\___|\\__,_|_|\\___||___/"CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" http://hercules.ws/board/ "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
- ShowMessage(""CL_BG_RED" "CL_BT_WHITE" "CL_BG_RED""CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" Hercules Development Team presents "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" _ _ _ "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" | | | | | | "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" | |_| | ___ _ __ ___ _ _| | ___ ___ "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" | _ |/ _ \\ '__/ __| | | | |/ _ \\/ __| "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" | | | | __/ | | (__| |_| | | __/\\__ \\ "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" \\_| |_/\\___|_| \\___|\\__,_|_|\\___||___/ "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" http://hercules.ws/board/ "CL_CLL""CL_NORMAL"\n");
+ ShowMessage(""CL_BG_RED""CL_BT_WHITE" "CL_CLL""CL_NORMAL"\n");
- if( git[0] != HERC_UNKNOWN_VER )
- ShowInfo("Git Hash: '"CL_WHITE"%s"CL_RESET"'\n", git);
- else if( svn[0] != HERC_UNKNOWN_VER )
- ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'\n", svn);
+ ShowInfo("Hercules %d-bit for %s\n", sysinfo->is64bit() ? 64 : 32, sysinfo->platform());
+ ShowInfo("%s revision (src): '"CL_WHITE"%s"CL_RESET"'\n", vcstype, sysinfo->vcsrevision_src());
+ ShowInfo("%s revision (scripts): '"CL_WHITE"%s"CL_RESET"'\n", vcstype, sysinfo->vcsrevision_scripts());
+ ShowInfo("OS version: '"CL_WHITE"%s"CL_RESET" [%s]'\n", sysinfo->osversion(), sysinfo->arch());
+ ShowInfo("CPU: '"CL_WHITE"%s [%d]"CL_RESET"'\n", sysinfo->cpu(), sysinfo->cpucores());
+ ShowInfo("Compiled with %s\n", sysinfo->compiler());
+ ShowInfo("Compile Flags: %s\n", sysinfo->cflags());
}
#ifdef CONSOLE_INPUT
-#ifdef _WIN32
+#if defined(WIN32)
int console_parse_key_pressed(void) {
return _kbhit();
}
-#else /* _WIN32 */
+#else /* WIN32 */
int console_parse_key_pressed(void) {
struct timeval tv;
fd_set fds;
@@ -79,35 +89,101 @@ int console_parse_key_pressed(void) {
return FD_ISSET(STDIN_FILENO, &fds);
}
#endif /* _WIN32 */
-CPCMD(exit) {
+
+/*======================================
+ * CORE: Console commands
+ *--------------------------------------*/
+
+/**
+ * Stops server
+ **/
+CPCMD_C(exit,server) {
runflag = 0;
}
-CPCMD(ers_report) {
+
+/**
+ * Displays ERS-related statistics (Entry Reusage System)
+ **/
+CPCMD_C(ers_report,server) {
ers_report();
}
-CPCMD(mem_report) {
+
+/**
+ * Displays memory usage
+ **/
+CPCMD_C(mem_report,server) {
memmgr_report(line?atoi(line):0);
}
+
+/**
+ * Displays command list
+ **/
CPCMD(help) {
unsigned int i = 0;
- for ( i = 0; i < console->cmd_list_count; i++ ) {
- if( console->cmd_list[i]->next_count ) {
- ShowInfo("- '"CL_WHITE"%s"CL_RESET"' subs\n",console->cmd_list[i]->cmd);
- console->parse_list_subs(console->cmd_list[i],2);
+ for ( i = 0; i < console->input->cmd_list_count; i++ ) {
+ if( console->input->cmd_list[i]->next_count ) {
+ ShowInfo("- '"CL_WHITE"%s"CL_RESET"' subs\n",console->input->cmd_list[i]->cmd);
+ console->input->parse_list_subs(console->input->cmd_list[i],2);
} else {
- ShowInfo("- '"CL_WHITE"%s"CL_RESET"'\n",console->cmd_list[i]->cmd);
+ ShowInfo("- '"CL_WHITE"%s"CL_RESET"'\n",console->input->cmd_list[i]->cmd);
}
}
}
-/* [Ind/Hercules] */
-CPCMD(malloc_usage) {
+
+/**
+ * [Ind/Hercules]
+ * Displays current malloc usage
+ */
+CPCMD_C(malloc_usage,server) {
unsigned int val = (unsigned int)iMalloc->usage();
ShowInfo("malloc_usage: %.2f MB\n",(double)(val)/1024);
}
+
+/**
+ * Skips an sql update
+ * Usage: sql update skip UPDATE-FILE.sql
+ **/
+CPCMD_C(skip,update) {
+ if( !line ) {
+ ShowDebug("usage example: sql update skip 2013-02-14--16-15.sql\n");
+ return;
+ }
+ Sql_HerculesUpdateSkip(console->input->SQL, line);
+}
+
+/**
+ * Defines a main category
+ * Categories can't be used as commands!
+ * E.G.
+ * sql update skip
+ * 'sql' is the main category
+ * CP_DEF_C(category)
+ **/
#define CP_DEF_C(x) { #x , NULL , NULL, NULL }
+/**
+ * Defines a sub-category
+ * Sub-categories can't be used as commands!
+ * E.G.
+ * sql update skip
+ * 'update' is a sub-category
+ * CP_DEF_C2(command, category)
+ **/
#define CP_DEF_C2(x,y) { #x , NULL , #y, NULL }
-#define CP_DEF_S(x,y) { #x , console_parse_ ## x , #y, NULL }
-#define CP_DEF(x) { #x , console_parse_ ## x , NULL, NULL }
+/**
+ * Defines a command that is inside a category or sub-category
+ * CP_DEF_S(command, category/sub-category)
+ **/
+#define CP_DEF_S(x,y) { #x, CPCMD_C_A(x,y), #y, NULL }
+/**
+ * Defines a command that is _not_ inside any category
+ * CP_DEF_S(command)
+ **/
+#define CP_DEF(x) { #x , CPCMD_A(x), NULL, NULL }
+
+/**
+ * Loads console commands list
+ * See CP_DEF_C, CP_DEF_C2, CP_DEF_S, CP_DEF
+ **/
void console_load_defaults(void) {
struct {
char *name;
@@ -116,16 +192,25 @@ void console_load_defaults(void) {
struct CParseEntry *self;
} default_list[] = {
CP_DEF(help),
+ /**
+ * Server related commands
+ **/
CP_DEF_C(server),
CP_DEF_S(ers_report,server),
CP_DEF_S(mem_report,server),
CP_DEF_S(malloc_usage,server),
CP_DEF_S(exit,server),
+ /**
+ * Sql related commands
+ **/
+ CP_DEF_C(sql),
+ CP_DEF_C2(update,sql),
+ CP_DEF_S(skip,update),
};
unsigned int i, len = ARRAYLENGTH(default_list);
struct CParseEntry *cmd;
- RECREATE(console->cmds,struct CParseEntry *, len);
+ RECREATE(console->input->cmds,struct CParseEntry *, len);
for(i = 0; i < len; i++) {
CREATE(cmd, struct CParseEntry, 1);
@@ -139,12 +224,12 @@ void console_load_defaults(void) {
cmd->next_count = 0;
- console->cmd_count++;
- console->cmds[i] = cmd;
+ console->input->cmd_count++;
+ console->input->cmds[i] = cmd;
default_list[i].self = cmd;
if( !default_list[i].connect ) {
- RECREATE(console->cmd_list,struct CParseEntry *, ++console->cmd_list_count);
- console->cmd_list[console->cmd_list_count - 1] = cmd;
+ RECREATE(console->input->cmd_list,struct CParseEntry *, ++console->input->cmd_list_count);
+ console->input->cmd_list[console->input->cmd_list_count - 1] = cmd;
}
}
@@ -152,16 +237,20 @@ void console_load_defaults(void) {
unsigned int k;
if( !default_list[i].connect )
continue;
- for(k = 0; k < console->cmd_count; k++) {
- if( strcmpi(default_list[i].connect,console->cmds[k]->cmd) == 0 ) {
+ for(k = 0; k < console->input->cmd_count; k++) {
+ if( strcmpi(default_list[i].connect,console->input->cmds[k]->cmd) == 0 ) {
cmd = default_list[i].self;
- RECREATE(console->cmds[k]->u.next, struct CParseEntry *, ++console->cmds[k]->next_count);
- console->cmds[k]->u.next[console->cmds[k]->next_count - 1] = cmd;
+ RECREATE(console->input->cmds[k]->u.next, struct CParseEntry *, ++console->input->cmds[k]->next_count);
+ console->input->cmds[k]->u.next[console->input->cmds[k]->next_count - 1] = cmd;
break;
}
}
}
}
+#undef CP_DEF_C
+#undef CP_DEF_C2
+#undef CP_DEF_S
+#undef CP_DEF
void console_parse_create(char *name, CParseFunc func) {
unsigned int i;
char *tok;
@@ -171,23 +260,23 @@ void console_parse_create(char *name, CParseFunc func) {
safestrncpy(sublist, name, CP_CMD_LENGTH * 5);
tok = strtok(sublist,":");
- for ( i = 0; i < console->cmd_list_count; i++ ) {
- if( strcmpi(tok,console->cmd_list[i]->cmd) == 0 )
+ for ( i = 0; i < console->input->cmd_list_count; i++ ) {
+ if( strcmpi(tok,console->input->cmd_list[i]->cmd) == 0 )
break;
}
- if( i == console->cmd_list_count ) {
- RECREATE(console->cmds,struct CParseEntry *, ++console->cmd_count);
+ if( i == console->input->cmd_list_count ) {
+ RECREATE(console->input->cmds,struct CParseEntry *, ++console->input->cmd_count);
CREATE(cmd, struct CParseEntry, 1);
safestrncpy(cmd->cmd, tok, CP_CMD_LENGTH);
cmd->next_count = 0;
- console->cmds[console->cmd_count - 1] = cmd;
- RECREATE(console->cmd_list,struct CParseEntry *, ++console->cmd_list_count);
- console->cmd_list[console->cmd_list_count - 1] = cmd;
- i = console->cmd_list_count - 1;
+ console->input->cmds[console->input->cmd_count - 1] = cmd;
+ RECREATE(console->input->cmd_list,struct CParseEntry *, ++console->input->cmd_list_count);
+ console->input->cmd_list[console->input->cmd_list_count - 1] = cmd;
+ i = console->input->cmd_list_count - 1;
}
- cmd = console->cmd_list[i];
+ cmd = console->input->cmd_list[i];
while( ( tok = strtok(NULL, ":") ) != NULL ) {
for(i = 0; i < cmd->next_count; i++) {
@@ -196,13 +285,13 @@ void console_parse_create(char *name, CParseFunc func) {
}
if ( i == cmd->next_count ) {
- RECREATE(console->cmds,struct CParseEntry *, ++console->cmd_count);
- CREATE(console->cmds[console->cmd_count-1], struct CParseEntry, 1);
- safestrncpy(console->cmds[console->cmd_count-1]->cmd, tok, CP_CMD_LENGTH);
- console->cmds[console->cmd_count-1]->next_count = 0;
+ RECREATE(console->input->cmds,struct CParseEntry *, ++console->input->cmd_count);
+ CREATE(console->input->cmds[console->input->cmd_count-1], struct CParseEntry, 1);
+ safestrncpy(console->input->cmds[console->input->cmd_count-1]->cmd, tok, CP_CMD_LENGTH);
+ console->input->cmds[console->input->cmd_count-1]->next_count = 0;
RECREATE(cmd->u.next, struct CParseEntry *, ++cmd->next_count);
- cmd->u.next[cmd->next_count - 1] = console->cmds[console->cmd_count-1];
- cmd = console->cmds[console->cmd_count-1];
+ cmd->u.next[cmd->next_count - 1] = console->input->cmds[console->input->cmd_count-1];
+ cmd = console->input->cmds[console->input->cmd_count-1];
continue;
}
@@ -217,7 +306,7 @@ void console_parse_list_subs(struct CParseEntry *cmd, unsigned char depth) {
memset(msg, '-', depth);
snprintf(msg + depth,CP_CMD_LENGTH * 2, " '"CL_WHITE"%s"CL_RESET"'",cmd->u.next[i]->cmd);
ShowInfo("%s subs\n",msg);
- console->parse_list_subs(cmd->u.next[i],depth + 1);
+ console->input->parse_list_subs(cmd->u.next[i],depth + 1);
} else {
memset(msg, '-', depth);
snprintf(msg + depth,CP_CMD_LENGTH * 2, " %s",cmd->u.next[i]->cmd);
@@ -235,21 +324,21 @@ void console_parse_sub(char *line) {
memcpy(bline, line, 200);
tok = strtok(line, " ");
- for ( i = 0; i < console->cmd_list_count; i++ ) {
- if( strcmpi(tok,console->cmd_list[i]->cmd) == 0 )
+ for ( i = 0; i < console->input->cmd_list_count; i++ ) {
+ if( strcmpi(tok,console->input->cmd_list[i]->cmd) == 0 )
break;
}
- if( i == console->cmd_list_count ) {
+ if( i == console->input->cmd_list_count ) {
ShowError("'"CL_WHITE"%s"CL_RESET"' is not a known command, type '"CL_WHITE"help"CL_RESET"' to list all commands\n",line);
return;
}
- cmd = console->cmd_list[i];
+ cmd = console->input->cmd_list[i];
len += snprintf(sublist,CP_CMD_LENGTH * 5,"%s", cmd->cmd) + 1;
- if( cmd->next_count == 0 && console->cmd_list[i]->u.func ) {
+ if( cmd->next_count == 0 && console->input->cmd_list[i]->u.func ) {
char *r = NULL;
if( (tok = strtok(NULL, " ")) ) {
r = bline;
@@ -266,7 +355,7 @@ void console_parse_sub(char *line) {
if( strcmpi("help",tok) == 0 ) {
if( cmd->next_count ) {
ShowInfo("- '"CL_WHITE"%s"CL_RESET"' subs\n",sublist);
- console->parse_list_subs(cmd,2);
+ console->input->parse_list_subs(cmd,2);
} else {
ShowError("'"CL_WHITE"%s"CL_RESET"' doesn't possess any subcommands\n",sublist);
}
@@ -288,7 +377,7 @@ void console_parse_sub(char *line) {
cmd = cmd->u.next[i];
len += snprintf(sublist + len,CP_CMD_LENGTH * 5,":%s", cmd->cmd);
}
- ShowError("it is only a category, type '"CL_WHITE"%s help"CL_RESET"' to list its subcommands\n",sublist);
+ ShowError("Is only a category, type '"CL_WHITE"%s help"CL_RESET"' to list its subcommands\n",sublist);
}
}
void console_parse(char* line) {
@@ -307,92 +396,95 @@ void console_parse(char* line) {
}
void *cThread_main(void *x) {
- while( console->ptstate ) {/* loopx */
- if( console->key_pressed() ) {
+ while( console->input->ptstate ) {/* loopx */
+ if( console->input->key_pressed() ) {
char input[MAX_CONSOLE_INPUT];
- console->parse(input);
+ console->input->parse(input);
if( input[0] != '\0' ) {/* did we get something? */
- EnterSpinLock(&console->ptlock);
+ EnterSpinLock(&console->input->ptlock);
if( cinput.count == CONSOLE_PARSE_SIZE ) {
- LeaveSpinLock(&console->ptlock);
+ LeaveSpinLock(&console->input->ptlock);
continue;/* drop */
}
safestrncpy(cinput.queue[cinput.count++],input,MAX_CONSOLE_INPUT);
- LeaveSpinLock(&console->ptlock);
+ LeaveSpinLock(&console->input->ptlock);
}
}
- ramutex_lock( console->ptmutex );
- racond_wait( console->ptcond, console->ptmutex, -1 );
- ramutex_unlock( console->ptmutex );
+ ramutex_lock( console->input->ptmutex );
+ racond_wait( console->input->ptcond, console->input->ptmutex, -1 );
+ ramutex_unlock( console->input->ptmutex );
}
return NULL;
}
-int console_parse_timer(int tid, unsigned int tick, int id, intptr_t data) {
+int console_parse_timer(int tid, int64 tick, int id, intptr_t data) {
int i;
- EnterSpinLock(&console->ptlock);
+ EnterSpinLock(&console->input->ptlock);
for(i = 0; i < cinput.count; i++) {
- console->parse_sub(cinput.queue[i]);
+ console->input->parse_sub(cinput.queue[i]);
}
cinput.count = 0;
- LeaveSpinLock(&console->ptlock);
- racond_signal(console->ptcond);
+ LeaveSpinLock(&console->input->ptlock);
+ racond_signal(console->input->ptcond);
return 0;
}
void console_parse_final(void) {
- if( console->ptstate ) {
- InterlockedDecrement(&console->ptstate);
- racond_signal(console->ptcond);
+ if( console->input->ptstate ) {
+ InterlockedDecrement(&console->input->ptstate);
+ racond_signal(console->input->ptcond);
/* wait for thread to close */
- rathread_wait(console->pthread, NULL);
-
- racond_destroy(console->ptcond);
- ramutex_destroy(console->ptmutex);
+ rathread_wait(console->input->pthread, NULL);
+
+ racond_destroy(console->input->ptcond);
+ ramutex_destroy(console->input->ptmutex);
}
}
void console_parse_init(void) {
cinput.count = 0;
- console->ptstate = 1;
+ console->input->ptstate = 1;
- InitializeSpinLock(&console->ptlock);
+ InitializeSpinLock(&console->input->ptlock);
- console->ptmutex = ramutex_create();
- console->ptcond = racond_create();
+ console->input->ptmutex = ramutex_create();
+ console->input->ptcond = racond_create();
- if( (console->pthread = rathread_create(console->pthread_main, NULL)) == NULL ){
+ if( (console->input->pthread = rathread_create(console->input->pthread_main, NULL)) == NULL ){
ShowFatalError("console_parse_init: failed to spawn console_parse thread.\n");
exit(EXIT_FAILURE);
}
- iTimer->add_timer_func_list(console->parse_timer, "console_parse_timer");
- iTimer->add_timer_interval(iTimer->gettick() + 1000, console->parse_timer, 0, 0, 500);/* start listening in 1s; re-try every 0.5s */
+ timer->add_func_list(console->input->parse_timer, "console_parse_timer");
+ timer->add_interval(timer->gettick() + 1000, console->input->parse_timer, 0, 0, 500);/* start listening in 1s; re-try every 0.5s */
}
+void console_setSQL(Sql *SQL_handle) {
+ console->input->SQL = SQL_handle;
+}
#endif /* CONSOLE_INPUT */
void console_init (void) {
#ifdef CONSOLE_INPUT
- console->cmd_count = console->cmd_list_count = 0;
- console->load_defaults();
- console->parse_init();
+ console->input->cmd_count = console->input->cmd_list_count = 0;
+ console->input->load_defaults();
+ console->input->parse_init();
#endif
}
void console_final(void) {
#ifdef CONSOLE_INPUT
unsigned int i;
- console->parse_final();
- for( i = 0; i < console->cmd_count; i++ ) {
- if( console->cmds[i]->next_count )
- aFree(console->cmds[i]->u.next);
- aFree(console->cmds[i]);
+ console->input->parse_final();
+ for( i = 0; i < console->input->cmd_count; i++ ) {
+ if( console->input->cmds[i]->next_count )
+ aFree(console->input->cmds[i]->u.next);
+ aFree(console->input->cmds[i]);
}
- aFree(console->cmds);
- aFree(console->cmd_list);
+ aFree(console->input->cmds);
+ aFree(console->input->cmd_list);
#endif
}
void console_defaults(void) {
@@ -401,15 +493,20 @@ void console_defaults(void) {
console->final = console_final;
console->display_title = display_title;
#ifdef CONSOLE_INPUT
- console->parse_init = console_parse_init;
- console->parse_final = console_parse_final;
- console->parse_timer = console_parse_timer;
- console->pthread_main = cThread_main;
- console->parse = console_parse;
- console->parse_sub = console_parse_sub;
- console->key_pressed = console_parse_key_pressed;
- console->load_defaults = console_load_defaults;
- console->parse_list_subs = console_parse_list_subs;
- console->addCommand = console_parse_create;
+ console->input = &console_input_s;
+ console->input->parse_init = console_parse_init;
+ console->input->parse_final = console_parse_final;
+ console->input->parse_timer = console_parse_timer;
+ console->input->pthread_main = cThread_main;
+ console->input->parse = console_parse;
+ console->input->parse_sub = console_parse_sub;
+ console->input->key_pressed = console_parse_key_pressed;
+ console->input->load_defaults = console_load_defaults;
+ console->input->parse_list_subs = console_parse_list_subs;
+ console->input->addCommand = console_parse_create;
+ console->input->setSQL = console_setSQL;
+ console->input->SQL = NULL;
+#else
+ console->input = NULL;
#endif
}
diff --git a/src/common/console.h b/src/common/console.h
index ebce013f7..062d48bbe 100644
--- a/src/common/console.h
+++ b/src/common/console.h
@@ -1,13 +1,16 @@
// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// See the LICENSE file
-#ifndef _CONSOLE_H_
-#define _CONSOLE_H_
+#ifndef COMMON_CONSOLE_H
+#define COMMON_CONSOLE_H
-#include "../common/thread.h"
+#include "../config/core.h" // MAX_CONSOLE_INPUT
+
+#include "../common/cbasetypes.h"
#include "../common/mutex.h"
#include "../common/spinlock.h"
-#include "../config/core.h"
+#include "../common/sql.h"
+#include "../common/thread.h"
/**
* Queue Max
@@ -15,9 +18,21 @@
**/
#define CONSOLE_PARSE_SIZE 10
+/**
+ * Default parsing function abstract prototype
+ **/
typedef void (*CParseFunc)(char *line);
+
+/**
+ * Console parsing function prototypes
+ * CPCMD: Console Parsing CoMmand
+ * x - command
+ * y - category
+ **/
#define CPCMD(x) void console_parse_ ##x (char *line)
#define CPCMD_A(x) console_parse_ ##x
+#define CPCMD_C(x,y) void console_parse_ ##y ##x (char *line)
+#define CPCMD_C_A(x,y) console_parse_ ##y ##x
#define CP_CMD_LENGTH 20
struct CParseEntry {
@@ -34,26 +49,25 @@ struct {
unsigned short count;
} cinput;
-struct console_interface {
- void (*init) (void);
- void (*final) (void);
- void (*display_title) (void);
#ifdef CONSOLE_INPUT
+struct console_input_interface {
/* vars */
SPIN_LOCK ptlock;/* parse thread lock */
- rAthread pthread;/* parse thread */
+ rAthread *pthread;/* parse thread */
volatile int32 ptstate;/* parse thread state */
- ramutex ptmutex;/* parse thread mutex */
- racond ptcond;/* parse thread cond */
+ ramutex *ptmutex;/* parse thread mutex */
+ racond *ptcond;/* parse thread cond */
/* */
struct CParseEntry **cmd_list;
struct CParseEntry **cmds;
unsigned int cmd_count;
unsigned int cmd_list_count;
/* */
+ Sql *SQL;
+ /* */
void (*parse_init) (void);
void (*parse_final) (void);
- int (*parse_timer) (int tid, unsigned int tick, int id, intptr_t data);
+ int (*parse_timer) (int tid, int64 tick, int id, intptr_t data);
void *(*pthread_main) (void *x);
void (*parse) (char* line);
void (*parse_sub) (char* line);
@@ -61,11 +75,22 @@ struct console_interface {
void (*load_defaults) (void);
void (*parse_list_subs) (struct CParseEntry *cmd, unsigned char depth);
void (*addCommand) (char *name, CParseFunc func);
+ void (*setSQL) (Sql *SQL_handle);
+};
+#else
+struct console_input_interface;
#endif
+
+struct console_interface {
+ void (*init) (void);
+ void (*final) (void);
+ void (*display_title) (void);
+
+ struct console_input_interface *input;
};
struct console_interface *console;
void console_defaults(void);
-#endif /* _CONSOLE_H_ */
+#endif /* COMMON_CONSOLE_H */
diff --git a/src/common/core.c b/src/common/core.c
index c53d2243b..99dbc36ec 100644
--- a/src/common/core.c
+++ b/src/common/core.c
@@ -2,32 +2,41 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
+#define HERCULES_CORE
+
+#include "../config/core.h"
+#include "core.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/cbasetypes.h"
+#include "../common/console.h"
+#include "../common/malloc.h"
#include "../common/mmo.h"
+#include "../common/random.h"
#include "../common/showmsg.h"
-#include "../common/malloc.h"
#include "../common/strlib.h"
-#include "core.h"
-#include "../common/console.h"
+#include "../common/sysinfo.h"
#ifndef MINICORE
- #include "../common/db.h"
- #include "../common/socket.h"
- #include "../common/timer.h"
- #include "../common/thread.h"
- #include "../common/mempool.h"
- #include "../common/sql.h"
- #include "../config/core.h"
- #include "../common/HPM.h"
+# include "../common/HPM.h"
+# include "../common/conf.h"
+# include "../common/db.h"
+# include "../common/ers.h"
+# include "../common/socket.h"
+# include "../common/sql.h"
+# include "../common/thread.h"
+# include "../common/timer.h"
+# include "../common/utils.h"
#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
#ifndef _WIN32
-#include <unistd.h>
+# include <unistd.h>
#else
-#include "../common/winapi.h" // Console close event handling
+# include "../common/winapi.h" // Console close event handling
#endif
/// Called when a terminate signal is received.
@@ -51,7 +60,7 @@ char *SERVER_NAME = NULL;
#endif
#ifndef POSIX
-#define compat_signal(signo, func) signal(signo, func)
+#define compat_signal(signo, func) signal((signo), (func))
#else
sigfunc *compat_signal(int signo, sigfunc *func) {
struct sigaction sact, oact;
@@ -75,7 +84,7 @@ sigfunc *compat_signal(int signo, sigfunc *func) {
*--------------------------------------*/
#ifdef _WIN32
static BOOL WINAPI console_handler(DWORD c_event) {
- switch(c_event) {
+ switch(c_event) {
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
@@ -86,11 +95,11 @@ static BOOL WINAPI console_handler(DWORD c_event) {
break;
default:
return FALSE;
- }
- return TRUE;
+ }
+ return TRUE;
}
-static void cevents_init() {
+static void cevents_init(void) {
if (SetConsoleCtrlHandler(console_handler,TRUE)==FALSE)
ShowWarning ("Unable to install the console handler!\n");
}
@@ -149,152 +158,37 @@ void signals_init (void) {
}
#endif
-#ifdef SVNVERSION
-const char *get_svn_revision(void) {
- return EXPAND_AND_QUOTE(SVNVERSION);
-}
-#else// not SVNVERSION
-const char* get_svn_revision(void) {
- static char svn_version_buffer[16] = "";
- FILE *fp;
-
- if( svn_version_buffer[0] != '\0' )
- return svn_version_buffer;
-
- // subversion 1.7 uses a sqlite3 database
- // FIXME this is hackish at best...
- // - ignores database file structure
- // - assumes the data in NODES.dav_cache column ends with "!svn/ver/<revision>/<path>)"
- // - since it's a cache column, the data might not even exist
- if( (fp = fopen(".svn"PATHSEP_STR"wc.db", "rb")) != NULL || (fp = fopen(".."PATHSEP_STR".svn"PATHSEP_STR"wc.db", "rb")) != NULL )
- {
- #ifndef SVNNODEPATH
- //not sure how to handle branches, so i'll leave this overridable define until a better solution comes up
- #define SVNNODEPATH trunk
- #endif
- const char* prefix = "!svn/ver/";
- const char* postfix = "/"EXPAND_AND_QUOTE(SVNNODEPATH)")"; // there should exist only 1 entry like this
- size_t prefix_len = strlen(prefix);
- size_t postfix_len = strlen(postfix);
- size_t i,j,len;
- char* buffer;
-
- // read file to buffer
- fseek(fp, 0, SEEK_END);
- len = ftell(fp);
- buffer = (char*)aMalloc(len + 1);
- fseek(fp, 0, SEEK_SET);
- len = fread(buffer, 1, len, fp);
- buffer[len] = '\0';
- fclose(fp);
-
- // parse buffer
- for( i = prefix_len + 1; i + postfix_len <= len; ++i ) {
- if( buffer[i] != postfix[0] || memcmp(buffer + i, postfix, postfix_len) != 0 )
- continue; // postfix missmatch
- for( j = i; j > 0; --j ) {// skip digits
- if( !ISDIGIT(buffer[j - 1]) )
- break;
- }
- if( memcmp(buffer + j - prefix_len, prefix, prefix_len) != 0 )
- continue; // prefix missmatch
- // done
- snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", atoi(buffer + j));
- break;
- }
- aFree(buffer);
-
- if( svn_version_buffer[0] != '\0' )
- return svn_version_buffer;
- }
-
- // subversion 1.6 and older?
- if ((fp = fopen(".svn/entries", "r")) != NULL) {
- char line[1024];
- int rev;
- // Check the version
- if (fgets(line, sizeof(line), fp)) {
- if(!ISDIGIT(line[0])) {
- // XML File format
- while (fgets(line,sizeof(line),fp))
- if (strstr(line,"revision=")) break;
- if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) {
- snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", rev);
- }
- } else {
- // Bin File format
- if ( fgets(line, sizeof(line), fp) == NULL ) { printf("Can't get bin name\n"); } // Get the name
- if ( fgets(line, sizeof(line), fp) == NULL ) { printf("Can't get entries kind\n"); } // Get the entries kind
- if(fgets(line, sizeof(line), fp)) { // Get the rev numver
- snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", atoi(line));
- }
- }
- }
- fclose(fp);
-
- if( svn_version_buffer[0] != '\0' )
- return svn_version_buffer;
- }
-
- // fallback
- svn_version_buffer[0] = HERC_UNKNOWN_VER;
- return svn_version_buffer;
-}
-#endif
-/* whats our origin */
-#define GIT_ORIGIN "refs/remotes/origin/master"
-/* Grabs the hash from the last time the user updated his working copy (last pull) */
-const char *get_git_hash (void) {
- static char HerculesGitHash[41] = "";//Sha(40) + 1
- FILE *fp;
-
- if( HerculesGitHash[0] != '\0' )
- return HerculesGitHash;
-
- if ( (fp = fopen (".git/"GIT_ORIGIN, "r")) != NULL) {
- char line[64];
- char *rev = malloc (sizeof (char) * 50);
-
- if (fgets (line, sizeof (line), fp) && sscanf (line, "%50s", rev))
- snprintf (HerculesGitHash, sizeof (HerculesGitHash), "%s", rev);
-
- free (rev);
- fclose (fp);
- } else {
- HerculesGitHash[0] = HERC_UNKNOWN_VER;
- }
-
- if (! (*HerculesGitHash)) {
- HerculesGitHash[0] = HERC_UNKNOWN_VER;
- }
-
- return HerculesGitHash;
-}
-// Warning if executed as superuser (root)
+/**
+ * Warns the user if executed as superuser (root)
+ */
void usercheck(void) {
-#ifndef _WIN32
- if (geteuid() == 0) {
- ShowWarning ("You are running Hercules with root privileges, it is not necessary.\n");
- }
-#endif
+ if (sysinfo->is_superuser()) {
+ ShowWarning("You are running Hercules with root privileges, it is not necessary.\n");
+ }
}
+
void core_defaults(void) {
#ifndef MINICORE
hpm_defaults();
+ HCache_defaults();
#endif
+ sysinfo_defaults();
console_defaults();
strlib_defaults();
malloc_defaults();
#ifndef MINICORE
+ libconfig_defaults();
sql_defaults();
timer_defaults();
db_defaults();
+ socket_defaults();
#endif
}
/*======================================
* CORE : MAINROUTINE
*--------------------------------------*/
int main (int argc, char **argv) {
+ int retval = EXIT_SUCCESS;
{// initialize program arguments
char *p1 = SERVER_NAME = argv[0];
char *p2 = p1;
@@ -306,22 +200,33 @@ int main (int argc, char **argv) {
arg_v = argv;
}
core_defaults();
+
+ {
+ int i;
+ for(i = 0; i < argc; i++) {
+ if( strcmp(argv[i], "--script-check") == 0 ) {
+ msg_silent = 0x7; // silence information and status messages
+ }
+ }
+ }
iMalloc->init();// needed for Show* in display_title() [FlavioJS]
-
- console->display_title();
-
-#ifdef MINICORE // minimalist Core
+
+ sysinfo->init();
+
+ if (!(msg_silent&0x1))
+ console->display_title();
+
usercheck();
+
+#ifdef MINICORE // minimalist Core
do_init(argc,argv);
do_final();
#else// not MINICORE
set_server_type();
- usercheck();
Sql_Init();
rathread_init();
- mempool_init();
DB->init();
signals_init();
@@ -329,39 +234,42 @@ int main (int argc, char **argv) {
cevents_init();
#endif
- iTimer->init();
+ timer->init();
+ /* timer first */
+ rnd_init();
+ srand((unsigned int)timer->gettick());
+
console->init();
-
-#ifndef MINICORE
+
+ HCache->init();
+
HPM->init();
-#endif
-
- socket_init();
+
+ sockt->init();
do_init(argc,argv);
{// Main runtime cycle
int next;
while (runflag != CORE_ST_STOP) {
- next = iTimer->do_timer(iTimer->gettick_nocache());
- do_sockets(next);
+ next = timer->perform(timer->gettick_nocache());
+ sockt->perform(next);
}
}
console->final();
- do_final();
-#ifndef MINICORE
+ retval = do_final();
HPM->final();
-#endif
- iTimer->final();
- socket_final();
+ timer->final();
+ sockt->final();
DB->final();
- mempool_final();
rathread_final();
+ ers_final();
#endif
+ //sysinfo->final(); Called by iMalloc->final()
iMalloc->final();
- return 0;
+ return retval;
}
diff --git a/src/common/core.h b/src/common/core.h
index 8fdcdcfc3..a8337e1b9 100644
--- a/src/common/core.h
+++ b/src/common/core.h
@@ -2,16 +2,15 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _CORE_H_
-#define _CORE_H_
+#ifndef COMMON_CORE_H
+#define COMMON_CORE_H
#include "../common/db.h"
#include "../common/mmo.h"
-#include "../config/core.h"
/* so that developers with --enable-debug can raise signals from any section of the code they'd like */
#ifdef DEBUG
- #include <signal.h>
+# include <signal.h>
#endif
extern int arg_c;
@@ -24,20 +23,18 @@ extern int runflag;
extern char *SERVER_NAME;
enum server_types {
- SERVER_TYPE_UNKNOWN = 0x0,
- SERVER_TYPE_LOGIN = 0x1,
- SERVER_TYPE_CHAR = 0x2,
- SERVER_TYPE_MAP = 0x4,
+ SERVER_TYPE_UNKNOWN = 0x0,
+ SERVER_TYPE_LOGIN = 0x1,
+ SERVER_TYPE_CHAR = 0x2,
+ SERVER_TYPE_MAP = 0x4,
};
enum server_types SERVER_TYPE;
-const char *get_svn_revision(void);
-const char *get_git_hash (void);
extern int do_init(int,char**);
extern void set_server_type(void);
extern void do_abort(void);
-extern void do_final(void);
+extern int do_final(void);
/// The main loop continues until runflag is CORE_ST_STOP
enum E_CORE_ST {
@@ -50,4 +47,4 @@ enum E_CORE_ST {
/// If NULL, runflag is set to CORE_ST_STOP instead.
extern void (*shutdown_callback)(void);
-#endif /* _CORE_H_ */
+#endif /* COMMON_CORE_H */
diff --git a/src/common/db.c b/src/common/db.c
index 99c758a8d..f414ca04d 100644
--- a/src/common/db.c
+++ b/src/common/db.c
@@ -3,7 +3,7 @@
* For more information, see LICENCE in the main folder
*
* This file is separated in five sections:
- * (1) Private typedefs, enums, structures, defines and gblobal variables
+ * (1) Private typedefs, enums, structures, defines and global variables
* (2) Private functions
* (3) Protected functions used internally
* (4) Protected functions used in the interface of the database
@@ -47,6 +47,7 @@
* - create a db that organizes itself by splaying
*
* HISTORY:
+ * 2013/08/25 - Added int64/uint64 support for keys [Ind/Hercules]
* 2013/04/27 - Added ERS to speed up iterator memory allocation [Ind/Hercules]
* 2012/03/09 - Added enum for data types (int, uint, void*)
* 2008/02/19 - Fixed db_obj_get not handling deleted entries correctly.
@@ -66,14 +67,18 @@
* @encoding US-ASCII
* @see #db.h
\*****************************************************************************/
+
+#define HERCULES_CORE
+
+#include "db.h"
+
#include <stdio.h>
#include <stdlib.h>
-#include "db.h"
-#include "../common/mmo.h"
+#include "../common/ers.h"
#include "../common/malloc.h"
+#include "../common/mmo.h"
#include "../common/showmsg.h"
-#include "../common/ers.h"
#include "../common/strlib.h"
/*****************************************************************************\
@@ -84,15 +89,15 @@
* DBNColor - Enumeration of colors of the nodes. *
* DBNode - Structure of a node in RED-BLACK trees. *
* struct db_free - Structure that holds a deleted node to be freed. *
- * DBMap_impl - Struture of the database. *
+ * DBMap_impl - Structure of the database. *
* stats - Statistics about the database system. *
\*****************************************************************************/
/**
- * If defined statistics about database nodes, database creating/destruction
- * and function usage are keept and displayed when finalizing the database
+ * If defined statistics about database nodes, database creating/destruction
+ * and function usage are kept and displayed when finalizing the database
* system.
- * WARNING: This adds overhead to every database operation (not shure how much).
+ * WARNING: This adds overhead to every database operation (not sure how much).
* @private
* @see #DBStats
* @see #stats
@@ -140,7 +145,7 @@ typedef struct dbn {
// Other
node_color color;
unsigned deleted : 1;
-} *DBNode;
+} DBNode;
/**
* Structure that holds a deleted node.
@@ -150,8 +155,8 @@ typedef struct dbn {
* @see DBMap_impl#free_list
*/
struct db_free {
- DBNode node;
- DBNode *root;
+ DBNode *node;
+ DBNode **root;
};
/**
@@ -188,12 +193,12 @@ typedef struct DBMap_impl {
unsigned int free_max;
unsigned int free_lock;
// Other
- ERS nodes;
+ ERS *nodes;
DBComparator cmp;
DBHasher hash;
DBReleaser release;
- DBNode ht[HASH_SIZE];
- DBNode cache;
+ DBNode *ht[HASH_SIZE];
+ DBNode *cache;
DBType type;
DBOptions options;
uint32 item_count;
@@ -217,7 +222,7 @@ typedef struct DBIterator_impl {
struct DBIterator vtable;
DBMap_impl* db;
int ht_index;
- DBNode node;
+ DBNode *node;
} DBIterator_impl;
#if defined(DB_ENABLE_STATS)
@@ -236,10 +241,14 @@ static struct db_stats {
uint32 db_uint_alloc;
uint32 db_string_alloc;
uint32 db_istring_alloc;
+ uint32 db_int64_alloc;
+ uint32 db_uint64_alloc;
uint32 db_int_destroy;
uint32 db_uint_destroy;
uint32 db_string_destroy;
uint32 db_istring_destroy;
+ uint32 db_int64_destroy;
+ uint32 db_uint64_destroy;
// Function usage counters
uint32 db_rotate_left;
uint32 db_rotate_right;
@@ -256,10 +265,14 @@ static struct db_stats {
uint32 db_uint_cmp;
uint32 db_string_cmp;
uint32 db_istring_cmp;
+ uint32 db_int64_cmp;
+ uint32 db_uint64_cmp;
uint32 db_int_hash;
uint32 db_uint_hash;
uint32 db_string_hash;
uint32 db_istring_hash;
+ uint32 db_int64_hash;
+ uint32 db_uint64_hash;
uint32 db_release_nothing;
uint32 db_release_key;
uint32 db_release_data;
@@ -298,6 +311,8 @@ static struct db_stats {
uint32 db_i2key;
uint32 db_ui2key;
uint32 db_str2key;
+ uint32 db_i642key;
+ uint32 db_ui642key;
uint32 db_i2data;
uint32 db_ui2data;
uint32 db_ptr2data;
@@ -315,7 +330,7 @@ static struct db_stats {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
-#define DB_COUNTSTAT(token) if (stats. ## token != UINT32_MAX) ++stats. ## token
+#define DB_COUNTSTAT(token) do { if ((stats.token) != UINT32_MAX) ++(stats.token); } while(0)
#else /* !defined(DB_ENABLE_STATS) */
#define DB_COUNTSTAT(token)
#endif /* !defined(DB_ENABLE_STATS) */
@@ -346,12 +361,12 @@ struct eri *db_alloc_ers;
* @param node Node to be rotated
* @param root Pointer to the root of the tree
* @private
- * @see #db_rebalance(DBNode,DBNode *)
- * @see #db_rebalance_erase(DBNode,DBNode *)
+ * @see #db_rebalance(DBNode *,DBNode **)
+ * @see #db_rebalance_erase(DBNode *,DBNode **)
*/
-static void db_rotate_left(DBNode node, DBNode *root)
+static void db_rotate_left(DBNode *node, DBNode **root)
{
- DBNode y = node->right;
+ DBNode *y = node->right;
DB_COUNTSTAT(db_rotate_left);
// put the left of y at the right of node
@@ -377,12 +392,12 @@ static void db_rotate_left(DBNode node, DBNode *root)
* @param node Node to be rotated
* @param root Pointer to the root of the tree
* @private
- * @see #db_rebalance(DBNode,DBNode *)
- * @see #db_rebalance_erase(DBNode,DBNode *)
+ * @see #db_rebalance(DBNode *,DBNode **)
+ * @see #db_rebalance_erase(DBNode *,DBNode **)
*/
-static void db_rotate_right(DBNode node, DBNode *root)
+static void db_rotate_right(DBNode *node, DBNode **root)
{
- DBNode y = node->left;
+ DBNode *y = node->left;
DB_COUNTSTAT(db_rotate_right);
// put the right of y at the left of node
@@ -409,13 +424,13 @@ static void db_rotate_right(DBNode node, DBNode *root)
* @param node Node to be rebalanced
* @param root Pointer to the root of the tree
* @private
- * @see #db_rotate_left(DBNode,DBNode *)
- * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_rotate_left(DBNode *,DBNode **)
+ * @see #db_rotate_right(DBNode *,DBNode **)
* @see #db_obj_put(DBMap*,DBKey,DBData)
*/
-static void db_rebalance(DBNode node, DBNode *root)
+static void db_rebalance(DBNode *node, DBNode **root)
{
- DBNode y;
+ DBNode *y;
DB_COUNTSTAT(db_rebalance);
// Restore the RED-BLACK properties
@@ -471,16 +486,16 @@ static void db_rebalance(DBNode node, DBNode *root)
* @param node Node to be erased from the tree
* @param root Root of the tree
* @private
- * @see #db_rotate_left(DBNode,DBNode *)
- * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_rotate_left(DBNode *,DBNode **)
+ * @see #db_rotate_right(DBNode *,DBNode **)
* @see #db_free_unlock(DBMap_impl*)
*/
-static void db_rebalance_erase(DBNode node, DBNode *root)
+static void db_rebalance_erase(DBNode *node, DBNode **root)
{
- DBNode y = node;
- DBNode x = NULL;
- DBNode x_parent = NULL;
- DBNode w;
+ DBNode *y = node;
+ DBNode *x = NULL;
+ DBNode *x_parent = NULL;
+ DBNode *w;
DB_COUNTSTAT(db_rebalance_erase);
// Select where to change the tree
@@ -496,7 +511,7 @@ static void db_rebalance_erase(DBNode node, DBNode *root)
}
// Remove the node from the tree
- if (y != node) { // both childs existed
+ if (y != node) { // both child existed
// put the left of 'node' in the left of 'y'
node->left->parent = y;
y->left = node->left;
@@ -507,7 +522,7 @@ static void db_rebalance_erase(DBNode node, DBNode *root)
x_parent = y->parent;
if (x) x->parent = y->parent;
y->parent->left = x;
- // put the right of 'node' in 'y'
+ // put the right of 'node' in 'y'
y->right = node->right;
node->right->parent = y;
// 'y' is a direct child of 'node'
@@ -635,8 +650,8 @@ static int db_is_key_null(DBType type, DBKey key)
* @param key Key to be duplicated
* @param Duplicated key
* @private
- * @see #db_free_add(DBMap_impl*,DBNode,DBNode *)
- * @see #db_free_remove(DBMap_impl*,DBNode)
+ * @see #db_free_add(DBMap_impl*,DBNode *,DBNode **)
+ * @see #db_free_remove(DBMap_impl*,DBNode *)
* @see #db_obj_put(DBMap*,DBKey,void *)
* @see #db_dup_key_free(DBMap_impl*,DBKey)
*/
@@ -695,9 +710,9 @@ static void db_dup_key_free(DBMap_impl* db, DBKey key)
* @see DBMap_impl#free_count
* @see DBMap_impl#free_max
* @see #db_obj_remove(DBMap*,DBKey)
- * @see #db_free_remove(DBMap_impl*,DBNode)
+ * @see #db_free_remove(DBMap_impl*,DBNode *)
*/
-static void db_free_add(DBMap_impl* db, DBNode node, DBNode *root)
+static void db_free_add(DBMap_impl* db, DBNode *node, DBNode **root)
{
DBKey old_key;
@@ -744,9 +759,9 @@ static void db_free_add(DBMap_impl* db, DBNode node, DBNode *root)
* @see DBMap_impl#free_list
* @see DBMap_impl#free_count
* @see #db_obj_put(DBMap*,DBKey,DBData)
- * @see #db_free_add(DBMap_impl*,DBNode*,DBNode)
+ * @see #db_free_add(DBMap_impl*,DBNode**,DBNode*)
*/
-static void db_free_remove(DBMap_impl* db, DBNode node)
+static void db_free_remove(DBMap_impl* db, DBNode *node)
{
unsigned int i;
@@ -795,7 +810,7 @@ static void db_free_lock(DBMap_impl* db)
* @param db Target database
* @private
* @see DBMap_impl#free_lock
- * @see #db_free_dbn(DBNode)
+ * @see #db_free_dbn(DBNode*)
* @see #db_lock(DBMap_impl*)
*/
static void db_free_unlock(DBMap_impl* db)
@@ -830,10 +845,14 @@ static void db_free_unlock(DBMap_impl* db)
* db_uint_cmp - Default comparator for DB_UINT databases. *
* db_string_cmp - Default comparator for DB_STRING databases. *
* db_istring_cmp - Default comparator for DB_ISTRING databases. *
+ * db_int64_cmp - Default comparator for DB_INT64 databases. *
+ * db_uint64_cmp - Default comparator for DB_UINT64 databases. *
* db_int_hash - Default hasher for DB_INT databases. *
* db_uint_hash - Default hasher for DB_UINT databases. *
* db_string_hash - Default hasher for DB_STRING databases. *
* db_istring_hash - Default hasher for DB_ISTRING databases. *
+ * db_int64_hash - Default hasher for DB_INT64 databases. *
+ * db_uint64_hash - Default hasher for DB_UINT64 databases. *
* db_release_nothing - Releaser that releases nothing. *
* db_release_key - Releaser that only releases the key. *
* db_release_data - Releaser that only releases the data. *
@@ -921,6 +940,51 @@ static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
}
/**
+ * Default comparator for DB_INT64 databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * <code>maxlen</code> is ignored.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see DBType#DB_INT64
+ * @see #DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_int64_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+ (void)maxlen;//not used
+ DB_COUNTSTAT(db_int64_cmp);
+ if (key1.i64 < key2.i64) return -1;
+ if (key1.i64 > key2.i64) return 1;
+ return 0;
+}
+
+/**
+ * Default comparator for DB_UINT64 databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * <code>maxlen</code> is ignored.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see DBType#DB_UINT64
+ * @see #DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_uint64_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+ (void)maxlen;//not used
+ DB_COUNTSTAT(db_uint64_cmp);
+ if (key1.ui64 < key2.ui64) return -1;
+ if (key1.ui64 > key2.ui64) return 1;
+ return 0;
+}
+
+
+/**
* Default hasher for DB_INT databases.
* Returns the value of the key as an unsigned int.
* <code>maxlen</code> is ignored.
@@ -931,11 +995,11 @@ static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
* @see #DBHasher
* @see #db_default_hash(DBType)
*/
-static unsigned int db_int_hash(DBKey key, unsigned short maxlen)
+static uint64 db_int_hash(DBKey key, unsigned short maxlen)
{
(void)maxlen;//not used
DB_COUNTSTAT(db_int_hash);
- return (unsigned int)key.i;
+ return (uint64)key.i;
}
/**
@@ -949,11 +1013,11 @@ static unsigned int db_int_hash(DBKey key, unsigned short maxlen)
* @see #DBHasher
* @see #db_default_hash(DBType)
*/
-static unsigned int db_uint_hash(DBKey key, unsigned short maxlen)
+static uint64 db_uint_hash(DBKey key, unsigned short maxlen)
{
(void)maxlen;//not used
DB_COUNTSTAT(db_uint_hash);
- return key.ui;
+ return (uint64)key.ui;
}
/**
@@ -965,7 +1029,7 @@ static unsigned int db_uint_hash(DBKey key, unsigned short maxlen)
* @see #DBHasher
* @see #db_default_hash(DBType)
*/
-static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
+static uint64 db_string_hash(DBKey key, unsigned short maxlen)
{
const char *k = key.str;
unsigned int hash = 0;
@@ -980,7 +1044,7 @@ static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
break;
}
- return hash;
+ return (uint64)hash;
}
/**
@@ -991,7 +1055,7 @@ static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
* @see DBType#DB_ISTRING
* @see #db_default_hash(DBType)
*/
-static unsigned int db_istring_hash(DBKey key, unsigned short maxlen)
+static uint64 db_istring_hash(DBKey key, unsigned short maxlen)
{
const char *k = key.str;
unsigned int hash = 0;
@@ -1006,7 +1070,43 @@ static unsigned int db_istring_hash(DBKey key, unsigned short maxlen)
break;
}
- return hash;
+ return (uint64)hash;
+}
+
+/**
+ * Default hasher for DB_INT64 databases.
+ * Returns the value of the key as an unsigned int.
+ * <code>maxlen</code> is ignored.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see DBType#DB_INT64
+ * @see #DBHasher
+ * @see #db_default_hash(DBType)
+ */
+static uint64 db_int64_hash(DBKey key, unsigned short maxlen)
+{
+ (void)maxlen;//not used
+ DB_COUNTSTAT(db_int64_hash);
+ return (uint64)key.i64;
+}
+
+/**
+ * Default hasher for DB_UINT64 databases.
+ * Just returns the value of the key.
+ * <code>maxlen</code> is ignored.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see DBType#DB_UINT64
+ * @see #DBHasher
+ * @see #db_default_hash(DBType)
+ */
+static uint64 db_uint64_hash(DBKey key, unsigned short maxlen)
+{
+ (void)maxlen;//not used
+ DB_COUNTSTAT(db_uint64_hash);
+ return key.ui64;
}
/**
@@ -1055,7 +1155,10 @@ static void db_release_data(DBKey key, DBData data, DBRelease which)
{
(void)key;//not used
DB_COUNTSTAT(db_release_data);
- if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) aFree(data.u.ptr);
+ if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) {
+ aFree(data.u.ptr);
+ data.u.ptr = NULL;
+ }
}
/**
@@ -1074,7 +1177,10 @@ static void db_release_both(DBKey key, DBData data, DBRelease which)
{
DB_COUNTSTAT(db_release_both);
if (which&DB_RELEASE_KEY) aFree((char*)key.str); // needs to be a pointer
- if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) aFree(data.u.ptr);
+ if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) {
+ aFree(data.u.ptr);
+ data.u.ptr = NULL;
+ }
}
/*****************************************************************************\
@@ -1167,8 +1273,8 @@ DBData* dbit_obj_last(DBIterator* self, DBKey* out_key)
DBData* dbit_obj_next(DBIterator* self, DBKey* out_key)
{
DBIterator_impl* it = (DBIterator_impl*)self;
- DBNode node;
- DBNode parent;
+ DBNode *node;
+ DBNode *parent;
struct dbn fake;
DB_COUNTSTAT(dbit_next);
@@ -1243,8 +1349,8 @@ DBData* dbit_obj_next(DBIterator* self, DBKey* out_key)
DBData* dbit_obj_prev(DBIterator* self, DBKey* out_key)
{
DBIterator_impl* it = (DBIterator_impl*)self;
- DBNode node;
- DBNode parent;
+ DBNode *node;
+ DBNode *parent;
struct dbn fake;
DB_COUNTSTAT(dbit_prev);
@@ -1309,7 +1415,7 @@ DBData* dbit_obj_prev(DBIterator* self, DBKey* out_key)
/**
* Returns true if the fetched entry exists.
- * The databases entries might have NULL data, so use this to to test if
+ * The databases entries might have NULL data, so use this to to test if
* the iterator is done.
* @param self Iterator
* @return true if the entry exists
@@ -1326,9 +1432,9 @@ bool dbit_obj_exists(DBIterator* self)
/**
* Removes the current entry from the database.
- * NOTE: {@link DBIterator#exists} will return false until another entry
+ * NOTE: {@link DBIterator#exists} will return false until another entry
* is fetched
- * Puts data of the removed entry in out_data, if out_data is not NULL.
+ * Puts data of the removed entry in out_data, if out_data is not NULL (unless data has been released)
* @param self Iterator
* @param out_data Data of the removed entry.
* @return 1 if entry was removed, 0 otherwise
@@ -1339,7 +1445,7 @@ bool dbit_obj_exists(DBIterator* self)
int dbit_obj_remove(DBIterator* self, DBData *out_data)
{
DBIterator_impl* it = (DBIterator_impl*)self;
- DBNode node;
+ DBNode *node;
int retval = 0;
DB_COUNTSTAT(dbit_remove);
@@ -1349,10 +1455,10 @@ int dbit_obj_remove(DBIterator* self, DBData *out_data)
DBMap_impl* db = it->db;
if( db->cache == node )
db->cache = NULL;
+ db->release(node->key, node->data, DB_RELEASE_DATA);
if( out_data )
memcpy(out_data, &node->data, sizeof(DBData));
retval = 1;
- db->release(node->key, node->data, DB_RELEASE_DATA);
db_free_add(db, node, &db->ht[it->ht_index]);
}
return retval;
@@ -1377,7 +1483,7 @@ void dbit_obj_destroy(DBIterator* self)
/**
* Returns a new iterator for this database.
* The iterator keeps the database locked until it is destroyed.
- * The database will keep functioning normally but will only free internal
+ * The database will keep functioning normally but will only free internal
* memory when unlocked, so destroy the iterator as soon as possible.
* @param self Database
* @return New iterator
@@ -1418,7 +1524,7 @@ static DBIterator* db_obj_iterator(DBMap* self)
static bool db_obj_exists(DBMap* self, DBKey key)
{
DBMap_impl* db = (DBMap_impl*)self;
- DBNode node;
+ DBNode *node;
int c;
bool found = false;
@@ -1469,7 +1575,7 @@ static bool db_obj_exists(DBMap* self, DBKey key)
static DBData* db_obj_get(DBMap* self, DBKey key)
{
DBMap_impl* db = (DBMap_impl*)self;
- DBNode node;
+ DBNode *node;
int c;
DBData *data = NULL;
@@ -1515,7 +1621,7 @@ static DBData* db_obj_get(DBMap* self, DBKey key)
* It puts a maximum of <code>max</code> entries into <code>buf</code>.
* If <code>buf</code> is NULL, it only counts the matches.
* Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
+ * NOTE: if the value returned is greater than <code>max</code>, only the
* first <code>max</code> entries found are put into the buffer.
* @param self Interface of the database
* @param buf Buffer to put the data of the matched entries
@@ -1530,8 +1636,8 @@ static unsigned int db_obj_vgetall(DBMap* self, DBData **buf, unsigned int max,
{
DBMap_impl* db = (DBMap_impl*)self;
unsigned int i;
- DBNode node;
- DBNode parent;
+ DBNode *node;
+ DBNode *parent;
unsigned int ret = 0;
DB_COUNTSTAT(db_vgetall);
@@ -1586,7 +1692,7 @@ static unsigned int db_obj_vgetall(DBMap* self, DBData **buf, unsigned int max,
* It puts a maximum of <code>max</code> entries into <code>buf</code>.
* If <code>buf</code> is NULL, it only counts the matches.
* Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
+ * NOTE: if the value returned is greater than <code>max</code>, only the
* first <code>max</code> entries found are put into the buffer.
* @param self Interface of the database
* @param buf Buffer to put the data of the matched entries
@@ -1614,7 +1720,7 @@ static unsigned int db_obj_getall(DBMap* self, DBData **buf, unsigned int max, D
/**
* Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
+ * If the entry does not exist, an entry is added with the data returned by
* <code>create</code>.
* @param self Interface of the database
* @param key Key that identifies the entry
@@ -1627,8 +1733,8 @@ static unsigned int db_obj_getall(DBMap* self, DBData **buf, unsigned int max, D
static DBData* db_obj_vensure(DBMap* self, DBKey key, DBCreateData create, va_list args)
{
DBMap_impl* db = (DBMap_impl*)self;
- DBNode node;
- DBNode parent = NULL;
+ DBNode *node;
+ DBNode *parent = NULL;
unsigned int hash;
int c = 0;
DBData *data = NULL;
@@ -1696,7 +1802,7 @@ static DBData* db_obj_vensure(DBMap* self, DBKey key, DBCreateData create, va_li
if (db->options&DB_OPT_DUP_KEY) {
node->key = db_dup_key(db, key);
if (db->options&DB_OPT_RELEASE_KEY)
- db->release(key, *data, DB_RELEASE_KEY);
+ db->release(key, node->data, DB_RELEASE_KEY);
} else {
node->key = key;
}
@@ -1713,7 +1819,7 @@ static DBData* db_obj_vensure(DBMap* self, DBKey key, DBCreateData create, va_li
/**
* Just calls {@link DBMap#vensure}.
* Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
+ * If the entry does not exist, an entry is added with the data returned by
* <code>create</code>.
* @param self Interface of the database
* @param key Key that identifies the entry
@@ -1740,7 +1846,7 @@ static DBData* db_obj_ensure(DBMap* self, DBKey key, DBCreateData create, ...)
/**
* Put the data identified by the key in the database.
- * Puts the previous data in out_data, if out_data is not NULL.
+ * Puts the previous data in out_data, if out_data is not NULL. (unless data has been released)
* NOTE: Uses the new key, the old one is released.
* @param self Interface of the database
* @param key Key that identifies the data
@@ -1750,12 +1856,14 @@ static DBData* db_obj_ensure(DBMap* self, DBKey key, DBCreateData create, ...)
* @protected
* @see #db_malloc_dbn(void)
* @see DBMap#put
+ * FIXME: If this method fails shouldn't it return another value?
+ * Other functions rely on this to know if they were able to put something [Panikon]
*/
static int db_obj_put(DBMap* self, DBKey key, DBData data, DBData *out_data)
{
DBMap_impl* db = (DBMap_impl*)self;
- DBNode node;
- DBNode parent = NULL;
+ DBNode *node;
+ DBNode *parent = NULL;
int c = 0, retval = 0;
unsigned int hash;
@@ -1846,20 +1954,20 @@ static int db_obj_put(DBMap* self, DBKey key, DBData data, DBData *out_data)
/**
* Remove an entry from the database.
- * Puts the previous data in out_data, if out_data is not NULL.
- * NOTE: The key (of the database) is released in {@link #db_free_add(DBMap_impl*,DBNode,DBNode *)}.
+ * Puts the previous data in out_data, if out_data is not NULL. (unless data has been released)
+ * NOTE: The key (of the database) is released in {@link #db_free_add(DBMap_impl*,DBNode*,DBNode **)}.
* @param self Interface of the database
* @param key Key that identifies the entry
* @param out_data Previous data if the entry exists
* @return 1 if if the entry already exists, 0 otherwise
* @protected
- * @see #db_free_add(DBMap_impl*,DBNode,DBNode *)
+ * @see #db_free_add(DBMap_impl*,DBNode*,DBNode **)
* @see DBMap#remove
*/
static int db_obj_remove(DBMap* self, DBKey key, DBData *out_data)
{
DBMap_impl* db = (DBMap_impl*)self;
- DBNode node;
+ DBNode *node;
unsigned int hash;
int c = 0, retval = 0;
@@ -1884,10 +1992,10 @@ static int db_obj_remove(DBMap* self, DBKey key, DBData *out_data)
if (!(node->deleted)) {
if (db->cache == node)
db->cache = NULL;
+ db->release(node->key, node->data, DB_RELEASE_DATA);
if (out_data)
memcpy(out_data, &node->data, sizeof(*out_data));
retval = 1;
- db->release(node->key, node->data, DB_RELEASE_DATA);
db_free_add(db, node, &db->ht[hash]);
}
break;
@@ -1916,8 +2024,8 @@ static int db_obj_vforeach(DBMap* self, DBApply func, va_list args)
DBMap_impl* db = (DBMap_impl*)self;
unsigned int i;
int sum = 0;
- DBNode node;
- DBNode parent;
+ DBNode *node;
+ DBNode *parent;
DB_COUNTSTAT(db_vforeach);
if (db == NULL) return 0; // nullpo candidate
@@ -1964,7 +2072,7 @@ static int db_obj_vforeach(DBMap* self, DBApply func, va_list args)
* Apply <code>func</code> to every entry in the database.
* Returns the sum of values returned by func.
* @param self Interface of the database
- * @param func Function to be applyed
+ * @param func Function to be applied
* @param ... Extra arguments for func
* @return Sum of the values returned by func
* @protected
@@ -2002,8 +2110,8 @@ static int db_obj_vclear(DBMap* self, DBApply func, va_list args)
DBMap_impl* db = (DBMap_impl*)self;
int sum = 0;
unsigned int i;
- DBNode node;
- DBNode parent;
+ DBNode *node;
+ DBNode *parent;
DB_COUNTSTAT(db_vclear);
if (db == NULL) return 0; // nullpo candidate
@@ -2061,7 +2169,7 @@ static int db_obj_vclear(DBMap* self, DBApply func, va_list args)
* Before deleting an entry, func is applied to it.
* Releases the key and the data.
* Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
+ * NOTE: This locks the database globally. Any attempt to insert or remove
* a database entry will give an error and be aborted (except for clearing).
* @param self Interface of the database
* @param func Function to be applied to every entry before deleting
@@ -2089,7 +2197,7 @@ static int db_obj_clear(DBMap* self, DBApply func, ...)
* Finalize the database, feeing all the memory it uses.
* Before deleting an entry, func is applied to it.
* Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
+ * NOTE: This locks the database globally. Any attempt to insert or remove
* a database entry will give an error and be aborted (except for clearing).
* @param self Interface of the database
* @param func Function to be applied to every entry before deleting
@@ -2122,6 +2230,8 @@ static int db_obj_vdestroy(DBMap* self, DBApply func, va_list args)
case DB_UINT: DB_COUNTSTAT(db_uint_destroy); break;
case DB_STRING: DB_COUNTSTAT(db_string_destroy); break;
case DB_ISTRING: DB_COUNTSTAT(db_istring_destroy); break;
+ case DB_INT64: DB_COUNTSTAT(db_int64_destroy); break;
+ case DB_UINT64: DB_COUNTSTAT(db_uint64_destroy); break;
}
#endif /* DB_ENABLE_STATS */
db_free_lock(db);
@@ -2142,7 +2252,7 @@ static int db_obj_vdestroy(DBMap* self, DBApply func, va_list args)
* Before deleting an entry, func is applied to it.
* Releases the key and the data.
* Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
+ * NOTE: This locks the database globally. Any attempt to insert or remove
* a database entry will give an error and be aborted.
* @param self Database
* @param func Function to be applied to every entry before deleting
@@ -2241,11 +2351,13 @@ static DBOptions db_obj_options(DBMap* self)
* db_default_cmp - Get the default comparator for a type of database.
* db_default_hash - Get the default hasher for a type of database.
* db_default_release - Get the default releaser for a type of database with the specified options.
- * db_custom_release - Get a releaser that behaves a certains way.
+ * db_custom_release - Get a releaser that behaves a certain way.
* db_alloc - Allocate a new database.
* db_i2key - Manual cast from 'int' to 'DBKey'.
* db_ui2key - Manual cast from 'unsigned int' to 'DBKey'.
* db_str2key - Manual cast from 'unsigned char *' to 'DBKey'.
+ * db_i642key - Manual cast from 'int64' to 'DBKey'.
+ * db_ui642key - Manual cast from 'uin64' to 'DBKey'.
* db_i2data - Manual cast from 'int' to 'DBData'.
* db_ui2data - Manual cast from 'unsigned int' to 'DBData'.
* db_ptr2data - Manual cast from 'void*' to 'DBData'.
@@ -2272,7 +2384,9 @@ DBOptions db_fix_options(DBType type, DBOptions options)
DB_COUNTSTAT(db_fix_options);
switch (type) {
case DB_INT:
- case DB_UINT: // Numeric database, do nothing with the keys
+ case DB_UINT:
+ case DB_INT64:
+ case DB_UINT64: // Numeric database, do nothing with the keys
return (DBOptions)(options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY));
default:
@@ -2292,6 +2406,8 @@ DBOptions db_fix_options(DBType type, DBOptions options)
* @see #db_uint_cmp(DBKey,DBKey,unsigned short)
* @see #db_string_cmp(DBKey,DBKey,unsigned short)
* @see #db_istring_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_int64_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_uint64_cmp(DBKey,DBKey,unsigned short)
*/
DBComparator db_default_cmp(DBType type)
{
@@ -2301,6 +2417,8 @@ DBComparator db_default_cmp(DBType type)
case DB_UINT: return &db_uint_cmp;
case DB_STRING: return &db_string_cmp;
case DB_ISTRING: return &db_istring_cmp;
+ case DB_INT64: return &db_int64_cmp;
+ case DB_UINT64: return &db_uint64_cmp;
default:
ShowError("db_default_cmp: Unknown database type %u\n", type);
return NULL;
@@ -2316,6 +2434,8 @@ DBComparator db_default_cmp(DBType type)
* @see #db_uint_hash(DBKey,unsigned short)
* @see #db_string_hash(DBKey,unsigned short)
* @see #db_istring_hash(DBKey,unsigned short)
+ * @see #db_int64_hash(DBKey,unsigned short)
+ * @see #db_uint64_hash(DBKey,unsigned short)
*/
DBHasher db_default_hash(DBType type)
{
@@ -2325,6 +2445,8 @@ DBHasher db_default_hash(DBType type)
case DB_UINT: return &db_uint_hash;
case DB_STRING: return &db_string_hash;
case DB_ISTRING: return &db_istring_hash;
+ case DB_INT64: return &db_int64_hash;
+ case DB_UINT64: return &db_uint64_hash;
default:
ShowError("db_default_hash: Unknown database type %u\n", type);
return NULL;
@@ -2332,7 +2454,7 @@ DBHasher db_default_hash(DBType type)
}
/**
- * Returns the default releaser for the specified type of database with the
+ * Returns the default releaser for the specified type of database with the
* specified options.
* NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)}
* before choosing the releaser.
@@ -2393,7 +2515,7 @@ DBReleaser db_custom_release(DBRelease which)
* @param line Line of the file where the database is being allocated
* @param type Type of database
* @param options Options of the database
- * @param maxlen Maximum length of the string to be used as key in string
+ * @param maxlen Maximum length of the string to be used as key in string
* databases. If 0, the maximum number of maxlen is used (64K).
* @return The interface of the database
* @public
@@ -2412,6 +2534,8 @@ DBMap* db_alloc(const char *file, const char *func, int line, DBType type, DBOpt
case DB_UINT: DB_COUNTSTAT(db_uint_alloc); break;
case DB_STRING: DB_COUNTSTAT(db_string_alloc); break;
case DB_ISTRING: DB_COUNTSTAT(db_istring_alloc); break;
+ case DB_INT64: DB_COUNTSTAT(db_int64_alloc); break;
+ case DB_UINT64: DB_COUNTSTAT(db_uint64_alloc); break;
}
#endif /* DB_ENABLE_STATS */
db = ers_alloc(db_alloc_ers, struct DBMap_impl);
@@ -2446,7 +2570,7 @@ DBMap* db_alloc(const char *file, const char *func, int line, DBType type, DBOpt
db->free_lock = 0;
/* Other */
snprintf(ers_name, 50, "db_alloc:nodes:%s:%s:%d",func,file,line);
- db->nodes = ers_new(sizeof(struct dbn),ers_name,ERS_OPT_WAIT|ERS_OPT_FREE_NAME);
+ db->nodes = ers_new(sizeof(struct dbn),ers_name,ERS_OPT_WAIT|ERS_OPT_FREE_NAME|ERS_OPT_CLEAN);
db->cmp = DB->default_cmp(type);
db->hash = DB->default_hash(type);
db->release = DB->default_release(type, options);
@@ -2511,6 +2635,36 @@ DBKey db_str2key(const char *key)
}
/**
+ * Manual cast from 'int64' to the union DBKey.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ */
+DBKey db_i642key(int64 key)
+{
+ DBKey ret;
+
+ DB_COUNTSTAT(db_i642key);
+ ret.i64 = key;
+ return ret;
+}
+
+/**
+ * Manual cast from 'uin64' to the union DBKey.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ */
+DBKey db_ui642key(uint64 key)
+{
+ DBKey ret;
+
+ DB_COUNTSTAT(db_ui642key);
+ ret.ui64 = key;
+ return ret;
+}
+
+/**
* Manual cast from 'int' to the struct DBData.
* @param data Data to be casted
* @return The data as a DBData struct
@@ -2609,9 +2763,10 @@ void* db_data2ptr(DBData *data)
* @see #db_final(void)
*/
void db_init(void) {
- db_iterator_ers = ers_new(sizeof(struct DBIterator_impl),"db.c::db_iterator_ers",ERS_OPT_NONE);
- db_alloc_ers = ers_new(sizeof(struct DBMap_impl),"db.c::db_alloc_ers",ERS_OPT_NONE);
+ db_iterator_ers = ers_new(sizeof(struct DBIterator_impl),"db.c::db_iterator_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
+ db_alloc_ers = ers_new(sizeof(struct DBMap_impl),"db.c::db_alloc_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
ers_chunk_size(db_alloc_ers, 50);
+ ers_chunk_size(db_iterator_ers, 10);
DB_COUNTSTAT(db_init);
}
@@ -2631,11 +2786,15 @@ void db_final(void)
"DB_INT : allocated %10u, destroyed %10u\n"
"DB_UINT : allocated %10u, destroyed %10u\n"
"DB_STRING : allocated %10u, destroyed %10u\n"
- "DB_ISTRING : allocated %10u, destroyed %10u\n",
+ "DB_ISTRING : allocated %10u, destroyed %10u\n"
+ "DB_INT64 : allocated %10u, destroyed %10u\n"
+ "DB_UINT64 : allocated %10u, destroyed %10u\n",
stats.db_int_alloc, stats.db_int_destroy,
stats.db_uint_alloc, stats.db_uint_destroy,
stats.db_string_alloc, stats.db_string_destroy,
- stats.db_istring_alloc, stats.db_istring_destroy);
+ stats.db_istring_alloc, stats.db_istring_destroy,
+ stats.db_int64_alloc, stats.db_int64_destroy,
+ stats.db_uint64_alloc, stats.db_uint64_destroy);
ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n"
"db_rotate_left %10u, db_rotate_right %10u,\n"
"db_rebalance %10u, db_rebalance_erase %10u,\n"
@@ -2645,8 +2804,10 @@ void db_final(void)
"db_free_lock %10u, db_free_unlock %10u,\n"
"db_int_cmp %10u, db_uint_cmp %10u,\n"
"db_string_cmp %10u, db_istring_cmp %10u,\n"
+ "db_int64_cmp %10u, db_uint64_cmp %10u,\n"
"db_int_hash %10u, db_uint_hash %10u,\n"
"db_string_hash %10u, db_istring_hash %10u,\n"
+ "db_int64_hash %10u, db_uint64_hash %10u,\n"
"db_release_nothing %10u, db_release_key %10u,\n"
"db_release_data %10u, db_release_both %10u,\n"
"dbit_first %10u, dbit_last %10u,\n"
@@ -2666,6 +2827,7 @@ void db_final(void)
"db_default_release %10u, db_custom_release %10u,\n"
"db_alloc %10u, db_i2key %10u,\n"
"db_ui2key %10u, db_str2key %10u,\n"
+ "db_i642key %10u, db_ui642key %10u,\n"
"db_i2data %10u, db_ui2data %10u,\n"
"db_ptr2data %10u, db_data2i %10u,\n"
"db_data2ui %10u, db_data2ptr %10u,\n"
@@ -2678,8 +2840,10 @@ void db_final(void)
stats.db_free_lock, stats.db_free_unlock,
stats.db_int_cmp, stats.db_uint_cmp,
stats.db_string_cmp, stats.db_istring_cmp,
+ stats.db_int64_cmp, stats.db_uint64_cmp,
stats.db_int_hash, stats.db_uint_hash,
stats.db_string_hash, stats.db_istring_hash,
+ stats.db_int64_hash, stats.db_uint64_hash,
stats.db_release_nothing, stats.db_release_key,
stats.db_release_data, stats.db_release_both,
stats.dbit_first, stats.dbit_last,
@@ -2699,6 +2863,7 @@ void db_final(void)
stats.db_default_release, stats.db_custom_release,
stats.db_alloc, stats.db_i2key,
stats.db_ui2key, stats.db_str2key,
+ stats.db_i642key, stats.db_ui642key,
stats.db_i2data, stats.db_ui2data,
stats.db_ptr2data, stats.db_data2i,
stats.db_data2ui, stats.db_data2ptr,
@@ -2730,20 +2895,26 @@ void linkdb_insert( struct linkdb_node** head, void *key, void* data)
node->data = data;
}
-void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... )
-{
+void linkdb_vforeach( struct linkdb_node** head, LinkDBFunc func, va_list ap) {
struct linkdb_node *node;
if( head == NULL ) return;
node = *head;
while ( node ) {
- va_list args;
- va_start(args, func);
- func( node->key, node->data, args );
- va_end(args);
+ va_list argscopy;
+ va_copy(argscopy, ap);
+ func(node->key, node->data, argscopy);
+ va_end(argscopy);
node = node->next;
}
}
+void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...) {
+ va_list ap;
+ va_start(ap, func);
+ linkdb_vforeach(head, func, ap);
+ va_end(ap);
+}
+
void* linkdb_search( struct linkdb_node** head, void *key)
{
int n = 0;
@@ -2849,4 +3020,7 @@ void db_defaults(void) {
DB->str2key = db_str2key;
DB->ui2data = db_ui2data;
DB->ui2key = db_ui2key;
+ DB->i642key = db_i642key;
+ DB->ui642key = db_ui642key;
+
}
diff --git a/src/common/db.h b/src/common/db.h
index 5a555b2fa..bf59e37d6 100644
--- a/src/common/db.h
+++ b/src/common/db.h
@@ -20,6 +20,7 @@
* - see what functions need or should be added to the database interface *
* *
* HISTORY: *
+ * 2013/08/25 - Added int64/uint64 support for keys *
* 2012/03/09 - Added enum for data types (int, uint, void*) *
* 2007/11/09 - Added an iterator to the database. *
* 2.1 (Athena build #???#) - Portability fix *
@@ -38,12 +39,13 @@
* @encoding US-ASCII *
* @see common#db.c *
\*****************************************************************************/
-#ifndef _DB_H_
-#define _DB_H_
+#ifndef COMMON_DB_H
+#define COMMON_DB_H
-#include "../common/cbasetypes.h"
#include <stdarg.h>
+#include "../common/cbasetypes.h"
+
/*****************************************************************************\
* (1) Section with public typedefs, enums, unions, structures and defines. *
* DBRelease - Enumeration of release options. *
@@ -77,12 +79,14 @@ typedef enum DBRelease {
/**
* Supported types of database.
- * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
* types of databases.
* @param DB_INT Uses int's for keys
* @param DB_UINT Uses unsigned int's for keys
* @param DB_STRING Uses strings for keys.
* @param DB_ISTRING Uses case insensitive strings for keys.
+ * @param DB_INT64 Uses int64's for keys
+ * @param DB_UINT64 Uses uint64's for keys
* @public
* @see #DBOptions
* @see #DBKey
@@ -96,21 +100,23 @@ typedef enum DBType {
DB_INT,
DB_UINT,
DB_STRING,
- DB_ISTRING
+ DB_ISTRING,
+ DB_INT64,
+ DB_UINT64,
} DBType;
/**
- * Bitfield of options that define the behaviour of the database.
- * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
+ * Bitfield of options that define the behavior of the database.
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
* types of databases.
* @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing
* and does not allow NULL keys or NULL data.
- * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY
+ * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY
* is defined, the real key is freed as soon as the entry is added.
* @param DB_OPT_RELEASE_KEY Releases the key.
- * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed
+ * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed
* from the database.
- * WARNING: for funtions that return the data (like DBMap::remove),
+ * WARNING: for functions that return the data (like DBMap::remove),
* a dangling pointer will be returned.
* @param DB_OPT_RELEASE_BOTH Releases both key and data.
* @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database.
@@ -145,6 +151,8 @@ typedef union DBKey {
int i;
unsigned int ui;
const char *str;
+ int64 i64;
+ uint64 ui64;
} DBKey;
/**
@@ -158,7 +166,7 @@ typedef union DBKey {
typedef enum DBDataType {
DB_DATA_INT,
DB_DATA_UINT,
- DB_DATA_PTR
+ DB_DATA_PTR,
} DBDataType;
/**
@@ -180,7 +188,7 @@ typedef struct DBData {
} DBData;
/**
- * Format of functions that create the data for the key when the entry doesn't
+ * Format of functions that create the data for the key when the entry doesn't
* exist in the database yet.
* @param key Key of the database entry
* @param args Extra arguments of the function
@@ -192,9 +200,9 @@ typedef struct DBData {
typedef DBData (*DBCreateData)(DBKey key, va_list args);
/**
- * Format of functions to be applied to an unspecified quantity of entries of
+ * Format of functions to be applied to an unspecified quantity of entries of
* a database.
- * Any function that applies this function to the database will return the sum
+ * Any function that applies this function to the database will return the sum
* of values returned by this function.
* @param key Key of the database entry
* @param data Data of the database entry
@@ -245,7 +253,7 @@ typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen);
* @public
* @see #db_default_hash(DBType)
*/
-typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen);
+typedef uint64 (*DBHasher)(DBKey key, unsigned short maxlen);
/**
* Format of the releaser used by the database system.
@@ -272,7 +280,7 @@ typedef struct DBMap DBMap;
* Database iterator.
* Supports forward iteration, backward iteration and removing entries from the database.
* The iterator is initially positioned before the first entry of the database.
- * While the iterator exists the database is locked internally, so invoke
+ * While the iterator exists the database is locked internally, so invoke
* {@link DBIterator#destroy} as soon as possible.
* @public
* @see #DBMap
@@ -326,7 +334,7 @@ struct DBIterator
/**
* Returns true if the fetched entry exists.
- * The databases entries might have NULL data, so use this to to test if
+ * The databases entries might have NULL data, so use this to to test if
* the iterator is done.
* @param self Iterator
* @return true is the entry exists
@@ -336,7 +344,7 @@ struct DBIterator
/**
* Removes the current entry from the database.
- * NOTE: {@link DBIterator#exists} will return false until another entry
+ * NOTE: {@link DBIterator#exists} will return false until another entry
* is fetched
* Puts data of the removed entry in out_data, if out_data is not NULL.
* @param self Iterator
@@ -357,7 +365,7 @@ struct DBIterator
};
/**
- * Public interface of a database. Only contains funtions.
+ * Public interface of a database. Only contains functions.
* All the functions take the interface as the first argument.
* @public
* @see #db_alloc(const char*,int,DBType,DBOptions,unsigned short)
@@ -367,7 +375,7 @@ struct DBMap {
/**
* Returns a new iterator for this database.
* The iterator keeps the database locked until it is destroyed.
- * The database will keep functioning normally but will only free internal
+ * The database will keep functioning normally but will only free internal
* memory when unlocked, so destroy the iterator as soon as possible.
* @param self Database
* @return New iterator
@@ -399,7 +407,7 @@ struct DBMap {
* It puts a maximum of <code>max</code> entries into <code>buf</code>.
* If <code>buf</code> is NULL, it only counts the matches.
* Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
+ * NOTE: if the value returned is greater than <code>max</code>, only the
* first <code>max</code> entries found are put into the buffer.
* @param self Database
* @param buf Buffer to put the data of the matched entries
@@ -417,7 +425,7 @@ struct DBMap {
* It puts a maximum of <code>max</code> entries into <code>buf</code>.
* If <code>buf</code> is NULL, it only counts the matches.
* Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
+ * NOTE: if the value returned is greater than <code>max</code>, only the
* first <code>max</code> entries found are put into the buffer.
* @param self Database
* @param buf Buffer to put the data of the matched entries
@@ -433,7 +441,7 @@ struct DBMap {
/**
* Just calls {@link DBMap#vensure}.
* Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
+ * If the entry does not exist, an entry is added with the data returned by
* <code>create</code>.
* @param self Database
* @param key Key that identifies the entry
@@ -447,7 +455,7 @@ struct DBMap {
/**
* Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
+ * If the entry does not exist, an entry is added with the data returned by
* <code>create</code>.
* @param self Database
* @param key Key that identifies the entry
@@ -544,7 +552,7 @@ struct DBMap {
* Before deleting an entry, func is applied to it.
* Releases the key and the data.
* Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
+ * NOTE: This locks the database globally. Any attempt to insert or remove
* a database entry will give an error and be aborted (except for clearing).
* @param self Database
* @param func Function to be applied to every entry before deleting
@@ -559,7 +567,7 @@ struct DBMap {
* Finalize the database, feeing all the memory it uses.
* Before deleting an entry, func is applied to it.
* Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
+ * NOTE: This locks the database globally. Any attempt to insert or remove
* a database entry will give an error and be aborted (except for clearing).
* @param self Database
* @param func Function to be applied to every entry before deleting
@@ -598,75 +606,96 @@ struct DBMap {
// For easy access to the common functions.
-#define db_exists(db,k) ( (db)->exists((db),(k)) )
-#define idb_exists(db,k) ( (db)->exists((db),DB->i2key(k)) )
-#define uidb_exists(db,k) ( (db)->exists((db),DB->ui2key(k)) )
-#define strdb_exists(db,k) ( (db)->exists((db),DB->str2key(k)) )
+#define db_exists(db,k) ( (db)->exists((db),(k)) )
+#define idb_exists(db,k) ( (db)->exists((db),DB->i2key(k)) )
+#define uidb_exists(db,k) ( (db)->exists((db),DB->ui2key(k)) )
+#define strdb_exists(db,k) ( (db)->exists((db),DB->str2key(k)) )
+#define i64db_exists(db,k) ( (db)->exists((db),DB->i642key(k)) )
+#define ui64db_exists(db,k) ( (db)->exists((db),DB->ui642key(k)) )
// Get pointer-type data from DBMaps of various key types
-#define db_get(db,k) ( DB->data2ptr((db)->get((db),(k))) )
-#define idb_get(db,k) ( DB->data2ptr((db)->get((db),DB->i2key(k))) )
-#define uidb_get(db,k) ( DB->data2ptr((db)->get((db),DB->ui2key(k))) )
-#define strdb_get(db,k) ( DB->data2ptr((db)->get((db),DB->str2key(k))) )
+#define db_get(db,k) ( DB->data2ptr((db)->get((db),(k))) )
+#define idb_get(db,k) ( DB->data2ptr((db)->get((db),DB->i2key(k))) )
+#define uidb_get(db,k) ( DB->data2ptr((db)->get((db),DB->ui2key(k))) )
+#define strdb_get(db,k) ( DB->data2ptr((db)->get((db),DB->str2key(k))) )
+#define i64db_get(db,k) ( DB->data2ptr((db)->get((db),DB->i642key(k))) )
+#define ui64db_get(db,k) ( DB->data2ptr((db)->get((db),DB->ui642key(k))) )
+
// Get int-type data from DBMaps of various key types
-#define db_iget(db,k) ( DB->data2i((db)->get((db),(k))) )
-#define idb_iget(db,k) ( DB->data2i((db)->get((db),DB->i2key(k))) )
-#define uidb_iget(db,k) ( DB->data2i((db)->get((db),DB->ui2key(k))) )
-#define strdb_iget(db,k) ( DB->data2i((db)->get((db),DB->str2key(k))) )
+#define db_iget(db,k) ( DB->data2i((db)->get((db),(k))) )
+#define idb_iget(db,k) ( DB->data2i((db)->get((db),DB->i2key(k))) )
+#define uidb_iget(db,k) ( DB->data2i((db)->get((db),DB->ui2key(k))) )
+#define strdb_iget(db,k) ( DB->data2i((db)->get((db),DB->str2key(k))) )
+#define i64db_iget(db,k) ( DB->data2i((db)->get((db),DB->i642key(k))) )
+#define ui64db_iget(db,k) ( DB->data2i((db)->get((db),DB->ui642key(k))) )
// Get uint-type data from DBMaps of various key types
-#define db_uiget(db,k) ( DB->data2ui((db)->get((db),(k))) )
-#define idb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->i2key(k))) )
-#define uidb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->ui2key(k))) )
-#define strdb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->str2key(k))) )
+#define db_uiget(db,k) ( DB->data2ui((db)->get((db),(k))) )
+#define idb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->i2key(k))) )
+#define uidb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->ui2key(k))) )
+#define strdb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->str2key(k))) )
+#define i64db_uiget(db,k) ( DB->data2ui((db)->get((db),DB->i642key(k))) )
+#define ui64db_uiget(db,k) ( DB->data2ui((db)->get((db),DB->ui642key(k))) )
// Put pointer-type data into DBMaps of various key types
-#define db_put(db,k,d) ( (db)->put((db),(k),DB->ptr2data(d),NULL) )
-#define idb_put(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ptr2data(d),NULL) )
-#define uidb_put(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ptr2data(d),NULL) )
-#define strdb_put(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ptr2data(d),NULL) )
+#define db_put(db,k,d) ( (db)->put((db),(k),DB->ptr2data(d),NULL) )
+#define idb_put(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ptr2data(d),NULL) )
+#define uidb_put(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ptr2data(d),NULL) )
+#define strdb_put(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ptr2data(d),NULL) )
+#define i64db_put(db,k,d) ( (db)->put((db),DB->i642key(k),DB->ptr2data(d),NULL) )
+#define ui64db_put(db,k,d) ( (db)->put((db),DB->ui642key(k),DB->ptr2data(d),NULL) )
// Put int-type data into DBMaps of various key types
-#define db_iput(db,k,d) ( (db)->put((db),(k),DB->i2data(d),NULL) )
-#define idb_iput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->i2data(d),NULL) )
-#define uidb_iput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->i2data(d),NULL) )
-#define strdb_iput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->i2data(d),NULL) )
+#define db_iput(db,k,d) ( (db)->put((db),(k),DB->i2data(d),NULL) )
+#define idb_iput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->i2data(d),NULL) )
+#define uidb_iput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->i2data(d),NULL) )
+#define strdb_iput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->i2data(d),NULL) )
+#define i64db_iput(db,k,d) ( (db)->put((db),DB->i642key(k),DB->i2data(d),NULL) )
+#define ui64db_iput(db,k,d) ( (db)->put((db),DB->ui642key(k),DB->i2data(d),NULL) )
// Put uint-type data into DBMaps of various key types
-#define db_uiput(db,k,d) ( (db)->put((db),(k),DB->ui2data(d),NULL) )
-#define idb_uiput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ui2data(d),NULL) )
-#define uidb_uiput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ui2data(d),NULL) )
-#define strdb_uiput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ui2data(d),NULL) )
+#define db_uiput(db,k,d) ( (db)->put((db),(k),DB->ui2data(d),NULL) )
+#define idb_uiput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ui2data(d),NULL) )
+#define uidb_uiput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ui2data(d),NULL) )
+#define strdb_uiput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ui2data(d),NULL) )
+#define i64db_uiput(db,k,d) ( (db)->put((db),DB->i642key(k),DB->ui2data(d),NULL) )
+#define ui64db_uiput(db,k,d) ( (db)->put((db),DB->ui642key(k),DB->ui2data(d),NULL) )
// Remove entry from DBMaps of various key types
-#define db_remove(db,k) ( (db)->remove((db),(k),NULL) )
-#define idb_remove(db,k) ( (db)->remove((db),DB->i2key(k),NULL) )
-#define uidb_remove(db,k) ( (db)->remove((db),DB->ui2key(k),NULL) )
-#define strdb_remove(db,k) ( (db)->remove((db),DB->str2key(k),NULL) )
+#define db_remove(db,k) ( (db)->remove((db),(k),NULL) )
+#define idb_remove(db,k) ( (db)->remove((db),DB->i2key(k),NULL) )
+#define uidb_remove(db,k) ( (db)->remove((db),DB->ui2key(k),NULL) )
+#define strdb_remove(db,k) ( (db)->remove((db),DB->str2key(k),NULL) )
+#define i64db_remove(db,k) ( (db)->remove((db),DB->i642key(k),NULL) )
+#define ui64db_remove(db,k) ( (db)->remove((db),DB->ui642key(k),NULL) )
//These are discarding the possible vargs you could send to the function, so those
//that require vargs must not use these defines.
-#define db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),(k),(f))) )
-#define idb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->i2key(k),(f))) )
-#define uidb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->ui2key(k),(f))) )
-#define strdb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->str2key(k),(f))) )
+#define db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),(k),(f))) )
+#define idb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->i2key(k),(f))) )
+#define uidb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->ui2key(k),(f))) )
+#define strdb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->str2key(k),(f))) )
+#define i64db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->i642key(k),(f))) )
+#define ui64db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->ui642key(k),(f))) )
// Database creation and destruction macros
#define idb_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_INT,(opt),sizeof(int))
#define uidb_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_UINT,(opt),sizeof(unsigned int))
#define strdb_alloc(opt,maxlen) DB->alloc(__FILE__,__func__,__LINE__,DB_STRING,(opt),(maxlen))
#define stridb_alloc(opt,maxlen) DB->alloc(__FILE__,__func__,__LINE__,DB_ISTRING,(opt),(maxlen))
+#define i64db_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_INT64,(opt),sizeof(int64))
+#define ui64db_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_UINT64,(opt),sizeof(uint64))
#define db_destroy(db) ( (db)->destroy((db),NULL) )
// Other macros
-#define db_clear(db) ( (db)->clear(db,NULL) )
+#define db_clear(db) ( (db)->clear((db),NULL) )
#define db_size(db) ( (db)->size(db) )
#define db_iterator(db) ( (db)->iterator(db) )
-#define dbi_first(dbi) ( DB->data2ptr((dbi)->first(dbi,NULL)) )
-#define dbi_last(dbi) ( DB->data2ptr((dbi)->last(dbi,NULL)) )
-#define dbi_next(dbi) ( DB->data2ptr((dbi)->next(dbi,NULL)) )
-#define dbi_prev(dbi) ( DB->data2ptr((dbi)->prev(dbi,NULL)) )
-#define dbi_remove(dbi) ( (dbi)->remove(dbi,NULL) )
+#define dbi_first(dbi) ( DB->data2ptr((dbi)->first((dbi),NULL)) )
+#define dbi_last(dbi) ( DB->data2ptr((dbi)->last((dbi),NULL)) )
+#define dbi_next(dbi) ( DB->data2ptr((dbi)->next((dbi),NULL)) )
+#define dbi_prev(dbi) ( DB->data2ptr((dbi)->prev((dbi),NULL)) )
+#define dbi_remove(dbi) ( (dbi)->remove((dbi),NULL) )
#define dbi_exists(dbi) ( (dbi)->exists(dbi) )
#define dbi_destroy(dbi) ( (dbi)->destroy(dbi) )
@@ -682,6 +711,8 @@ struct DBMap {
* db_i2key - Manual cast from 'int' to 'DBKey'. *
* db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
* db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
+ * db_i642key - Manual cast from 'int64' to 'DBKey'. *
+ * db_ui642key - Manual cast from 'uint64' to 'DBKey'. *
* db_i2data - Manual cast from 'int' to 'DBData'. *
* db_ui2data - Manual cast from 'unsigned int' to 'DBData'. *
* db_ptr2data - Manual cast from 'void*' to 'DBData'. *
@@ -728,7 +759,7 @@ DBComparator (*default_cmp) (DBType type);
DBHasher (*default_hash) (DBType type);
/**
- * Returns the default releaser for the specified type of database with the
+ * Returns the default releaser for the specified type of database with the
* specified options.
* NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
* before choosing the releaser
@@ -757,7 +788,7 @@ DBReleaser (*custom_release) (DBRelease which);
/**
* Allocate a new database of the specified type.
- * It uses the default comparator, hasher and releaser of the specified
+ * It uses the default comparator, hasher and releaser of the specified
* database type and fixed options.
* NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
* before creating the database.
@@ -765,7 +796,7 @@ DBReleaser (*custom_release) (DBRelease which);
* @param line Line of the file where the database is being allocated
* @param type Type of database
* @param options Options of the database
- * @param maxlen Maximum length of the string to be used as key in string
+ * @param maxlen Maximum length of the string to be used as key in string
* databases. If 0, the maximum number of maxlen is used (64K).
* @return The interface of the database
* @public
@@ -803,6 +834,22 @@ DBKey (*ui2key) (unsigned int key);
DBKey (*str2key) (const char *key);
/**
+ * Manual cast from 'int64' to the union DBKey.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ */
+DBKey (*i642key) (int64 key);
+
+/**
+ * Manual cast from 'uint64' to the union DBKey.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ */
+DBKey (*ui642key) (uint64 key);
+
+/**
* Manual cast from 'int' to the struct DBData.
* @param data Data to be casted
* @return The data as a DBData struct
@@ -882,12 +929,13 @@ struct linkdb_node {
typedef void (*LinkDBFunc)(void* key, void* data, va_list args);
-void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // d•¡‚ðl—¶‚µ‚È‚¢
-void linkdb_replace( struct linkdb_node** head, void *key, void* data); // d•¡‚ðl—¶‚·‚é
-void* linkdb_search ( struct linkdb_node** head, void *key);
-void* linkdb_erase ( struct linkdb_node** head, void *key);
-void linkdb_final ( struct linkdb_node** head );
-void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
+void linkdb_insert (struct linkdb_node** head, void *key, void* data); // Doesn't take into account duplicate keys
+void linkdb_replace (struct linkdb_node** head, void *key, void* data); // Takes into account duplicate keys
+void* linkdb_search (struct linkdb_node** head, void *key);
+void* linkdb_erase (struct linkdb_node** head, void *key);
+void linkdb_final (struct linkdb_node** head);
+void linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap);
+void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...);
@@ -1090,8 +1138,8 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
do{ \
if( (__n) > VECTOR_CAPACITY(__vec) ) \
{ /* increase size */ \
- if( VECTOR_CAPACITY(__vec) == 0 ) SET_POINTER(VECTOR_DATA(__vec), aMalloc((__n)*sizeof(VECTOR_FIRST(__vec)))); /* allocate new */ \
- else SET_POINTER(VECTOR_DATA(__vec), aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec)))); /* reallocate */ \
+ if( VECTOR_CAPACITY(__vec) == 0 ) VECTOR_DATA(__vec) = aMalloc((__n)*sizeof(VECTOR_FIRST(__vec))); /* allocate new */ \
+ else VECTOR_DATA(__vec) = aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec))); /* reallocate */ \
memset(VECTOR_DATA(__vec)+VECTOR_LENGTH(__vec), 0, (VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec))*sizeof(VECTOR_FIRST(__vec))); /* clear new data */ \
VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
} \
@@ -1103,7 +1151,7 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
} \
else if( (__n) < VECTOR_CAPACITY(__vec) ) \
{ /* reduce size */ \
- SET_POINTER(VECTOR_DATA(__vec), aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec)))); /* reallocate */ \
+ VECTOR_DATA(__vec) = aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec))); /* reallocate */ \
VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
if( VECTOR_LENGTH(__vec) > (__n) ) VECTOR_LENGTH(__vec) = (__n); /* update length */ \
} \
@@ -1120,8 +1168,10 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
#define VECTOR_ENSURE(__vec,__n,__step) \
do{ \
size_t _empty_ = VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec); \
- while( (__n) > _empty_ ) _empty_ += (__step); \
- if( _empty_ != VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec) ) VECTOR_RESIZE(__vec,_empty_+VECTOR_LENGTH(__vec)); \
+ if( (__n) > _empty_ ) { \
+ while( (__n) > _empty_ ) _empty_ += (__step); \
+ VECTOR_RESIZE(__vec,_empty_+VECTOR_LENGTH(__vec)); \
+ } \
}while(0)
@@ -1392,7 +1442,8 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
/// @param __heap Binary heap
/// @param __val Value
/// @param __topcmp Comparator
-#define BHEAP_PUSH(__heap,__val,__topcmp) \
+/// @param __swp Swapper
+#define BHEAP_PUSH(__heap,__val,__topcmp,__swp) \
do{ \
size_t _i_ = VECTOR_LENGTH(__heap); \
VECTOR_PUSH(__heap,__val); /* insert at end */ \
@@ -1401,7 +1452,7 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
size_t _parent_ = (_i_-1)/2; \
if( __topcmp(VECTOR_INDEX(__heap,_parent_),VECTOR_INDEX(__heap,_i_)) < 0 ) \
break; /* done */ \
- swap(VECTOR_INDEX(__heap,_parent_),VECTOR_INDEX(__heap,_i_)); \
+ __swp(VECTOR_INDEX(__heap,_parent_),VECTOR_INDEX(__heap,_i_)); \
_i_ = _parent_; \
} \
}while(0)
@@ -1418,7 +1469,8 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
///
/// @param __heap Binary heap
/// @param __topcmp Comparator
-#define BHEAP_POP(__heap,__topcmp) BHEAP_POPINDEX(__heap,0,__topcmp)
+/// @param __swp Swapper
+#define BHEAP_POP(__heap,__topcmp,__swp) BHEAP_POPINDEX(__heap,0,__topcmp,__swp)
@@ -1433,16 +1485,19 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
/// @param __heap Binary heap
/// @param __idx Index
/// @param __topcmp Comparator
-#define BHEAP_POPINDEX(__heap,__idx,__topcmp) \
+/// @param __swp Swapper
+#define BHEAP_POPINDEX(__heap,__idx,__topcmp,__swp) \
do{ \
size_t _i_ = __idx; \
VECTOR_INDEX(__heap,__idx) = VECTOR_POP(__heap); /* put last at index */ \
+ if( _i_ >= VECTOR_LENGTH(__heap)) /* removed last, nothing to do */ \
+ break; \
while( _i_ ) \
{ /* restore heap property in parents */ \
size_t _parent_ = (_i_-1)/2; \
if( __topcmp(VECTOR_INDEX(__heap,_parent_),VECTOR_INDEX(__heap,_i_)) < 0 ) \
break; /* done */ \
- swap(VECTOR_INDEX(__heap,_parent_),VECTOR_INDEX(__heap,_i_)); \
+ __swp(VECTOR_INDEX(__heap,_parent_),VECTOR_INDEX(__heap,_i_)); \
_i_ = _parent_; \
} \
while( _i_ < VECTOR_LENGTH(__heap) ) \
@@ -1454,12 +1509,12 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
break; /* done */ \
else if( _rchild_ >= VECTOR_LENGTH(__heap) || __topcmp(VECTOR_INDEX(__heap,_lchild_),VECTOR_INDEX(__heap,_rchild_)) <= 0 ) \
{ /* left child */ \
- swap(VECTOR_INDEX(__heap,_i_),VECTOR_INDEX(__heap,_lchild_)); \
+ __swp(VECTOR_INDEX(__heap,_i_),VECTOR_INDEX(__heap,_lchild_)); \
_i_ = _lchild_; \
} \
else \
{ /* right child */ \
- swap(VECTOR_INDEX(__heap,_i_),VECTOR_INDEX(__heap,_rchild_)); \
+ __swp(VECTOR_INDEX(__heap,_i_),VECTOR_INDEX(__heap,_rchild_)); \
_i_ = _rchild_; \
} \
} \
@@ -1494,4 +1549,4 @@ void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
-#endif /* _DB_H_ */
+#endif /* COMMON_DB_H */
diff --git a/src/common/des.c b/src/common/des.c
index ed6d098dc..7f952be76 100644
--- a/src/common/des.c
+++ b/src/common/des.c
@@ -1,8 +1,11 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#include "../common/cbasetypes.h"
-#include "../common/des.h"
+#define HERCULES_CORE
+
+#include "des.h"
+
+#include "../common/cbasetypes.h"
/// DES (Data Encryption Standard) algorithm, modified version.
/// @see http://www.eathena.ws/board/index.php?autocom=bugtracker&showbug=5099.
diff --git a/src/common/des.h b/src/common/des.h
index e42136436..2c7190f23 100644
--- a/src/common/des.h
+++ b/src/common/des.h
@@ -1,8 +1,9 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#ifndef _DES_H_
-#define _DES_H_
+#ifndef COMMON_DES_H
+#define COMMON_DES_H
+#include "../common/cbasetypes.h"
/// One 64-bit block.
typedef struct BIT64 { uint8_t b[8]; } BIT64;
@@ -12,4 +13,4 @@ void des_decrypt_block(BIT64* block);
void des_decrypt(unsigned char* data, size_t size);
-#endif // _DES_H_
+#endif // COMMON_DES_H
diff --git a/src/common/ers.c b/src/common/ers.c
index 22269a51f..c8a11d2a9 100644
--- a/src/common/ers.c
+++ b/src/common/ers.c
@@ -13,16 +13,16 @@
* If it has reusable entries (freed entry), it uses one. *
* So no assumption should be made about the data of the entry. *
* Entries should be freed in the manager they where allocated from. *
- * Failure to do so can lead to unexpected behaviours. *
+ * Failure to do so can lead to unexpected behaviors. *
* *
* <H2>Advantages:</H2> *
* - The same manager is used for entries of the same size. *
* So entries freed in one instance of the manager can be used by other *
* instances of the manager. *
* - Much less memory allocation/deallocation - program will be faster. *
- * - Avoids memory fragmentaion - program will run better for longer. *
+ * - Avoids memory fragmentation - program will run better for longer. *
* *
- * <H2>Disavantages:</H2> *
+ * <H2>Disadvantages:</H2> *
* - Unused entries are almost inevitable - memory being wasted. *
* - A manager will only auto-destroy when all of its instances are *
* destroyed so memory will usually only be recovered near the end. *
@@ -39,12 +39,18 @@
* @encoding US-ASCII *
* @see common#ers.h *
\*****************************************************************************/
+
+#define HERCULES_CORE
+
+#include "ers.h"
+
#include <stdlib.h>
+#include <string.h>
#include "../common/cbasetypes.h"
#include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
+#include "../common/nullpo.h"
#include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
-#include "ers.h"
#ifndef DISABLE_ERS
@@ -72,7 +78,7 @@ typedef struct ers_cache
// Memory blocks array
unsigned char **Blocks;
- // Max number of blocks
+ // Max number of blocks
unsigned int Max;
// Free objects count
@@ -87,6 +93,9 @@ typedef struct ers_cache
// Default = ERS_BLOCK_ENTRIES, can be adjusted for performance for individual cache sizes.
unsigned int ChunkSize;
+ // Misc options, some options are shared from the instance
+ enum ERSOptions Options;
+
// Linked list
struct ers_cache *Next, *Prev;
} ers_cache_t;
@@ -95,7 +104,7 @@ struct ers_instance_t {
// Interface to ERS
struct eri VTable;
- // Name, used for debbuging purpouses
+ // Name, used for debugging purposes
char *Name;
// Misc options
@@ -110,24 +119,23 @@ struct ers_instance_t {
#ifdef DEBUG
/* for data analysis [Ind/Hercules] */
unsigned int Peak;
- struct ers_instance_t *Next, *Prev;
#endif
-
+ struct ers_instance_t *Next, *Prev;
};
// Array containing a pointer for all ers_cache structures
static ers_cache_t *CacheList = NULL;
-#ifdef DEBUG
- static struct ers_instance_t *InstanceList = NULL;
-#endif
+static struct ers_instance_t *InstanceList = NULL;
-static ers_cache_t *ers_find_cache(unsigned int size)
-{
+/**
+ * @param Options the options from the instance seeking a cache, we use it to give it a cache with matching configuration
+ **/
+static ers_cache_t *ers_find_cache(unsigned int size, enum ERSOptions Options) {
ers_cache_t *cache;
for (cache = CacheList; cache; cache = cache->Next)
- if (cache->ObjectSize == size)
+ if ( cache->ObjectSize == size && cache->Options == ( Options & ERS_CACHE_OPTIONS ) )
return cache;
CREATE(cache, ers_cache_t, 1);
@@ -140,6 +148,7 @@ static ers_cache_t *ers_find_cache(unsigned int size)
cache->UsedObjs = 0;
cache->Max = 0;
cache->ChunkSize = ERS_BLOCK_ENTRIES;
+ cache->Options = (Options & ERS_CACHE_OPTIONS);
if (CacheList == NULL)
{
@@ -172,34 +181,28 @@ static void ers_free_cache(ers_cache_t *cache, bool remove)
CacheList = cache->Next;
aFree(cache->Blocks);
+
aFree(cache);
}
-static void *ers_obj_alloc_entry(ERS self)
+static void *ers_obj_alloc_entry(ERS *self)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
void *ret;
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_alloc_entry: NULL object, aborting entry freeing.\n");
return NULL;
}
- if (instance->Cache->ReuseList != NULL)
- {
+ if (instance->Cache->ReuseList != NULL) {
ret = (void *)((unsigned char *)instance->Cache->ReuseList + sizeof(struct ers_list));
instance->Cache->ReuseList = instance->Cache->ReuseList->Next;
- }
- else if (instance->Cache->Free > 0)
- {
+ } else if (instance->Cache->Free > 0) {
instance->Cache->Free--;
ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
- }
- else
- {
- if (instance->Cache->Used == instance->Cache->Max)
- {
+ } else {
+ if (instance->Cache->Used == instance->Cache->Max) {
instance->Cache->Max = (instance->Cache->Max * 4) + 3;
RECREATE(instance->Cache->Blocks, unsigned char *, instance->Cache->Max);
}
@@ -222,47 +225,45 @@ static void *ers_obj_alloc_entry(ERS self)
return ret;
}
-static void ers_obj_free_entry(ERS self, void *entry)
+static void ers_obj_free_entry(ERS *self, void *entry)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
struct ers_list *reuse = (struct ers_list *)((unsigned char *)entry - sizeof(struct ers_list));
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
return;
- }
- else if (entry == NULL)
- {
+ } else if (entry == NULL) {
ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
return;
}
+ if( instance->Cache->Options & ERS_OPT_CLEAN )
+ memset((unsigned char*)reuse + sizeof(struct ers_list), 0, instance->Cache->ObjectSize - sizeof(struct ers_list));
+
reuse->Next = instance->Cache->ReuseList;
instance->Cache->ReuseList = reuse;
instance->Count--;
instance->Cache->UsedObjs--;
}
-static size_t ers_obj_entry_size(ERS self)
+static size_t ers_obj_entry_size(ERS *self)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_entry_size: NULL object, aborting entry freeing.\n");
return 0;
- }
+ }
return instance->Cache->ObjectSize;
}
-static void ers_obj_destroy(ERS self)
+static void ers_obj_destroy(ERS *self)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_destroy: NULL object, aborting entry freeing.\n");
return;
}
@@ -274,7 +275,6 @@ static void ers_obj_destroy(ERS self)
if (--instance->Cache->ReferenceCount <= 0)
ers_free_cache(instance->Cache, true);
-#ifdef DEBUG
if (instance->Next)
instance->Next->Prev = instance->Prev;
@@ -282,7 +282,6 @@ static void ers_obj_destroy(ERS self)
instance->Prev->Next = instance->Next;
else
InstanceList = instance->Next;
-#endif
if( instance->Options & ERS_OPT_FREE_NAME )
aFree(instance->Name);
@@ -290,19 +289,20 @@ static void ers_obj_destroy(ERS self)
aFree(instance);
}
-void ers_cache_size(ERS self, unsigned int new_size) {
+void ers_cache_size(ERS *self, unsigned int new_size) {
struct ers_instance_t *instance = (struct ers_instance_t *)self;
- if (instance == NULL) {//change as per piotrhalaczkiewicz comment
- ShowError("ers_cache_size: NULL object, skipping...\n");
- return;
+ nullpo_retv(instance);
+
+ if( !(instance->Cache->Options&ERS_OPT_FLEX_CHUNK) ) {
+ ShowWarning("ers_cache_size: '%s' has adjusted its chunk size to '%d', however ERS_OPT_FLEX_CHUNK is missing!\n",instance->Name,new_size);
}
instance->Cache->ChunkSize = new_size;
}
-ERS ers_new(uint32 size, char *name, enum ERSOptions options)
+ERS *ers_new(uint32 size, char *name, enum ERSOptions options)
{
struct ers_instance_t *instance;
CREATE(instance,struct ers_instance_t, 1);
@@ -320,9 +320,10 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options)
instance->Name = ( options & ERS_OPT_FREE_NAME ) ? aStrdup(name) : name;
instance->Options = options;
- instance->Cache = ers_find_cache(size);
+ instance->Cache = ers_find_cache(size,instance->Options);
+
instance->Cache->ReferenceCount++;
-#ifdef DEBUG
+
if (InstanceList == NULL) {
InstanceList = instance;
} else {
@@ -331,7 +332,6 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options)
InstanceList = instance;
InstanceList->Prev = NULL;
}
-#endif
instance->Count = 0;
@@ -379,12 +379,17 @@ void ers_report(void) {
ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' blocks total, consuming '"CL_WHITE"%.2f MB"CL_NORMAL"' \n",blocks_a,(double)((memory_t)/1024)/1024);
}
-void ers_force_destroy_all(void)
-{
- ers_cache_t *cache;
+/**
+ * Call on shutdown to clear remaining entries
+ **/
+void ers_final(void) {
+ struct ers_instance_t *instance = InstanceList, *next;
- for (cache = CacheList; cache; cache = cache->Next)
- ers_free_cache(cache, false);
+ while( instance ) {
+ next = instance->Next;
+ ers_obj_destroy((ERS*)instance);
+ instance = next;
+ }
}
#endif
diff --git a/src/common/ers.h b/src/common/ers.h
index 51701d778..904f7fb81 100644
--- a/src/common/ers.h
+++ b/src/common/ers.h
@@ -13,16 +13,16 @@
* If it has reusable entries (freed entry), it uses one. *
* So no assumption should be made about the data of the entry. *
* Entries should be freed in the manager they where allocated from. *
- * Failure to do so can lead to unexpected behaviours. *
+ * Failure to do so can lead to unexpected behaviors. *
* *
* <H2>Advantages:</H2> *
* - The same manager is used for entries of the same size. *
* So entries freed in one instance of the manager can be used by other *
* instances of the manager. *
* - Much less memory allocation/deallocation - program will be faster. *
- * - Avoids memory fragmentaion - program will run better for longer. *
+ * - Avoids memory fragmentation - program will run better for longer. *
* *
- * <H2>Disavantages:</H2> *
+ * <H2>Disadvantages:</H2> *
* - Unused entries are almost inevitable - memory being wasted. *
* - A manager will only auto-destroy when all of its instances are *
* destroyed so memory will usually only be recovered near the end. *
@@ -37,8 +37,8 @@
* @author Flavio @ Amazon Project *
* @encoding US-ASCII *
\*****************************************************************************/
-#ifndef _ERS_H_
-#define _ERS_H_
+#ifndef COMMON_ERS_H
+#define COMMON_ERS_H
#include "../common/cbasetypes.h"
@@ -49,13 +49,13 @@
* ERS - Entry manager. *
* ers_new - Allocate an instance of an entry manager. *
* ers_report - Print a report about the current state. *
- * ers_force_destroy_all - Force the destruction of all the managers. *
+ * ers_final - Clears the remainder of the managers. *
\*****************************************************************************/
/**
* Define this to disable the Entry Reusage System.
* All code except the typedef of ERInterface will be disabled.
- * To allow a smooth transition,
+ * To allow a smooth transition,
*/
//#define DISABLE_ERS
@@ -63,18 +63,23 @@
* Entries are aligned to ERS_ALIGNED bytes in the blocks of entries.
* By default it aligns to one byte, using the "natural order" of the entries.
* This should NEVER be set to zero or less.
- * If greater than one, some memory can be wasted. This should never be needed
- * but is here just in case some aligment issues arise.
+ * If greater than one, some memory can be wasted. This should never be needed
+ * but is here just in case some alignment issues arise.
*/
#ifndef ERS_ALIGNED
# define ERS_ALIGNED 1
#endif /* not ERS_ALIGN_ENTRY */
enum ERSOptions {
- ERS_OPT_NONE = 0x0,
- ERS_OPT_CLEAR = 0x1,/* silently clears any entries left in the manager upon destruction */
- ERS_OPT_WAIT = 0x2,/* wait for entries to come in order to list! */
- ERS_OPT_FREE_NAME = 0x4,/* name is dynamic memory, and should be freed */
+ ERS_OPT_NONE = 0x0,
+ ERS_OPT_CLEAR = 0x1,/* silently clears any entries left in the manager upon destruction */
+ ERS_OPT_WAIT = 0x2,/* wait for entries to come in order to list! */
+ ERS_OPT_FREE_NAME = 0x4,/* name is dynamic memory, and should be freed */
+ ERS_OPT_CLEAN = 0x8,/* clears used memory upon ers_free so that its all new to be reused on the next alloc */
+ ERS_OPT_FLEX_CHUNK = 0x10,/* signs that it should look for its own cache given it'll have a dynamic chunk size, so that it doesn't affect the other ERS it'd otherwise be sharing */
+
+ /* Compound, is used to determine whether it should be looking for a cache of matching options */
+ ERS_CACHE_OPTIONS = ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK,
};
/**
@@ -97,7 +102,7 @@ typedef struct eri {
/**
* Free an entry allocated from this manager.
* WARNING: Does not check if the entry was allocated by this manager.
- * Freeing such an entry can lead to unexpected behaviour.
+ * Freeing such an entry can lead to unexpected behavior.
* @param self Interface of the entry manager
* @param entry Entry to be freed
*/
@@ -113,7 +118,7 @@ typedef struct eri {
/**
* Destroy this instance of the manager.
* The manager is actually only destroyed when all the instances are destroyed.
- * When destroying the manager a warning is shown if the manager has
+ * When destroying the manager a warning is shown if the manager has
* missing/extra entries.
* @param self Interface of the entry manager
*/
@@ -121,7 +126,7 @@ typedef struct eri {
/* */
void (*chunk_size) (struct eri *self, unsigned int new_size);
-} *ERS;
+} ERS;
#ifdef DISABLE_ERS
// Use memory manager to allocate/free and disable other interface functions
@@ -133,27 +138,27 @@ typedef struct eri {
// Disable the public functions
# define ers_new(size,name,options) NULL
# define ers_report()
-# define ers_force_destroy_all()
+# define ers_final()
#else /* not DISABLE_ERS */
-// These defines should be used to allow the code to keep working whenever
+// These defines should be used to allow the code to keep working whenever
// the system is disabled
-# define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
-# define ers_free(obj,entry) (obj)->free((obj),(entry))
-# define ers_entry_size(obj) (obj)->entry_size(obj)
-# define ers_destroy(obj) (obj)->destroy(obj)
-# define ers_chunk_size(obj,size) (obj)->chunk_size(obj,size)
+# define ers_alloc(obj,type) ((type *)(obj)->alloc(obj))
+# define ers_free(obj,entry) ((obj)->free((obj),(entry)))
+# define ers_entry_size(obj) ((obj)->entry_size(obj))
+# define ers_destroy(obj) ((obj)->destroy(obj))
+# define ers_chunk_size(obj,size) ((obj)->chunk_size((obj),(size)))
/**
* Get a new instance of the manager that handles the specified entry size.
* Size has to greater than 0.
- * If the specified size is smaller than a pointer, the size of a pointer is
+ * If the specified size is smaller than a pointer, the size of a pointer is
* used instead.
- * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
+ * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
* ERS_ALIGNED that is greater or equal to size is what's actually used.
* @param The requested size of the entry in bytes
* @return Interface of the object
*/
-ERS ers_new(uint32 size, char *name, enum ERSOptions options);
+ERS *ers_new(uint32 size, char *name, enum ERSOptions options);
/**
* Print a report about the current state of the Entry Reusage System.
@@ -165,14 +170,9 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options);
void ers_report(void);
/**
- * Forcibly destroy all the entry managers, checking for nothing.
- * The system is left as if no instances or entries had ever been allocated.
- * All previous entries and instances of the managers become invalid.
- * The use of this is NOT recommended.
- * It should only be used in extreme situations to make shure all the memory
- * allocated by this system is released.
- */
-void ers_force_destroy_all(void);
+ * Clears the remainder of the managers
+ **/
+void ers_final(void);
#endif /* DISABLE_ERS / not DISABLE_ERS */
-#endif /* _ERS_H_ */
+#endif /* COMMON_ERS_H */
diff --git a/src/common/evdp.h b/src/common/evdp.h
deleted file mode 100644
index bc3454686..000000000
--- a/src/common/evdp.h
+++ /dev/null
@@ -1,168 +0,0 @@
-#ifndef _rA_EVDP_H_
-#define _rA_EVDP_H_
-
-#include "../common/cbasetypes.h"
-
-typedef struct EVDP_DATA EVDP_DATA;
-
-
-//#idef EVDP_EPOLL
-#include <sys/epoll.h>
-struct EVDP_DATA{
- struct epoll_event ev_data;
- bool ev_added;
-};
-//#endif
-
-
-enum EVDP_EVENTFLAGS{
- EVDP_EVENT_IN = 1, // Incomming data
- EVDP_EVENT_OUT = 2, // Connection accepts writing.
- EVDP_EVENT_HUP = 4 // Connection Closed.
-};
-
-typedef struct EVDP_EVENT{
- int32 events; // due to performance reasons, this should be the first member.
- int32 fd; // Connection Identifier
-} EVDP_EVENT;
-
-
-
-/**
- * Network Event Dispatcher Initialization / Finalization routines
- */
-void evdp_init();
-void evdp_final();
-
-
-/**
- * Will Wait for events.
- *
- * @param *out_ev pointer to array in size at least of max_events.
- * @param max_events max no of events to report with this call (coalesc)
- * @param timeout_ticks max time to wait in ticks (milliseconds)
- *
- * @Note:
- * The function will block until an event has occured on one of the monitored connections
- * or the timeout of timeout_ticks has passed by.
- * Upon successfull call (changed connections) this function will write the connection
- * Identifier & event to the out_fds array.
- *
- * @return 0 -> Timeout, > 0 no of changed connections.
- */
-int32 evdp_wait(EVDP_EVENT *out_fds, int32 max_events, int32 timeout_ticks);
-
-
-/**
- * Applys the given mask on the given connection.
- *
- * @param fd connection identifier
- * @param *ep event data pointer for the connection
- * @param mask new event mask we're monitoring for.
- */
-//void evdp_apply(int32 fd, EVDP_DATA *ep, int32 mask);
-
-
-/**
- * Adds a connection (listner) to the event notification system.
- *
- * @param fd connection identifier
- * @param *ep event data pointer for the connection
- *
- * @note:
- * Listener type sockets are edge triggered, (see epoll manual for more information)
- * - This basicaly means that youll receive one event, adn you have to accept until accept returns an error (nothing to accept)
- *
- * MONITORS by default: IN
- *
- * @return success indicator.
- */
-bool evdp_addlistener(int32 fd, EVDP_DATA *ep);
-
-/**
- * Adds a connection (client connectioN) to the event notification system
- *
- * @param fd connection identifier
- * @param *ep event data pointr for the connection
- *
- * @note:
- *
- * MONITORS by default: IN, HUP
- *
- * @return success indicator.
- */
-bool evdp_addclient(int32 fd, EVDP_DATA *ep);
-
-/**
- * Adds a connection (pending / outgoing connection!) to the event notification system.
- *
- * @param fd connection identifier
- * @param *ep event data pointer for the conneciton.
- *
- * @note:
- * Outgoing connection type sockets are getting monitored for connection established
- * successfull
- * - if the connection has been established - we're generitng a writable notification .. (send)
- * this is typical for BSD / posix conform network stacks.
- * - Additinionally its edge triggered.
- *
- * @see evdp_outgoingconnection_established
- *
- *
- * @return success indicator
- */
-bool evdp_addconnecting(int32 fd, EVDP_DATA *ep);
-
-/**
- * Adds an outgoing connection to the normal event notification system after it has been successfully established.
- *
- * @param fd connection identifier
- * @param *ep event data pointer for the conneciton.
-
- * @note
- * after this call, its handled like a normal "client" connection (incomming)
- *
- * @rturn success indicator
- */
-bool evdp_outgoingconnection_established(int32 fd, EVDP_DATA *ep);
-
-/**
- * Marks a connection to be monitored for writable.
- *
- * @param fd connection identifier
- * @param *ep event data pointer for the connection
- *
- * @note:
- * the connection must be already added (as client or listener)
- *
- *
- * @return sucess indicator
- */
-bool evdp_writable_add(int32 fd, EVDP_DATA *ep);
-
-/**
- * Removes the connection from writable notification monitoring
- *
- * @param fd connection identifier
- * @param *ep event data pointr for the connection
- *
- */
-void evdp_writable_remove(int32 fd, EVDP_DATA *ep);
-
-/**
- * Removes an connectio from the event notification system.
- *
- * @param fd connection iditentfir
- * @param *ep event data pointer for th connection
- *
- *
- * @note:
- * this will also clear the given EVENT_DATA block
- * so the connection slot is in an "initial" blank status / ready to get reused.
- *
- */
-void evdp_remove(int32 fd, EVDP_DATA *ep);
-
-
-
-#endif
diff --git a/src/common/evdp_epoll.c b/src/common/evdp_epoll.c
deleted file mode 100644
index 0357dfc66..000000000
--- a/src/common/evdp_epoll.c
+++ /dev/null
@@ -1,232 +0,0 @@
-//
-// Event Dispatcher Abstraction for EPOLL
-//
-// Author: Florian Wilkemeyer <fw@f-ws.de>
-//
-// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-//
-//
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <sys/epoll.h>
-#include <sys/fcntl.h>
-#include <sys/socket.h>
-
-#include "../common/cbasetypes.h"
-#include "../common/showmsg.h"
-#include "../common/evdp.h"
-
-
-#define EPOLL_MAX_PER_CYCLE 10 // Max Events to coalesc. per cycle.
-
-
-static int epoll_fd = -1;
-
-
-void evdp_init(){
-
- epoll_fd = epoll_create( EPOLL_MAX_PER_CYCLE );
- if(epoll_fd == -1){
- ShowFatalError("evdp [EPOLL]: Cannot create event dispatcher (errno: %u / %s)\n", errno, strerror(errno) );
- exit(1);
- }
-
-}//end: evdp_init()
-
-
-void evdp_final(){
-
- if(epoll_fd != -1){
- close(epoll_fd);
- epoll_fd = -1;
- }
-
-}//end: evdp_final()
-
-
-int32 evdp_wait(EVDP_EVENT *out_fds, int32 max_events, int32 timeout_ticks){
- struct epoll_event l_events[EPOLL_MAX_PER_CYCLE];
- register struct epoll_event *ev;
- register int nfds, n;
-
- if(max_events > EPOLL_MAX_PER_CYCLE)
- max_events = EPOLL_MAX_PER_CYCLE;
-
- nfds = epoll_wait( epoll_fd, l_events, max_events, timeout_ticks);
- if(nfds == -1){
- // @TODO: check if core is in shutdown mode. if - ignroe error.
-
- ShowFatalError("evdp [EPOLL]: epoll_wait returned bad / unexpected status (errno: %u / %s)\n", errno, strerror(errno));
- exit(1); //..
- }
-
- // Loop thru all events and copy it to the local ra evdp_event.. struct.
- for(n = 0; n < nfds; n++){
- ev = &l_events[n];
-
- out_fds->fd = ev->data.fd;
- out_fds->events = 0; // clear
-
- if(ev->events & EPOLLHUP)
- out_fds->events |= EVDP_EVENT_HUP;
-
- if(ev->events & EPOLLIN)
- out_fds->events |= EVDP_EVENT_IN;
-
- if(ev->events & EPOLLOUT)
- out_fds->events |= EVDP_EVENT_OUT;
-
- out_fds++;
- }
-
- return nfds; // 0 on timeout or > 0 ..
-}//end: evdp_wait()
-
-
-void evdp_remove(int32 fd, EVDP_DATA *ep){
-
- if(ep->ev_added == true){
-
- if( epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ep->ev_data) != 0){
- ShowError("evdp [EPOLL]: evdp_remove - epoll_ctl (EPOLL_CTL_DEL) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
- }
-
- ep->ev_data.events = 0; // clear struct.
- ep->ev_data.data.fd = -1; // .. clear struct ..
-
- ep->ev_added = false; // not added!
- }
-
-
-}//end: evdp_remove()
-
-
-bool evdp_addlistener(int32 fd, EVDP_DATA *ep){
-
- ep->ev_data.events = EPOLLET|EPOLLIN;
- ep->ev_data.data.fd = fd;
-
- // No check here for 'added ?'
- // listeners cannot be added twice.
- //
- if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ep->ev_data) != 0 ){
- ShowError("evdp [EPOLL]: evdp_addlistener - epoll_ctl (EPOLL_CTL_ADD) faield! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
- ep->ev_data.events = 0;
- ep->ev_data.data.fd = -1;
- return false;
- }
-
- ep->ev_added = true;
-
- return true;
-}//end: evdp_addlistener()
-
-
-bool evdp_addclient(int32 fd, EVDP_DATA *ep){
-
- ep->ev_data.events = EPOLLIN | EPOLLHUP;
- ep->ev_data.data.fd = fd;
-
- // No check for "added?" here,
- // this function only gets called upon accpept.
- //
-
- if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ep->ev_data) != 0){
- ShowError("evdp [EPOLL]: evdp_addclient - epoll_ctl (EPOLL_CTL_ADD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
- ep->ev_data.events = 0;
- ep->ev_data.data.fd = -1;
- return false;
- }
-
- ep->ev_added = true;
-
- return true;
-}//end: evdp_addclient()
-
-
-bool evdp_addconnecting(int32 fd, EVDP_DATA *ep){
-
- ep->ev_data.events = EPOLLET | EPOLLOUT | EPOLLHUP;
- ep->ev_data.data.fd = fd;
-
- if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ep->ev_data) != 0){
- ShowError("evdp [EPOLL]: evdp_addconnecting - epoll_ctl (EPOLL_CTL_ADD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
- ep->ev_data.events = 0;
- ep->ev_data.data.fd = -1;
- }
-
- ep->ev_added = true;
-
- return true;
-}//end: evdp_addconnecting()
-
-
-bool evdp_outgoingconnection_established(int32 fd, EVDP_DATA *ep){
- int32 saved_mask;
-
- if(ep->ev_added != true){
- // !
- ShowError("evdp [EPOLL]: evdp_outgoingconnection_established fd #%u is not added to event dispatcher! invalid call.\n", fd);
- return false;
- }
-
- saved_mask = ep->ev_data.events;
-
- ep->ev_data.events = EPOLLIN | EPOLLHUP;
-
- if( epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ep->ev_data) != 0){
- ep->ev_data.events = saved_mask; // restore old mask.
- ShowError("evdp [EPOLL]: evdp_outgoingconnection_established - epoll_ctl (EPOLL_CTL_MOD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
- return false;
- }
-
- return true;
-}//end: evdp_outgoingconnection_established()
-
-
-bool evdp_writable_add(int32 fd, EVDP_DATA *ep){
-
- if(ep->ev_added != true){
- ShowError("evdp [EPOLL]: evdp_writable_add - tried to add not added fd #%u\n",fd);
- return false;
- }
-
- if(! (ep->ev_data.events & EPOLLOUT) ){ //
-
- ep->ev_data.events |= EPOLLOUT;
- if( epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ep->ev_data) != 0 ){
- ShowError("evdp [EPOLL]: evdp_writable_add - epoll_ctl (EPOLL_CTL_MOD) failed! fd #%u (errno: %u / %s)\n", fd, errno, strerror(errno));
- ep->ev_data.events &= ~EPOLLOUT; // remove from local flagmask due to failed syscall.
- return false;
- }
- }
-
- return true;
-}//end: evdp_writable_add()
-
-
-void evdp_writable_remove(int32 fd, EVDP_DATA *ep){
-
- if(ep->ev_added != true){
- ShowError("evdp [EPOLL]: evdp_writable_remove - tried to remove not added fd #%u\n", fd);
- return;
- }
-
- if( ep->ev_data.events & EPOLLOUT ){
-
- ep->ev_data.events &= ~EPOLLOUT;
- if( epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ep->ev_data) != 0){
- ShowError("evdp [EPOLL]: evdp_writable_remove - epoll_ctl (EPOLL_CTL_MOD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
- ep->ev_data.events |= EPOLLOUT; // add back to local flagmask because of failed syscall.
- return;
- }
- }
-
- return;
-}//end: evdp_writable_remove()
diff --git a/src/common/grfio.c b/src/common/grfio.c
index 77b976926..5be0c8237 100644
--- a/src/common/grfio.c
+++ b/src/common/grfio.c
@@ -2,12 +2,8 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/cbasetypes.h"
-#include "../common/des.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/strlib.h"
-#include "../common/utils.h"
+#define HERCULES_CORE
+
#include "grfio.h"
#include <stdio.h>
@@ -16,19 +12,27 @@
#include <sys/stat.h>
#include <zlib.h>
+#include "../common/cbasetypes.h"
+#include "../common/des.h"
+#include "../common/malloc.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
+
//----------------------------
// file entry table struct
//----------------------------
-typedef struct _FILELIST {
- int srclen; // compressed size
- int srclen_aligned;
- int declen; // original size
- int srcpos; // position of entry in grf
- int next; // index of next filelist entry with same hash (-1: end of entry chain)
- char type;
- char fn[128-4*5]; // file name
- char* fnd; // if the file was cloned, contains name of original file
- char gentry; // read grf file select
+typedef struct FILELIST {
+ int srclen; ///< compressed size
+ int srclen_aligned;
+ int declen; ///< original size
+ int srcpos; ///< position of entry in grf
+ int next; ///< index of next filelist entry with same hash (-1: end of entry chain)
+ char type;
+ char fn[128-4*5]; ///< file name
+ char *fnd; ///< if the file was cloned, contains name of original file
+ int8 gentry; ///< read grf file select
} FILELIST;
#define FILELIST_TYPE_FILE 0x01 // entry is a file
@@ -305,17 +309,21 @@ static FILELIST* filelist_find(const char* fname)
// returns the original file name
char* grfio_find_file(const char* fname)
{
- FILELIST *filelist = filelist_find(fname);
- if (!filelist) return NULL;
- return (!filelist->fnd ? filelist->fn : filelist->fnd);
+ FILELIST *flist = filelist_find(fname);
+ if (!flist) return NULL;
+ return (!flist->fnd ? flist->fn : flist->fnd);
}
// adds a FILELIST entry into the list of loaded files
-static FILELIST* filelist_add(FILELIST* entry)
-{
+static FILELIST* filelist_add(FILELIST* entry) {
int hash;
+ nullpo_ret(entry);
+#ifdef __clang_analyzer__
+ // Make clang's static analyzer shut up about a possible NULL pointer in &filelist[filelist_entrys]
+ nullpo_ret(&filelist[filelist_entrys]);
+#endif // __clang_analyzer__
- #define FILELIST_ADDS 1024 // number increment of file lists `
+#define FILELIST_ADDS 1024 // number increment of file lists `
if (filelist_entrys >= filelist_maxentry) {
filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST));
@@ -323,7 +331,9 @@ static FILELIST* filelist_add(FILELIST* entry)
filelist_maxentry += FILELIST_ADDS;
}
- memcpy (&filelist[filelist_entrys], entry, sizeof(FILELIST));
+#undef FILELIST_ADDS
+
+ memcpy(&filelist[filelist_entrys], entry, sizeof(FILELIST));
hash = filehash(entry->fn);
filelist[filelist_entrys].next = filelist_hash[hash];
@@ -405,10 +415,10 @@ void* grfio_reads(const char* fname, int* size)
if( in != NULL ) {
int declen;
fseek(in,0,SEEK_END);
- declen = ftell(in);
+ declen = (int)ftell(in);
fseek(in,0,SEEK_SET);
buf2 = (unsigned char *)aMalloc(declen+1); // +1 for resnametable zero-termination
- if(fread(buf2, 1, declen, in) != (size_t)declen) ShowError("An error occured in fread grfio_reads, fname=%s \n",fname);
+ if(fread(buf2, 1, declen, in) != (size_t)declen) ShowError("An error occurred in fread grfio_reads, fname=%s \n",fname);
fclose(in);
if( size )
@@ -430,7 +440,7 @@ void* grfio_reads(const char* fname, int* size)
int fsize = entry->srclen_aligned;
unsigned char *buf = (unsigned char *)aMalloc(fsize);
fseek(in, entry->srcpos, 0);
- if(fread(buf, 1, fsize, in) != (size_t)fsize) ShowError("An error occured in fread in grfio_reads, grfname=%s\n",grfname);
+ if(fread(buf, 1, fsize, in) != (size_t)fsize) ShowError("An error occurred in fread in grfio_reads, grfname=%s\n",grfname);
fclose(in);
buf2 = (unsigned char *)aMalloc(entry->declen+1); // +1 for resnametable zero-termination
@@ -573,7 +583,7 @@ static int grfio_entryread(const char* grfname, int gentry)
unsigned char *rBuf;
uLongf rSize, eSize;
- if(fread(eheader,1,8,fp) != 8) ShowError("An error occured in fread while reading eheader buffer\n");
+ if(fread(eheader,1,8,fp) != 8) ShowError("An error occurred in fread while reading header buffer\n");
rSize = getlong(eheader); // Read Size
eSize = getlong(eheader+4); // Extend Size
@@ -585,7 +595,7 @@ static int grfio_entryread(const char* grfname, int gentry)
rBuf = (unsigned char *)aMalloc(rSize); // Get a Read Size
grf_filelist = (unsigned char *)aMalloc(eSize); // Get a Extend Size
- if(fread(rBuf,1,rSize,fp) != rSize) ShowError("An error occured in fread \n");
+ if(fread(rBuf,1,rSize,fp) != rSize) ShowError("An error occurred in fread \n");
fclose(fp);
decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function
aFree(rBuf);
@@ -645,7 +655,7 @@ static bool grfio_parse_restable_row(const char* row)
char local[256];
FILELIST* entry;
- if( sscanf(row, "%[^#\r\n]#%[^#\r\n]#", w1, w2) != 2 )
+ if (sscanf(row, "%255[^#\r\n]#%255[^#\r\n]#", w1, w2) != 2)
return false;
if( strstr(w2, ".gat") == NULL && strstr(w2, ".rsw") == NULL )
@@ -795,7 +805,7 @@ void grfio_init(const char* fname)
if( line[0] == '/' && line[1] == '/' )
continue; // skip comments
- if( sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2 )
+ if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
continue; // skip unrecognized lines
// Entry table reading
@@ -817,7 +827,7 @@ void grfio_init(const char* fname)
if( grf_num == 0 )
ShowInfo("No GRF loaded, using default data directory\n");
- // Unneccessary area release of filelist
+ // Unnecessary area release of filelist
filelist_compact();
// Resource check
diff --git a/src/common/grfio.h b/src/common/grfio.h
index c5a56a14e..15659c17c 100644
--- a/src/common/grfio.h
+++ b/src/common/grfio.h
@@ -1,17 +1,17 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#ifndef _GRFIO_H_
-#define _GRFIO_H_
+#ifndef COMMON_GRFIO_H
+#define COMMON_GRFIO_H
void grfio_init(const char* fname);
void grfio_final(void);
void* grfio_reads(const char* fname, int* size);
char* grfio_find_file(const char* fname);
-#define grfio_read(fn) grfio_reads(fn, NULL)
+#define grfio_read(fn) grfio_reads((fn), NULL)
unsigned long grfio_crc32(const unsigned char *buf, unsigned int len);
int decode_zip(void* dest, unsigned long* destLen, const void* source, unsigned long sourceLen);
int encode_zip(void* dest, unsigned long* destLen, const void* source, unsigned long sourceLen);
-#endif /* _GRFIO_H_ */
+#endif /* COMMON_GRFIO_H */
diff --git a/src/common/malloc.c b/src/common/malloc.c
index d629aa63f..eae9ad423 100644
--- a/src/common/malloc.c
+++ b/src/common/malloc.c
@@ -2,43 +2,49 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/malloc.h"
-#include "../common/core.h"
-#include "../common/showmsg.h"
+#define HERCULES_CORE
+
+#include "malloc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include "../common/core.h"
+#include "../common/showmsg.h"
+#include "../common/sysinfo.h"
+
+struct malloc_interface iMalloc_s;
+
////////////// Memory Libraries //////////////////
#if defined(MEMWATCH)
-# include <string.h>
+# include <string.h>
# include "memwatch.h"
-# define MALLOC(n,file,line,func) mwMalloc((n),(file),(line))
-# define CALLOC(m,n,file,line,func) mwCalloc((m),(n),(file),(line))
-# define REALLOC(p,n,file,line,func) mwRealloc((p),(n),(file),(line))
-# define STRDUP(p,file,line,func) mwStrdup((p),(file),(line))
-# define FREE(p,file,line,func) mwFree((p),(file),(line))
-# define MEMORY_USAGE() 0
-# define MEMORY_VERIFY(ptr) mwIsSafeAddr(ptr, 1)
-# define MEMORY_CHECK() CHECK()
+# define MALLOC(n,file,line,func) mwMalloc((n),(file),(line))
+# define CALLOC(m,n,file,line,func) mwCalloc((m),(n),(file),(line))
+# define REALLOC(p,n,file,line,func) mwRealloc((p),(n),(file),(line))
+# define STRDUP(p,file,line,func) mwStrdup((p),(file),(line))
+# define FREE(p,file,line,func) mwFree((p),(file),(line))
+# define MEMORY_USAGE() (size_t)0
+# define MEMORY_VERIFY(ptr) mwIsSafeAddr((ptr), 1)
+# define MEMORY_CHECK() CHECK()
#elif defined(DMALLOC)
# include <string.h>
# include <stdlib.h>
# include "dmalloc.h"
-# define MALLOC(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0)
-# define CALLOC(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0)
-# define REALLOC(p,n,file,line,func) dmalloc_realloc((file),(line),(p),(n),DMALLOC_FUNC_REALLOC,0)
-# define STRDUP(p,file,line,func) strdup(p)
-# define FREE(p,file,line,func) free(p)
-# define MEMORY_USAGE() dmalloc_memory_allocated()
-# define MEMORY_VERIFY(ptr) (dmalloc_verify(ptr) == DMALLOC_VERIFY_NOERROR)
-# define MEMORY_CHECK() dmalloc_log_stats(); dmalloc_log_unfreed()
+# define MALLOC(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0)
+# define CALLOC(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0)
+# define REALLOC(p,n,file,line,func) dmalloc_realloc((file),(line),(p),(n),DMALLOC_FUNC_REALLOC,0)
+# define STRDUP(p,file,line,func) strdup(p)
+# define FREE(p,file,line,func) free(p)
+# define MEMORY_USAGE() dmalloc_memory_allocated()
+# define MEMORY_VERIFY(ptr) (dmalloc_verify(ptr) == DMALLOC_VERIFY_NOERROR)
+# define MEMORY_CHECK() do { dmalloc_log_stats(); dmalloc_log_unfreed() } while(0)
#elif defined(GCOLLECT)
@@ -48,24 +54,26 @@
# else
# define RETURN_ADDR
# endif
-# define MALLOC(n,file,line,func) GC_debug_malloc((n), RETURN_ADDR (file),(line))
-# define CALLOC(m,n,file,line,func) GC_debug_malloc((m)*(n), RETURN_ADDR (file),(line))
-# define REALLOC(p,n,file,line,func) GC_debug_realloc((p),(n), RETURN_ADDR (file),(line))
-# define STRDUP(p,file,line,func) GC_debug_strdup((p), RETURN_ADDR (file),(line))
-# define FREE(p,file,line,func) GC_debug_free(p)
-# define MEMORY_USAGE() GC_get_heap_size()
-# define MEMORY_VERIFY(ptr) (GC_base(ptr) != NULL)
-# define MEMORY_CHECK() GC_gcollect()
+# define MALLOC(n,file,line,func) GC_debug_malloc((n), RETURN_ADDR (file),(line))
+# define CALLOC(m,n,file,line,func) GC_debug_malloc((m)*(n), RETURN_ADDR (file),(line))
+# define REALLOC(p,n,file,line,func) GC_debug_realloc((p),(n), RETURN_ADDR (file),(line))
+# define STRDUP(p,file,line,func) GC_debug_strdup((p), RETURN_ADDR (file),(line))
+# define FREE(p,file,line,func) GC_debug_free(p)
+# define MEMORY_USAGE() GC_get_heap_size()
+# define MEMORY_VERIFY(ptr) (GC_base(ptr) != NULL)
+# define MEMORY_CHECK() GC_gcollect()
+
+# undef RETURN_ADDR
#else
-# define MALLOC(n,file,line,func) malloc(n)
-# define CALLOC(m,n,file,line,func) calloc((m),(n))
-# define REALLOC(p,n,file,line,func) realloc((p),(n))
-# define STRDUP(p,file,line,func) strdup(p)
-# define FREE(p,file,line,func) free(p)
-# define MEMORY_USAGE() 0
-# define MEMORY_VERIFY(ptr) true
+# define MALLOC(n,file,line,func) malloc(n)
+# define CALLOC(m,n,file,line,func) calloc((m),(n))
+# define REALLOC(p,n,file,line,func) realloc((p),(n))
+# define STRDUP(p,file,line,func) strdup(p)
+# define FREE(p,file,line,func) free(p)
+# define MEMORY_USAGE() (size_t)0
+# define MEMORY_VERIFY(ptr) true
# define MEMORY_CHECK()
#endif
@@ -227,14 +235,13 @@ static size_t hash2size( unsigned short hash )
}
}
-void* _mmalloc(size_t size, const char *file, int line, const char *func )
-{
+void *mmalloc_(size_t size, const char *file, int line, const char *func) {
struct block *block;
short size_hash = size2hash( size );
struct unit_head *head;
if (((long) size) < 0) {
- ShowError("_mmalloc: %d\n", size);
+ ShowError("mmalloc_: %"PRIdS"\n", size);
return NULL;
}
@@ -265,7 +272,8 @@ void* _mmalloc(size_t size, const char *file, int line, const char *func )
*(long*)((char*)p + sizeof(struct unit_head_large) - sizeof(long) + size) = 0xdeadbeaf;
return (char *)p + sizeof(struct unit_head_large) - sizeof(long);
} else {
- ShowFatalError("Memory manager::memmgr_alloc failed (allocating %d+%d bytes at %s:%d).\n", sizeof(struct unit_head_large), size, file, line);
+ ShowFatalError("Memory manager::memmgr_alloc failed (allocating %"PRIuS"+%"PRIuS" bytes at %s:%d).\n",
+ sizeof(struct unit_head_large), size, file, line);
exit(EXIT_FAILURE);
}
}
@@ -333,15 +341,13 @@ void* _mmalloc(size_t size, const char *file, int line, const char *func )
return (char *)head + sizeof(struct unit_head) - sizeof(long);
}
-void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func )
-{
+void *mcalloc_(size_t num, size_t size, const char *file, int line, const char *func) {
void *p = iMalloc->malloc(num * size,file,line,func);
memset(p,0,num * size);
return p;
}
-void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func )
-{
+void *mrealloc_(void *memblock, size_t size, const char *file, int line, const char *func) {
size_t old_size;
if(memblock == NULL) {
return iMalloc->malloc(size,file,line,func);
@@ -365,8 +371,38 @@ void* _mrealloc(void *memblock, size_t size, const char *file, int line, const c
}
}
-char* _mstrdup(const char *p, const char *file, int line, const char *func )
-{
+/* a mrealloc_ clone with the difference it 'z'eroes the newly created memory */
+void *mreallocz_(void *memblock, size_t size, const char *file, int line, const char *func) {
+ size_t old_size;
+ void *p = NULL;
+
+ if(memblock == NULL) {
+ p = iMalloc->malloc(size,file,line,func);
+ memset(p,0,size);
+ return p;
+ }
+
+ old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(long)))->size;
+ if( old_size == 0 ) {
+ old_size = ((struct unit_head_large *)((char *)memblock - sizeof(struct unit_head_large) + sizeof(long)))->size;
+ }
+ if(old_size > size) {
+ // Size reduction - return> as it is (negligence)
+ return memblock;
+ } else {
+ // Size Large
+ p = iMalloc->malloc(size,file,line,func);
+ if(p != NULL) {
+ memcpy(p,memblock,old_size);
+ memset((char*)p+old_size,0,size-old_size);
+ }
+ iMalloc->free(memblock,file,line,func);
+ return p;
+ }
+}
+
+
+char *mstrdup_(const char *p, const char *file, int line, const char *func) {
if(p == NULL) {
return NULL;
} else {
@@ -377,12 +413,11 @@ char* _mstrdup(const char *p, const char *file, int line, const char *func )
}
}
-void _mfree(void *ptr, const char *file, int line, const char *func )
-{
+void mfree_(void *ptr, const char *file, int line, const char *func) {
struct unit_head *head;
if (ptr == NULL)
- return;
+ return;
head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(long));
if(head->size == 0) {
@@ -534,22 +569,18 @@ size_t memmgr_usage (void)
static char memmer_logfile[128];
static FILE *log_fp;
-static void memmgr_log (char *buf)
-{
+static void memmgr_log(char *buf, char *vcsinfo) {
if( !log_fp ) {
time_t raw;
struct tm* t;
- const char* svn = get_svn_revision();
- const char* git = get_git_hash();
log_fp = fopen(memmer_logfile,"at");
if (!log_fp) log_fp = stdout;
time(&raw);
t = localtime(&raw);
- fprintf(log_fp, "\nMemory manager: Memory leaks found at %d/%02d/%02d %02dh%02dm%02ds (rev %s).\n",
- (t->tm_year+1900), (t->tm_mon+1), t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
- git[0] != HERC_UNKNOWN_VER ? git : svn[0] != HERC_UNKNOWN_VER ? svn : "Unknown");
+ fprintf(log_fp, "\nMemory manager: Memory leaks found at %d/%02d/%02d %02dh%02dm%02ds (%s).\n",
+ (t->tm_year+1900), (t->tm_mon+1), t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, vcsinfo);
}
fprintf(log_fp, "%s", buf);
return;
@@ -606,10 +637,12 @@ static void memmgr_final (void)
{
struct block *block = block_first;
struct unit_head_large *large = unit_head_large_first;
-
+ char vcsinfo[256];
#ifdef LOG_MEMMGR
int count = 0;
#endif /* LOG_MEMMGR */
+ snprintf(vcsinfo, sizeof(vcsinfo), "%s rev '%s'", sysinfo->vcstype(), sysinfo->vcsrevision_src()); // Cache VCS info before we free() it
+ sysinfo->final();
while (block) {
if (block->unit_used) {
@@ -623,7 +656,7 @@ static void memmgr_final (void)
sprintf (buf,
"%04d : %s line %d size %lu address 0x%p\n", ++count,
head->file, head->line, (unsigned long)head->size, ptr);
- memmgr_log (buf);
+ memmgr_log(buf, vcsinfo);
#endif /* LOG_MEMMGR */
// get block pointer and free it [celest]
iMalloc->free(ptr, ALC_MARK);
@@ -640,7 +673,7 @@ static void memmgr_final (void)
sprintf (buf,
"%04d : %s line %d size %lu address 0x%p\n", ++count,
large->unit_head.file, large->unit_head.line, (unsigned long)large->size, &large->unit_head.checksum);
- memmgr_log (buf);
+ memmgr_log(buf, vcsinfo);
#endif /* LOG_MEMMGR */
large2 = large->next;
FREE(large,file,line,func);
@@ -665,14 +698,14 @@ void memmgr_report (int extra) {
struct {
const char *file;
unsigned short line;
- unsigned int size;
+ size_t size;
unsigned int count;
} data[100];
memset(&data, 0, sizeof(data));
if( extra != 0 )
msize = extra;
-
+
while (block) {
if (block->unit_used) {
int i;
@@ -728,10 +761,10 @@ void memmgr_report (int extra) {
ShowMessage("[malloc] : reporting %u instances | %.2f MB\n",count,(double)((size)/1024)/1024);
ShowMessage("[malloc] : internal usage %.2f MB | %.2f MB\n",(double)((memmgr_usage_bytes_t-memmgr_usage_bytes)/1024)/1024,(double)((memmgr_usage_bytes_t)/1024)/1024);
- if( extra ) {
- ShowMessage("[malloc] : unit_head_large: %d bytes\n",sizeof(struct unit_head_large));
- ShowMessage("[malloc] : unit_head: %d bytes\n",sizeof(struct unit_head));
- ShowMessage("[malloc] : block: %d bytes\n",sizeof(struct block));
+ if (extra) {
+ ShowMessage("[malloc] : unit_head_large: %"PRIuS" bytes\n", sizeof(struct unit_head_large));
+ ShowMessage("[malloc] : unit_head: %"PRIuS" bytes\n", sizeof(struct unit_head));
+ ShowMessage("[malloc] : block: %"PRIuS" bytes\n", sizeof(struct block));
}
}
@@ -740,7 +773,7 @@ static void memmgr_init (void)
{
#ifdef LOG_MEMMGR
sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME);
- ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile);
+ ShowStatus("Memory manager initialized: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile);
memset(hash_unfill, 0, sizeof(hash_unfill));
#endif /* LOG_MEMMGR */
}
@@ -748,7 +781,7 @@ static void memmgr_init (void)
/*======================================
-* Initialise
+* Initialize
*--------------------------------------
*/
@@ -784,6 +817,8 @@ void malloc_final (void) {
memmgr_final ();
#endif
MEMORY_CHECK();
+ if( iMalloc->post_shutdown )
+ iMalloc->post_shutdown();
}
void malloc_init (void) {
@@ -813,16 +848,19 @@ void malloc_defaults(void) {
// Athena's built-in Memory Manager
#ifdef USE_MEMMGR
- iMalloc->malloc = _mmalloc;
- iMalloc->calloc = _mcalloc;
- iMalloc->realloc = _mrealloc;
- iMalloc->astrdup = _mstrdup;
- iMalloc->free = _mfree;
+ iMalloc->malloc = mmalloc_;
+ iMalloc->calloc = mcalloc_;
+ iMalloc->realloc = mrealloc_;
+ iMalloc->reallocz= mreallocz_;
+ iMalloc->astrdup = mstrdup_;
+ iMalloc->free = mfree_;
#else
iMalloc->malloc = aMalloc_;
iMalloc->calloc = aCalloc_;
iMalloc->realloc = aRealloc_;
+ iMalloc->reallocz= aRealloc_;/* not using memory manager huhum o.o perhaps we could still do something about */
iMalloc->astrdup = aStrdup_;
iMalloc->free = aFree_;
#endif
+ iMalloc->post_shutdown = NULL;
}
diff --git a/src/common/malloc.h b/src/common/malloc.h
index 834781905..8dace2d68 100644
--- a/src/common/malloc.h
+++ b/src/common/malloc.h
@@ -1,8 +1,8 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#ifndef _MALLOC_H_
-#define _MALLOC_H_
+#ifndef COMMON_MALLOC_H
+#define COMMON_MALLOC_H
#include "../common/cbasetypes.h"
@@ -30,11 +30,12 @@
#undef LOG_MEMMGR
#endif
-# define aMalloc(n) iMalloc->malloc (n,ALC_MARK)
-# define aCalloc(m,n) iMalloc->calloc (m,n,ALC_MARK)
-# define aRealloc(p,n) iMalloc->realloc (p,n,ALC_MARK)
-# define aStrdup(p) iMalloc->astrdup (p,ALC_MARK)
-# define aFree(p) iMalloc->free (p,ALC_MARK)
+# define aMalloc(n) (iMalloc->malloc((n),ALC_MARK))
+# define aCalloc(m,n) (iMalloc->calloc((m),(n),ALC_MARK))
+# define aRealloc(p,n) (iMalloc->realloc((p),(n),ALC_MARK))
+# define aReallocz(p,n) (iMalloc->reallocz((p),(n),ALC_MARK))
+# define aStrdup(p) (iMalloc->astrdup((p),ALC_MARK))
+# define aFree(p) (iMalloc->free((p),ALC_MARK))
/////////////// Buffer Creation /////////////////
// Full credit for this goes to Shinomori [Ajarn]
@@ -46,15 +47,15 @@
#else // others don't, so we emulate them
-#define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc (size, sizeof(type))
+#define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc((size), sizeof(type))
#define DELETE_BUFFER(name) aFree(name)
#endif
////////////// Others //////////////////////////
// should be merged with any of above later
-#define CREATE(result, type, number) (result) = (type *) aCalloc ((number), sizeof(type))
-#define RECREATE(result, type, number) (result) = (type *) aRealloc ((result), sizeof(type) * (number))
+#define CREATE(result, type, number) ((result) = (type *) aCalloc((number), sizeof(type)))
+#define RECREATE(result, type, number) ((result) = (type *) aReallocz((result), sizeof(type) * (number)))
////////////////////////////////////////////////
@@ -67,20 +68,24 @@
void malloc_defaults(void);
struct malloc_interface {
+ void (*init) (void);
+ void (*final) (void);
+ /* */
void* (*malloc )(size_t size, const char *file, int line, const char *func);
void* (*calloc )(size_t num, size_t size, const char *file, int line, const char *func);
void* (*realloc )(void *p, size_t size, const char *file, int line, const char *func);
+ void* (*reallocz)(void *p, size_t size, const char *file, int line, const char *func);
char* (*astrdup )(const char *p, const char *file, int line, const char *func);
void (*free )(void *p, const char *file, int line, const char *func);
-
+ /* */
void (*memory_check)(void);
bool (*verify_ptr)(void* ptr);
size_t (*usage) (void);
- void (*init) (void);
- void (*final) (void);
-} iMalloc_s;
+ /* */
+ void (*post_shutdown) (void);
+};
void memmgr_report (int extra);
struct malloc_interface *iMalloc;
-#endif /* _MALLOC_H_ */
+#endif /* COMMON_MALLOC_H */
diff --git a/src/common/mapindex.c b/src/common/mapindex.c
index 83de21b2b..f540c98d8 100644
--- a/src/common/mapindex.c
+++ b/src/common/mapindex.c
@@ -2,26 +2,23 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/mmo.h"
-#include "../common/showmsg.h"
-#include "../common/malloc.h"
-#include "../common/strlib.h"
-#include "../common/db.h"
+#define HERCULES_CORE
+
#include "mapindex.h"
-#include <string.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
-struct _indexes {
- char name[MAP_NAME_LENGTH]; //Stores map name
-} indexes[MAX_MAPINDEX];
-
-int max_index = 0;
+#include "../common/db.h"
+#include "../common/malloc.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
-char mapindex_cfgfile[80] = "db/map_index.txt";
+/* mapindex.c interface source */
+struct mapindex_interface mapindex_s;
-#define mapindex_exists(id) (indexes[id].name[0] != '\0')
/// Retrieves the map name from 'string' (removing .gat extension if present).
/// Result gets placed either into 'buf' or in a static local buffer.
const char* mapindex_getmapname(const char* string, char* output) {
@@ -52,12 +49,12 @@ const char* mapindex_getmapname_ext(const char* string, char* output) {
size_t len;
strcpy(buf,string);
- sscanf(string,"%*[^#]%*[#]%s",buf);
+ sscanf(string, "%*[^#]%*[#]%15s", buf);
len = safestrnlen(buf, MAP_NAME_LENGTH);
if (len == MAP_NAME_LENGTH) {
- ShowWarning("(mapindex_normalize_name) Map name '%*s' is too long!\n", 2*MAP_NAME_LENGTH, buf);
+ ShowWarning("(mapindex_normalize_name) Map name '%s' is too long!\n", buf);
len--;
}
safestrncpy(dest, buf, len+1);
@@ -73,14 +70,13 @@ const char* mapindex_getmapname_ext(const char* string, char* output) {
}
/// Adds a map to the specified index
-/// Returns 1 if successful, 0 oherwise
+/// Returns 1 if successful, 0 otherwise
int mapindex_addmap(int index, const char* name) {
char map_name[MAP_NAME_LENGTH];
if (index == -1){
- for (index = 1; index < max_index; index++) {
- //if (strcmp(indexes[index].name,"#CLEARED#")==0)
- if (indexes[index].name[0] == '\0')
+ for (index = 1; index < mapindex->num; index++) {
+ if (mapindex->list[index].name[0] == '\0')
break;
}
}
@@ -90,7 +86,7 @@ int mapindex_addmap(int index, const char* name) {
return 0;
}
- mapindex_getmapname(name, map_name);
+ mapindex->getmapname(name, map_name);
if (map_name[0] == '\0') {
ShowError("(mapindex_add) Cannot add maps with no name.\n");
@@ -103,14 +99,15 @@ int mapindex_addmap(int index, const char* name) {
}
if (mapindex_exists(index)) {
- ShowWarning("(mapindex_add) Overriding index %d: map \"%s\" -> \"%s\"\n", index, indexes[index].name, map_name);
- strdb_remove(mapindex_db, indexes[index].name);
+ ShowWarning("(mapindex_add) Overriding index %d: map \"%s\" -> \"%s\"\n", index, mapindex->list[index].name, map_name);
+ strdb_remove(mapindex->db, mapindex->list[index].name);
}
- safestrncpy(indexes[index].name, map_name, MAP_NAME_LENGTH);
- strdb_iput(mapindex_db, map_name, index);
- if (max_index <= index)
- max_index = index+1;
+ safestrncpy(mapindex->list[index].name, map_name, MAP_NAME_LENGTH);
+ strdb_iput(mapindex->db, map_name, index);
+
+ if (mapindex->num <= index)
+ mapindex->num = index+1;
return index;
}
@@ -119,9 +116,9 @@ unsigned short mapindex_name2id(const char* name) {
int i;
char map_name[MAP_NAME_LENGTH];
- mapindex_getmapname(name, map_name);
+ mapindex->getmapname(name, map_name);
- if( (i = strdb_iget(mapindex_db, map_name)) )
+ if( (i = strdb_iget(mapindex->db, map_name)) )
return i;
ShowDebug("mapindex_name2id: Map \"%s\" not found in index list!\n", map_name);
@@ -131,24 +128,25 @@ unsigned short mapindex_name2id(const char* name) {
const char* mapindex_id2name_sub(unsigned short id,const char *file, int line, const char *func) {
if (id > MAX_MAPINDEX || !mapindex_exists(id)) {
ShowDebug("mapindex_id2name: Requested name for non-existant map index [%d] in cache. %s:%s:%d\n", id,file,func,line);
- return indexes[0].name; // dummy empty string so that the callee doesn't crash
+ return mapindex->list[0].name; // dummy empty string so that the callee doesn't crash
}
- return indexes[id].name;
+ return mapindex->list[id].name;
}
-void mapindex_init(void) {
+int mapindex_init(void) {
FILE *fp;
char line[1024];
int last_index = -1;
- int index;
+ int index, total = 0;
char map_name[12];
- if( ( fp = fopen(mapindex_cfgfile,"r") ) == NULL ){
- ShowFatalError("Unable to read mapindex config file %s!\n", mapindex_cfgfile);
+ if( ( fp = fopen(mapindex->config_file,"r") ) == NULL ){
+ ShowFatalError("Unable to read mapindex config file %s!\n", mapindex->config_file);
exit(EXIT_FAILURE); //Server can't really run without this file.
}
- memset (&indexes, 0, sizeof (indexes));
- mapindex_db = strdb_alloc(DB_OPT_DUP_KEY, MAP_NAME_LENGTH);
+
+ mapindex->db = strdb_alloc(DB_OPT_DUP_KEY, MAP_NAME_LENGTH);
+
while(fgets(line, sizeof(line), fp)) {
if(line[0] == '/' && line[1] == '/')
continue;
@@ -157,7 +155,8 @@ void mapindex_init(void) {
case 1: //Map with no ID given, auto-assign
index = last_index+1;
case 2: //Map with ID given
- mapindex_addmap(index,map_name);
+ mapindex->addmap(index,map_name);
+ total++;
break;
default:
continue;
@@ -166,16 +165,40 @@ void mapindex_init(void) {
}
fclose(fp);
- if( !strdb_iget(mapindex_db, MAP_DEFAULT) ) {
+ if( !strdb_iget(mapindex->db, MAP_DEFAULT) ) {
ShowError("mapindex_init: MAP_DEFAULT '%s' not found in cache! update mapindex.h MAP_DEFAULT var!!!\n",MAP_DEFAULT);
}
+
+ return total;
}
-int mapindex_removemap(int index){
- indexes[index].name[0] = '\0';
- return 0;
+void mapindex_removemap(int index){
+ strdb_remove(mapindex->db, mapindex->list[index].name);
+ mapindex->list[index].name[0] = '\0';
}
void mapindex_final(void) {
- db_destroy(mapindex_db);
+ db_destroy(mapindex->db);
+}
+
+void mapindex_defaults(void) {
+ mapindex = &mapindex_s;
+
+ /* TODO: place it in inter-server.conf? */
+ snprintf(mapindex->config_file, 80, "%s","db/map_index.txt");
+ /* */
+ mapindex->db = NULL;
+ mapindex->num = 0;
+ memset (&mapindex->list, 0, sizeof (mapindex->list));
+
+ /* */
+ mapindex->init = mapindex_init;
+ mapindex->final = mapindex_final;
+ /* */
+ mapindex->addmap = mapindex_addmap;
+ mapindex->removemap = mapindex_removemap;
+ mapindex->getmapname = mapindex_getmapname;
+ mapindex->getmapname_ext = mapindex_getmapname_ext;
+ mapindex->name2id = mapindex_name2id;
+ mapindex->id2name = mapindex_id2name_sub;
}
diff --git a/src/common/mapindex.h b/src/common/mapindex.h
index 43953a8e0..446a2422d 100644
--- a/src/common/mapindex.h
+++ b/src/common/mapindex.h
@@ -2,21 +2,20 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _MAPINDEX_H_
-#define _MAPINDEX_H_
+#ifndef COMMON_MAPINDEX_H
+#define COMMON_MAPINDEX_H
#include "../common/db.h"
+#include "../common/mmo.h"
+
+#define MAX_MAPINDEX 2000
+
+/* wohoo, someone look at all those |: map_default could (or *should*) be a char-server.conf */
// When a map index search fails, return results from what map? default:prontera
#define MAP_DEFAULT "prontera"
#define MAP_DEFAULT_X 150
#define MAP_DEFAULT_Y 150
-DBMap *mapindex_db;
-
-//File in charge of assigning a numberic ID to each map in existance for space saving when passing map info between servers.
-extern char mapindex_cfgfile[80];
-
-#define MAX_MAPINDEX 2000
//Some definitions for the mayor city maps.
#define MAP_PRONTERA "prontera"
@@ -56,15 +55,39 @@ extern char mapindex_cfgfile[80];
#define MAP_MALAYA "malaya"
#define MAP_ECLAGE "eclage"
-const char* mapindex_getmapname(const char* string, char* output);
-const char* mapindex_getmapname_ext(const char* string, char* output);
-unsigned short mapindex_name2id(const char*);
-#define mapindex_id2name(n) mapindex_id2name_sub(n,__FILE__, __LINE__, __func__)
-const char* mapindex_id2name_sub(unsigned short,const char *file, int line, const char *func);
-void mapindex_init(void);
-void mapindex_final(void);
+#define mapindex_id2name(n) mapindex->id2name((n),__FILE__, __LINE__, __func__)
+#define mapindex_exists(n) ( mapindex->list[(n)].name[0] != '\0' )
+
+/**
+ * mapindex.c interface
+ **/
+struct mapindex_interface {
+ char config_file[80];
+ /* mapname (str) -> index (int) */
+ DBMap *db;
+ /* number of entries in the index table */
+ int num;
+ /* index list -- since map server map count is *unlimited* this should be too */
+ struct {
+ char name[MAP_NAME_LENGTH];
+ } list[MAX_MAPINDEX];
+ /* */
+ int (*init) (void);
+ void (*final) (void);
+ /* */
+ int (*addmap) (int index, const char* name);
+ void (*removemap) (int index);
+ const char* (*getmapname) (const char* string, char* output);
+ /* TODO: server shouldn't be handling the extension, game client automatically adds .gat/.rsw/.whatever
+ * and there are official map names taking advantage of it that we cant support due to the .gat room being saved */
+ const char* (*getmapname_ext) (const char* string, char* output);
+ /* TODO: Hello World! make up your mind, this thing is int on some places and unsigned short on others */
+ unsigned short (*name2id) (const char*);
+ const char* (*id2name) (unsigned short,const char *file, int line, const char *func);
+};
+
+struct mapindex_interface *mapindex;
-int mapindex_addmap(int index, const char* name);
-int mapindex_removemap(int index);
+void mapindex_defaults(void);
-#endif /* _MAPINDEX_H_ */
+#endif /* COMMON_MAPINDEX_H */
diff --git a/src/common/md5calc.c b/src/common/md5calc.c
index 05fde42cc..e7b506e27 100644
--- a/src/common/md5calc.c
+++ b/src/common/md5calc.c
@@ -6,11 +6,15 @@
*
***********************************************************/
-#include "../common/random.h"
+#define HERCULES_CORE
+
#include "md5calc.h"
-#include <string.h>
+
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+
+#include "../common/random.h"
#ifndef UINT_MAX
#define UINT_MAX 4294967295U
diff --git a/src/common/md5calc.h b/src/common/md5calc.h
index 323affa2c..740e2edcc 100644
--- a/src/common/md5calc.h
+++ b/src/common/md5calc.h
@@ -1,8 +1,8 @@
-#ifndef _MD5CALC_H_
-#define _MD5CALC_H_
+#ifndef COMMON_MD5CALC_H
+#define COMMON_MD5CALC_H
void MD5_String(const char * string, char * output);
void MD5_Binary(const char * string, unsigned char * output);
void MD5_Salt(unsigned int len, char * output);
-#endif /* _MD5CALC_H_ */
+#endif /* COMMON_MD5CALC_H */
diff --git a/src/common/mempool.c b/src/common/mempool.c
deleted file mode 100644
index 5eccbf178..000000000
--- a/src/common/mempool.c
+++ /dev/null
@@ -1,568 +0,0 @@
-
-//
-// Memory Pool Implementation (Threadsafe)
-//
-//
-// Author: Florian Wilkemeyer <fw@f-ws.de>
-//
-// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-//
-//
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#ifdef WIN32
-#include "../common/winapi.h"
-#else
-#include <unistd.h>
-#endif
-
-#include "../common/cbasetypes.h"
-#include "../common/showmsg.h"
-#include "../common/mempool.h"
-#include "../common/atomic.h"
-#include "../common/spinlock.h"
-#include "../common/thread.h"
-#include "../common/malloc.h"
-#include "../common/mutex.h"
-
-#define ALIGN16 ra_align(16)
-#define ALIGN_TO(x, a) (x + ( a - ( x % a) ) )
-#define ALIGN_TO_16(x) ALIGN_TO(x, 16)
-
-#undef MEMPOOL_DEBUG
-#define MEMPOOLASSERT
-
-
-#define NODE_TO_DATA(x) ( ((char*)x) + sizeof(struct node) )
-#define DATA_TO_NODE(x) ( (struct node*)(((char*)x) - sizeof(struct node)) )
-struct ra_align(16) node{
- void *next;
- void *segment;
-#ifdef MEMPOOLASSERT
- bool used;
- uint64 magic;
- #define NODE_MAGIC 0xBEEF00EAEACAFE07ll
-#endif
-};
-
-
-// The Pointer to this struct is the base address of the segment itself.
-struct pool_segment{
- mempool pool; // pool, this segment belongs to
- struct pool_segment *next;
- int64 num_nodes_total;
- int64 num_bytes;
-};
-
-
-struct mempool{
- // Settings
- char *name;
- uint64 elem_size;
- uint64 elem_realloc_step;
- int64 elem_realloc_thresh;
-
- // Callbacks that get called for every node that gets allocated
- // Example usage: initialization of mutex/lock for each node.
- memPoolOnNodeAllocationProc onalloc;
- memPoolOnNodeDeallocationProc ondealloc;
-
- // Locks
- SPIN_LOCK segmentLock;
- SPIN_LOCK nodeLock;
-
-
- // Internal
- struct pool_segment *segments;
- struct node *free_list;
-
- volatile int64 num_nodes_total;
- volatile int64 num_nodes_free;
-
- volatile int64 num_segments;
- volatile int64 num_bytes_total;
-
- volatile int64 peak_nodes_used; // Peak Node Usage
- volatile int64 num_realloc_events; // Number of reallocations done. (allocate additional nodes)
-
- // list (used for global management such as allocator..)
- struct mempool *next;
-} ra_align(8); // Dont touch the alignment, otherwise interlocked functions are broken ..
-
-
-///
-// Implementation:
-//
-static void segment_allocate_add(mempool p, uint64 count);
-
-static SPIN_LOCK l_mempoolListLock;
-static mempool l_mempoolList = NULL;
-static rAthread l_async_thread = NULL;
-static ramutex l_async_lock = NULL;
-static racond l_async_cond = NULL;
-static volatile int32 l_async_terminate = 0;
-
-static void *mempool_async_allocator(void *x){
- mempool p;
-
-
- while(1){
- if(l_async_terminate > 0)
- break;
-
- EnterSpinLock(&l_mempoolListLock);
-
- for(p = l_mempoolList; p != NULL; p = p->next){
-
- if(p->num_nodes_free < p->elem_realloc_thresh){
- // add new segment.
- segment_allocate_add(p, p->elem_realloc_step);
- // increase stats counter
- InterlockedIncrement64(&p->num_realloc_events);
- }
-
- }
-
- LeaveSpinLock(&l_mempoolListLock);
-
- ramutex_lock( l_async_lock );
- racond_wait( l_async_cond, l_async_lock, -1 );
- ramutex_unlock( l_async_lock );
- }
-
-
- return NULL;
-}//end: mempool_async_allocator()
-
-
-void mempool_init(){
-
- if( rand()%2 + 1 )
- return;
-
- if(sizeof(struct node)%16 != 0 ){
- ShowFatalError("mempool_init: struct node alignment failure. %u != multiple of 16\n", sizeof(struct node));
- exit(EXIT_FAILURE);
- }
-
- // Global List start
- InitializeSpinLock(&l_mempoolListLock);
- l_mempoolList = NULL;
-
- // Initialize mutex + stuff needed for async allocator worker.
- l_async_terminate = 0;
- l_async_lock = ramutex_create();
- l_async_cond = racond_create();
-
- l_async_thread = rathread_createEx(mempool_async_allocator, NULL, 1024*1024, RAT_PRIO_NORMAL);
- if(l_async_thread == NULL){
- ShowFatalError("mempool_init: cannot spawn Async Allocator Thread.\n");
- exit(EXIT_FAILURE);
- }
-
-}//end: mempool_init()
-
-
-void mempool_final(){
- mempool p, pn;
-
- if( rand()%2 + 1 )
- return;
-
- ShowStatus("Mempool: Terminating async. allocation worker and remaining pools.\n");
-
- // Terminate worker / wait until its terminated.
- InterlockedIncrement(&l_async_terminate);
- racond_signal(l_async_cond);
- rathread_wait(l_async_thread, NULL);
-
- // Destroy cond var and mutex.
- racond_destroy( l_async_cond );
- ramutex_destroy( l_async_lock );
-
- // Free remaining mempools
- // ((bugged code! this should halppen, every mempool should
- // be freed by the subsystem that has allocated it.)
- //
- EnterSpinLock(&l_mempoolListLock);
- p = l_mempoolList;
- while(1){
- if(p == NULL)
- break;
-
- pn = p->next;
-
- ShowWarning("Mempool [%s] was not properly destroyed - forcing destroy.\n", p->name);
- mempool_destroy(p);
-
- p = pn;
- }
- LeaveSpinLock(&l_mempoolListLock);
-
-}//end: mempool_final()
-
-
-static void segment_allocate_add(mempool p, uint64 count){
-
- // Required Memory:
- // sz( segment )
- // count * sz( real_node_size )
- //
- // where real node size is:
- // ALIGN_TO_16( sz( node ) ) + p->elem_size
- // so the nodes usable address is nodebase + ALIGN_TO_16(sz(node))
- //
- size_t total_sz;
- struct pool_segment *seg = NULL;
- struct node *nodeList = NULL;
- struct node *node = NULL;
- char *ptr = NULL;
- uint64 i;
-
- total_sz = ALIGN_TO_16( sizeof(struct pool_segment) )
- + ( (size_t)count * (sizeof(struct node) + (size_t)p->elem_size) ) ;
-
-#ifdef MEMPOOL_DEBUG
- ShowDebug("Mempool [%s] Segment AllocateAdd (num: %u, total size: %0.2fMiB)\n", p->name, count, (float)total_sz/1024.f/1024.f);
-#endif
-
- // allocate! (spin forever until weve got the memory.)
- i=0;
- while(1){
- ptr = (char*)aMalloc(total_sz);
- if(ptr != NULL) break;
-
- i++; // increase failcount.
- if(!(i & 7)){
- ShowWarning("Mempool [%s] Segment AllocateAdd => System seems to be Out of Memory (%0.2f MiB). Try #%u\n", (float)total_sz/1024.f/1024.f, i);
-#ifdef WIN32
- Sleep(1000);
-#else
- sleep(1);
-#endif
- }else{
- rathread_yield(); /// allow/force vuln. ctxswitch
- }
- }//endwhile: allocation spinloop.
-
- // Clear Memory.
- memset(ptr, 0x00, total_sz);
-
- // Initialize segment struct.
- seg = (struct pool_segment*)ptr;
- ptr += ALIGN_TO_16(sizeof(struct pool_segment));
-
- seg->pool = p;
- seg->num_nodes_total = count;
- seg->num_bytes = total_sz;
-
-
- // Initialze nodes!
- nodeList = NULL;
- for(i = 0; i < count; i++){
- node = (struct node*)ptr;
- ptr += sizeof(struct node);
- ptr += p->elem_size;
-
- node->segment = seg;
-#ifdef MEMPOOLASSERT
- node->used = false;
- node->magic = NODE_MAGIC;
-#endif
-
- if(p->onalloc != NULL) p->onalloc( NODE_TO_DATA(node) );
-
- node->next = nodeList;
- nodeList = node;
- }
-
-
-
- // Link in Segment.
- EnterSpinLock(&p->segmentLock);
- seg->next = p->segments;
- p->segments = seg;
- LeaveSpinLock(&p->segmentLock);
-
- // Link in Nodes
- EnterSpinLock(&p->nodeLock);
- nodeList->next = p->free_list;
- p->free_list = nodeList;
- LeaveSpinLock(&p->nodeLock);
-
-
- // Increase Stats:
- InterlockedExchangeAdd64(&p->num_nodes_total, count);
- InterlockedExchangeAdd64(&p->num_nodes_free, count);
- InterlockedIncrement64(&p->num_segments);
- InterlockedExchangeAdd64(&p->num_bytes_total, total_sz);
-
-}//end: segment_allocate_add()
-
-
-mempool mempool_create(const char *name,
- uint64 elem_size,
- uint64 initial_count,
- uint64 realloc_count,
- memPoolOnNodeAllocationProc onNodeAlloc,
- memPoolOnNodeDeallocationProc onNodeDealloc){
- //..
- uint64 realloc_thresh;
- mempool pool;
- pool = (mempool)aCalloc( 1, sizeof(struct mempool) );
-
- if(pool == NULL){
- ShowFatalError("mempool_create: Failed to allocate %u bytes memory.\n", sizeof(struct mempool) );
- exit(EXIT_FAILURE);
- }
-
- // Check minimum initial count / realloc count requirements.
- if(initial_count < 50)
- initial_count = 50;
- if(realloc_count < 50)
- realloc_count = 50;
-
- // Set Reallocation threshold to 5% of realloc_count, at least 10.
- realloc_thresh = (realloc_count/100)*5; //
- if(realloc_thresh < 10)
- realloc_thresh = 10;
-
- // Initialize members..
- pool->name = aStrdup(name);
- pool->elem_size = ALIGN_TO_16(elem_size);
- pool->elem_realloc_step = realloc_count;
- pool->elem_realloc_thresh = realloc_thresh;
- pool->onalloc = onNodeAlloc;
- pool->ondealloc = onNodeDealloc;
-
- InitializeSpinLock(&pool->segmentLock);
- InitializeSpinLock(&pool->nodeLock);
-
- // Initial Statistic values:
- pool->num_nodes_total = 0;
- pool->num_nodes_free = 0;
- pool->num_segments = 0;
- pool->num_bytes_total = 0;
- pool->peak_nodes_used = 0;
- pool->num_realloc_events = 0;
-
- //
-#ifdef MEMPOOL_DEBUG
- ShowDebug("Mempool [%s] Init (ElemSize: %u, Initial Count: %u, Realloc Count: %u)\n", pool->name, pool->elem_size, initial_count, pool->elem_realloc_step);
-#endif
-
- // Allocate first segment directly :)
- segment_allocate_add(pool, initial_count);
-
-
- // Add Pool to the global pool list
- EnterSpinLock(&l_mempoolListLock);
- pool->next = l_mempoolList;
- l_mempoolList = pool;
- LeaveSpinLock(&l_mempoolListLock);
-
-
- return pool;
-}//end: mempool_create()
-
-
-void mempool_destroy(mempool p){
- struct pool_segment *seg, *segnext;
- struct node *niter;
- mempool piter, pprev;
- char *ptr;
- int64 i;
-
-#ifdef MEMPOOL_DEBUG
- ShowDebug("Mempool [%s] Destroy\n", p->name);
-#endif
-
- // Unlink from global list.
- EnterSpinLock(&l_mempoolListLock);
- piter = l_mempoolList;
- pprev = l_mempoolList;
- while(1){
- if(piter == NULL)
- break;
-
-
- if(piter == p){
- // unlink from list,
- //
- if(pprev == l_mempoolList){
- // this (p) is list begin. so set next as head.
- l_mempoolList = p->next;
- }else{
- // replace prevs next wuth our next.
- pprev->next = p->next;
- }
- break;
- }
-
- pprev = piter;
- piter = piter->next;
- }
-
- p->next = NULL;
- LeaveSpinLock(&l_mempoolListLock);
-
-
- // Get both locks.
- EnterSpinLock(&p->segmentLock);
- EnterSpinLock(&p->nodeLock);
-
-
- if(p->num_nodes_free != p->num_nodes_total)
- ShowWarning("Mempool [%s] Destroy - %u nodes are not freed properly!\n", p->name, (p->num_nodes_total - p->num_nodes_free) );
-
- // Free All Segments (this will also free all nodes)
- // The segment pointer is the base pointer to the whole segment.
- seg = p->segments;
- while(1){
- if(seg == NULL)
- break;
-
- segnext = seg->next;
-
- // ..
- if(p->ondealloc != NULL){
- // walk over the segment, and call dealloc callback!
- ptr = (char*)seg;
- ptr += ALIGN_TO_16(sizeof(struct pool_segment));
- for(i = 0; i < seg->num_nodes_total; i++){
- niter = (struct node*)ptr;
- ptr += sizeof(struct node);
- ptr += p->elem_size;
-#ifdef MEMPOOLASSERT
- if(niter->magic != NODE_MAGIC){
- ShowError("Mempool [%s] Destroy - walk over segment - node %p invalid magic!\n", p->name, niter);
- continue;
- }
-#endif
-
- p->ondealloc( NODE_TO_DATA(niter) );
-
-
- }
- }//endif: ondealloc callback?
-
- // simple ..
- aFree(seg);
-
- seg = segnext;
- }
-
- // Clear node ptr
- p->free_list = NULL;
- InterlockedExchange64(&p->num_nodes_free, 0);
- InterlockedExchange64(&p->num_nodes_total, 0);
- InterlockedExchange64(&p->num_segments, 0);
- InterlockedExchange64(&p->num_bytes_total, 0);
-
- LeaveSpinLock(&p->nodeLock);
- LeaveSpinLock(&p->segmentLock);
-
- // Free pool itself :D
- aFree(p->name);
- aFree(p);
-
-}//end: mempool_destroy()
-
-
-void *mempool_node_get(mempool p){
- struct node *node;
- int64 num_used;
-
- if(p->num_nodes_free < p->elem_realloc_thresh)
- racond_signal(l_async_cond);
-
- while(1){
-
- EnterSpinLock(&p->nodeLock);
-
- node = p->free_list;
- if(node != NULL)
- p->free_list = node->next;
-
- LeaveSpinLock(&p->nodeLock);
-
- if(node != NULL)
- break;
-
- rathread_yield();
- }
-
- InterlockedDecrement64(&p->num_nodes_free);
-
- // Update peak value
- num_used = (p->num_nodes_total - p->num_nodes_free);
- if(num_used > p->peak_nodes_used){
- InterlockedExchange64(&p->peak_nodes_used, num_used);
- }
-
-#ifdef MEMPOOLASSERT
- node->used = true;
-#endif
-
- return NODE_TO_DATA(node);
-}//end: mempool_node_get()
-
-
-void mempool_node_put(mempool p, void *data){
- struct node *node;
-
- node = DATA_TO_NODE(data);
-#ifdef MEMPOOLASSERT
- if(node->magic != NODE_MAGIC){
- ShowError("Mempool [%s] node_put failed, given address (%p) has invalid magic.\n", p->name, data);
- return; // lost,
- }
-
- {
- struct pool_segment *node_seg = node->segment;
- if(node_seg->pool != p){
- ShowError("Mempool [%s] node_put faild, given node (data address %p) doesnt belongs to this pool. ( Node Origin is [%s] )\n", p->name, data, node_seg->pool);
- return;
- }
- }
-
- // reset used flag.
- node->used = false;
-#endif
-
- //
- EnterSpinLock(&p->nodeLock);
- node->next = p->free_list;
- p->free_list = node;
- LeaveSpinLock(&p->nodeLock);
-
- InterlockedIncrement64(&p->num_nodes_free);
-
-}//end: mempool_node_put()
-
-
-mempool_stats mempool_get_stats(mempool pool){
- mempool_stats stats;
-
- // initialize all with zeros
- memset(&stats, 0x00, sizeof(mempool_stats));
-
- stats.num_nodes_total = pool->num_nodes_total;
- stats.num_nodes_free = pool->num_nodes_free;
- stats.num_nodes_used = (stats.num_nodes_total - stats.num_nodes_free);
- stats.num_segments = pool->num_segments;
- stats.num_realloc_events= pool->num_realloc_events;
- stats.peak_nodes_used = pool->peak_nodes_used;
- stats.num_bytes_total = pool->num_bytes_total;
-
- // Pushing such a large block over the stack as return value isnt nice
- // but lazy :) and should be okay in this case (Stats / Debug..)
- // if you dont like it - feel free and refactor it.
- return stats;
-}//end: mempool_get_stats()
-
diff --git a/src/common/mempool.h b/src/common/mempool.h
deleted file mode 100644
index aeaebe7fe..000000000
--- a/src/common/mempool.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef _rA_MEMPOOL_H_
-#define _rA_MEMPOOL_H_
-
-#include "../common/cbasetypes.h"
-
-typedef struct mempool *mempool;
-
-typedef void (*memPoolOnNodeAllocationProc)(void *ptr);
-typedef void (*memPoolOnNodeDeallocationProc)(void *ptr);
-
-typedef struct mempool_stats{
- int64 num_nodes_total;
- int64 num_nodes_free;
- int64 num_nodes_used;
-
- int64 num_segments;
- int64 num_realloc_events;
-
- int64 peak_nodes_used;
-
- int64 num_bytes_total;
-} mempool_stats;
-
-
-//
-void mempool_init();
-void mempool_final();
-
-
-/**
- * Creates a new Mempool
- *
- * @param name - Name of the pool (used for debug / error messages)
- * @param elem_size - size of each element
- * @param initial_count - preallocation count
- * @param realloc_count - #no of nodes being allocated when pool is running empty.
- * @param onNodeAlloc - Node Allocation callback (see @note!)
- * @param onNodeDealloc - Node Deallocation callback (see @note!)
- *
- * @note:
- * The onNode(De)alloc callbacks are only called once during segment allocation
- * (pool initialization / rallocation )
- * you can use this callbacks for example to initlaize a mutex or somethingelse
- * you definitly need during runtime
- *
- * @return not NULL
- */
-mempool mempool_create(const char *name,
- uint64 elem_size,
- uint64 initial_count,
- uint64 realloc_count,
-
- memPoolOnNodeAllocationProc onNodeAlloc,
- memPoolOnNodeDeallocationProc onNodeDealloc);
-
-
-/**
- * Destroys a Mempool
- *
- * @param pool - the mempool to destroy
- *
- * @note:
- * Everything gets deallocated, regardless if everything was freed properly!
- * So you have to ensure that all references are cleared properly!
- */
-void mempool_destroy(mempool pool);
-
-
-/**
- * Gets a new / empty node from the given mempool.
- *
- * @param pool - the pool to get an empty node from.
- *
- * @return Address of empty Node
- */
-void *mempool_node_get(mempool pool);
-
-
-/**
- * Returns the given node to the given mempool
- *
- * @param pool - the pool to put the node, to
- * @param node - the node to return
- */
-void mempool_node_put(mempool pool, void *node);
-
-
-/**
- * Returns Statistics for the given mempool
- *
- * @param pool - the pool to get thats for
- *
- * @note: i dont like pushing masses of values over the stack, too - but its lazy and okay for stats. (blacksirius)
- *
- * @return stats struct
- */
-mempool_stats mempool_get_stats(mempool pool);
-
-
-#endif
diff --git a/src/common/mmo.h b/src/common/mmo.h
index 8643d2b54..ff7c1da28 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -2,13 +2,14 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _MMO_H_
-#define _MMO_H_
+#ifndef COMMON_MMO_H
+#define COMMON_MMO_H
-#include "cbasetypes.h"
-#include "../common/db.h"
#include <time.h>
+#include "../common/cbasetypes.h"
+#include "../common/db.h"
+
// server->client protocol version
// 0 - pre-?
// 1 - ? - 0x196
@@ -25,7 +26,7 @@
// 20071106 - 2007-11-06aSakexe+ - 0x78, 0x7c, 0x22c
// 20080102 - 2008-01-02aSakexe+ - 0x2ec, 0x2ed , 0x2ee
// 20081126 - 2008-11-26aSakexe+ - 0x1a2
-// 20090408 - 2009-04-08aSakexe+ - 0x44a (dont use as it overlaps with RE client packets)
+// 20090408 - 2009-04-08aSakexe+ - 0x44a (don't use as it overlaps with RE client packets)
// 20080827 - 2008-08-27aRagexeRE+ - First RE Client
// 20081217 - 2008-12-17aRagexeRE+ - 0x6d (Note: This one still use old Char Info Packet Structure)
// 20081218 - 2008-12-17bRagexeRE+ - 0x6d (Note: From this one client use new Char Info Packet Structure)
@@ -48,22 +49,25 @@
// 20120307 - 2012-03-07aRagexeRE+ - 0x970
#ifndef PACKETVER
- #define PACKETVER 20120418
-#endif
+ #define PACKETVER 20131223
+#endif // PACKETVER
-// Comment the following line if your client is NOT ragexeRE (required because of conflicting packets in ragexe vs ragexeRE).
-#define PACKETVER_RE
+//Uncomment the following line if your client is ragexeRE instead of ragexe (required because of conflicting packets in ragexe vs ragexeRE).
+//#define ENABLE_PACKETVER_RE
+#ifdef ENABLE_PACKETVER_RE
+ #define PACKETVER_RE
+ #undef ENABLE_PACKETVER_RE
+#endif // DISABLE_PACKETVER_RE
// Client support for experimental RagexeRE UI present in 2012-04-10 and 2012-04-18
-#ifdef PACKETVER_RE
-#if (PACKETVER == 20120410) || (PACKETVER == 20120418)
- #define PARTY_RECRUIT
-#endif
-#endif
+#if defined(PACKETVER_RE) && ( PACKETVER == 20120410 || PACKETVER == 20120418 )
+#define PARTY_RECRUIT
+#endif // PACKETVER_RE && (PACKETVER == 20120410 || PACKETVER == 10120418)
// Comment the following line to disable sc_data saving. [Skotlex]
#define ENABLE_SC_SAVING
+#if PACKETVER >= 20070227
// Comment the following like to disable server-side hot-key saving support. [Skotlex]
// Note that newer clients no longer save hotkeys in the registry!
#define HOTKEY_SAVING
@@ -74,9 +78,18 @@
#elif PACKETVER < 20090617
// (36 = 9 skills x 4 bars) (0x07d9,254)
#define MAX_HOTKEYS 36
-#else
+#else // >= 20090617
// (38 = 9 skills x 4 bars & 2 Quickslots)(0x07d9,268)
#define MAX_HOTKEYS 38
+#endif // 20090603
+#endif // 20070227
+
+/* Feb 1st 2012 */
+#if PACKETVER >= 20120201
+# define NEW_CARTS
+# define MAX_CARTS 9
+#else
+# define MAX_CARTS 5
#endif
#define MAX_INVENTORY 100
@@ -88,31 +101,32 @@
//Max amount of a single stacked item
#define MAX_AMOUNT 30000
#define MAX_ZENY 1000000000
+
+//Official Limit: 2.1b ( the var that stores the money doesn't go much higher than this by default )
+#define MAX_BANK_ZENY 2100000000
+
+#define MAX_LEVEL 175
#define MAX_FAME 1000000000
#define MAX_CART 100
#define MAX_SKILL 1478
#define MAX_SKILL_ID 10015 // [Ind/Hercules] max used skill ID
-#define GLOBAL_REG_NUM 256 // Max permanent character variables per char
-#define ACCOUNT_REG_NUM 64 // Max permanent local account variables per account
-#define ACCOUNT_REG2_NUM 16 // Max permanent global account variables per account
-//Should hold the max of GLOBAL/ACCOUNT/ACCOUNT2 (needed for some arrays that hold all three)
-#define MAX_REG_NUM 256
+// Update this max as necessary. 86 is the value needed for Expanded Super Novice.
+#define MAX_SKILL_TREE 86
#define DEFAULT_WALK_SPEED 150
-#define MIN_WALK_SPEED 0
+#define MIN_WALK_SPEED 20 /* below 20 clips animation */
#define MAX_WALK_SPEED 1000
#define MAX_STORAGE 600
#define MAX_GUILD_STORAGE 600
#define MAX_PARTY 12
-#define MAX_GUILD 16+10*6 // Increased max guild members +6 per 1 extension levels [Lupus]
-#define MAX_GUILDPOSITION 20 // Increased max guild positions to accomodate for all members [Valaris] (removed) [PoW]
+#define MAX_GUILD (16+10*6) // Increased max guild members +6 per 1 extension levels [Lupus]
+#define MAX_GUILDPOSITION 20 // Increased max guild positions to accommodate for all members [Valaris] (removed) [PoW]
#define MAX_GUILDEXPULSION 32
#define MAX_GUILDALLIANCE 16
-#define MAX_GUILDSKILL 15 // Increased max guild skills because of new skills [Sara-chan]
+#define MAX_GUILDSKILL 15 // Increased max guild skills because of new skills [Sara-chan]
#define MAX_GUILDLEVEL 50
#define MAX_GUARDIANS 8 // Local max per castle. [Skotlex]
-#define MAX_QUEST_DB 2410 // Max quests that the server will load
#define MAX_QUEST_OBJECTIVES 3 // Max quest objectives for a quest
-#define MAX_START_ITEMS 32 // Max number of items allowed to be given to a char whenever it's created. [mkbu95]
+#define MAX_START_ITEMS 32 // Max number of items allowed to be given to a char whenever it's created. [mkbu95]
// for produce
#define MIN_ATTRIBUTE 0
@@ -153,7 +167,7 @@
// Base Homun skill.
#define HM_SKILLBASE 8001
#define MAX_HOMUNSKILL 43
-#define MAX_HOMUNCULUS_CLASS 52 // [orn] Increased to 60 from 16 to allow new Homun-S.
+#define MAX_HOMUNCULUS_CLASS 52 // [orn] Increased to 60 from 16 to allow new Homun-S.
#define HM_CLASS_BASE 6001
#define HM_CLASS_MAX (HM_CLASS_BASE+MAX_HOMUNCULUS_CLASS-1)
@@ -175,6 +189,8 @@
#define EL_CLASS_BASE 2114
#define EL_CLASS_MAX (EL_CLASS_BASE+MAX_ELEMENTAL_CLASS-1)
+struct HPluginData;
+
enum item_types {
IT_HEALING = 0,
IT_UNKNOWN, //1
@@ -192,31 +208,64 @@ enum item_types {
IT_MAX
};
+#define INDEX_NOT_FOUND (-1) ///< Used as invalid/failure value in various functions that return an index
-// Questlog system [Kevin] [Inkfish]
-typedef enum quest_state { Q_INACTIVE, Q_ACTIVE, Q_COMPLETE } quest_state;
+// Questlog states
+enum quest_state {
+ Q_INACTIVE, ///< Inactive quest (the user can toggle between active and inactive quests)
+ Q_ACTIVE, ///< Active quest
+ Q_COMPLETE, ///< Completed quest
+};
+/// Questlog entry
struct quest {
- int quest_id;
- unsigned int time;
- int count[MAX_QUEST_OBJECTIVES];
- quest_state state;
+ int quest_id; ///< Quest ID
+ unsigned int time; ///< Expiration time
+ int count[MAX_QUEST_OBJECTIVES]; ///< Kill counters of each quest objective
+ enum quest_state state; ///< Current quest state
};
struct item {
int id;
short nameid;
short amount;
- unsigned short equip; // Location(s) where item is equipped (using enum equip_pos for bitmasking).
+ unsigned int equip; // Location(s) where item is equipped (using enum equip_pos for bitmasking).
char identify;
char refine;
char attribute;
short card[MAX_SLOTS];
unsigned int expire_time;
char favorite;
+ unsigned char bound;
uint64 unique_id;
};
+//Equip position constants
+enum equip_pos {
+ EQP_HEAD_LOW = 0x000001,
+ EQP_HEAD_MID = 0x000200, //512
+ EQP_HEAD_TOP = 0x000100, //256
+ EQP_HAND_R = 0x000002, //2
+ EQP_HAND_L = 0x000020, //32
+ EQP_ARMOR = 0x000010, //16
+ EQP_SHOES = 0x000040, //64
+ EQP_GARMENT = 0x000004, //4
+ EQP_ACC_L = 0x000008, //8
+ EQP_ACC_R = 0x000080, //128
+ EQP_COSTUME_HEAD_TOP = 0x000400, //1024
+ EQP_COSTUME_HEAD_MID = 0x000800, //2048
+ EQP_COSTUME_HEAD_LOW = 0x001000, //4096
+ EQP_COSTUME_GARMENT = 0x002000, //8192
+ //UNUSED_COSTUME_FLOOR = 0x004000, //16384
+ EQP_AMMO = 0x008000, //32768
+ EQP_SHADOW_ARMOR = 0x010000, //65536
+ EQP_SHADOW_WEAPON = 0x020000, //131072
+ EQP_SHADOW_SHIELD = 0x040000, //262144
+ EQP_SHADOW_SHOES = 0x080000, //524288
+ EQP_SHADOW_ACC_R = 0x100000, //1048576
+ EQP_SHADOW_ACC_L = 0x200000, //2097152
+};
+
struct point {
unsigned short map;
short x,y;
@@ -227,15 +276,26 @@ enum e_skill_flag
SKILL_FLAG_PERMANENT,
SKILL_FLAG_TEMPORARY,
SKILL_FLAG_PLAGIARIZED,
+ SKILL_FLAG_UNUSED, ///< needed to maintain the order since the values are saved, can be renamed and used if a new flag is necessary
+ SKILL_FLAG_PERM_GRANTED, ///< Permanent, granted through someway (e.g. script).
+ /* */
+ /* MUST be the last, because with it the flag value stores a dynamic value (flag+lv) */
SKILL_FLAG_REPLACED_LV_0, // Temporary skill overshadowing permanent skill of level 'N - SKILL_FLAG_REPLACED_LV_0',
- SKILL_FLAG_PERM_GRANTED, // Permanent, granted through someway (e.g. script).
- //...
};
enum e_mmo_charstatus_opt {
- OPT_NONE = 0x0,
- OPT_SHOW_EQUIP = 0x1,
- OPT_ALLOW_PARTY = 0x2,
+ OPT_NONE = 0x0,
+ OPT_SHOW_EQUIP = 0x1,
+ OPT_ALLOW_PARTY = 0x2,
+};
+
+enum e_item_bound_type {
+ IBT_MIN = 0x1,
+ IBT_ACCOUNT = 0x1,
+ IBT_GUILD = 0x2,
+ IBT_PARTY = 0x3,
+ IBT_CHARACTER = 0x4,
+ IBT_MAX = 0x4,
};
struct s_skill {
@@ -244,22 +304,26 @@ struct s_skill {
unsigned char flag; // See enum e_skill_flag
};
-struct global_reg {
- char str[32];
- char value[256];
+struct script_reg_state {
+ unsigned int type : 1;/* because I'm a memory hoarder and having them in the same struct would be a 8-byte/instance waste while ints outnumber str on a 10000-to-1 ratio. */
+ unsigned int update : 1;/* whether it needs to be sent to char server for insertion/update/delete */
};
-// Holds array of global registries, used by the char server and converter.
-struct accreg {
- int account_id, char_id;
- int reg_num;
- struct global_reg reg[MAX_REG_NUM];
+struct script_reg_num {
+ struct script_reg_state flag;
+ int value;
+};
+
+struct script_reg_str {
+ struct script_reg_state flag;
+ char *value;
};
// For saving status changes across sessions. [Skotlex]
struct status_change_data {
unsigned short type; //SC_type
- long val1, val2, val3, val4, tick; //Remaining duration.
+ int val1, val2, val3, val4;
+ unsigned int tick; //Remaining duration.
};
struct storage_data {
@@ -288,17 +352,17 @@ struct s_pet {
short hungry;//pet hungry
char name[NAME_LENGTH];
char rename_flag;
- char incuvate;
+ char incubate;
};
-struct s_homunculus { //[orn]
+struct s_homunculus { //[orn]
char name[NAME_LENGTH];
int hom_id;
int char_id;
short class_;
short prev_class;
int hp,max_hp,sp,max_sp;
- unsigned int intimacy; //[orn]
+ unsigned int intimacy;
short hunger;
struct s_skill hskill[MAX_HOMUNSKILL]; //albator
short skillpts;
@@ -306,14 +370,13 @@ struct s_homunculus { //[orn]
unsigned int exp;
short rename_flag;
short vaporize; //albator
- int str ;
- int agi ;
- int vit ;
- int int_ ;
- int dex ;
- int luk ;
-
- char spiritball; //for homun S [lighta]
+ int str;
+ int agi;
+ int vit;
+ int int_;
+ int dex;
+ int luk;
+ int8 spiritball; //for homun S [lighta]
};
struct s_mercenary {
@@ -359,12 +422,13 @@ struct mmo_charstatus {
unsigned int base_exp,job_exp;
int zeny;
+ int bank_vault;
short class_;
unsigned int status_point,skill_point;
int hp,max_hp,sp,max_sp;
unsigned int option;
- short manner;
+ short manner; // Defines how many minutes a char will be muted, each negative point is equivalent to a minute.
unsigned char karma;
short hair,hair_color,clothes_color;
int party_id,guild_id,pet_id,hom_id,mer_id,ele_id;
@@ -402,6 +466,13 @@ struct mmo_charstatus {
unsigned short slotchange;
time_t delete_date;
+
+ /* `account_data` modifiers */
+ unsigned short mod_exp,mod_drop,mod_death;
+
+ unsigned char font;
+
+ uint32 uniqueitem_counter;
};
typedef enum mail_status {
@@ -451,15 +522,6 @@ struct auction_data {
int auction_end_timer;
};
-struct registry {
- int global_num;
- struct global_reg global[GLOBAL_REG_NUM];
- int account_num;
- struct global_reg account[ACCOUNT_REG_NUM];
- int account2_num;
- struct global_reg account2[ACCOUNT_REG2_NUM];
-};
-
struct party_member {
int account_id;
int char_id;
@@ -515,6 +577,7 @@ struct guild_skill {
int id,lv;
};
+struct hChSysCh;
struct guild {
int guild_id;
short guild_lv, connect_member, max_member, average_lv;
@@ -531,13 +594,17 @@ struct guild {
struct guild_expulsion expulsion[MAX_GUILDEXPULSION];
struct guild_skill skill[MAX_GUILDSKILL];
- /* TODO: still used for something?|: */
- unsigned short save_flag; // for TXT saving
+ /* used on char.c to state what kind of data is being saved/processed */
+ unsigned short save_flag;
- unsigned short *instance;
+ short *instance;
unsigned short instances;
- void *channel;
+ struct hChSysCh *channel;
+
+ /* HPM Custom Struct */
+ struct HPluginData **hdata;
+ unsigned int hdatac;
};
struct guild_castle {
@@ -568,15 +635,32 @@ struct fame_list {
char name[NAME_LENGTH];
};
-enum { //Change Guild Infos
- GBI_EXP =1, // Guild Experience (EXP)
- GBI_GUILDLV, // Guild level
- GBI_SKILLPOINT, // Guild skillpoints
- GBI_SKILLLV, // Guild skill_lv ?? seem unused
+enum fame_list_type {
+ RANKTYPE_BLACKSMITH = 0,
+ RANKTYPE_ALCHEMIST = 1,
+ RANKTYPE_TAEKWON = 2,
+ RANKTYPE_PK = 3, //Not supported yet
+};
+
+/**
+ * Guild Basic Information
+ * It is used to request changes via intif_guild_change_basicinfo in map-server and to
+ * signalize changes made in char-server via mapif_parse_GuildMemberInfoChange
+ **/
+enum guild_basic_info {
+ GBI_EXP = 1, ///< Guild Experience (EXP)
+ GBI_GUILDLV, ///< Guild level
+ GBI_SKILLPOINT, ///< Guild skillpoints
+
+ /**
+ * Changes a skill level, struct guild_skill should be sent.
+ * All checks regarding max skill level should be done in _map-server_
+ **/
+ GBI_SKILLLV, ///< Guild skill_lv
};
enum { //Change Member Infos
- GMI_POSITION =0,
+ GMI_POSITION = 0,
GMI_EXP,
GMI_HAIR,
GMI_HAIR_COLOR,
@@ -758,19 +842,95 @@ enum {
JOB_KAGEROU = 4211,
JOB_OBORO,
+ JOB_REBELLION = 4215,
JOB_MAX,
};
+//Total number of classes (for data storage)
+#define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC)
+
enum {
SEX_FEMALE = 0,
SEX_MALE,
SEX_SERVER
};
+enum weapon_type {
+ W_FIST, ///< Bare hands
+ W_DAGGER, //1
+ W_1HSWORD, //2
+ W_2HSWORD, //3
+ W_1HSPEAR, //4
+ W_2HSPEAR, //5
+ W_1HAXE, //6
+ W_2HAXE, //7
+ W_MACE, //8
+ W_2HMACE, //9 (unused)
+ W_STAFF, //10
+ W_BOW, //11
+ W_KNUCKLE, //12
+ W_MUSICAL, //13
+ W_WHIP, //14
+ W_BOOK, //15
+ W_KATAR, //16
+ W_REVOLVER, //17
+ W_RIFLE, //18
+ W_GATLING, //19
+ W_SHOTGUN, //20
+ W_GRENADE, //21
+ W_HUUMA, //22
+ W_2HSTAFF, //23
+ MAX_WEAPON_TYPE,
+ // dual-wield constants
+ W_DOUBLE_DD, ///< 2 daggers
+ W_DOUBLE_SS, ///< 2 swords
+ W_DOUBLE_AA, ///< 2 axes
+ W_DOUBLE_DS, ///< dagger + sword
+ W_DOUBLE_DA, ///< dagger + axe
+ W_DOUBLE_SA, ///< sword + axe
+};
+
+enum ammo_type {
+ A_ARROW = 1,
+ A_DAGGER, //2
+ A_BULLET, //3
+ A_SHELL, //4
+ A_GRENADE, //5
+ A_SHURIKEN, //6
+ A_KUNAI, //7
+ A_CANNONBALL, //8
+ A_THROWWEAPON, //9
+};
+
+enum e_char_server_type {
+ CST_NORMAL = 0,
+ CST_MAINTENANCE = 1,
+ CST_OVER18 = 2,
+ CST_PAYING = 3,
+ CST_F2P = 4,
+};
+
+enum e_pc_reg_loading {
+ PRL_NONE = 0x0,
+ PRL_CHAR = 0x1,
+ PRL_ACCL = 0x2,/* local */
+ PRL_ACCG = 0x4,/* global */
+ PRL_ALL = 0xFF,
+};
+
+/* packet size constant for itemlist */
+#if MAX_INVENTORY > MAX_STORAGE && MAX_INVENTORY > MAX_CART
+#define MAX_ITEMLIST MAX_INVENTORY
+#elif MAX_CART > MAX_INVENTORY && MAX_CART > MAX_STORAGE
+#define MAX_ITEMLIST MAX_CART
+#else
+#define MAX_ITEMLIST MAX_STORAGE
+#endif
+
// sanity checks...
#if MAX_ZENY > INT_MAX
#error MAX_ZENY is too big
#endif
-#endif /* _MMO_H_ */
+#endif /* COMMON_MMO_H */
diff --git a/src/common/mutex.c b/src/common/mutex.c
index 6bb1efdab..f046febf6 100644
--- a/src/common/mutex.c
+++ b/src/common/mutex.c
@@ -1,6 +1,12 @@
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
+#define HERCULES_CORE
+
+#include "mutex.h"
+
+#include "../common/cbasetypes.h" // for WIN32
+
#ifdef WIN32
#include "../common/winapi.h"
#else
@@ -9,11 +15,9 @@
#include <sys/time.h>
#endif
-#include "../common/cbasetypes.h"
#include "../common/malloc.h"
#include "../common/showmsg.h"
#include "../common/timer.h"
-#include "../common/mutex.h"
struct ramutex{
#ifdef WIN32
@@ -46,12 +50,12 @@ struct racond{
//
-ramutex ramutex_create(){
+ramutex *ramutex_create(void) {
struct ramutex *m;
m = (struct ramutex*)aMalloc( sizeof(struct ramutex) );
- if(m == NULL){
- ShowFatalError("ramutex_create: OOM while allocating %u bytes.\n", sizeof(struct ramutex));
+ if (m == NULL) {
+ ShowFatalError("ramutex_create: OOM while allocating %"PRIuS" bytes.\n", sizeof(struct ramutex));
return NULL;
}
@@ -65,7 +69,7 @@ ramutex ramutex_create(){
}//end: ramutex_create()
-void ramutex_destroy( ramutex m ){
+void ramutex_destroy(ramutex *m) {
#ifdef WIN32
DeleteCriticalSection(&m->hMutex);
@@ -78,7 +82,7 @@ void ramutex_destroy( ramutex m ){
}//end: ramutex_destroy()
-void ramutex_lock( ramutex m ){
+void ramutex_lock(ramutex *m) {
#ifdef WIN32
EnterCriticalSection(&m->hMutex);
@@ -88,7 +92,7 @@ void ramutex_lock( ramutex m ){
}//end: ramutex_lock
-bool ramutex_trylock( ramutex m ){
+bool ramutex_trylock(ramutex *m) {
#ifdef WIN32
if(TryEnterCriticalSection(&m->hMutex) == TRUE)
return true;
@@ -103,7 +107,7 @@ bool ramutex_trylock( ramutex m ){
}//end: ramutex_trylock()
-void ramutex_unlock( ramutex m ){
+void ramutex_unlock(ramutex *m) {
#ifdef WIN32
LeaveCriticalSection(&m->hMutex);
#else
@@ -116,16 +120,16 @@ void ramutex_unlock( ramutex m ){
///////////////
// Condition Variables
-//
+//
// Implementation:
//
-racond racond_create(){
+racond *racond_create(void) {
struct racond *c;
c = (struct racond*)aMalloc( sizeof(struct racond) );
- if(c == NULL){
- ShowFatalError("racond_create: OOM while allocating %u bytes\n", sizeof(struct racond));
+ if (c == NULL) {
+ ShowFatalError("racond_create: OOM while allocating %"PRIuS" bytes\n", sizeof(struct racond));
return NULL;
}
@@ -142,7 +146,7 @@ racond racond_create(){
}//end: racond_create()
-void racond_destroy( racond c ){
+void racond_destroy(racond *c) {
#ifdef WIN32
CloseHandle( c->events[ EVENT_COND_SIGNAL ] );
CloseHandle( c->events[ EVENT_COND_BROADCAST ] );
@@ -155,7 +159,7 @@ void racond_destroy( racond c ){
}//end: racond_destroy()
-void racond_wait( racond c, ramutex m, sysint timeout_ticks){
+void racond_wait(racond *c, ramutex *m, sysint timeout_ticks) {
#ifdef WIN32
register DWORD ms;
int result;
@@ -201,7 +205,7 @@ void racond_wait( racond c, ramutex m, sysint timeout_ticks){
pthread_cond_wait( &c->hCond, &m->hMutex );
}else{
struct timespec wtime;
- int64 exact_timeout = iTimer->gettick() + timeout_ticks;
+ int64 exact_timeout = timer->gettick() + timeout_ticks;
wtime.tv_sec = exact_timeout/1000;
wtime.tv_nsec = (exact_timeout%1000)*1000000;
@@ -213,7 +217,7 @@ void racond_wait( racond c, ramutex m, sysint timeout_ticks){
}//end: racond_wait()
-void racond_signal( racond c ){
+void racond_signal(racond *c) {
#ifdef WIN32
// bool has_waiters = false;
// EnterCriticalSection(&c->waiters_lock);
@@ -229,7 +233,7 @@ void racond_signal( racond c ){
}//end: racond_signal()
-void racond_broadcast( racond c ){
+void racond_broadcast(racond *c) {
#ifdef WIN32
// bool has_waiters = false;
// EnterCriticalSection(&c->waiters_lock);
diff --git a/src/common/mutex.h b/src/common/mutex.h
index 1999627cd..d298c05af 100644
--- a/src/common/mutex.h
+++ b/src/common/mutex.h
@@ -1,92 +1,93 @@
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+// For more information, see LICENCE in the main folder
-#ifndef _rA_MUTEX_H_
-#define _rA_MUTEX_H_
+#ifndef COMMON_MUTEX_H
+#define COMMON_MUTEX_H
+#include "../common/cbasetypes.h"
-typedef struct ramutex *ramutex; // Mutex
-typedef struct racond *racond; // Condition Var
+typedef struct ramutex ramutex; // Mutex
+typedef struct racond racond; // Condition Var
/**
- * Creates a Mutex
+ * Creates a Mutex
*
* @return not NULL
*/
-ramutex ramutex_create();
+ramutex *ramutex_create();
-/**
+/**
* Destroys a Mutex
- *
+ *
* @param m - the mutex to destroy
*/
-void ramutex_destroy( ramutex m );
+void ramutex_destroy(ramutex *m);
-/**
+/**
* Gets a lock
*
* @param m - the mutex to lock
*/
-void ramutex_lock( ramutex m);
+void ramutex_lock(ramutex *m);
-/**
+/**
* Trys to get the Lock
- *
+ *
* @param m - the mutex try to lock
- *
+ *
* @return boolean (true = got the lock)
*/
-bool ramutex_trylock( ramutex m );
+bool ramutex_trylock(ramutex *m);
-/**
+/**
* Unlocks a mutex
*
* @param m - the mutex to unlock
*/
-void ramutex_unlock( ramutex m);
+void ramutex_unlock(ramutex *m);
-/**
+/**
* Creates a Condition variable
*
* @return not NULL
*/
-racond racond_create();
+racond *racond_create();
-/**
+/**
* Destroy a Condition variable
*
- * @param c - the condition varaible to destroy
+ * @param c - the condition variable to destroy
*/
-void racond_destroy( racond c );
+void racond_destroy(racond *c);
/**
- * Waits Until state is signalled
- *
- * @param c - the condition var to wait for signalled state
- * @param m - the mutex used for syncronization
+ * Waits Until state is signaled
+ *
+ * @param c - the condition var to wait for signaled state
+ * @param m - the mutex used for synchronization
* @param timeout_ticks - timeout in ticks ( -1 = INFINITE )
*/
-void racond_wait( racond c, ramutex m, sysint timeout_ticks);
+void racond_wait(racond *c, ramutex *m, sysint timeout_ticks);
-/**
- * Sets the given condition var to signalled state
+/**
+ * Sets the given condition var to signaled state
*
- * @param c - condition var to set in signalled state.
+ * @param c - condition var to set in signaled state.
*
* @note:
* Only one waiter gets notified.
*/
-void racond_signal( racond c );
+void racond_signal(racond *c);
-/**
- * Sets notifys all waiting threads thats signalled.
- * @param c - condition var to set in signalled state
- *
+/**
+ * Sets notifies all waiting threads thats signaled.
+ * @param c - condition var to set in signaled state
+ *
* @note:
* All Waiters getting notified.
- */
-void racond_broadcast( racond c );
+ */
+void racond_broadcast(racond *c);
-#endif
+#endif /* COMMON_MUTEX_H */
diff --git a/src/common/netbuffer.c b/src/common/netbuffer.c
deleted file mode 100644
index 60a299aa9..000000000
--- a/src/common/netbuffer.c
+++ /dev/null
@@ -1,221 +0,0 @@
-
-//
-// Network Buffer Subsystem (iobuffer)
-//
-//
-// Author: Florian Wilkemeyer <fw@f-ws.de>
-//
-// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-//
-//
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "../common/cbasetypes.h"
-#include "../common/atomic.h"
-#include "../common/mempool.h"
-#include "../common/showmsg.h"
-#include "../common/raconf.h"
-#include "../common/thread.h"
-#include "../common/malloc.h"
-#include "../common/core.h"
-
-#include "../common/netbuffer.h"
-
-
-//
-// Buffers are available in the following sizes:
-// 48, 192, 2048, 8192
-// 65536 (inter server connects may use it for charstatus struct..)
-//
-
-
-///
-// Implementation:
-//
-static volatile int32 l_nEmergencyAllocations = 0; // stats.
-static sysint l_nPools = 0;
-static sysint *l_poolElemSize = NULL;
-static mempool *l_pool = NULL;
-
-
-void netbuffer_init(){
- char localsection[32];
- raconf conf;
- sysint i;
-
- // Initialize Statistic counters:
- l_nEmergencyAllocations = 0;
-
- // Set localsection name according to running serverype.
- switch(SERVER_TYPE){
- case SERVER_TYPE_LOGIN: strcpy(localsection, "login-netbuffer"); break;
- case SERVER_TYPE_CHAR: strcpy(localsection, "char-netbuffer"); break;
- //case ATHENA_SERVER_INTER: strcpy(localsection, "inter-netbuffer"); break;
- case SERVER_TYPE_MAP: strcpy(localsection, "map-netbuffer"); break;
- default: strcpy(localsection, "unsupported_type"); break;
- }
-
-
- conf = raconf_parse("conf/network.conf");
- if(conf == NULL){
- ShowFatalError("Failed to Parse required Configuration (conf/network.conf)");
- exit(EXIT_FAILURE);
- }
-
- // Get Values from config file
- l_nPools = (sysint)raconf_getintEx(conf, localsection, "netbuffer", "num", 0);
- if(l_nPools == 0){
- ShowFatalError("Netbuffer (network.conf) failure - requires at least 1 Pool.\n");
- exit(EXIT_FAILURE);
- }
-
- // Allocate arrays.
- l_poolElemSize = (sysint*)aCalloc( l_nPools, sizeof(sysint) );
- l_pool = (mempool*)aCalloc( l_nPools, sizeof(mempool) );
-
-
- for(i = 0; i < l_nPools; i++){
- int64 num_prealloc, num_realloc;
- char key[32];
-
- sprintf(key, "pool_%u_size", (uint32)i+1);
- l_poolElemSize[i] = (sysint)raconf_getintEx(conf, localsection, "netbuffer", key, 4096);
- if(l_poolElemSize[i] < 32){
- ShowWarning("Netbuffer (network.conf) failure - minimum allowed buffer size is 32 byte) - fixed.\n");
- l_poolElemSize[i] = 32;
- }
-
- sprintf(key, "pool_%u_prealloc", (uint32)i+1);
- num_prealloc = raconf_getintEx(conf, localsection, "netbuffer", key, 150);
-
- sprintf(key, "pool_%u_realloc_step", (uint32)i+1);
- num_realloc = raconf_getintEx(conf, localsection, "netbuffer", key, 100);
-
- // Create Pool!
- sprintf(key, "Netbuffer %u", (uint32)l_poolElemSize[i]); // name.
-
- // Info
- ShowInfo("NetBuffer: Creating Pool %u (Prealloc: %u, Realloc Step: %u) - %0.2f MiB\n", l_poolElemSize[i], num_prealloc, num_realloc, (float)((sizeof(struct netbuf) + l_poolElemSize[i] - 32)* num_prealloc)/1024.0f/1024.0f);
-
- //
- // Size Calculation:
- // struct netbuf + requested buffer size - 32 (because the struct already contains 32 byte buffer space at the end of struct)
- l_pool[i] = mempool_create(key, (sizeof(struct netbuf) + l_poolElemSize[i] - 32), num_prealloc, num_realloc, NULL, NULL);
- if(l_pool[i] == NULL){
- ShowFatalError("Netbuffer: cannot create Pool for %u byte buffers.\n", l_poolElemSize[i]);
- // @leak: clean everything :D
- exit(EXIT_FAILURE);
- }
-
- }//
-
-
- raconf_destroy(conf);
-
-}//end: netbuffer_init()
-
-
-void netbuffer_final(){
- sysint i;
-
- if(l_nPools > 0){
- /// .. finalize mempools
- for(i = 0; i < l_nPools; i++){
- mempool_stats stats = mempool_get_stats(l_pool[i]);
-
- ShowInfo("Netbuffer: Freeing Pool %u (Peak Usage: %u, Realloc Events: %u)\n", l_poolElemSize[i], stats.peak_nodes_used, stats.num_realloc_events);
-
- mempool_destroy(l_pool[i]);
- }
-
- if(l_nEmergencyAllocations > 0){
- ShowWarning("Netbuffer: did %u Emergency Allocations, please tune your network.conf!\n", l_nEmergencyAllocations);
- l_nEmergencyAllocations = 0;
- }
-
- aFree(l_poolElemSize); l_poolElemSize = NULL;
- aFree(l_pool); l_pool = NULL;
- l_nPools = 0;
- }
-
-
-}//end: netbuffer_final()
-
-
-netbuf netbuffer_get( sysint sz ){
- sysint i;
- netbuf nb = NULL;
-
- // Search an appropriate pool
- for(i = 0; i < l_nPools; i++){
- if(sz <= l_poolElemSize[i]){
- // match
-
- nb = (netbuf)mempool_node_get(l_pool[i]);
- nb->pool = i;
-
- break;
- }
- }
-
- // No Bufferpool found that mets there quirements?.. (thats bad..)
- if(nb == NULL){
- ShowWarning("Netbuffer: get(%u): => no appropriate pool found - emergency allocation required.\n", sz);
- ShowWarning("Please reconfigure your network.conf!");
-
- InterlockedIncrement(&l_nEmergencyAllocations);
-
- // .. better to check (netbuf struct provides 32 byte bufferspace itself.
- if(sz < 32) sz = 32;
-
- // allocate memory using malloc ..
- while(1){
- nb = (netbuf) aMalloc( (sizeof(struct netbuf) + sz - 32) );
- if(nb != NULL){
- memset(nb, 0x00, (sizeof(struct netbuf) + sz - 32) ); // zero memory! (to enforce commit @ os.)
- nb->pool = -1; // emergency alloc.
- break;
- }
-
- rathread_yield();
- }// spin allocation.
-
- }
-
-
- nb->refcnt = 1; // Initial refcount is 1
-
- return nb;
-}//end: netbuffer_get()
-
-
-void netbuffer_put( netbuf nb ){
-
- // Decrement reference counter, if > 0 do nothing :)
- if( InterlockedDecrement(&nb->refcnt) > 0 )
- return;
-
- // Is this buffer an emergency allocated buffer?
- if(nb->pool == -1){
- aFree(nb);
- return;
- }
-
-
- // Otherwise its a normal mempool based buffer
- // return it to the according mempool:
- mempool_node_put( l_pool[nb->pool], nb);
-
-
-}//end: netbuffer_put()
-
-
-void netbuffer_incref( netbuf nb ){
-
- InterlockedIncrement(&nb->refcnt);
-
-}//end: netbuf_incref()
diff --git a/src/common/netbuffer.h b/src/common/netbuffer.h
deleted file mode 100644
index 844241226..000000000
--- a/src/common/netbuffer.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _rA_NETBUFFER_H_
-#define _rA_NETBUFFER_H_
-
-#include "../common/cbasetypes.h"
-
-typedef struct netbuf{
- sysint pool; // The pool ID this buffer belongs to,
- // is set to -1 if its an emergency allocated buffer
-
- struct netbuf *next; // Used by Network system.
-
- volatile int32 refcnt; // Internal Refcount, it gets lowered every call to netbuffer_put,
- // if its getting zero, the buffer will returned back to the pool
- // and can be reused.
-
- int32 dataPos; // Current Offset
- // Used only for Reading (recv job)
- // write cases are using the sessions local datapos member due to
- // shared write buffer support.
-
- int32 dataLen; // read buffer case:
- // The length expected to read to.
- // when this->dataPos == dateLen, read job has been completed.
- // write buffer case:
- // The lngth of data in te buffer
- // when s->dataPos == dataLen, write job has been completed
- //
- // Note:
- // leftBytes = (dateLen - dataPos)
- //
- // Due to shared buffer support
- // dataPos gets not used in write case (each connection has its local offset)
- //
-
- // The Bufferspace itself.
- char buf[32];
-} *netbuf;
-
-
-void netbuffer_init();
-void netbuffer_final();
-
-/**
- * Gets a netbuffer that has atleast (sz) byes space.
- *
- * @note: The netbuffer system guarantees that youll always recevie a buffer.
- * no check for null is required!
- *
- * @param sz - minimum size needed.
- *
- * @return pointer to netbuf struct
- */
-netbuf netbuffer_get( sysint sz );
-
-
-/**
- * Returns the given netbuffer (decreases refcount, if its 0 - the buffer will get returned to the pool)
- *
- * @param buf - the buffer to return
- */
-void netbuffer_put( netbuf buf );
-
-
-/**
- * Increases the Refcount on the given buffer
- * (used for areasends .. etc)
- *
- */
-void netbuffer_incref( netbuf buf );
-
-
-// Some Useful macros
-#define NBUFP(netbuf,pos) (((uint8*)(netbuf->buf)) + (pos))
-#define NBUFB(netbuf,pos) (*(uint8*)((netbuf->buf) + (pos)))
-#define NBUFW(netbuf,pos) (*(uint16*)((netbuf->buf) + (pos)))
-#define NBUFL(netbuf,pos) (*(uint32*)((netbuf->buf) + (pos)))
-
-
-
-#endif
diff --git a/src/common/network.c b/src/common/network.c
deleted file mode 100644
index 1f1621363..000000000
--- a/src/common/network.c
+++ /dev/null
@@ -1,1061 +0,0 @@
-//
-// Network Subsystem (previously known as socket system)
-//
-// Author: Florian Wilkemeyer <fw@f-ws.de>
-//
-// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-//
-//
-//#ifdef HAVE_ACCETP4
-#define _GNU_SOURCE
-//#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/fcntl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-
-
-#include "../common/cbasetypes.h"
-#include "../common/showmsg.h"
-#include "../common/timer.h"
-#include "../common/evdp.h"
-#include "../common/netbuffer.h"
-
-#include "../common/network.h"
-
-#define ENABLE_IPV6
-#define HAVE_ACCEPT4
-#define EVENTS_PER_CYCLE 10
-#define PARANOID_CHECKS
-
-// Local Vars (settings..)
-static int l_ListenBacklog = 64;
-
-//
-// Global Session Array (previously exported as session[]
-//
-SESSION g_Session[MAXCONN];
-
-
-//
-static bool onSend(int32 fd);
-
-
-#define _network_free_netbuf_async( buf ) add_timer( 0, _network_async_free_netbuf_proc, 0, (intptr_t) buf)
-static int _network_async_free_netbuf_proc(int tid, unsigned int tick, int id, intptr_t data){
- // netbuf is in data
- netbuffer_put( (netbuf)data );
-
- return 0;
-}//end: _network_async_free_netbuf_proc()
-
-
-
-void network_init(){
- SESSION *s;
- int32 i;
-
- memset(g_Session, 0x00, (sizeof(SESSION) * MAXCONN) );
-
- for(i = 0; i < MAXCONN; i++){
- s = &g_Session[i];
-
- s->type = NST_FREE;
- s->disconnect_in_progress = false;
-
- }
-
- // Initialize the correspondig event dispatcher
- evdp_init();
-
- //
- add_timer_func_list(_network_async_free_netbuf_proc, "_network_async_free_netbuf_proc");
-
-}//end: network_init()
-
-
-void network_final(){
-
- // @TODO:
- // .. disconnect and cleanup everything!
-
- evdp_final();
-
-}//end: network_final()
-
-
-void network_do(){
- struct EVDP_EVENT l_events[EVENTS_PER_CYCLE];
- register struct EVDP_EVENT *ev;
- register int n, nfds;
- register SESSION *s;
-
- nfds = evdp_wait( l_events, EVENTS_PER_CYCLE, 1000); // @TODO: timer_getnext()
-
- for(n = 0; n < nfds; n++){
- ev = &l_events[n];
- s = &g_Session[ ev->fd ];
-
- if(ev->events & EVDP_EVENT_HUP){
- network_disconnect( ev->fd );
- continue; // no further event processing.
- }// endif vent is HUP (disconnect)
-
-
- if(ev->events & EVDP_EVENT_IN){
-
- if(s->onRecv != NULL){
- if( false == s->onRecv(ev->fd) ){
- network_disconnect(ev->fd);
- continue; // ..
- }
- }else{
- ShowError("network_do: fd #%u has no onRecv proc set. - disconnecting\n", ev->fd);
- network_disconnect(ev->fd);
- continue;
- }
-
- }// endif event is IN (recv)
-
-
- if(ev->events & EVDP_EVENT_OUT){
- if(s->onSend != NULL){
- if( false == s->onSend(ev->fd) ){
- network_disconnect(ev->fd);
- continue;
- }
- }else{
- ShowError("network_do: fd #%u has no onSend proc set. - disconnecting\n", ev->fd);
- network_disconnect(ev->fd);
- continue;
- }
- }// endif event is OUT (send)
-
- }//endfor
-
-}//end: network_do()
-
-
-static bool _setnonblock(int32 fd){
- int flags = fcntl(fd, F_GETFL, 0);
- if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0)
- return false;
-
- return true;
-}//end: _setnonblock()
-
-
-static bool _network_accept(int32 fd){
- SESSION *listener = &g_Session[fd];
- SESSION *s;
- union{
- struct sockaddr_in v4;
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 v6;
-#endif
- } _addr;
- int newfd;
- socklen_t addrlen;
- struct sockaddr *addr;
-
- // Accept until OS returns - nothing to accept anymore
- // - this is required due to our EVDP abstraction. (which handles on listening sockets similar to epoll's EPOLLET flag.)
- while(1){
-#ifdef ENABLE_IPV6
- if(listener->v6 == true){
- addrlen = sizeof(_addr.v6);
- addr = (struct sockaddr*)&_addr.v6;
- }else{
-#endif
- addrlen = sizeof(_addr.v4);
- addr = (struct sockaddr*)&_addr.v4;
-#ifdef ENABLE_IPV6
- }
-#endif
-
-#ifdef HAVE_ACCEPT4
- newfd = accept4(fd, addr, &addrlen, SOCK_NONBLOCK);
-#else
- newfd = accept(fd, addr, &addrlen);
-#endif
-
- if(newfd == -1){
- if(errno == EAGAIN || errno == EWOULDBLOCK)
- break; // this is fully valid & whished., se explaination on top of while(1)
-
- // Otherwis .. we have serious problems :( seems tahat our listner has gone away..
- // @TODO handle this ..
- ShowError("_network_accept: accept() returned error. closing listener. (errno: %u / %s)\n", errno, strerror(errno));
-
- return false; // will call disconnect after return.
- //break;
- }
-
-#ifndef HAVE_ACCEPT4 // no accept4 means, we have to set nonblock by ourself. ..
- if(_setnonblock(newfd) == false){
- ShowError("_network_accept: failed to set newly accepted connection nonblocking (errno: %u / %s). - disconnecting.\n", errno, strerror(errno));
- close(newfd);
- continue;
- }
-#endif
-
- // Check connection limits.
- if(newfd >= MAXCONN){
- ShowError("_network_accept: failed to accept connection - MAXCONN (%u) exceeded.\n", MAXCONN);
- close(newfd);
- continue; // we have to loop over the events (and disconnect them too ..) but otherwise we would leak event notifications.
- }
-
-
- // Create new Session.
- s = &g_Session[newfd];
- s->type = NST_CLIENT;
-
- // The new connection inherits listenr's handlers.
- s->onDisconnect = listener->onDisconnect;
- s->onConnect = listener->onConnect; // maybe useless but .. fear the future .. :~
-
- // Register the new connection @ EVDP
- if( evdp_addclient(newfd, &s->evdp_data) == false){
- ShowError("_network_accept: failed to accept connection - event subsystem returned an error.\n");
- close(newfd);
- s->type = NST_FREE;
- }
-
- // Call the onConnect handler on the listener.
- if( listener->onConnect(newfd) == false ){
- // Resfused by onConnect handler..
- evdp_remove(newfd, &s->evdp_data);
-
- close(newfd);
- s->type = NST_FREE;
-
- s->data = NULL; // be on the safe side ~ !
- continue;
- }
-
-
- }
-
- return true;
-}//end: _network_accept()
-
-
-void network_disconnect(int32 fd){
- SESSION *s = &g_Session[fd];
- netbuf b, bn;
-
- // Prevent recursive calls
- // by wrong implemented on disconnect handlers.. and such..
- if(s->disconnect_in_progress == true)
- return;
-
- s->disconnect_in_progress = true;
-
-
- // Disconnect Todo:
- // - Call onDisconnect Handler
- // - Release all Assigned buffers.
- // - remove from event system (notifications)
- // - cleanup session structure
- // - close connection.
- //
-
- if(s->onDisconnect != NULL &&
- s->type != NST_LISTENER){
-
- s->onDisconnect( fd );
- }
-
- // Read Buffer
- if(s->read.buf != NULL){
- netbuffer_put(s->read.buf);
- s->read.buf = NULL;
- }
-
- // Write Buffer(s)
- b = s->write.buf;
- while(1){
- if(b == NULL) break;
-
- bn = b->next;
-
- netbuffer_put(b);
-
- b = bn;
- }
- s->write.buf = NULL;
- s->write.buf_last = NULL;
-
- s->write.n_outstanding = 0;
- s->write.max_outstanding = 0;
-
-
- // Remove from event system.
- evdp_remove(fd, &s->evdp_data);
-
- // Cleanup Session Structure.
- s->type = NST_FREE;
- s->data = NULL; // no application level data assigned
- s->disconnect_in_progress = false;
-
-
- // Close connection
- close(fd);
-
-}//end: network_disconnect()
-
-
-int32 network_addlistener(bool v6, const char *addr, uint16 port){
- SESSION *s;
- int optval, fd;
-
-#if !defined(ENABLE_IPV6)
- if(v6 == true){
- ShowError("network_addlistener(%c, '%s', %u): this release has no IPV6 support.\n", (v6==true?'t':'f'), addr, port);
- return -1;
- }
-#endif
-
-
-#ifdef ENABLE_IPV6
- if(v6 == true)
- fd = socket(AF_INET6, SOCK_STREAM, 0);
- else
-#endif
- fd = socket(AF_INET, SOCK_STREAM, 0);
-
- // Error?
- if(fd == -1){
- ShowError("network_addlistener(%c, '%s', %u): socket() failed (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- return -1;
- }
-
- // Too many connections?
- if(fd >= MAXCONN){
- ShowError("network_addlistener(%c, '%s', %u): cannot create listener, exceeds more than supported connections (%u).\n", (v6==true?'t':'f'), addr, port, MAXCONN);
- close(fd);
- return -1;
- }
-
-
- s = &g_Session[fd];
- if(s->type != NST_FREE){ // additional checks.. :)
- ShowError("network_addlistener(%c, '%s', %u): failed, got fd #%u which is already in use in local session table?!\n", (v6==true?'t':'f'), addr, port, fd);
- close(fd);
- return -1;
- }
-
-
- // Fill ip addr structs
-#ifdef ENABLE_IPV6
- if(v6 == true){
- memset(&s->addr.v6, 0x00, sizeof(s->addr.v6));
- s->addr.v6.sin6_family = AF_INET6;
- s->addr.v6.sin6_port = htons(port);
- if(inet_pton(AF_INET6, addr, &s->addr.v6.sin6_addr) != 1){
- ShowError("network_addlistener(%c, '%s', %u): failed to parse the given IPV6 address.\n", (v6==true?'t':'f'), addr, port);
- close(fd);
- return -1;
- }
-
- }else{
-#endif
- memset(&s->addr.v4, 0x00, sizeof(s->addr.v4));
- s->addr.v4.sin_family = AF_INET;
- s->addr.v4.sin_port = htons(port);
- s->addr.v4.sin_addr.s_addr = inet_addr(addr);
-#ifdef ENABLE_IPV6
- }
-#endif
-
-
- // if OS has support for SO_REUSEADDR, apply the flag
- // so the address could be used when there're still time_wait sockets outstanding from previous application run.
-#ifdef SO_REUSEADDR
- optval=1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
-#endif
-
- // Bind
-#ifdef ENABLE_IPV6
- if(v6 == true){
- if( bind(fd, (struct sockaddr*)&s->addr.v6, sizeof(s->addr.v6)) == -1) {
- ShowError("network_addlistener(%c, '%s', %u): bind failed (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
- }else{
-#endif
- if( bind(fd, (struct sockaddr*)&s->addr.v4, sizeof(s->addr.v4)) == -1) {
- ShowError("network_addlistener(%c, '%s', %u): bind failed (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
-#ifdef ENABLE_IPV6
- }
-#endif
-
- if( listen(fd, l_ListenBacklog) == -1){
- ShowError("network_addlistener(%c, '%s', %u): listen failed (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
-
-
- // Set to nonblock!
- if(_setnonblock(fd) == false){
- ShowError("network_addlistener(%c, '%s', %u): cannot set to nonblock (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
-
-
- // Rgister @ evdp.
- if( evdp_addlistener(fd, &s->evdp_data) != true){
- ShowError("network_addlistener(%c, '%s', %u): eventdispatcher subsystem returned an error.\n", (v6==true?'t':'f'), addr, port);
- close(fd);
- return -1;
- }
-
-
- // Apply flags on Session array for this conneciton.
- if(v6 == true) s->v6 = true;
- else s->v6 = false;
-
- s->type = NST_LISTENER;
- s->onRecv = _network_accept;
-
- ShowStatus("Added Listener on '%s':%u\n", addr, port, (v6==true ? "(ipv6)":"(ipv4)") );
-
- return fd;
-}//end: network_addlistener()
-
-
-static bool _network_connect_establishedHandler(int32 fd){
- register SESSION *s = &g_Session[fd];
- int val;
- socklen_t val_len;
-
- if(s->type == NST_FREE)
- return true; // due to multiple non coalesced event notifications
- // this can happen .. when a previous handled event has already disconnected the connection
- // within the same cycle..
-
- val = -1;
- val_len = sizeof(val);
- getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &val_len);
-
- if(val != 0){
- // :( .. cleanup session..
- s->type = NST_FREE;
- s->onSend = NULL;
- s->onConnect = NULL;
- s->onDisconnect = NULL;
-
- evdp_remove(fd, &s->evdp_data);
- close(fd);
-
- return true; // we CANT return false,
- // becuase the normal disconnect procedure would execute the ondisconnect handler, which we dont want .. in this case.
- }else{
- // ok
- if(s->onConnect(fd) == false) {
- // onConnect handler has refused the connection ..
- // cleanup .. and ok
- s->type = NST_FREE;
- s->onSend = NULL;
- s->onConnect = NULL;
- s->onDisconnect = NULL;
-
- evdp_remove(fd, &s->evdp_data);
- close(fd);
-
- return true; // we dnot want the ondisconnect handler to be executed, so its okay to handle this by ourself.
- }
-
- // connection established !
- //
- if( evdp_outgoingconnection_established(fd, &s->evdp_data) == false ){
- return false; // we want the normal disconnect procedure.. with call to ondisconnect handler.
- }
-
- s->onSend = NULL;
-
- ShowStatus("#%u connection successfull!\n", fd);
- }
-
- return true;
-}//end: _network_connect_establishedHandler()
-
-
-int32 network_connect(bool v6,
- const char *addr,
- uint16 port,
- const char *from_addr,
- uint16 from_port,
- bool (*onConnectionEstablishedHandler)(int32 fd),
- void (*onConnectionLooseHandler)(int32 fd)
-){
- register SESSION *s;
- int32 fd, optval, ret;
- struct sockaddr_in ip4;
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 ip6;
-#endif
-
-#ifdef ENABLE_IPV6
- if(v6 == true)
- fd = socket(AF_INET6, SOCK_STREAM, 0);
- else
-#endif
- fd = socket(AF_INET, SOCK_STREAM, 0);
-
-#ifndef ENABLE_IPV6
- // check..
- if(v6 == true){
- ShowError("network_connect(%c, '%s', %u...): tried to create an ipv6 connection, IPV6 is not supported in this release.\n", (v6==true?'t':'f'), addr, port);
- return -1;
- }
-#endif
-
- // check connection limits.
- if(fd >= MAXCONN){
- ShowError("network_connect(%c, '%s', %u...): cannot create new connection, exceeeds more than supported connections (%u)\n", (v6==true?'t':'f'), addr, port );
- close(fd);
- return -1;
- }
-
-
- // Originating IP/Port pair given ?
- if(from_addr != NULL && *from_addr != 0){
- //..
- #ifdef SO_REUSEADDR
- optval=1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
- #endif
-
- #ifdef ENABLE_IPV6
- if(v6 == true){
- memset(&ip6, 0x00, sizeof(ip6));
- ip6.sin6_family = AF_INET6;
- ip6.sin6_port = htons(from_port);
-
- if(inet_pton(AF_INET6, from_addr, &ip6.sin6_addr) != 1){
- ShowError("network_connect(%c, '%s', %u...): cannot parse originating (from) IPV6 address (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
-
- ret = bind(fd, (struct sockaddr*)&ip6, sizeof(ip6));
- }else{
- #endif
- memset(&ip4, 0x00, sizeof(ip4));
-
- ip4.sin_family = AF_INET;
- ip4.sin_port = htons(from_port);
- ip4.sin_addr.s_addr = inet_addr(from_addr);
- ret = bind(fd, (struct sockaddr*)&ip4, sizeof(ip4));
- #ifdef ENABLE_IPV6
- }
- #endif
-
- }
-
-
- // Set non block
- if(_setnonblock(fd) == false){
- ShowError("network_connect(%c, '%s', %u...): cannot set socket to nonblocking (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
-
-
- // Create ip addr block to connect to ..
-#ifdef ENABLE_IPV6
- if(v6 == true){
- memset(&ip6, 0x00, sizeof(ip6));
- ip6.sin6_family = AF_INET6;
- ip6.sin6_port = htons(port);
-
- if(inet_pton(AF_INET6, addr, &ip6.sin6_addr) != 1){
- ShowError("network_connect(%c, '%s', %u...): cannot parse destination IPV6 address (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
- close(fd);
- return -1;
- }
-
- }else{
-#endif
- memset(&ip4, 0x00, sizeof(ip4));
-
- ip4.sin_family = AF_INET;
- ip4.sin_port = htons(port);
- ip4.sin_addr.s_addr = inet_addr(addr);
-#ifdef ENABLE_IPV6
- }
-#endif
-
-
- // Assign Session..
- s = &g_Session[fd];
- s->type = NST_OUTGOING;
- s->v6 = v6;
- s->onConnect = onConnectionEstablishedHandler;
- s->onDisconnect = onConnectionLooseHandler;
- s->onRecv = NULL;
- s->onSend = _network_connect_establishedHandler;
-#ifdef ENABLE_IPV6
- if(v6 == true)
- memcpy(&s->addr.v6, &ip6, sizeof(ip6));
- else
-#endif
- memcpy(&s->addr.v4, &ip4, sizeof(ip4));
-
- // Register @ EVDP. as outgoing (see doc of the function)
- if(evdp_addconnecting(fd, &s->evdp_data) == false){
- ShowError("network_connect(%c, '%s', %u...): eventdispatcher subsystem returned an error.\n", (v6==true?'t':'f'), addr, port);
-
- // cleanup session x.x..
- s->type = NST_FREE;
- s->onConnect = NULL;
- s->onDisconnect = NULL;
- s->onSend = NULL;
-
- // close, return error code.
- close(fd);
- return -1;
- }
-
-
-#ifdef ENABLE_IPV6
- if(v6 == true)
- ret = connect(fd, (struct sockaddr*)&ip6, sizeof(ip6));
- else
-#endif
- ret = connect(fd, (struct sockaddr*)&ip4, sizeof(ip4));
-
-
- //
- if(ret != 0 && errno != EINPROGRESS){
- ShowWarning("network_connect(%c, '%s', %u...): connection failed (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno));
-
- // Cleanup session ..
- s->type = NST_FREE;
- s->onConnect = NULL;
- s->onDisconnect = NULL;
- s->onSend = NULL;
-
- // .. remove from evdp and close fd.
- evdp_remove(fd, &s->evdp_data);
- close(fd);
- return -1;
- }
-
-
- // ! The Info Message :~D
- ShowStatus("network_connect fd#%u (%s:%u) in progress.. \n", fd, addr, port);
-
-return fd;
-}//end: network_connect()
-
-
-static bool _onSend(int32 fd){
- register SESSION *s = &g_Session[fd];
- register netbuf buf, buf_next;
- register uint32 szNeeded;
- register int wLen;
-
- if(s->type == NST_FREE)
- return true; // Possible due to multipl non coalsced event notifications
- // so onSend gets called after disconnect caused by an previous vent.
- // we can ignore the call to onSend, then.
-
- buf = s->write.buf;
- while(1){
- if(buf == NULL)
- break;
-
- buf_next = buf->next;
-
-
- szNeeded = (buf->dataLen - s->write.dataPos); // using th session-local .dataPos member, due to shared write buffer support.
-
- // try to write.
- wLen = write(fd, &buf->buf[s->write.dataPos], szNeeded);
- if(wLen == 0){
- return false; // eof.
- }else if(wLen == -1){
- if(errno == EAGAIN || errno == EWOULDBLOCK)
- return true; // dont disconnect / try again later.
-
- // all other errors. .
- return false;
- }
-
- // Wrote data.. =>
- szNeeded -= wLen;
- if(szNeeded > 0){
- // still data left ..
- //
- s->write.dataPos += wLen; // fix offset.
- return true;
- }else{
- // this buffer has been written successfully
- // could be returned to pool.
- netbuffer_put(buf);
- s->write.n_outstanding--; // When threadsafe -> Interlocked here.
- s->write.dataPos = 0;
- }
-
-
- buf = buf_next;
- }
-
- // okay,
- // reaching this part means:
- // while interrupted by break -
- // which means all buffers are written, nothing left
- //
-
- s->write.buf_last = NULL;
- s->write.buf = NULL;
- s->write.n_outstanding = 0;
- s->write.dataPos = 0;
-
- // Remove from event dispatcher (write notification)
- //
- evdp_writable_remove(fd, &s->evdp_data);
-
- return true;
-}//end: _onSend()
-
-
-static bool _onRORecv(int32 fd){
- register SESSION *s = &g_Session[fd];
- register uint32 szNeeded;
- register char *p;
- register int rLen;
-
- if(s->type == NST_FREE)
- return true; // Possible due to multiple non coalesced events by evdp.
- // simply ignore this call returning positive result.
-
- // Initialize p and szNeeded depending on change
- //
- switch(s->read.state){
- case NRS_WAITOP:
- szNeeded = s->read.head_left;
- p = ((char*)&s->read.head[0]) + (2-szNeeded);
- break;
-
- case NRS_WAITLEN:
- szNeeded = s->read.head_left;
- p = ((char*)&s->read.head[1]) + (2-szNeeded);
- break;
-
- case NRS_WAITDATA:{
- register netbuf buf = s->read.buf;
-
- szNeeded = (buf->dataLen - buf->dataPos);
- p = (char*)&buf->buf[ buf->dataPos ];
- }
- break;
-
- default:
- // .. the impossible gets possible ..
- ShowError("_onRORecv: fd #%u has unknown read.state (%d) - disconnecting\n", fd, s->read.state);
- return false;
- break;
- }
-
-
- //
-
- rLen = read(fd, p, szNeeded);
- if(rLen == 0){
- // eof..
- return false;
- }else if(rLen == -1){
-
- if(errno == EAGAIN || errno == EWOULDBLOCK){
- // try again later .. (this case shouldnt happen, because we're event trigered.. but .. sometimes it happens :)
- return true;
- }
-
- // an additional interesting case would be
- // EINTR, this 'could' be handled .. but:
- // posix says that its possible that data gets currupted during irq
- // or data gor read and not reported.., so we'd have a data loss..
- // (which shouldnt happen with stream based protocols such as tcp)
- // its better to disonnect the client in that case.
-
- return false;
- }
-
- //
- // Got Data:
- // next action also depends on current state ..
- //
- szNeeded -= rLen;
- switch(s->read.state){
- case NRS_WAITOP:
-
- if(szNeeded > 0){
- // still data missing ..
- s->read.head_left = szNeeded;
- return true; // wait for completion.
- }else{
- // complete ..
- // next state depends on packet type.
-
- s->read.head[1] = ((uint16*)s->netparser_data)[ s->read.head[0] ]; // store lenght of packet by opcode head[0] to head[1]
-
- if(s->read.head[1] == ROPACKET_UNKNOWN){
- // unknown packet - disconnect
- ShowWarning("_onRORecv: fd #%u got unlnown packet 0x%04x - disconnecting.\n", fd, s->read.head[0]);
- return false;
- }
- else if(s->read.head[1] == ROPACKET_DYNLEN){
- // dynamic length
- // next state: requrie len.
- s->read.state = NRS_WAITLEN;
- s->read.head_left = 2;
- return true; //
- }
- else if(s->read.head[1] == 2){
- // packet has no data (only opcode)
- register netbuf buf = netbuffer_get(2); // :D whoohoo its giant!
-
- NBUFW(buf, 0) = s->read.head[0]; // store opcode @ packet begin.
- buf->dataPos = 2;
- buf->dataLen = 2;
- buf->next = NULL;
-
- // Back to initial state -> Need opcode.
- s->read.state = NRS_WAITOP;
- s->read.head_left = 2;
- s->read.buf = NULL;
-
- // Call completion routine here.
- s->onPacketComplete(fd, s->read.head[0], 2, buf);
-
- return true; // done :)
- }
- else{
- // paket needs .. data ..
- register netbuf buf = netbuffer_get( s->read.head[1] );
-
- NBUFW(buf, 0) = s->read.head[0]; // store opcode @ packet begin.
- buf->dataPos = 2;
- buf->dataLen = s->read.head[1];
- buf->next = NULL;
-
- // attach buffer.
- s->read.buf = buf;
-
- // set state:
- s->read.state = NRS_WAITDATA;
-
- return true;
- }
-
- }//endif: szNeeded > 0 (opcode read completed?)
-
- break;
-
-
- case NRS_WAITLEN:
-
- if(szNeeded > 0){
- // incomplete ..
- s->read.head_left = szNeeded;
- return true;
- }else{
-
- if(s->read.head[1] == 4){
- // packet has no data (only opcode + length)
- register netbuf buf = netbuffer_get( 4 );
-
- NBUFL(buf, 0) = *((uint32*)&s->read.head[0]); // copy Opcode + length to netbuffer using MOVL
- buf->dataPos = 4;
- buf->dataLen = 4;
- buf->next = NULL;
-
- // set initial state (need opcode)
- s->read.state = NRS_WAITOP;
- s->read.head_left = 2;
- s->read.buf = NULL;
-
- // call completion routine.
- s->onPacketComplete(fd, s->read.head[0], 4, buf);
-
- return true;
- }
- else if(s->read.head[1] < 4){
- // invalid header.
- ShowWarning("_onRORecv: fd #%u invalid header - got packet 0x%04x, reported length < 4 - INVALID - disconnecting\n", fd, s->read.head[0]);
- return false;
- }
- else{
- // Data needed
- // next state -> waitdata!
- register netbuf buf = netbuffer_get( s->read.head[1] );
-
- NBUFL(buf, 0) = *((uint32*)&s->read.head[0]); // copy Opcode + length to netbuffer using MOVL
- buf->dataPos = 4;
- buf->dataLen = s->read.head[1];
- buf->next = NULL;
-
- // attach to session:
- s->read.buf = buf;
- s->read.state = NRS_WAITDATA;
-
- return true;
- }
-
- }//endif: szNeeded > 0 (length read complete?)
-
- break;
-
-
- case NRS_WAITDATA:
-
- if(szNeeded == 0){
- // Packet finished!
- // compltion.
- register netbuf buf = s->read.buf;
-
- // set initial state.
- s->read.state = NRS_WAITOP;
- s->read.head_left = 2;
- s->read.buf = NULL;
-
- // Call completion routine.
- s->onPacketComplete(fd, NBUFW(buf, 0), buf->dataLen, buf);
-
- return true;
- }else{
- // still data needed
- s->read.buf->dataPos += rLen;
-
- return true;
- }
- break;
-
-
- //
- default:
- ShowError("_onRORecv: fd #%u has unknown read.state (%d) [2] - disconnecting\n", fd, s->read.state);
- return false;
- break;
- }
-
-
- return false;
-}//end: _onRORecv()
-
-
-void network_send(int32 fd, netbuf buf){
- register SESSION *s = &g_Session[fd];
-
-#ifdef PARANOID_CHECKS
- if(fd >= MAXCONN){
- ShowError("network_send: tried to attach buffer to connection idientifer #%u which is out of bounds.\n", fd);
- _network_free_netbuf_async(buf);
- return;
- }
-#endif
-
-
- if(s->type == NST_FREE)
- return;
-
- // Check Max Outstanding buffers limit.
- if( (s->write.max_outstanding > 0) &&
- (s->write.n_outstanding >= s->write.max_outstanding) ){
-
- ShowWarning("network_send: fd #%u max Outstanding buffers exceeded. - disconnecting.\n", fd);
- network_disconnect(fd);
- //
- _network_free_netbuf_async(buf);
- return;
- }
-
-
- // Attach to the end:
- buf->next = NULL;
- if(s->write.buf_last != NULL){
- s->write.buf_last->next = buf;
- s->write.buf_last = buf;
-
- }else{
- // currently no buffer attached.
- s->write.buf = s->write.buf_last = buf;
-
- // register @ evdp for writable notification.
- evdp_writable_add(fd, &s->evdp_data); //
- }
-
-
- //
- s->write.n_outstanding++;
-
-}//end: network_send()
-
-
-void network_parser_set_ro(int32 fd,
- int16 *packetlentable,
- void (*onPacketCompleteProc)(int32 fd, uint16 op, uint16 len, netbuf buf)
- ){
- register SESSION *s = &g_Session[fd];
- register netbuf b, nb; // used for potential free attached buffers.
-
- if(s->type == NST_FREE)
- return;
-
- s->onPacketComplete = onPacketCompleteProc;
-
- s->onRecv = _onRORecv; // ..
- s->onSend = _onSend; // Using the normal generic netbuf based send function.
-
- s->netparser_data = packetlentable;
-
- // Initial State -> Need Packet OPCode.
- s->read.state = NRS_WAITOP;
- s->read.head_left = 2;
-
-
- // Detach (if..) all buffers.
- if(s->read.buf != NULL){
- _network_free_netbuf_async(s->read.buf); //
- s->read.buf = NULL;
- }
-
- if(s->write.buf != NULL){
- b = s->write.buf;
- while(1){
- nb = b->next;
-
- _network_free_netbuf_async(b);
-
- b = nb;
- }
-
- s->write.buf = NULL;
- s->write.buf_last = NULL;
- s->write.n_outstanding = 0;
- }
-
- // not changing any limits on outstanding ..
- //
-
-}//end: network_parser_set_ro()
diff --git a/src/common/network.h b/src/common/network.h
deleted file mode 100644
index d7b463a2f..000000000
--- a/src/common/network.h
+++ /dev/null
@@ -1,189 +0,0 @@
-#ifndef _rA_NETWORK_H_
-#define _rA_NETWORK_H_
-
-#include <netinet/in.h>
-#include "../common/cbasetypes.h"
-#include "../common/netbuffer.h"
-#include "../common/evdp.h"
-
-#ifndef MAXCONN
-#define MAXCONN 16384
-#endif
-
-
-typedef struct SESSION{
- EVDP_DATA evdp_data; // Must be always the frist member! (some evdp's may rely on this fact)
-
- // Connection Type
- enum{ NST_FREE=0, NST_LISTENER = 1, NST_CLIENT=2, NST_OUTGOING=3} type;
-
- // Flags / Settings.
- bool v6; // is v6?
- bool disconnect_in_progress; // To prevent stack overflows / recursive calls.
-
-
- union{ // union to save memory.
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- }addr;
-
-
- // "lowlevel" Handlers
- // (Implemented by the protocol specific parser)
- //
- bool (*onRecv)(int32 fd); // return false = disconnect
- bool (*onSend)(int32 fd); // return false = disconnect
-
- // Event Handlers for LISTENER type sockets
- //
- // onConnect gets Called when a connection has been
- // successfully accepted.
- // Session entry is available in this Handler!
- // A returncode of false will reejct the connection (disconnect)
- // Note: When rejecting a connection in onConnect by returning false
- // The onDisconnect handler wont get called!
- // Note: the onConnect Handler is also responsible for setting
- // the appropriate netparser (which implements onRecv/onSend..) [protocol specific]
- //
- // onDisconnect gets called when a connection gets disconnected
- // (by peer as well as by core)
- //
- bool (*onConnect)(int32 fd); // return false = disconnect (wont accept)
- void (*onDisconnect)(int32 fd);
-
-
- //
- // Parser specific data
- //
- void *netparser_data; // incase of RO Packet Parser, pointer to packet len table (uint16array)
- void (*onPacketComplete)(int32 fd, uint16 op, uint16 len, netbuf buf);
-
-
- //
- // Buffers
- //
- struct{
- enum NETREADSTATE { NRS_WAITOP = 0, NRS_WAITLEN = 1, NRS_WAITDATA = 2} state;
-
- uint32 head_left;
- uint16 head[2];
-
- netbuf buf;
- } read;
-
- struct{
- uint32 max_outstanding;
- uint32 n_outstanding;
-
- uint32 dataPos;
-
- netbuf buf, buf_last;
- } write;
-
- // Application Level data Pointer
- // (required for backward compatibility with previous athena socket system.)
- void *data;
-
-} SESSION;
-
-
-/**
- * Subsystem Initialization / Finalization.
- *
- */
-void network_init();
-void network_final();
-
-
-/**
- * Will do the net work :) ..
- */
-void network_do();
-
-
-/**
- * Adds a new listner.
- *
- * @param v6 v6 listner?
- * @param *addr the address to listen on.
- * @param port port to listen on
- *
- * @return -1 on error otherwise the identifier of the new listener.
- */
-int32 network_addlistener(bool v6, const char *addr, uint16 port);
-
-
-/**
- * Tries to establish an outgoing connection.
- *
- * @param v6 operate with IPv6 addresses?
- * @param addr the address to connect to
- * @param port the port to connect to
- * @param from_addr the address to connect from (local source / optional if auto -> NULL)
- * @param from_port the port to connect from (local source / optional if auto -> 0)
- * @param onConnectionEstablishedHandler callback that gets called when the connection is established.
- * @param onConnectionLooseHandler callback that gets called when the connection gets disconnected (or the connection couldnt be established)
- *
- * @return -1 on error otherwise the identifier of the new connection
- */
-int32 network_connect(bool v6,
- const char *addr,
- uint16 port,
- const char *from_addr,
- uint16 from_port,
- bool (*onConnectionEstablishedHandler)(int32 fd),
- void (*onConnectionLooseHandler)(int32 fd)
-);
-
-
-
-/**
- * Disconnects the given connection
- *
- * @param fd connection identifier.
- *
- * @Note:
- * - onDisconnect callback gets called!
- * - cleares (returns) all assigned buffers
- *
- */
-void network_disconnect(int32 fd);
-
-
-/**
- * Attach's a netbuffer at the end of sending queue to the given connection
- *
- * @param fd connection identifier
- * @param buf netbuffer to attach.
- */
-void network_send(int32 fd, netbuf buf);
-
-
-/**
- * Sets the parser to RO Protocol like Packet Parser.
- *
- * @param fd connection identifier
- * @param *packetlentable pointer to array of uint16 in size of UINT16_MAX,
- * @param onComplteProc callback for packet completion.
- *
- * @note:
- * PacketLen Table Fromat:
- * each element's offsets represents th ro opcode.
- * value is length.
- * a length of 0 means the packet is dynamic.
- * a length of UINT16_MAX means the packet is unknown.
- *
- * Static Packets must contain their hader in len so (0x64 == 55 ..)
- *
- */
-void network_parser_set_ro(int32 fd,
- int16 *packetlentable,
- void (*onPacketCompleteProc)(int32 fd, uint16 op, uint16 len, netbuf buf)
- );
-#define ROPACKET_UNKNOWN UINT16_MAX
-#define ROPACKET_DYNLEN 0
-
-
-
-
-#endif
diff --git a/src/common/nullpo.c b/src/common/nullpo.c
index 4383109a7..a3471b00d 100644
--- a/src/common/nullpo.c
+++ b/src/common/nullpo.c
@@ -1,91 +1,34 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Portions Copyright (c) Athena Dev Teams
+
+#define HERCULES_CORE
+
+#include "nullpo.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
-#include "nullpo.h"
-#include "../common/showmsg.h"
-// #include "logs.h" // •z΂µ‚Ă݂é
-
-static void nullpo_info_core(const char *file, int line, const char *func,
- const char *fmt, va_list ap);
-
-/*======================================
- * Nullƒ`ƒFƒbƒN ‹y‚Ñ î•ño—Í
- *--------------------------------------*/
-int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
- const char *fmt, ...)
-{
- va_list ap;
-
- if (target != NULL)
- return 0;
-
- va_start(ap, fmt);
- nullpo_info_core(file, line, func, fmt, ap);
- va_end(ap);
- return 1;
-}
-
-int nullpo_chk(const char *file, int line, const char *func, const void *target)
-{
- if (target != NULL)
- return 0;
-
- nullpo_info_core(file, line, func, NULL, NULL);
- return 1;
-}
-
-
-/*======================================
- * nullpoî•ño—Í(ŠO•”ŒÄo‚µŒü‚¯ƒ‰ƒbƒp)
- *--------------------------------------*/
-void nullpo_info_f(const char *file, int line, const char *func,
- const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- nullpo_info_core(file, line, func, fmt, ap);
- va_end(ap);
-}
-
-void nullpo_info(const char *file, int line, const char *func)
-{
- nullpo_info_core(file, line, func, NULL, NULL);
-}
+#include "../common/showmsg.h"
-/*======================================
- * nullpoî•ño—Í(Main)
- *--------------------------------------*/
-static void nullpo_info_core(const char *file, int line, const char *func,
- const char *fmt, va_list ap)
-{
+/**
+ * Reports failed assertions or NULL pointers
+ *
+ * @param file Source file where the error was detected
+ * @param line Line
+ * @param func Function
+ * @param targetname Name of the checked symbol
+ * @param title Message title to display (i.e. failed assertion or nullpo info)
+ */
+void assert_report(const char *file, int line, const char *func, const char *targetname, const char *title) {
if (file == NULL)
file = "??";
- func =
- func == NULL ? "unknown":
- func[0] == '\0' ? "unknown":
- func;
-
- ShowMessage("--- nullpo info --------------------------------------------\n");
- ShowMessage("%s:%d: in func `%s'\n", file, line, func);
- if (fmt != NULL)
- {
- if (fmt[0] != '\0')
- {
- vprintf(fmt, ap);
-
- // ÅŒã‚ɉüs‚µ‚½‚©Šm”F
- if (fmt[strlen(fmt)-1] != '\n')
- ShowMessage("\n");
- }
- }
- ShowMessage("--- end nullpo info ----------------------------------------\n");
+ if (func == NULL || *func == '\0')
+ func = "unknown";
- // ‚±‚±‚ç‚ÅnullpoƒƒO‚ðƒtƒ@ƒCƒ‹‚É‘‚«o‚¹‚½‚ç
- // ‚܂Ƃ߂Ēño‚Å‚«‚é‚ȂƎv‚Á‚Ä‚¢‚½‚èB
+ ShowError("--- %s --------------------------------------------\n", title);
+ ShowError("%s:%d: '%s' in function `%s'\n", file, line, targetname, func);
+ ShowError("--- end %s ----------------------------------------\n", title);
}
diff --git a/src/common/nullpo.h b/src/common/nullpo.h
index 8ee86a782..581252cca 100644
--- a/src/common/nullpo.h
+++ b/src/common/nullpo.h
@@ -1,225 +1,128 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _NULLPO_H_
-#define _NULLPO_H_
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Portions Copyright (c) Athena Dev Teams
+#ifndef COMMON_NULLPO_H
+#define COMMON_NULLPO_H
#include "../common/cbasetypes.h"
-#define NLP_MARK __FILE__, __LINE__, __func__
-
// enabled by default on debug builds
#if defined(DEBUG) && !defined(NULLPO_CHECK)
#define NULLPO_CHECK
#endif
-/*----------------------------------------------------------------------------
- * Macros
- *----------------------------------------------------------------------------
- */
-/*======================================
- * Nullƒ`ƒFƒbƒN ‹y‚Ñ î•ño—ÍŒã return
- *E“WŠJ‚·‚邯if‚Æ‚©return“™‚ªo‚é‚Ì‚Å
- * ˆês’P‘̂Ŏg‚Á‚Ä‚­‚¾‚³‚¢B
- *Enullpo_ret(x = func());
- * ‚̂悤‚ÈŽg—p–@‚à‘z’肵‚Ä‚¢‚Ü‚·B
- *--------------------------------------
- * nullpo_ret(t)
- * –ß‚è’l 0ŒÅ’è
- * [ˆø”]
- * t ƒ`ƒFƒbƒN‘ÎÛ
- *--------------------------------------
- * nullpo_retv(t)
- * –ß‚è’l ‚È‚µ
- * [ˆø”]
- * t ƒ`ƒFƒbƒN‘ÎÛ
- *--------------------------------------
- * nullpo_retr(ret, t)
- * –ß‚è’l Žw’è
- * [ˆø”]
- * ret return(ret);
- * t ƒ`ƒFƒbƒN‘ÎÛ
- *--------------------------------------
- * nullpo_ret_f(t, fmt, ...)
- * Ú×î•ño—Í—p
- * –ß‚è’l 0
- * [ˆø”]
- * t ƒ`ƒFƒbƒN‘ÎÛ
- * fmt ... vprintf‚É“n‚³‚ê‚é
- * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚ȂǂÉ
- *--------------------------------------
- * nullpo_retv_f(t, fmt, ...)
- * Ú×î•ño—Í—p
- * –ß‚è’l ‚È‚µ
- * [ˆø”]
- * t ƒ`ƒFƒbƒN‘ÎÛ
- * fmt ... vprintf‚É“n‚³‚ê‚é
- * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚ȂǂÉ
- *--------------------------------------
- * nullpo_retr_f(ret, t, fmt, ...)
- * Ú×î•ño—Í—p
- * –ß‚è’l Žw’è
- * [ˆø”]
- * ret return(ret);
- * t ƒ`ƒFƒbƒN‘ÎÛ
- * fmt ... vprintf‚É“n‚³‚ê‚é
- * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚ȂǂÉ
- *--------------------------------------
- */
+// Skip assert checks on release builds
+#if !defined(RELEASE) && !defined(ASSERT_CHECK)
+#define ASSERT_CHECK
+#endif
+
+/** Assert */
+
+#if defined(ASSERT_CHECK)
+// extern "C" {
+#include <assert.h>
+// }
+#if !defined(DEFCPP) && defined(WIN32) && !defined(MINGW)
+#include <crtdbg.h>
+#endif // !DEFCPP && WIN && !MINGW
+#define Assert(EX) assert(EX)
+#define Assert_chk(EX) ( (EX) ? false : (assert_report(__FILE__, __LINE__, __func__, #EX, "failed assertion"), true) )
+#else // ! ASSERT_CHECK
+#define Assert(EX) (EX)
+#define Assert_chk(EX) ((EX), false)
+#endif // ASSERT_CHECK
#if defined(NULLPO_CHECK)
+/**
+ * Reports NULL pointer information if the passed pointer is NULL
+ *
+ * @param t pointer to check
+ * @return true if the passed pointer is NULL, false otherwise
+ */
+#define nullpo_chk(t) ( (t) != NULL ? false : (assert_report(__FILE__, __LINE__, __func__, #t, "nullpo info"), true) )
+#else // ! NULLPO_CHECK
+#define nullpo_chk(t) ((void)(t), false)
+#endif // NULLPO_CHECK
+
+/**
+ * The following macros check for NULL pointers and return from the current
+ * function or block in case one is found.
+ *
+ * It is guaranteed that the argument is evaluated once and only once, so it
+ * is safe to call them as:
+ * nullpo_ret(x = func());
+ * The macros can be used safely in any context, as they expand to a do/while
+ * construct, except nullpo_retb, which expands to an if/else construct.
+ */
+/**
+ * Returns 0 if a NULL pointer is found.
+ *
+ * @param t pointer to check
+ */
#define nullpo_ret(t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {return(0);}
-
-#define nullpo_retv(t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {return;}
-
-#define nullpo_retr(ret, t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {return(ret);}
-
-#define nullpo_retb(t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {break;}
-
-// ‰Â•ψø”ƒ}ƒNƒ‚ÉŠÖ‚·‚éðŒƒRƒ“ƒpƒCƒ‹
-#if __STDC_VERSION__ >= 199901L
-/* C99‚ɑΉž */
-#define nullpo_ret_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(0);}
-
-#define nullpo_retv_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return;}
-
-#define nullpo_retr_f(ret, t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(ret);}
-
-#define nullpo_retb_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {break;}
+ do { if (nullpo_chk(t)) return(0); } while(0)
-#elif __GNUC__ >= 2
-/* GCC—p */
-#define nullpo_ret_f(t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(0);}
-
-#define nullpo_retv_f(t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return;}
-
-#define nullpo_retr_f(ret, t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(ret);}
-
-#define nullpo_retb_f(t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {break;}
-
-#else
-
-/* ‚»‚Ì‘¼‚Ìê‡EEE orz */
-
-#endif
-
-#else /* NULLPO_CHECK */
-/* No Nullpo check */
-
-// if((t)){;}
-// —Ç‚¢•û–@‚ªŽv‚¢‚‚©‚È‚©‚Á‚½‚Ì‚ÅEEE‹ê“÷‚Ìô‚Å‚·B
-// ˆê‰žƒ[ƒjƒ“ƒO‚Ío‚È‚¢‚Í‚¸
-
-#define nullpo_ret(t) (void)(t)
-#define nullpo_retv(t) (void)(t)
-#define nullpo_retr(ret, t) (void)(t)
-#define nullpo_retb(t) (void)(t)
-
-// ‰Â•ψø”ƒ}ƒNƒ‚ÉŠÖ‚·‚éðŒƒRƒ“ƒpƒCƒ‹
-#if __STDC_VERSION__ >= 199901L
-/* C99‚ɑΉž */
-#define nullpo_ret_f(t, fmt, ...) (void)(t)
-#define nullpo_retv_f(t, fmt, ...) (void)(t)
-#define nullpo_retr_f(ret, t, fmt, ...) (void)(t)
-#define nullpo_retb_f(t, fmt, ...) (void)(t)
-
-#elif __GNUC__ >= 2
-/* GCC—p */
-#define nullpo_ret_f(t, fmt, args...) (void)(t)
-#define nullpo_retv_f(t, fmt, args...) (void)(t)
-#define nullpo_retr_f(ret, t, fmt, args...) (void)(t)
-#define nullpo_retb_f(t, fmt, args...) (void)(t)
-
-#else
-/* ‚»‚Ì‘¼‚Ìê‡EEE orz */
-#endif
+/**
+ * Returns 0 if the given assertion fails.
+ *
+ * @param t statement to check
+ */
+#define Assert_ret(t) \
+ do { if (Assert_chk(t)) return(0); } while(0)
-#endif /* NULLPO_CHECK */
+/**
+ * Returns void if a NULL pointer is found.
+ *
+ * @param t pointer to check
+ */
+#define nullpo_retv(t) \
+ do { if (nullpo_chk(t)) return; } while(0)
-/*----------------------------------------------------------------------------
- * Functions
- *----------------------------------------------------------------------------
+/**
+ * Returns void if the given assertion fails.
+ *
+ * @param t statement to check
*/
-/*======================================
- * nullpo_chk
- * Nullƒ`ƒFƒbƒN ‹y‚Ñ î•ño—Í
- * [ˆø”]
- * file __FILE__
- * line __LINE__
- * func __func__ (ŠÖ”–¼)
- * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Ƃ悢
- * target ƒ`ƒFƒbƒN‘ÎÛ
- * [•Ô‚è’l]
- * 0 OK
- * 1 NULL
- *--------------------------------------
+#define Assert_retv(t) \
+ do { if (Assert_chk(t)) return; } while(0)
+
+/**
+ * Returns the given value if a NULL pointer is found.
+ *
+ * @param ret value to return
+ * @param t pointer to check
*/
-int nullpo_chk(const char *file, int line, const char *func, const void *target);
-
-
-/*======================================
- * nullpo_chk_f
- * Nullƒ`ƒFƒbƒN ‹y‚Ñ ÚׂÈî•ño—Í
- * [ˆø”]
- * file __FILE__
- * line __LINE__
- * func __func__ (ŠÖ”–¼)
- * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Ƃ悢
- * target ƒ`ƒFƒbƒN‘ÎÛ
- * fmt ... vprintf‚É“n‚³‚ê‚é
- * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚ȂǂÉ
- * [•Ô‚è’l]
- * 0 OK
- * 1 NULL
- *--------------------------------------
+#define nullpo_retr(ret, t) \
+ do { if (nullpo_chk(t)) return(ret); } while(0)
+
+/**
+ * Returns the given value if the given assertion fails.
+ *
+ * @param ret value to return
+ * @param t statement to check
*/
-int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
- const char *fmt, ...)
- __attribute__((format(printf,5,6)));
-
-
-/*======================================
- * nullpo_info
- * nullpoî•ño—Í
- * [ˆø”]
- * file __FILE__
- * line __LINE__
- * func __func__ (ŠÖ”–¼)
- * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Ƃ悢
- *--------------------------------------
+#define Assert_retr(ret, t) \
+ do { if (Assert_chk(t)) return(ret); } while(0)
+
+/**
+ * Breaks from the current loop/switch if a NULL pointer is found.
+ *
+ * @param t pointer to check
*/
-void nullpo_info(const char *file, int line, const char *func);
-
-
-/*======================================
- * nullpo_info_f
- * nullpoÚ×î•ño—Í
- * [ˆø”]
- * file __FILE__
- * line __LINE__
- * func __func__ (ŠÖ”–¼)
- * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Ƃ悢
- * fmt ... vprintf‚É“n‚³‚ê‚é
- * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚ȂǂÉ
- *--------------------------------------
+#define nullpo_retb(t) \
+ if (nullpo_chk(t)) break; else (void)0
+
+/**
+ * Breaks from the current loop/switch if the given assertion fails.
+ *
+ * @param t statement to check
*/
-void nullpo_info_f(const char *file, int line, const char *func,
- const char *fmt, ...)
- __attribute__((format(printf,4,5)));
+#define Assert_retb(t) \
+ if (Assert_chk(t)) break; else (void)0
+
+void assert_report(const char *file, int line, const char *func, const char *targetname, const char *title);
-#endif /* _NULLPO_H_ */
+#endif /* COMMON_NULLPO_H */
diff --git a/src/common/raconf.c b/src/common/raconf.c
deleted file mode 100644
index f7d1284b7..000000000
--- a/src/common/raconf.c
+++ /dev/null
@@ -1,584 +0,0 @@
-//
-// Athena style config parser
-// (would be better to have "one" implementation instead of .. 4 :)
-//
-//
-// Author: Florian Wilkemeyer <fw@f-ws.de>
-//
-// Copyright (c) RAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-//
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../common/cbasetypes.h"
-#include "../common/showmsg.h"
-#include "../common/db.h"
-#include "../common/malloc.h"
-
-#include "../common/raconf.h"
-
-#define SECTION_LEN 32
-#define VARNAME_LEN 64
-
-struct raconf {
- DBMap *db;
-};
-
-
-struct conf_value{
- int64 intval;
- bool bval;
- double floatval;
- size_t strval_len; // not includung \0
- char strval[16];
-};
-
-
-
-static struct conf_value *makeValue(const char *key, char *val, size_t val_len){
- struct conf_value *v;
-/* size_t sz;
-
- sz = sizeof(struct conf_value);
- if(val_len >= sizeof(v->strval))
- sz += (val_len - sizeof(v->strval) + 1);*/
-
- v = (struct conf_value*)aCalloc(1, sizeof(struct conf_value));
- if(v == NULL){
- ShowFatalError("raconf: makeValue => Out of Memory while allocating new node.\n");
- return NULL;
- }
-
- memcpy(v->strval, val, val_len);
- v->strval[val_len+1] = '\0';
- v->strval_len = val_len;
-
-
- // Parse boolean value:
- if((val_len == 4) && (strncmpi("true", val, 4) == 0))
- v->bval = true;
- else if((val_len == 3) && (strncmpi("yes", val, 3) == 0))
- v->bval = true;
- else if((val_len == 3) && (strncmpi("oui", val, 3) == 0))
- v->bval = true;
- else if((val_len == 2) && (strncmpi("si", val, 2) == 0))
- v->bval = true;
- else if((val_len == 2) && (strncmpi("ja", val, 2) == 0))
- v->bval = true;
- else if((val_len == 1) && (*val == '1'))
- v->bval = true;
- else if((val_len == 5) && (strncmpi("false", val, 5) == 0))
- v->bval = false;
- else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
- v->bval = false;
- else if((val_len == 3) && (strncmpi("non", val, 3) == 0))
- v->bval = false;
- else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
- v->bval = false;
- else if((val_len == 4) && (strncmpi("nein", val, 4) == 0))
- v->bval = false;
- else if((val_len == 1) && (*val == '0'))
- v->bval = false;
- else
- v->bval = false; // assume false.
-
- // Parse number
- // Supported formats:
- // prefix: 0x hex .
- // postix: h for hex
- // b for bin (dual)
- if( (val_len >= 1 && (val[val_len] == 'h')) || (val_len >= 2 && (val[0] == '0' && val[1] == 'x')) ){//HEX!
- if(val[val_len] == 'h'){
- val[val_len]= '\0';
- v->intval = strtoull(val, NULL, 16);
- val[val_len] = 'h';
- }else
- v->intval = strtoull(&val[2], NULL, 16);
- }else if( val_len >= 1 && (val[val_len] == 'b') ){ //BIN
- val[val_len] = '\0';
- v->intval = strtoull(val, NULL, 2);
- val[val_len] = 'b';
- }else if( *val >='0' && *val <= '9'){ // begins with normal digit, so assume its dec.
- // is it float?
- bool is_float = false;
- char *p;
-
- for(p = val; *p != '\0'; p++){
- if(*p == '.'){
- v->floatval = strtod(val, NULL);
- v->intval = (int64) v->floatval;
- is_float = true;
- break;
- }
- }
-
- if(is_float == false){
- v->intval = strtoull(val, NULL, 10);
- v->floatval = (double) v->intval;
- }
- }else{
- // Everything else: lets use boolean for fallback
- if(v->bval == true)
- v->intval = 1;
- else
- v->intval = 0;
- }
-
- return v;
-}//end: makeValue()
-
-
-static bool configParse(raconf inst, const char *fileName){
- FILE *fp;
- char line[4096];
- char currentSection[SECTION_LEN];
- char *p;
- char c;
- int linecnt;
- size_t linelen;
- size_t currentSection_len;
-
- fp = fopen(fileName, "r");
- if(fp == NULL){
- ShowError("configParse: cannot open '%s' for reading.\n", fileName);
- return false;
- }
-
-
- // Start with empty section:
- currentSection[0] = '\0';
- currentSection_len = 0;
-
- //
- linecnt = 0;
- while(1){
- linecnt++;
-
- if(fgets(line, sizeof(line), fp) != line)
- break;
-
- linelen = strlen(line);
- p = line;
-
- // Skip whitespaces from beginning (space and tab)
- _line_begin_skip_whities:
- c = *p;
- if(c == ' ' || c == '\t'){
- p++;
- linelen--;
- goto _line_begin_skip_whities;
- }
-
- // Remove linebreaks as (cr or lf) and whitespaces from line end!
- _line_end_skip_whities_and_breaks:
- c = p[linelen-1];
- if(c == '\r' || c == '\n' || c == ' ' || c == '\t'){
- p[--linelen] = '\0';
- goto _line_end_skip_whities_and_breaks;
- }
-
- // Empty line?
- // or line starts with comment (commented out)?
- if(linelen == 0 || (p[0] == '/' && p[1] == '/') || p[0] == ';')
- continue;
-
- // Variable names can contain:
- // A-Za-z-_.0-9
- //
- // Sections start with [ .. ] (INI Style)
- //
- c = *p;
-
- // check what we have.. :)
- if(c == '['){ // got section!
- // Got Section!
- // Search for ]
- char *start = (p+1);
-
- while(1){
- ++p;
- c = *p;
-
- if(c == '\0'){
- ShowError("Syntax Error: unterminated Section name in %s:%u (expected ']')\n", fileName, linecnt);
- fclose(fp);
- return false;
- }else if(c == ']'){ // closing backet (section name termination)
- if( (p - start + 1) > (sizeof(currentSection) ) ){
- ShowError("Syntax Error: Section name in %s:%u is too large (max Supported length: %u chars)\n", fileName, linecnt, sizeof(currentSection)-1);
- fclose(fp);
- return false;
- }
-
- // Set section!
- *p = '\0'; // add termination here.
- memcpy(currentSection, start, (p-start)+1 ); // we'll copy \0, too! (we replaced the ] backet with \0.)
- currentSection_len = (p-start);
-
- break;
-
- }else if( (c >= '0' && c <= '9') || (c == '-') || (c == ' ') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
- // skip .. (allowed char / specifier)
- continue;
- }else{
- ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Section name.\n", c, fileName, linecnt, (p-line));
- fclose(fp);
- return false;
- }
-
- }//endwhile: parse section name
-
-
- }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
- // Got variable!
- // Search for '=' or ':' wich termiantes the name
- char *start = p;
- char *valuestart = NULL;
- size_t start_len;
-
- while(1){
- ++p;
- c = *p;
-
- if(c == '\0'){
- ShowError("Syntax Error: unterminated Variable name in %s:%u\n", fileName, linecnt);
- fclose(fp);
- return false;
- }else if( (c == '=') || (c == ':') ){
- // got name termination
-
- *p = '\0'; // Terminate it so (start) will hold the pointer to the name.
-
- break;
-
- }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
- // skip .. allowed char
- continue;
- }else{
- ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Variable name.\n", c, fileName, linecnt, (p-line));
- fclose(fp);
- return false;
- }
-
- }//endwhile: parse var name
-
- start_len = (p-start);
- if(start_len >= VARNAME_LEN){
- ShowError("%s:%u Variable length exceeds limit of %u Characters.\n", fileName, linecnt, VARNAME_LEN-1);
- fclose(fp);
- return false;
- }else if(start_len == 0){
- ShowError("%s:%u Empty Variable name is not allowed.\n", fileName, linecnt);
- fclose(fp);
- return false;
- }
-
-
- valuestart = (p+1);
-
-
- // Skip whitespace from begin of value (tab and space)
- _skip_value_begin_whities:
- c = *valuestart;
- if(c == ' ' || c == '\t'){
- valuestart++;
- goto _skip_value_begin_whities;
- }
-
- // Scan for value termination,
- // wich can be \0 or comment start (// or ; (INI) )
- //
- p = valuestart;
- while(1){
- c = *p;
- if(c == '\0'){
- // Terminated by line end.
- break;
- }else if(c == '/' && p[1] == '/'){
- // terminated by c++ style comment.
- *p = '\0';
- break;
- }else if(c == ';'){
- // terminated by ini style comment.
- *p = '\0';
- break;
- }
-
- p++;
- }//endwhile: search var value end.
-
-
- // Strip whitespaces from end of value.
- if(valuestart != p){ // not empty!
- p--;
- _strip_value_end_whities:
- c = *p;
- if(c == ' ' || c == '\t'){
- *p = '\0';
- p--;
- goto _strip_value_end_whities;
- }
- p++;
- }
-
-
- // Buildin Hook:
- if( stricmp(start, "import") == 0){
- if( configParse(inst, valuestart) != true){
- ShowError("%s:%u - Import of '%s' failed!\n", fileName, linecnt, valuestart);
- }
- }else{
- // put it to db.
- struct conf_value *v, *o;
- char key[ (SECTION_LEN+VARNAME_LEN+1+1) ]; //+1 for delimiter, +1 for termination.
- size_t section_len;
-
- if(*currentSection == '\0'){ // empty / none
- strncpy(key, "<unnamed>",9);
- section_len = 9;
- }else{
- strncpy(key, currentSection, currentSection_len);
- section_len = currentSection_len;
- }
-
- key[section_len] = '.'; // Delim
-
- strncpy(&key[section_len+1], start, start_len);
-
- key[section_len + start_len + 1] = '\0';
-
-
- v = makeValue(key, valuestart, (p-valuestart) );
-
- // Try to get the old one before
- o = strdb_get(inst->db, key);
- if(o != NULL){
- strdb_remove(inst->db, key);
- aFree(o); //
- }
-
- strdb_put( inst->db, key, v);
- }
-
-
- }else{
- ShowError("Syntax Error: unexpected Character '%c' in %s:%u (offset %u)\n", c, fileName, linecnt, (p-line) );
- fclose(fp);
- return false;
- }
-
-
-
- }
-
-
-
- fclose(fp);
- return true;
-}//end: configParse()
-
-
-#define MAKEKEY(dest, section, key) { size_t section_len, key_len; \
- if(section == NULL || *section == '\0'){ \
- strncpy(dest, "<unnamed>", 9); \
- section_len = 9; \
- }else{ \
- section_len = strlen(section); \
- strncpy(dest, section, section_len); \
- } \
- \
- dest[section_len] = '.'; \
- \
- key_len = strlen(key); \
- strncpy(&dest[section_len+1], key, key_len); \
- dest[section_len + key_len + 1] = '\0'; \
- }
-
-
-raconf raconf_parse(const char *file_name){
- struct raconf *rc;
-
- rc = aCalloc(1, sizeof(struct raconf) );
- if(rc == NULL){
- ShowFatalError("raconf_parse: failed to allocate memory for new handle\n");
- return NULL;
- }
-
- rc->db = strdb_alloc(DB_OPT_BASE | DB_OPT_DUP_KEY, 98);
- //
-
- if(configParse(rc, file_name) != true){
- ShowError("Failed to Parse Configuration file '%s'\n", file_name);
- }
-
- return rc;
-}//end: raconf_parse()
-
-
-void raconf_destroy(raconf rc){
- DBIterator *iter;
- struct conf_value *v;
-
- // Clear all entrys in db.
- iter = db_iterator(rc->db);
- for( v = (struct conf_value*)dbi_first(iter); dbi_exists(iter); v = (struct conf_value*)dbi_next(iter) ){
- aFree(v);
- }
- dbi_destroy(iter);
-
- db_destroy(rc->db);
-
- aFree(rc);
-
-}//end: raconf_destroy()
-
-bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
-
- v = strdb_get(rc->db, keystr);
- if(v == NULL)
- return _default;
- else
- return v->bval;
-}//end: raconf_getbool()
-
-
-float raconf_getfloat(raconf rc,const char *section, const char *key, float _default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
-
- v = strdb_get(rc->db, keystr);
- if(v == NULL)
- return _default;
- else
- return (float)v->floatval;
-}//end: raconf_getfloat()
-
-
-int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
-
- v = strdb_get(rc->db, keystr);
- if(v == NULL)
- return _default;
- else
- return v->intval;
-
-}//end: raconf_getint()
-
-
-const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
-
- v = strdb_get(rc->db, keystr);
- if(v == NULL)
- return _default;
- else
- return v->strval;
-}//end: raconf_getstr()
-
-
-bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
-
- MAKEKEY(keystr, fallback_section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
- return _default;
- }else{
- return v->bval;
- }
-
- }else{
- return v->bval;
- }
-}//end: raconf_getboolEx()
-
-
-float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
-
- MAKEKEY(keystr, fallback_section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
- return _default;
- }else{
- return (float)v->floatval;
- }
-
- }else{
- return (float)v->floatval;
- }
-
-}//end: raconf_getfloatEx()
-
-
-int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
-
- MAKEKEY(keystr, fallback_section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
- return _default;
- }else{
- return v->intval;
- }
-
- }else{
- return v->intval;
- }
-
-}//end: raconf_getintEx()
-
-
-const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default){
- char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
- struct conf_value *v;
-
- MAKEKEY(keystr, section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
-
- MAKEKEY(keystr, fallback_section, key);
- v = strdb_get(rc->db, keystr);
- if(v == NULL){
- return _default;
- }else{
- return v->strval;
- }
-
- }else{
- return v->strval;
- }
-
-}//end: raconf_getstrEx()
diff --git a/src/common/raconf.h b/src/common/raconf.h
deleted file mode 100644
index 68a2b51b2..000000000
--- a/src/common/raconf.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _rA_CONF_H_
-#define _rA_CONF_H_
-
-#include "../common/cbasetypes.h"
-
-// rAthena generic configuration file parser
-//
-// Config file Syntax is athena style
-// extended with ini style support (including sections)
-//
-// Comments are started with // or ; (ini style)
-//
-
-typedef struct raconf *raconf;
-
-
-/**
- * Parses a rAthna Configuration file
- *
- * @param file_name path to the file to parse
- *
- * @returns not NULL incase of success
- */
-raconf raconf_parse(const char *file_name);
-
-
-/**
- * Frees a Handle received from raconf_parse
- *
- * @param rc - the handle to free
- */
-void raconf_destroy(raconf rc);
-
-
-/**
- * Gets the value for Section / Key pair, if key not exists returns _default!
- *
- */
-bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default);
-float raconf_getfloat(raconf rc,const char *section, const char *key, float _default);
-int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default);
-const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default);
-
-/**
- * Gets the value for Section / Key pair, but has fallback section option if not found in section,
- * if not found in both - default gets returned.
- *
- */
-bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default);
-float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default);
-int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default);
-const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default);
-
-
-
-#endif
diff --git a/src/common/random.c b/src/common/random.c
index a7d432e34..88d5748cf 100644
--- a/src/common/random.c
+++ b/src/common/random.c
@@ -1,33 +1,40 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
+#define HERCULES_CORE
+
+#include "random.h"
+
+#include <time.h> // time
+
+#include <mt19937ar.h> // init_genrand, genrand_int32, genrand_res53
+
+#include "../common/cbasetypes.h" // for WIN32
#include "../common/showmsg.h"
#include "../common/timer.h" // gettick
-#include "random.h"
+
#if defined(WIN32)
- #include "../common/winapi.h"
+# include "../common/winapi.h"
#elif defined(HAVE_GETPID) || defined(HAVE_GETTID)
- #include <sys/types.h>
- #include <unistd.h>
+# include <sys/types.h>
+# include <unistd.h>
#endif
-#include <time.h> // time
-#include <mt19937ar.h> // init_genrand, genrand_int32, genrand_res53
/// Initializes the random number generator with an appropriate seed.
void rnd_init(void)
{
- uint32 seed = iTimer->gettick();
- seed += (uint32)time(NULL);
+ unsigned long seed = (unsigned long)timer->gettick();
+ seed += (unsigned long)time(NULL);
#if defined(WIN32)
- seed += GetCurrentProcessId();
- seed += GetCurrentThreadId();
+ seed += (unsigned long)GetCurrentProcessId();
+ seed += (unsigned long)GetCurrentThreadId();
#else
#if defined(HAVE_GETPID)
- seed += (uint32)getpid();
+ seed += (unsigned long)getpid();
#endif // HAVE_GETPID
#if defined(HAVE_GETTID)
- seed += (uint32)gettid();
+ seed += (unsigned long)gettid();
#endif // HAVE_GETTID
#endif
init_genrand(seed);
diff --git a/src/common/random.h b/src/common/random.h
index 43dfd36c0..15d7f8ab1 100644
--- a/src/common/random.h
+++ b/src/common/random.h
@@ -1,8 +1,8 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#ifndef _RANDOM_H_
-#define _RANDOM_H_
+#ifndef COMMON_RANDOM_H
+#define COMMON_RANDOM_H
#include "../common/cbasetypes.h"
@@ -15,4 +15,4 @@ int32 rnd_value(int32 min, int32 max);// [min, max]
double rnd_uniform(void);// [0.0, 1.0)
double rnd_uniform53(void);// [0.0, 1.0)
-#endif /* _RANDOM_H_ */
+#endif /* COMMON_RANDOM_H */
diff --git a/src/common/showmsg.c b/src/common/showmsg.c
index 2a3146d35..f3982d364 100644
--- a/src/common/showmsg.c
+++ b/src/common/showmsg.c
@@ -2,47 +2,34 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/cbasetypes.h"
-#include "../common/strlib.h" // StringBuf
+#define HERCULES_CORE
+
#include "showmsg.h"
-#include "core.h" //[Ind] - For SERVER_TYPE
+#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h> // atexit
#include <string.h>
-#include <stdarg.h>
#include <time.h>
-#include <stdlib.h> // atexit
-#include "libconfig.h"
+#include "../../3rdparty/libconfig/libconfig.h"
+
+#include "../common/cbasetypes.h"
+#include "../common/core.h" //[Ind] - For SERVER_TYPE
+#include "../common/strlib.h" // StringBuf
#ifdef WIN32
- #include "../common/winapi.h"
-
- #ifdef DEBUGLOGMAP
- #define DEBUGLOGPATH "log\\map-server.log"
- #else
- #ifdef DEBUGLOGCHAR
- #define DEBUGLOGPATH "log\\char-server.log"
- #else
- #ifdef DEBUGLOGLOGIN
- #define DEBUGLOGPATH "log\\login-server.log"
- #endif
- #endif
- #endif
-#else
- #include <unistd.h>
-
- #ifdef DEBUGLOGMAP
- #define DEBUGLOGPATH "log/map-server.log"
- #else
- #ifdef DEBUGLOGCHAR
- #define DEBUGLOGPATH "log/char-server.log"
- #else
- #ifdef DEBUGLOGLOGIN
- #define DEBUGLOGPATH "log/login-server.log"
- #endif
- #endif
- #endif
+# include "../common/winapi.h"
+#else // not WIN32
+# include <unistd.h>
+#endif // WIN32
+
+#if defined(DEBUGLOGMAP)
+#define DEBUGLOGPATH "log"PATHSEP_STR"map-server.log"
+#elif defined(DEBUGLOGCHAR)
+#define DEBUGLOGPATH "log"PATHSEP_STR"char-server.log"
+#elif defined(DEBUGLOGLOGIN)
+#define DEBUGLOGPATH "log"PATHSEP_STR"login-server.log"
#endif
///////////////////////////////////////////////////////////////////////////////
@@ -61,51 +48,50 @@ int console_msg_log = 0;//[Ind] msg error logging
#define SBUF_SIZE 2054 // never put less that what's required for the debug message
-#define NEWBUF(buf) \
- struct { \
- char s_[SBUF_SIZE]; \
- StringBuf *d_; \
- char *v_; \
- int l_; \
- } buf ={"",NULL,NULL,0}; \
+#define NEWBUF(buf) \
+ struct { \
+ char s_[SBUF_SIZE]; \
+ StringBuf *d_; \
+ char *v_; \
+ int l_; \
+ } buf ={"",NULL,NULL,0}; \
//define NEWBUF
-#define BUFVPRINTF(buf,fmt,args) \
- buf.l_ = vsnprintf(buf.s_, SBUF_SIZE, fmt, args); \
- if( buf.l_ >= 0 && buf.l_ < SBUF_SIZE ) \
- {/* static buffer */ \
- buf.v_ = buf.s_; \
- } \
- else \
- {/* dynamic buffer */ \
- buf.d_ = StrBuf->Malloc(); \
- buf.l_ = StrBuf->Vprintf(buf.d_, fmt, args); \
- buf.v_ = StrBuf->Value(buf.d_); \
- ShowDebug("showmsg: dynamic buffer used, increase the static buffer size to %d or more.\n", buf.l_+1);\
- } \
-//define BUFVPRINTF
-
-#define BUFVAL(buf) buf.v_
-#define BUFLEN(buf) buf.l_
-
-#define FREEBUF(buf) \
- if( buf.d_ ) \
- { \
- StrBuf->Free(buf.d_); \
- buf.d_ = NULL; \
- } \
- buf.v_ = NULL; \
-//define FREEBUF
+#define BUFVPRINTF(buf,fmt,args) do { \
+ (buf).l_ = vsnprintf((buf).s_, SBUF_SIZE, (fmt), args); \
+ if( (buf).l_ >= 0 && (buf).l_ < SBUF_SIZE ) \
+ {/* static buffer */ \
+ (buf).v_ = (buf).s_; \
+ } \
+ else \
+ {/* dynamic buffer */ \
+ (buf).d_ = StrBuf->Malloc(); \
+ (buf).l_ = StrBuf->Vprintf((buf).d_, (fmt), args); \
+ (buf).v_ = StrBuf->Value((buf).d_); \
+ ShowDebug("showmsg: dynamic buffer used, increase the static buffer size to %d or more.\n", (buf).l_+1); \
+ } \
+} while(0) //define BUFVPRINTF
+
+#define BUFVAL(buf) ((buf).v_)
+#define BUFLEN(buf) ((buf).l_)
+
+#define FREEBUF(buf) do {\
+ if( (buf).d_ ) { \
+ StrBuf->Free((buf).d_); \
+ (buf).d_ = NULL; \
+ } \
+ (buf).v_ = NULL; \
+} while(0) //define FREEBUF
///////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
// XXX adapted from eApp (comments are left untouched) [flaviojs]
///////////////////////////////////////////////////////////////////////////////
-// ansi compatible printf with control sequence parser for windows
+// ANSI compatible printf with control sequence parser for windows
// fast hack, handle with care, not everything implemented
//
-// \033[#;...;#m - Set Graphics Rendition (SGR)
+// \033[#;...;#m - Set Graphics Rendition (SGR)
//
// printf("\x1b[1;31;40m"); // Bright red on black
// printf("\x1b[3;33;45m"); // Blinking yellow on magenta (blink not implemented)
@@ -124,19 +110,19 @@ int console_msg_log = 0;//[Ind] msg error logging
// 8 - Concealed (invisible)
//
// \033[#A - Cursor Up (CUU)
-// Moves the cursor up by the specified number of lines without changing columns.
+// Moves the cursor up by the specified number of lines without changing columns.
// If the cursor is already on the top line, this sequence is ignored. \e[A is equivalent to \e[1A.
//
// \033[#B - Cursor Down (CUD)
-// Moves the cursor down by the specified number of lines without changing columns.
+// Moves the cursor down by the specified number of lines without changing columns.
// If the cursor is already on the bottom line, this sequence is ignored. \e[B is equivalent to \e[1B.
//
// \033[#C - Cursor Forward (CUF)
-// Moves the cursor forward by the specified number of columns without changing lines.
+// Moves the cursor forward by the specified number of columns without changing lines.
// If the cursor is already in the rightmost column, this sequence is ignored. \e[C is equivalent to \e[1C.
//
// \033[#D - Cursor Backward (CUB)
-// Moves the cursor back by the specified number of columns without changing lines.
+// Moves the cursor back by the specified number of columns without changing lines.
// If the cursor is already in the leftmost column, this sequence is ignored. \e[D is equivalent to \e[1D.
//
// \033[#E - Cursor Next Line (CNL)
@@ -149,19 +135,19 @@ int console_msg_log = 0;//[Ind] msg error logging
// Moves the cursor to indicated column in current row. \e[G is equivalent to \e[1G.
//
// \033[#;#H - Cursor Position (CUP)
-// Moves the cursor to the specified position. The first # specifies the line number,
-// the second # specifies the column. If you do not specify a position, the cursor moves to the home position:
+// Moves the cursor to the specified position. The first # specifies the line number,
+// the second # specifies the column. If you do not specify a position, the cursor moves to the home position:
// the upper-left corner of the screen (line 1, column 1).
//
// \033[#;#f - Horizontal & Vertical Position
// (same as \033[#;#H)
//
// \033[s - Save Cursor Position (SCP)
-// The current cursor position is saved.
+// The current cursor position is saved.
//
// \033[u - Restore cursor position (RCP)
// Restores the cursor position saved with the (SCP) sequence \033[s.
-// (addition, restore to 0,0 if nothinh was saved before)
+// (addition, restore to 0,0 if nothing was saved before)
//
// \033[#J - Erase Display (ED)
@@ -227,7 +213,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
WriteFile(handle, p, (DWORD)(q-p), &written, 0);
if( q[1]!='[' )
- { // write the escape char (whatever purpose it has)
+ { // write the escape char (whatever purpose it has)
if(0==WriteConsole(handle, q, 1, &written, 0) )
WriteFile(handle,q, 1, &written, 0);
p=q+1; //and start searching again
@@ -247,7 +233,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
q=q+2;
for(;;)
{
- if( ISDIGIT(*q) )
+ if( ISDIGIT(*q) )
{ // add number to number array, only accept 2digits, shift out the rest
// so // \033[123456789m will become \033[89m
numbers[numpoint] = (numbers[numpoint]<<4) | (*q-'0');
@@ -296,7 +282,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
}
//case '2': // not existing
//case '3': // blinking (not implemented)
- //case '4': // unterline (not implemented)
+ //case '4': // underline (not implemented)
//case '6': // not existing
//case '8': // concealed (not implemented)
//case '9': // not existing
@@ -364,12 +350,12 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
else if(num==2)
{ // Number of chars on screen.
cnt = info.dwSize.X * info.dwSize.Y;
- SetConsoleCursorPosition(handle, origin);
+ SetConsoleCursorPosition(handle, origin);
}
else// 0 and default
{ // number of chars from cursor to end
origin = info.dwCursorPosition;
- cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X;
+ cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X;
}
FillConsoleOutputAttribute(handle, info.wAttributes, cnt, origin, &tmp);
FillConsoleOutputCharacter(handle, ' ', cnt, origin, &tmp);
@@ -403,7 +389,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
else if( *q == 'H' || *q == 'f' )
{ // \033[#;#H - Cursor Position (CUP)
// \033[#;#f - Horizontal & Vertical Position
- // The first # specifies the line number, the second # specifies the column.
+ // The first # specifies the line number, the second # specifies the column.
// The default for both is 1
info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+((numbers[numpoint]&0x0F)-1):0;
info.dwCursorPosition.Y = (numpoint && numbers[numpoint-1])?(numbers[numpoint-1]>>4)*10+((numbers[numpoint-1]&0x0F)-1):0;
@@ -500,7 +486,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
--q;
}
// skip the sequencer and search again
- p = q+1;
+ p = q+1;
break;
}// end while
}
@@ -512,8 +498,8 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
return 0;
}
-int FPRINTF(HANDLE handle, const char *fmt, ...)
-{
+int FPRINTF(HANDLE handle, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+int FPRINTF(HANDLE handle, const char *fmt, ...) {
int ret;
va_list argptr;
va_start(argptr, fmt);
@@ -556,7 +542,7 @@ int VFPRINTF(FILE *file, const char *fmt, va_list argptr)
{ // find the escape character
fprintf(file, "%.*s", (int)(q-p), p); // write up to the escape
if( q[1]!='[' )
- { // write the escape char (whatever purpose it has)
+ { // write the escape char (whatever purpose it has)
fprintf(file, "%.*s", 1, q);
p=q+1; //and start searching again
}
@@ -569,7 +555,7 @@ int VFPRINTF(FILE *file, const char *fmt, va_list argptr)
q=q+2;
while(1)
{
- if( ISDIGIT(*q) )
+ if( ISDIGIT(*q) )
{
++q;
// and next character
@@ -638,7 +624,7 @@ int VFPRINTF(FILE *file, const char *fmt, va_list argptr)
--q;
}
// skip the sequencer and search again
- p = q+1;
+ p = q+1;
break;
}// end while
}
@@ -648,8 +634,8 @@ int VFPRINTF(FILE *file, const char *fmt, va_list argptr)
FREEBUF(tempbuf);
return 0;
}
-int FPRINTF(FILE *file, const char *fmt, ...)
-{
+int FPRINTF(FILE *file, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+int FPRINTF(FILE *file, const char *fmt, ...) {
int ret;
va_list argptr;
va_start(argptr, fmt);
@@ -666,17 +652,9 @@ int FPRINTF(FILE *file, const char *fmt, ...)
#endif// not _WIN32
-
-
-
-
-
-
-
-
char timestamp_format[20] = ""; //For displaying Timestamps
-int _vShowMessage(enum msg_type flag, const char *string, va_list ap)
+int vShowMessage_(enum msg_type flag, const char *string, va_list ap)
{
va_list apcopy;
char prefix[100];
@@ -685,7 +663,7 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap)
#endif
if (!string || *string == '\0') {
- ShowError("Empty string passed to _vShowMessage().\n");
+ ShowError("Empty string passed to vShowMessage_().\n");
return 1;
}
if(
@@ -756,7 +734,7 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap)
strcat(prefix,CL_RED"[Fatal Error]"CL_RESET":");
break;
default:
- ShowError("In function _vShowMessage() -> Invalid flag passed.\n");
+ ShowError("In function vShowMessage_() -> Invalid flag passed.\n");
return 1;
}
@@ -804,80 +782,89 @@ void ClearScreen(void)
ShowMessage(CL_CLS); // to prevent empty string passed messages
#endif
}
-int _ShowMessage(enum msg_type flag, const char *string, ...)
-{
+int ShowMessage_(enum msg_type flag, const char *string, ...) __attribute__((format(printf, 2, 3)));
+int ShowMessage_(enum msg_type flag, const char *string, ...) {
int ret;
va_list ap;
va_start(ap, string);
- ret = _vShowMessage(flag, string, ap);
+ ret = vShowMessage_(flag, string, ap);
va_end(ap);
return ret;
}
// direct printf replacement
+void ShowMessage(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowMessage(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_NONE, string, ap);
+ vShowMessage_(MSG_NONE, string, ap);
va_end(ap);
}
+void ShowStatus(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowStatus(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_STATUS, string, ap);
+ vShowMessage_(MSG_STATUS, string, ap);
va_end(ap);
}
+void ShowSQL(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowSQL(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_SQL, string, ap);
+ vShowMessage_(MSG_SQL, string, ap);
va_end(ap);
}
+void ShowInfo(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowInfo(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_INFORMATION, string, ap);
+ vShowMessage_(MSG_INFORMATION, string, ap);
va_end(ap);
}
+void ShowNotice(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowNotice(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_NOTICE, string, ap);
+ vShowMessage_(MSG_NOTICE, string, ap);
va_end(ap);
}
+void ShowWarning(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowWarning(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_WARNING, string, ap);
+ vShowMessage_(MSG_WARNING, string, ap);
va_end(ap);
}
-void ShowConfigWarning(config_setting_t *config, const char *string, ...)
-{
+void ShowConfigWarning(config_setting_t *config, const char *string, ...) __attribute__((format(printf, 2, 3)));
+void ShowConfigWarning(config_setting_t *config, const char *string, ...) {
StringBuf buf;
va_list ap;
StrBuf->Init(&buf);
StrBuf->AppendStr(&buf, string);
StrBuf->Printf(&buf, " (%s:%d)\n", config_setting_source_file(config), config_setting_source_line(config));
va_start(ap, string);
- _vShowMessage(MSG_WARNING, StrBuf->Value(&buf), ap);
+ vShowMessage_(MSG_WARNING, StrBuf->Value(&buf), ap);
va_end(ap);
StrBuf->Destroy(&buf);
}
+void ShowDebug(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowDebug(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_DEBUG, string, ap);
+ vShowMessage_(MSG_DEBUG, string, ap);
va_end(ap);
}
+void ShowError(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowError(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_ERROR, string, ap);
+ vShowMessage_(MSG_ERROR, string, ap);
va_end(ap);
}
+void ShowFatalError(const char *string, ...) __attribute__((format(printf, 1, 2)));
void ShowFatalError(const char *string, ...) {
va_list ap;
va_start(ap, string);
- _vShowMessage(MSG_FATALERROR, string, ap);
+ vShowMessage_(MSG_FATALERROR, string, ap);
va_end(ap);
}
diff --git a/src/common/showmsg.h b/src/common/showmsg.h
index a88985770..f3fb50257 100644
--- a/src/common/showmsg.h
+++ b/src/common/showmsg.h
@@ -2,12 +2,19 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _SHOWMSG_H_
-#define _SHOWMSG_H_
+#ifndef COMMON_SHOWMSG_H
+#define COMMON_SHOWMSG_H
-#ifndef _HPMi_H_
- #include "libconfig.h"
+#include <stdarg.h>
+
+#include "../common/cbasetypes.h"
+
+#ifdef HERCULES_CORE
+# include "../../3rdparty/libconfig/libconfig.h"
+#else
+# include "../common/HPMi.h"
#endif
+
// for help with the console colors look here:
// http://www.edoceo.com/liberum/?doc=printf-with-color
// some code explanation (used here):
@@ -67,9 +74,9 @@
#define CL_XXBL "\033[0;44m" // default on blue
#define CL_PASS "\033[0;32;42m" // green on green
-#define CL_SPACE " " // space aquivalent of the print messages
+#define CL_SPACE " " // space equivalent of the print messages
-extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs]
+extern int stdout_with_ansisequence; //If the color ANSI sequences are to be used. [flaviojs]
extern int msg_silent; //Specifies how silent the console is. [Skotlex]
extern int console_msg_log; //Specifies what error messages to log. [Ind]
extern char timestamp_format[20]; //For displaying Timestamps [Skotlex]
@@ -87,17 +94,29 @@ enum msg_type {
};
extern void ClearScreen(void);
-#ifndef _HPMi_H_
- extern void ShowMessage(const char *, ...);
- extern void ShowStatus(const char *, ...);
- extern void ShowSQL(const char *, ...);
- extern void ShowInfo(const char *, ...);
- extern void ShowNotice(const char *, ...);
- extern void ShowWarning(const char *, ...);
- extern void ShowDebug(const char *, ...);
- extern void ShowError(const char *, ...);
- extern void ShowFatalError(const char *, ...);
- extern void ShowConfigWarning(config_setting_t *config, const char *string, ...);
+#ifdef HERCULES_CORE
+ extern void ShowMessage(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowStatus(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowSQL(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowInfo(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowNotice(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowWarning(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowDebug(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowError(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowFatalError(const char *, ...) __attribute__((format(printf, 1, 2)));
+ extern void ShowConfigWarning(config_setting_t *config, const char *string, ...) __attribute__((format(printf, 2, 3)));
+#else
+ HPExport void (*ShowMessage) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowStatus) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowSQL) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowInfo) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowNotice) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowWarning) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowDebug) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowError) (const char *, ...) __attribute__((format(printf, 1, 2)));
+ HPExport void (*ShowFatalError) (const char *, ...) __attribute__((format(printf, 1, 2)));
#endif
-#endif /* _SHOWMSG_H_ */
+extern int vShowMessage_(enum msg_type flag, const char *string, va_list ap);
+
+#endif /* COMMON_SHOWMSG_H */
diff --git a/src/common/socket.c b/src/common/socket.c
index 15b20b16f..0c48c7c46 100644
--- a/src/common/socket.c
+++ b/src/common/socket.c
@@ -2,49 +2,69 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/cbasetypes.h"
-#include "../common/mmo.h"
-#include "../common/timer.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/strlib.h"
+#define HERCULES_CORE
+
+#include "../config/core.h" // SHOW_SERVER_STATS
+#define H_SOCKET_C
#include "socket.h"
+#undef H_SOCKET_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include "../common/HPM.h"
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/timer.h"
+
#ifdef WIN32
- #include "../common/winapi.h"
+# include "../common/winapi.h"
#else
- #include <errno.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <net/if.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
- #include <netdb.h>
- #include <arpa/inet.h>
-
- #ifndef SIOCGIFCONF
- #include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori]
- #endif
- #ifndef FIONBIO
- #include <sys/filio.h> // FIONBIO on Solaris [FlavioJS]
- #endif
-
- #ifdef HAVE_SETRLIMIT
- #include <sys/resource.h>
- #endif
+# include <arpa/inet.h>
+# include <errno.h>
+# include <net/if.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <sys/ioctl.h>
+# include <sys/socket.h>
+# include <sys/time.h>
+# include <unistd.h>
+
+# ifndef SIOCGIFCONF
+# include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori]
+# endif
+# ifndef FIONBIO
+# include <sys/filio.h> // FIONBIO on Solaris [FlavioJS]
+# endif
+
+# ifdef HAVE_SETRLIMIT
+# include <sys/resource.h>
+# endif
+#endif
+
+/**
+ * Socket Interface Source
+ **/
+struct socket_interface sockt_s;
+
+#ifdef SEND_SHORTLIST
+ // Add a fd to the shortlist so that it'll be recognized as a fd that needs
+ // sending done on it.
+ void send_shortlist_add_fd(int fd);
+ // Do pending network sends (and eof handling) from the shortlist.
+ void send_shortlist_do_sends();
#endif
/////////////////////////////////////////////////////////////////////
#if defined(WIN32)
/////////////////////////////////////////////////////////////////////
-// windows portability layer
+// windows portability layer
typedef int socklen_t;
@@ -90,7 +110,7 @@ int sock2fd(SOCKET s)
/// Inserts the socket into the global array of sockets.
/// Returns a new fd associated with the socket.
-/// If there are too many sockets it closes the socket, sets an error and
+/// If there are too many sockets it closes the socket, sets an error and
// returns -1 instead.
/// Since fd 0 is reserved, it returns values in the range [1,FD_SETSIZE[.
///
@@ -155,19 +175,19 @@ char* sErr(int code)
return sbuf;
}
-#define sBind(fd,name,namelen) bind(fd2sock(fd),name,namelen)
-#define sConnect(fd,name,namelen) connect(fd2sock(fd),name,namelen)
-#define sIoctl(fd,cmd,argp) ioctlsocket(fd2sock(fd),cmd,argp)
-#define sListen(fd,backlog) listen(fd2sock(fd),backlog)
-#define sRecv(fd,buf,len,flags) recv(fd2sock(fd),buf,len,flags)
-#define sSelect select
-#define sSend(fd,buf,len,flags) send(fd2sock(fd),buf,len,flags)
-#define sSetsockopt(fd,level,optname,optval,optlen) setsockopt(fd2sock(fd),level,optname,optval,optlen)
-#define sShutdown(fd,how) shutdown(fd2sock(fd),how)
-#define sFD_SET(fd,set) FD_SET(fd2sock(fd),set)
-#define sFD_CLR(fd,set) FD_CLR(fd2sock(fd),set)
-#define sFD_ISSET(fd,set) FD_ISSET(fd2sock(fd),set)
-#define sFD_ZERO FD_ZERO
+#define sBind(fd,name,namelen) bind(fd2sock(fd),(name),(namelen))
+#define sConnect(fd,name,namelen) connect(fd2sock(fd),(name),(namelen))
+#define sIoctl(fd,cmd,argp) ioctlsocket(fd2sock(fd),(cmd),(argp))
+#define sListen(fd,backlog) listen(fd2sock(fd),(backlog))
+#define sRecv(fd,buf,len,flags) recv(fd2sock(fd),(buf),(len),(flags))
+#define sSelect select
+#define sSend(fd,buf,len,flags) send(fd2sock(fd),(buf),(len),(flags))
+#define sSetsockopt(fd,level,optname,optval,optlen) setsockopt(fd2sock(fd),(level),(optname),(optval),(optlen))
+#define sShutdown(fd,how) shutdown(fd2sock(fd),(how))
+#define sFD_SET(fd,set) FD_SET(fd2sock(fd),(set))
+#define sFD_CLR(fd,set) FD_CLR(fd2sock(fd),(set))
+#define sFD_ISSET(fd,set) FD_ISSET(fd2sock(fd),(set))
+#define sFD_ZERO FD_ZERO
/////////////////////////////////////////////////////////////////////
#else
@@ -210,17 +230,18 @@ char* sErr(int code)
#endif
fd_set readfds;
-int fd_max;
-time_t last_tick;
-time_t stall_time = 60;
-
-uint32 addr_[16]; // ip addresses of local host (host byte order)
-int naddr_ = 0; // # of ip addresses
// Maximum packet size in bytes, which the client is able to handle.
// Larger packets cause a buffer overflow and stack corruption.
static size_t socket_max_client_packet = 24576;
+#ifdef SHOW_SERVER_STATS
+// Data I/O statistics
+static size_t socket_data_i = 0, socket_data_ci = 0, socket_data_qi = 0;
+static size_t socket_data_o = 0, socket_data_co = 0, socket_data_qo = 0;
+static time_t socket_data_last_tick = 0;
+#endif
+
// initial recv buffer size (this will also be the max. size)
// biggest known packet: S 0153 <len>.w <emblem data>.?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes)
#define RFIFO_SIZE (2*1024)
@@ -231,8 +252,6 @@ static size_t socket_max_client_packet = 24576;
// The connection is closed if it goes over the limit.
#define WFIFO_MAX (1*1024*1024)
-struct socket_data* session[FD_SETSIZE];
-
#ifdef SEND_SHORTLIST
int send_shortlist_array[FD_SETSIZE];// we only support FD_SETSIZE sockets, limit the array to that
int send_shortlist_count = 0;// how many fd's are in the shortlist
@@ -274,8 +293,8 @@ void set_defaultparse(ParseFunc defaultparse)
*--------------------------------------*/
void set_nonblocking(int fd, unsigned long yes)
{
- // FIONBIO Use with a nonzero argp parameter to enable the nonblocking mode of socket s.
- // The argp parameter is zero if nonblocking is to be disabled.
+ // FIONBIO Use with a nonzero argp parameter to enable the nonblocking mode of socket s.
+ // The argp parameter is zero if nonblocking is to be disabled.
if( sIoctl(fd, FIONBIO, &yes) != 0 )
ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (%s) - Please report this!!!\n", fd, error_msg());
}
@@ -321,7 +340,7 @@ void setsocketopts(int fd, struct hSockOpt *opt) {
*--------------------------------------*/
void set_eof(int fd)
{
- if( session_isActive(fd) )
+ if( sockt->session_isActive(fd) )
{
#ifdef SEND_SHORTLIST
// Add this socket to the shortlist for eof handling.
@@ -333,15 +352,15 @@ void set_eof(int fd)
int recv_to_fifo(int fd)
{
- int len;
+ ssize_t len;
- if( !session_isActive(fd) )
+ if( !sockt->session_isActive(fd) )
return -1;
len = sRecv(fd, (char *) session[fd]->rdata + session[fd]->rdata_size, (int)RFIFOSPACE(fd), 0);
if( len == SOCKET_ERROR )
- {//An exception has occured
+ {//An exception has occurred
if( sErrno != S_EWOULDBLOCK ) {
//ShowDebug("recv_to_fifo: %s, closing connection #%d\n", error_msg(), fd);
set_eof(fd);
@@ -356,15 +375,23 @@ int recv_to_fifo(int fd)
}
session[fd]->rdata_size += len;
- session[fd]->rdata_tick = last_tick;
+ session[fd]->rdata_tick = sockt->last_tick;
+#ifdef SHOW_SERVER_STATS
+ socket_data_i += len;
+ socket_data_qi += len;
+ if (!session[fd]->flag.server)
+ {
+ socket_data_ci += len;
+ }
+#endif
return 0;
}
int send_from_fifo(int fd)
{
- int len;
+ ssize_t len;
- if( !session_isValid(fd) )
+ if( !sockt->session_isValid(fd) )
return -1;
if( session[fd]->wdata_size == 0 )
@@ -373,9 +400,12 @@ int send_from_fifo(int fd)
len = sSend(fd, (const char *) session[fd]->wdata, (int)session[fd]->wdata_size, MSG_NOSIGNAL);
if( len == SOCKET_ERROR )
- {//An exception has occured
+ {//An exception has occurred
if( sErrno != S_EWOULDBLOCK ) {
//ShowDebug("send_from_fifo: %s, ending connection #%d\n", error_msg(), fd);
+#ifdef SHOW_SERVER_STATS
+ socket_data_qo -= session[fd]->wdata_size;
+#endif
session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex]
set_eof(fd);
}
@@ -390,6 +420,14 @@ int send_from_fifo(int fd)
memmove(session[fd]->wdata, session[fd]->wdata + len, session[fd]->wdata_size - len);
session[fd]->wdata_size -= len;
+#ifdef SHOW_SERVER_STATS
+ socket_data_o += len;
+ socket_data_qo -= len;
+ if (!session[fd]->flag.server)
+ {
+ socket_data_co += len;
+ }
+#endif
}
return 0;
@@ -405,8 +443,8 @@ void flush_fifo(int fd)
void flush_fifos(void)
{
int i;
- for(i = 1; i < fd_max; i++)
- flush_fifo(i);
+ for(i = 1; i < sockt->fd_max; i++)
+ sockt->flush_fifo(i);
}
/*======================================
@@ -440,12 +478,12 @@ int connect_client(int listen_fd) {
#ifndef MINICORE
if( ip_rules && !connect_check(ntohl(client_address.sin_addr.s_addr)) ) {
- do_close(fd);
+ sockt->close(fd);
return -1;
}
#endif
- if( fd_max <= fd ) fd_max = fd + 1;
+ if( sockt->fd_max <= fd ) sockt->fd_max = fd + 1;
sFD_SET(fd,&readfds);
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse);
@@ -495,7 +533,7 @@ int make_listen_bind(uint32 ip, uint16 port)
exit(EXIT_FAILURE);
}
- if(fd_max <= fd) fd_max = fd + 1;
+ if(sockt->fd_max <= fd) sockt->fd_max = fd + 1;
sFD_SET(fd, &readfds);
create_session(fd, connect_client, null_send, null_parse);
@@ -540,13 +578,13 @@ int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) {
if( result == SOCKET_ERROR ) {
if( !( opt && opt->silent ) )
ShowError("make_connection: connect failed (socket #%d, %s)!\n", fd, error_msg());
- do_close(fd);
+ sockt->close(fd);
return -1;
}
//Now the socket can be made non-blocking. [Skotlex]
set_nonblocking(fd, 1);
- if (fd_max <= fd) fd_max = fd + 1;
+ if (sockt->fd_max <= fd) sockt->fd_max = fd + 1;
sFD_SET(fd,&readfds);
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse);
@@ -565,17 +603,33 @@ static int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseF
session[fd]->func_recv = func_recv;
session[fd]->func_send = func_send;
session[fd]->func_parse = func_parse;
- session[fd]->rdata_tick = last_tick;
+ session[fd]->rdata_tick = sockt->last_tick;
+ session[fd]->session_data = NULL;
+ session[fd]->hdata = NULL;
+ session[fd]->hdatac = 0;
return 0;
}
static void delete_session(int fd)
{
- if( session_isValid(fd) )
- {
+ if( sockt->session_isValid(fd) ) {
+ unsigned int i;
+#ifdef SHOW_SERVER_STATS
+ socket_data_qi -= session[fd]->rdata_size - session[fd]->rdata_pos;
+ socket_data_qo -= session[fd]->wdata_size;
+#endif
aFree(session[fd]->rdata);
aFree(session[fd]->wdata);
- aFree(session[fd]->session_data);
+ if( session[fd]->session_data )
+ aFree(session[fd]->session_data);
+ for(i = 0; i < session[fd]->hdatac; i++) {
+ if( session[fd]->hdata[i]->flag.free ) {
+ aFree(session[fd]->hdata[i]->data);
+ }
+ aFree(session[fd]->hdata[i]);
+ }
+ if( session[fd]->hdata )
+ aFree(session[fd]->hdata);
aFree(session[fd]);
session[fd] = NULL;
}
@@ -583,7 +637,7 @@ static void delete_session(int fd)
int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size)
{
- if( !session_isValid(fd) )
+ if( !sockt->session_isValid(fd) )
return 0;
if( session[fd]->max_rdata != rfifo_size && session[fd]->rdata_size < rfifo_size) {
@@ -602,7 +656,7 @@ int realloc_writefifo(int fd, size_t addition)
{
size_t newsize;
- if( !session_isValid(fd) ) // might not happen
+ if( !sockt->session_isValid(fd) ) // might not happen
return 0;
if( session[fd]->wdata_size + addition > session[fd]->max_wdata )
@@ -630,17 +684,20 @@ int RFIFOSKIP(int fd, size_t len)
{
struct socket_data *s;
- if ( !session_isActive(fd) )
+ if ( !sockt->session_isActive(fd) )
return 0;
s = session[fd];
- if ( s->rdata_size < s->rdata_pos + len ) {
- ShowError("RFIFOSKIP: skipped past end of read buffer! Adjusting from %d to %d (session #%d)\n", len, RFIFOREST(fd), fd);
+ if (s->rdata_size < s->rdata_pos + len) {
+ ShowError("RFIFOSKIP: skipped past end of read buffer! Adjusting from %"PRIuS" to %"PRIuS" (session #%d)\n", len, RFIFOREST(fd), fd);
len = RFIFOREST(fd);
}
s->rdata_pos = s->rdata_pos + len;
+#ifdef SHOW_SERVER_STATS
+ socket_data_qi -= len;
+#endif
return 0;
}
@@ -650,7 +707,7 @@ int WFIFOSET(int fd, size_t len)
size_t newreserve;
struct socket_data* s = session[fd];
- if( !session_isValid(fd) || s->wdata == NULL )
+ if( !sockt->session_isValid(fd) || s->wdata == NULL )
return 0;
// we have written len bytes to the buffer already before calling WFIFOSET
@@ -681,19 +738,24 @@ int WFIFOSET(int fd, size_t len)
if( !s->flag.server ) {
- if( len > socket_max_client_packet ) {// see declaration of socket_max_client_packet for details
- ShowError("WFIFOSET: Dropped too large client packet 0x%04x (length=%u, max=%u).\n", WFIFOW(fd,0), len, socket_max_client_packet);
+ if (len > socket_max_client_packet) { // see declaration of socket_max_client_packet for details
+ ShowError("WFIFOSET: Dropped too large client packet 0x%04x (length=%"PRIuS", max=%"PRIuS").\n",
+ WFIFOW(fd,0), len, socket_max_client_packet);
return 0;
}
- if( s->wdata_size+len > WFIFO_MAX ) {// reached maximum write fifo size
- ShowError("WFIFOSET: Maximum write buffer size for client connection %d exceeded, most likely caused by packet 0x%04x (len=%u, ip=%lu.%lu.%lu.%lu).\n", fd, WFIFOW(fd,0), len, CONVIP(s->client_addr));
+ if (s->wdata_size+len > WFIFO_MAX) { // reached maximum write fifo size
+ ShowError("WFIFOSET: Maximum write buffer size for client connection %d exceeded, most likely caused by packet 0x%04x (len=%"PRIuS", ip=%u.%u.%u.%u).\n",
+ fd, WFIFOW(fd,0), len, CONVIP(s->client_addr));
set_eof(fd);
return 0;
}
}
s->wdata_size += len;
+#ifdef SHOW_SERVER_STATS
+ socket_data_qo += len;
+#endif
//If the interserver has 200% of its normal size full, flush the data.
if( s->flag.server && s->wdata_size >= 2*FIFOSIZE_SERVERLINK )
flush_fifo(fd);
@@ -723,7 +785,7 @@ int do_sockets(int next)
#ifdef SEND_SHORTLIST
send_shortlist_do_sends();
#else
- for (i = 1; i < fd_max; i++)
+ for (i = 1; i < sockt->fd_max; i++)
{
if(!session[i])
continue;
@@ -738,7 +800,7 @@ int do_sockets(int next)
timeout.tv_usec = next%1000*1000;
memcpy(&rfd, &readfds, sizeof(rfd));
- ret = sSelect(fd_max, &rfd, NULL, NULL, &timeout);
+ ret = sSelect(sockt->fd_max, &rfd, NULL, NULL, &timeout);
if( ret == SOCKET_ERROR )
{
@@ -750,7 +812,7 @@ int do_sockets(int next)
return 0; // interrupted by a signal, just loop and try again
}
- last_tick = time(NULL);
+ sockt->last_tick = time(NULL);
#if defined(WIN32)
// on windows, enumerating all members of the fd_set is way faster if we access the internals
@@ -762,7 +824,7 @@ int do_sockets(int next)
}
#else
// otherwise assume that the fd_set is a bit-array and enumerate it in a standard way
- for( i = 1; ret && i < fd_max; ++i )
+ for( i = 1; ret && i < sockt->fd_max; ++i )
{
if(sFD_ISSET(i,&rfd) && session[i])
{
@@ -776,7 +838,7 @@ int do_sockets(int next)
#ifdef SEND_SHORTLIST
send_shortlist_do_sends();
#else
- for (i = 1; i < fd_max; i++)
+ for (i = 1; i < sockt->fd_max; i++)
{
if(!session[i])
continue;
@@ -785,19 +847,19 @@ int do_sockets(int next)
session[i]->func_send(i);
if(session[i]->flag.eof) //func_send can't free a session, this is safe.
- { //Finally, even if there is no data to parse, connections signalled eof should be closed, so we call parse_func [Skotlex]
+ { //Finally, even if there is no data to parse, connections signaled eof should be closed, so we call parse_func [Skotlex]
session[i]->func_parse(i); //This should close the session immediately.
}
}
#endif
// parse input data on each socket
- for(i = 1; i < fd_max; i++)
+ for(i = 1; i < sockt->fd_max; i++)
{
if(!session[i])
continue;
- if (session[i]->rdata_tick && DIFF_TICK(last_tick, session[i]->rdata_tick) > stall_time) {
+ if (session[i]->rdata_tick && DIFF_TICK(sockt->last_tick, session[i]->rdata_tick) > sockt->stall_time) {
if( session[i]->flag.server ) {/* server is special */
if( session[i]->flag.ping != 2 )/* only update if necessary otherwise it'd resend the ping unnecessarily */
session[i]->flag.ping = 1;
@@ -807,19 +869,40 @@ int do_sockets(int next)
}
}
+#ifdef __clang_analyzer__
+ // Let Clang's static analyzer know this never happens (it thinks it might because of a NULL check in session_isValid)
+ if (!session[i]) continue;
+#endif // __clang_analyzer__
session[i]->func_parse(i);
if(!session[i])
continue;
-
+
+ RFIFOFLUSH(i);
// after parse, check client's RFIFO size to know if there is an invalid packet (too big and not parsed)
if (session[i]->rdata_size == session[i]->max_rdata) {
set_eof(i);
continue;
}
- RFIFOFLUSH(i);
}
+#ifdef SHOW_SERVER_STATS
+ if (sockt->last_tick != socket_data_last_tick)
+ {
+ char buf[1024];
+
+ sprintf(buf, "In: %.03f kB/s (%.03f kB/s, Q: %.03f kB) | Out: %.03f kB/s (%.03f kB/s, Q: %.03f kB) | RAM: %.03f MB", socket_data_i/1024., socket_data_ci/1024., socket_data_qi/1024., socket_data_o/1024., socket_data_co/1024., socket_data_qo/1024., iMalloc->usage()/1024.);
+#ifdef _WIN32
+ SetConsoleTitle(buf);
+#else
+ ShowMessage("\033[s\033[1;1H\033[2K%s\033[u", buf);
+#endif
+ socket_data_last_tick = sockt->last_tick;
+ socket_data_i = socket_data_ci = 0;
+ socket_data_o = socket_data_co = 0;
+ }
+#endif
+
return 0;
}
@@ -828,20 +911,20 @@ int do_sockets(int next)
//////////////////////////////
// IP rules and DDoS protection
-typedef struct _connect_history {
- struct _connect_history* next;
+typedef struct connect_history {
+ struct connect_history* next;
uint32 ip;
- uint32 tick;
+ int64 tick;
int count;
unsigned ddos : 1;
} ConnectHistory;
-typedef struct _access_control {
+typedef struct access_control {
uint32 ip;
uint32 mask;
} AccessControl;
-enum _aco {
+enum aco {
ACO_DENY_ALLOW,
ACO_ALLOW_DENY,
ACO_MUTUAL_FAILURE
@@ -947,9 +1030,9 @@ static int connect_check_(uint32 ip)
if( hist->ddos )
{// flagged as DDoS
return (connect_ok == 2 ? 1 : 0);
- } else if( DIFF_TICK(iTimer->gettick(),hist->tick) < ddos_interval )
+ } else if( DIFF_TICK(timer->gettick(),hist->tick) < ddos_interval )
{// connection within ddos_interval
- hist->tick = iTimer->gettick();
+ hist->tick = timer->gettick();
if( hist->count++ >= ddos_count )
{// DDoS attack detected
hist->ddos = 1;
@@ -959,7 +1042,7 @@ static int connect_check_(uint32 ip)
return connect_ok;
} else
{// not within ddos_interval, clear data
- hist->tick = iTimer->gettick();
+ hist->tick = timer->gettick();
hist->count = 0;
return connect_ok;
}
@@ -970,7 +1053,7 @@ static int connect_check_(uint32 ip)
CREATE(hist, ConnectHistory, 1);
memset(hist, 0, sizeof(ConnectHistory));
hist->ip = ip;
- hist->tick = iTimer->gettick();
+ hist->tick = timer->gettick();
hist->next = connect_history[ip&0xFFFF];
connect_history[ip&0xFFFF] = hist;
return connect_ok;
@@ -978,8 +1061,7 @@ static int connect_check_(uint32 ip)
/// Timer function.
/// Deletes old connection history records.
-static int connect_check_clear(int tid, unsigned int tick, int id, intptr_t data)
-{
+static int connect_check_clear(int tid, int64 tick, int id, intptr_t data) {
int i;
int clear = 0;
int list = 0;
@@ -1072,17 +1154,16 @@ int socket_config_read(const char* cfgName)
return 1;
}
- while(fgets(line, sizeof(line), fp))
- {
+ while (fgets(line, sizeof(line), fp)) {
if(line[0] == '/' && line[1] == '/')
continue;
- if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
+ if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
continue;
if (!strcmpi(w1, "stall_time")) {
- stall_time = atoi(w2);
- if( stall_time < 3 )
- stall_time = 3;/* a minimum is required to refrain it from killing itself */
+ sockt->stall_time = atoi(w2);
+ if( sockt->stall_time < 3 )
+ sockt->stall_time = 3;/* a minimum is required to refrain it from killing itself */
}
#ifndef MINICORE
else if (!strcmpi(w1, "enable_ip_rules")) {
@@ -1150,14 +1231,16 @@ void socket_final(void)
aFree(access_deny);
#endif
- for( i = 1; i < fd_max; i++ )
+ for( i = 1; i < sockt->fd_max; i++ )
if(session[i])
- do_close(i);
+ sockt->close(i);
- // session[0] ‚̃_ƒ~[ƒf[ƒ^‚ðíœ
+ // session[0]
aFree(session[0]->rdata);
aFree(session[0]->wdata);
aFree(session[0]);
+
+ aFree(session);
}
/// Closes a socket.
@@ -1264,7 +1347,7 @@ int socket_getips(uint32* ips, int max)
void socket_init(void)
{
char *SOCKET_CONF_FILENAME = "conf/packet.conf";
- unsigned int rlim_cur = FD_SETSIZE;
+ uint64 rlim_cur = FD_SETSIZE;
#ifdef WIN32
{// Start up windows networking
@@ -1282,7 +1365,7 @@ void socket_init(void)
}
}
#elif defined(HAVE_SETRLIMIT) && !defined(CYGWIN)
- // NOTE: getrlimit and setrlimit have bogus behaviour in cygwin.
+ // NOTE: getrlimit and setrlimit have bogus behavior in cygwin.
// "Number of fds is virtually unlimited in cygwin" (sys/param.h)
{// set socket limit to FD_SETSIZE
struct rlimit rlp;
@@ -1312,17 +1395,19 @@ void socket_init(void)
#endif
// Get initial local ips
- naddr_ = socket_getips(addr_,16);
+ sockt->naddr_ = socket_getips(sockt->addr_,16);
sFD_ZERO(&readfds);
#if defined(SEND_SHORTLIST)
memset(send_shortlist_set, 0, sizeof(send_shortlist_set));
#endif
+ CREATE(session, struct socket_data *, FD_SETSIZE);
+
socket_config_read(SOCKET_CONF_FILENAME);
- // initialise last send-receive tick
- last_tick = time(NULL);
+ // initialize last send-receive tick
+ sockt->last_tick = time(NULL);
// session[0] is now currently used for disconnected sessions of the map server, and as such,
// should hold enough buffer (it is a vacuum so to speak) as it is never flushed. [Skotlex]
@@ -1331,11 +1416,14 @@ void socket_init(void)
#ifndef MINICORE
// Delete old connection history every 5 minutes
memset(connect_history, 0, sizeof(connect_history));
- iTimer->add_timer_func_list(connect_check_clear, "connect_check_clear");
- iTimer->add_timer_interval(iTimer->gettick()+1000, connect_check_clear, 0, 0, 5*60*1000);
+ timer->add_func_list(connect_check_clear, "connect_check_clear");
+ timer->add_interval(timer->gettick()+1000, connect_check_clear, 0, 0, 5*60*1000);
#endif
- ShowInfo("Server supports up to '"CL_WHITE"%u"CL_RESET"' concurrent connections.\n", rlim_cur);
+ ShowInfo("Server supports up to '"CL_WHITE"%"PRId64""CL_RESET"' concurrent connections.\n", rlim_cur);
+
+ /* Hercules Plugin Manager */
+ HPM->share(session,"session");
}
bool session_isValid(int fd)
@@ -1345,7 +1433,7 @@ bool session_isValid(int fd)
bool session_isActive(int fd)
{
- return ( session_isValid(fd) && !session[fd]->flag.eof );
+ return ( sockt->session_isValid(fd) && !session[fd]->flag.eof );
}
// Resolves hostname into a numeric ip.
@@ -1371,7 +1459,7 @@ uint32 str2ip(const char* ip_str)
}
// Reorders bytes from network to little endian (Windows).
-// Neccessary for sending port numbers to the RO client until Gravity notices that they forgot ntohs() calls.
+// Necessary for sending port numbers to the RO client until Gravity notices that they forgot ntohs() calls.
uint16 ntows(uint16 netshort)
{
return ((netshort & 0xFF) << 8) | ((netshort & 0xFF00) >> 8);
@@ -1387,8 +1475,6 @@ void socket_datasync(int fd, bool send) {
{ sizeof(struct item) },
{ sizeof(struct point) },
{ sizeof(struct s_skill) },
- { sizeof(struct global_reg) },
- { sizeof(struct accreg) },
{ sizeof(struct status_change_data) },
{ sizeof(struct storage_data) },
{ sizeof(struct guild_storage) },
@@ -1399,7 +1485,6 @@ void socket_datasync(int fd, bool send) {
{ sizeof(struct s_friend) },
{ sizeof(struct mail_message) },
{ sizeof(struct mail_data) },
- { sizeof(struct registry) },
{ sizeof(struct party_member) },
{ sizeof(struct party) },
{ sizeof(struct guild_member) },
@@ -1410,6 +1495,7 @@ void socket_datasync(int fd, bool send) {
{ sizeof(struct guild) },
{ sizeof(struct guild_castle) },
{ sizeof(struct fame_list) },
+ { PACKETVER },
};
unsigned short i;
unsigned int alen = ARRAYLENGTH(data_list);
@@ -1451,7 +1537,7 @@ void send_shortlist_add_fd(int fd)
int i;
int bit;
- if( !session_isValid(fd) )
+ if( !sockt->session_isValid(fd) )
return;// out of range
i = fd/32;
@@ -1460,9 +1546,9 @@ void send_shortlist_add_fd(int fd)
if( (send_shortlist_set[i]>>bit)&1 )
return;// already in the list
- if( send_shortlist_count >= ARRAYLENGTH(send_shortlist_array) )
- {
- ShowDebug("send_shortlist_add_fd: shortlist is full, ignoring... (fd=%d shortlist.count=%d shortlist.length=%d)\n", fd, send_shortlist_count, ARRAYLENGTH(send_shortlist_array));
+ if (send_shortlist_count >= ARRAYLENGTH(send_shortlist_array)) {
+ ShowDebug("send_shortlist_add_fd: shortlist is full, ignoring... (fd=%d shortlist.count=%d shortlist.length=%"PRIuS")\n",
+ fd, send_shortlist_count, ARRAYLENGTH(send_shortlist_array));
return;
}
@@ -1520,3 +1606,44 @@ void send_shortlist_do_sends()
}
}
#endif
+
+void socket_defaults(void) {
+ sockt = &sockt_s;
+
+ sockt->fd_max = 0;
+ /* */
+ sockt->stall_time = 60;
+ sockt->last_tick = 0;
+ /* */
+ memset(&sockt->addr_, 0, sizeof(sockt->addr_));
+ sockt->naddr_ = 0;
+ /* */
+ sockt->init = socket_init;
+ sockt->final = socket_final;
+ /* */
+ sockt->perform = do_sockets;
+ /* */
+ sockt->datasync = socket_datasync;
+ /* */
+ sockt->make_listen_bind = make_listen_bind;
+ sockt->make_connection = make_connection;
+ sockt->realloc_fifo = realloc_fifo;
+ sockt->realloc_writefifo = realloc_writefifo;
+ sockt->WFIFOSET = WFIFOSET;
+ sockt->RFIFOSKIP = RFIFOSKIP;
+ sockt->close = do_close;
+ /* */
+ sockt->session_isValid = session_isValid;
+ sockt->session_isActive = session_isActive;
+ /* */
+ sockt->flush_fifo = flush_fifo;
+ sockt->flush_fifos = flush_fifos;
+ sockt->set_nonblocking = set_nonblocking;
+ sockt->set_defaultparse = set_defaultparse;
+ sockt->host2ip = host2ip;
+ sockt->ip2str = ip2str;
+ sockt->str2ip = str2ip;
+ sockt->ntows = ntows;
+ sockt->getips = socket_getips;
+ sockt->set_eof = set_eof;
+}
diff --git a/src/common/socket.h b/src/common/socket.h
index b58cbdccf..42b0efe3b 100644
--- a/src/common/socket.h
+++ b/src/common/socket.h
@@ -2,38 +2,40 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _SOCKET_H_
-#define _SOCKET_H_
+#ifndef COMMON_SOCKET_H
+#define COMMON_SOCKET_H
+
+#include <time.h>
#include "../common/cbasetypes.h"
#ifdef WIN32
- #include "../common/winapi.h"
+# include "../common/winapi.h"
typedef long in_addr_t;
#else
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+# include <sys/types.h>
#endif
-#include <time.h>
+struct HPluginData;
#define FIFOSIZE_SERVERLINK 256*1024
// socket I/O macros
#define RFIFOHEAD(fd)
-#define WFIFOHEAD(fd, size) do{ if((fd) && session[fd]->wdata_size + (size) > session[fd]->max_wdata ) realloc_writefifo(fd, size); }while(0)
+#define WFIFOHEAD(fd, size) do{ if((fd) && session[fd]->wdata_size + (size) > session[fd]->max_wdata ) realloc_writefifo((fd), (size)); }while(0)
#define RFIFOP(fd,pos) (session[fd]->rdata + session[fd]->rdata_pos + (pos))
#define WFIFOP(fd,pos) (session[fd]->wdata + session[fd]->wdata_size + (pos))
-#define RFIFOB(fd,pos) (*(uint8*)RFIFOP(fd,pos))
-#define WFIFOB(fd,pos) (*(uint8*)WFIFOP(fd,pos))
-#define RFIFOW(fd,pos) (*(uint16*)RFIFOP(fd,pos))
-#define WFIFOW(fd,pos) (*(uint16*)WFIFOP(fd,pos))
-#define RFIFOL(fd,pos) (*(uint32*)RFIFOP(fd,pos))
-#define WFIFOL(fd,pos) (*(uint32*)WFIFOP(fd,pos))
-#define RFIFOQ(fd,pos) (*(uint64*)RFIFOP(fd,pos))
-#define WFIFOQ(fd,pos) (*(uint64*)WFIFOP(fd,pos))
+#define RFIFOB(fd,pos) (*(uint8*)RFIFOP((fd),(pos)))
+#define WFIFOB(fd,pos) (*(uint8*)WFIFOP((fd),(pos)))
+#define RFIFOW(fd,pos) (*(uint16*)RFIFOP((fd),(pos)))
+#define WFIFOW(fd,pos) (*(uint16*)WFIFOP((fd),(pos)))
+#define RFIFOL(fd,pos) (*(uint32*)RFIFOP((fd),(pos)))
+#define WFIFOL(fd,pos) (*(uint32*)WFIFOP((fd),(pos)))
+#define RFIFOQ(fd,pos) (*(uint64*)RFIFOP((fd),(pos)))
+#define WFIFOQ(fd,pos) (*(uint64*)WFIFOP((fd),(pos)))
#define RFIFOSPACE(fd) (session[fd]->max_rdata - session[fd]->rdata_size)
#define WFIFOSPACE(fd) (session[fd]->max_wdata - session[fd]->wdata_size)
@@ -50,7 +52,7 @@
} while(0)
/* [Ind/Hercules] */
-#define RFIFO2PTR(fd,len) (void*)(session[fd]->rdata + len)
+#define RFIFO2PTR(fd) (void*)(session[fd]->rdata + session[fd]->rdata_pos)
// buffer I/O macros
#define RBUFP(p,pos) (((uint8*)(p)) + (pos))
@@ -75,8 +77,7 @@ typedef int (*RecvFunc)(int fd);
typedef int (*SendFunc)(int fd);
typedef int (*ParseFunc)(int fd);
-struct socket_data
-{
+struct socket_data {
struct {
unsigned char eof : 1;
unsigned char server : 1;
@@ -96,6 +97,9 @@ struct socket_data
ParseFunc func_parse;
void* session_data; // stores application-specific data related to the session
+
+ struct HPluginData **hdata;
+ unsigned int hdatac;
};
struct hSockOpt {
@@ -103,72 +107,93 @@ struct hSockOpt {
unsigned int setTimeo : 1;
};
-// Data prototype declaration
-
-extern struct socket_data* session[FD_SETSIZE];
-
-extern int fd_max;
-
-extern time_t last_tick;
-extern time_t stall_time;
-
-//////////////////////////////////
-// some checking on sockets
-extern bool session_isValid(int fd);
-extern bool session_isActive(int fd);
-//////////////////////////////////
-
-// Function prototype declaration
-
-int make_listen_bind(uint32 ip, uint16 port);
-int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt);
-int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size);
-int realloc_writefifo(int fd, size_t addition);
-int WFIFOSET(int fd, size_t len);
-int RFIFOSKIP(int fd, size_t len);
-
-int do_sockets(int next);
-void do_close(int fd);
-void socket_init(void);
-void socket_final(void);
-
-extern void flush_fifo(int fd);
-extern void flush_fifos(void);
-extern void set_nonblocking(int fd, unsigned long yes);
-
-void set_defaultparse(ParseFunc defaultparse);
-
-// hostname/ip conversion functions
-uint32 host2ip(const char* hostname);
-const char* ip2str(uint32 ip, char ip_str[16]);
-uint32 str2ip(const char* ip_str);
-#define CONVIP(ip) ((ip)>>24)&0xFF,((ip)>>16)&0xFF,((ip)>>8)&0xFF,((ip)>>0)&0xFF
-#define MAKEIP(a,b,c,d) (uint32)( ( ( (a)&0xFF ) << 24 ) | ( ( (b)&0xFF ) << 16 ) | ( ( (c)&0xFF ) << 8 ) | ( ( (d)&0xFF ) << 0 ) )
-uint16 ntows(uint16 netshort);
-
-int socket_getips(uint32* ips, int max);
-
-extern uint32 addr_[16]; // ip addresses of local host (host byte order)
-extern int naddr_; // # of ip addresses
-
-void set_eof(int fd);
-
-/* [Ind/Hercules] - socket_datasync */
-void socket_datasync(int fd, bool send);
-
-/// Use a shortlist of sockets instead of iterating all sessions for sockets
+/// Use a shortlist of sockets instead of iterating all sessions for sockets
/// that have data to send or need eof handling.
/// Adapted to use a static array instead of a linked list.
///
/// @author Buuyo-tama
#define SEND_SHORTLIST
-#ifdef SEND_SHORTLIST
-// Add a fd to the shortlist so that it'll be recognized as a fd that needs
-// sending done on it.
-void send_shortlist_add_fd(int fd);
-// Do pending network sends (and eof handling) from the shortlist.
-void send_shortlist_do_sends();
-#endif
+// Note: purposely returns four comma-separated arguments
+#define CONVIP(ip) ((ip)>>24)&0xFF,((ip)>>16)&0xFF,((ip)>>8)&0xFF,((ip)>>0)&0xFF
+#define MAKEIP(a,b,c,d) ((uint32)( ( ( (a)&0xFF ) << 24 ) | ( ( (b)&0xFF ) << 16 ) | ( ( (c)&0xFF ) << 8 ) | ( ( (d)&0xFF ) << 0 ) ))
+
+/**
+ * This stays out of the interface.
+ **/
+struct socket_data **session;
+
+/**
+ * Socket.c interface, mostly for reading however.
+ **/
+struct socket_interface {
+ int fd_max;
+ /* */
+ time_t stall_time;
+ time_t last_tick;
+ /* */
+ uint32 addr_[16]; // ip addresses of local host (host byte order)
+ int naddr_; // # of ip addresses
+ /* */
+ void (*init) (void);
+ void (*final) (void);
+ /* */
+ int (*perform) (int next);
+ /* [Ind/Hercules] - socket_datasync */
+ void (*datasync) (int fd, bool send);
+ /* */
+ int (*make_listen_bind) (uint32 ip, uint16 port);
+ int (*make_connection) (uint32 ip, uint16 port, struct hSockOpt *opt);
+ int (*realloc_fifo) (int fd, unsigned int rfifo_size, unsigned int wfifo_size);
+ int (*realloc_writefifo) (int fd, size_t addition);
+ int (*WFIFOSET) (int fd, size_t len);
+ int (*RFIFOSKIP) (int fd, size_t len);
+ void (*close) (int fd);
+ /* */
+ bool (*session_isValid) (int fd);
+ bool (*session_isActive) (int fd);
+ /* */
+ void (*flush_fifo) (int fd);
+ void (*flush_fifos) (void);
+ void (*set_nonblocking) (int fd, unsigned long yes);
+ void (*set_defaultparse) (ParseFunc defaultparse);
+ /* hostname/ip conversion functions */
+ uint32 (*host2ip) (const char* hostname);
+ const char * (*ip2str) (uint32 ip, char ip_str[16]);
+ uint32 (*str2ip) (const char* ip_str);
+ /* */
+ uint16 (*ntows) (uint16 netshort);
+ /* */
+ int (*getips) (uint32* ips, int max);
+ /* */
+ void (*set_eof) (int fd);
+};
-#endif /* _SOCKET_H_ */
+struct socket_interface *sockt;
+
+void socket_defaults(void);
+
+/* the purpose of these macros is simply to not make calling them be an annoyance */
+#ifndef H_SOCKET_C
+ #define make_listen_bind(ip, port) ( sockt->make_listen_bind(ip, port) )
+ #define make_connection(ip, port, opt) ( sockt->make_connection(ip, port, opt) )
+ #define realloc_fifo(fd, rfifo_size, wfifo_size) ( sockt->realloc_fifo(fd, rfifo_size, wfifo_size) )
+ #define realloc_writefifo(fd, addition) ( sockt->realloc_writefifo(fd, addition) )
+ #define WFIFOSET(fd, len) ( sockt->WFIFOSET(fd, len) )
+ #define RFIFOSKIP(fd, len) ( sockt->RFIFOSKIP(fd, len) )
+ #define do_close(fd) ( sockt->close(fd) )
+ #define session_isValid(fd) ( sockt->session_isValid(fd) )
+ #define session_isActive(fd) ( sockt->session_isActive(fd) )
+ #define flush_fifo(fd) ( sockt->flush_fifo(fd) )
+ #define flush_fifos() ( sockt->flush_fifos() )
+ #define set_nonblocking(fd, yes) ( sockt->set_nonblocking(fd, yes) )
+ #define set_defaultparse(defaultparse) ( sockt->set_defaultparse(defaultparse) )
+ #define host2ip(hostname) ( sockt->host2ip(hostname) )
+ #define ip2str(ip, ip_str) ( sockt->ip2str(ip, ip_str) )
+ #define str2ip(ip_str) ( sockt->str2ip(ip_str) )
+ #define ntows(netshort) ( sockt->ntows(netshort) )
+ #define getips(ips, max) ( sockt->getips(ips, max) )
+ #define set_eof(fd) ( sockt->set_eof(fd) )
+#endif /* H_SOCKET_C */
+
+#endif /* COMMON_SOCKET_H */
diff --git a/src/common/spinlock.h b/src/common/spinlock.h
index 3419bfdd5..bde36b8e5 100644
--- a/src/common/spinlock.h
+++ b/src/common/spinlock.h
@@ -1,11 +1,10 @@
-#pragma once
-#ifndef _rA_SPINLOCK_H_
-#define _rA_SPINLOCK_H_
+#ifndef COMMON_SPINLOCK_H
+#define COMMON_SPINLOCK_H
//
// CAS based Spinlock Implementation
//
-// CamelCase names are choosen to be consistent with microsofts winapi
+// CamelCase names are chosen to be consistent with Microsoft's WinAPI
// which implements CriticalSection by this naming...
//
// Author: Florian Wilkemeyer <fw@f-ws.de>
@@ -15,50 +14,50 @@
//
//
+#include "../common/atomic.h"
+#include "../common/cbasetypes.h"
+#include "../common/thread.h"
+
#ifdef WIN32
#include "../common/winapi.h"
#endif
-#include "../common/cbasetypes.h"
-#include "../common/atomic.h"
-#include "../common/thread.h"
-
#ifdef WIN32
typedef struct __declspec( align(64) ) SPIN_LOCK{
volatile LONG lock;
volatile LONG nest;
volatile LONG sync_lock;
-} SPIN_LOCK, *PSPIN_LOCK;
+} SPIN_LOCK;
#else
typedef struct SPIN_LOCK{
volatile int32 lock;
volatile int32 nest; // nesting level.
volatile int32 sync_lock;
-} __attribute__((aligned(64))) SPIN_LOCK, *PSPIN_LOCK;
+} __attribute__((aligned(64))) SPIN_LOCK;
#endif
-static forceinline void InitializeSpinLock(PSPIN_LOCK lck){
+static forceinline void InitializeSpinLock(SPIN_LOCK *lck){
lck->lock = 0;
lck->nest = 0;
lck->sync_lock = 0;
}
-static forceinline void FinalizeSpinLock(PSPIN_LOCK lck){
+static forceinline void FinalizeSpinLock(SPIN_LOCK *lck){
return;
}
-#define getsynclock(l) { while(1){ if(InterlockedCompareExchange(l, 1, 0) == 0) break; rathread_yield(); } }
-#define dropsynclock(l) { InterlockedExchange(l, 0); }
+#define getsynclock(l) do { if(InterlockedCompareExchange((l), 1, 0) == 0) break; rathread_yield(); } while(/*always*/1)
+#define dropsynclock(l) do { InterlockedExchange((l), 0); } while(0)
-static forceinline void EnterSpinLock(PSPIN_LOCK lck){
+static forceinline void EnterSpinLock(SPIN_LOCK *lck){
int tid = rathread_get_tid();
- // Get Sync Lock && Check if the requester thread already owns the lock.
+ // Get Sync Lock && Check if the requester thread already owns the lock.
// if it owns, increase nesting level
getsynclock(&lck->sync_lock);
if(InterlockedCompareExchange(&lck->lock, tid, tid) == tid){
@@ -70,7 +69,7 @@ static forceinline void EnterSpinLock(PSPIN_LOCK lck){
dropsynclock(&lck->sync_lock);
- // Spin until we've got it !
+ // Spin until we've got it !
while(1){
if(InterlockedCompareExchange(&lck->lock, tid, 0) == 0){
@@ -85,7 +84,7 @@ static forceinline void EnterSpinLock(PSPIN_LOCK lck){
}
-static forceinline void LeaveSpinLock(PSPIN_LOCK lck){
+static forceinline void LeaveSpinLock(SPIN_LOCK *lck){
int tid = rathread_get_tid();
getsynclock(&lck->sync_lock);
@@ -101,4 +100,4 @@ static forceinline void LeaveSpinLock(PSPIN_LOCK lck){
-#endif
+#endif /* COMMON_SPINLOCK_H */
diff --git a/src/common/sql.c b/src/common/sql.c
index 441b860da..8ae9d3cdb 100644
--- a/src/common/sql.c
+++ b/src/common/sql.c
@@ -2,19 +2,23 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
+#define HERCULES_CORE
+
+#include "sql.h"
+
+#include <stdlib.h> // strtoul
+#include <string.h> // strlen/strnlen/memcpy/memset
+
#include "../common/cbasetypes.h"
#include "../common/malloc.h"
#include "../common/showmsg.h"
#include "../common/strlib.h"
#include "../common/timer.h"
-#include "sql.h"
#ifdef WIN32
-#include "../common/winapi.h"
+# include "../common/winapi.h" // Needed before mysql.h
#endif
#include <mysql.h>
-#include <string.h>// strlen/strnlen/memcpy/memset
-#include <stdlib.h>// strtoul
void hercules_mysql_error_handler(unsigned int ecode);
@@ -34,7 +38,7 @@ struct Sql {
// Column length receiver.
-// Takes care of the possible size missmatch between uint32 and unsigned long.
+// Takes care of the possible size mismatch between uint32 and unsigned long.
struct s_column_length {
uint32* out_length;
unsigned long length;
@@ -180,7 +184,7 @@ int Sql_Ping(Sql* self)
/// Wrapper function for Sql_Ping.
///
/// @private
-static int Sql_P_KeepaliveTimer(int tid, unsigned int tick, int id, intptr_t data)
+static int Sql_P_KeepaliveTimer(int tid, int64 tick, int id, intptr_t data)
{
Sql* self = (Sql*)data;
ShowInfo("Pinging SQL server to keep connection alive...\n");
@@ -210,7 +214,7 @@ static int Sql_P_Keepalive(Sql* self)
// establish keepalive
ping_interval = timeout - 30; // 30-second reserve
//add_timer_func_list(Sql_P_KeepaliveTimer, "Sql_P_KeepaliveTimer");
- return iTimer->add_timer_interval(iTimer->gettick() + ping_interval*1000, Sql_P_KeepaliveTimer, 0, (intptr_t)self, ping_interval*1000);
+ return timer->add_interval(timer->gettick() + ping_interval*1000, Sql_P_KeepaliveTimer, 0, (intptr_t)self, ping_interval*1000);
}
@@ -238,8 +242,8 @@ size_t Sql_EscapeStringLen(Sql* self, char *out_to, const char *from, size_t fro
/// Executes a query.
-int Sql_Query(Sql* self, const char* query, ...)
-{
+int Sql_Query(Sql *self, const char *query, ...) __attribute__((format(printf, 2, 3)));
+int Sql_Query(Sql *self, const char *query, ...) {
int res;
va_list args;
@@ -398,13 +402,12 @@ void Sql_ShowDebug_(Sql* self, const char* debug_file, const unsigned long debug
/// Frees a Sql handle returned by Sql_Malloc.
-void Sql_Free(Sql* self)
-{
+void Sql_Free(Sql* self) {
if( self )
{
SQL->FreeResult(self);
StrBuf->Destroy(&self->buf);
- if( self->keepalive != INVALID_TIMER ) iTimer->delete_timer(self->keepalive, Sql_P_KeepaliveTimer);
+ if( self->keepalive != INVALID_TIMER ) timer->delete(self->keepalive, Sql_P_KeepaliveTimer);
aFree(self);
}
}
@@ -514,15 +517,13 @@ static int Sql_P_BindSqlDataType(MYSQL_BIND* bind, enum SqlDataType buffer_type,
/// Prints debug information about a field (type and length).
///
/// @private
-static void Sql_P_ShowDebugMysqlFieldInfo(const char* prefix, enum enum_field_types type, int is_unsigned, unsigned long length, const char* length_postfix)
-{
- const char* sign = (is_unsigned ? "UNSIGNED " : "");
- const char* type_string;
- switch( type )
- {
- default:
- ShowDebug("%stype=%s%u, length=%d\n", prefix, sign, type, length);
- return;
+static void Sql_P_ShowDebugMysqlFieldInfo(const char* prefix, enum enum_field_types type, int is_unsigned, unsigned long length, const char* length_postfix) {
+ const char *sign = (is_unsigned ? "UNSIGNED " : "");
+ const char *type_string = NULL;
+ switch (type) {
+ default:
+ ShowDebug("%stype=%s%u, length=%lu\n", prefix, sign, type, length);
+ return;
#define SHOW_DEBUG_OF(x) case x: type_string = #x; break
SHOW_DEBUG_OF(MYSQL_TYPE_TINY);
SHOW_DEBUG_OF(MYSQL_TYPE_SHORT);
@@ -545,7 +546,7 @@ static void Sql_P_ShowDebugMysqlFieldInfo(const char* prefix, enum enum_field_ty
SHOW_DEBUG_OF(MYSQL_TYPE_NULL);
#undef SHOW_DEBUG_TYPE_OF
}
- ShowDebug("%stype=%s%s, length=%d%s\n", prefix, sign, type_string, length, length_postfix);
+ ShowDebug("%stype=%s%s, length=%lu%s\n", prefix, sign, type_string, length, length_postfix);
}
@@ -566,7 +567,7 @@ static void SqlStmt_P_ShowDebugTruncatedColumn(SqlStmt* self, size_t i)
Sql_P_ShowDebugMysqlFieldInfo("data - ", field->type, field->flags&UNSIGNED_FLAG, self->column_lengths[i].length, "");
column = &self->columns[i];
if( column->buffer_type == MYSQL_TYPE_STRING )
- Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "+1(nul-terminator)");
+ Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "+1(null-terminator)");
else
Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "");
mysql_free_result(meta);
@@ -604,8 +605,8 @@ SqlStmt* SqlStmt_Malloc(Sql* sql) {
/// Prepares the statement.
-int SqlStmt_Prepare(SqlStmt* self, const char* query, ...)
-{
+int SqlStmt_Prepare(SqlStmt *self, const char *query, ...) __attribute__((format(printf, 2, 3)));
+int SqlStmt_Prepare(SqlStmt *self, const char *query, ...) {
int res;
va_list args;
@@ -753,19 +754,16 @@ size_t SqlStmt_NumColumns(SqlStmt* self)
/// Binds the result of a column to a buffer.
-int SqlStmt_BindColumn(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len, uint32* out_length, int8* out_is_null)
-{
- if( self == NULL )
+int SqlStmt_BindColumn(SqlStmt *self, size_t idx, enum SqlDataType buffer_type, void *buffer, size_t buffer_len, uint32 *out_length, int8 *out_is_null) {
+ if (self == NULL)
return SQL_ERROR;
- if( buffer_type == SQLDT_STRING || buffer_type == SQLDT_ENUM )
- {
- if( buffer_len < 1 )
- {
- ShowDebug("SqlStmt_BindColumn: buffer_len(%d) is too small, no room for the nul-terminator\n", buffer_len);
+ if (buffer_type == SQLDT_STRING || buffer_type == SQLDT_ENUM) {
+ if (buffer_len < 1) {
+ ShowDebug("SqlStmt_BindColumn: buffer_len(%"PRIuS") is too small, no room for the null-terminator\n", buffer_len);
return SQL_ERROR;
}
- --buffer_len;// nul-terminator
+ --buffer_len;// null-terminator
}
if( !self->bind_columns )
{// initialize the bindings
@@ -888,7 +886,7 @@ int SqlStmt_NextRow(SqlStmt* self)
if( self->column_lengths[i].out_length )
*self->column_lengths[i].out_length = (uint32)length;
if( column->buffer_type == MYSQL_TYPE_STRING )
- {// clear unused part of the string/enum buffer (and nul-terminate)
+ {// clear unused part of the string/enum buffer (and null-terminate)
memset((char*)column->buffer + length, 0, column->buffer_length - length + 1);
}
else if( column->buffer_type == MYSQL_TYPE_BLOB && length < column->buffer_length )
@@ -971,9 +969,9 @@ void Sql_inter_server_read(const char* cfgName, bool first) {
return;
}
- while(fgets(line, sizeof(line), fp)) {
- i = sscanf(line, "%[^:]: %[^\r\n]", w1, w2);
- if(i != 2)
+ while (fgets(line, sizeof(line), fp)) {
+ i = sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2);
+ if (i != 2)
continue;
if(!strcmpi(w1,"mysql_reconnect_type")) {
@@ -1003,18 +1001,24 @@ void Sql_HerculesUpdateCheck(Sql* self) {
char line[22];// "yyyy-mm-dd--hh-mm" (17) + ".sql" (4) + 1
FILE* ifp;/* index fp */
unsigned int performed = 0;
-
+ StringBuf buf;
+
+ if( self == NULL )
+ return;/* return silently, build has no mysql connection */
+
if( !( ifp = fopen("sql-files/upgrades/index.txt", "r") ) ) {
ShowError("SQL upgrade index was not found!\n");
return;
}
+ StrBuf->Init(&buf);
+
while(fgets(line, sizeof(line), ifp)) {
char path[41];// "sql-files/upgrades/" (19) + "yyyy-mm-dd--hh-mm" (17) + ".sql" (4) + 1
char timestamp[11];// "1360186680" (10) + 1
FILE* ufp;/* upgrade fp */
- if( line[0] == '\n' || ( line[0] == '/' && line[1] == '/' ) )/* skip \n and "//" comments */
+ if( line[0] == '\n' || line[0] == '\r' || ( line[0] == '/' && line[1] == '/' ) )/* skip \n, \r and "//" comments */
continue;
sprintf(path,"sql-files/upgrades/%s",line);
@@ -1030,11 +1034,11 @@ void Sql_HerculesUpdateCheck(Sql* self) {
fseek (ufp,1,SEEK_SET);/* woo. skip the # */
if( fgets(timestamp,sizeof(timestamp),ufp) ) {
- unsigned int timestampui = atol(timestamp);
+ unsigned int timestampui = (unsigned int)atol(timestamp);
if( SQL_ERROR == SQL->Query(self, "SELECT 1 FROM `sql_updates` WHERE `timestamp` = '%u' LIMIT 1", timestampui) )
Sql_ShowDebug(self);
if( Sql_NumRows(self) != 1 ) {
- ShowSQL("'"CL_WHITE"%s"CL_RESET"' wasn't applied to the database\n",path);
+ StrBuf->Printf(&buf,CL_MAGENTA"[SQL]"CL_RESET": -- '"CL_WHITE"%s"CL_RESET"'\n", path);
performed++;
}
}
@@ -1045,8 +1049,49 @@ void Sql_HerculesUpdateCheck(Sql* self) {
fclose(ifp);
if( performed ) {
- ShowSQL("If you did apply these updates or would like to be skip, insert a new entry in your sql_updates table with the timestamp of each file\n");
+ ShowSQL("- detected %d new "CL_WHITE"SQL updates"CL_RESET"\n",performed);
+ ShowMessage("%s",StrBuf->Value(&buf));
+ ShowSQL("To manually skip, type: 'sql update skip <file name>'\n");
+ }
+
+ StrBuf->Destroy(&buf);
+}
+
+void Sql_HerculesUpdateSkip(Sql* self,const char *filename) {
+ char path[41];// "sql-files/upgrades/" (19) + "yyyy-mm-dd--hh-mm" (17) + ".sql" (4) + 1
+ char timestamp[11];// "1360186680" (10) + 1
+ FILE* ifp;/* index fp */
+
+ if( !self ) {
+ ShowError("SQL not hooked!\n");
+ return;
}
+
+ snprintf(path,41,"sql-files/upgrades/%s",filename);
+
+ if( !( ifp = fopen(path, "r") ) ) {
+ ShowError("Upgrade file '%s' was not found!\n",filename);
+ return;
+ }
+
+ fseek (ifp,1,SEEK_SET);/* woo. skip the # */
+
+ if( fgets(timestamp,sizeof(timestamp),ifp) ) {
+ unsigned int timestampui = (unsigned int)atol(timestamp);
+ if( SQL_ERROR == SQL->Query(self, "SELECT 1 FROM `sql_updates` WHERE `timestamp` = '%u' LIMIT 1", timestampui) )
+ Sql_ShowDebug(self);
+ else if( Sql_NumRows(self) == 1 ) {
+ ShowError("Upgrade '%s' has already been skipped\n",filename);
+ } else {
+ if( SQL_ERROR == SQL->Query(self, "INSERT INTO `sql_updates` (`timestamp`,`ignored`) VALUES ('%u','Yes') ", timestampui) )
+ Sql_ShowDebug(self);
+ else {
+ ShowInfo("SQL Upgrade '%s' successfully skipped\n",filename);
+ }
+ }
+ }
+ fclose(ifp);
+ return;
}
void Sql_Init(void) {
diff --git a/src/common/sql.h b/src/common/sql.h
index 535990649..b1f1f41c3 100644
--- a/src/common/sql.h
+++ b/src/common/sql.h
@@ -2,16 +2,15 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _COMMON_SQL_H_
-#define _COMMON_SQL_H_
+#ifndef COMMON_SQL_H
+#define COMMON_SQL_H
-#include "../common/cbasetypes.h"
#include <stdarg.h>// va_list
-
+#include "../common/cbasetypes.h"
// Return codes
-#define SQL_ERROR -1
+#define SQL_ERROR (-1)
#define SQL_SUCCESS 0
#define SQL_NO_DATA 100
@@ -99,7 +98,7 @@ struct sql_interface {
/// The query is constructed as if it was sprintf.
///
/// @return SQL_SUCCESS or SQL_ERROR
- int (*Query) (Sql* self, const char* query, ...);
+ int (*Query) (Sql *self, const char *query, ...) __attribute__((format(printf, 2, 3)));
/// Executes a query.
/// Any previous result is freed.
/// The query is constructed as if it was svprintf.
@@ -148,10 +147,10 @@ struct sql_interface {
///////////////////////////////////////////////////////////////////////////////
// Prepared Statements
///////////////////////////////////////////////////////////////////////////////
- // Parameters are placed in the statement by embedding question mark ('?')
+ // Parameters are placed in the statement by embedding question mark ('?')
// characters into the query at the appropriate positions.
// The markers are legal only in places where they represent data.
- // The markers cannot be inside quotes. Quotes will be added automatically
+ // The markers cannot be inside quotes. Quotes will be added automatically
// when they are required.
//
// example queries with parameters:
@@ -167,7 +166,7 @@ struct sql_interface {
/// It uses the connection of the parent Sql handle.
/// Queries in Sql and SqlStmt are independent and don't affect each other.
///
- /// @return SqlStmt handle or NULL if an error occured
+ /// @return SqlStmt handle or NULL if an error occurred
struct SqlStmt* (*StmtMalloc)(Sql* sql);
@@ -177,7 +176,7 @@ struct sql_interface {
/// The query is constructed as if it was sprintf.
///
/// @return SQL_SUCCESS or SQL_ERROR
- int (*StmtPrepare)(SqlStmt* self, const char* query, ...);
+ int (*StmtPrepare) (SqlStmt *self, const char *query, ...) __attribute__((format(printf, 2, 3)));
/// Prepares the statement.
/// Any previous result is freed and all parameter bindings are removed.
@@ -199,7 +198,7 @@ struct sql_interface {
/// Returns the number of parameters in the prepared statement.
///
- /// @return Number or paramenters
+ /// @return Number or parameters
size_t (*StmtNumParams)(SqlStmt* self);
@@ -237,8 +236,8 @@ struct sql_interface {
/// Binds the result of a column to a buffer.
/// The buffer will be filled with data when the next row is fetched.
- /// For string/enum buffer types there has to be enough space for the data
- /// and the nul-terminator (an extra byte).
+ /// For string/enum buffer types there has to be enough space for the data
+ /// and the null-terminator (an extra byte).
///
/// @return SQL_SUCCESS or SQL_ERROR
int (*StmtBindColumn)(SqlStmt* self, size_t idx, SqlDataType buffer_type, void* buffer, size_t buffer_len, uint32* out_length, int8* out_is_null);
@@ -277,24 +276,19 @@ void sql_defaults(void);
#if defined(SQL_REMOVE_SHOWDEBUG)
#define Sql_ShowDebug(self) (void)0
#else
-#define Sql_ShowDebug(self) SQL->ShowDebug_(self, __FILE__, __LINE__)
+#define Sql_ShowDebug(self) (SQL->ShowDebug_((self), __FILE__, __LINE__))
#endif
void Sql_HerculesUpdateCheck(Sql* self);
+void Sql_HerculesUpdateSkip(Sql* self,const char *filename);
#if defined(SQL_REMOVE_SHOWDEBUG)
#define SqlStmt_ShowDebug(self) (void)0
#else
-#define SqlStmt_ShowDebug(self) SQL->StmtShowDebug_(self, __FILE__, __LINE__)
-#endif
/// Shows debug information (with statement).
-
-
-
-
-
+#define SqlStmt_ShowDebug(self) (SQL->StmtShowDebug_((self), __FILE__, __LINE__))
+#endif
void Sql_Init(void);
-
-#endif /* _COMMON_SQL_H_ */
+#endif /* COMMON_SQL_H */
diff --git a/src/common/strlib.c b/src/common/strlib.c
index 686b2e47d..592390770 100644
--- a/src/common/strlib.c
+++ b/src/common/strlib.c
@@ -2,16 +2,19 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/cbasetypes.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#define STRLIB_C
+#define HERCULES_CORE
+
+#define H_STRLIB_C
#include "strlib.h"
+#undef H_STRLIB_C
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <errno.h>
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
#define J_MAX_MALLOC_SIZE 65535
@@ -144,15 +147,15 @@ char* trim(char* str)
if( start == end )
*str = '\0';// empty string
else
- {// move string with nul terminator
+ {// move string with null-terminator
str[end] = '\0';
memmove(str,str+start,end-start+1);
}
return str;
}
-// Converts one or more consecutive occurences of the delimiters into a single space
-// and removes such occurences from the beginning and end of string
+// Converts one or more consecutive occurrences of the delimiters into a single space
+// and removes such occurrences from the beginning and end of string
// NOTE: make sure the string is not const!!
char* normalize_name(char* str,const char* delims)
{
@@ -189,7 +192,7 @@ char* normalize_name(char* str,const char* delims)
return str;
}
-//stristr: Case insensitive version of strstr, code taken from
+//stristr: Case insensitive version of strstr, code taken from
//http://www.daniweb.com/code/snippet313.html, Dave Sinkula
//
const char* stristr(const char* haystack, const char* needle)
@@ -221,7 +224,7 @@ const char* stristr(const char* haystack, const char* needle)
}
#ifdef __WIN32
-char* _strtok_r(char *s1, const char *s2, char **lasts) {
+char* strtok_r_(char *s1, const char *s2, char **lasts) {
char *ret;
if (s1 == NULL)
@@ -240,16 +243,19 @@ char* _strtok_r(char *s1, const char *s2, char **lasts) {
}
#endif
+// TODO: The _MSC_VER check can probably be removed (we no longer support VS
+// versions <= 2003, do we?), but this implementation might be still necessary
+// for NetBSD 5.x and possibly some Solaris versions.
#if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)
/* Find the length of STRING, but scan at most MAXLEN characters.
If no '\0' terminator is found in that many characters, return MAXLEN. */
-size_t strnlen (const char* string, size_t maxlen)
-{
- const char* end = (const char*)memchr(string, '\0', maxlen);
- return end ? (size_t) (end - string) : maxlen;
+size_t strnlen(const char* string, size_t maxlen) {
+ const char* end = (const char*)memchr(string, '\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
}
#endif
+// TODO: This should probably be removed, I don't think we support MSVC++ 6.0 anymore.
#if defined(WIN32) && defined(_MSC_VER) && _MSC_VER <= 1200
uint64 strtoull(const char* str, char** endptr, int base)
{
@@ -331,30 +337,39 @@ int e_mail_check(char* email)
//--------------------------------------------------
// Return numerical value of a switch configuration
-// on/off, english, français, deutsch, español
+// on/off, yes/no, true/false, number
//--------------------------------------------------
-int config_switch(const char* str)
-{
- if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+int config_switch(const char* str) {
+ size_t len = strlen(str);
+ if ((len == 2 && strcmpi(str, "on") == 0)
+ || (len == 3 && strcmpi(str, "yes") == 0)
+ || (len == 4 && strcmpi(str, "true") == 0)
+ // || (len == 3 && strcmpi(str, "oui") == 0) // Uncomment and edit to add your own localized versions
+ )
return 1;
- if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+
+ if ((len == 3 && strcmpi(str, "off") == 0)
+ || (len == 2 && strcmpi(str, "no") == 0)
+ || (len == 5 && strcmpi(str, "false") == 0)
+ // || (len == 3 && strcmpi(str, "non") == 0) // Uncomment and edit to add your own localized versions
+ )
return 0;
return (int)strtol(str, NULL, 0);
}
-/// strncpy that always nul-terminates the string
+/// strncpy that always null-terminates the string
char* safestrncpy(char* dst, const char* src, size_t n)
{
if( n > 0 )
{
char* d = dst;
const char* s = src;
- d[--n] = '\0';/* nul-terminate string */
+ d[--n] = '\0';/* null-terminate string */
for( ; n > 0; --n )
{
if( (*d++ = *s++) == '\0' )
- {/* nul-pad remaining bytes */
+ {/* null-pad remaining bytes */
while( --n > 0 )
*d++ = '\0';
break;
@@ -370,26 +385,25 @@ size_t safestrnlen(const char* string, size_t maxlen)
return ( string != NULL ) ? strnlen(string, maxlen) : 0;
}
-/// Works like snprintf, but always nul-terminates the buffer.
-/// Returns the size of the string (without nul-terminator)
+/// Works like snprintf, but always null-terminates the buffer.
+/// Returns the size of the string (without null-terminator)
/// or -1 if the buffer is too small.
///
/// @param buf Target buffer
-/// @param sz Size of the buffer (including nul-terminator)
+/// @param sz Size of the buffer (including null-terminator)
/// @param fmt Format string
/// @param ... Format arguments
/// @return The size of the string or -1 if the buffer is too small
-int safesnprintf(char* buf, size_t sz, const char* fmt, ...)
-{
+int safesnprintf(char *buf, size_t sz, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
+int safesnprintf(char *buf, size_t sz, const char *fmt, ...) {
va_list ap;
int ret;
va_start(ap,fmt);
ret = vsnprintf(buf, sz, fmt, ap);
va_end(ap);
- if( ret < 0 || (size_t)ret >= sz )
- {// overflow
- buf[sz-1] = '\0';// always nul-terminate
+ if (ret < 0 || (size_t)ret >= sz) { // overflow
+ buf[sz-1] = '\0';// always null-terminate
return -1;
}
return ret;
@@ -444,9 +458,9 @@ bool bin2hex(char* output, unsigned char* input, size_t count)
/// Parses a single field in a delim-separated string.
/// The delimiter after the field is skipped.
///
-/// @param sv Parse state
+/// @param svstate Parse state
/// @return 1 if a field was parsed, 0 if already done, -1 on error.
-int sv_parse_next(struct s_svstate* sv)
+int sv_parse_next(struct s_svstate* svstate)
{
enum {
START_OF_FIELD,
@@ -462,13 +476,13 @@ int sv_parse_next(struct s_svstate* sv)
char delim;
int i;
- if( sv == NULL )
+ if( svstate == NULL )
return -1;// error
- str = sv->str;
- len = sv->len;
- opt = sv->opt;
- delim = sv->delim;
+ str = svstate->str;
+ len = svstate->len;
+ opt = svstate->opt;
+ delim = svstate->delim;
// check opt
if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) )
@@ -482,9 +496,9 @@ int sv_parse_next(struct s_svstate* sv)
return -1;// error
}
- if( sv->done || str == NULL )
+ if( svstate->done || str == NULL )
{
- sv->done = true;
+ svstate->done = true;
return 0;// nothing to parse
}
@@ -495,10 +509,10 @@ int sv_parse_next(struct s_svstate* sv)
((opt&SV_TERMINATE_CR) && str[i] == '\r') || \
((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') )
#define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' )
-#define SET_FIELD_START() sv->start = i
-#define SET_FIELD_END() sv->end = i
+#define SET_FIELD_START() svstate->start = i
+#define SET_FIELD_END() svstate->end = i
- i = sv->off;
+ i = svstate->off;
state = START_OF_FIELD;
while( state != END )
{
@@ -578,14 +592,14 @@ int sv_parse_next(struct s_svstate* sv)
else
++i;// CR or LF
#endif
- sv->done = true;
+ svstate->done = true;
state = END;
break;
}
}
if( IS_END() )
- sv->done = true;
- sv->off = i;
+ svstate->done = true;
+ svstate->off = i;
#undef IS_END
#undef IS_DELIM
@@ -603,47 +617,47 @@ int sv_parse_next(struct s_svstate* sv)
/// out_pos[0] and out_pos[1] are the start and end of line.
/// Other position pairs are the start and end of fields.
/// Returns the number of fields found or -1 if an error occurs.
-///
+///
/// out_pos can be NULL.
/// If a line terminator is found, the end position is placed there.
-/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
+/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
/// for the seconds field and so on.
/// Unfilled positions are set to -1.
-///
+///
/// @param str String to parse
/// @param len Length of the string
/// @param startoff Where to start parsing
/// @param delim Field delimiter
/// @param out_pos Array of resulting positions
/// @param npos Size of the pos array
-/// @param opt Options that determine the parsing behaviour
-/// @return Number of fields found in the string or -1 if an error occured
+/// @param opt Options that determine the parsing behavior
+/// @return Number of fields found in the string or -1 if an error occurred
int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt) {
- struct s_svstate sv;
+ struct s_svstate svstate;
int count;
// initialize
if( out_pos == NULL ) npos = 0;
for( count = 0; count < npos; ++count )
out_pos[count] = -1;
- sv.str = str;
- sv.len = len;
- sv.off = startoff;
- sv.opt = opt;
- sv.delim = delim;
- sv.done = false;
+ svstate.str = str;
+ svstate.len = len;
+ svstate.off = startoff;
+ svstate.opt = opt;
+ svstate.delim = delim;
+ svstate.done = false;
// parse
count = 0;
if( npos > 0 ) out_pos[0] = startoff;
- while( !sv.done ) {
+ while( !svstate.done ) {
++count;
- if( sv_parse_next(&sv) <= 0 )
+ if( sv_parse_next(&svstate) <= 0 )
return -1;// error
- if( npos > count*2 ) out_pos[count*2] = sv.start;
- if( npos > count*2+1 ) out_pos[count*2+1] = sv.end;
+ if( npos > count*2 ) out_pos[count*2] = svstate.start;
+ if( npos > count*2+1 ) out_pos[count*2+1] = svstate.end;
}
- if( npos > 1 ) out_pos[1] = sv.off;
+ if( npos > 1 ) out_pos[1] = svstate.off;
return count;
}
@@ -651,21 +665,21 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
/// WARNING: this function modifies the input string
/// Starts splitting at startoff and fills the out_fields array.
/// out_fields[0] is the start of the next line.
-/// Other entries are the start of fields (nul-teminated).
+/// Other entries are the start of fields (null-terminated).
/// Returns the number of fields found or -1 if an error occurs.
-///
+///
/// out_fields can be NULL.
-/// Fields that don't fit in out_fields are not nul-terminated.
+/// Fields that don't fit in out_fields are not null-terminated.
/// Extra entries in out_fields are filled with the end of the last field (empty string).
-///
+///
/// @param str String to parse
/// @param len Length of the string
/// @param startoff Where to start parsing
/// @param delim Field delimiter
/// @param out_fields Array of resulting fields
/// @param nfields Size of the field array
-/// @param opt Options that determine the parsing behaviour
-/// @return Number of fields found in the string or -1 if an error occured
+/// @param opt Options that determine the parsing behavior
+/// @return Number of fields found in the string or -1 if an error occurred
int sv_split(char* str, int len, int startoff, char delim, char** out_fields, int nfields, enum e_svopt opt) {
int pos[1024];
int i;
@@ -952,7 +966,7 @@ bool sv_readdb(const char* directory, const char* filename, char delim, int minc
if( line[0] == '\0' || line[0] == '\n' || line[0] == '\r')
continue;
- columns = sv_split(line, strlen(line), 0, delim, fields, fields_length, (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF));
+ columns = sv_split(line, (int)strlen(line), 0, delim, fields, fields_length, (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF));
if( columns < mincols ) {
ShowError("sv_readdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincols);
@@ -1005,7 +1019,8 @@ void StringBuf_Init(StringBuf* self) {
}
/// Appends the result of printf to the StringBuf
-int StringBuf_Printf(StringBuf* self, const char* fmt, ...) {
+int StringBuf_Printf(StringBuf *self, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+int StringBuf_Printf(StringBuf *self, const char *fmt, ...) {
int len;
va_list ap;
@@ -1018,7 +1033,8 @@ int StringBuf_Printf(StringBuf* self, const char* fmt, ...) {
/// Appends the result of vprintf to the StringBuf
int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap) {
- int n, size, off;
+ int n, off;
+ size_t size;
for(;;) {
va_list apcopy;
@@ -1028,7 +1044,7 @@ int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap) {
n = vsnprintf(self->ptr_, size, fmt, apcopy);
va_end(apcopy);
/* If that worked, return the length. */
- if( n > -1 && n < size ) {
+ if( n > -1 && (size_t)n < size ) {
self->ptr_ += n;
return (int)(self->ptr_ - self->buf_);
}
@@ -1042,11 +1058,11 @@ int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap) {
/// Appends the contents of another StringBuf to the StringBuf
int StringBuf_Append(StringBuf* self, const StringBuf* sbuf) {
- int available = self->max_ - (self->ptr_ - self->buf_);
- int needed = (int)(sbuf->ptr_ - sbuf->buf_);
+ size_t available = self->max_ - (self->ptr_ - self->buf_);
+ size_t needed = sbuf->ptr_ - sbuf->buf_;
if( needed >= available ) {
- int off = (int)(self->ptr_ - self->buf_);
+ size_t off = (self->ptr_ - self->buf_);
self->max_ += needed;
self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
self->ptr_ = self->buf_ + off;
@@ -1059,12 +1075,12 @@ int StringBuf_Append(StringBuf* self, const StringBuf* sbuf) {
// Appends str to the StringBuf
int StringBuf_AppendStr(StringBuf* self, const char* str) {
- int available = self->max_ - (self->ptr_ - self->buf_);
- int needed = (int)strlen(str);
+ size_t available = self->max_ - (self->ptr_ - self->buf_);
+ size_t needed = strlen(str);
if( needed >= available ) {
// not enough space, expand the buffer (minimum expansion = 1024)
- int off = (int)(self->ptr_ - self->buf_);
+ size_t off = (self->ptr_ - self->buf_);
self->max_ += max(needed, 1024);
self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
self->ptr_ = self->buf_ + off;
@@ -1118,10 +1134,14 @@ void strlib_defaults(void) {
#if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)
strlib->strnlen = strnlen;
+#else
+ strlib->strnlen = NULL;
#endif
#if defined(WIN32) && defined(_MSC_VER) && _MSC_VER <= 1200
strlib->strtoull = strtoull;
+#else
+ strlib->strtoull = NULL;
#endif
strlib->e_mail_check = e_mail_check;
strlib->config_switch = config_switch;
diff --git a/src/common/strlib.h b/src/common/strlib.h
index 9b1875d45..00a588772 100644
--- a/src/common/strlib.h
+++ b/src/common/strlib.h
@@ -2,27 +2,21 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _STRLIB_H_
-#define _STRLIB_H_
+#ifndef COMMON_STRLIB_H
+#define COMMON_STRLIB_H
-#include "../common/cbasetypes.h"
#include <stdarg.h>
+#include <string.h>
-#ifndef __USE_GNU
- #define __USE_GNU // required to enable strnlen on some platforms
- #include <string.h>
- #undef __USE_GNU
-#else
- #include <string.h>
-#endif
+#include "../common/cbasetypes.h"
#ifdef WIN32
#define HAVE_STRTOK_R
- #define strtok_r(s,delim,save_ptr) _strtok_r((s),(delim),(save_ptr))
- char *_strtok_r(char* s1, const char* s2, char** lasts);
+ #define strtok_r(s,delim,save_ptr) strtok_r_((s),(delim),(save_ptr))
+ char *strtok_r_(char* s1, const char* s2, char** lasts);
#endif
-/// Bitfield determining the behaviour of sv_parse and sv_split.
+/// Bitfield determining the behavior of sv_parse and sv_split.
typedef enum e_svopt {
// default: no escapes and no line terminator
SV_NOESCAPE_NOTERMINATE = 0,
@@ -70,27 +64,25 @@ struct strlib_interface {
char *(*normalize_name) (char* str,const char* delims);
const char *(*stristr) (const char *haystack, const char *needle);
-#if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)
+ /* only used when '!(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)', needs to be defined at all times however */
size_t (*strnlen) (const char* string, size_t maxlen);
-#endif
-
-#if defined(WIN32) && defined(_MSC_VER) && _MSC_VER <= 1200
+
+ /* only used when 'defined(WIN32) && defined(_MSC_VER) && _MSC_VER <= 1200', needs to be defined at all times however */
uint64 (*strtoull) (const char* str, char** endptr, int base);
-#endif
int (*e_mail_check) (char* email);
int (*config_switch) (const char* str);
- /// strncpy that always nul-terminates the string
+ /// strncpy that always null-terminates the string
char *(*safestrncpy) (char* dst, const char* src, size_t n);
/// doesn't crash on null pointer
size_t (*safestrnlen) (const char* string, size_t maxlen);
- /// Works like snprintf, but always nul-terminates the buffer.
- /// Returns the size of the string (without nul-terminator)
+ /// Works like snprintf, but always null-terminates the buffer.
+ /// Returns the size of the string (without null-terminator)
/// or -1 if the buffer is too small.
- int (*safesnprintf) (char* buf, size_t sz, const char* fmt, ...);
+ int (*safesnprintf) (char *buf, size_t sz, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
/// Returns the line of the target position in the string.
/// Lines start at 1.
@@ -107,7 +99,7 @@ struct strlib_interface *strlib;
struct stringbuf_interface {
StringBuf* (*Malloc) (void);
void (*Init) (StringBuf* self);
- int (*Printf) (StringBuf* self, const char* fmt, ...);
+ int (*Printf) (StringBuf *self, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
int (*Vprintf) (StringBuf* self, const char* fmt, va_list args);
int (*Append) (StringBuf* self, const StringBuf *sbuf);
int (*AppendStr) (StringBuf* self, const char* str);
@@ -124,9 +116,9 @@ struct sv_interface {
/// Parses a single field in a delim-separated string.
/// The delimiter after the field is skipped.
///
- /// @param sv Parse state
+ /// @param svstate Parse state
/// @return 1 if a field was parsed, 0 if done, -1 on error.
- int (*parse_next) (struct s_svstate* sv);
+ int (*parse_next) (struct s_svstate* svstate);
/// Parses a delim-separated string.
/// Starts parsing at startoff and fills the pos array with position pairs.
@@ -139,7 +131,7 @@ struct sv_interface {
/// WARNING: this function modifies the input string
/// Starts splitting at startoff and fills the out_fields array.
/// out_fields[0] is the start of the next line.
- /// Other entries are the start of fields (nul-teminated).
+ /// Other entries are the start of fields (null-terminated).
/// Returns the number of fields found or -1 if an error occurs.
int (*split) (char* str, int len, int startoff, char delim, char** out_fields, int nfields, enum e_svopt opt);
@@ -167,29 +159,30 @@ struct sv_interface *sv;
void strlib_defaults(void);
/* the purpose of these macros is simply to not make calling them be an annoyance */
-#ifndef STRLIB_C
- #define jstrescape(pt) strlib->jstrescape(pt)
- #define jstrescapecpy(pt,spt) strlib->jstrescapecpy(pt,spt)
- #define jmemescapecpy(pt,spt,size) strlib->jmemescapecpy(pt,spt,size)
- #define remove_control_chars(str) strlib->remove_control_chars(str)
- #define trim(str) strlib->trim(str)
- #define normalize_name(str,delims) strlib->normalize_name(str,delims)
- #define stristr(haystack,needle) strlib->stristr(haystack,needle)
+#ifndef H_STRLIB_C
+ #define jstrescape(pt) (strlib->jstrescape(pt))
+ #define jstrescapecpy(pt,spt) (strlib->jstrescapecpy((pt),(spt)))
+ #define jmemescapecpy(pt,spt,size) (strlib->jmemescapecpy((pt),(spt),(size)))
+ #define remove_control_chars(str) (strlib->remove_control_chars(str))
+ #define trim(str) (strlib->trim(str))
+ #define normalize_name(str,delims) (strlib->normalize_name((str),(delims)))
+ #define stristr(haystack,needle) (strlib->stristr((haystack),(needle)))
#if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)
- #define strnln(string,maxlen) strlib->strnlen(string,maxlen)
+ #define strnlen(string,maxlen) (strlib->strnlen((string),(maxlen)))
#endif
#if defined(WIN32) && defined(_MSC_VER) && _MSC_VER <= 1200
- #define strtoull(str,endptr,base) strlib->strtoull(str,endptr,base)
+ #define strtoull(str,endptr,base) (strlib->strtoull((str),(endptr),(base)))
#endif
- #define e_mail_check(email) strlib->e_mail_check(email)
- #define config_switch(str) strlib->config_switch(str)
- #define safestrncpy(dst,src,n) strlib->safestrncpy(dst,src,n)
- #define safestrnlen(string,maxlen) strlib->safestrnlen(string,maxlen)
- #define safesnprintf(buf,sz,fmt,...) strlib->safesnprintf(buf,sz,fmt,##__VA_ARGS__)
- #define strline(str,pos) strlib->strline(str,pos)
- #define bin2hex(output,input,count) strlib->bin2hex(output,input,count)
-#endif /* STRLIB_C */
-#endif /* _STRLIB_H_ */
+ #define e_mail_check(email) (strlib->e_mail_check(email))
+ #define config_switch(str) (strlib->config_switch(str))
+ #define safestrncpy(dst,src,n) (strlib->safestrncpy((dst),(src),(n)))
+ #define safestrnlen(string,maxlen) (strlib->safestrnlen((string),(maxlen)))
+ #define safesnprintf(buf,sz,fmt,...) (strlib->safesnprintf((buf),(sz),(fmt),##__VA_ARGS__))
+ #define strline(str,pos) (strlib->strline((str),(pos)))
+ #define bin2hex(output,input,count) (strlib->bin2hex((output),(input),(count)))
+#endif /* H_STRLIB_C */
+
+#endif /* COMMON_STRLIB_H */
diff --git a/src/common/sysinfo.c b/src/common/sysinfo.c
new file mode 100644
index 000000000..605256100
--- /dev/null
+++ b/src/common/sysinfo.c
@@ -0,0 +1,1061 @@
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Base Author: Haru @ http://hercules.ws
+
+/// See sysinfo.h for a description of this file
+
+#define HERCULES_CORE
+
+#include "sysinfo.h"
+
+#include <stdio.h> // fopen
+#include <stdlib.h> // atoi
+
+#include "../common/cbasetypes.h"
+#include "../common/core.h"
+#include "../common/malloc.h"
+#include "../common/strlib.h"
+
+#ifdef WIN32
+# include <string.h> // strlen
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+/// Private interface fields
+struct sysinfo_private {
+ char *platform;
+ char *osversion;
+ char *cpu;
+ int cpucores;
+ char *arch;
+ char *compiler;
+ char *cflags;
+ char *vcstype_name;
+ int vcstype;
+ char *vcsrevision_src;
+ char *vcsrevision_scripts;
+};
+
+/// sysinfo.c interface source
+struct sysinfo_interface sysinfo_s;
+struct sysinfo_private sysinfo_p;
+
+#define VCSTYPE_UNKNOWN 0
+#define VCSTYPE_GIT 1
+#define VCSTYPE_SVN 2
+#define VCSTYPE_NONE -1
+
+#ifdef WIN32
+/**
+ * Values to be used with GetProductInfo.
+ *
+ * These aren't defined in MSVC2008/WindowsXP, so we gotta define them here.
+ * Values from: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724358%28v=vs.85%29.aspx
+ */
+enum windows_product_type {
+ msPRODUCT_UNDEFINED = 0x00000000, ///< An unknown product
+ msPRODUCT_ULTIMATE = 0x00000001, ///< Ultimate
+ msPRODUCT_HOME_BASIC = 0x00000002, ///< Home Basic
+ msPRODUCT_HOME_PREMIUM = 0x00000003, ///< Home Premium
+ msPRODUCT_ENTERPRISE = 0x00000004, ///< Enterprise
+ msPRODUCT_HOME_BASIC_N = 0x00000005, ///< Home Basic N
+ msPRODUCT_BUSINESS = 0x00000006, ///< Business
+ msPRODUCT_STANDARD_SERVER = 0x00000007, ///< Server Standard
+ msPRODUCT_DATACENTER_SERVER = 0x00000008, ///< Server Datacenter (full installation)
+ msPRODUCT_SMALLBUSINESS_SERVER = 0x00000009, ///< Windows Small Business Server
+ msPRODUCT_ENTERPRISE_SERVER = 0x0000000A, ///< Server Enterprise (full installation)
+ msPRODUCT_STARTER = 0x0000000B, ///< Starter
+ msPRODUCT_DATACENTER_SERVER_CORE = 0x0000000C, ///< Server Datacenter (core installation)
+ msPRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E, ///< Server Enterprise (core installation)
+ msPRODUCT_STANDARD_SERVER_CORE = 0x0000000D, ///< Server Standard (core installation)
+ msPRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F, ///< Server Enterprise for Itanium-based Systems
+ msPRODUCT_BUSINESS_N = 0x00000010, ///< Business N
+ msPRODUCT_WEB_SERVER = 0x00000011, ///< Web Server (full installation)
+ msPRODUCT_CLUSTER_SERVER = 0x00000012, ///< HPC Edition
+ msPRODUCT_HOME_SERVER = 0x00000013, ///< Windows Storage Server 2008 R2 Essentials
+ msPRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014, ///< Storage Server Express
+ msPRODUCT_STORAGE_STANDARD_SERVER = 0x00000015, ///< Storage Server Standard
+ msPRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016, ///< Storage Server Workgroup
+ msPRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017, ///< Storage Server Enterprise
+ msPRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018, ///< Windows Server 2008 for Windows Essential Server Solutions
+ msPRODUCT_SMALLBUSINESS_SERVER_PREMIUM = 0x00000019, ///< Small Business Server Premium
+ msPRODUCT_HOME_PREMIUM_N = 0x0000001A, ///< Home Premium N
+ msPRODUCT_ENTERPRISE_N = 0x0000001B, ///< Enterprise N
+ msPRODUCT_ULTIMATE_N = 0x0000001C, ///< Ultimate N
+ msPRODUCT_WEB_SERVER_CORE = 0x0000001D, ///< Web Server (core installation)
+ msPRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E, ///< Windows Essential Business Server Management Server
+ msPRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F, ///< Windows Essential Business Server Security Server
+ msPRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020, ///< Windows Essential Business Server Messaging Server
+ msPRODUCT_SERVER_FOUNDATION = 0x00000021, ///< Server Foundation
+ msPRODUCT_HOME_PREMIUM_SERVER = 0x00000022, ///< Windows Home Server 2011
+ msPRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023, ///< Windows Server 2008 without Hyper-V for Windows Essential Server Solutions
+ msPRODUCT_STANDARD_SERVER_V = 0x00000024, ///< Server Standard without Hyper-V
+ msPRODUCT_DATACENTER_SERVER_V = 0x00000025, ///< Server Datacenter without Hyper-V (full installation)
+ msPRODUCT_ENTERPRISE_SERVER_V = 0x00000026, ///< Server Enterprise without Hyper-V (full installation)
+ msPRODUCT_DATACENTER_SERVER_CORE_V = 0x00000027, ///< Server Datacenter without Hyper-V (core installation)
+ msPRODUCT_STANDARD_SERVER_CORE_V = 0x00000028, ///< Server Standard without Hyper-V (core installation)
+ msPRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029, ///< Server Enterprise without Hyper-V (core installation)
+ msPRODUCT_HYPERV = 0x0000002A, ///< Microsoft Hyper-V Server
+ msPRODUCT_STORAGE_EXPRESS_SERVER_CORE = 0x0000002B, ///< Storage Server Express (core installation)
+ msPRODUCT_STORAGE_STANDARD_SERVER_CORE = 0x0000002C, ///< Storage Server Standard (core installation)
+ msPRODUCT_STORAGE_WORKGROUP_SERVER_CORE = 0x0000002D, ///< Storage Server Workgroup (core installation)
+ msPRODUCT_STORAGE_ENTERPRISE_SERVER_CORE = 0x0000002E, ///< Storage Server Enterprise (core installation)
+ msPRODUCT_STARTER_N = 0x0000002F, ///< Starter N
+ msPRODUCT_PROFESSIONAL = 0x00000030, ///< Professional
+ msPRODUCT_PROFESSIONAL_N = 0x00000031, ///< Professional N
+ msPRODUCT_SB_SOLUTION_SERVER = 0x00000032, ///< Windows Small Business Server 2011 Essentials
+ msPRODUCT_SERVER_FOR_SB_SOLUTIONS = 0x00000033, ///< Server For SB Solutions
+ msPRODUCT_STANDARD_SERVER_SOLUTIONS = 0x00000034, ///< Server Solutions Premium
+ msPRODUCT_STANDARD_SERVER_SOLUTIONS_CORE = 0x00000035, ///< Server Solutions Premium (core installation)
+ msPRODUCT_SB_SOLUTION_SERVER_EM = 0x00000036, ///< Server For SB Solutions EM
+ msPRODUCT_SERVER_FOR_SB_SOLUTIONS_EM = 0x00000037, ///< Server For SB Solutions EM
+ msPRODUCT_SOLUTION_EMBEDDEDSERVER = 0x00000038, ///< Windows MultiPoint Server
+ msPRODUCT_ESSENTIALBUSINESS_SERVER_MGMT = 0x0000003B, ///< Windows Essential Server Solution Management
+ msPRODUCT_ESSENTIALBUSINESS_SERVER_ADDL = 0x0000003C, ///< Windows Essential Server Solution Additional
+ msPRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC = 0x0000003D, ///< Windows Essential Server Solution Management SVC
+ msPRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC = 0x0000003E, ///< Windows Essential Server Solution Additional SVC
+ msPRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE = 0x0000003F, ///< Small Business Server Premium (core installation)
+ msPRODUCT_CLUSTER_SERVER_V = 0x00000040, ///< Server Hyper Core V
+ msPRODUCT_STARTER_E = 0x00000042, ///< Not supported
+ msPRODUCT_HOME_BASIC_E = 0x00000043, ///< Not supported
+ msPRODUCT_HOME_PREMIUM_E = 0x00000044, ///< Not supported
+ msPRODUCT_PROFESSIONAL_E = 0x00000045, ///< Not supported
+ msPRODUCT_ENTERPRISE_E = 0x00000046, ///< Not supported
+ msPRODUCT_ULTIMATE_E = 0x00000047, ///< Not supported
+ msPRODUCT_ENTERPRISE_EVALUATION = 0x00000048, ///< Server Enterprise (evaluation installation)
+ msPRODUCT_MULTIPOINT_STANDARD_SERVER = 0x0000004C, ///< Windows MultiPoint Server Standard (full installation)
+ msPRODUCT_MULTIPOINT_PREMIUM_SERVER = 0x0000004D, ///< Windows MultiPoint Server Premium (full installation)
+ msPRODUCT_STANDARD_EVALUATION_SERVER = 0x0000004F, ///< Server Standard (evaluation installation)
+ msPRODUCT_DATACENTER_EVALUATION_SERVER = 0x00000050, ///< Server Datacenter (evaluation installation)
+ msPRODUCT_ENTERPRISE_N_EVALUATION = 0x00000054, ///< Enterprise N (evaluation installation)
+ msPRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER = 0x0000005F, ///< Storage Server Workgroup (evaluation installation)
+ msPRODUCT_STORAGE_STANDARD_EVALUATION_SERVER = 0x00000060, ///< Storage Server Standard (evaluation installation)
+ msPRODUCT_CORE_N = 0x00000062, ///< Windows 8 N
+ msPRODUCT_CORE_COUNTRYSPECIFIC = 0x00000063, ///< Windows 8 China
+ msPRODUCT_CORE_SINGLELANGUAGE = 0x00000064, ///< Windows 8 Single Language
+ msPRODUCT_CORE = 0x00000065, ///< Windows 8
+ msPRODUCT_PROFESSIONAL_WMC = 0x00000067, ///< Professional with Media Center
+};
+
+/**
+ * Values to be used with GetSystemMetrics.
+ *
+ * Values from http://msdn.microsoft.com/en-us/library/windows/desktop/ms724385%28v=vs.85%29.aspx
+ */
+enum windows_metrics {
+ msSM_SERVERR2 = 89, ///< Obtains the build number if the system is Windows Server 2003 R2; otherwise, 0.
+};
+
+/**
+ * Values to be used with OSVERSIONINFOEX.wSuiteMask.
+ *
+ * Values from http://msdn.microsoft.com/en-us/library/windows/desktop/ms724833%28v=vs.85%29.aspx
+ */
+enum windows_ver_suite {
+ msVER_SUITE_BLADE = 0x00000400, ///< Windows Server 2003, Web Edition is installed.
+ msVER_SUITE_STORAGE_SERVER = 0x00002000, ///< Windows Storage Server 2003 R2 or Windows Storage Server 2003 is installed.
+ msVER_SUITE_COMPUTE_SERVER = 0x00004000, ///< Windows Server 2003, Compute Cluster Edition is installed.
+ msVER_SUITE_WH_SERVER = 0x00008000, ///< Windows Home Server is installed.
+};
+
+#else // not WIN32
+// UNIX. Use build-time cached values
+#include "sysinfo.inc"
+#endif // WIN32
+
+// Compiler detection <http://sourceforge.net/p/predef/wiki/Compilers/>
+#if defined(__BORLANDC__)
+#define SYSINFO_COMPILER "Borland C++"
+#elif defined(__clang__)
+#define SYSINFO_COMPILER "Clang v" EXPAND_AND_QUOTE(__clang_major__) "." EXPAND_AND_QUOTE(__clang_minor__) "." EXPAND_AND_QUOTE(__clang_patchlevel__)
+#elif defined(__INTEL_COMPILER)
+#define SYSINFO_COMPILER "Intel CC v" EXPAND_AND_QUOTE(__INTEL_COMPILER)
+#elif defined(__MINGW32__)
+#if defined(__MINGW64__)
+#define SYSINFO_COMPILER "MinGW-w64 64 Bit v" EXPAND_AND_QUOTE(__MINGW64_VERSION_MAJOR) "." EXPAND_AND_QUOTE(__MINGW64_VERSION_MINOR) \
+ " (MinGW " EXPAND_AND_QUOTE(__MINGW32_MAJOR_VERSION) "." EXPAND_AND_QUOTE(__MINGW32_MINOR_VERSION) ")"
+#elif defined(__MINGW64_VERSION_MAJOR)
+#define SYSINFO_COMPILER "MinGW-w64 32 Bit v" EXPAND_AND_QUOTE(__MINGW64_VERSION_MAJOR) "." EXPAND_AND_QUOTE(__MINGW64_VERSION_MINOR) \
+ " (MinGW " EXPAND_AND_QUOTE(__MINGW32_MAJOR_VERSION) "." EXPAND_AND_QUOTE(__MINGW32_MINOR_VERSION) ")"
+#else
+#define SYSINFO_COMPILER "MinGW32 v" EXPAND_AND_QUOTE(__MINGW32_MAJOR_VERSION) "." EXPAND_AND_QUOTE(__MINGW32_MINOR_VERSION)
+#endif
+#elif defined(__GNUC__)
+#define SYSINFO_COMPILER "GCC v" EXPAND_AND_QUOTE(__GNUC__) "." EXPAND_AND_QUOTE(__GNUC_MINOR__) "." EXPAND_AND_QUOTE(__GNUC_PATCHLEVEL__)
+#elif defined(_MSC_VER)
+#if _MSC_VER >= 1300 && _MSC_VER < 1310
+#define SYSINFO_COMPILER "Microsoft Visual C++ 7.0 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#elif _MSC_VER >= 1310 && _MSC_VER < 1400
+#define SYSINFO_COMPILER "Microsoft Visual C++ 2003 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#elif _MSC_VER >= 1400 && _MSC_VER < 1500
+#define SYSINFO_COMPILER "Microsoft Visual C++ 2005 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#elif _MSC_VER >= 1500 && _MSC_VER < 1600
+#define SYSINFO_COMPILER "Microsoft Visual C++ 2008 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#elif _MSC_VER >= 1600 && _MSC_VER < 1700
+#define SYSINFO_COMPILER "Microsoft Visual C++ 2010 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#elif _MSC_VER >= 1700 && _MSC_VER < 1800
+#define SYSINFO_COMPILER "Microsoft Visual C++ 2012 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#elif _MSC_VER >= 1800 && _MSC_VER < 1900
+#define SYSINFO_COMPILER "Microsoft Visual C++ 2013 (v" EXPAND_AND_QUOTE(_MSC_VER) ")"
+#else // < 1300 || >= 1900
+#define SYSINFO_COMPILER "Microsoft Visual C++ v" EXPAND_AND_QUOTE(_MSC_VER)
+#endif
+#else
+#define SYSINFO_COMPILER "Unknown"
+#endif
+// end compiler detection
+
+/**
+ * Retrieves the current SVN revision.
+ *
+ * @param out[out] a string pointer to return the value (to be aFree()'d.)
+ * @retval true if a revision was correctly detected.
+ * @retval false if no revision was detected. out is set to NULL in this case.
+ */
+bool sysinfo_svn_get_revision(char **out) {
+ // Only include SVN support if detected it, or we're on MSVC
+#if !defined(SYSINFO_VCSTYPE) || SYSINFO_VCSTYPE == VCSTYPE_SVN || SYSINFO_VCSTYPE == VCSTYPE_UNKNOWN
+ FILE *fp;
+
+ // subversion 1.7 uses a sqlite3 database
+ // FIXME this is hackish at best...
+ // - ignores database file structure
+ // - assumes the data in NODES.dav_cache column ends with "!svn/ver/<revision>/<path>)"
+ // - since it's a cache column, the data might not even exist
+ if ((fp = fopen(".svn"PATHSEP_STR"wc.db", "rb")) != NULL || (fp = fopen(".."PATHSEP_STR".svn"PATHSEP_STR"wc.db", "rb")) != NULL) {
+
+#ifndef SVNNODEPATH //not sure how to handle branches, so I'll leave this overridable define until a better solution comes up
+#define SVNNODEPATH trunk
+#endif // SVNNODEPATH
+
+ const char* prefix = "!svn/ver/";
+ const char* postfix = "/"EXPAND_AND_QUOTE(SVNNODEPATH)")"; // there should exist only 1 entry like this
+ size_t prefix_len = strlen(prefix);
+ size_t postfix_len = strlen(postfix);
+ size_t i,j,flen;
+ char* buffer;
+
+ // read file to buffer
+ fseek(fp, 0, SEEK_END);
+ flen = ftell(fp);
+ buffer = (char*)aMalloc(flen + 1);
+ fseek(fp, 0, SEEK_SET);
+ flen = fread(buffer, 1, flen, fp);
+ buffer[flen] = '\0';
+ fclose(fp);
+
+ // parse buffer
+ for (i = prefix_len + 1; i + postfix_len <= flen; ++i) {
+ if (buffer[i] != postfix[0] || memcmp(buffer + i, postfix, postfix_len) != 0)
+ continue; // postfix mismatch
+ for (j = i; j > 0; --j) { // skip digits
+ if (!ISDIGIT(buffer[j - 1]))
+ break;
+ }
+ if (memcmp(buffer + j - prefix_len, prefix, prefix_len) != 0)
+ continue; // prefix mismatch
+ // done
+ if (*out != NULL)
+ aFree(*out);
+ *out = aCalloc(1, 8);
+ snprintf(*out, 8, "%d", atoi(buffer + j));
+ break;
+ }
+ aFree(buffer);
+
+ if (*out != NULL)
+ return true;
+ }
+
+ // subversion 1.6 and older?
+ if ((fp = fopen(".svn/entries", "r")) != NULL) {
+ char line[1024];
+ int rev;
+ // Check the version
+ if (fgets(line, sizeof(line), fp)) {
+ if (!ISDIGIT(line[0])) {
+ // XML File format
+ while (fgets(line,sizeof(line),fp))
+ if (strstr(line,"revision=")) break;
+ if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) {
+ if (*out != NULL)
+ aFree(*out);
+ *out = aCalloc(1, 8);
+ snprintf(*out, 8, "%d", rev);
+ }
+ } else {
+ // Bin File format
+ if (fgets(line, sizeof(line), fp) == NULL) { printf("Can't get bin name\n"); } // Get the name
+ if (fgets(line, sizeof(line), fp) == NULL) { printf("Can't get entries kind\n"); } // Get the entries kind
+ if (fgets(line, sizeof(line), fp)) { // Get the rev numver
+ if (*out != NULL)
+ aFree(*out);
+ *out = aCalloc(1, 8);
+ snprintf(*out, 8, "%d", atoi(line));
+ }
+ }
+ }
+ fclose(fp);
+
+ if (*out != NULL)
+ return true;
+ }
+#endif
+ if (*out != NULL)
+ aFree(*out);
+ *out = NULL;
+ return false;
+}
+
+/**
+ * Retrieves the current Git revision.
+ *
+ * @param out[out] a string pointer to return the value (to be aFree()'d.)
+ * @retval true if a revision was correctly detected.
+ * @retval false if no revision was detected. out is set to NULL in this case.
+ */
+bool sysinfo_git_get_revision(char **out) {
+ // Only include Git support if we detected it, or we're on MSVC
+#if !defined(SYSINFO_VCSTYPE) || SYSINFO_VCSTYPE == VCSTYPE_GIT || SYSINFO_VCSTYPE == VCSTYPE_UNKNOWN
+ FILE *fp;
+ char ref[128], filepath[128], line[128];
+
+ strcpy(ref, "HEAD");
+
+ while (*ref) {
+ snprintf(filepath, sizeof(filepath), ".git/%s", ref);
+ if ((fp = fopen(filepath, "r")) != NULL) {
+ if (fgets(line, sizeof(line)-1, fp) == NULL) {
+ fclose(fp);
+ break;
+ }
+ fclose(fp);
+ if (sscanf(line, "ref: %127[^\n]", ref) == 1) {
+ continue;
+ } else if (sscanf(line, "%127[a-f0-9]", ref) == 1 && strlen(ref) == 40) {
+ if (*out != NULL)
+ aFree(*out);
+ *out = aStrdup(ref);
+ }
+ }
+ break;
+ }
+ if (*out != NULL)
+ return true;
+#else
+ if (*out != NULL)
+ aFree(*out);
+ *out = NULL;
+#endif
+ return false;
+}
+
+#ifdef WIN32
+
+/// Windows-specific runtime detection functions.
+
+typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+/**
+ * Retrieves the Operating System version (Windows only).
+ *
+ * Once retrieved, the version string is stored into sysinfo->p->osversion.
+ */
+void sysinfo_osversion_retrieve(void) {
+ OSVERSIONINFOEX osvi;
+ StringBuf buf;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ StrBuf->Init(&buf);
+
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ if (sysinfo->p->osversion != NULL) {
+ aFree(sysinfo->p->osversion);
+ sysinfo->p->osversion = NULL;
+ }
+
+ /*
+ * #pragma rantmode (on)
+ * Some engineer at Microsoft moronically decided that, since some applications use this information to do version checks and refuse to
+ * run if they detect a new, unknown version of Windows, now nobody will be able to rely on this information anymore, not even those who
+ * need it for reporting or logging.
+ * The correct fix was to let those applications break, and their developer fix them (and in the meanwhile let the users use the
+ * Compatibility settings to run them) but no, they decided they'd deprecate the API, and make it lie for those who use it, reporting
+ * windows 8 even if they're running on 8.1 or newer.
+ * The API wasn't broken, applications were. Now we have broken applications, and a broken API. Great move, Microsoft. Oh right,
+ * there's the Version API helper functions. Or maybe not, since you can only do 'are we running on at least version X?' checks with
+ * those, it's not what we need.
+ * You know what? I'll just silence your deprecation warning for the time being. Maybe by the time you release the next version of
+ * Windows, you'll have provided a less crippled API or something.
+ * #pragma rantmode (off)
+ */
+#pragma warning (push)
+#pragma warning (disable : 4996)
+ if (!GetVersionEx((OSVERSIONINFO*) &osvi)) {
+ sysinfo->p->osversion = aStrdup("Unknown Version");
+ return;
+ }
+#pragma warning (pop)
+
+ if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId // Windows NT Family
+ && ((osvi.dwMajorVersion > 4 && osvi.dwMajorVersion < 6) || (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 3)) // Between XP and 8.1
+ ) {
+ if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 3) { // Between Vista and 8.1
+ PGPI pGPI;
+ DWORD dwType;
+ if (osvi.dwMinorVersion == 0) {
+ StrBuf->AppendStr(&buf, osvi.wProductType == VER_NT_WORKSTATION ? "Windows Vista" : "Windows Server 2008");
+ } else if (osvi.dwMinorVersion == 1) {
+ StrBuf->AppendStr(&buf, osvi.wProductType == VER_NT_WORKSTATION ? "Windows 7" : "Windows Server 2008 R2");
+ } else {
+ // If it's 2, it can be Windows 8, or any newer version (8.1 at the time of writing this) -- see above for the reason.
+ switch (osvi.dwMinorVersion) {
+ case 2:
+ {
+ ULONGLONG mask = 0;
+ OSVERSIONINFOEX osvi2;
+ ZeroMemory(&osvi2, sizeof(OSVERSIONINFOEX));
+ osvi2.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ osvi2.dwMajorVersion = 6;
+ osvi2.dwMinorVersion = 2;
+ VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_LESS_EQUAL);
+ VER_SET_CONDITION(mask, VER_MINORVERSION, VER_LESS_EQUAL);
+ if (VerifyVersionInfo(&osvi2, VER_MAJORVERSION | VER_MINORVERSION, mask)) {
+ StrBuf->AppendStr(&buf, osvi.wProductType == VER_NT_WORKSTATION ? "Windows 8" : "Windows Server 2012");
+ break;
+ }
+ }
+ case 3:
+ StrBuf->AppendStr(&buf, osvi.wProductType == VER_NT_WORKSTATION ? "Windows 8.1" : "Windows Server 2012 R2");
+ }
+ }
+
+ pGPI = (PGPI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
+
+ pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
+
+ switch (dwType) {
+ case msPRODUCT_ULTIMATE:
+ case msPRODUCT_ULTIMATE_N:
+ StrBuf->AppendStr(&buf, " Ultimate");
+ break;
+ case msPRODUCT_PROFESSIONAL:
+ case msPRODUCT_PROFESSIONAL_N:
+ case msPRODUCT_PROFESSIONAL_WMC:
+ StrBuf->AppendStr(&buf, " Professional");
+ break;
+ case msPRODUCT_HOME_PREMIUM:
+ case msPRODUCT_HOME_PREMIUM_N:
+ StrBuf->AppendStr(&buf, " Home Premium");
+ break;
+ case msPRODUCT_HOME_BASIC:
+ case msPRODUCT_HOME_BASIC_N:
+ StrBuf->AppendStr(&buf, " Home Basic");
+ break;
+ case msPRODUCT_ENTERPRISE:
+ case msPRODUCT_ENTERPRISE_N:
+ case msPRODUCT_ENTERPRISE_SERVER:
+ case msPRODUCT_ENTERPRISE_SERVER_CORE:
+ case msPRODUCT_ENTERPRISE_SERVER_IA64:
+ case msPRODUCT_ENTERPRISE_SERVER_V:
+ case msPRODUCT_ENTERPRISE_SERVER_CORE_V:
+ case msPRODUCT_ENTERPRISE_EVALUATION:
+ case msPRODUCT_ENTERPRISE_N_EVALUATION:
+ StrBuf->AppendStr(&buf, " Enterprise");
+ break;
+ case msPRODUCT_BUSINESS:
+ case msPRODUCT_BUSINESS_N:
+ StrBuf->AppendStr(&buf, " Business");
+ break;
+ case msPRODUCT_STARTER:
+ case msPRODUCT_STARTER_N:
+ StrBuf->AppendStr(&buf, " Starter");
+ break;
+ case msPRODUCT_CLUSTER_SERVER:
+ case msPRODUCT_CLUSTER_SERVER_V:
+ StrBuf->AppendStr(&buf, " Cluster Server");
+ break;
+ case msPRODUCT_DATACENTER_SERVER:
+ case msPRODUCT_DATACENTER_SERVER_CORE:
+ case msPRODUCT_DATACENTER_SERVER_V:
+ case msPRODUCT_DATACENTER_SERVER_CORE_V:
+ case msPRODUCT_DATACENTER_EVALUATION_SERVER:
+ StrBuf->AppendStr(&buf, " Datacenter");
+ break;
+ case msPRODUCT_SMALLBUSINESS_SERVER:
+ case msPRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ case msPRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE:
+ StrBuf->AppendStr(&buf, " Small Business Server");
+ break;
+ case PRODUCT_STANDARD_SERVER:
+ case PRODUCT_STANDARD_SERVER_CORE:
+ case msPRODUCT_STANDARD_SERVER_V:
+ case msPRODUCT_STANDARD_SERVER_CORE_V:
+ case msPRODUCT_STANDARD_EVALUATION_SERVER:
+ StrBuf->AppendStr(&buf, " Standard");
+ break;
+ case msPRODUCT_WEB_SERVER:
+ case msPRODUCT_WEB_SERVER_CORE:
+ StrBuf->AppendStr(&buf, " Web Server");
+ break;
+ case msPRODUCT_STORAGE_EXPRESS_SERVER:
+ case msPRODUCT_STORAGE_STANDARD_SERVER:
+ case msPRODUCT_STORAGE_WORKGROUP_SERVER:
+ case msPRODUCT_STORAGE_ENTERPRISE_SERVER:
+ case msPRODUCT_STORAGE_EXPRESS_SERVER_CORE:
+ case msPRODUCT_STORAGE_STANDARD_SERVER_CORE:
+ case msPRODUCT_STORAGE_WORKGROUP_SERVER_CORE:
+ case msPRODUCT_STORAGE_ENTERPRISE_SERVER_CORE:
+ case msPRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER:
+ case msPRODUCT_STORAGE_STANDARD_EVALUATION_SERVER:
+ StrBuf->AppendStr(&buf, " Storage Server");
+ break;
+ case msPRODUCT_HOME_SERVER:
+ case msPRODUCT_SERVER_FOR_SMALLBUSINESS:
+ case msPRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT:
+ case msPRODUCT_MEDIUMBUSINESS_SERVER_SECURITY:
+ case msPRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING:
+ case msPRODUCT_SERVER_FOR_SMALLBUSINESS_V:
+ case msPRODUCT_SERVER_FOUNDATION:
+ case msPRODUCT_HOME_PREMIUM_SERVER:
+ case msPRODUCT_HYPERV:
+ case msPRODUCT_SB_SOLUTION_SERVER:
+ case msPRODUCT_SERVER_FOR_SB_SOLUTIONS:
+ case msPRODUCT_STANDARD_SERVER_SOLUTIONS:
+ case msPRODUCT_STANDARD_SERVER_SOLUTIONS_CORE:
+ case msPRODUCT_SB_SOLUTION_SERVER_EM:
+ case msPRODUCT_SERVER_FOR_SB_SOLUTIONS_EM:
+ case msPRODUCT_SOLUTION_EMBEDDEDSERVER:
+ case msPRODUCT_ESSENTIALBUSINESS_SERVER_MGMT:
+ case msPRODUCT_ESSENTIALBUSINESS_SERVER_ADDL:
+ case msPRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC:
+ case msPRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC:
+ case msPRODUCT_MULTIPOINT_STANDARD_SERVER:
+ case msPRODUCT_MULTIPOINT_PREMIUM_SERVER:
+ StrBuf->AppendStr(&buf, " Server (other)");
+ break;
+ case msPRODUCT_CORE_N:
+ case msPRODUCT_CORE_COUNTRYSPECIFIC:
+ case msPRODUCT_CORE_SINGLELANGUAGE:
+ case msPRODUCT_CORE:
+ StrBuf->AppendStr(&buf, " Workstation (other)");
+ break;
+ }
+
+ } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { // XP x64 and Server 2003
+ if (osvi.wProductType == VER_NT_WORKSTATION) {
+ StrBuf->AppendStr(&buf, "Windows XP Professional");
+ } else {
+ if (GetSystemMetrics(msSM_SERVERR2))
+ StrBuf->AppendStr(&buf, "Windows Server 2003 R2");
+ else if (osvi.wSuiteMask & msVER_SUITE_STORAGE_SERVER)
+ StrBuf->AppendStr(&buf, "Windows Storage Server 2003");
+ else if (osvi.wSuiteMask & msVER_SUITE_WH_SERVER)
+ StrBuf->AppendStr(&buf, "Windows Home Server");
+ else
+ StrBuf->AppendStr(&buf, "Windows Server 2003");
+
+ // Test for the server type.
+ if (osvi.wSuiteMask & msVER_SUITE_COMPUTE_SERVER)
+ StrBuf->AppendStr(&buf, " Compute Cluster");
+ else if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ StrBuf->AppendStr(&buf, " Datacenter");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ StrBuf->AppendStr(&buf, " Enterprise");
+ else if (osvi.wSuiteMask & msVER_SUITE_BLADE)
+ StrBuf->AppendStr(&buf, " Web");
+ else
+ StrBuf->AppendStr(&buf, " Standard");
+ }
+ } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { // XP
+ StrBuf->AppendStr(&buf, "Windows XP");
+ if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT)
+ StrBuf->AppendStr(&buf, " Embedded");
+ else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
+ StrBuf->AppendStr(&buf, " Home");
+ else
+ StrBuf->AppendStr(&buf, " Professional");
+ } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { // 2000
+ StrBuf->AppendStr(&buf, "Windows 2000");
+
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ StrBuf->AppendStr(&buf, " Professional");
+ else if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ StrBuf->AppendStr(&buf, " Datacenter Server");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ StrBuf->AppendStr(&buf, " Advanced Server");
+ else
+ StrBuf->AppendStr(&buf, " Server");
+ } else {
+ StrBuf->Printf(&buf, "Unknown Windows version %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
+ }
+ }
+
+ // Include service pack (if any) and build number.
+
+ if (strlen(osvi.szCSDVersion) > 0) {
+ StrBuf->Printf(&buf, " %s", osvi.szCSDVersion);
+ }
+
+ StrBuf->Printf(&buf, " (build %d)", osvi.dwBuildNumber);
+
+ sysinfo->p->osversion = aStrdup(StrBuf->Value(&buf));
+
+ StrBuf->Destroy(&buf);
+ return;
+}
+
+typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
+
+/**
+ * Retrieves SYSTEM_INFO (Windows only)
+ * System info is not stored anywhere after retrieval
+ * @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx
+ **/
+void sysinfo_systeminfo_retrieve( LPSYSTEM_INFO info ) {
+ PGNSI pGNSI;
+
+ // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
+ pGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo");
+ if (NULL != pGNSI)
+ pGNSI(info);
+ else
+ GetSystemInfo(info);
+
+ return;
+}
+
+/**
+ * Returns number of bytes in a memory page
+ * Only needed when compiling with MSVC
+ **/
+long sysinfo_getpagesize( void ) {
+ SYSTEM_INFO si;
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+
+ sysinfo_systeminfo_retrieve(&si);
+ return si.dwPageSize;
+}
+
+/**
+ * Retrieves the CPU type (Windows only).
+ *
+ * Once retrieved, the name is stored into sysinfo->p->cpu and the
+ * number of cores in sysinfo->p->cpucores.
+ */
+void sysinfo_cpu_retrieve(void) {
+ StringBuf buf;
+ SYSTEM_INFO si;
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+ StrBuf->Init(&buf);
+
+ if (sysinfo->p->cpu != NULL) {
+ aFree(sysinfo->p->cpu);
+ sysinfo->p->cpu = NULL;
+ }
+
+ sysinfo_systeminfo_retrieve(&si);
+
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL
+ || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64
+ ) {
+ StrBuf->Printf(&buf, "%s CPU, Family %d, Model %d, Stepping %d",
+ si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ? "x86" : "x86_64",
+ si.wProcessorLevel,
+ (si.wProcessorRevision&0xff00)>>8,
+ (si.wProcessorRevision&0xff));
+ } else {
+ StrBuf->AppendStr(&buf, "Unknown");
+ }
+
+ sysinfo->p->cpu = aStrdup(StrBuf->Value(&buf));
+ sysinfo->p->cpucores = si.dwNumberOfProcessors;
+
+ StrBuf->Destroy(&buf);
+}
+
+/**
+ * Retrieves the OS architecture (Windows only).
+ *
+ * Once retrieved, the name is stored into sysinfo->p->arch.
+ */
+void sysinfo_arch_retrieve(void) {
+ SYSTEM_INFO si;
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+
+ if (sysinfo->p->arch != NULL) {
+ aFree(sysinfo->p->arch);
+ sysinfo->p->arch = NULL;
+ }
+
+ sysinfo_systeminfo_retrieve(&si);
+
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) // x64
+ sysinfo->p->arch = aStrdup("x86_64");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) // x32
+ sysinfo->p->arch = aStrdup("x86");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM) // ARM
+ sysinfo->p->arch = aStrdup("ARM");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) // Itanium
+ sysinfo->p->arch = aStrdup("IA-64");
+ else
+ sysinfo->p->arch = aStrdup("Unknown");
+}
+
+/**
+ * Retrieves the startup-time VCS revision information.
+ *
+ * Once retrieved, the value is stored in sysinfo->p->vcsrevision_src.
+ */
+void sysinfo_vcsrevision_src_retrieve(void) {
+ if (sysinfo->p->vcsrevision_src != NULL) {
+ aFree(sysinfo->p->vcsrevision_src);
+ sysinfo->p->vcsrevision_src = NULL;
+ }
+ // Try Git, then SVN
+ if (sysinfo_git_get_revision(&sysinfo->p->vcsrevision_src)) {
+ sysinfo->p->vcstype = VCSTYPE_GIT;
+ return;
+ }
+ if (sysinfo_svn_get_revision(&sysinfo->p->vcsrevision_src)) {
+ sysinfo->p->vcstype = VCSTYPE_SVN;
+ return;
+ }
+ sysinfo->p->vcstype = VCSTYPE_NONE;
+ sysinfo->p->vcsrevision_src = aStrdup("Unknown");
+}
+#endif // WIN32
+
+/**
+ * Retrieves the VCS type name.
+ *
+ * Once retrieved, the value is stored in sysinfo->p->vcstype_name.
+ */
+void sysinfo_vcstype_name_retrieve(void) {
+ if (sysinfo->p->vcstype_name != NULL) {
+ aFree(sysinfo->p->vcstype_name);
+ sysinfo->p->vcstype_name = NULL;
+ }
+ switch (sysinfo->p->vcstype) {
+ case VCSTYPE_GIT:
+ sysinfo->p->vcstype_name = aStrdup("Git");
+ break;
+ case VCSTYPE_SVN:
+ sysinfo->p->vcstype_name = aStrdup("SVN");
+ break;
+ default:
+ sysinfo->p->vcstype_name = aStrdup("Exported");
+ break;
+ }
+}
+
+/**
+ * Returns the platform (OS type) this application is running on.
+ *
+ * This information is cached at compile time, since it's unlikely to change.
+ *
+ * @return the OS platform name.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "Linux", "Darwin", "Windows", etc.
+ */
+const char *sysinfo_platform(void) {
+ return sysinfo->p->platform;
+}
+
+/**
+ * Returns the Operating System version the application is running on.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time, since it is uncommon that an application is compiled and runs
+ * on different machines.
+ *
+ * @return the OS name.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "Windows 2008 Small Business Server", "OS X 10.8 Mountain Lion",
+ * "Gentoo Base System Release 2.2", "Debian GNU/Linux 6.0.6 (squeeze)", etc.
+ */
+const char *sysinfo_osversion(void) {
+ return sysinfo->p->osversion;
+}
+
+/**
+ * Returns the CPU model the application is running on.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time, since it is uncommon that an application is compiled and runs
+ * on different machines.
+ *
+ * @return the CPU model name.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "Intel(R) Atom(TM) CPU D2500 @ 1.86GHz",
+ * "Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz", "Intel Core i7",
+ * "x86 CPU, Family 6, Model 54, Stepping 1", etc.
+ */
+const char *sysinfo_cpu(void) {
+ return sysinfo->p->cpu;
+}
+
+/**
+ * Returns the number of CPU cores available.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time, since it is uncommon that an application is compiled and runs
+ * on different machines.
+ *
+ * @return the number of CPU cores.
+ */
+int sysinfo_cpucores(void) {
+ return sysinfo->p->cpucores;
+}
+
+/**
+ * Returns the CPU architecture the application was compiled for.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time, since it is uncommon that an application is compiled and runs
+ * on different machines.
+ *
+ * @return the CPU architecture name.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "x86", "x86_64", "IA-64", "ARM", etc.
+ */
+const char *sysinfo_arch(void) {
+ return sysinfo->p->arch;
+}
+
+/**
+ * Returns info about the 32 or 64 bit build of Hercules.
+ *
+ * @retval true if this is a 64 bit build.
+ * @retval false if this isn't a 64 bit build (i.e. it is a 32 bit build).
+ */
+bool sysinfo_is64bit(void) {
+#ifdef _LP64
+ return true;
+#else
+ return false;
+#endif
+}
+
+/**
+ * Returns the compiler the application was compiled with.
+ *
+ * @return the compiler name.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "Microsoft Visual C++ 2012 (v170050727)",
+ * "Clang v5.0.0", "MinGW32 v3.20", "GCC v4.7.3", etc.
+ */
+const char *sysinfo_compiler(void) {
+ return sysinfo->p->compiler;
+}
+
+/**
+ * Returns the compiler flags the application was compiled with.
+ *
+ * On Windows (MSVC), an empty string is returned instead.
+ *
+ * @return the compiler flags.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "-ggdb -O2 -flto -pipe -ffast-math ..."
+ */
+const char *sysinfo_cflags(void) {
+ return sysinfo->p->cflags;
+}
+
+/**
+ * Returns the Version Control System the application was downloaded with.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time. On Windows (MSVC), it is cached when the function is first
+ * called (most likely on server startup).
+ *
+ * @return the VCS type (numerical).
+ *
+ * @see VCSTYPE_NONE, VCSTYPE_GIT, VCSTYPE_SVN, VCSTYPE_UNKNOWN
+ */
+int sysinfo_vcstypeid(void) {
+ return sysinfo->p->vcstype;
+}
+
+/**
+ * Returns the Version Control System the application was downloaded with.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time. On Windows (MSVC), it is cached when the function is first
+ * called (most likely on server startup).
+ *
+ * @return the VCS type.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: "Git", "SVN", "Exported"
+ */
+const char *sysinfo_vcstype(void) {
+ return sysinfo->p->vcstype_name;
+}
+
+/**
+ * Returns the Version Control System revision.
+ *
+ * On platforms other than Windows (MSVC), this information is cached at
+ * compile time for better reliability. On Windows (MSVC), it is cached when
+ * the function is first called (most likely on server startup), so it may
+ * diverge from the actual revision that was compiled.
+ *
+ * @return the VCS revision.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: Git: "9128feccf3bddda94a7f8a170305565416815b40", SVN: "17546"
+ */
+const char *sysinfo_vcsrevision_src(void) {
+ return sysinfo->p->vcsrevision_src;
+}
+
+/**
+ * Returns the Version Control System revision.
+ *
+ * This information is cached during a script reload, so that it matches the
+ * version of the loaded scripts.
+ *
+ * @return the VCS revision.
+ *
+ * Note: Ownership is NOT transferred, the value should not be freed.
+ *
+ * Output example: Git: "9128feccf3bddda94a7f8a170305565416815b40", SVN: "17546"
+ */
+const char *sysinfo_vcsrevision_scripts(void) {
+ return sysinfo->p->vcsrevision_scripts;
+}
+
+/**
+ * Reloads the run-time (scripts) VCS revision information. To be used during
+ * script reloads to refresh the cached version.
+ */
+void sysinfo_vcsrevision_reload(void) {
+ if (sysinfo->p->vcsrevision_scripts != NULL) {
+ aFree(sysinfo->p->vcsrevision_scripts);
+ sysinfo->p->vcsrevision_scripts = NULL;
+ }
+ // Try Git, then SVN
+ if (sysinfo_git_get_revision(&sysinfo->p->vcsrevision_scripts)) {
+ return;
+ }
+ if (sysinfo_svn_get_revision(&sysinfo->p->vcsrevision_scripts)) {
+ return;
+ }
+ sysinfo->p->vcsrevision_scripts = aStrdup("Unknown");
+}
+
+/**
+ * Checks if we're running (unnecessarily) as superuser.
+ *
+ * @retval true if the current process is running as UNIX super-user.
+ * @retval false if the current process is running as regular user, or
+ * in any case under Windows.
+ */
+bool sysinfo_is_superuser(void) {
+#ifndef _WIN32
+ if (geteuid() == 0)
+ return true;
+#endif
+ return false;
+}
+
+/**
+ * Interface runtime initialization.
+ */
+void sysinfo_init(void) {
+ sysinfo->p->compiler = SYSINFO_COMPILER;
+#ifdef WIN32
+ sysinfo->p->platform = "Windows";
+ sysinfo->p->cflags = "N/A";
+ sysinfo_osversion_retrieve();
+ sysinfo_cpu_retrieve();
+ sysinfo_arch_retrieve();
+ sysinfo_vcsrevision_src_retrieve();
+#else
+ sysinfo->p->platform = SYSINFO_PLATFORM;
+ sysinfo->p->osversion = SYSINFO_OSVERSION;
+ sysinfo->p->cpucores = SYSINFO_CPUCORES;
+ sysinfo->p->cpu = SYSINFO_CPU;
+ sysinfo->p->arch = SYSINFO_ARCH;
+ sysinfo->p->cflags = SYSINFO_CFLAGS;
+ sysinfo->p->vcstype = SYSINFO_VCSTYPE;
+ sysinfo->p->vcsrevision_src = SYSINFO_VCSREV;
+#endif
+ sysinfo->vcsrevision_reload();
+ sysinfo_vcstype_name_retrieve(); // Must be called after setting vcstype
+}
+
+/**
+ * Interface shutdown cleanup.
+ */
+void sysinfo_final(void) {
+#ifdef WIN32
+ // Only need to be free'd in win32, they're #defined elsewhere
+ if (sysinfo->p->osversion)
+ aFree(sysinfo->p->osversion);
+ if (sysinfo->p->cpu)
+ aFree(sysinfo->p->cpu);
+ if (sysinfo->p->arch)
+ aFree(sysinfo->p->arch);
+ if (sysinfo->p->vcsrevision_src)
+ aFree(sysinfo->p->vcsrevision_src);
+#endif
+ sysinfo->p->platform = NULL;
+ sysinfo->p->osversion = NULL;
+ sysinfo->p->cpu = NULL;
+ sysinfo->p->arch = NULL;
+ sysinfo->p->vcsrevision_src = NULL;
+ sysinfo->p->cflags = NULL;
+ if (sysinfo->p->vcsrevision_scripts)
+ aFree(sysinfo->p->vcsrevision_scripts);
+ sysinfo->p->vcsrevision_scripts = NULL;
+ if (sysinfo->p->vcstype_name)
+ aFree(sysinfo->p->vcstype_name);
+ sysinfo->p->vcstype_name = NULL;
+}
+
+/**
+ * Interface default values initialization.
+ */
+void sysinfo_defaults(void) {
+ sysinfo = &sysinfo_s;
+ memset(&sysinfo_p, '\0', sizeof(sysinfo_p));
+ sysinfo->p = &sysinfo_p;
+#if defined(WIN32) && !defined(__CYGWIN__)
+ sysinfo->getpagesize = sysinfo_getpagesize;
+#else
+ sysinfo->getpagesize = getpagesize;
+#endif
+ sysinfo->platform = sysinfo_platform;
+ sysinfo->osversion = sysinfo_osversion;
+ sysinfo->cpu = sysinfo_cpu;
+ sysinfo->cpucores = sysinfo_cpucores;
+ sysinfo->arch = sysinfo_arch;
+ sysinfo->is64bit = sysinfo_is64bit;
+ sysinfo->compiler = sysinfo_compiler;
+ sysinfo->cflags = sysinfo_cflags;
+ sysinfo->vcstype = sysinfo_vcstype;
+ sysinfo->vcstypeid = sysinfo_vcstypeid;
+ sysinfo->vcsrevision_src = sysinfo_vcsrevision_src;
+ sysinfo->vcsrevision_scripts = sysinfo_vcsrevision_scripts;
+ sysinfo->vcsrevision_reload = sysinfo_vcsrevision_reload;
+ sysinfo->is_superuser = sysinfo_is_superuser;
+ sysinfo->init = sysinfo_init;
+ sysinfo->final = sysinfo_final;
+}
diff --git a/src/common/sysinfo.h b/src/common/sysinfo.h
new file mode 100644
index 000000000..600206a21
--- /dev/null
+++ b/src/common/sysinfo.h
@@ -0,0 +1,51 @@
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Base Author: Haru @ http://hercules.ws
+
+#ifndef COMMON_SYSINFO_H
+#define COMMON_SYSINFO_H
+
+/**
+ * Provides various bits of information about the system Hercules is running on
+ * (note: on unix systems, to avoid runtime detection, most of the data is
+ * cached at compile time)
+ */
+
+#include "../common/cbasetypes.h"
+
+struct sysinfo_private;
+
+/**
+ * sysinfo.c interface
+ **/
+struct sysinfo_interface {
+ struct sysinfo_private *p;
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+ long (*getpagesize) (void);
+#else
+ int (*getpagesize) (void);
+#endif
+ const char *(*platform) (void);
+ const char *(*osversion) (void);
+ const char *(*cpu) (void);
+ int (*cpucores) (void);
+ const char *(*arch) (void);
+ bool (*is64bit) (void);
+ const char *(*compiler) (void);
+ const char *(*cflags) (void);
+ const char *(*vcstype) (void);
+ int (*vcstypeid) (void);
+ const char *(*vcsrevision_src) (void);
+ const char *(*vcsrevision_scripts) (void);
+ void (*vcsrevision_reload) (void);
+ bool (*is_superuser) (void);
+ void (*init) (void);
+ void (*final) (void);
+};
+
+struct sysinfo_interface *sysinfo;
+
+void sysinfo_defaults(void);
+
+#endif /* COMMON_SYSINFO_H */
diff --git a/src/common/thread.c b/src/common/thread.c
index 315b310b2..1d0285302 100644
--- a/src/common/thread.c
+++ b/src/common/thread.c
@@ -6,30 +6,32 @@
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
+#define HERCULES_CORE
+
+#include "thread.h"
+
+#include "../common/sysinfo.h" // sysinfo->getpagesize()
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
#ifdef WIN32
-#include "../common/winapi.h"
-#define getpagesize() 4096 // @TODO: implement this properly (GetSystemInfo .. dwPageSize..). (Atm as on all supported win platforms its 4k its static.)
-#define __thread __declspec( thread )
+# include "../common/winapi.h"
+# define __thread __declspec( thread )
#else
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-#include <pthread.h>
-#include <sched.h>
+# include <pthread.h>
+# include <sched.h>
+# include <signal.h>
+# include <stdlib.h>
+# include <string.h>
+# include <unistd.h>
#endif
-#include "cbasetypes.h"
-#include "malloc.h"
-#include "showmsg.h"
-#include "thread.h"
-
// When Compiling using MSC (on win32..) we know we have support in any case!
-#ifdef _MSC_VER
-#define HAS_TLS
+#ifdef _MSC_VER
+#define HAS_TLS
#endif
-
#define RA_THREADS_MAX 64
struct rAthread {
@@ -37,7 +39,7 @@ struct rAthread {
RATHREAD_PRIO prio;
rAthreadProc proc;
- void *param;
+ void *param;
#ifdef WIN32
HANDLE hThread;
@@ -57,7 +59,7 @@ __thread int g_rathread_ID = -1;
///
static struct rAthread l_threads[RA_THREADS_MAX];
-void rathread_init(){
+void rathread_init(void) {
register unsigned int i;
memset(&l_threads, 0x00, RA_THREADS_MAX * sizeof(struct rAthread) );
@@ -65,7 +67,7 @@ void rathread_init(){
l_threads[i].myID = i;
}
- // now lets init thread id 0, which represnts the main thread
+ // now lets init thread id 0, which represents the main thread
#ifdef HAS_TLS
g_rathread_ID = 0;
#endif
@@ -76,12 +78,12 @@ void rathread_init(){
-void rathread_final(){
+void rathread_final(void) {
register unsigned int i;
- // Unterminated Threads Left?
- // Should'nt happen ..
- // Kill 'em all!
+ // Unterminated Threads Left?
+ // Shouldn't happen ..
+ // Kill 'em all!
//
for(i = 1; i < RA_THREADS_MAX; i++){
if(l_threads[i].proc != NULL){
@@ -96,35 +98,31 @@ void rathread_final(){
// gets called whenever a thread terminated ..
-static void rat_thread_terminated( rAthread handle ){
-
- int id_backup = handle->myID;
-
- // Simply set all members to 0 (except the id)
- memset(handle, 0x00, sizeof(struct rAthread));
-
- handle->myID = id_backup; // done ;)
-
+static void rat_thread_terminated(rAthread *handle) {
+ // Preserve handle->myID and handle->hThread, set everything else to its default value
+ handle->param = NULL;
+ handle->proc = NULL;
+ handle->prio = RAT_PRIO_NORMAL;
}//end: rat_thread_terminated()
#ifdef WIN32
-DWORD WINAPI _raThreadMainRedirector(LPVOID p){
+DWORD WINAPI raThreadMainRedirector(LPVOID p){
#else
-static void *_raThreadMainRedirector( void *p ){
+static void *raThreadMainRedirector( void *p ){
sigset_t set; // on Posix Thread platforms
#endif
void *ret;
// Update myID @ TLS to right id.
#ifdef HAS_TLS
- g_rathread_ID = ((rAthread)p)->myID;
+ g_rathread_ID = ((rAthread*)p)->myID;
#endif
#ifndef WIN32
// When using posix threads
- // the threads inherits the Signal mask from the thread which's spawned
+ // the threads inherits the Signal mask from the thread which spawned
// this thread
- // so we've to block everything we dont care about.
+ // so we've to block everything we don't care about.
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
@@ -135,19 +133,19 @@ static void *_raThreadMainRedirector( void *p ){
#endif
- ret = ((rAthread)p)->proc( ((rAthread)p)->param ) ;
+ ret = ((rAthread*)p)->proc( ((rAthread*)p)->param ) ;
-#ifdef WIN32
- CloseHandle( ((rAthread)p)->hThread );
+#ifdef WIN32
+ CloseHandle( ((rAthread*)p)->hThread );
#endif
- rat_thread_terminated( (rAthread)p );
+ rat_thread_terminated( (rAthread*)p );
#ifdef WIN32
return (DWORD)ret;
#else
return ret;
#endif
-}//end: _raThreadMainRedirector()
+}//end: raThreadMainRedirector()
@@ -155,28 +153,28 @@ static void *_raThreadMainRedirector( void *p ){
///
/// API Level
-///
-rAthread rathread_create( rAthreadProc entryPoint, void *param ){
+///
+rAthread *rathread_create(rAthreadProc entryPoint, void *param) {
return rathread_createEx( entryPoint, param, (1<<23) /*8MB*/, RAT_PRIO_NORMAL );
}//end: rathread_create()
-rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio ){
+rAthread *rathread_createEx(rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio) {
#ifndef WIN32
pthread_attr_t attr;
#endif
size_t tmp;
unsigned int i;
- rAthread handle = NULL;
+ rAthread *handle = NULL;
// given stacksize aligned to systems pagesize?
- tmp = szStack % getpagesize();
+ tmp = szStack % sysinfo->getpagesize();
if(tmp != 0)
szStack += tmp;
- // Get a free Thread Slot.
+ // Get a free Thread Slot.
for(i = 0; i < RA_THREADS_MAX; i++){
if(l_threads[i].proc == NULL){
handle = &l_threads[i];
@@ -195,12 +193,12 @@ rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szSta
handle->param = param;
#ifdef WIN32
- handle->hThread = CreateThread(NULL, szStack, _raThreadMainRedirector, (void*)handle, 0, NULL);
+ handle->hThread = CreateThread(NULL, szStack, raThreadMainRedirector, (void*)handle, 0, NULL);
#else
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, szStack);
- if(pthread_create(&handle->hThread, &attr, _raThreadMainRedirector, (void*)handle) != 0){
+ if(pthread_create(&handle->hThread, &attr, raThreadMainRedirector, (void*)handle) != 0){
handle->proc = NULL;
handle->param = NULL;
return NULL;
@@ -214,7 +212,7 @@ rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szSta
}//end: rathread_createEx
-void rathread_destroy ( rAthread handle ){
+void rathread_destroy(rAthread *handle) {
#ifdef WIN32
if( TerminateThread(handle->hThread, 0) != FALSE){
CloseHandle(handle->hThread);
@@ -223,24 +221,23 @@ void rathread_destroy ( rAthread handle ){
#else
if( pthread_cancel( handle->hThread ) == 0){
- // We have to join it, otherwise pthread wont re-cycle its internal ressources assoc. with this thread.
- //
+ // We have to join it, otherwise pthread wont re-cycle its internal resources assoc. with this thread.
pthread_join( handle->hThread, NULL );
- // Tell our manager to release ressources ;)
+ // Tell our manager to release resources ;)
rat_thread_terminated(handle);
}
#endif
}//end: rathread_destroy()
-rAthread rathread_self( ){
+rAthread *rathread_self(void) {
#ifdef HAS_TLS
- rAthread handle = &l_threads[g_rathread_ID];
+ rAthread *handle = &l_threads[g_rathread_ID];
- if(handle->proc != NULL) // entry point set, so its used!
+ if(handle->proc != NULL) // entry point set, so its used!
return handle;
#else
- // .. so no tls means we have to search the thread by its api-handle ..
+ // .. so no tls means we have to search the thread by its api-handle ..
int i;
#ifdef WIN32
@@ -258,16 +255,16 @@ rAthread rathread_self( ){
#endif
- return NULL;
+ return NULL;
}//end: rathread_self()
-int rathread_get_tid(){
+int rathread_get_tid(void) {
-#ifdef HAS_TLS
+#ifdef HAS_TLS
return g_rathread_ID;
#else
- // todo
+ // TODO
#ifdef WIN32
return (int)GetCurrentThreadId();
#else
@@ -279,7 +276,7 @@ int rathread_get_tid(){
}//end: rathread_get_tid()
-bool rathread_wait( rAthread handle, void* *out_exitCode ){
+bool rathread_wait(rAthread *handle, void **out_exitCode) {
// Hint:
// no thread data cleanup routine call here!
@@ -287,7 +284,7 @@ bool rathread_wait( rAthread handle, void* *out_exitCode ){
//
#ifdef WIN32
WaitForSingleObject(handle->hThread, INFINITE);
- return true;
+ return true;
#else
if(pthread_join(handle->hThread, out_exitCode) == 0)
return true;
@@ -297,21 +294,21 @@ bool rathread_wait( rAthread handle, void* *out_exitCode ){
}//end: rathread_wait()
-void rathread_prio_set( rAthread handle, RATHREAD_PRIO prio ){
- handle->prio = RAT_PRIO_NORMAL;
- //@TODO
+void rathread_prio_set(rAthread *handle, RATHREAD_PRIO prio) {
+ handle->prio = RAT_PRIO_NORMAL;
+ //@TODO
}//end: rathread_prio_set()
-RATHREAD_PRIO rathread_prio_get( rAthread handle){
+RATHREAD_PRIO rathread_prio_get(rAthread *handle) {
return handle->prio;
}//end: rathread_prio_get()
-void rathread_yield(){
-#ifdef WIN32
+void rathread_yield(void) {
+#ifdef WIN32
SwitchToThread();
#else
sched_yield();
-#endif
+#endif
}//end: rathread_yield()
diff --git a/src/common/thread.h b/src/common/thread.h
index a5a66e954..c7582366d 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -1,19 +1,18 @@
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
-#pragma once
-#ifndef _rA_THREAD_H_
-#define _rA_THREAD_H_
+#ifndef COMMON_THREAD_H
+#define COMMON_THREAD_H
#include "../common/cbasetypes.h"
-typedef struct rAthread *rAthread;
+typedef struct rAthread rAthread;
typedef void* (*rAthreadProc)(void*);
typedef enum RATHREAD_PRIO {
RAT_PRIO_LOW = 0,
RAT_PRIO_NORMAL,
- RAT_PRIO_HIGH
+ RAT_PRIO_HIGH
} RATHREAD_PRIO;
@@ -21,99 +20,99 @@ typedef enum RATHREAD_PRIO {
* Creates a new Thread
*
* @param entyPoint - entryProc,
- * @param param - general purpose parameter, would be given as parameter to the thread's entrypoint.
- *
+ * @param param - general purpose parameter, would be given as parameter to the thread's entry point.
+ *
* @return not NULL if success
*/
-rAthread rathread_create( rAthreadProc entryPoint, void *param );
+rAthread *rathread_create(rAthreadProc entryPoint, void *param);
-/**
+/**
* Creates a new Thread (with more creation options)
*
* @param entyPoint - entryProc,
- * @param param - general purpose parameter, would be given as parameter to the thread's entrypoint
- * @param szStack - stack Size in bytes
+ * @param param - general purpose parameter, would be given as parameter to the thread's entry point
+ * @param szStack - stack Size in bytes
* @param prio - Priority of the Thread @ OS Scheduler..
*
* @return not NULL if success
*/
-rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio );
+rAthread *rathread_createEx(rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio);
/**
- * Destroys the given Thread immediatly
+ * Destroys the given Thread immediately
*
- * @note The Handle gets invalid after call! dont use it afterwards.
+ * @note The Handle gets invalid after call! don't use it afterwards.
*
* @param handle - thread to destroy.
*/
-void rathread_destroy ( rAthread handle );
+void rathread_destroy(rAthread *handle);
-/**
+/**
* Returns the thread handle of the thread calling this function
- *
- * @note this wont work @ programms main thread
- * @note the underlying implementation might not perform very well, cache the value received!
- *
+ *
+ * @note this wont work @ programs main thread
+ * @note the underlying implementation might not perform very well, cache the value received!
+ *
* @return not NULL if success
*/
-rAthread rathread_self( );
+rAthread *rathread_self(void);
/**
- * Returns own thrad id (TID)
+ * Returns own thread id (TID)
*
- * @note this is an unique identifier for the calling thread, and
- * depends on platfrom / compiler, and may not be the systems Thread ID!
+ * @note this is an unique identifier for the calling thread, and
+ * depends on platform/ compiler, and may not be the systems Thread ID!
*
* @return -1 when fails, otherwise >= 0
*/
-int rathread_get_tid();
+int rathread_get_tid(void);
/**
- * Waits for the given thread to terminate
+ * Waits for the given thread to terminate
*
* @param handle - thread to wait (join) for
* @param out_Exitcode - [OPTIONAL] - if given => Exitcode (value) of the given thread - if it's terminated
- *
+ *
* @return true - if the given thread has been terminated.
*/
-bool rathread_wait( rAthread handle, void* *out_exitCode );
+bool rathread_wait(rAthread *handle, void **out_exitCode);
-/**
+/**
* Sets the given PRIORITY @ OS Task Scheduler
- *
+ *
* @param handle - thread to set prio for
* @param rio - the priority (RAT_PRIO_LOW ... )
*/
-void rathread_prio_set( rAthread handle, RATHREAD_PRIO prio );
+void rathread_prio_set(rAthread *handle, RATHREAD_PRIO prio);
-/**
- * Gets the current Prio of the given trhead
+/**
+ * Gets the current Prio of the given thread
*
* @param handle - the thread to get the prio for.
*/
-RATHREAD_PRIO rathread_prio_get( rAthread handle);
+RATHREAD_PRIO rathread_prio_get(rAthread *handle);
/**
* Tells the OS scheduler to yield the execution of the calling thread
- *
+ *
* @note: this will not "pause" the thread,
- * it just allows the OS to spent the remaining time
+ * it just allows the OS to spent the remaining time
* of the slice to another thread.
*/
-void rathread_yield();
+void rathread_yield(void);
-void rathread_init();
-void rathread_final();
+void rathread_init(void);
+void rathread_final(void);
-#endif
+#endif /* COMMON_THREAD_H */
diff --git a/src/common/timer.c b/src/common/timer.c
index 955a971c8..5d0a45b99 100644
--- a/src/common/timer.c
+++ b/src/common/timer.c
@@ -2,11 +2,8 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/cbasetypes.h"
-#include "../common/db.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/utils.h"
+#define HERCULES_CORE
+
#include "timer.h"
#include <stdio.h>
@@ -14,13 +11,21 @@
#include <string.h>
#include <time.h>
+#include "../common/cbasetypes.h"
+#include "../common/db.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/utils.h"
+
#ifdef WIN32
-#include "../common/winapi.h" // GetTickCount()
+# include "../common/winapi.h" // GetTickCount()
#else
-#include <unistd.h>
-#include <sys/time.h> // struct timeval, gettimeofday()
+# include <sys/time.h> // struct timeval, gettimeofday()
+# include <unistd.h>
#endif
+struct timer_interface timer_s;
+
// If the server can't handle processing thousands of monsters
// or many connected clients, please increase TIMER_MIN_INTERVAL.
#define TIMER_MIN_INTERVAL 50
@@ -63,8 +68,7 @@ struct timer_func_list {
} *tfl_root = NULL;
/// Sets the name of a timer function.
-int timer_add_func_list(TimerFunc func, char* name)
-{
+int timer_add_func_list(TimerFunc func, char* name) {
struct timer_func_list* tfl;
if (name) {
@@ -103,7 +107,7 @@ char* search_timer_func_list(TimerFunc func)
#if defined(ENABLE_RDTSC)
static uint64 RDTSC_BEGINTICK = 0, RDTSC_CLOCK = 0;
-static __inline uint64 _rdtsc(){
+static __inline uint64 rdtsc_(void) {
register union{
uint64 qw;
uint32 dw[2];
@@ -114,7 +118,7 @@ static __inline uint64 _rdtsc(){
return t.qw;
}
-static void rdtsc_calibrate(){
+static void rdtsc_calibrate(void){
uint64 t1, t2;
int32 i;
@@ -123,36 +127,76 @@ static void rdtsc_calibrate(){
RDTSC_CLOCK = 0;
for(i = 0; i < 5; i++){
- t1 = _rdtsc();
+ t1 = rdtsc_();
usleep(1000000); //1000 MS
- t2 = _rdtsc();
- RDTSC_CLOCK += (t2 - t1) / 1000;
+ t2 = rdtsc_();
+ RDTSC_CLOCK += (t2 - t1) / 1000;
}
RDTSC_CLOCK /= 5;
- RDTSC_BEGINTICK = _rdtsc();
+ RDTSC_BEGINTICK = rdtsc_();
ShowMessage(" done. (Frequency: %u Mhz)\n", (uint32)(RDTSC_CLOCK/1000) );
}
#endif
-/// platform-abstracted tick retrieval
-static unsigned int tick(void) {
+/**
+ * platform-abstracted tick retrieval
+ * @return server's current tick
+ */
+static int64 sys_tick(void) {
#if defined(WIN32)
- return GetTickCount();
+ // Windows: GetTickCount/GetTickCount64: Return the number of
+ // milliseconds that have elapsed since the system was started.
+
+ // TODO: GetTickCount/GetTickCount64 has a resolution of only 10~15ms.
+ // Ai4rei recommends that we replace it with either performance
+ // counters or multimedia timers if we want it to be more accurate.
+ // I'm leaving this for a future follow-up patch.
+
+ // GetTickCount64 is only available in Windows Vista / Windows Server
+ // 2008 or newer. Since we still support older versions, this runtime
+ // check is required in order not to crash.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724411%28v=vs.85%29.aspx
+ static bool first = true;
+ static ULONGLONG (WINAPI *pGetTickCount64)(void) = NULL;
+
+ if( first ) {
+ HMODULE hlib = GetModuleHandle(TEXT("KERNEL32.DLL"));
+ if( hlib != NULL )
+ pGetTickCount64 = (ULONGLONG (WINAPI *)(void))GetProcAddress(hlib, "GetTickCount64");
+ first = false;
+ }
+ if (pGetTickCount64)
+ return (int64)pGetTickCount64();
+ // 32-bit fall back. Note: This will wrap around every ~49 days since system startup!!!
+ return (int64)GetTickCount();
#elif defined(ENABLE_RDTSC)
- //
- return (unsigned int)((_rdtsc() - RDTSC_BEGINTICK) / RDTSC_CLOCK);
- //
+ // RDTSC: Returns the number of CPU cycles since reset. Unreliable if
+ // the CPU frequency is variable.
+ return (int64)((rdtsc_() - RDTSC_BEGINTICK) / RDTSC_CLOCK);
#elif defined(HAVE_MONOTONIC_CLOCK)
+ // Monotonic clock: Implementation-defined.
+ // Clock that cannot be set and represents monotonic time since some
+ // unspecified starting point. This clock is not affected by
+ // discontinuous jumps in the system time (e.g., if the system
+ // administrator manually changes the clock), but is affected by
+ // the incremental adjustments performed by adjtime(3) and NTP.
struct timespec tval;
clock_gettime(CLOCK_MONOTONIC, &tval);
- return tval.tv_sec * 1000 + tval.tv_nsec / 1000000;
+ // int64 cast to avoid overflows on platforms where time_t is 32 bit
+ return (int64)tval.tv_sec * 1000 + tval.tv_nsec / 1000000;
#else
+ // Fall back, regular clock: Number of milliseconds since epoch.
+ // The time returned by gettimeofday() is affected by discontinuous
+ // jumps in the system time (e.g., if the system administrator
+ // manually changes the system time). If you need a monotonically
+ // increasing clock, see clock_gettime(2).
struct timeval tval;
gettimeofday(&tval, NULL);
- return tval.tv_sec * 1000 + tval.tv_usec / 1000;
+ // int64 cast to avoid overflows on platforms where time_t is 32 bit
+ return (int64)tval.tv_sec * 1000 + tval.tv_usec / 1000;
#endif
}
@@ -160,29 +204,29 @@ static unsigned int tick(void) {
#if defined(TICK_CACHE) && TICK_CACHE > 1
//////////////////////////////////////////////////////////////////////////
// tick is cached for TICK_CACHE calls
-static unsigned int gettick_cache;
+static int64 gettick_cache;
static int gettick_count = 1;
-unsigned int timer_gettick_nocache(void) {
+int64 timer_gettick_nocache(void) {
gettick_count = TICK_CACHE;
- gettick_cache = tick();
+ gettick_cache = sys_tick();
return gettick_cache;
}
-unsigned int timer_gettick(void) {
+int64 timer_gettick(void) {
return ( --gettick_count == 0 ) ? gettick_nocache() : gettick_cache;
}
//////////////////////////////
#else
//////////////////////////////
// tick doesn't get cached
-unsigned int timer_gettick_nocache(void)
+int64 timer_gettick_nocache(void)
{
- return tick();
+ return sys_tick();
}
-unsigned int timer_gettick(void) {
- return tick();
+int64 timer_gettick(void) {
+ return sys_tick();
}
//////////////////////////////////////////////////////////////////////////
#endif
@@ -195,7 +239,7 @@ unsigned int timer_gettick(void) {
/// Adds a timer to the timer_heap
static void push_timer_heap(int tid) {
BHEAP_ENSURE(timer_heap, 1, 256);
- BHEAP_PUSH(timer_heap, tid, DIFFTICK_MINTOPCMP);
+ BHEAP_PUSH(timer_heap, tid, DIFFTICK_MINTOPCMP, swap);
}
/*==========================
@@ -235,7 +279,7 @@ static int acquire_timer(void) {
/// Starts a new timer that is deleted once it expires (single-use).
/// Returns the timer's id.
-int timer_add(unsigned int tick, TimerFunc func, int id, intptr_t data) {
+int timer_add(int64 tick, TimerFunc func, int id, intptr_t data) {
int tid;
tid = acquire_timer();
@@ -252,12 +296,12 @@ int timer_add(unsigned int tick, TimerFunc func, int id, intptr_t data) {
/// Starts a new timer that automatically restarts itself (infinite loop until manually removed).
/// Returns the timer's id, or INVALID_TIMER if it fails.
-int timer_add_interval(unsigned int tick, TimerFunc func, int id, intptr_t data, int interval)
-{
+int timer_add_interval(int64 tick, TimerFunc func, int id, intptr_t data, int interval) {
int tid;
- if( interval < 1 ) {
- ShowError("timer_add_interval: invalid interval (tick=%u %p[%s] id=%d data=%d diff_tick=%d)\n", tick, func, search_timer_func_list(func), id, data, DIFF_TICK(tick, iTimer->gettick()));
+ if (interval < 1) {
+ ShowError("timer_add_interval: invalid interval (tick=%"PRId64" %p[%s] id=%d data=%"PRIdPTR" diff_tick=%"PRId64")\n",
+ tick, func, search_timer_func_list(func), id, data, DIFF_TICK(tick, timer->gettick()));
return INVALID_TIMER;
}
@@ -299,13 +343,13 @@ int timer_do_delete(int tid, TimerFunc func) {
/// Adjusts a timer's expiration time.
/// Returns the new tick value, or -1 if it fails.
-int timer_addtick(int tid, unsigned int tick) {
- return iTimer->settick_timer(tid, timer_data[tid].tick+tick);
+int64 timer_addtick(int tid, int64 tick) {
+ return timer->settick(tid, timer_data[tid].tick+tick);
}
/// Modifies a timer's expiration time (an alternative to deleting a timer and starting a new one).
/// Returns the new tick value, or -1 if it fails.
-int timer_settick(int tid, unsigned int tick) {
+int64 timer_settick(int tid, int64 tick) {
size_t i;
// search timer position
@@ -315,23 +359,23 @@ int timer_settick(int tid, unsigned int tick) {
return -1;
}
- if( (int)tick == -1 )
- tick = 0;// add 1ms to avoid the error value -1
+ if( tick == -1 )
+ tick = 0; // add 1ms to avoid the error value -1
if( timer_data[tid].tick == tick )
- return (int)tick;// nothing to do, already in propper position
+ return tick; // nothing to do, already in proper position
// pop and push adjusted timer
- BHEAP_POPINDEX(timer_heap, i, DIFFTICK_MINTOPCMP);
+ BHEAP_POPINDEX(timer_heap, i, DIFFTICK_MINTOPCMP, swap);
timer_data[tid].tick = tick;
- BHEAP_PUSH(timer_heap, tid, DIFFTICK_MINTOPCMP);
- return (int)tick;
+ BHEAP_PUSH(timer_heap, tid, DIFFTICK_MINTOPCMP, swap);
+ return tick;
}
/// Executes all expired timers.
/// Returns the value of the smallest non-expired timer (or 1 second if there aren't any).
-int do_timer(unsigned int tick) {
- int diff = TIMER_MAX_INTERVAL; // return value
+int do_timer(int64 tick) {
+ int64 diff = TIMER_MAX_INTERVAL; // return value
// process all timers one by one
while( BHEAP_LENGTH(timer_heap) ) {
@@ -342,7 +386,7 @@ int do_timer(unsigned int tick) {
break; // no more expired timers to process
// remove timer
- BHEAP_POP(timer_heap, DIFFTICK_MINTOPCMP);
+ BHEAP_POP(timer_heap, DIFFTICK_MINTOPCMP, swap);
timer_data[tid].type |= TIMER_REMOVE_HEAP;
if( timer_data[tid].func ) {
@@ -379,7 +423,7 @@ int do_timer(unsigned int tick) {
}
}
- return cap_value(diff, TIMER_MIN_INTERVAL, TIMER_MAX_INTERVAL);
+ return (int)cap_value(diff, TIMER_MIN_INTERVAL, TIMER_MAX_INTERVAL);
}
unsigned long timer_get_uptime(void) {
@@ -410,25 +454,25 @@ void timer_final(void) {
if (free_timer_list) aFree(free_timer_list);
}
/*=====================================
-* Default Functions : timer.h
+* Default Functions : timer.h
* Generated by HerculesInterfaceMaker
* created by Susu
*-------------------------------------*/
void timer_defaults(void) {
- iTimer = &iTimer_s;
+ timer = &timer_s;
/* funcs */
- iTimer->gettick = timer_gettick;
- iTimer->gettick_nocache = timer_gettick_nocache;
- iTimer->add_timer = timer_add;
- iTimer->add_timer_interval = timer_add_interval;
- iTimer->add_timer_func_list = timer_add_func_list;
- iTimer->get_timer = timer_get;
- iTimer->delete_timer = timer_do_delete;
- iTimer->addtick_timer = timer_addtick;
- iTimer->settick_timer = timer_settick;
- iTimer->get_uptime = timer_get_uptime;
- iTimer->do_timer = do_timer;
- iTimer->init = timer_init;
- iTimer->final = timer_final;
+ timer->gettick = timer_gettick;
+ timer->gettick_nocache = timer_gettick_nocache;
+ timer->add = timer_add;
+ timer->add_interval = timer_add_interval;
+ timer->add_func_list = timer_add_func_list;
+ timer->get = timer_get;
+ timer->delete = timer_do_delete;
+ timer->addtick = timer_addtick;
+ timer->settick = timer_settick;
+ timer->get_uptime = timer_get_uptime;
+ timer->perform = do_timer;
+ timer->init = timer_init;
+ timer->final = timer_final;
}
diff --git a/src/common/timer.h b/src/common/timer.h
index 50f1c354b..d0927adde 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -1,13 +1,16 @@
// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#ifndef _TIMER_H_
-#define _TIMER_H_
+
+#ifndef COMMON_TIMER_H
+#define COMMON_TIMER_H
+
#include "../common/cbasetypes.h"
-#define DIFF_TICK(a,b) ((int)((a)-(b)))
+#define DIFF_TICK(a,b) ((a)-(b))
+#define DIFF_TICK32(a,b) ((int32)((a)-(b)))
-#define INVALID_TIMER -1
+#define INVALID_TIMER (-1)
// timer flags
enum {
@@ -18,51 +21,50 @@ enum {
// Struct declaration
-typedef int (*TimerFunc)(int tid, unsigned int tick, int id, intptr_t data);
+typedef int (*TimerFunc)(int tid, int64 tick, int id, intptr_t data);
struct TimerData {
- unsigned int tick;
+ int64 tick;
TimerFunc func;
- int type;
+ unsigned char type;
int interval;
- int heap_pos;
// general-purpose storage
- int id;
+ int id;
intptr_t data;
};
/*=====================================
-* Interface : timer.h
+* Interface : timer.h
* Generated by HerculesInterfaceMaker
* created by Susu
*-------------------------------------*/
struct timer_interface {
/* funcs */
- unsigned int (*gettick) (void);
- unsigned int (*gettick_nocache) (void);
+ int64 (*gettick) (void);
+ int64 (*gettick_nocache) (void);
- int (*add_timer) (unsigned int tick, TimerFunc func, int id, intptr_t data);
- int (*add_timer_interval) (unsigned int tick, TimerFunc func, int id, intptr_t data, int interval);
- const struct TimerData *(*get_timer) (int tid);
- int (*delete_timer) (int tid, TimerFunc func);
+ int (*add) (int64 tick, TimerFunc func, int id, intptr_t data);
+ int (*add_interval) (int64 tick, TimerFunc func, int id, intptr_t data, int interval);
+ const struct TimerData *(*get) (int tid);
+ int (*delete) (int tid, TimerFunc func);
- int (*addtick_timer) (int tid, unsigned int tick);
- int (*settick_timer) (int tid, unsigned int tick);
+ int64 (*addtick) (int tid, int64 tick);
+ int64 (*settick) (int tid, int64 tick);
- int (*add_timer_func_list) (TimerFunc func, char* name);
+ int (*add_func_list) (TimerFunc func, char* name);
unsigned long (*get_uptime) (void);
- int (*do_timer) (unsigned int tick);
+ int (*perform) (int64 tick);
void (*init) (void);
void (*final) (void);
-} iTimer_s;
+};
-struct timer_interface *iTimer;
+struct timer_interface *timer;
void timer_defaults(void);
-#endif /* _TIMER_H_ */
+#endif /* COMMON_TIMER_H */
diff --git a/src/common/utils.c b/src/common/utils.c
index 296df7e70..79232b25c 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -1,30 +1,37 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Portions Copyright (c) Athena Dev Teams
+
+#define HERCULES_CORE
-#include "../common/cbasetypes.h"
-#include "../common/mmo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "socket.h"
#include "utils.h"
-#include <stdio.h>
+#include <math.h> // floor()
#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <math.h> // floor()
+#include <sys/stat.h> // cache purposes [Ind/Hercules]
+
+#include "../common/cbasetypes.h"
+#include "../common/core.h"
+#include "../common/malloc.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h"
#ifdef WIN32
- #include "../common/winapi.h"
- #ifndef F_OK
- #define F_OK 0x0
- #endif /* F_OK */
+# include "../common/winapi.h"
+# ifndef F_OK
+# define F_OK 0x0
+# endif /* F_OK */
#else
- #include <unistd.h>
- #include <dirent.h>
- #include <sys/stat.h>
+# include <dirent.h>
+# include <sys/stat.h>
+# include <unistd.h>
#endif
+struct HCache_interface HCache_s;
/// Dumps given buffer into file pointed to by a handle.
void WriteDump(FILE* fp, const void* buffer, size_t length)
@@ -57,31 +64,27 @@ void WriteDump(FILE* fp, const void* buffer, size_t length)
/// Dumps given buffer on the console.
-void ShowDump(const void* buffer, size_t length)
-{
+void ShowDump(const void *buffer, size_t length) {
size_t i;
char hex[48+1], ascii[16+1];
ShowDebug("--- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F 0123456789ABCDEF\n");
ascii[16] = 0;
- for( i = 0; i < length; i++ )
- {
+ for (i = 0; i < length; i++) {
char c = RBUFB(buffer,i);
ascii[i%16] = ISCNTRL(c) ? '.' : c;
sprintf(hex+(i%16)*3, "%02X ", RBUFB(buffer,i));
- if( (i%16) == 15 )
- {
- ShowDebug("%03X %s %s\n", i/16, hex, ascii);
+ if ((i%16) == 15) {
+ ShowDebug("%03"PRIXS" %s %s\n", i/16, hex, ascii);
}
}
- if( (i%16) != 0 )
- {
+ if ((i%16) != 0) {
ascii[i%16] = 0;
- ShowDebug("%03X %-48s %-16s\n", i/16, hex, ascii);
+ ShowDebug("%03"PRIXS" %-48s %-16s\n", i/16, hex, ascii);
}
}
@@ -193,7 +196,7 @@ void findfile(const char *p, const char *pat, void (func)(const char*))
sprintf(tmppath,"%s%c%s",path, PATHSEP, entry->d_name);
- // check if the pattern matchs.
+ // check if the pattern matches.
if (entry->d_name && strstr(entry->d_name, pattern)) {
func( tmppath );
}
@@ -204,7 +207,7 @@ void findfile(const char *p, const char *pat, void (func)(const char*))
}
// is this a directory?
if (S_ISDIR(dir_stat.st_mode)) {
- // decent recursivly
+ // decent recursively
findfile(tmppath, pat, func);
}
}//end while
@@ -259,6 +262,58 @@ uint32 MakeDWord(uint16 word0, uint16 word1)
( (uint32)(word1 << 0x10) );
}
+/*************************************
+* Big-endian compatibility functions *
+*************************************/
+
+// Converts an int16 from current machine order to little-endian
+int16 MakeShortLE(int16 val)
+{
+ unsigned char buf[2];
+ buf[0] = (unsigned char)( (val & 0x00FF) );
+ buf[1] = (unsigned char)( (val & 0xFF00) >> 0x08 );
+ return *((int16*)buf);
+}
+
+// Converts an int32 from current machine order to little-endian
+int32 MakeLongLE(int32 val)
+{
+ unsigned char buf[4];
+ buf[0] = (unsigned char)( (val & 0x000000FF) );
+ buf[1] = (unsigned char)( (val & 0x0000FF00) >> 0x08 );
+ buf[2] = (unsigned char)( (val & 0x00FF0000) >> 0x10 );
+ buf[3] = (unsigned char)( (val & 0xFF000000) >> 0x18 );
+ return *((int32*)buf);
+}
+
+// Reads an uint16 in little-endian from the buffer
+uint16 GetUShort(const unsigned char* buf)
+{
+ return ( ((uint16)(buf[0])) )
+ |( ((uint16)(buf[1])) << 0x08 );
+}
+
+// Reads an uint32 in little-endian from the buffer
+uint32 GetULong(const unsigned char* buf)
+{
+ return ( ((uint32)(buf[0])) )
+ |( ((uint32)(buf[1])) << 0x08 )
+ |( ((uint32)(buf[2])) << 0x10 )
+ |( ((uint32)(buf[3])) << 0x18 );
+}
+
+// Reads an int32 in little-endian from the buffer
+int32 GetLong(const unsigned char* buf)
+{
+ return (int32)GetULong(buf);
+}
+
+// Reads a float (32 bits) from the buffer
+float GetFloat(const unsigned char* buf)
+{
+ uint32 val = GetULong(buf);
+ return *((float*)(void*)&val);
+}
/// calculates the value of A / B, in percent (rounded down)
unsigned int get_percentage(const unsigned int A, const unsigned int B)
@@ -267,7 +322,7 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B)
if( B == 0 )
{
- ShowError("get_percentage(): divison by zero! (A=%u,B=%u)\n", A, B);
+ ShowError("get_percentage(): division by zero! (A=%u,B=%u)\n", A, B);
return ~0U;
}
@@ -281,3 +336,114 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B)
return (unsigned int)floor(result);
}
+
+//-----------------------------------------------------
+// custom timestamp formatting (from eApp)
+//-----------------------------------------------------
+const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format)
+{
+ size_t len = strftime(str, size, format, localtime(&timestamp));
+ memset(str + len, '\0', size - len);
+ return str;
+}
+
+
+/* [Ind/Hercules] Caching */
+bool HCache_check(const char *file) {
+ struct stat bufa, bufb;
+ FILE *first, *second;
+ char s_path[255], dT[1];
+ time_t rtime;
+
+ if( !(first = fopen(file,"rb")) )
+ return false;
+
+ if( file[0] == '.' && file[1] == '/' )
+ file += 2;
+ else if( file[0] == '.' )
+ file++;
+
+ snprintf(s_path, 255, "./cache/%s", file);
+
+ if( !(second = fopen(s_path,"rb")) ) {
+ fclose(first);
+ return false;
+ }
+
+ if( fread(dT,sizeof(dT),1,second) != 1 || fread(&rtime,sizeof(rtime),1,second) != 1 || dT[0] != HCACHE_KEY || HCache->recompile_time > rtime ) {
+ fclose(first);
+ fclose(second);
+ return false;
+ }
+
+ fstat(fileno(first), &bufa);
+ fstat(fileno(second), &bufb);
+
+ fclose(first);
+ fclose(second);
+
+ if( bufa.st_mtime > bufb.st_mtime )
+ return false;
+
+ return true;
+}
+
+FILE *HCache_open(const char *file, const char *opt) {
+ FILE *first;
+ char s_path[255];
+
+ if( file[0] == '.' && file[1] == '/' )
+ file += 2;
+ else if( file[0] == '.' )
+ file++;
+
+ snprintf(s_path, 255, "./cache/%s", file);
+
+ if( !(first = fopen(s_path,opt)) ) {
+ return NULL;
+ }
+
+ if( opt[0] != 'r' ) {
+ char dT[1];/* 1-byte key to ensure our method is the latest, we can modify to ensure the method matches */
+ dT[0] = HCACHE_KEY;
+ hwrite(dT,sizeof(dT),1,first);
+ hwrite(&HCache->recompile_time,sizeof(HCache->recompile_time),1,first);
+ }
+ fseek(first, 20, SEEK_SET);/* skip first 20, might wanna store something else later */
+
+ return first;
+}
+void HCache_init(void) {
+ FILE *server;
+
+ if( (server = fopen(SERVER_NAME,"rb")) ) {
+ struct stat buf;
+
+ fstat(fileno(server), &buf);
+ HCache->recompile_time = buf.st_mtime;
+ fclose(server);
+
+ HCache->enabled = true;
+ } else
+ ShowWarning("Unable to open '%s', caching capabilities have been disabled!\n",SERVER_NAME);
+}
+/* transit to fread, shields vs warn_unused_result */
+size_t hread(void * ptr, size_t size, size_t count, FILE * stream) {
+ return fread(ptr, size, count, stream);
+}
+/* transit to fwrite, shields vs warn_unused_result */
+size_t hwrite(const void * ptr, size_t size, size_t count, FILE * stream) {
+ return fwrite(ptr, size, count, stream);
+}
+
+void HCache_defaults(void) {
+
+ HCache = &HCache_s;
+
+ HCache->init = HCache_init;
+
+ HCache->check = HCache_check;
+ HCache->open = HCache_open;
+ HCache->recompile_time = 0;
+ HCache->enabled = false;
+}
diff --git a/src/common/utils.h b/src/common/utils.h
index 8e39f2655..421698d95 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -1,11 +1,17 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+// Portions Copyright (c) Athena Dev Teams
-#ifndef _UTILS_H_
-#define _UTILS_H_
+#ifndef COMMON_UTILS_H
+#define COMMON_UTILS_H
-#include "../common/cbasetypes.h"
#include <stdio.h> // FILE*
+#include <time.h>
+
+#include "../common/cbasetypes.h"
+
+/* [HCache] 1-byte key to ensure our method is the latest, we can modify to ensure the method matches */
+#define HCACHE_KEY 'k'
// generate a hex dump of the first 'length' bytes of 'buffer'
void WriteDump(FILE* fp, const void* buffer, size_t length);
@@ -15,11 +21,13 @@ void findfile(const char *p, const char *pat, void (func)(const char*));
bool exists(const char* filename);
//Caps values to min/max
-#define cap_value(a, min, max) ((a >= max) ? max : (a <= min) ? min : a)
+#define cap_value(a, min, max) (((a) >= (max)) ? (max) : ((a) <= (min)) ? (min) : (a))
/// calculates the value of A / B, in percent (rounded down)
unsigned int get_percentage(const unsigned int A, const unsigned int B);
+const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format);
+
//////////////////////////////////////////////////////////////////////////
// byte word dword access [Shinomori]
//////////////////////////////////////////////////////////////////////////
@@ -29,4 +37,32 @@ extern uint16 GetWord(uint32 val, int idx);
extern uint16 MakeWord(uint8 byte0, uint8 byte1);
extern uint32 MakeDWord(uint16 word0, uint16 word1);
-#endif /* _UTILS_H_ */
+//////////////////////////////////////////////////////////////////////////
+// Big-endian compatibility functions
+//////////////////////////////////////////////////////////////////////////
+extern int16 MakeShortLE(int16 val);
+extern int32 MakeLongLE(int32 val);
+extern uint16 GetUShort(const unsigned char* buf);
+extern uint32 GetULong(const unsigned char* buf);
+extern int32 GetLong(const unsigned char* buf);
+extern float GetFloat(const unsigned char* buf);
+
+size_t hread(void * ptr, size_t size, size_t count, FILE * stream);
+size_t hwrite(const void * ptr, size_t size, size_t count, FILE * stream);
+
+/* [Ind/Hercules] Caching */
+struct HCache_interface {
+ void (*init) (void);
+ /* */
+ bool (*check) (const char *file);
+ FILE *(*open) (const char *file, const char *opt);
+ /* */
+ time_t recompile_time;
+ bool enabled;
+};
+
+struct HCache_interface *HCache;
+
+void HCache_defaults(void);
+
+#endif /* COMMON_UTILS_H */
diff --git a/src/common/winapi.h b/src/common/winapi.h
index 7ce555049..a8a38f30e 100644
--- a/src/common/winapi.h
+++ b/src/common/winapi.h
@@ -7,7 +7,7 @@
#define WINVER 0x0500
#define _WIN32_IE 0x0600
#define WIN32_LEAN_AND_MEAN
-#define NOCOMM
+#define NOCOMM
#define NOKANJI
#define NOHELP
#define NOMCX
@@ -24,6 +24,7 @@
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NONSTDC_NO_WARNINGS
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <io.h>
#include <Windows.h>
@@ -32,5 +33,3 @@
#include <Ws2tcpip.h>
#include <Mswsock.h>
#include <MMSystem.h>
-
-