diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/HPM.c | 10 | ||||
-rw-r--r-- | src/common/HPMDataCheck.h | 34 | ||||
-rw-r--r-- | src/common/HPMSymbols.inc.h | 38 | ||||
-rw-r--r-- | src/common/console.c | 38 | ||||
-rw-r--r-- | src/common/console.h | 16 | ||||
-rw-r--r-- | src/common/core.c | 97 | ||||
-rw-r--r-- | src/common/db.h | 10 | ||||
-rw-r--r-- | src/common/des.c | 155 | ||||
-rw-r--r-- | src/common/des.h | 45 | ||||
-rw-r--r-- | src/common/grfio.c | 493 | ||||
-rw-r--r-- | src/common/grfio.h | 108 | ||||
-rw-r--r-- | src/common/md5calc.c | 156 | ||||
-rw-r--r-- | src/common/md5calc.h | 44 | ||||
-rw-r--r-- | src/common/mmo.h | 77 | ||||
-rw-r--r-- | src/common/mutex.c | 158 | ||||
-rw-r--r-- | src/common/mutex.h | 168 | ||||
-rw-r--r-- | src/common/random.c | 62 | ||||
-rw-r--r-- | src/common/random.h | 84 | ||||
-rw-r--r-- | src/common/showmsg.c | 4 | ||||
-rw-r--r-- | src/common/showmsg.h | 4 | ||||
-rw-r--r-- | src/common/socket.c | 280 | ||||
-rw-r--r-- | src/common/spinlock.h | 89 | ||||
-rw-r--r-- | src/common/strlib.c | 1 | ||||
-rw-r--r-- | src/common/thread.c | 250 | ||||
-rw-r--r-- | src/common/thread.h | 232 |
25 files changed, 1751 insertions, 902 deletions
diff --git a/src/common/HPM.c b/src/common/HPM.c index 02b675704..8e0bbe992 100644 --- a/src/common/HPM.c +++ b/src/common/HPM.c @@ -389,12 +389,12 @@ bool hpm_add_arg(unsigned int pluginID, char *name, bool has_param, CmdlineExecF ARR_FIND(0, VECTOR_LENGTH(cmdline->args_data), i, strcmp(VECTOR_INDEX(cmdline->args_data, i).name, name) == 0); - if (i != VECTOR_LENGTH(cmdline->args_data)) { - ShowError("HPM:add_arg:%s duplicate! (from %s)\n",name,HPM->pid2name(pluginID)); - return false; - } + if (i != VECTOR_LENGTH(cmdline->args_data)) { + ShowError("HPM:add_arg:%s duplicate! (from %s)\n",name,HPM->pid2name(pluginID)); + return false; + } - return cmdline->arg_add(pluginID, name, '\0', func, help, has_param ? CMDLINE_OPT_PARAM : CMDLINE_OPT_NORMAL); + return cmdline->arg_add(pluginID, name, '\0', func, help, has_param ? CMDLINE_OPT_PARAM : CMDLINE_OPT_NORMAL); } /** diff --git a/src/common/HPMDataCheck.h b/src/common/HPMDataCheck.h index 3d25d7330..d0e23811c 100644 --- a/src/common/HPMDataCheck.h +++ b/src/common/HPMDataCheck.h @@ -23,6 +23,8 @@ * as it will get overwritten. */ +/* GENERATED FILE DO NOT EDIT */ + #ifndef HPM_DATA_CHECK_H #define HPM_DATA_CHECK_H @@ -148,7 +150,8 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #define COMMON_DB_H #endif // COMMON_DB_H #ifdef COMMON_DES_H - { "BIT64", sizeof(struct BIT64), SERVER_TYPE_ALL }, + { "des_bit64", sizeof(struct des_bit64), SERVER_TYPE_ALL }, + { "des_interface", sizeof(struct des_interface), SERVER_TYPE_ALL }, #else #define COMMON_DES_H #endif // COMMON_DES_H @@ -157,6 +160,11 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #else #define COMMON_ERS_H #endif // COMMON_ERS_H + #ifdef COMMON_GRFIO_H + { "grfio_interface", sizeof(struct grfio_interface), SERVER_TYPE_MAP }, + #else + #define COMMON_GRFIO_H + #endif // COMMON_GRFIO_H #ifdef COMMON_HPMI_H { "HPMi_interface", sizeof(struct HPMi_interface), SERVER_TYPE_ALL }, { "hplugin_info", sizeof(struct hplugin_info), SERVER_TYPE_ALL }, @@ -169,6 +177,11 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #else #define COMMON_MAPINDEX_H #endif // COMMON_MAPINDEX_H + #ifdef COMMON_MD5CALC_H + { "md5_interface", sizeof(struct md5_interface), SERVER_TYPE_ALL }, + #else + #define COMMON_MD5CALC_H + #endif // COMMON_MD5CALC_H #ifdef COMMON_MEMMGR_H { "malloc_interface", sizeof(struct malloc_interface), SERVER_TYPE_ALL }, #else @@ -208,11 +221,21 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #else #define COMMON_MMO_H #endif // COMMON_MMO_H + #ifdef COMMON_MUTEX_H + { "mutex_interface", sizeof(struct mutex_interface), SERVER_TYPE_ALL }, + #else + #define COMMON_MUTEX_H + #endif // COMMON_MUTEX_H #ifdef COMMON_NULLPO_H { "nullpo_interface", sizeof(struct nullpo_interface), SERVER_TYPE_ALL }, #else #define COMMON_NULLPO_H #endif // COMMON_NULLPO_H + #ifdef COMMON_RANDOM_H + { "rnd_interface", sizeof(struct rnd_interface), SERVER_TYPE_ALL }, + #else + #define COMMON_RANDOM_H + #endif // COMMON_RANDOM_H #ifdef COMMON_SHOWMSG_H { "showmsg_interface", sizeof(struct showmsg_interface), SERVER_TYPE_ALL }, #else @@ -228,7 +251,7 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #define COMMON_SOCKET_H #endif // COMMON_SOCKET_H #ifdef COMMON_SPINLOCK_H - { "SPIN_LOCK", sizeof(struct SPIN_LOCK), SERVER_TYPE_ALL }, + { "spin_lock", sizeof(struct spin_lock), SERVER_TYPE_ALL }, #else #define COMMON_SPINLOCK_H #endif // COMMON_SPINLOCK_H @@ -251,6 +274,11 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #else #define COMMON_SYSINFO_H #endif // COMMON_SYSINFO_H + #ifdef COMMON_THREAD_H + { "thread_interface", sizeof(struct thread_interface), SERVER_TYPE_ALL }, + #else + #define COMMON_THREAD_H + #endif // COMMON_THREAD_H #ifdef COMMON_TIMER_H { "TimerData", sizeof(struct TimerData), SERVER_TYPE_ALL }, { "timer_interface", sizeof(struct timer_interface), SERVER_TYPE_ALL }, @@ -648,6 +676,7 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { { "casecheck_data", sizeof(struct casecheck_data), SERVER_TYPE_MAP }, { "reg_db", sizeof(struct reg_db), SERVER_TYPE_MAP }, { "script_array", sizeof(struct script_array), SERVER_TYPE_MAP }, + { "script_buf", sizeof(struct script_buf), SERVER_TYPE_MAP }, { "script_code", sizeof(struct script_code), SERVER_TYPE_MAP }, { "script_data", sizeof(struct script_data), SERVER_TYPE_MAP }, { "script_function", sizeof(struct script_function), SERVER_TYPE_MAP }, @@ -662,6 +691,7 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { { "script_syntax_data", sizeof(struct script_syntax_data), SERVER_TYPE_MAP }, { "str_data_struct", sizeof(struct str_data_struct), SERVER_TYPE_MAP }, { "string_translation", sizeof(struct string_translation), SERVER_TYPE_MAP }, + { "string_translation_entry", sizeof(struct string_translation_entry), SERVER_TYPE_MAP }, #else #define MAP_SCRIPT_H #endif // MAP_SCRIPT_H diff --git a/src/common/HPMSymbols.inc.h b/src/common/HPMSymbols.inc.h index a6bf1622b..7ccb960de 100644 --- a/src/common/HPMSymbols.inc.h +++ b/src/common/HPMSymbols.inc.h @@ -23,6 +23,8 @@ * as it will get overwritten. */ +/* GENERATED FILE DO NOT EDIT */ + #if !defined(HERCULES_CORE) #ifdef COMMON_UTILS_H /* HCache */ struct HCache_interface *HCache; @@ -66,6 +68,9 @@ struct core_interface *core; #ifdef COMMON_DB_H /* DB */ struct db_interface *DB; #endif // COMMON_DB_H +#ifdef COMMON_DES_H /* des */ +struct des_interface *des; +#endif // COMMON_DES_H #ifdef MAP_DUEL_H /* duel */ struct duel_interface *duel; #endif // MAP_DUEL_H @@ -75,6 +80,9 @@ struct elemental_interface *elemental; #ifdef CHAR_GEOIP_H /* geoip */ struct geoip_interface *geoip; #endif // CHAR_GEOIP_H +#ifdef COMMON_GRFIO_H /* grfio */ +struct grfio_interface *grfio; +#endif // COMMON_GRFIO_H #ifdef MAP_GUILD_H /* guild */ struct guild_interface *guild; #endif // MAP_GUILD_H @@ -165,12 +173,18 @@ struct mapit_interface *mapit; #ifdef MAP_MAPREG_H /* mapreg */ struct mapreg_interface *mapreg; #endif // MAP_MAPREG_H +#ifdef COMMON_MD5CALC_H /* md5 */ +struct md5_interface *md5; +#endif // COMMON_MD5CALC_H #ifdef MAP_MERCENARY_H /* mercenary */ struct mercenary_interface *mercenary; #endif // MAP_MERCENARY_H #ifdef MAP_MOB_H /* mob */ struct mob_interface *mob; #endif // MAP_MOB_H +#ifdef COMMON_MUTEX_H /* mutex */ +struct mutex_interface *mutex; +#endif // COMMON_MUTEX_H #ifdef MAP_NPC_H /* npc_chat */ struct npc_chat_interface *npc_chat; #endif // MAP_NPC_H @@ -204,6 +218,9 @@ struct pincode_interface *pincode; #ifdef MAP_QUEST_H /* quest */ struct quest_interface *quest; #endif // MAP_QUEST_H +#ifdef COMMON_RANDOM_H /* rnd */ +struct rnd_interface *rnd; +#endif // COMMON_RANDOM_H #ifdef MAP_SCRIPT_H /* script */ struct script_interface *script; #endif // MAP_SCRIPT_H @@ -240,6 +257,9 @@ struct sv_interface *sv; #ifdef COMMON_SYSINFO_H /* sysinfo */ struct sysinfo_interface *sysinfo; #endif // COMMON_SYSINFO_H +#ifdef COMMON_THREAD_H /* thread */ +struct thread_interface *thread; +#endif // COMMON_THREAD_H #ifdef COMMON_TIMER_H /* timer */ struct timer_interface *timer; #endif // COMMON_TIMER_H @@ -298,6 +318,9 @@ if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("core", core)) return "core"; #ifdef COMMON_DB_H /* DB */ if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("DB", DB)) return "DB"; #endif // COMMON_DB_H +#ifdef COMMON_DES_H /* des */ +if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("des", des)) return "des"; +#endif // COMMON_DES_H #ifdef MAP_DUEL_H /* duel */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("duel", duel)) return "duel"; #endif // MAP_DUEL_H @@ -307,6 +330,9 @@ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("elemental", elemental)) retu #ifdef CHAR_GEOIP_H /* geoip */ if ((server_type&(SERVER_TYPE_CHAR)) && !HPM_SYMBOL("geoip", geoip)) return "geoip"; #endif // CHAR_GEOIP_H +#ifdef COMMON_GRFIO_H /* grfio */ +if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("grfio", grfio)) return "grfio"; +#endif // COMMON_GRFIO_H #ifdef MAP_GUILD_H /* guild */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("guild", guild)) return "guild"; #endif // MAP_GUILD_H @@ -397,12 +423,18 @@ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("mapit", mapit)) return "mapi #ifdef MAP_MAPREG_H /* mapreg */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("mapreg", mapreg)) return "mapreg"; #endif // MAP_MAPREG_H +#ifdef COMMON_MD5CALC_H /* md5 */ +if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("md5", md5)) return "md5"; +#endif // COMMON_MD5CALC_H #ifdef MAP_MERCENARY_H /* mercenary */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("mercenary", mercenary)) return "mercenary"; #endif // MAP_MERCENARY_H #ifdef MAP_MOB_H /* mob */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("mob", mob)) return "mob"; #endif // MAP_MOB_H +#ifdef COMMON_MUTEX_H /* mutex */ +if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("mutex", mutex)) return "mutex"; +#endif // COMMON_MUTEX_H #ifdef MAP_NPC_H /* npc_chat */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("npc_chat", npc_chat)) return "npc_chat"; #endif // MAP_NPC_H @@ -436,6 +468,9 @@ if ((server_type&(SERVER_TYPE_CHAR)) && !HPM_SYMBOL("pincode", pincode)) return #ifdef MAP_QUEST_H /* quest */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("quest", quest)) return "quest"; #endif // MAP_QUEST_H +#ifdef COMMON_RANDOM_H /* rnd */ +if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("rnd", rnd)) return "rnd"; +#endif // COMMON_RANDOM_H #ifdef MAP_SCRIPT_H /* script */ if ((server_type&(SERVER_TYPE_MAP)) && !HPM_SYMBOL("script", script)) return "script"; #endif // MAP_SCRIPT_H @@ -472,6 +507,9 @@ if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("sv", sv)) return "sv"; #ifdef COMMON_SYSINFO_H /* sysinfo */ if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("sysinfo", sysinfo)) return "sysinfo"; #endif // COMMON_SYSINFO_H +#ifdef COMMON_THREAD_H /* thread */ +if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("thread", thread)) return "thread"; +#endif // COMMON_THREAD_H #ifdef COMMON_TIMER_H /* timer */ if ((server_type&(SERVER_TYPE_ALL)) && !HPM_SYMBOL("timer", timer)) return "timer"; #endif // COMMON_TIMER_H diff --git a/src/common/console.c b/src/common/console.c index 0be33e5c3..f1b4523e2 100644 --- a/src/common/console.c +++ b/src/common/console.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -57,6 +57,7 @@ struct console_interface console_s; struct console_interface *console; #ifdef CONSOLE_INPUT struct console_input_interface console_input_s; +struct spin_lock console_ptlock_s; struct { char queue[CONSOLE_PARSE_SIZE][MAX_CONSOLE_INPUT]; @@ -464,45 +465,45 @@ void *cThread_main(void *x) { console->input->parse(input); if( input[0] != '\0' ) {/* did we get something? */ - EnterSpinLock(&console->input->ptlock); + EnterSpinLock(console->input->ptlock); if( cinput.count == CONSOLE_PARSE_SIZE ) { - LeaveSpinLock(&console->input->ptlock); + LeaveSpinLock(console->input->ptlock); continue;/* drop */ } safestrncpy(cinput.queue[cinput.count++],input,MAX_CONSOLE_INPUT); - LeaveSpinLock(&console->input->ptlock); + LeaveSpinLock(console->input->ptlock); } } - ramutex_lock( console->input->ptmutex ); - racond_wait( console->input->ptcond, console->input->ptmutex, -1 ); - ramutex_unlock( console->input->ptmutex ); + mutex->lock(console->input->ptmutex); + mutex->cond_wait(console->input->ptcond, console->input->ptmutex, -1); + mutex->unlock(console->input->ptmutex); } return NULL; } int console_parse_timer(int tid, int64 tick, int id, intptr_t data) { int i; - EnterSpinLock(&console->input->ptlock); + EnterSpinLock(console->input->ptlock); for(i = 0; i < cinput.count; i++) { console->input->parse_sub(cinput.queue[i]); } cinput.count = 0; - LeaveSpinLock(&console->input->ptlock); - racond_signal(console->input->ptcond); + LeaveSpinLock(console->input->ptlock); + mutex->cond_signal(console->input->ptcond); return 0; } void console_parse_final(void) { if( console->input->ptstate ) { InterlockedDecrement(&console->input->ptstate); - racond_signal(console->input->ptcond); + mutex->cond_signal(console->input->ptcond); /* wait for thread to close */ - rathread_wait(console->input->pthread, NULL); + thread->wait(console->input->pthread, NULL); - racond_destroy(console->input->ptcond); - ramutex_destroy(console->input->ptmutex); + mutex->cond_destroy(console->input->ptcond); + mutex->destroy(console->input->ptmutex); } } void console_parse_init(void) { @@ -510,12 +511,12 @@ void console_parse_init(void) { console->input->ptstate = 1; - InitializeSpinLock(&console->input->ptlock); + InitializeSpinLock(console->input->ptlock); - console->input->ptmutex = ramutex_create(); - console->input->ptcond = racond_create(); + console->input->ptmutex = mutex->create(); + console->input->ptcond = mutex->cond_create(); - if( (console->input->pthread = rathread_create(console->input->pthread_main, NULL)) == NULL ){ + if( (console->input->pthread = thread->create(console->input->pthread_main, NULL)) == NULL ){ ShowFatalError("console_parse_init: failed to spawn console_parse thread.\n"); exit(EXIT_FAILURE); } @@ -563,6 +564,7 @@ void console_defaults(void) console->display_gplnotice = display_gplnotice; #ifdef CONSOLE_INPUT console->input = &console_input_s; + console->input->ptlock = &console_ptlock_s; console->input->parse_init = console_parse_init; console->input->parse_final = console_parse_final; console->input->parse_timer = console_parse_timer; diff --git a/src/common/console.h b/src/common/console.h index 57c750a7d..dd3a9cf2e 100644 --- a/src/common/console.h +++ b/src/common/console.h @@ -22,12 +22,14 @@ #include "common/hercules.h" #include "common/db.h" -#include "common/mutex.h" #include "common/spinlock.h" -#include "common/thread.h" /* Forward Declarations */ struct Sql; // common/sql.h +struct cond_data; +struct mutex_data; +struct spin_lock; +struct thread_handle; /** * Queue Max @@ -71,11 +73,11 @@ struct CParseEntry { struct console_input_interface { #ifdef CONSOLE_INPUT /* vars */ - SPIN_LOCK ptlock;/* parse thread lock */ - rAthread *pthread;/* parse thread */ - volatile int32 ptstate;/* parse thread state */ - ramutex *ptmutex;/* parse thread mutex */ - racond *ptcond;/* parse thread cond */ + struct spin_lock *ptlock; ///< parse thread lock. + struct thread_handle *pthread; ///< parse thread. + volatile int32 ptstate; ///< parse thread state. + struct mutex_data *ptmutex; ///< parse thread mutex. + struct cond_data *ptcond; ///< parse thread conditional variable. /* */ VECTOR_DECL(struct CParseEntry *) command_list; VECTOR_DECL(struct CParseEntry *) commands; diff --git a/src/common/core.c b/src/common/core.c index ccd80c44b..63123dfea 100644 --- a/src/common/core.c +++ b/src/common/core.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -26,23 +26,27 @@ #include "common/cbasetypes.h" #include "common/console.h" #include "common/db.h" +#include "common/des.h" +#include "common/grfio.h" #include "common/memmgr.h" #include "common/mmo.h" -#include "common/random.h" +#include "common/nullpo.h" #include "common/showmsg.h" #include "common/strlib.h" #include "common/sysinfo.h" -#include "common/nullpo.h" +#include "common/timer.h" +#include "common/utils.h" #ifndef MINICORE # include "common/HPM.h" # include "common/conf.h" # include "common/ers.h" +# include "common/md5calc.h" +# include "common/mutex.h" +# include "common/random.h" # include "common/socket.h" # include "common/sql.h" # include "common/thread.h" -# include "common/timer.h" -# include "common/utils.h" #endif #ifndef _WIN32 @@ -54,6 +58,28 @@ #include <stdio.h> #include <stdlib.h> +/* + * Uncomment the line below if you want to silence the root warning on startup + * (not recommended, as it opens the machine to security risks. You should + * never ever run software as root unless it requires the extra privileges + * (which Hercules does not.) + * More info: + * http://www.tldp.org/HOWTO/Security-HOWTO/local-security.html + * http://www.gentoo.org/doc/en/security/security-handbook.xml?style=printable&part=1&chap=1#doc_chap4 + * http://wiki.centos.org/TipsAndTricks/BecomingRoot + * http://fedoraproject.org/wiki/Configuring_Sudo + * https://help.ubuntu.com/community/RootSudo + * http://www.freebsdwiki.net/index.php/Root + * + * If your service provider forces (or encourages) you to run server software + * as root, please complain to them before and after uncommenting this line, + * since it is a very bad idea. + * Please note that NO SUPPORT will be given if you uncomment the following line. + */ +//#define I_AM_AWARE_OF_THE_RISK_AND_STILL_WANT_TO_RUN_HERCULES_AS_ROOT +// And don't complain to us if the XYZ plugin you installed wiped your hard disk, or worse. +// Note: This feature is deprecated, and should not be used. + /// Called when a terminate signal is received. void (*shutdown_callback)(void) = NULL; @@ -172,11 +198,51 @@ void signals_init (void) { /** * Warns the user if executed as superuser (root) + * + * @retval false if the check didn't pass and the program should be terminated. */ -void usercheck(void) { +bool usercheck(void) +{ +#ifndef _WIN32 if (sysinfo->is_superuser()) { - ShowWarning("You are running Hercules with root privileges, it is not necessary.\n"); + if (!isatty(fileno(stdin))) { +#ifdef BUILDBOT + return true; +#else // BUILDBOT + ShowFatalError("You are running Hercules with root privileges, it is not necessary, nor recommended. " + "Aborting.\n"); + return false; // Don't allow noninteractive execution regardless. +#endif // BUILDBOT + } + ShowError("You are running Hercules with root privileges, it is not necessary, nor recommended.\n"); +#ifdef I_AM_AWARE_OF_THE_RISK_AND_STILL_WANT_TO_RUN_HERCULES_AS_ROOT +#ifndef BUILDBOT +#warning This Hercules build is not eligible to obtain support by the developers. +#warning The setting I_AM_AWARE_OF_THE_RISK_AND_STILL_WANT_TO_RUN_HERCULES_AS_ROOT is deprecated and should not be used. +#endif // BUILDBOT +#else // not I_AM_AWARE_OF_THE_RISK_AND_STILL_WANT_TO_RUN_HERCULES_AS_ROOT + ShowNotice("Execution will be paused for 60 seconds. Press Ctrl-C if you wish to quit.\n"); + ShowNotice("If you want to get rid of this message, please open %s and uncomment, near the top, the line saying:\n" + "\t\"//#define I_AM_AWARE_OF_THE_RISK_AND_STILL_WANT_TO_RUN_HERCULES_AS_ROOT\"\n", __FILE__); + ShowNotice("Note: In a near future, this courtesy notice will go away. " + "Please update your infrastructure not to require root privileges before then.\n"); + ShowWarning("It's recommended that you " CL_WHITE "press CTRL-C now!" CL_RESET "\n"); + { + int i; + for (i = 0; i < 60; i++) { + ShowMessage("\a *"); + HSleep(1); + } + } + ShowMessage("\n"); + ShowNotice("Resuming operations with root privileges. " + CL_RED "If anything breaks, you get to keep the pieces, " + "and the Hercules developers won't be able to help you." + CL_RESET "\n"); +#endif // I_AM_AWARE_OF_THE_RISK_AND_STILL_WANT_TO_RUN_HERCULES_AS_ROOT } +#endif // not _WIN32 + return true; } void core_defaults(void) { @@ -191,12 +257,18 @@ void core_defaults(void) { malloc_defaults(); showmsg_defaults(); cmdline_defaults(); + des_defaults(); + grfio_defaults(); // Note: grfio is lazily loaded. grfio->init() and grfio->final() are not automatically called. #ifndef MINICORE + mutex_defaults(); libconfig_defaults(); sql_defaults(); timer_defaults(); db_defaults(); socket_defaults(); + rnd_defaults(); + md5_defaults(); + thread_defaults(); #endif } /** @@ -426,7 +498,8 @@ int main (int argc, char **argv) { if (!(showmsg->silent&0x1)) console->display_title(); - usercheck(); + if (!usercheck()) + return EXIT_FAILURE; #ifdef MINICORE // minimalist Core do_init(argc,argv); @@ -435,7 +508,7 @@ int main (int argc, char **argv) { set_server_type(); Sql_Init(); - rathread_init(); + thread->init(); DB->init(); signals_init(); @@ -446,8 +519,7 @@ int main (int argc, char **argv) { timer->init(); /* timer first */ - rnd_init(); - srand((unsigned int)timer->gettick()); + rnd->init(); console->init(); @@ -472,8 +544,9 @@ int main (int argc, char **argv) { timer->final(); sockt->final(); DB->final(); - rathread_final(); + thread->final(); ers_final(); + rnd->final(); #endif cmdline->final(); //sysinfo->final(); Called by iMalloc->final() diff --git a/src/common/db.h b/src/common/db.h index d7d111c86..1c0955221 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -1395,6 +1395,16 @@ HPShared struct db_interface *DB; } while(false) /** + * Removes all values from the vector. + * + * Does not free the allocated data. + */ +#define VECTOR_TRUNCATE(_vec) \ + do { \ + VECTOR_LENGTH(_vec) = 0; \ + } while (false) + +/** * Clears the vector, freeing allocated data. * * @param _vec Vector. diff --git a/src/common/des.c b/src/common/des.c index ce64309f3..c680610e9 100644 --- a/src/common/des.c +++ b/src/common/des.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -24,23 +24,23 @@ #include "common/cbasetypes.h" -/// DES (Data Encryption Standard) algorithm, modified version. -/// @see http://www.eathena.ws/board/index.php?autocom=bugtracker&showbug=5099. -/// @see http://en.wikipedia.org/wiki/Data_Encryption_Standard -/// @see http://en.wikipedia.org/wiki/DES_supplementary_material +/** @file + * Implementation of the des interface. + */ +struct des_interface des_s; +struct des_interface *des; /// Bitmask for accessing individual bits of a byte. static const uint8_t mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; - -/// Initial permutation (IP). -static void IP(BIT64* src) +/** + * Initial permutation (IP). + */ +static void des_IP(struct des_bit64 *src) { - BIT64 tmp = {{0}}; - static const uint8_t ip_table[64] = { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, @@ -51,24 +51,23 @@ static void IP(BIT64* src) 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, }; + struct des_bit64 tmp = {{0}}; + int i; - size_t i; - for( i = 0; i < ARRAYLENGTH(ip_table); ++i ) - { + for(i = 0; i < ARRAYLENGTH(ip_table); ++i) { uint8_t j = ip_table[i] - 1; - if( src->b[(j >> 3) & 7] & mask[j & 7] ) - tmp .b[(i >> 3) & 7] |= mask[i & 7]; + if (src->b[(j >> 3) & 7] & mask[j & 7]) + tmp.b[(i >> 3) & 7] |= mask[i & 7]; } *src = tmp; } - -/// Final permutation (IP^-1). -static void FP(BIT64* src) +/** + * Final permutation (IP^-1). + */ +static void des_FP(struct des_bit64 *src) { - BIT64 tmp = {{0}}; - static const uint8_t fp_table[64] = { 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, @@ -79,24 +78,26 @@ static void FP(BIT64* src) 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, }; + struct des_bit64 tmp = {{0}}; + int i; - size_t i; - for( i = 0; i < ARRAYLENGTH(fp_table); ++i ) - { + for (i = 0; i < ARRAYLENGTH(fp_table); ++i) { uint8_t j = fp_table[i] - 1; - if( src->b[(j >> 3) & 7] & mask[j & 7] ) - tmp .b[(i >> 3) & 7] |= mask[i & 7]; + if (src->b[(j >> 3) & 7] & mask[j & 7]) + tmp.b[(i >> 3) & 7] |= mask[i & 7]; } *src = tmp; } - -/// Expansion (E). -/// Expands upper four 8-bits (32b) into eight 6-bits (48b). -static void E(BIT64* src) +/** + * Expansion (E). + * + * Expands upper four 8-bits (32b) into eight 6-bits (48b). + */ +static void des_E(struct des_bit64 *src) { - BIT64 tmp = {{0}}; + struct des_bit64 tmp = {{0}}; #if 0 // original @@ -110,13 +111,12 @@ static void E(BIT64* src) 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1, }; + int i; - size_t i; - for( i = 0; i < ARRAYLENGTH(expand_table); ++i ) - { + for (i = 0; i < ARRAYLENGTH(expand_table); ++i) { uint8_t j = expand_table[i] - 1; - if( src->b[j / 8 + 4] & mask[j % 8] ) - tmp .b[i / 6 + 0] |= mask[i % 6]; + if (src->b[j / 8 + 4] & mask[j % 8]) + tmp.b[i / 6 + 0] |= mask[i % 6]; } #endif // optimized @@ -132,12 +132,11 @@ static void E(BIT64* src) *src = tmp; } - -/// Transposition (P-BOX). -static void TP(BIT64* src) +/** + * Transposition (P-BOX). + */ +static void des_TP(struct des_bit64 *src) { - BIT64 tmp = {{0}}; - static const uint8_t tp_table[32] = { 16, 7, 20, 21, 29, 12, 28, 17, @@ -148,25 +147,27 @@ static void TP(BIT64* src) 19, 13, 30, 6, 22, 11, 4, 25, }; + struct des_bit64 tmp = {{0}}; + int i; - size_t i; - for( i = 0; i < ARRAYLENGTH(tp_table); ++i ) - { + for (i = 0; i < ARRAYLENGTH(tp_table); ++i) { uint8_t j = tp_table[i] - 1; - if( src->b[(j >> 3) + 0] & mask[j & 7] ) - tmp .b[(i >> 3) + 4] |= mask[i & 7]; + if (src->b[(j >> 3) + 0] & mask[j & 7]) + tmp.b[(i >> 3) + 4] |= mask[i & 7]; } *src = tmp; } -/// Substitution boxes (S-boxes). -/// NOTE: This implementation was optimized to process two nibbles in one step (twice as fast). -static void SBOX(BIT64* src) +/** + * Substitution boxes (S-boxes). + * + * This implementation was optimized to process two nibbles in one step (twice + * as fast). + */ +static void des_SBOX(struct des_bit64 *src) { - BIT64 tmp = {{0}}; - static const uint8_t s_table[4][64] = { { 0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e, @@ -190,10 +191,10 @@ static void SBOX(BIT64* src) 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb, } }; + struct des_bit64 tmp = {{0}}; + int i; - size_t i; - for( i = 0; i < ARRAYLENGTH(s_table); ++i ) - { + for (i = 0; i < ARRAYLENGTH(s_table); ++i) { tmp.b[i] = (s_table[i][src->b[i*2+0]] & 0xf0) | (s_table[i][src->b[i*2+1]] & 0x0f); } @@ -201,15 +202,17 @@ static void SBOX(BIT64* src) *src = tmp; } - -/// DES round function. -/// XORs src[0..3] with TP(SBOX(E(src[4..7]))). -static void RoundFunction(BIT64* src) +/** + * DES round function. + * + * XORs src[0..3] with TP(SBOX(E(src[4..7]))). + */ +static void des_RoundFunction(struct des_bit64 *src) { - BIT64 tmp = *src; - E(&tmp); - SBOX(&tmp); - TP(&tmp); + struct des_bit64 tmp = *src; + des_E(&tmp); + des_SBOX(&tmp); + des_TP(&tmp); src->b[0] ^= tmp.b[4]; src->b[1] ^= tmp.b[5]; @@ -217,20 +220,30 @@ static void RoundFunction(BIT64* src) src->b[3] ^= tmp.b[7]; } - -void des_decrypt_block(BIT64* block) +/// @copydoc des_interface::decrypt_block() +void des_decrypt_block(struct des_bit64 *block) { - IP(block); - RoundFunction(block); - FP(block); + des_IP(block); + des_RoundFunction(block); + des_FP(block); } - -void des_decrypt(unsigned char* data, size_t size) +/// @copydoc des_interface::decrypt() +void des_decrypt(unsigned char *data, size_t size) { - BIT64* p = (BIT64*)data; + struct des_bit64 *p = (struct des_bit64 *)data; size_t i; - for( i = 0; i*8 < size; i += 8 ) - des_decrypt_block(p); + for (i = 0; i*8 < size; i += 8) + des->decrypt_block(p); +} + +/** + * Interface base initialization. + */ +void des_defaults(void) +{ + des = &des_s; + des->decrypt = des_decrypt; + des->decrypt_block = des_decrypt_block; } diff --git a/src/common/des.h b/src/common/des.h index d62b5cc49..9924a044b 100644 --- a/src/common/des.h +++ b/src/common/des.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -21,14 +21,49 @@ #ifndef COMMON_DES_H #define COMMON_DES_H -#include "common/cbasetypes.h" +#include "common/hercules.h" + +/** + * @file + * + * DES (Data Encryption Standard) algorithm, modified version. + * + * @see http://www.eathena.ws/board/index.php?autocom=bugtracker&showbug=5099 + * @see http://en.wikipedia.org/wiki/Data_Encryption_Standard + * @see http://en.wikipedia.org/wiki/DES_supplementary_material + */ + +/* Struct definitions */ /// One 64-bit block. -typedef struct BIT64 { uint8_t b[8]; } BIT64; +struct des_bit64 { + uint8_t b[8]; +}; + +/* Interface */ + +/// The des interface. +struct des_interface { + /** + * Decrypts a block. + * + * @param[in,out] block The block to decrypt (in-place). + */ + void (*decrypt_block) (struct des_bit64 *block); + + /** + * Decrypts a buffer. + * + * @param [in,out] data The buffer to decrypt (in-place). + * @param [in] size The size of the data. + */ + void (*decrypt) (unsigned char *data, size_t size); +}; #ifdef HERCULES_CORE -void des_decrypt_block(BIT64* block); -void des_decrypt(unsigned char* data, size_t size); +void des_defaults(void); #endif // HERCULES_CORE +HPShared struct des_interface *des; ///< Pointer to the des interface implementation. + #endif // COMMON_DES_H diff --git a/src/common/grfio.c b/src/common/grfio.c index c6e47d357..0a9708f17 100644 --- a/src/common/grfio.c +++ b/src/common/grfio.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -35,10 +35,12 @@ #include <sys/stat.h> #include <zlib.h> -//---------------------------- -// file entry table struct -//---------------------------- -typedef struct FILELIST { +/** @file + * Implementation of the GRF I/O interface. + */ + +/// File entry table struct. +struct grf_filelist { int srclen; ///< compressed size int srclen_aligned; int declen; ///< original size @@ -48,11 +50,13 @@ typedef struct FILELIST { 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 -#define FILELIST_TYPE_ENCRYPT_MIXED 0x02 // encryption mode 0 (header DES + periodic DES/shuffle) -#define FILELIST_TYPE_ENCRYPT_HEADER 0x04 // encryption mode 1 (header DES only) +enum grf_filelist_type { + FILELIST_TYPE_FILE = 0x01, ///< entry is a file + FILELIST_TYPE_ENCRYPT_MIXED = 0x02, ///< encryption mode 0 (header DES + periodic DES/shuffle) + FILELIST_TYPE_ENCRYPT_HEADER = 0x04, ///< encryption mode 1 (header DES only) +}; //gentry ... > 0 : data read from a grf file (gentry_table[gentry-1]) //gentry ... 0 : data read from a local file (data directory) @@ -64,7 +68,7 @@ typedef struct FILELIST { //#define GRFIO_LOCAL // stores info about every loaded file -FILELIST* filelist = NULL; +struct grf_filelist *filelist = NULL; int filelist_entrys = 0; int filelist_maxentry = 0; @@ -76,24 +80,30 @@ int gentry_maxentry = 0; // the path to the data directory char data_dir[1024] = ""; +struct grfio_interface grfio_s; +struct grfio_interface *grfio; + // little endian char array to uint conversion -static unsigned int getlong(unsigned char* p) +static unsigned int getlong(unsigned char *p) { return (p[0] << 0 | p[1] << 8 | p[2] << 16 | p[3] << 24); } -static void NibbleSwap(unsigned char* src, int len) +static void NibbleSwap(unsigned char *src, int len) { - while( len > 0 ) - { + while (len > 0) { *src = (*src >> 4) | (*src << 4); ++src; --len; } } -/// Substitutes some specific values for others, leaves rest intact. Obfuscation. -/// NOTE: Operation is symmetric (calling it twice gives back the original input). +/** + * (De)-obfuscates data. + * + * Substitutes some specific values for others, leaves rest intact. + * NOTE: Operation is symmetric (calling it twice gives back the original input). + */ static uint8_t grf_substitution(uint8_t in) { uint8_t out; @@ -121,9 +131,9 @@ static uint8_t grf_substitution(uint8_t in) } #if 0 /* this is not used anywhere, is it ok to delete? */ -static void grf_shuffle_enc(BIT64* src) +static void grf_shuffle_enc(struct des_bit64 *src) { - BIT64 out; + struct des_bit64 out; out.b[0] = src->b[3]; out.b[1] = src->b[4]; @@ -138,9 +148,9 @@ static void grf_shuffle_enc(BIT64* src) } #endif // 0 -static void grf_shuffle_dec(BIT64* src) +static void grf_shuffle_dec(struct des_bit64 *src) { - BIT64 out; + struct des_bit64 out; out.b[0] = src->b[3]; out.b[1] = src->b[4]; @@ -154,29 +164,42 @@ static void grf_shuffle_dec(BIT64* src) *src = out; } -static void grf_decode_header(unsigned char* buf, size_t len) +/** + * Decodes header-encrypted grf data. + * + * @param[in,out] buf Data to decode (in-place). + * @param[in] len Length of the data. + */ +static void grf_decode_header(unsigned char *buf, size_t len) { - BIT64* p = (BIT64*)buf; - size_t nblocks = len / sizeof(BIT64); + struct des_bit64 *p = (struct des_bit64 *)buf; + size_t nblocks = len / sizeof(struct des_bit64); size_t i; // first 20 blocks are all des-encrypted - for( i = 0; i < 20 && i < nblocks; ++i ) - des_decrypt_block(&p[i]); + for (i = 0; i < 20 && i < nblocks; ++i) + des->decrypt_block(&p[i]); // the rest is plaintext, done. } -static void grf_decode_full(unsigned char* buf, size_t len, int cycle) +/** + * Decodes fully encrypted grf data + * + * @param[in,out] buf Data to decode (in-place). + * @param[in] len Length of the data. + * @param[in] cycle The current decoding cycle. + */ +static void grf_decode_full(unsigned char *buf, size_t len, int cycle) { - BIT64* p = (BIT64*)buf; - size_t nblocks = len / sizeof(BIT64); + struct des_bit64 *p = (struct des_bit64 *)buf; + size_t nblocks = len / sizeof(struct des_bit64); int dcycle, scycle; size_t i, j; // first 20 blocks are all des-encrypted - for( i = 0; i < 20 && i < nblocks; ++i ) - des_decrypt_block(&p[i]); + for (i = 0; i < 20 && i < nblocks; ++i) + des->decrypt_block(&p[i]); // after that only one of every 'dcycle' blocks is des-encrypted dcycle = cycle; @@ -186,17 +209,16 @@ static void grf_decode_full(unsigned char* buf, size_t len, int cycle) // so decrypt/de-shuffle periodically j = (size_t)-1; // 0, adjusted to fit the ++j step - for( i = 20; i < nblocks; ++i ) - { - if( i % dcycle == 0 ) - {// decrypt block - des_decrypt_block(&p[i]); + for (i = 20; i < nblocks; ++i) { + if (i % dcycle == 0) { + // decrypt block + des->decrypt_block(&p[i]); continue; } ++j; - if( j % scycle == 0 && j != 0 ) - {// de-shuffle block + if (j % scycle == 0 && j != 0) { + // de-shuffle block grf_shuffle_dec(&p[i]); continue; } @@ -205,22 +227,25 @@ static void grf_decode_full(unsigned char* buf, size_t len, int cycle) } } -/// Decodes grf data. -/// @param buf data to decode (in-place) -/// @param len length of the data -/// @param entry_type flags associated with the data -/// @param entry_len true (unaligned) length of the data -static void grf_decode(unsigned char* buf, size_t len, char entry_type, int entry_len) +/** + * Decodes grf data. + * + * @param[in,out] buf Data to decode (in-place). + * @param[in] len Length of the data + * @param[in] entry_type Flags associated with the data. + * @param[in] entry_len True (unaligned) length of the data. + */ +static void grf_decode(unsigned char *buf, size_t len, char entry_type, int entry_len) { - if( entry_type & FILELIST_TYPE_ENCRYPT_MIXED ) - {// fully encrypted + if (entry_type & FILELIST_TYPE_ENCRYPT_MIXED) { + // fully encrypted int digits; int cycle; int i; // compute number of digits of the entry length digits = 1; - for( i = 10; i <= entry_len; i *= 10 ) + for (i = 10; i <= entry_len; i *= 10) ++digits; // choose size of gap between two encrypted blocks @@ -232,51 +257,47 @@ static void grf_decode(unsigned char* buf, size_t len, char entry_type, int entr : digits + 15; grf_decode_full(buf, len, cycle); - } - else - if( entry_type & FILELIST_TYPE_ENCRYPT_HEADER ) - {// header encrypted + } else if (entry_type & FILELIST_TYPE_ENCRYPT_HEADER) { + // header encrypted grf_decode_header(buf, len); } - else - {// plaintext - ; - } + /* else plaintext */ } -/****************************************************** - *** Zlib Subroutines *** - ******************************************************/ +/* Zlib Subroutines */ -/// zlib crc32 -unsigned long grfio_crc32(const unsigned char* buf, unsigned int len) +/// @copydoc grfio_interface::crc32() +unsigned long grfio_crc32(const unsigned char *buf, unsigned int len) { return crc32(crc32(0L, Z_NULL, 0), buf, len); } -/// zlib uncompress -int decode_zip(void* dest, unsigned long* destLen, const void* source, unsigned long sourceLen) +/// @copydoc grfio_interface::decode_zip +int grfio_decode_zip(void *dest, unsigned long *dest_len, const void *source, unsigned long source_len) { - return uncompress((Bytef*)dest, destLen, (const Bytef*)source, sourceLen); + return uncompress(dest, dest_len, source, source_len); } -/// zlib compress -int encode_zip(void* dest, unsigned long* destLen, const void* source, unsigned long sourceLen) { - if( *destLen == 0 ) /* [Ind/Hercules] */ - *destLen = compressBound(sourceLen); - if( dest == NULL ) { /* [Ind/Hercules] */ - CREATE(dest, unsigned char, *destLen); +/// @copydoc grfio_interface::encode_zip +int grfio_encode_zip(void *dest, unsigned long *dest_len, const void *source, unsigned long source_len) +{ + if (*dest_len == 0) /* [Ind/Hercules] */ + *dest_len = compressBound(source_len); + if (dest == NULL) { + /* [Ind/Hercules] */ + CREATE(dest, unsigned char, *dest_len); } - return compress((Bytef*)dest, destLen, (const Bytef*)source, sourceLen); + return compress(dest, dest_len, source, source_len); } -/*********************************************************** - *** File List Subroutines *** - ***********************************************************/ -// file list hash table +/* File List Subroutines */ + +/// File list hash table int filelist_hash[256]; -// initializes the table that holds the first elements of all hash chains +/** + * Initializes the table that holds the first elements of all hash chains + */ static void hashinit(void) { int i; @@ -284,26 +305,37 @@ static void hashinit(void) filelist_hash[i] = -1; } -// hashes a filename string into a number from {0..255} -static int filehash(const char* fname) +/** + * Hashes a filename string into a number from {0..255} + * + * @param fname The filename to hash. + * @return The hash. + */ +static int grf_filehash(const char *fname) { - unsigned int hash = 0; - while(*fname) { + uint32 hash = 0; + while (*fname != '\0') { hash = (hash<<1) + (hash>>7)*9 + TOLOWER(*fname); fname++; } return hash & 255; } -// finds a FILELIST entry with the specified file name -static FILELIST* filelist_find(const char* fname) +/** + * Finds a grf_filelist entry with the specified file name + * + * @param fname The file to find. + * @return The file entry. + * @retval NULL if the file wasn't found. + */ +static struct grf_filelist *grfio_filelist_find(const char *fname) { int hash, index; - if (!filelist) + if (filelist == NULL) return NULL; - hash = filelist_hash[filehash(fname)]; + hash = filelist_hash[grf_filehash(fname)]; for (index = hash; index != -1; index = filelist[index].next) if(!strcmpi(filelist[index].fn, fname)) break; @@ -311,16 +343,23 @@ static FILELIST* filelist_find(const char* fname) return (index >= 0) ? &filelist[index] : NULL; } -// returns the original file name -char* grfio_find_file(const char* fname) +/// @copydoc grfio_interface::find_file() +const char *grfio_find_file(const char *fname) { - FILELIST *flist = filelist_find(fname); - if (!flist) return NULL; + struct grf_filelist *flist = grfio_filelist_find(fname); + if (flist == NULL) + 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) { +/** + * Adds a grf_filelist entry into the list of loaded files. + * + * @param entry The entry to add. + * @return A pointer to the added entry. + */ +static struct grf_filelist *grfio_filelist_add(struct grf_filelist *entry) +{ int hash; nullpo_ret(entry); #ifdef __clang_analyzer__ @@ -331,16 +370,16 @@ static FILELIST* filelist_add(FILELIST* entry) { #define FILELIST_ADDS 1024 // number increment of file lists ` if (filelist_entrys >= filelist_maxentry) { - filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST)); - memset(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST)); + filelist = aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(struct grf_filelist)); + memset(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(struct grf_filelist)); filelist_maxentry += FILELIST_ADDS; } #undef FILELIST_ADDS - memcpy(&filelist[filelist_entrys], entry, sizeof(FILELIST)); + memcpy(&filelist[filelist_entrys], entry, sizeof(struct grf_filelist)); - hash = filehash(entry->fn); + hash = grf_filehash(entry->fn); filelist[filelist_entrys].next = filelist_hash[hash]; filelist_hash[hash] = filelist_entrys; @@ -349,63 +388,69 @@ static FILELIST* filelist_add(FILELIST* entry) { return &filelist[filelist_entrys - 1]; } -// adds a new FILELIST entry or overwrites an existing one -static FILELIST* filelist_modify(FILELIST* entry) +/** + * Adds a new grf_filelist entry or overwrites an existing one. + * + * @param entry The entry to add. + * @return A pointer to the added entry. + */ +static struct grf_filelist *grfio_filelist_modify(struct grf_filelist *entry) { - FILELIST* fentry = filelist_find(entry->fn); + struct grf_filelist *fentry = grfio_filelist_find(entry->fn); if (fentry != NULL) { int tmp = fentry->next; - memcpy(fentry, entry, sizeof(FILELIST)); + memcpy(fentry, entry, sizeof(struct grf_filelist)); fentry->next = tmp; } else { - fentry = filelist_add(entry); + fentry = grfio_filelist_add(entry); } return fentry; } -// shrinks the file list array if too long -static void filelist_compact(void) +/// Shrinks the file list array if too long. +static void grfio_filelist_compact(void) { if (filelist == NULL) return; if (filelist_entrys < filelist_maxentry) { - filelist = (FILELIST *)aRealloc(filelist, filelist_entrys * sizeof(FILELIST)); + filelist = aRealloc(filelist, filelist_entrys * sizeof(struct grf_filelist)); filelist_maxentry = filelist_entrys; } } -/*********************************************************** - *** Grfio Subroutines *** - ***********************************************************/ +/* GRF I/O Subroutines */ -/// Combines are resource path with the data folder location to create local resource path. -static void grfio_localpath_create(char* buffer, size_t size, const char* filename) +/** + * Combines a resource path with the data folder location to create local + * resource path. + * + * @param[out] buffer The output buffer. + * @param[in] size The size of the output buffer. + * @param[in] filename Resource path. + */ +static void grfio_localpath_create(char *buffer, size_t size, const char *filename) { - unsigned int i; + int i; size_t len; len = strlen(data_dir); - if( data_dir[0] == '\0' || data_dir[len-1] == '/' || data_dir[len-1] == '\\' ) - { + if (data_dir[0] == '\0' || data_dir[len-1] == '/' || data_dir[len-1] == '\\') safesnprintf(buffer, size, "%s%s", data_dir, filename); - } else - { safesnprintf(buffer, size, "%s/%s", data_dir, filename); - } // normalize path - for( i = 0; buffer[i] != '\0'; ++i ) - if( buffer[i] == '\\' ) + for (i = 0; buffer[i] != '\0'; ++i) + if (buffer[i] == '\\') buffer[i] = '/'; } -/// Reads a file into a newly allocated buffer (from grf or data directory). +/// @copydoc grfio_interface::reads() void *grfio_reads(const char *fname, int *size) { - FILELIST* entry = filelist_find(fname); + struct grf_filelist *entry = grfio_filelist_find(fname); if (entry == NULL || entry->gentry <= 0) { // LocalFileCheck char lfname[256]; @@ -424,7 +469,7 @@ void *grfio_reads(const char *fname, int *size) return NULL; } fseek(in,0,SEEK_SET); - buf = (unsigned char *)aMalloc(declen+1); // +1 for resnametable zero-termination + buf = aMalloc(declen+1); // +1 for resnametable zero-termination buf[declen] = '\0'; if (fread(buf, 1, declen, in) != (size_t)declen) { ShowError("An error occurred in fread grfio_reads, fname=%s \n",fname); @@ -454,7 +499,7 @@ void *grfio_reads(const char *fname, int *size) if (in != NULL) { int fsize = entry->srclen_aligned; - unsigned char *buf = (unsigned char *)aMalloc(fsize); + unsigned char *buf = aMalloc(fsize); unsigned char *buf2 = NULL; if (fseek(in, entry->srcpos, SEEK_SET) != 0 || fread(buf, 1, fsize, in) != (size_t)fsize) { @@ -465,14 +510,14 @@ void *grfio_reads(const char *fname, int *size) } fclose(in); - buf2 = (unsigned char *)aMalloc(entry->declen+1); // +1 for resnametable zero-termination + buf2 = aMalloc(entry->declen+1); // +1 for resnametable zero-termination buf2[entry->declen] = '\0'; if (entry->type & FILELIST_TYPE_FILE) { // file uLongf len; grf_decode(buf, fsize, entry->type, entry->srclen); len = entry->declen; - decode_zip(buf2, &len, buf, entry->srclen); + grfio->decode_zip(buf2, &len, buf, entry->srclen); if (len != (uLong)entry->declen) { ShowError("decode_zip size mismatch err: %d != %d\n", (int)len, entry->declen); aFree(buf); @@ -498,35 +543,51 @@ void *grfio_reads(const char *fname, int *size) return NULL; } -/// Decodes encrypted filename from a version 01xx grf index. -static char* decode_filename(unsigned char* buf, int len) +/** + * Decodes encrypted filename from a version 01xx grf index. + * + * @param[in,out] buf The encrypted filename (decrypted in-place). + * @param[in] len The filename length. + * @return A pointer to the decrypted filename. + */ +static char *grfio_decode_filename(unsigned char *buf, int len) { - int lop; - for(lop=0;lop<len;lop+=8) { - NibbleSwap(&buf[lop],8); - des_decrypt(&buf[lop],8); + int i; + for (i = 0; i < len; i += 8) { + NibbleSwap(&buf[i],8); + des->decrypt(&buf[i],8); } return (char*)buf; } -/// Compares file extension against known large file types. -/// @return true if the file should undergo full mode 0 decryption, and true otherwise. -static bool isFullEncrypt(const char* fname) +/** + * Compares file extension against known large file types. + * + * @param fname The file name. + * @return true if the file should undergo full mode 0 decryption, and true otherwise. + */ +static bool grfio_is_full_encrypt(const char *fname) { - const char* ext = strrchr(fname, '.'); - if( ext != NULL ) { - static const char extensions[4][5] = { ".gnd", ".gat", ".act", ".str" }; - size_t i; - for( i = 0; i < ARRAYLENGTH(extensions); ++i ) - if( strcmpi(ext, extensions[i]) == 0 ) + const char *ext = strrchr(fname, '.'); + if (ext != NULL) { + static const char *extensions[] = { ".gnd", ".gat", ".act", ".str" }; + int i; + for (i = 0; i < ARRAYLENGTH(extensions); ++i) + if (strcmpi(ext, extensions[i]) == 0) return false; } return true; } -/// Loads all entries in the specified grf file into the filelist. -/// @param gentry index of the grf file name in the gentry_table +/** + * Loads all entries in the specified grf file into the filelist. + * + * @param grfname Name of the grf file. + * @param gentry Index of the grf file name in the gentry_table. + * @return Error code. + * @retval 0 in case of success. + */ static int grfio_entryread(const char *grfname, int gentry) { long grf_size; @@ -535,12 +596,11 @@ static int grfio_entryread(const char *grfname, int gentry) unsigned char *grf_filelist; FILE *fp = fopen(grfname, "rb"); - if( fp == NULL ) { - ShowWarning("GRF data file not found: '%s'\n",grfname); + if (fp == NULL) { + ShowWarning("GRF data file not found: '%s'\n", grfname); return 1; // 1:not found error - } else { - ShowInfo("GRF data file found: '%s'\n",grfname); } + ShowInfo("GRF data file found: '%s'\n", grfname); fseek(fp,0,SEEK_END); grf_size = ftell(fp); @@ -551,7 +611,7 @@ static int grfio_entryread(const char *grfname, int gentry) fclose(fp); return 2; // 2:file format error } - if (strcmp((const char*)grf_header,"Master of Magic") != 0 || fseek(fp,getlong(grf_header+0x1e),SEEK_CUR) != 0) { + if (strcmp((const char*)grf_header, "Master of Magic") != 0 || fseek(fp, getlong(grf_header+0x1e), SEEK_CUR) != 0) { fclose(fp); ShowError("GRF %s read error\n", grfname); return 2; // 2:file format error @@ -561,9 +621,8 @@ static int grfio_entryread(const char *grfname, int gentry) if (grf_version == 0x01) { // ****** Grf version 01xx ****** - long list_size; - list_size = grf_size - ftell(fp); - grf_filelist = (unsigned char *)aMalloc(list_size); + long list_size = grf_size - ftell(fp); + grf_filelist = aMalloc(list_size); if (fread(grf_filelist,1,list_size,fp) != (size_t)list_size) { ShowError("Couldn't read all grf_filelist element of %s \n", grfname); aFree(grf_filelist); @@ -576,11 +635,11 @@ static int grfio_entryread(const char *grfname, int gentry) // Get an entry for (entry = 0, ofs = 0; entry < entrys; ++entry) { - FILELIST aentry; + struct grf_filelist aentry = { 0 }; int ofs2 = ofs+getlong(grf_filelist+ofs)+4; unsigned char type = grf_filelist[ofs2+12]; if (type&FILELIST_TYPE_FILE) { - char *fname = decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6); + char *fname = grfio_decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6); int srclen = getlong(grf_filelist+ofs2+0) - getlong(grf_filelist+ofs2+8) - 715; if (strlen(fname) > sizeof(aentry.fn) - 1) { @@ -589,7 +648,7 @@ static int grfio_entryread(const char *grfname, int gentry) return 5; // 5: file name too long } - type |= isFullEncrypt(fname) ? FILELIST_TYPE_ENCRYPT_MIXED : FILELIST_TYPE_ENCRYPT_HEADER; + type |= grfio_is_full_encrypt(fname) ? FILELIST_TYPE_ENCRYPT_MIXED : FILELIST_TYPE_ENCRYPT_HEADER; aentry.srclen = srclen; aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579; @@ -603,7 +662,7 @@ static int grfio_entryread(const char *grfname, int gentry) #else aentry.gentry = (char)(gentry+1); // With no first time LocalFileCheck #endif - filelist_modify(&aentry); + grfio_filelist_modify(&aentry); } ofs = ofs2 + 17; @@ -630,7 +689,7 @@ static int grfio_entryread(const char *grfname, int gentry) return 4; } - rBuf = (unsigned char *)aMalloc(rSize); // Get a Read Size + rBuf = aMalloc(rSize); // Get a Read Size if (fread(rBuf,1,rSize,fp) != rSize) { ShowError("An error occurred in fread \n"); fclose(fp); @@ -638,8 +697,8 @@ static int grfio_entryread(const char *grfname, int gentry) return 4; } fclose(fp); - grf_filelist = (unsigned char *)aMalloc(eSize); // Get a Extend Size - decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function + grf_filelist = aMalloc(eSize); // Get a Extend Size + grfio->decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function aFree(rBuf); entrys = getlong(grf_header+0x26) - 7; @@ -647,7 +706,7 @@ static int grfio_entryread(const char *grfname, int gentry) // Get an entry for (entry = 0, ofs = 0; entry < entrys; ++entry) { - FILELIST aentry; + struct grf_filelist aentry; char *fname = (char*)(grf_filelist+ofs); int ofs2 = ofs + (int)strlen(fname)+1; int type = grf_filelist[ofs2+12]; @@ -672,84 +731,89 @@ static int grfio_entryread(const char *grfname, int gentry) #else aentry.gentry = (char)(gentry+1); // With no first time LocalFileCheck #endif - filelist_modify(&aentry); + grfio_filelist_modify(&aentry); } ofs = ofs2 + 17; } aFree(grf_filelist); - } else {// ****** Grf Other version ****** + } else { + // ****** Grf Other version ****** fclose(fp); ShowError("GRF version %04x not supported\n",getlong(grf_header+0x2a)); return 4; } - filelist_compact(); // Unnecessary area release of filelist + grfio_filelist_compact(); // Unnecessary area release of filelist return 0; // 0:no error } -static bool grfio_parse_restable_row(const char* row) +/** + * Parses a Resource file table row. + * + * @param row The row data. + * @return success state. + * @retval true in case of success. + */ +static bool grfio_parse_restable_row(const char *row) { char w1[256], w2[256]; char src[256], dst[256]; char local[256]; - FILELIST* entry; + struct grf_filelist *entry = NULL; if (sscanf(row, "%255[^#\r\n]#%255[^#\r\n]#", w1, w2) != 2) return false; - if( strstr(w2, ".gat") == NULL && strstr(w2, ".rsw") == NULL ) + if (strstr(w2, ".gat") == NULL && strstr(w2, ".rsw") == NULL) return false; // we only need the maps' GAT and RSW files sprintf(src, "data\\%s", w1); sprintf(dst, "data\\%s", w2); - entry = filelist_find(dst); - if( entry != NULL ) - {// alias for GRF resource - FILELIST fentry; - memcpy(&fentry, entry, sizeof(FILELIST)); + entry = grfio_filelist_find(dst); + if (entry != NULL) { + // alias for GRF resource + struct grf_filelist fentry = *entry; safestrncpy(fentry.fn, src, sizeof(fentry.fn)); fentry.fnd = aStrdup(dst); - filelist_modify(&fentry); + grfio_filelist_modify(&fentry); return true; } grfio_localpath_create(local, sizeof(local), dst); - if( exists(local) ) - {// alias for local resource - FILELIST fentry; - memset(&fentry, 0, sizeof(fentry)); + if (exists(local)) { + // alias for local resource + struct grf_filelist fentry = { 0 }; safestrncpy(fentry.fn, src, sizeof(fentry.fn)); fentry.fnd = aStrdup(dst); - filelist_modify(&fentry); + grfio_filelist_modify(&fentry); return true; } return false; } -/// Grfio Resource file check. +/** + * Grfio Resource file check. + */ static void grfio_resourcecheck(void) { char restable[256]; - char *buf; - int size; - FILE* fp; - int i = 0; + char *buf = NULL; + FILE *fp = NULL; + int size = 0, i = 0; // read resnametable from data directory and return if successful grfio_localpath_create(restable, sizeof(restable), "data\\resnametable.txt"); fp = fopen(restable, "rb"); - if( fp != NULL ) - { + if (fp != NULL) { char line[256]; - while( fgets(line, sizeof(line), fp) ) - { - if( grfio_parse_restable_row(line) ) + while (fgets(line, sizeof(line), fp)) { + if (grfio_parse_restable_row(line)) ++i; } @@ -759,20 +823,19 @@ static void grfio_resourcecheck(void) } // read resnametable from loaded GRF's, only if it cannot be loaded from the data directory - buf = (char *)grfio_reads("data\\resnametable.txt", &size); - if( buf != NULL ) - { - char *ptr; + buf = grfio->reads("data\\resnametable.txt", &size); + if (buf != NULL) { + char *ptr = NULL; buf[size] = '\0'; ptr = buf; - while( ptr - buf < size ) - { - if( grfio_parse_restable_row(ptr) ) + while (ptr - buf < size) { + if (grfio_parse_restable_row(ptr)) ++i; ptr = strchr(ptr, '\n'); - if( ptr == NULL ) break; + if (ptr == NULL) + break; ptr++; } @@ -782,13 +845,19 @@ static void grfio_resourcecheck(void) } } -/// Reads a grf file and adds it to the list. -static int grfio_add(const char* fname) +/** + * Reads a grf file and adds it to the list. + * + * @param fname The file name to read. + * @return Error code. + * @retval 0 in case of success. + */ +static int grfio_add(const char *fname) { if (gentry_entrys >= gentry_maxentry) { #define GENTRY_ADDS 4 // The number increment of gentry_table entries gentry_maxentry += GENTRY_ADDS; - gentry_table = (char**)aRealloc(gentry_table, gentry_maxentry * sizeof(char*)); + gentry_table = aRealloc(gentry_table, gentry_maxentry * sizeof(char*)); memset(gentry_table + (gentry_maxentry - GENTRY_ADDS), 0, sizeof(char*) * GENTRY_ADDS); #undef GENTRY_ADDS } @@ -798,7 +867,7 @@ static int grfio_add(const char* fname) return grfio_entryread(fname, gentry_entrys - 1); } -/// Finalizes grfio. +/// @copydoc grfio_interface::final() void grfio_final(void) { if (filelist != NULL) { @@ -824,36 +893,33 @@ void grfio_final(void) gentry_entrys = gentry_maxentry = 0; } -/// Initializes grfio. -void grfio_init(const char* fname) +/// @copydoc grfio_interface::init() +void grfio_init(const char *fname) { - FILE* data_conf; + FILE *data_conf; int grf_num = 0; hashinit(); // hash table initialization data_conf = fopen(fname, "r"); - if( data_conf != NULL ) - { + if (data_conf != NULL) { char line[1024]; - while( fgets(line, sizeof(line), data_conf) ) - { + while (fgets(line, sizeof(line), data_conf)) { char w1[1024], w2[1024]; - if( line[0] == '/' && line[1] == '/' ) + if (line[0] == '/' && line[1] == '/') continue; // skip comments if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) continue; // skip unrecognized lines // Entry table reading - if( strcmp(w1, "grf") == 0 ) // GRF file - { - if( grfio_add(w2) == 0 ) + if (strcmp(w1, "grf") == 0) { + // GRF file + if (grfio_add(w2) == 0) ++grf_num; - } - else if( strcmp(w1,"data_dir") == 0 ) // Data directory - { + } else if (strcmp(w1,"data_dir") == 0) { + // Data directory safestrncpy(data_dir, w2, sizeof(data_dir)); } } @@ -862,12 +928,25 @@ void grfio_init(const char* fname) ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", fname); } - if( grf_num == 0 ) + if (grf_num == 0) ShowInfo("No GRF loaded, using default data directory\n"); // Unnecessary area release of filelist - filelist_compact(); + grfio_filelist_compact(); // Resource check grfio_resourcecheck(); } + +/// Interface base initialization. +void grfio_defaults(void) +{ + grfio = &grfio_s; + grfio->init = grfio_init; + grfio->final = grfio_final; + grfio->reads = grfio_reads; + grfio->find_file = grfio_find_file; + grfio->crc32 = grfio_crc32; + grfio->decode_zip = grfio_decode_zip; + grfio->encode_zip = grfio_encode_zip; +} diff --git a/src/common/grfio.h b/src/common/grfio.h index 36ed8fb39..857bc507e 100644 --- a/src/common/grfio.h +++ b/src/common/grfio.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -21,16 +21,104 @@ #ifndef COMMON_GRFIO_H #define COMMON_GRFIO_H +#include "common/hercules.h" + +/** @file + * GRF I/O library. + */ + +/// The GRF I/O interface. +struct grfio_interface { + /** + * Interface initialization. + * + * @param fname Name of the configuration file. + */ + void (*init) (const char *fname); + + /// Interface finalization. + void (*final) (void); + + /** + * Reads a file into a newly allocated buffer (from grf or data directory). + * + * @param[in] fname Name of the file to read. + * @param[out] size Buffer to return the size of the read file (optional). + * @return The file data. + * @retval NULL in case of error. + */ + void *(*reads) (const char *fname, int *size); + + /** + * Finds a file in the grf or data directory + * + * @param fname The file to find. + * @return The original file name. + * @retval NULL if the file wasn't found. + */ + const char *(*find_file) (const char *fname); + + /** + * Calculates a CRC32 hash. + * + * @param buf The data to hash. + * @param len Data length. + * + * @return The CRC32 hash. + */ + unsigned long (*crc32) (const unsigned char *buf, unsigned int len); + + /** + * Decompresses ZIP data. + * + * Decompresses the source buffer into the destination buffer. + * source_len is the byte length of the source buffer. Upon entry, + * dest_len is the total size of the destination buffer, which must be + * large enough to hold the entire uncompressed data. (The size of the + * uncompressed data must have been saved previously by the compressor + * and transmitted to the decompressor by some mechanism outside the + * scope of this compression library.) Upon exit, dest_len is the + * actual size of the uncompressed buffer. + * + * @param[in,out] dest The destination (uncompressed) buffer. + * @param[in,out] dest_len Max length of the destination buffer, returns length of the decompressed data. + * @param[in] source The source (compressed) buffer. + * @param[in] source_len Source data length. + * @return error code. + * @retval Z_OK in case of success. + */ + int (*decode_zip) (void *dest, unsigned long *dest_len, const void *source, unsigned long source_len); + + /** + * Compresses data to ZIP format. + * + * Compresses the source buffer into the destination buffer. + * source_len is the byte length of the source buffer. Upon entry, + * dest_len is the total size of the destination buffer, which must be + * at least the value returned by compressBound(source_len). Upon + * exit, dest_len is the actual size of the compressed buffer. + * + * @param[in,out] dest The destination (compressed) buffer (if NULL, a new buffer will be created). + * @param[in,out] dest_len Max length of the destination buffer (if 0, it will be calculated). + * @param[in] source The source (uncompressed) buffer. + * @param[in] source_len Source data length. + */ + int (*encode_zip) (void *dest, unsigned long *dest_len, const void *source, unsigned long source_len); +}; + +/** + * Reads a file into a newly allocated buffer (from grf or data directory) + * + * @param fn The filename to read. + * + * @see grfio_interface::reads() + * @related grfio_interface + */ +#define grfio_read(fn) grfio->reads((fn), NULL) + #ifdef HERCULES_CORE -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) - -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); +void grfio_defaults(void); #endif // HERCULES_CORE +HPShared struct grfio_interface *grfio; ///< Pointer to the grfio interface. #endif /* COMMON_GRFIO_H */ diff --git a/src/common/md5calc.c b/src/common/md5calc.c index d346c8aa4..bd6b48f10 100644 --- a/src/common/md5calc.c +++ b/src/common/md5calc.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -20,27 +20,27 @@ */ #define HERCULES_CORE -/*********************************************************** - * md5 calculation algorithm - * - * The source code referred to the following URL. - * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html - * - ***********************************************************/ - #include "md5calc.h" #include "common/cbasetypes.h" +#include "common/nullpo.h" #include "common/random.h" #include <stdio.h> #include <stdlib.h> #include <string.h> -// Global variable +/** @file + * Implementation of the md5 interface. + */ + +struct md5_interface md5_s; +struct md5_interface *md5; + +/// Global variable static unsigned int *pX; -// String Table +/// String Table static const unsigned int T[] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 @@ -60,99 +60,102 @@ static const unsigned int T[] = { 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 }; -// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. +/// The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) // The function used for other calculation -static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) +static unsigned int md5_F(unsigned int X, unsigned int Y, unsigned int Z) { return (X & Y) | (~X & Z); } -static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) + +static unsigned int md5_G(unsigned int X, unsigned int Y, unsigned int Z) { return (X & Z) | (Y & ~Z); } -static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) + +static unsigned int md5_H(unsigned int X, unsigned int Y, unsigned int Z) { return X ^ Y ^ Z; } -static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) + +static unsigned int md5_I(unsigned int X, unsigned int Y, unsigned int Z) { return Y ^ (X | ~Z); } -static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, +static unsigned int md5_Round(unsigned int a, unsigned int b, unsigned int FGHI, unsigned int k, unsigned int s, unsigned int i) { return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); } -static void Round1(unsigned int *a, unsigned int b, unsigned int c, +static void md5_Round1(unsigned int *a, unsigned int b, unsigned int c, unsigned int d,unsigned int k, unsigned int s, unsigned int i) { - *a = Round(*a, b, F(b,c,d), k, s, i); + *a = md5_Round(*a, b, md5_F(b,c,d), k, s, i); } -static void Round2(unsigned int *a, unsigned int b, unsigned int c, +static void md5_Round2(unsigned int *a, unsigned int b, unsigned int c, unsigned int d,unsigned int k, unsigned int s, unsigned int i) { - *a = Round(*a, b, G(b,c,d), k, s, i); + *a = md5_Round(*a, b, md5_G(b,c,d), k, s, i); } -static void Round3(unsigned int *a, unsigned int b, unsigned int c, +static void md5_Round3(unsigned int *a, unsigned int b, unsigned int c, unsigned int d,unsigned int k, unsigned int s, unsigned int i) { - *a = Round(*a, b, H(b,c,d), k, s, i); + *a = md5_Round(*a, b, md5_H(b,c,d), k, s, i); } -static void Round4(unsigned int *a, unsigned int b, unsigned int c, +static void md5_Round4(unsigned int *a, unsigned int b, unsigned int c, unsigned int d,unsigned int k, unsigned int s, unsigned int i) { - *a = Round(*a, b, I(b,c,d), k, s, i); + *a = md5_Round(*a, b, md5_I(b,c,d), k, s, i); } -static void MD5_Round_Calculate(const unsigned char *block, +static void md5_Round_Calculate(const unsigned char *block, unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) { //create X It is since it is required. unsigned int X[16]; //512bit 64byte - int j,k; + int j, k; //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) - unsigned int A=*A2, B=*B2, C=*C2, D=*D2; - unsigned int AA = A,BB = B,CC = C,DD = D; + unsigned int A = *A2, B = *B2, C = *C2, D = *D2; + unsigned int AA = A, BB = B, CC = C, DD = D; //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 pX = X; //Copy block(padding_message) i into X - for (j=0,k=0; j<64; j+=4,k++) - X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion - | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC - | ( ((unsigned int )block[j+2]) << 16 ) - | ( ((unsigned int )block[j+3]) << 24 ); - + for (j = 0, k = 0; j < 64; j += 4, k++) { + X[k] = ((unsigned int)block[j]) // 8byte*4 -> 32byte conversion + | (((unsigned int)block[j+1]) << 8) // A function called Decode as used in the field of RFC + | (((unsigned int)block[j+2]) << 16) + | (((unsigned int)block[j+3]) << 24); + } //Round 1 - Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); - Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); - Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); - Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); + md5_Round1(&A,B,C,D, 0, 7, 0); md5_Round1(&D,A,B,C, 1, 12, 1); md5_Round1(&C,D,A,B, 2, 17, 2); md5_Round1(&B,C,D,A, 3, 22, 3); + md5_Round1(&A,B,C,D, 4, 7, 4); md5_Round1(&D,A,B,C, 5, 12, 5); md5_Round1(&C,D,A,B, 6, 17, 6); md5_Round1(&B,C,D,A, 7, 22, 7); + md5_Round1(&A,B,C,D, 8, 7, 8); md5_Round1(&D,A,B,C, 9, 12, 9); md5_Round1(&C,D,A,B, 10, 17, 10); md5_Round1(&B,C,D,A, 11, 22, 11); + md5_Round1(&A,B,C,D, 12, 7, 12); md5_Round1(&D,A,B,C, 13, 12, 13); md5_Round1(&C,D,A,B, 14, 17, 14); md5_Round1(&B,C,D,A, 15, 22, 15); //Round 2 - Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); - Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); - Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); - Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); + md5_Round2(&A,B,C,D, 1, 5, 16); md5_Round2(&D,A,B,C, 6, 9, 17); md5_Round2(&C,D,A,B, 11, 14, 18); md5_Round2(&B,C,D,A, 0, 20, 19); + md5_Round2(&A,B,C,D, 5, 5, 20); md5_Round2(&D,A,B,C, 10, 9, 21); md5_Round2(&C,D,A,B, 15, 14, 22); md5_Round2(&B,C,D,A, 4, 20, 23); + md5_Round2(&A,B,C,D, 9, 5, 24); md5_Round2(&D,A,B,C, 14, 9, 25); md5_Round2(&C,D,A,B, 3, 14, 26); md5_Round2(&B,C,D,A, 8, 20, 27); + md5_Round2(&A,B,C,D, 13, 5, 28); md5_Round2(&D,A,B,C, 2, 9, 29); md5_Round2(&C,D,A,B, 7, 14, 30); md5_Round2(&B,C,D,A, 12, 20, 31); //Round 3 - Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); - Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); - Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); - Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); + md5_Round3(&A,B,C,D, 5, 4, 32); md5_Round3(&D,A,B,C, 8, 11, 33); md5_Round3(&C,D,A,B, 11, 16, 34); md5_Round3(&B,C,D,A, 14, 23, 35); + md5_Round3(&A,B,C,D, 1, 4, 36); md5_Round3(&D,A,B,C, 4, 11, 37); md5_Round3(&C,D,A,B, 7, 16, 38); md5_Round3(&B,C,D,A, 10, 23, 39); + md5_Round3(&A,B,C,D, 13, 4, 40); md5_Round3(&D,A,B,C, 0, 11, 41); md5_Round3(&C,D,A,B, 3, 16, 42); md5_Round3(&B,C,D,A, 6, 23, 43); + md5_Round3(&A,B,C,D, 9, 4, 44); md5_Round3(&D,A,B,C, 12, 11, 45); md5_Round3(&C,D,A,B, 15, 16, 46); md5_Round3(&B,C,D,A, 2, 23, 47); //Round 4 - Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); - Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); - Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); - Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); + md5_Round4(&A,B,C,D, 0, 6, 48); md5_Round4(&D,A,B,C, 7, 10, 49); md5_Round4(&C,D,A,B, 14, 15, 50); md5_Round4(&B,C,D,A, 5, 21, 51); + md5_Round4(&A,B,C,D, 12, 6, 52); md5_Round4(&D,A,B,C, 3, 10, 53); md5_Round4(&C,D,A,B, 10, 15, 54); md5_Round4(&B,C,D,A, 1, 21, 55); + md5_Round4(&A,B,C,D, 8, 6, 56); md5_Round4(&D,A,B,C, 15, 10, 57); md5_Round4(&C,D,A,B, 6, 15, 58); md5_Round4(&B,C,D,A, 13, 21, 59); + md5_Round4(&A,B,C,D, 4, 6, 60); md5_Round4(&D,A,B,C, 11, 10, 61); md5_Round4(&C,D,A,B, 2, 15, 62); md5_Round4(&B,C,D,A, 9, 21, 63); // Then perform the following additions. (let's add) *A2 = A + AA; @@ -164,7 +167,8 @@ static void MD5_Round_Calculate(const unsigned char *block, memset(pX, 0, sizeof(X)); } -static void MD5_String2binary(const char * string, unsigned char * output) +/// @copydoc md5_interface::binary() +static void md5_string2binary(const char *string, unsigned char *output) { //var /*8bit*/ @@ -196,7 +200,7 @@ static void MD5_String2binary(const char * string, unsigned char * output) //1-2 Repeat calculation until length becomes less than 64 bytes. for (i=string_byte_len; 64<=i; i-=64,pstring+=64) - MD5_Round_Calculate(pstring, A,B,C,D); + md5_Round_Calculate(pstring, A,B,C,D); //1-3 copy_len = string_byte_len % 64; //The number of bytes which remained is computed. @@ -207,7 +211,7 @@ static void MD5_String2binary(const char * string, unsigned char * output) //1-4 //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. if (56 <= copy_len) { - MD5_Round_Calculate(padding_message, A,B,C,D); + md5_Round_Calculate(padding_message, A,B,C,D); memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. } @@ -219,31 +223,26 @@ static void MD5_String2binary(const char * string, unsigned char * output) if (UINT_MAX / 8 < string_byte_len) { unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; memcpy(&padding_message[60], &high, 4); - } else + } else { memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. + } //Step 4.Process Message in 16-Word Blocks (calculation of MD5) - MD5_Round_Calculate(padding_message, A,B,C,D); + md5_Round_Calculate(padding_message, A,B,C,D); //Step 5.Output (output) memcpy(output,msg_digest,16); } -//------------------------------------------------------------------- -// The function for the exteriors - -/** output is the coded binary in the character sequence which wants to code string. */ -void MD5_Binary(const char * string, unsigned char * output) -{ - MD5_String2binary(string,output); -} - -/** output is the coded character sequence in the character sequence which wants to code string. */ -void MD5_String(const char *string, char *output) +/// @copydoc md5_interface::string() +void md5_string(const char *string, char *output) { unsigned char digest[16]; - MD5_String2binary(string,digest); + nullpo_retv(string); + nullpo_retv(output); + + md5->binary(string,digest); snprintf(output, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[ 0], digest[ 1], digest[ 2], digest[ 3], digest[ 4], digest[ 5], digest[ 6], digest[ 7], @@ -251,11 +250,24 @@ void MD5_String(const char *string, char *output) digest[12], digest[13], digest[14], digest[15]); } -/** output is a sequence of non-zero characters to be used as password salt. */ -void MD5_Salt(unsigned int len, char * output) +/// @copydoc md5_interface::salt(); +void md5_salt(int len, char *output) { - unsigned int i; - for( i = 0; i < len; ++i ) + int i; + Assert_retv(len > 0); + + for (i = 0; i < len; ++i) output[i] = (char)(1 + rnd() % 255); } + +/** + * Interface base initialization. + */ +void md5_defaults(void) +{ + md5 = &md5_s; + md5->binary = md5_string2binary; + md5->string = md5_string; + md5->salt = md5_salt; +} diff --git a/src/common/md5calc.h b/src/common/md5calc.h index 0294c3ca1..b4d4995f9 100644 --- a/src/common/md5calc.h +++ b/src/common/md5calc.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -21,10 +21,46 @@ #ifndef COMMON_MD5CALC_H #define COMMON_MD5CALC_H +#include "common/hercules.h" + +/** @file + * md5 calculation algorithm. + * + * The source code referred to the following URL. + * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html + */ + +/// The md5 interface +struct md5_interface { + /** + * Hashes a string, returning the hash in string format. + * + * @param[in] string The source string (NUL terminated). + * @param[out] output Output buffer (at least 33 bytes available). + */ + void (*string) (const char *string, char *output); + + /** + * Hashes a string, returning the buffer in binary format. + * + * @param[in] string The source string. + * @param[out] output Output buffer (at least 16 bytes available). + */ + void (*binary) (const char *string, unsigned char *output); + + /** + * Generates a random salt. + * + * @param[in] len The desired salt length. + * @param[out] output The output buffer (at least len bytes available). + */ + void (*salt) (int len, char *output); +}; + #ifdef HERCULES_CORE -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); +void md5_defaults(void); #endif // HERCULES_CORE +HPShared struct md5_interface *md5; ///< Pointer to the md5 interface. + #endif /* COMMON_MD5CALC_H */ diff --git a/src/common/mmo.h b/src/common/mmo.h index a2080d900..0a5d9d053 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -125,29 +125,69 @@ //Official Limit: 2.1b ( the var that stores the money doesn't go much higher than this by default ) #define MAX_BANK_ZENY INT_MAX +#ifndef MAX_LEVEL #define MAX_LEVEL 175 +#endif #define MAX_FAME 1000000000 #define MAX_CART 100 +#ifndef MAX_SKILL #define MAX_SKILL 1478 +#endif +#ifndef MAX_SKILL_ID #define MAX_SKILL_ID 10015 // [Ind/Hercules] max used skill ID +#endif +#ifndef MAX_SKILL_TREE // Update this max as necessary. 86 is the value needed for Expanded Super Novice. #define MAX_SKILL_TREE 86 +#endif +#ifndef DEFAULT_WALK_SPEED #define DEFAULT_WALK_SPEED 150 +#endif +#ifndef MIN_WALK_SPEED #define MIN_WALK_SPEED 20 /* below 20 clips animation */ +#endif +#ifndef MAX_WALK_SPEED #define MAX_WALK_SPEED 1000 +#endif +#ifndef MAX_STORAGE #define MAX_STORAGE 600 +#endif +#ifndef MAX_GUILD_STORAGE #define MAX_GUILD_STORAGE 600 +#endif +#ifndef MAX_PARTY #define MAX_PARTY 12 +#endif +#ifndef BASE_GUILD_SIZE #define BASE_GUILD_SIZE 16 // Base guild members (without GD_EXTENSION) +#endif +#ifndef MAX_GUILD #define MAX_GUILD (BASE_GUILD_SIZE+10*6) // Increased max guild members +6 per 1 extension levels [Lupus] +#endif +#ifndef MAX_GUILDPOSITION #define MAX_GUILDPOSITION 20 // Increased max guild positions to accomodate for all members [Valaris] (removed) [PoW] +#endif +#ifndef MAX_GUILDEXPULSION #define MAX_GUILDEXPULSION 32 +#endif +#ifndef MAX_GUILDALLIANCE #define MAX_GUILDALLIANCE 16 +#endif +#ifndef MAX_GUILDSKILL #define MAX_GUILDSKILL 15 // Increased max guild skills because of new skills [Sara-chan] +#endif +#ifndef MAX_GUILDLEVEL #define MAX_GUILDLEVEL 50 +#endif +#ifndef MAX_GUARDIANS #define MAX_GUARDIANS 8 // Local max per castle. [Skotlex] +#endif +#ifndef MAX_QUEST_OBJECTIVES #define MAX_QUEST_OBJECTIVES 3 // Max quest objectives for a quest +#endif +#ifndef MAX_START_ITEMS #define MAX_START_ITEMS 32 // Max number of items allowed to be given to a char whenever it's created. [mkbu95] +#endif // for produce #define MIN_ATTRIBUTE 0 @@ -170,7 +210,9 @@ #define MAP_NAME_LENGTH (11 + 1) #define MAP_NAME_LENGTH_EXT (MAP_NAME_LENGTH + 4) +#ifndef MAX_FRIENDS #define MAX_FRIENDS 40 +#endif #define MAX_MEMOPOINTS 3 // Size of the fame list arrays. @@ -186,8 +228,12 @@ #define MAX_GUILDMES2 120 // Base Homun skill. +#ifndef HM_SKILLBASE #define HM_SKILLBASE 8001 +#endif +#ifndef MAX_HOMUNSKILL #define MAX_HOMUNSKILL 43 +#endif // Mail System #define MAIL_MAX_INBOX 30 @@ -195,13 +241,23 @@ #define MAIL_BODY_LENGTH 200 // Mercenary System +#ifndef MC_SKILLBASE #define MC_SKILLBASE 8201 +#endif +#ifndef MAX_MERCSKILL #define MAX_MERCSKILL 40 +#endif // Elemental System +#ifndef MAX_ELEMENTALSKILL #define MAX_ELEMENTALSKILL 42 +#endif +#ifndef EL_SKILLBASE #define EL_SKILLBASE 8401 +#endif +#ifndef MAX_ELESKILLTREE #define MAX_ELESKILLTREE 3 +#endif // The following system marks a different job ID system used by the map server, // which makes a lot more sense than the normal one. [Skotlex] @@ -233,7 +289,9 @@ enum item_types { IT_AMMO, //10 IT_DELAYCONSUME,//11 IT_CASH = 18, +#ifndef IT_MAX IT_MAX +#endif }; #define INDEX_NOT_FOUND (-1) ///< Used as invalid/failure value in various functions that return an index @@ -253,6 +311,11 @@ struct quest { enum quest_state state; ///< Current quest state }; +enum attribute_flag { + ATTR_NONE = 0, + ATTR_BROKEN = 1, +}; + struct item { int id; short nameid; @@ -325,7 +388,9 @@ enum e_item_bound_type { IBT_GUILD = 0x2, IBT_PARTY = 0x3, IBT_CHARACTER = 0x4, +#ifndef IBT_MAX IBT_MAX = 0x4, +#endif }; enum { @@ -505,7 +570,7 @@ struct mmo_charstatus { int bank_vault; short class_; - unsigned int status_point,skill_point; + int status_point, skill_point; int hp,max_hp,sp,max_sp; unsigned int option; short manner; // Defines how many minutes a char will be muted, each negative point is equivalent to a minute. @@ -525,7 +590,7 @@ struct mmo_charstatus { short robe; char name[NAME_LENGTH]; - unsigned int base_level,job_level; + int base_level, job_level; short str,agi,vit,int_,dex,luk; unsigned char slot,sex; @@ -772,7 +837,9 @@ enum { GD_RESTORE=10012, GD_EMERGENCYCALL=10013, GD_DEVELOPMENT=10014, +#ifndef GD_MAX GD_MAX, +#endif }; //These mark the ID of the jobs, as expected by the client. [Skotlex] @@ -929,7 +996,9 @@ enum { JOB_OBORO, JOB_REBELLION = 4215, +#ifndef JOB_MAX JOB_MAX, +#endif }; //Total number of classes (for data storage) @@ -966,7 +1035,9 @@ enum weapon_type { W_GRENADE, //21 W_HUUMA, //22 W_2HSTAFF, //23 +#ifndef MAX_SINGLE_WEAPON_TYPE MAX_SINGLE_WEAPON_TYPE, +#endif // dual-wield constants W_DOUBLE_DD, ///< 2 daggers W_DOUBLE_SS, ///< 2 swords @@ -974,7 +1045,9 @@ enum weapon_type { W_DOUBLE_DS, ///< dagger + sword W_DOUBLE_DA, ///< dagger + axe W_DOUBLE_SA, ///< sword + axe +#ifndef MAX_WEAPON_TYPE MAX_WEAPON_TYPE, +#endif }; enum ammo_type { diff --git a/src/common/mutex.c b/src/common/mutex.c index 0f02b153f..bdc2fb4dc 100644 --- a/src/common/mutex.c +++ b/src/common/mutex.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) rAthena Project (www.rathena.org) * * Hercules is free software: you can redistribute it and/or modify @@ -34,7 +34,14 @@ #include <sys/time.h> #endif -struct ramutex{ +/** @file + * Implementation of the mutex interface. + */ + +struct mutex_interface mutex_s; +struct mutex_interface *mutex; + +struct mutex_data { #ifdef WIN32 CRITICAL_SECTION hMutex; #else @@ -42,32 +49,26 @@ struct ramutex{ #endif }; -struct racond{ +struct cond_data { #ifdef WIN32 HANDLE events[2]; ra_align(8) volatile LONG nWaiters; CRITICAL_SECTION waiters_lock; - #define EVENT_COND_SIGNAL 0 #define EVENT_COND_BROADCAST 1 - #else pthread_cond_t hCond; #endif }; -//////////////////// -// Mutex -// -// Implementation: -// +/* Mutex */ -ramutex *ramutex_create(void) { - struct ramutex *m; - - m = (struct ramutex*)aMalloc( sizeof(struct ramutex) ); +/// @copydoc mutex_interface::create() +struct mutex_data *mutex_create(void) +{ + struct mutex_data *m = aMalloc(sizeof(struct mutex_data)); if (m == NULL) { - ShowFatalError("ramutex_create: OOM while allocating %"PRIuS" bytes.\n", sizeof(struct ramutex)); + ShowFatalError("ramutex_create: OOM while allocating %"PRIuS" bytes.\n", sizeof(struct mutex_data)); return NULL; } @@ -78,10 +79,11 @@ ramutex *ramutex_create(void) { #endif return m; -}//end: ramutex_create() - -void ramutex_destroy(ramutex *m) { +} +/// @copydoc mutex_interface::destroy() +void mutex_destroy(struct mutex_data *m) +{ #ifdef WIN32 DeleteCriticalSection(&m->hMutex); #else @@ -89,53 +91,49 @@ void ramutex_destroy(ramutex *m) { #endif aFree(m); +} -}//end: ramutex_destroy() - -void ramutex_lock(ramutex *m) { - +/// @copydoc mutex_interface::lock() +void mutex_lock(struct mutex_data *m) +{ #ifdef WIN32 EnterCriticalSection(&m->hMutex); #else pthread_mutex_lock(&m->hMutex); #endif -}//end: ramutex_lock +} -bool ramutex_trylock(ramutex *m) { +/// @copydoc mutex_interface::trylock() +bool mutex_trylock(struct mutex_data *m) +{ #ifdef WIN32 - if(TryEnterCriticalSection(&m->hMutex) != FALSE) + if (TryEnterCriticalSection(&m->hMutex) != FALSE) return true; - - return false; #else - if(pthread_mutex_trylock(&m->hMutex) == 0) + if (pthread_mutex_trylock(&m->hMutex) == 0) return true; - - return false; #endif -}//end: ramutex_trylock() + return false; +} -void ramutex_unlock(ramutex *m) { +/// @copydoc mutex_interface::unlock() +void mutex_unlock(struct mutex_data *m) +{ #ifdef WIN32 LeaveCriticalSection(&m->hMutex); #else pthread_mutex_unlock(&m->hMutex); #endif +} -}//end: ramutex_unlock() - -/////////////// -// Condition Variables -// -// Implementation: -// +/* Conditional variable */ -racond *racond_create(void) { - struct racond *c; - - c = (struct racond*)aMalloc( sizeof(struct racond) ); +/// @copydoc mutex_interface::cond_create() +struct cond_data *cond_create(void) +{ + struct cond_data *c = aMalloc(sizeof(struct cond_data)); if (c == NULL) { - ShowFatalError("racond_create: OOM while allocating %"PRIuS" bytes\n", sizeof(struct racond)); + ShowFatalError("racond_create: OOM while allocating %"PRIuS" bytes\n", sizeof(struct cond_data)); return NULL; } @@ -149,21 +147,25 @@ racond *racond_create(void) { #endif return c; -}//end: racond_create() +} -void racond_destroy(racond *c) { +/// @copydoc mutex_interface::cond_destroy() +void cond_destroy(struct cond_data *c) +{ #ifdef WIN32 - CloseHandle( c->events[ EVENT_COND_SIGNAL ] ); - CloseHandle( c->events[ EVENT_COND_BROADCAST ] ); - DeleteCriticalSection( &c->waiters_lock ); + CloseHandle(c->events[EVENT_COND_SIGNAL]); + CloseHandle(c->events[EVENT_COND_BROADCAST]); + DeleteCriticalSection(&c->waiters_lock); #else pthread_cond_destroy(&c->hCond); #endif aFree(c); -}//end: racond_destroy() +} -void racond_wait(racond *c, ramutex *m, sysint timeout_ticks) { +/// @copydoc mutex_interface::cond_wait() +void cond_wait(struct cond_data *c, struct mutex_data *m, sysint timeout_ticks) +{ #ifdef WIN32 register DWORD ms; int result; @@ -173,7 +175,7 @@ void racond_wait(racond *c, ramutex *m, sysint timeout_ticks) { c->nWaiters++; LeaveCriticalSection(&c->waiters_lock); - if(timeout_ticks < 0) + if (timeout_ticks < 0) ms = INFINITE; else ms = (timeout_ticks > MAXDWORD) ? (MAXDWORD - 1) : (DWORD)timeout_ticks; @@ -181,27 +183,27 @@ void racond_wait(racond *c, ramutex *m, sysint timeout_ticks) { // we can release the mutex (m) here, cause win's // manual reset events maintain state when used with // SetEvent() - ramutex_unlock(m); + mutex->unlock(m); result = WaitForMultipleObjects(2, c->events, FALSE, ms); EnterCriticalSection(&c->waiters_lock); c->nWaiters--; - if( (result == WAIT_OBJECT_0 + EVENT_COND_BROADCAST) && (c->nWaiters == 0) ) + if ((result == WAIT_OBJECT_0 + EVENT_COND_BROADCAST) && (c->nWaiters == 0)) is_last = true; // Broadcast called! LeaveCriticalSection(&c->waiters_lock); // we are the last waiter that has to be notified, or to stop waiting // so we have to do a manual reset - if(is_last == true) + if (is_last == true) ResetEvent( c->events[EVENT_COND_BROADCAST] ); - ramutex_lock(m); + mutex->lock(m); #else - if(timeout_ticks < 0){ - pthread_cond_wait( &c->hCond, &m->hMutex ); - }else{ + if (timeout_ticks < 0) { + pthread_cond_wait(&c->hCond, &m->hMutex); + } else { struct timespec wtime; int64 exact_timeout = timer->gettick() + timeout_ticks; @@ -210,11 +212,12 @@ void racond_wait(racond *c, ramutex *m, sysint timeout_ticks) { pthread_cond_timedwait( &c->hCond, &m->hMutex, &wtime); } - #endif -}//end: racond_wait() +} -void racond_signal(racond *c) { +/// @copydoc mutex_interface::cond_signal() +void cond_signal(struct cond_data *c) +{ #ifdef WIN32 # if 0 bool has_waiters = false; @@ -225,13 +228,15 @@ void racond_signal(racond *c) { if(has_waiters == true) # endif // 0 - SetEvent( c->events[ EVENT_COND_SIGNAL ] ); + SetEvent(c->events[EVENT_COND_SIGNAL]); #else pthread_cond_signal(&c->hCond); #endif -}//end: racond_signal() +} -void racond_broadcast(racond *c) { +/// @copydoc mutex_interface::cond_broadcast() +void cond_broadcast(struct cond_data *c) +{ #ifdef WIN32 # if 0 bool has_waiters = false; @@ -242,8 +247,27 @@ void racond_broadcast(racond *c) { if(has_waiters == true) # endif // 0 - SetEvent( c->events[ EVENT_COND_BROADCAST ] ); + SetEvent(c->events[EVENT_COND_BROADCAST]); #else pthread_cond_broadcast(&c->hCond); #endif -}//end: racond_broadcast() +} + +/** + * Interface base initialization. + */ +void mutex_defaults(void) +{ + mutex = &mutex_s; + mutex->create = mutex_create; + mutex->destroy = mutex_destroy; + mutex->lock = mutex_lock; + mutex->trylock = mutex_trylock; + mutex->unlock = mutex_unlock; + + mutex->cond_create = cond_create; + mutex->cond_destroy = cond_destroy; + mutex->cond_wait = cond_wait; + mutex->cond_signal = cond_signal; + mutex->cond_broadcast = cond_broadcast; +} diff --git a/src/common/mutex.h b/src/common/mutex.h index e49791493..0569fb0da 100644 --- a/src/common/mutex.h +++ b/src/common/mutex.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) rAthena Project (www.rathena.org) * * Hercules is free software: you can redistribute it and/or modify @@ -21,91 +21,111 @@ #ifndef COMMON_MUTEX_H #define COMMON_MUTEX_H -#include "common/cbasetypes.h" +#include "common/hercules.h" -typedef struct ramutex ramutex; // Mutex -typedef struct racond racond; // Condition Var - -#ifdef HERCULES_CORE -/** - * Creates a Mutex - * - * @return not NULL +/** @file + * Mutex and conditional variables implementation for Hercules. */ -ramutex *ramutex_create(void); -/** - * Destroys a Mutex - * - * @param m - the mutex to destroy - */ -void ramutex_destroy(ramutex *m); +/* Opaque types */ -/** - * Gets a lock - * - * @param m - the mutex to lock - */ -void ramutex_lock(ramutex *m); +struct mutex_data; ///< Mutex +struct cond_data; ///< Conditional variable -/** - * Trys to get the Lock - * - * @param m - the mutex try to lock - * - * @return boolean (true = got the lock) - */ -bool ramutex_trylock(ramutex *m); +/* Interface */ -/** - * Unlocks a mutex - * - * @param m - the mutex to unlock - */ -void ramutex_unlock(ramutex *m); +/// The mutex interface. +struct mutex_interface { + /** + * Creates a mutex. + * + * @return The created mutex. + */ + struct mutex_data *(*create) (void); + /** + * Destroys a mutex. + * + * @param m the mutex to destroy. + */ + void (*destroy) (struct mutex_data *m); -/** - * Creates a Condition variable - * - * @return not NULL - */ -racond *racond_create(void); + /** + * Gets a lock. + * + * This function blocks until the lock can be acquired. + * + * @param m The mutex to lock. + */ + void (*lock) (struct mutex_data *m); -/** - * Destroy a Condition variable - * - * @param c - the condition variable to destroy - */ -void racond_destroy(racond *c); + /** + * Tries to get a lock. + * + * This function returns immediately. + * + * @param m The mutex to try to lock. + * @return success status. + * @retval true if the lock was acquired. + * @retval false if the mutex couldn't be locked. + */ + bool (*trylock) (struct mutex_data *m); -/** - * 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); + /** + * Unlocks a mutex. + * + * @param m The mutex to unlock. + */ + void (*unlock) (struct mutex_data *m); -/** - * Sets the given condition var to signaled state - * - * @param c - condition var to set in signaled state. - * - * @note: - * Only one waiter gets notified. - */ -void racond_signal(racond *c); + /** + * Creates a conditional variable. + * + * @return the created conditional variable. + */ + struct cond_data *(*cond_create) (void); -/** - * 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); + /** + * Destroys a conditional variable. + * + * @param c the conditional variable to destroy. + */ + void (*cond_destroy) (struct cond_data *c); + + /** + * 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 (*cond_wait) (struct cond_data *c, struct mutex_data *m, sysint timeout_ticks); + + /** + * Sets the given condition var to signaled state. + * + * @remark + * Only one waiter gets notified. + * + * @param c Condition var to set in signaled state. + */ + void (*cond_signal) (struct cond_data *c); + + /** + * Sets notifies all waiting threads thats signaled. + * + * @remark + * All Waiters getting notified. + * + * @param c Condition var to set in signaled state. + */ + void (*cond_broadcast) (struct cond_data *c); +}; + +#ifdef HERCULES_CORE +void mutex_defaults(void); #endif // HERCULES_CORE +HPShared struct mutex_interface *mutex; ///< Pointer to the mutex interface. + #endif /* COMMON_MUTEX_H */ diff --git a/src/common/random.c b/src/common/random.c index ba70a5c1d..b3a13e054 100644 --- a/src/common/random.c +++ b/src/common/random.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include "common/timer.h" // gettick #include <mt19937ar/mt19937ar.h> // init_genrand, genrand_int32, genrand_res53 +#include <stdlib.h> #if defined(WIN32) # include "common/winapi.h" @@ -35,8 +36,14 @@ # include <unistd.h> #endif +/** @file + * Implementation of the random number generator interface. + */ + +struct rnd_interface rnd_s; +struct rnd_interface *rnd; -/// Initializes the random number generator with an appropriate seed. +/// @copydoc rnd_interface::init() void rnd_init(void) { unsigned long seed = (unsigned long)timer->gettick(); @@ -53,53 +60,64 @@ void rnd_init(void) #endif // HAVE_GETTID #endif init_genrand(seed); + + // Also initialize the stdlib rng, just in case it's used somewhere. + srand((unsigned int)seed); } +/// @copydoc rnd_interface::final() +void rnd_final(void) +{ +} -/// Initializes the random number generator. +/// @copydoc rnd_interface::seed() void rnd_seed(uint32 seed) { init_genrand(seed); } - -/// Generates a random number in the interval [0, SINT32_MAX] -int32 rnd(void) +/// @copydoc rnd_interface::random() +int32 rnd_random(void) { return (int32)genrand_int31(); } - -/// Generates a random number in the interval [0, dice_faces) -/// NOTE: interval is open ended, so dice_faces is excluded (unless it's 0) +/// @copydoc rnd_interface::roll() uint32 rnd_roll(uint32 dice_faces) { - return (uint32)(rnd_uniform()*dice_faces); + return (uint32)(rnd->uniform()*dice_faces); } - -/// Generates a random number in the interval [min, max] -/// Returns min if range is invalid. +/// @copydoc rnd_interface::value() int32 rnd_value(int32 min, int32 max) { - if( min >= max ) + if (min >= max) return min; - return min + (int32)(rnd_uniform()*(max-min+1)); + return min + (int32)(rnd->uniform()*(max-min+1)); } - -/// Generates a random number in the interval [0.0, 1.0) -/// NOTE: interval is open ended, so 1.0 is excluded +/// @copydoc rnd_interface::uniform() double rnd_uniform(void) { return ((uint32)genrand_int32())*(1.0/4294967296.0);// divided by 2^32 } - -/// Generates a random number in the interval [0.0, 1.0) with 53-bit resolution -/// NOTE: interval is open ended, so 1.0 is excluded -/// NOTE: 53 bits is the maximum precision of a double +/// @copydoc rnd_interface::uniform53() double rnd_uniform53(void) { return genrand_res53(); } + +/// Interface base initialization. +void rnd_defaults(void) +{ + rnd = &rnd_s; + rnd->init = rnd_init; + rnd->final = rnd_final; + rnd->seed = rnd_seed; + rnd->random = rnd_random; + rnd->roll = rnd_roll; + rnd->value = rnd_value; + rnd->uniform = rnd_uniform; + rnd->uniform53 = rnd_uniform53; +} diff --git a/src/common/random.h b/src/common/random.h index 0b5abbcdc..1b249ef19 100644 --- a/src/common/random.h +++ b/src/common/random.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -21,17 +21,81 @@ #ifndef COMMON_RANDOM_H #define COMMON_RANDOM_H -#include "common/cbasetypes.h" +#include "common/hercules.h" + +/** @file + * The random number generator interface. + */ + +/// Random interface. +struct rnd_interface { + /** + * Interface initialization. + * + * During initialization, the RNG is seeded with a random seed. + */ + void (*init) (void); + + /// Interface finalization. + void (*final) (void); + + /** + * Re-seeds the random number generator. + * + * @param seed The new seed. + */ + void (*seed) (uint32 seed); + + /** + * Generates a random number in the interval [0, SINT32_MAX]. + */ + int32 (*random) (void); + + /** + * Generates a random number in the interval [0, dice_faces). + * + * @remark + * interval is open ended, so dice_faces is excluded (unless it's 0) + */ + uint32 (*roll) (uint32 dice_faces); + + /** + * Generates a random number in the interval [min, max]. + * + * @retval min if range is invalid. + */ + int32 (*value) (int32 min, int32 max); + + /** + * Generates a random number in the interval [0.0, 1.0) + * + * @remark + * interval is open ended, so 1.0 is excluded + */ + double (*uniform) (void); + + /** + * Generates a random number in the interval [0.0, 1.0) with 53-bit resolution. + * + * 53 bits is the maximum precision of a double. + * + * @remark + * interval is open ended, so 1.0 is excluded + */ + double (*uniform53) (void); +}; + +/** + * Utlity macro to call the frequently used rnd_interface#random(). + * + * @related rnd_interface. + */ +#define rnd() rnd->random() #ifdef HERCULES_CORE -void rnd_init(void); -void rnd_seed(uint32); - -int32 rnd(void);// [0, SINT32_MAX] -uint32 rnd_roll(uint32 dice_faces);// [0, dice_faces) -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) +void rnd_defaults(void); #endif // HERCULES_CORE +HPShared struct rnd_interface *rnd; ///< Pointer to the random interface. + #endif /* COMMON_RANDOM_H */ diff --git a/src/common/showmsg.c b/src/common/showmsg.c index 8ed8efc1d..d8864684d 100644 --- a/src/common/showmsg.c +++ b/src/common/showmsg.c @@ -804,7 +804,11 @@ void showmsg_showConfigWarning(struct config_setting_t *config, const char *stri StrBuf->AppendStr(&buf, string); StrBuf->Printf(&buf, " (%s:%u)\n", config_setting_source_file(config), config_setting_source_line(config)); va_start(ap, string); +#ifdef BUILDBOT + vShowMessage_(MSG_ERROR, StrBuf->Value(&buf), ap); +#else // BUILDBOT vShowMessage_(MSG_WARNING, StrBuf->Value(&buf), ap); +#endif // BUILDBOT va_end(ap); StrBuf->Destroy(&buf); } diff --git a/src/common/showmsg.h b/src/common/showmsg.h index 7b48d0df2..303c8dd28 100644 --- a/src/common/showmsg.h +++ b/src/common/showmsg.h @@ -130,7 +130,11 @@ struct showmsg_interface { #define ShowSQL(fmt, ...) (showmsg->showSQL((fmt), ##__VA_ARGS__)) #define ShowInfo(fmt, ...) (showmsg->showInfo((fmt), ##__VA_ARGS__)) #define ShowNotice(fmt, ...) (showmsg->showNotice((fmt), ##__VA_ARGS__)) +#ifdef BUILDBOT +#define ShowWarning(fmt, ...) (showmsg->showError((fmt), ##__VA_ARGS__)) +#else // BUILDBOT #define ShowWarning(fmt, ...) (showmsg->showWarning((fmt), ##__VA_ARGS__)) +#endif // BUILDBOT #define ShowDebug(fmt, ...) (showmsg->showDebug((fmt), ##__VA_ARGS__)) #define ShowError(fmt, ...) (showmsg->showError((fmt), ##__VA_ARGS__)) #define ShowFatalError(fmt, ...) (showmsg->showFatalError((fmt), ##__VA_ARGS__)) diff --git a/src/common/socket.c b/src/common/socket.c index 5d4ea06a0..af5e8aa73 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -38,35 +38,39 @@ #include <stdlib.h> #include <sys/types.h> +#ifdef SOCKET_EPOLL +#include <sys/epoll.h> +#endif // SOCKET_EPOLL + #ifdef WIN32 # include "common/winapi.h" -#else +#else // WIN32 # include <arpa/inet.h> # include <errno.h> # include <net/if.h> # include <netdb.h> #if defined __linux__ || defined __linux # include <linux/tcp.h> -#else +#else // defined __linux__ || defined __linux # include <netinet/in.h> # include <netinet/tcp.h> -#endif +#endif // defined __linux__ || defined __linux # 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 +#ifndef SIOCGIFCONF +# include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori] +#endif // SIOCGIFCONF +#ifndef FIONBIO +# include <sys/filio.h> // FIONBIO on Solaris [FlavioJS] +#endif // FIONBIO -# ifdef HAVE_SETRLIMIT -# include <sys/resource.h> -# endif -#endif +#ifdef HAVE_SETRLIMIT +# include <sys/resource.h> +#endif // HAVE_SETRLIMIT +#endif // WIN32 /** * Socket Interface Source @@ -82,7 +86,7 @@ struct socket_data **session; void send_shortlist_add_fd(int fd); // Do pending network sends (and eof handling) from the shortlist. void send_shortlist_do_sends(void); -#endif +#endif // SEND_SHORTLIST ///////////////////////////////////////////////////////////////////// #if defined(WIN32) @@ -212,7 +216,7 @@ char* sErr(int code) #define sFD_ZERO FD_ZERO ///////////////////////////////////////////////////////////////////// -#else +#else // defined(WIN32) ///////////////////////////////////////////////////////////////////// // nix portability layer @@ -244,29 +248,40 @@ char* sErr(int code) #define sFD_ZERO FD_ZERO ///////////////////////////////////////////////////////////////////// -#endif +#endif // defined(WIN32) ///////////////////////////////////////////////////////////////////// #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 -#endif +#endif // MSG_NOSIGNAL +#ifndef SOCKET_EPOLL +// Select based Event Dispatcher: fd_set readfds; +#else // SOCKET_EPOLL +// Epoll based Event Dispatcher: +static int epoll_maxevents = (FD_SETSIZE / 2); +static int epfd = SOCKET_ERROR; +static struct epoll_event epevent; +static struct epoll_event *epevents = NULL; + +#endif // SOCKET_EPOLL + // Maximum packet size in bytes, which the client is able to handle. // Larger packets cause a buffer overflow and stack corruption. #if PACKETVER >= 20131223 static size_t socket_max_client_packet = 0xFFFF; -#else +#else // PACKETVER >= 20131223 static size_t socket_max_client_packet = 0x6000; -#endif +#endif // PACKETVER >= 20131223 #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 +#endif // SHOW_SERVER_STATS // 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) @@ -282,14 +297,14 @@ static time_t socket_data_last_tick = 0; 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 uint32 send_shortlist_set[(FD_SETSIZE+31)/32];// to know if specific fd's are already in the shortlist -#endif +#endif // SEND_SHORTLIST static int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseFunc func_parse); #ifndef MINICORE int ip_rules = 1; static int connect_check(uint32 ip); -#endif +#endif // MINICORE const char* error_msg(void) { @@ -380,13 +395,13 @@ void setsocketopts(int fd, struct hSockOpt *opt) ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!\n", fd); #ifdef TCP_THIN_LINEAR_TIMEOUTS - if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, (char *)&yes, sizeof(yes))) - ShowWarning("setsocketopts: Unable to set TCP_THIN_LINEAR_TIMEOUTS mode for connection #%d!\n", fd); -#endif + if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, (char *)&yes, sizeof(yes))) + ShowWarning("setsocketopts: Unable to set TCP_THIN_LINEAR_TIMEOUTS mode for connection #%d!\n", fd); +#endif // TCP_THIN_LINEAR_TIMEOUTS #ifdef TCP_THIN_DUPACK - if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, (char *)&yes, sizeof(yes))) - ShowWarning("setsocketopts: Unable to set TCP_THIN_DUPACK mode for connection #%d!\n", fd); -#endif + if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, (char *)&yes, sizeof(yes))) + ShowWarning("setsocketopts: Unable to set TCP_THIN_DUPACK mode for connection #%d!\n", fd); +#endif // TCP_THIN_DUPACK } /*====================================== @@ -398,7 +413,7 @@ void set_eof(int fd) #ifdef SEND_SHORTLIST // Add this socket to the shortlist for eof handling. send_shortlist_add_fd(fd); -#endif +#endif // SEND_SHORTLIST sockt->session[fd]->flag.eof = 1; } } @@ -436,7 +451,7 @@ int recv_to_fifo(int fd) { socket_data_ci += len; } -#endif +#endif // SHOW_SERVER_STATS return 0; } @@ -453,12 +468,12 @@ int send_from_fifo(int fd) len = sSend(fd, (const char *) sockt->session[fd]->wdata, (int)sockt->session[fd]->wdata_size, MSG_NOSIGNAL); if( len == SOCKET_ERROR ) - {//An exception has occurred + { //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 -= sockt->session[fd]->wdata_size; -#endif +#endif // SHOW_SERVER_STATS sockt->session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex] sockt->eof(fd); } @@ -480,7 +495,7 @@ int send_from_fifo(int fd) { socket_data_co += len; } -#endif +#endif // SHOW_SERVER_STATS } return 0; @@ -534,11 +549,27 @@ int connect_client(int listen_fd) { sockt->close(fd); return -1; } -#endif +#endif // MINICORE - if( sockt->fd_max <= fd ) sockt->fd_max = fd + 1; +#ifndef SOCKET_EPOLL + // Select Based Event Dispatcher sFD_SET(fd,&readfds); +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + + if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == SOCKET_ERROR){ + ShowError("connect_client: New Socket #%d failed to add to epoll event dispatcher: %s\n", fd, error_msg()); + sClose(fd); + return -1; + } + +#endif // SOCKET_EPOLL + + if( sockt->fd_max <= fd ) sockt->fd_max = fd + 1; + create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); sockt->session[fd]->client_addr = ntohl(client_address.sin_addr.s_addr); @@ -586,8 +617,26 @@ int make_listen_bind(uint32 ip, uint16 port) exit(EXIT_FAILURE); } + +#ifndef SOCKET_EPOLL + // Select Based Event Dispatcher + sFD_SET(fd,&readfds); + +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + + if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == SOCKET_ERROR){ + ShowError("make_listen_bind: failed to add listener socket #%d to epoll event dispatcher: %s\n", fd, error_msg()); + sClose(fd); + exit(EXIT_FAILURE); + } + +#endif // SOCKET_EPOLL + if(sockt->fd_max <= fd) sockt->fd_max = fd + 1; - sFD_SET(fd, &readfds); + create_session(fd, connect_client, null_send, null_parse); sockt->session[fd]->client_addr = 0; // just listens @@ -637,9 +686,26 @@ int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) { //Now the socket can be made non-blocking. [Skotlex] sockt->set_nonblocking(fd, 1); - if (sockt->fd_max <= fd) sockt->fd_max = fd + 1; + +#ifndef SOCKET_EPOLL + // Select Based Event Dispatcher sFD_SET(fd,&readfds); +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + + if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == SOCKET_ERROR){ + ShowError("make_connection: failed to add socket #%d to epoll event dispatcher: %s\n", fd, error_msg()); + sClose(fd); + return -1; + } + +#endif // SOCKET_EPOLL + + if(sockt->fd_max <= fd) sockt->fd_max = fd + 1; + create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); sockt->session[fd]->client_addr = ntohl(remote_address.sin_addr.s_addr); @@ -668,7 +734,7 @@ static void delete_session(int fd) #ifdef SHOW_SERVER_STATS socket_data_qi -= sockt->session[fd]->rdata_size - sockt->session[fd]->rdata_pos; socket_data_qo -= sockt->session[fd]->wdata_size; -#endif +#endif // SHOW_SERVER_STATS aFree(sockt->session[fd]->rdata); aFree(sockt->session[fd]->wdata); if( sockt->session[fd]->session_data ) @@ -741,7 +807,7 @@ int rfifoskip(int fd, size_t len) s->rdata_pos = s->rdata_pos + len; #ifdef SHOW_SERVER_STATS socket_data_qi -= len; -#endif +#endif // SHOW_SERVER_STATS return 0; } @@ -792,7 +858,7 @@ int wfifoset(int fd, size_t len) s->wdata_size += len; #ifdef SHOW_SERVER_STATS socket_data_qo += len; -#endif +#endif // SHOW_SERVER_STATS //If the interserver has 200% of its normal size full, flush the data. if( s->flag.server && s->wdata_size >= 2*FIFOSIZE_SERVERLINK ) sockt->flush(fd); @@ -806,22 +872,24 @@ int wfifoset(int fd, size_t len) #ifdef SEND_SHORTLIST send_shortlist_add_fd(fd); -#endif +#endif // SEND_SHORTLIST return 0; } int do_sockets(int next) { +#ifndef SOCKET_EPOLL fd_set rfd; struct timeval timeout; +#endif // SOCKET_EPOLL int ret,i; // PRESEND Timers are executed before do_sendrecv and can send packets and/or set sessions to eof. // Send remaining data and process client-side disconnects here. #ifdef SEND_SHORTLIST send_shortlist_do_sends(); -#else +#else // SEND_SHORTLIST for (i = 1; i < sockt->fd_max; i++) { if(!sockt->session[fd] @@ -830,7 +898,10 @@ int do_sockets(int next) if(sockt->session[fd]>wdata_size) sockt->session[fd]>func_send(i); } -#endif +#endif // SEND_SHORTLIST + +#ifndef SOCKET_EPOLL + // Select based Event Dispatcher: // can timeout until the next tick timeout.tv_sec = next/1000; @@ -848,6 +919,20 @@ int do_sockets(int next) } return 0; // interrupted by a signal, just loop and try again } +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + + ret = epoll_wait(epfd, epevents, epoll_maxevents, next); + if(ret == SOCKET_ERROR) + { + if( sErrno != S_EINTR ) + { + ShowFatalError("do_sockets: epoll_wait() failed, %s!\n", error_msg()); + exit(EXIT_FAILURE); + } + return 0; // interrupted by a signal, just loop and try again + } +#endif // SOCKET_EPOLL sockt->last_tick = time(NULL); @@ -859,7 +944,33 @@ int do_sockets(int next) if( sockt->session[fd] ) sockt->session[fd]->func_recv(fd); } -#else +#elif defined(SOCKET_EPOLL) + // epoll based selection + + for( i = 0; i < ret; i++ ) + { + struct epoll_event *it = &epevents[i]; + struct socket_data *sock = sockt->session[ it->data.fd ]; + + if(!sock) + continue; + + if ((it->events & EPOLLERR) || + (it->events & EPOLLHUP) || + (!(it->events & EPOLLIN))) + { + // Got Error on this connection + sockt->eof( it->data.fd ); + + } else if (it->events & EPOLLIN) { + // data wainting + sock->func_recv( it->data.fd ); + + } + + } + +#else // defined(SOCKET_EPOLL) // otherwise assume that the fd_set is a bit-array and enumerate it in a standard way for( i = 1; ret && i < sockt->fd_max; ++i ) { @@ -869,12 +980,12 @@ int do_sockets(int next) --ret; } } -#endif +#endif // defined(SOCKET_EPOLL) // POSTSEND Send remaining data and handle eof sessions. #ifdef SEND_SHORTLIST send_shortlist_do_sends(); -#else +#else // SEND_SHORTLIST for (i = 1; i < sockt->fd_max; i++) { if(!sockt->session[i]) @@ -888,7 +999,7 @@ int do_sockets(int next) sockt->session[i]->func_parse(i); //This should close the session immediately. } } -#endif +#endif // SEND_SHORTLIST // parse input data on each socket for(i = 1; i < sockt->fd_max; i++) @@ -931,14 +1042,14 @@ int do_sockets(int next) 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 +#else // _WIN32 ShowMessage("\033[s\033[1;1H\033[2K%s\033[u", buf); -#endif +#endif // _WIN32 socket_data_last_tick = sockt->last_tick; socket_data_i = socket_data_ci = 0; socket_data_o = socket_data_co = 0; } -#endif +#endif // SHOW_SERVER_STATS return 0; } @@ -1160,7 +1271,7 @@ int access_ipmask(const char* str, AccessControl* acc) return 1; } ////////////////////////////// -#endif +#endif // MINICORE ////////////////////////////// int socket_config_read(const char* cfgName) @@ -1184,7 +1295,15 @@ int socket_config_read(const char* cfgName) sockt->stall_time = atoi(w2); if( sockt->stall_time < 3 ) sockt->stall_time = 3;/* a minimum is required to refrain it from killing itself */ + } +#ifdef SOCKET_EPOLL + else if(!strcmpi(w1, "epoll_maxevents")) { + epoll_maxevents = atoi(w2); + if(epoll_maxevents < 16){ + epoll_maxevents = 16; // minimum that seems to be useful + } } +#endif // SOCKET_EPOLL #ifndef MINICORE else if (!strcmpi(w1, "enable_ip_rules")) { ip_rules = config_switch(w2); @@ -1218,7 +1337,7 @@ int socket_config_read(const char* cfgName) access_debug = config_switch(w2); else if (!strcmpi(w1,"socket_max_client_packet")) socket_max_client_packet = strtoul(w2, NULL, 0); -#endif +#endif // MINICORE else if (!strcmpi(w1, "import")) socket_config_read(w2); else @@ -1239,7 +1358,7 @@ void socket_final(void) aFree(access_allow); if( access_deny ) aFree(access_deny); -#endif +#endif // MINICORE for( i = 1; i < sockt->fd_max; i++ ) if(sockt->session[i]) @@ -1255,6 +1374,18 @@ void socket_final(void) VECTOR_CLEAR(sockt->lan_subnets); VECTOR_CLEAR(sockt->allowed_ips); VECTOR_CLEAR(sockt->trusted_ips); + +#ifdef SOCKET_EPOLL + if(epfd != SOCKET_ERROR){ + close(epfd); + epfd = SOCKET_ERROR; + } + if(epevents != NULL){ + aFree(epevents); + epevents = NULL; + } +#endif // SOCKET_EPOLL + } /// Closes a socket. @@ -1264,7 +1395,17 @@ void socket_close(int fd) return;// invalid sockt->flush(fd); // Try to send what's left (although it might not succeed since it's a nonblocking socket) + +#ifndef SOCKET_EPOLL + // Select based Event Dispatcher sFD_CLR(fd, &readfds);// this needs to be done before closing the socket +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &epevent); // removing the socket from epoll when it's being closed is not required but recommended +#endif // SOCKET_EPOLL + sShutdown(fd, SHUT_RDWR); // Disallow further reads/writes sClose(fd); // We don't really care if these closing functions return an error, we are just shutting down and not reusing this socket. if (sockt->session[fd]) delete_session(fd); @@ -1401,20 +1542,39 @@ void socket_init(void) } } } -#endif +#endif // defined(HAVE_SETRLIMIT) && !defined(CYGWIN) // Get initial local ips sockt->naddr_ = sockt->getips(sockt->addr_,16); + socket_config_read(SOCKET_CONF_FILENAME); + +#ifndef SOCKET_EPOLL + // Select based Event Dispatcher: sFD_ZERO(&readfds); + ShowInfo("Server uses '" CL_WHITE "select" CL_RESET "' as event dispatcher\n"); + +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher: + epfd = epoll_create(FD_SETSIZE); // 2.6.8 or newer ignores the expected socket amount argument + if(epfd == SOCKET_ERROR){ + ShowError("Failed to Create Epoll Event Dispatcher: %s\n", error_msg()); + exit(EXIT_FAILURE); + } + + memset(&epevent, 0x00, sizeof(struct epoll_event)); + epevents = aCalloc(epoll_maxevents, sizeof(struct epoll_event)); + + ShowInfo("Server uses '" CL_WHITE "epoll" CL_RESET "' with up to " CL_WHITE "%d" CL_RESET " events per cycle as event dispatcher\n", epoll_maxevents); + +#endif // SOCKET_EPOLL + #if defined(SEND_SHORTLIST) memset(send_shortlist_set, 0, sizeof(send_shortlist_set)); -#endif +#endif // defined(SEND_SHORTLIST) CREATE(sockt->session, struct socket_data *, FD_SETSIZE); - socket_config_read(SOCKET_CONF_FILENAME); - // initialize last send-receive tick sockt->last_tick = time(NULL); @@ -1427,7 +1587,7 @@ void socket_init(void) connect_history = uidb_alloc(DB_OPT_RELEASE_DATA); 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 +#endif // MINICORE ShowInfo("Server supports up to '"CL_WHITE"%"PRIu64""CL_RESET"' concurrent connections.\n", rlim_cur); } @@ -1617,7 +1777,7 @@ void send_shortlist_do_sends(void) } } } -#endif +#endif // SEND_SHORTLIST /** * Checks whether the given IP comes from LAN or WAN. @@ -1752,10 +1912,12 @@ void socket_net_config_read(const char *filename) ShowError("No allowed server IP ranges configured. This server won't be able to accept connections from any char servers.\n"); } ARR_FIND(0, VECTOR_LENGTH(sockt->allowed_ips), i, SUBNET_MATCH(0, VECTOR_INDEX(sockt->allowed_ips, i).ip, VECTOR_INDEX(sockt->allowed_ips, i).mask)); +#ifndef BUILDBOT if (i != VECTOR_LENGTH(sockt->allowed_ips)) { ShowWarning("Using a wildcard IP range in the allowed server IPs is NOT RECOMMENDED.\n"); ShowNotice("Please edit your '%s' allowed list to fit your network configuration.\n", filename); } +#endif // BUILDBOT libconfig->destroy(&network_config); return; } diff --git a/src/common/spinlock.h b/src/common/spinlock.h index 4d9c4c668..c04416285 100644 --- a/src/common/spinlock.h +++ b/src/common/spinlock.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) rAthena Project (www.rathena.org) * * Hercules is free software: you can redistribute it and/or modify @@ -37,74 +37,75 @@ #endif #ifdef WIN32 - -typedef struct __declspec( align(64) ) SPIN_LOCK{ +struct __declspec(align(64)) spin_lock { volatile LONG lock; volatile LONG nest; volatile LONG sync_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; +struct spin_lock { + volatile int32 lock; + volatile int32 nest; // nesting level. + volatile int32 sync_lock; +} __attribute__((aligned(64))); #endif - #ifdef HERCULES_CORE -static forceinline void InitializeSpinLock(SPIN_LOCK *lck){ - lck->lock = 0; - lck->nest = 0; - lck->sync_lock = 0; +static forceinline void InitializeSpinLock(struct spin_lock *lck) +{ + lck->lock = 0; + lck->nest = 0; + lck->sync_lock = 0; } -static forceinline void FinalizeSpinLock(SPIN_LOCK *lck){ +static forceinline void FinalizeSpinLock(struct spin_lock *lck) +{ return; } -#define getsynclock(l) do { if(InterlockedCompareExchange((l), 1, 0) == 0) break; rathread_yield(); } while(/*always*/1) +#define getsynclock(l) do { if(InterlockedCompareExchange((l), 1, 0) == 0) break; thread->yield(); } while(/*always*/1) #define dropsynclock(l) do { InterlockedExchange((l), 0); } while(0) -static forceinline void EnterSpinLock(SPIN_LOCK *lck){ - int tid = rathread_get_tid(); +static forceinline void EnterSpinLock(struct spin_lock *lck) +{ + int tid = thread->get_tid(); - // 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){ - InterlockedIncrement(&lck->nest); - dropsynclock(&lck->sync_lock); - return; // Got Lock - } - // drop sync 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) { + InterlockedIncrement(&lck->nest); dropsynclock(&lck->sync_lock); - - // Spin until we've got it ! - while(1){ - if(InterlockedCompareExchange(&lck->lock, tid, 0) == 0){ - InterlockedIncrement(&lck->nest); - return; // Got Lock - } - rathread_yield(); // Force ctxswitch to another thread. + return; // Got Lock + } + // drop sync lock + dropsynclock(&lck->sync_lock); + + // Spin until we've got it ! + while (true) { + if (InterlockedCompareExchange(&lck->lock, tid, 0) == 0) { + InterlockedIncrement(&lck->nest); + return; // Got Lock } + thread->yield(); // Force ctxswitch to another thread. + } } -static forceinline void LeaveSpinLock(SPIN_LOCK *lck){ - int tid = rathread_get_tid(); +static forceinline void LeaveSpinLock(struct spin_lock *lck) +{ + int tid = thread->get_tid(); - getsynclock(&lck->sync_lock); + getsynclock(&lck->sync_lock); - if(InterlockedCompareExchange(&lck->lock, tid, tid) == tid){ // this thread owns the lock. - if(InterlockedDecrement(&lck->nest) == 0) - InterlockedExchange(&lck->lock, 0); // Unlock! - } + if (InterlockedCompareExchange(&lck->lock, tid, tid) == tid) { // this thread owns the lock. + if (InterlockedDecrement(&lck->nest) == 0) + InterlockedExchange(&lck->lock, 0); // Unlock! + } - dropsynclock(&lck->sync_lock); + dropsynclock(&lck->sync_lock); } #endif // HERCULES_CORE diff --git a/src/common/strlib.c b/src/common/strlib.c index 9690151b4..b67adb63c 100644 --- a/src/common/strlib.c +++ b/src/common/strlib.c @@ -772,6 +772,7 @@ size_t sv_escape_c(char* out_dest, const char* src, size_t len, const char* esca case '\v': out_dest[j++] = 'v'; break; case '\f': out_dest[j++] = 'f'; break; case '\?': out_dest[j++] = '?'; break; + case '\"': out_dest[j++] = '"'; break; default:// to octal out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0700)>>6)); out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0070)>>3)); diff --git a/src/common/thread.c b/src/common/thread.c index b724344e6..aaafab943 100644 --- a/src/common/thread.c +++ b/src/common/thread.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) rAthena Project (www.rathena.org) * * Hercules is free software: you can redistribute it and/or modify @@ -20,10 +20,6 @@ */ #define HERCULES_CORE -// Basic Threading abstraction (for pthread / win32 based systems) -// -// Author: Florian Wilkemeyer <fw@f-ws.de> - #include "thread.h" #include "common/cbasetypes.h" @@ -48,13 +44,22 @@ #define HAS_TLS #endif -#define RA_THREADS_MAX 64 +/** @file + * Thread interface implementation. + * @author Florian Wilkemeyer <fw@f-ws.de> + */ + +struct thread_interface thread_s; +struct thread_interface *thread; -struct rAthread { +/// The maximum amount of threads. +#define THREADS_MAX 64 + +struct thread_handle { unsigned int myID; - RATHREAD_PRIO prio; - rAthreadProc proc; + enum thread_priority prio; + threadFunc proc; void *param; #ifdef WIN32 @@ -68,16 +73,17 @@ struct rAthread { __thread int g_rathread_ID = -1; #endif -/// -/// Subystem Code -/// -static struct rAthread l_threads[RA_THREADS_MAX]; +// Subystem Code + +static struct thread_handle l_threads[THREADS_MAX]; -void rathread_init(void) { - register unsigned int i; - memset(&l_threads, 0x00, RA_THREADS_MAX * sizeof(struct rAthread) ); +/// @copydoc thread_interface::init() +void thread_init(void) +{ + register int i; + memset(&l_threads, 0x00, THREADS_MAX * sizeof(struct thread_handle)); - for(i = 0; i < RA_THREADS_MAX; i++){ + for (i = 0; i < THREADS_MAX; i++) { l_threads[i].myID = i; } @@ -85,46 +91,53 @@ void rathread_init(void) { #ifdef HAS_TLS g_rathread_ID = 0; #endif - l_threads[0].prio = RAT_PRIO_NORMAL; - l_threads[0].proc = (rAthreadProc)0xDEADCAFE; + l_threads[0].prio = THREADPRIO_NORMAL; + l_threads[0].proc = (threadFunc)0xDEADCAFE; -}//end: rathread_init() +} -void rathread_final(void) { - register unsigned int i; +/// @copydoc thread_interface::final() +void thread_final(void) +{ + register int i; // Unterminated Threads Left? - // Shouldn't happen .. - // Kill 'em all! - // - for(i = 1; i < RA_THREADS_MAX; i++){ - if(l_threads[i].proc != NULL){ - ShowWarning("rAthread_final: unterminated Thread (tid %u entryPoint %p) - forcing to terminate (kill)\n", i, l_threads[i].proc); - rathread_destroy(&l_threads[i]); + // Shouldn't happen ... Kill 'em all! + for (i = 1; i < THREADS_MAX; i++) { + if (l_threads[i].proc != NULL){ + ShowWarning("thread_final: unterminated Thread (tid %d entry_point %p) - forcing to terminate (kill)\n", i, l_threads[i].proc); + thread->destroy(&l_threads[i]); } } +} -}//end: rathread_final() - -// gets called whenever a thread terminated .. -static void rat_thread_terminated(rAthread *handle) { +/** + * Gets called whenever a thread terminated. + * + * @param handle The terminated thread's handle. + */ +static void thread_terminated(struct thread_handle *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() + handle->prio = THREADPRIO_NORMAL; +} #ifdef WIN32 -DWORD WINAPI raThreadMainRedirector(LPVOID p){ +DWORD WINAPI thread_main_redirector(LPVOID p) +{ #else -static void *raThreadMainRedirector( void *p ){ +static void *thread_main_redirector(void *p) +{ sigset_t set; // on Posix Thread platforms #endif void *ret; + struct thread_handle *self = p; // Update myID @ TLS to right id. #ifdef HAS_TLS - g_rathread_ID = ((rAthread*)p)->myID; + g_rathread_ID = self->myID; #endif #ifndef WIN32 @@ -138,66 +151,68 @@ static void *raThreadMainRedirector( void *p ){ (void)sigaddset(&set, SIGPIPE); pthread_sigmask(SIG_BLOCK, &set, NULL); - #endif - ret = ((rAthread*)p)->proc( ((rAthread*)p)->param ) ; + ret = self->proc(self->param); #ifdef WIN32 - CloseHandle( ((rAthread*)p)->hThread ); + CloseHandle(self->hThread); #endif - rat_thread_terminated( (rAthread*)p ); + thread_terminated(self); #ifdef WIN32 return (DWORD)ret; #else return ret; #endif -}//end: raThreadMainRedirector() +} + +// API Level -/// -/// API Level -/// -rAthread *rathread_create(rAthreadProc entryPoint, void *param) { - return rathread_createEx( entryPoint, param, (1<<23) /*8MB*/, RAT_PRIO_NORMAL ); -}//end: rathread_create() +/// @copydoc thread_interface::create() +struct thread_handle *thread_create(threadFunc entry_point, void *param) +{ + return thread->create_opt(entry_point, param, (1<<23) /*8MB*/, THREADPRIO_NORMAL); +} -rAthread *rathread_createEx(rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio) { +/// @copydoc thread_interface::create_opt() +struct thread_handle *thread_create_opt(threadFunc entry_point, void *param, size_t stack_size, enum thread_priority prio) +{ #ifndef WIN32 pthread_attr_t attr; #endif size_t tmp; - unsigned int i; - rAthread *handle = NULL; + int i; + struct thread_handle *handle = NULL; // given stacksize aligned to systems pagesize? - tmp = szStack % sysinfo->getpagesize(); - if(tmp != 0) - szStack += tmp; + tmp = stack_size % sysinfo->getpagesize(); + if (tmp != 0) + stack_size += tmp; // Get a free Thread Slot. - for(i = 0; i < RA_THREADS_MAX; i++){ + for (i = 0; i < THREADS_MAX; i++) { if(l_threads[i].proc == NULL){ handle = &l_threads[i]; break; } } - if(handle == NULL){ - ShowError("rAthread: cannot create new thread (entryPoint: %p) - no free thread slot found!", entryPoint); + if (handle == NULL) { + ShowError("thread_create_opt: cannot create new thread (entry_point: %p) - no free thread slot found!", entry_point); return NULL; } - handle->proc = entryPoint; + handle->proc = entry_point; handle->param = param; #ifdef WIN32 - handle->hThread = CreateThread(NULL, szStack, raThreadMainRedirector, (void*)handle, 0, NULL); + handle->hThread = CreateThread(NULL, stack_size, thread_main_redirector, handle, 0, NULL); #else pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, szStack); + pthread_attr_setstacksize(&attr, stack_size); - if(pthread_create(&handle->hThread, &attr, raThreadMainRedirector, (void*)handle) != 0){ + if (pthread_create(&handle->hThread, &attr, thread_main_redirector, handle) != 0) { handle->proc = NULL; handle->param = NULL; return NULL; @@ -205,99 +220,122 @@ rAthread *rathread_createEx(rAthreadProc entryPoint, void *param, size_t szStack pthread_attr_destroy(&attr); #endif - rathread_prio_set( handle, prio ); + thread->prio_set(handle, prio); return handle; -}//end: rathread_createEx +} -void rathread_destroy(rAthread *handle) { +/// @copydoc thread_interface::destroy() +void thread_destroy(struct thread_handle *handle) +{ #ifdef WIN32 - if( TerminateThread(handle->hThread, 0) != FALSE){ + if (TerminateThread(handle->hThread, 0) != FALSE) { CloseHandle(handle->hThread); - rat_thread_terminated(handle); + thread_terminated(handle); } #else - if( pthread_cancel( handle->hThread ) == 0){ + if (pthread_cancel(handle->hThread) == 0) { // We have to join it, otherwise pthread wont re-cycle its internal resources assoc. with this thread. - pthread_join( handle->hThread, NULL ); + pthread_join(handle->hThread, NULL); // Tell our manager to release resources ;) - rat_thread_terminated(handle); + thread_terminated(handle); } #endif -}//end: rathread_destroy() +} -rAthread *rathread_self(void) { +struct thread_handle *thread_self(void) +{ #ifdef HAS_TLS - rAthread *handle = &l_threads[g_rathread_ID]; + struct thread_handle *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 .. int i; - #ifdef WIN32 - HANDLE hSelf; - hSelf = GetCurrent = GetCurrentThread(); - #else - pthread_t hSelf; - hSelf = pthread_self(); - #endif +#ifdef WIN32 + HANDLE hSelf; + hSelf = GetCurrent = GetCurrentThread(); +#else + pthread_t hSelf; + hSelf = pthread_self(); +#endif - for(i = 0; i < RA_THREADS_MAX; i++){ - if(l_threads[i].hThread == hSelf && l_threads[i].proc != NULL) + for (i = 0; i < THREADS_MAX; i++) { + if (l_threads[i].hThread == hSelf && l_threads[i].proc != NULL) return &l_threads[i]; } #endif return NULL; -}//end: rathread_self() +} -int rathread_get_tid(void) { - -#ifdef HAS_TLS +/// @copydoc thread_interface::get_tid() +int thread_get_tid(void) +{ +#if defined(HAS_TLS) return g_rathread_ID; +#elif defined(WIN32) + return (int)GetCurrentThreadId(); #else - // TODO - #ifdef WIN32 - return (int)GetCurrentThreadId(); - #else - return (int)pthread_self(); - #endif + return (int)pthread_self(); #endif +} -}//end: rathread_get_tid() - -bool rathread_wait(rAthread *handle, void **out_exitCode) { +/// @copydoc thread_interface::wait() +bool thread_wait(struct thread_handle *handle, void **out_exit_code) +{ // Hint: // no thread data cleanup routine call here! // its managed by the callProxy itself.. - // #ifdef WIN32 WaitForSingleObject(handle->hThread, INFINITE); return true; #else - if(pthread_join(handle->hThread, out_exitCode) == 0) + if (pthread_join(handle->hThread, out_exit_code) == 0) return true; return false; #endif -}//end: rathread_wait() +} -void rathread_prio_set(rAthread *handle, RATHREAD_PRIO prio) { - handle->prio = RAT_PRIO_NORMAL; +/// @copydoc thread_interface::prio_set() +void thread_prio_set(struct thread_handle *handle, enum thread_priority prio) +{ + handle->prio = THREADPRIO_NORMAL; //@TODO -}//end: rathread_prio_set() +} -RATHREAD_PRIO rathread_prio_get(rAthread *handle) { +/// @copydoc thread_interface::prio_get() +enum thread_priority thread_prio_get(struct thread_handle *handle) +{ return handle->prio; -}//end: rathread_prio_get() +} -void rathread_yield(void) { +/// @copydoc thread_interface::yield() +void thread_yield(void) { #ifdef WIN32 SwitchToThread(); #else sched_yield(); #endif -}//end: rathread_yield() +} + +/// Interface base initialization. +void thread_defaults(void) +{ + thread = &thread_s; + thread->init = thread_init; + thread->final = thread_final; + thread->create = thread_create; + thread->create_opt = thread_create_opt; + thread->destroy = thread_destroy; + thread->self = thread_self; + thread->get_tid = thread_get_tid; + thread->wait = thread_wait; + thread->prio_set = thread_prio_set; + thread->prio_get = thread_prio_get; + thread->yield = thread_yield; +} diff --git a/src/common/thread.h b/src/common/thread.h index 261735306..c668afbb4 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) rAthena Project (www.rathena.org) * * Hercules is free software: you can redistribute it and/or modify @@ -21,114 +21,136 @@ #ifndef COMMON_THREAD_H #define COMMON_THREAD_H -#include "common/cbasetypes.h" +#include "common/hercules.h" -typedef struct rAthread rAthread; -typedef void* (*rAthreadProc)(void*); - -typedef enum RATHREAD_PRIO { - RAT_PRIO_LOW = 0, - RAT_PRIO_NORMAL, - RAT_PRIO_HIGH -} RATHREAD_PRIO; - - -#ifdef HERCULES_CORE -/** - * Creates a new Thread - * - * @param entyPoint - entryProc, - * @param param - general purpose parameter, would be given as parameter to the thread's entry point. - * - * @return not NULL if success +/** @file + * Basic Threading abstraction (for pthread / win32 based systems). */ -rAthread *rathread_create(rAthreadProc entryPoint, void *param); +/* Opaque Types */ +struct thread_handle; ///< Thread handle. +typedef void *(*threadFunc)(void *); ///< Thread entry point function. + +/* Enums */ + +/// Thread priority +enum thread_priority { + THREADPRIO_LOW = 0, + THREADPRIO_NORMAL, + THREADPRIO_HIGH +}; + +/// The thread interface +struct thread_interface { + /// Interface initialization. + void (*init) (void); + + /// Interface finalization. + void (*final) (void); + + /** + * Creates a new Thread. + * + * @param enty_point Thread's entry point. + * @param param General purpose parameter, would be given as + * parameter to the thread's entry point. + * + * @return The created thread object. + * @retval NULL in vase of failure. + */ + struct thread_handle *(*create) (threadFunc entry_point, void *param); + + /** + * Creates a new Thread (with more creation options). + * + * @param enty_point Thread's entry point. + * @param param General purpose parameter, would be given as + * parameter to the thread's entry point. + * @param stack_size Stack Size in bytes. + * @param prio Priority of the Thread in the OS scheduler. + * + * @return The created thread object. + * @retval NULL in case of failure. + */ + struct thread_handle *(*create_opt) (threadFunc entry_point, void *param, size_t stack_size, enum thread_priority prio); + + /** + * Destroys the given Thread immediately. + * + * @remark + * The Handle gets invalid after call! don't use it afterwards. + * + * @param handle The thread to destroy. + */ + void (*destroy) (struct thread_handle *handle); + + /** + * Returns the thread handle of the thread calling this function. + * + * @remark + * This won't work in the program's main thread. + * + * @warning + * The underlying implementation might not perform very well, cache + * the value received! + * + * @return the thread handle. + * @retval NULL in case of failure. + */ + struct thread_handle *(*self) (void); + + /** + * Returns own thread id (TID). + * + * @remark + * This is an unique identifier for the calling thread, and depends + * on platform/ compiler, and may not be the systems Thread ID! + * + * @return the thread ID. + * @retval -1 in case of failure. + */ + int (*get_tid) (void); + + /** + * Waits for the given thread to terminate. + * + * @param[in] handle The thread to wait (join) for. + * @param[out] out_exit_code Pointer to return the exit code of the + * given thread after termination (optional). + * + * @retval true if the given thread has been terminated. + */ + bool (*wait) (struct thread_handle *handle, void **out_exit_code); + + /** + * Sets the given priority in the OS scheduler. + * + * @param handle The thread to set the priority for. + * @param prio The priority to set (@see enum thread_priority). + */ + void (*prio_set) (struct thread_handle *handle, enum thread_priority prio); + + /** + * Gets the current priority of the given thread. + * + * @param handle The thread to get the priority for. + */ + enum thread_priority (*prio_get) (struct thread_handle *handle); + + /** + * Tells the OS scheduler to yield the execution of the calling thread. + * + * @remark + * This will not "pause" the thread, it just allows the OS to spend + * the remaining time of the slice to another thread. + */ + void (*yield) (void); +}; -/** - * 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 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); - - -/** - * Destroys the given Thread immediately - * - * @note The Handle gets invalid after call! don't use it afterwards. - * - * @param handle - thread to destroy. - */ -void rathread_destroy(rAthread *handle); - - -/** - * Returns the thread handle of the thread calling this function - * - * @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(void); - - -/** - * Returns own thread id (TID) - * - * @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(void); - - -/** - * 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); - - -/** - * 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); - - -/** - * Gets the current Prio of the given thread - * - * @param handle - the thread to get the prio for. - */ -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 spend the remaining time - * of the slice to another thread. - */ -void rathread_yield(void); - -void rathread_init(void); -void rathread_final(void); +#ifdef HERCULES_CORE +void thread_defaults(void); #endif // HERCULES_CORE +HPShared struct thread_interface *thread; ///< Pointer to the thread interface. + #endif /* COMMON_THREAD_H */ |