summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2006-12-05 13:23:07 +0000
committerFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2006-12-05 13:23:07 +0000
commit288490094a7fe9167747dc78d416940759a31197 (patch)
tree53dc4f5c2375f4b688b53ca8841630ddec5e1f88 /src/common
parent8ec1c47aed09c90343949d57c92760ba84738a46 (diff)
downloadhercules-288490094a7fe9167747dc78d416940759a31197.tar.gz
hercules-288490094a7fe9167747dc78d416940759a31197.tar.bz2
hercules-288490094a7fe9167747dc78d416940759a31197.tar.xz
hercules-288490094a7fe9167747dc78d416940759a31197.zip
- Massive EOL normalization & 'svn:eol-style native' flag setting for all txt/conf/h/c files.
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@9410 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src/common')
-rw-r--r--src/common/core.c606
-rw-r--r--src/common/core.h44
-rw-r--r--src/common/db.c4896
-rw-r--r--src/common/db.h1496
-rw-r--r--src/common/ers.h386
-rw-r--r--src/common/graph.c636
-rw-r--r--src/common/graph.h54
-rw-r--r--src/common/grfio.c2062
-rw-r--r--src/common/grfio.h44
-rw-r--r--src/common/lock.c142
-rw-r--r--src/common/lock.h22
-rw-r--r--src/common/malloc.c1466
-rw-r--r--src/common/malloc.h356
-rw-r--r--src/common/nullpo.c188
-rw-r--r--src/common/nullpo.h474
-rw-r--r--src/common/plugin.h80
-rw-r--r--src/common/plugins.c734
-rw-r--r--src/common/plugins.h122
-rw-r--r--src/common/showmsg.c1652
-rw-r--r--src/common/showmsg.h192
-rw-r--r--src/common/strlib.h48
-rw-r--r--src/common/timer.c872
-rw-r--r--src/common/timer.h120
-rw-r--r--src/common/utils.c768
-rw-r--r--src/common/utils.h104
-rw-r--r--src/common/version.h60
26 files changed, 8812 insertions, 8812 deletions
diff --git a/src/common/core.c b/src/common/core.c
index 06ee6b2b8..7b977632d 100644
--- a/src/common/core.c
+++ b/src/common/core.c
@@ -1,303 +1,303 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-#include <signal.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "core.h"
-#include "../common/db.h"
-#include "../common/mmo.h"
-#include "../common/malloc.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/graph.h"
-#include "../common/plugins.h"
-#include "../common/version.h"
-#include "../common/showmsg.h"
-
-#ifndef _WIN32
- #include "svnversion.h"
-#endif
-
-int runflag = 1;
-int arg_c = 0;
-char **arg_v = NULL;
-
-char *SERVER_NAME = NULL;
-char SERVER_TYPE = ATHENA_SERVER_NONE;
-static void (*term_func)(void) = NULL;
-#ifndef SVNVERSION
- static char eA_svn_version[10];
-#endif
-/*======================================
- * CORE : Set function
- *--------------------------------------
- */
-void set_termfunc(void (*termfunc)(void))
-{
- term_func = termfunc;
-}
-
-#ifndef MINICORE // minimalist Core
-// Added by Gabuzomeu
-//
-// This is an implementation of signal() using sigaction() for portability.
-// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced
-// Programming in the UNIX Environment_.
-//
-#ifdef WIN32 // windows don't have SIGPIPE
-#define SIGPIPE SIGINT
-#endif
-
-#ifndef POSIX
-#define compat_signal(signo, func) signal(signo, func)
-#else
-sigfunc *compat_signal(int signo, sigfunc *func)
-{
- struct sigaction sact, oact;
-
- sact.sa_handler = func;
- sigemptyset(&sact.sa_mask);
- sact.sa_flags = 0;
-#ifdef SA_INTERRUPT
- sact.sa_flags |= SA_INTERRUPT; /* SunOS */
-#endif
-
- if (sigaction(signo, &sact, &oact) < 0)
- return (SIG_ERR);
-
- return (oact.sa_handler);
-}
-#endif
-
-/*======================================
- * CORE : Signal Sub Function
- *--------------------------------------
- */
-static void sig_proc(int sn)
-{
- static int is_called = 0;
-
- switch (sn) {
- case SIGINT:
- case SIGTERM:
- if (++is_called > 3)
- exit(0);
- runflag = 0;
- break;
-#ifndef _WIN32
- case SIGXFSZ:
- // ignore and allow it to set errno to EFBIG
- ShowWarning ("Max file size reached!\n");
- //run_flag = 0; // should we quit?
- break;
- case SIGPIPE:
- ShowMessage ("Broken pipe found... closing socket\n"); // set to eof in socket.c
- break; // does nothing here
-#endif
- }
-}
-
-void signals_init (void)
-{
- compat_signal(SIGTERM, sig_proc);
- compat_signal(SIGINT, sig_proc);
-
- // Signal to create coredumps by system when necessary (crash)
- compat_signal(SIGSEGV, SIG_DFL);
- compat_signal(SIGFPE, SIG_DFL);
- compat_signal(SIGILL, SIG_DFL);
- #ifndef _WIN32
- compat_signal(SIGXFSZ, sig_proc);
- compat_signal(SIGPIPE, sig_proc);
- compat_signal(SIGBUS, SIG_DFL);
- compat_signal(SIGTRAP, SIG_DFL);
- #endif
-}
-#endif
-
-#ifdef SVNVERSION
- #define xstringify(x) stringify(x)
- #define stringify(x) #x
- const char *get_svn_revision(void)
- {
- return xstringify(SVNVERSION);
- }
-#else
-const char* get_svn_revision(void)
-{
- FILE *fp;
-
- if(*eA_svn_version)
- return eA_svn_version;
-
- if ((fp = fopen(".svn/entries", "r")))
- {
- char line[1024];
- int rev;
- // Check the version
- if (fgets(line,sizeof(line),fp))
- {
- if(!isdigit(line[0]))
- {
- // XML File format
- while (fgets(line,sizeof(line),fp))
- if (strstr(line,"revision=")) break;
- fclose(fp);
- if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) {
- snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", rev);
- }
- }
- else
- {
- // Bin File format
- fgets(line,sizeof(line),fp); // Get the name
- fgets(line,sizeof(line),fp); // Get the entries kind
- if(fgets(line,sizeof(line),fp)) // Get the rev numver
- {
- snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", atoi(line));
- }
- }
- }
- }
-
- if(!(*eA_svn_version))
- snprintf(eA_svn_version, sizeof(eA_svn_version), "Unknown");
-
- return eA_svn_version;
-}
-#endif
-
-/*======================================
- * CORE : Display title
- *--------------------------------------
- */
-
-static void display_title(void)
-{
- //The clearscreeen is usually more of an annoyance than anything else... [Skotlex]
-// ClearScreen(); // clear screen and go up/left (0, 0 position in text)
- //ShowMessage("\n"); //A blank message??
- printf("\n");
- ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n"); // white writing (37) on blue background (44), \033[K clean until end of file
- ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" (c)2005 eAthena Development Team presents "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
- ShowMessage(""CL_XXBL" ("CL_BOLD" ______ __ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ _ \\/\\ \\__/\\ \\ v%2d.%02d.%02d "CL_XXBL")"CL_CLL""CL_NORMAL"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" _ _ _ _ _ _ _ _ _ _ _ _ _ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
- ShowMessage(""CL_XXBL" ("CL_BOLD" "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
- ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" Advanced Fusion Maps (c) 2003-2005 The Fusion Project "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
- ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n\n"); // reset color
-
- ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'.\n", get_svn_revision());
-}
-
-// Warning if logged in as superuser (root)
-void usercheck(void){
-#ifndef _WIN32
- if ((getuid() == 0) && (getgid() == 0)) {
- ShowWarning ("You are running eAthena as the root superuser.\n");
- ShowWarning ("It is unnecessary and unsafe to run eAthena with root privileges.\n");
- sleep(3);
- }
-#endif
-}
-
-/*======================================
- * CORE : MAINROUTINE
- *--------------------------------------
- */
-#ifndef MINICORE // minimalist Core
-int main (int argc, char **argv)
-{
- int next;
-
- // initialise program arguments
- {
- char *p1 = SERVER_NAME = argv[0];
- char *p2 = p1;
- while ((p1 = strchr(p2, '/')) != NULL || (p1 = strchr(p2, '\\')) != NULL)
- {
- SERVER_NAME = ++p1;
- p2 = p1;
- }
- arg_c = argc;
- arg_v = argv;
- #ifndef SVNVERSION
- *eA_svn_version = '\0';
- #endif
- }
-
- set_server_type();
- display_title();
- usercheck();
-
- malloc_init(); /* 一番最初に実行する必要がある */
- db_init();
- signals_init();
-
- timer_init();
- socket_init();
- plugins_init();
-
- do_init(argc,argv);
- graph_init();
- plugin_event_trigger("Athena_Init");
-
- while (runflag) {
- next = do_timer(gettick_nocache());
- do_sendrecv(next);
-#ifndef TURBO
- do_parsepacket();
-#endif
- }
-
- plugin_event_trigger("Athena_Final");
- graph_final();
- do_final();
-
- timer_final();
- plugins_final();
- socket_final();
- db_final();
- malloc_final();
-
- return 0;
-}
-#else
-int main (int argc, char **argv)
-{
- // initialise program arguments
- {
- char *p = SERVER_NAME = argv[0];
- while ((p = strchr(p, '/')) != NULL)
- SERVER_NAME = ++p;
- arg_c = argc;
- arg_v = argv;
- }
-
- display_title();
- usercheck();
- do_init(argc,argv);
- do_final();
-
- return 0;
-}
-#endif
-
-#ifdef BCHECK
-unsigned int __invalid_size_argument_for_IOC;
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "core.h"
+#include "../common/db.h"
+#include "../common/mmo.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/graph.h"
+#include "../common/plugins.h"
+#include "../common/version.h"
+#include "../common/showmsg.h"
+
+#ifndef _WIN32
+ #include "svnversion.h"
+#endif
+
+int runflag = 1;
+int arg_c = 0;
+char **arg_v = NULL;
+
+char *SERVER_NAME = NULL;
+char SERVER_TYPE = ATHENA_SERVER_NONE;
+static void (*term_func)(void) = NULL;
+#ifndef SVNVERSION
+ static char eA_svn_version[10];
+#endif
+/*======================================
+ * CORE : Set function
+ *--------------------------------------
+ */
+void set_termfunc(void (*termfunc)(void))
+{
+ term_func = termfunc;
+}
+
+#ifndef MINICORE // minimalist Core
+// Added by Gabuzomeu
+//
+// This is an implementation of signal() using sigaction() for portability.
+// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced
+// Programming in the UNIX Environment_.
+//
+#ifdef WIN32 // windows don't have SIGPIPE
+#define SIGPIPE SIGINT
+#endif
+
+#ifndef POSIX
+#define compat_signal(signo, func) signal(signo, func)
+#else
+sigfunc *compat_signal(int signo, sigfunc *func)
+{
+ struct sigaction sact, oact;
+
+ sact.sa_handler = func;
+ sigemptyset(&sact.sa_mask);
+ sact.sa_flags = 0;
+#ifdef SA_INTERRUPT
+ sact.sa_flags |= SA_INTERRUPT; /* SunOS */
+#endif
+
+ if (sigaction(signo, &sact, &oact) < 0)
+ return (SIG_ERR);
+
+ return (oact.sa_handler);
+}
+#endif
+
+/*======================================
+ * CORE : Signal Sub Function
+ *--------------------------------------
+ */
+static void sig_proc(int sn)
+{
+ static int is_called = 0;
+
+ switch (sn) {
+ case SIGINT:
+ case SIGTERM:
+ if (++is_called > 3)
+ exit(0);
+ runflag = 0;
+ break;
+#ifndef _WIN32
+ case SIGXFSZ:
+ // ignore and allow it to set errno to EFBIG
+ ShowWarning ("Max file size reached!\n");
+ //run_flag = 0; // should we quit?
+ break;
+ case SIGPIPE:
+ ShowMessage ("Broken pipe found... closing socket\n"); // set to eof in socket.c
+ break; // does nothing here
+#endif
+ }
+}
+
+void signals_init (void)
+{
+ compat_signal(SIGTERM, sig_proc);
+ compat_signal(SIGINT, sig_proc);
+
+ // Signal to create coredumps by system when necessary (crash)
+ compat_signal(SIGSEGV, SIG_DFL);
+ compat_signal(SIGFPE, SIG_DFL);
+ compat_signal(SIGILL, SIG_DFL);
+ #ifndef _WIN32
+ compat_signal(SIGXFSZ, sig_proc);
+ compat_signal(SIGPIPE, sig_proc);
+ compat_signal(SIGBUS, SIG_DFL);
+ compat_signal(SIGTRAP, SIG_DFL);
+ #endif
+}
+#endif
+
+#ifdef SVNVERSION
+ #define xstringify(x) stringify(x)
+ #define stringify(x) #x
+ const char *get_svn_revision(void)
+ {
+ return xstringify(SVNVERSION);
+ }
+#else
+const char* get_svn_revision(void)
+{
+ FILE *fp;
+
+ if(*eA_svn_version)
+ return eA_svn_version;
+
+ if ((fp = fopen(".svn/entries", "r")))
+ {
+ char line[1024];
+ int rev;
+ // Check the version
+ if (fgets(line,sizeof(line),fp))
+ {
+ if(!isdigit(line[0]))
+ {
+ // XML File format
+ while (fgets(line,sizeof(line),fp))
+ if (strstr(line,"revision=")) break;
+ fclose(fp);
+ if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) {
+ snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", rev);
+ }
+ }
+ else
+ {
+ // Bin File format
+ fgets(line,sizeof(line),fp); // Get the name
+ fgets(line,sizeof(line),fp); // Get the entries kind
+ if(fgets(line,sizeof(line),fp)) // Get the rev numver
+ {
+ snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", atoi(line));
+ }
+ }
+ }
+ }
+
+ if(!(*eA_svn_version))
+ snprintf(eA_svn_version, sizeof(eA_svn_version), "Unknown");
+
+ return eA_svn_version;
+}
+#endif
+
+/*======================================
+ * CORE : Display title
+ *--------------------------------------
+ */
+
+static void display_title(void)
+{
+ //The clearscreeen is usually more of an annoyance than anything else... [Skotlex]
+// ClearScreen(); // clear screen and go up/left (0, 0 position in text)
+ //ShowMessage("\n"); //A blank message??
+ printf("\n");
+ ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n"); // white writing (37) on blue background (44), \033[K clean until end of file
+ ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" (c)2005 eAthena Development Team presents "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
+ ShowMessage(""CL_XXBL" ("CL_BOLD" ______ __ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ _ \\/\\ \\__/\\ \\ v%2d.%02d.%02d "CL_XXBL")"CL_CLL""CL_NORMAL"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" _ _ _ _ _ _ _ _ _ _ _ _ _ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
+ ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" Advanced Fusion Maps (c) 2003-2005 The Fusion Project "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
+ ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n\n"); // reset color
+
+ ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'.\n", get_svn_revision());
+}
+
+// Warning if logged in as superuser (root)
+void usercheck(void){
+#ifndef _WIN32
+ if ((getuid() == 0) && (getgid() == 0)) {
+ ShowWarning ("You are running eAthena as the root superuser.\n");
+ ShowWarning ("It is unnecessary and unsafe to run eAthena with root privileges.\n");
+ sleep(3);
+ }
+#endif
+}
+
+/*======================================
+ * CORE : MAINROUTINE
+ *--------------------------------------
+ */
+#ifndef MINICORE // minimalist Core
+int main (int argc, char **argv)
+{
+ int next;
+
+ // initialise program arguments
+ {
+ char *p1 = SERVER_NAME = argv[0];
+ char *p2 = p1;
+ while ((p1 = strchr(p2, '/')) != NULL || (p1 = strchr(p2, '\\')) != NULL)
+ {
+ SERVER_NAME = ++p1;
+ p2 = p1;
+ }
+ arg_c = argc;
+ arg_v = argv;
+ #ifndef SVNVERSION
+ *eA_svn_version = '\0';
+ #endif
+ }
+
+ set_server_type();
+ display_title();
+ usercheck();
+
+ malloc_init(); /* 一番最初に実行する必要がある */
+ db_init();
+ signals_init();
+
+ timer_init();
+ socket_init();
+ plugins_init();
+
+ do_init(argc,argv);
+ graph_init();
+ plugin_event_trigger("Athena_Init");
+
+ while (runflag) {
+ next = do_timer(gettick_nocache());
+ do_sendrecv(next);
+#ifndef TURBO
+ do_parsepacket();
+#endif
+ }
+
+ plugin_event_trigger("Athena_Final");
+ graph_final();
+ do_final();
+
+ timer_final();
+ plugins_final();
+ socket_final();
+ db_final();
+ malloc_final();
+
+ return 0;
+}
+#else
+int main (int argc, char **argv)
+{
+ // initialise program arguments
+ {
+ char *p = SERVER_NAME = argv[0];
+ while ((p = strchr(p, '/')) != NULL)
+ SERVER_NAME = ++p;
+ arg_c = argc;
+ arg_v = argv;
+ }
+
+ display_title();
+ usercheck();
+ do_init(argc,argv);
+ do_final();
+
+ return 0;
+}
+#endif
+
+#ifdef BCHECK
+unsigned int __invalid_size_argument_for_IOC;
+#endif
diff --git a/src/common/core.h b/src/common/core.h
index ce57c28a6..fffd44028 100644
--- a/src/common/core.h
+++ b/src/common/core.h
@@ -1,22 +1,22 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _CORE_H_
-#define _CORE_H_
-
-//#define SQL_DEBUG //uncomment for debug_mysql_query instead of mysql_real_query
-
-extern int arg_c;
-extern char **arg_v;
-
-extern int runflag;
-extern char *SERVER_NAME;
-extern char SERVER_TYPE;
-
-extern const char *get_svn_revision(void);
-extern int do_init(int,char**);
-extern void set_server_type(void);
-extern void set_termfunc(void (*termfunc)(void));
-extern void do_final(void);
-
-#endif // _CORE_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CORE_H_
+#define _CORE_H_
+
+//#define SQL_DEBUG //uncomment for debug_mysql_query instead of mysql_real_query
+
+extern int arg_c;
+extern char **arg_v;
+
+extern int runflag;
+extern char *SERVER_NAME;
+extern char SERVER_TYPE;
+
+extern const char *get_svn_revision(void);
+extern int do_init(int,char**);
+extern void set_server_type(void);
+extern void set_termfunc(void (*termfunc)(void));
+extern void do_final(void);
+
+#endif // _CORE_H_
diff --git a/src/common/db.c b/src/common/db.c
index ebb5b9a30..28b5721e9 100644
--- a/src/common/db.c
+++ b/src/common/db.c
@@ -1,2448 +1,2448 @@
-/*****************************************************************************\
- * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
- * For more information, see LICENCE in the main folder *
- * *
- * This file is separated in five sections: *
- * (1) Private typedefs, enums, structures, defines and gblobal variables *
- * (2) Private functions *
- * (3) Protected functions used internally *
- * (4) Protected functions used in the interface of the database *
- * (5) Public functions *
- * *
- * The databases are structured as a hashtable of RED-BLACK trees. *
- * *
- * <B>Properties of the RED-BLACK trees being used:</B> *
- * 1. The value of any node is greater than the value of its left child and *
- * less than the value of its right child. *
- * 2. Every node is colored either RED or BLACK. *
- * 3. Every red node that is not a leaf has only black children. *
- * 4. Every path from the root to a leaf contains the same number of black *
- * nodes. *
- * 5. The root node is black. *
- * An <code>n</code> node in a RED-BLACK tree has the property that its *
- * height is <code>O(lg(n))</code>. *
- * Another important property is that after adding a node to a RED-BLACK *
- * tree, the tree can be readjusted in <code>O(lg(n))</code> time. *
- * Similarly, after deleting a node from a RED-BLACK tree, the tree can be *
- * readjusted in <code>O(lg(n))</code> time. *
- * {@link http://www.cs.mcgill.ca/~cs251/OldCourses/1997/topic18/} *
- * *
- * <B>How to add new database types:</B> *
- * 1. Add the identifier of the new database type to the enum DBType *
- * 2. If not already there, add the data type of the key to the union DBKey *
- * 3. If the key can be considered NULL, update the function db_is_key_null *
- * 4. If the key can be duplicated, update the functions db_dup_key and *
- * db_dup_key_free *
- * 5. Create a comparator and update the function db_default_cmp *
- * 6. Create a hasher and update the function db_default_hash *
- * 7. If the new database type requires or does not support some options, *
- * update the function db_fix_options *
- * *
- * TODO: *
- * - create test cases to test the database system thoroughly *
- * - make data an enumeration *
- * - finish this header describing the database system *
- * - create custom database allocator *
- * - make the system thread friendly *
- * - change the structure of the database to T-Trees *
- * - create a db that organizes itself by splaying *
- * *
- * HISTORY: *
- * 2.1 (Athena build #???#) - Portability fix *
- * - Fixed the portability of casting to union and added the functions *
- * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and *
- * {@link DBInterface#clear(DBInterface,DBApply,...)}. *
- * 2.0 (Athena build 4859) - Transition version *
- * - Almost everything recoded with a strategy similar to objects, *
- * database structure is maintained. *
- * 1.0 (up to Athena build 4706) *
- * - Previous database system. *
- * *
- * @version 2.1 (Athena build #???#) - Portability fix *
- * @author (Athena build 4859) Flavio @ Amazon Project *
- * @author (up to Athena build 4706) Athena Dev Teams *
- * @encoding US-ASCII *
- * @see common#db.h *
-\*****************************************************************************/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "db.h"
-#include "../common/mmo.h"
-#include "../common/utils.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/ers.h"
-
-/*****************************************************************************\
- * (1) Private typedefs, enums, structures, defines and global variables of *
- * the database system. *
- * DB_ENABLE_STATS - Define to enable database statistics. *
- * HASH_SIZE - Define with the size of the hashtable. *
- * DBNColor - Enumeration of colors of the nodes. *
- * DBNode - Structure of a node in RED-BLACK trees. *
- * struct db_free - Structure that holds a deleted node to be freed. *
- * Database - Struture of the database. *
- * stats - Statistics about the database system. *
-\*****************************************************************************/
-
-/**
- * If defined statistics about database nodes, database creating/destruction
- * and function usage are keept and displayed when finalizing the database
- * system.
- * WARNING: This adds overhead to every database operation (not shure how much).
- * @private
- * @see #DBStats
- * @see #stats
- * @see #db_final(void)
- */
-//#define DB_ENABLE_STATS
-
-/**
- * Size of the hashtable in the database.
- * @private
- * @see Database#ht
- */
-#define HASH_SIZE (256+27)
-
-/**
- * A node in a RED-BLACK tree of the database.
- * @param parent Parent node
- * @param left Left child node
- * @param right Right child node
- * @param key Key of this database entry
- * @param data Data of this database entry
- * @param deleted If the node is deleted
- * @param color Color of the node
- * @private
- * @see Database#ht
- */
-typedef struct dbn {
- // Tree structure
- struct dbn *parent;
- struct dbn *left;
- struct dbn *right;
- // Node data
- DBKey key;
- void *data;
- // Other
- enum {RED, BLACK} color;
- unsigned deleted : 1;
-} *DBNode;
-
-/**
- * Structure that holds a deleted node.
- * @param node Deleted node
- * @param root Address to the root of the tree
- * @private
- * @see Database#free_list
- */
-struct db_free {
- DBNode node;
- DBNode *root;
-};
-
-/**
- * Complete database structure.
- * @param dbi Interface of the database
- * @param alloc_file File where the database was allocated
- * @param alloc_line Line in the file where the database was allocated
- * @param free_list Array of deleted nodes to be freed
- * @param free_count Number of deleted nodes in free_list
- * @param free_max Current maximum capacity of free_list
- * @param free_lock Lock for freeing the nodes
- * @param nodes Manager of reusable tree nodes
- * @param cmp Comparator of the database
- * @param hash Hasher of the database
- * @param release Releaser of the database
- * @param ht Hashtable of RED-BLACK trees
- * @param type Type of the database
- * @param options Options of the database
- * @param item_count Number of items in the database
- * @param maxlen Maximum length of strings in DB_STRING and DB_ISTRING databases
- * @param global_lock Global lock of the database
- * @private
- * @see common\db.h#DBInterface
- * @see #HASH_SIZE
- * @see #DBNode
- * @see #struct db_free
- * @see common\db.h#DBComparator(void *,void *)
- * @see common\db.h#DBHasher(void *)
- * @see common\db.h#DBReleaser(void *,void *,DBRelease)
- * @see common\db.h#DBOptions
- * @see common\db.h#DBType
- * @see #db_alloc(const char *,int,DBOptions,DBType,...)
- */
-typedef struct db {
- // Database interface
- struct dbt dbi;
- // File and line of allocation
- const char *alloc_file;
- int alloc_line;
- // Lock system
- struct db_free *free_list;
- unsigned int free_count;
- unsigned int free_max;
- unsigned int free_lock;
- // Other
- ERInterface nodes;
- DBComparator cmp;
- DBHasher hash;
- DBReleaser release;
- DBNode ht[HASH_SIZE];
- DBType type;
- DBOptions options;
- unsigned int item_count;
- unsigned short maxlen;
- unsigned global_lock : 1;
-} *Database;
-
-#ifdef DB_ENABLE_STATS
-/**
- * Structure with what is counted when the database estatistics are enabled.
- * @private
- * @see #DB_ENABLE_STATS
- * @see #stats
- */
-static struct {
- // Node alloc/free
- unsigned int db_node_alloc;
- unsigned int db_node_free;
- // Database creating/destruction counters
- unsigned int db_int_alloc;
- unsigned int db_uint_alloc;
- unsigned int db_string_alloc;
- unsigned int db_istring_alloc;
- unsigned int db_int_destroy;
- unsigned int db_uint_destroy;
- unsigned int db_string_destroy;
- unsigned int db_istring_destroy;
- // Function usage counters
- unsigned int db_rotate_left;
- unsigned int db_rotate_right;
- unsigned int db_rebalance;
- unsigned int db_rebalance_erase;
- unsigned int db_is_key_null;
- unsigned int db_dup_key;
- unsigned int db_dup_key_free;
- unsigned int db_free_add;
- unsigned int db_free_remove;
- unsigned int db_free_lock;
- unsigned int db_free_unlock;
- unsigned int db_int_cmp;
- unsigned int db_uint_cmp;
- unsigned int db_string_cmp;
- unsigned int db_istring_cmp;
- unsigned int db_int_hash;
- unsigned int db_uint_hash;
- unsigned int db_string_hash;
- unsigned int db_istring_hash;
- unsigned int db_release_nothing;
- unsigned int db_release_key;
- unsigned int db_release_data;
- unsigned int db_release_both;
- unsigned int db_get;
- unsigned int db_getall;
- unsigned int db_vgetall;
- unsigned int db_ensure;
- unsigned int db_vensure;
- unsigned int db_put;
- unsigned int db_remove;
- unsigned int db_foreach;
- unsigned int db_vforeach;
- unsigned int db_clear;
- unsigned int db_vclear;
- unsigned int db_destroy;
- unsigned int db_vdestroy;
- unsigned int db_size;
- unsigned int db_type;
- unsigned int db_options;
- unsigned int db_fix_options;
- unsigned int db_default_cmp;
- unsigned int db_default_hash;
- unsigned int db_default_release;
- unsigned int db_custom_release;
- unsigned int db_alloc;
- unsigned int db_i2key;
- unsigned int db_ui2key;
- unsigned int db_str2key;
- unsigned int db_init;
- unsigned int db_final;
-} stats = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0
-};
-#endif /* DB_ENABLE_STATS */
-
-/*****************************************************************************\
- * (2) Section of private functions used by the database system. *
- * db_rotate_left - Rotate a tree node to the left. *
- * db_rotate_right - Rotate a tree node to the right. *
- * db_rebalance - Rebalance the tree. *
- * db_rebalance_erase - Rebalance the tree after a BLACK node was erased. *
- * db_is_key_null - Returns not 0 if the key is considered NULL. *
- * db_dup_key - Duplicate a key for internal use. *
- * db_dup_key_free - Free the duplicated key. *
- * db_free_add - Add a node to the free_list of a database. *
- * db_free_remove - Remove a node from the free_list of a database. *
- * db_free_lock - Increment the free_lock of a database. *
- * db_free_unlock - Decrement the free_lock of a database. *
- * If it was the last lock, frees the nodes in free_list. *
- * NOTE: Keeps the database trees balanced. *
-\*****************************************************************************/
-
-/**
- * Rotate a node to the left.
- * @param node Node to be rotated
- * @param root Pointer to the root of the tree
- * @private
- * @see #db_rebalance(DBNode,DBNode *)
- * @see #db_rebalance_erase(DBNode,DBNode *)
- */
-static void db_rotate_left(DBNode node, DBNode *root)
-{
- DBNode y = node->right;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_rotate_left != (unsigned int)~0) stats.db_rotate_left++;
-#endif /* DB_ENABLE_STATS */
- // put the left of y at the right of node
- node->right = y->left;
- if (y->left)
- y->left->parent = node;
- y->parent = node->parent;
- // link y and node's parent
- if (node == *root) {
- *root = y; // node was root
- } else if (node == node->parent->left) {
- node->parent->left = y; // node was at the left
- } else {
- node->parent->right = y; // node was at the right
- }
- // put node at the left of y
- y->left = node;
- node->parent = y;
-}
-
-/**
- * Rotate a node to the right
- * @param node Node to be rotated
- * @param root Pointer to the root of the tree
- * @private
- * @see #db_rebalance(DBNode,DBNode *)
- * @see #db_rebalance_erase(DBNode,DBNode *)
- */
-static void db_rotate_right(DBNode node, DBNode *root)
-{
- DBNode y = node->left;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_rotate_right != (unsigned int)~0) stats.db_rotate_right++;
-#endif /* DB_ENABLE_STATS */
- // put the right of y at the left of node
- node->left = y->right;
- if (y->right != 0)
- y->right->parent = node;
- y->parent = node->parent;
- // link y and node's parent
- if (node == *root) {
- *root = y; // node was root
- } else if (node == node->parent->right) {
- node->parent->right = y; // node was at the right
- } else {
- node->parent->left = y; // node was at the left
- }
- // put node at the right of y
- y->right = node;
- node->parent = y;
-}
-
-/**
- * Rebalance the RED-BLACK tree.
- * Called when the node and it's parent are both RED.
- * @param node Node to be rebalanced
- * @param root Pointer to the root of the tree
- * @private
- * @see #db_rotate_left(DBNode,DBNode *)
- * @see #db_rotate_right(DBNode,DBNode *)
- * @see #db_put(DBInterface,DBKey,void *)
- */
-static void db_rebalance(DBNode node, DBNode *root)
-{
- DBNode y;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_rebalance != (unsigned int)~0) stats.db_rebalance++;
-#endif /* DB_ENABLE_STATS */
- // Restore the RED-BLACK properties
- node->color = RED;
- while (node != *root && node->parent->color == RED) {
- if (node->parent == node->parent->parent->left) {
- // If node's parent is a left, y is node's right 'uncle'
- y = node->parent->parent->right;
- if (y && y->color == RED) { // case 1
- // change the colors and move up the tree
- node->parent->color = BLACK;
- y->color = BLACK;
- node->parent->parent->color = RED;
- node = node->parent->parent;
- } else {
- if (node == node->parent->right) { // case 2
- // move up and rotate
- node = node->parent;
- db_rotate_left(node, root);
- }
- // case 3
- node->parent->color = BLACK;
- node->parent->parent->color = RED;
- db_rotate_right(node->parent->parent, root);
- }
- } else {
- // If node's parent is a right, y is node's left 'uncle'
- y = node->parent->parent->left;
- if (y && y->color == RED) { // case 1
- // change the colors and move up the tree
- node->parent->color = BLACK;
- y->color = BLACK;
- node->parent->parent->color = RED;
- node = node->parent->parent;
- } else {
- if (node == node->parent->left) { // case 2
- // move up and rotate
- node = node->parent;
- db_rotate_right(node, root);
- }
- // case 3
- node->parent->color = BLACK;
- node->parent->parent->color = RED;
- db_rotate_left(node->parent->parent, root);
- }
- }
- }
- (*root)->color = BLACK; // the root can and should always be black
-}
-
-/**
- * Erase a node from the RED-BLACK tree, keeping the tree balanced.
- * @param node Node to be erased from the tree
- * @param root Root of the tree
- * @private
- * @see #db_rotate_left(DBNode,DBNode *)
- * @see #db_rotate_right(DBNode,DBNode *)
- * @see #db_free_unlock(Database)
- */
-static void db_rebalance_erase(DBNode node, DBNode *root)
-{
- DBNode y = node;
- DBNode x = NULL;
- DBNode x_parent = NULL;
- DBNode w;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_rebalance_erase != (unsigned int)~0) stats.db_rebalance_erase++;
-#endif /* DB_ENABLE_STATS */
- // Select where to change the tree
- if (y->left == NULL) { // no left
- x = y->right;
- } else if (y->right == NULL) { // no right
- x = y->left;
- } else { // both exist, go to the leftmost node of the right sub-tree
- y = y->right;
- while (y->left != NULL)
- y = y->left;
- x = y->right;
- }
-
- // Remove the node from the tree
- if (y != node) { // both childs existed
- // put the left of 'node' in the left of 'y'
- node->left->parent = y;
- y->left = node->left;
-
- // 'y' is not the direct child of 'node'
- if (y != node->right) {
- // put 'x' in the old position of 'y'
- x_parent = y->parent;
- if (x) x->parent = y->parent;
- y->parent->left = x;
- // put the right of 'node' in 'y'
- y->right = node->right;
- node->right->parent = y;
- // 'y' is a direct child of 'node'
- } else {
- x_parent = y;
- }
-
- // link 'y' and the parent of 'node'
- if (*root == node) {
- *root = y; // 'node' was the root
- } else if (node->parent->left == node) {
- node->parent->left = y; // 'node' was at the left
- } else {
- node->parent->right = y; // 'node' was at the right
- }
- y->parent = node->parent;
- // switch colors
- {
- int tmp = y->color;
- y->color = node->color;
- node->color = tmp;
- }
- y = node;
- } else { // one child did not exist
- // put x in node's position
- x_parent = y->parent;
- if (x) x->parent = y->parent;
- // link x and node's parent
- if (*root == node) {
- *root = x; // node was the root
- } else if (node->parent->left == node) {
- node->parent->left = x; // node was at the left
- } else {
- node->parent->right = x; // node was at the right
- }
- }
-
- // Restore the RED-BLACK properties
- if (y->color != RED) {
- while (x != *root && (x == NULL || x->color == BLACK)) {
- if (x == x_parent->left) {
- w = x_parent->right;
- if (w->color == RED) {
- w->color = BLACK;
- x_parent->color = RED;
- db_rotate_left(x_parent, root);
- w = x_parent->right;
- }
- if ((w->left == NULL || w->left->color == BLACK) &&
- (w->right == NULL || w->right->color == BLACK)) {
- w->color = RED;
- x = x_parent;
- x_parent = x_parent->parent;
- } else {
- if (w->right == NULL || w->right->color == BLACK) {
- if (w->left) w->left->color = BLACK;
- w->color = RED;
- db_rotate_right(w, root);
- w = x_parent->right;
- }
- w->color = x_parent->color;
- x_parent->color = BLACK;
- if (w->right) w->right->color = BLACK;
- db_rotate_left(x_parent, root);
- break;
- }
- } else {
- w = x_parent->left;
- if (w->color == RED) {
- w->color = BLACK;
- x_parent->color = RED;
- db_rotate_right(x_parent, root);
- w = x_parent->left;
- }
- if ((w->right == NULL || w->right->color == BLACK) &&
- (w->left == NULL || w->left->color == BLACK)) {
- w->color = RED;
- x = x_parent;
- x_parent = x_parent->parent;
- } else {
- if (w->left == NULL || w->left->color == BLACK) {
- if (w->right) w->right->color = BLACK;
- w->color = RED;
- db_rotate_left(w, root);
- w = x_parent->left;
- }
- w->color = x_parent->color;
- x_parent->color = BLACK;
- if (w->left) w->left->color = BLACK;
- db_rotate_right(x_parent, root);
- break;
- }
- }
- }
- if (x) x->color = BLACK;
- }
-}
-
-/**
- * Returns not 0 if the key is considerd to be NULL.
- * @param type Type of database
- * @param key Key being tested
- * @return not 0 if considered NULL, 0 otherwise
- * @private
- * @see common\db.h#DBType
- * @see common\db.h#DBKey
- * @see #db_get(DBInterface,DBKey)
- * @see #db_put(DBInterface,DBKey,void *)
- * @see #db_remove(DBInterface,DBKey)
- */
-static int db_is_key_null(DBType type, DBKey key)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_is_key_null != (unsigned int)~0) stats.db_is_key_null++;
-#endif /* DB_ENABLE_STATS */
- switch (type) {
- case DB_STRING:
- case DB_ISTRING:
- return (key.str == NULL);
-
- default: // Not a pointer
- return 0;
- }
-}
-
-/**
- * Duplicate the key used in the database.
- * @param db Database the key is being used in
- * @param key Key to be duplicated
- * @param Duplicated key
- * @private
- * @see #db_free_add(Database,DBNode,DBNode *)
- * @see #db_free_remove(Database,DBNode)
- * @see #db_put(DBInterface,DBKey,void *)
- * @see #db_dup_key_free(Database,DBKey)
- */
-static DBKey db_dup_key(Database db, DBKey key)
-{
- unsigned char *str;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_dup_key != (unsigned int)~0) stats.db_dup_key++;
-#endif /* DB_ENABLE_STATS */
- switch (db->type) {
- case DB_STRING:
- case DB_ISTRING:
- if (db->maxlen) {
- CREATE(str, unsigned char, db->maxlen +1);
- memcpy(str, key.str, db->maxlen);
- str[db->maxlen] = '\0';
- key.str = str;
- } else {
- key.str = (unsigned char *)aStrdup((const char *)key.str);
- }
- return key;
-
- default:
- return key;
- }
-}
-
-/**
- * Free a key duplicated by db_dup_key.
- * @param db Database the key is being used in
- * @param key Key to be freed
- * @private
- * @see #db_dup_key(Database,DBKey)
- */
-static void db_dup_key_free(Database db, DBKey key)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_dup_key_free != (unsigned int)~0) stats.db_dup_key_free++;
-#endif /* DB_ENABLE_STATS */
- switch (db->type) {
- case DB_STRING:
- case DB_ISTRING:
- aFree(key.str);
- return;
-
- default:
- return;
- }
-}
-
-/**
- * Add a node to the free_list of the database.
- * Marks the node as deleted.
- * If the key isn't duplicated, the key is duplicated and released.
- * @param db Target database
- * @param root Root of the tree from the node
- * @param node Target node
- * @private
- * @see #struct db_free
- * @see Database#free_list
- * @see Database#free_count
- * @see Database#free_max
- * @see #db_remove(DBInterface,DBKey)
- * @see #db_free_remove(Database,DBNode)
- */
-static void db_free_add(Database db, DBNode node, DBNode *root)
-{
- DBKey old_key;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_free_add != (unsigned int)~0) stats.db_free_add++;
-#endif /* DB_ENABLE_STATS */
- if (db->free_lock == (unsigned int)~0) {
- ShowFatalError("db_free_add: free_lock overflow\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- exit(EXIT_FAILURE);
- }
- if (!(db->options&DB_OPT_DUP_KEY)) { // Make shure we have a key until the node is freed
- old_key = node->key;
- node->key = db_dup_key(db, node->key);
- db->release(old_key, node->data, DB_RELEASE_KEY);
- }
- if (db->free_count == db->free_max) { // No more space, expand free_list
- db->free_max = (db->free_max<<2) +3; // = db->free_max*4 +3
- if (db->free_max <= db->free_count) {
- if (db->free_count == (unsigned int)~0) {
- ShowFatalError("db_free_add: free_count overflow\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- exit(EXIT_FAILURE);
- }
- db->free_max = (unsigned int)~0;
- }
- RECREATE(db->free_list, struct db_free, db->free_max);
- }
- node->deleted = 1;
- db->free_list[db->free_count].node = node;
- db->free_list[db->free_count].root = root;
- db->free_count++;
- db->item_count--;
-}
-
-/**
- * Remove a node from the free_list of the database.
- * Marks the node as not deleted.
- * NOTE: Frees the duplicated key of the node.
- * @param db Target database
- * @param node Node being removed from free_list
- * @private
- * @see #struct db_free
- * @see Database#free_list
- * @see Database#free_count
- * @see #db_put(DBInterface,DBKey,void *)
- * @see #db_free_add(Database,DBNode *,DBNode)
- */
-static void db_free_remove(Database db, DBNode node)
-{
- unsigned int i;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_free_remove != (unsigned int)~0) stats.db_free_remove++;
-#endif /* DB_ENABLE_STATS */
- for (i = 0; i < db->free_count; i++) {
- if (db->free_list[i].node == node) {
- if (i < db->free_count -1) // copy the last item to where the removed one was
- memcpy(&db->free_list[i], &db->free_list[db->free_count -1], sizeof(struct db_free));
- db_dup_key_free(db, node->key);
- break;
- }
- }
- node->deleted = 0;
- if (i == db->free_count) {
- ShowWarning("db_free_remove: node was not found - database allocated at %s:%d\n", db->alloc_file, db->alloc_line);
- } else {
- db->free_count--;
- }
- db->item_count++;
-}
-
-/**
- * Increment the free_lock of the database.
- * @param db Target database
- * @private
- * @see Database#free_lock
- * @see #db_unlock(Database)
- */
-static void db_free_lock(Database db)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_free_lock != (unsigned int)~0) stats.db_free_lock++;
-#endif /* DB_ENABLE_STATS */
- if (db->free_lock == (unsigned int)~0) {
- ShowFatalError("db_free_lock: free_lock overflow\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- exit(EXIT_FAILURE);
- }
- db->free_lock++;
-}
-
-/**
- * Decrement the free_lock of the database.
- * If it was the last lock, frees the nodes of the database.
- * Keeps the tree balanced.
- * NOTE: Frees the duplicated keys of the nodes
- * @param db Target database
- * @private
- * @see Database#free_lock
- * @see #db_free_dbn(DBNode)
- * @see #db_lock(Database)
- */
-static void db_free_unlock(Database db)
-{
- unsigned int i;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_free_unlock != (unsigned int)~0) stats.db_free_unlock++;
-#endif /* DB_ENABLE_STATS */
- if (db->free_lock == 0) {
- ShowWarning("db_free_unlock: free_lock was already 0\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- } else {
- db->free_lock--;
- }
- if (db->free_lock)
- return; // Not last lock
-
- for (i = 0; i < db->free_count ; i++) {
- db_rebalance_erase(db->free_list[i].node, db->free_list[i].root);
- db_dup_key_free(db, db->free_list[i].node->key);
-#ifdef DB_ENABLE_STATS
- if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++;
-#endif /* DB_ENABLE_STATS */
- ers_free(db->nodes, db->free_list[i].node);
- }
- db->free_count = 0;
-}
-
-/*****************************************************************************\
- * (3) Section of protected functions used internally. *
- * NOTE: the protected functions used in the database interface are in the *
- * next section. *
- * db_int_cmp - Default comparator for DB_INT databases. *
- * db_uint_cmp - Default comparator for DB_UINT databases. *
- * db_string_cmp - Default comparator for DB_STRING databases. *
- * db_istring_cmp - Default comparator for DB_ISTRING databases. *
- * db_int_hash - Default hasher for DB_INT databases. *
- * db_uint_hash - Default hasher for DB_UINT databases. *
- * db_string_hash - Default hasher for DB_STRING databases. *
- * db_istring_hash - Default hasher for DB_ISTRING databases. *
- * db_release_nothing - Releaser that releases nothing. *
- * db_release_key - Releaser that only releases the key. *
- * db_release_data - Releaser that only releases the data. *
- * db_release_both - Releaser that releases key and data. *
-\*****************************************************************************/
-
-/**
- * Default comparator for DB_INT databases.
- * Compares key1 to key2.
- * Return 0 if equal, negative if lower and positive if higher.
- * <code>maxlen</code> is ignored.
- * @param key1 Key to be compared
- * @param key2 Key being compared to
- * @param maxlen Maximum length of the key to hash
- * @return 0 if equal, negative if lower and positive if higher
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_INT
- * @see common\db.h#DBComparator
- * @see #db_default_cmp(DBType)
- */
-static int db_int_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_int_cmp != (unsigned int)~0) stats.db_int_cmp++;
-#endif /* DB_ENABLE_STATS */
- if (key1.i < key2.i) return -1;
- if (key1.i > key2.i) return 1;
- return 0;
-}
-
-/**
- * Default comparator for DB_UINT databases.
- * Compares key1 to key2.
- * Return 0 if equal, negative if lower and positive if higher.
- * <code>maxlen</code> is ignored.
- * @param key1 Key to be compared
- * @param key2 Key being compared to
- * @param maxlen Maximum length of the key to hash
- * @return 0 if equal, negative if lower and positive if higher
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_UINT
- * @see common\db.h#DBComparator
- * @see #db_default_cmp(DBType)
- */
-static int db_uint_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_uint_cmp != (unsigned int)~0) stats.db_uint_cmp++;
-#endif /* DB_ENABLE_STATS */
- if (key1.ui < key2.ui) return -1;
- if (key1.ui > key2.ui) return 1;
- return 0;
-}
-
-/**
- * Default comparator for DB_STRING databases.
- * Compares key1 to key2.
- * Return 0 if equal, negative if lower and positive if higher.
- * @param key1 Key to be compared
- * @param key2 Key being compared to
- * @param maxlen Maximum length of the key to hash
- * @return 0 if equal, negative if lower and positive if higher
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_STRING
- * @see common\db.h#DBComparator
- * @see #db_default_cmp(DBType)
- */
-static int db_string_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_string_cmp != (unsigned int)~0) stats.db_string_cmp++;
-#endif /* DB_ENABLE_STATS */
- if (maxlen == 0) maxlen = (unsigned short)~0;
- return strncmp((const char *)key1.str, (const char *)key2.str, maxlen);
-}
-
-/**
- * Default comparator for DB_ISTRING databases.
- * Compares key1 to key2 case insensitively.
- * Return 0 if equal, negative if lower and positive if higher.
- * @param key1 Key to be compared
- * @param key2 Key being compared to
- * @param maxlen Maximum length of the key to hash
- * @return 0 if equal, negative if lower and positive if higher
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_ISTRING
- * @see common\db.h#DBComparator
- * @see #db_default_cmp(DBType)
- */
-static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_istring_cmp != (unsigned int)~0) stats.db_istring_cmp++;
-#endif /* DB_ENABLE_STATS */
- if (maxlen == 0) maxlen = (unsigned short)~0;
- return strncasecmp((const char *)key1.str, (const char *)key2.str, maxlen);
-}
-
-/**
- * Default hasher for DB_INT databases.
- * Returns the value of the key as an unsigned int.
- * <code>maxlen</code> is ignored.
- * @param key Key to be hashed
- * @param maxlen Maximum length of the key to hash
- * @return hash of the key
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_INT
- * @see common\db.h#DBHasher
- * @see #db_default_hash(DBType)
- */
-static unsigned int db_int_hash(DBKey key, unsigned short maxlen)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_int_hash != (unsigned int)~0) stats.db_int_hash++;
-#endif /* DB_ENABLE_STATS */
- return (unsigned int)key.i;
-}
-
-/**
- * Default hasher for DB_UINT databases.
- * Just returns the value of the key.
- * <code>maxlen</code> is ignored.
- * @param key Key to be hashed
- * @param maxlen Maximum length of the key to hash
- * @return hash of the key
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_UINT
- * @see #db_default_hash(DBType)
- */
-static unsigned int db_uint_hash(DBKey key, unsigned short maxlen)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_uint_hash != (unsigned int)~0) stats.db_uint_hash++;
-#endif /* DB_ENABLE_STATS */
- return key.ui;
-}
-
-/**
- * Default hasher for DB_STRING databases.
- * If maxlen if 0, the maximum number of maxlen is used instead.
- * @param key Key to be hashed
- * @param maxlen Maximum length of the key to hash
- * @return hash of the key
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_STRING
- * @see #db_default_hash(DBType)
- */
-static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
-{
- unsigned char *k = key.str;
- unsigned int hash = 0;
- unsigned short i;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_string_hash != (unsigned int)~0) stats.db_string_hash++;
-#endif /* DB_ENABLE_STATS */
- if (maxlen == 0)
- maxlen = (unsigned short)~0; // Maximum
-
- for (i = 0; *k; i++) {
- hash = (hash*33 + *k++)^(hash>>24);
- if (i == maxlen)
- break;
- }
-
- return hash;
-}
-
-/**
- * Default hasher for DB_ISTRING databases.
- * If maxlen if 0, the maximum number of maxlen is used instead.
- * @param key Key to be hashed
- * @param maxlen Maximum length of the key to hash
- * @return hash of the key
- * @see common\db.h#DBKey
- * @see common\db.h\DBType#DB_ISTRING
- * @see #db_default_hash(DBType)
- */
-static unsigned int db_istring_hash(DBKey key, unsigned short maxlen)
-{
- unsigned char *k = key.str;
- unsigned int hash = 0;
- unsigned short i;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_istring_hash != (unsigned int)~0) stats.db_istring_hash++;
-#endif /* DB_ENABLE_STATS */
- if (maxlen == 0)
- maxlen = (unsigned short)~0; // Maximum
-
- for (i = 0; *k; i++) {
- hash = (hash*33 + LOWER(*k))^(hash>>24);
- k++;
- if (i == maxlen)
- break;
- }
-
- return hash;
-}
-
-/**
- * Releaser that releases nothing.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param which What is being requested to be released
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBRelease
- * @see common\db.h#DBReleaser
- * @see #db_default_releaser(DBType,DBOptions)
- */
-static void db_release_nothing(DBKey key, void *data, DBRelease which)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_release_nothing != (unsigned int)~0) stats.db_release_nothing++;
-#endif /* DB_ENABLE_STATS */
-}
-
-/**
- * Releaser that only releases the key.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param which What is being requested to be released
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBRelease
- * @see common\db.h#DBReleaser
- * @see #db_default_release(DBType,DBOptions)
- */
-static void db_release_key(DBKey key, void *data, DBRelease which)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_release_key != (unsigned int)~0) stats.db_release_key++;
-#endif /* DB_ENABLE_STATS */
- if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer
-}
-
-/**
- * Releaser that only releases the data.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param which What is being requested to be released
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBRelease
- * @see common\db.h#DBReleaser
- * @see #db_default_release(DBType,DBOptions)
- */
-static void db_release_data(DBKey key, void *data, DBRelease which)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_release_data != (unsigned int)~0) stats.db_release_data++;
-#endif /* DB_ENABLE_STATS */
- if (which&DB_RELEASE_DATA) aFree(data);
-}
-
-/**
- * Releaser that releases both key and data.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param which What is being requested to be released
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBRelease
- * @see common\db.h#DBReleaser
- * @see #db_default_release(DBType,DBOptions)
- */
-static void db_release_both(DBKey key, void *data, DBRelease which)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_release_both != (unsigned int)~0) stats.db_release_both++;
-#endif /* DB_ENABLE_STATS */
- if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer
- if (which&DB_RELEASE_DATA) aFree(data);
-}
-
-/*****************************************************************************\
- * (4) Section with protected functions used in the interface of the *
- * database. *
- * db_obj_get - Get the data identified by the key. *
- * db_obj_vgetall - Get the data of the matched entries. *
- * db_obj_getall - Get the data of the matched entries. *
- * db_obj_vensure - Get the data identified by the key, creating if it *
- * doesn't exist yet. *
- * db_obj_ensure - Get the data identified by the key, creating if it *
- * doesn't exist yet. *
- * db_obj_put - Put data identified by the key in the database. *
- * db_obj_remove - Remove an entry from the database. *
- * db_obj_vforeach - Apply a function to every entry in the database. *
- * db_obj_foreach - Apply a function to every entry in the database. *
- * db_obj_vclear - Remove all entries from the database. *
- * db_obj_clear - Remove all entries from the database. *
- * db_obj_vdestroy - Destroy the database, freeing all the used memory. *
- * db_obj_destroy - Destroy the database, freeing all the used memory. *
- * db_obj_size - Return the size of the database. *
- * db_obj_type - Return the type of the database. *
- * db_obj_options - Return the options of the database. *
-\*****************************************************************************/
-
-/**
- * Get the data of the entry identifid by the key.
- * @param self Interface of the database
- * @param key Key that identifies the entry
- * @return Data of the entry or NULL if not found
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#get(DBInterface,DBKey)
- */
-static void *db_obj_get(DBInterface self, DBKey key)
-{
- Database db = (Database)self;
- DBNode node;
- int c;
- void *data = NULL;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_get != (unsigned int)~0) stats.db_get++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return NULL; // nullpo candidate
- if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
- ShowError("db_get: Attempted to retrieve non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
-
- db_free_lock(db);
- node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE];
- while (node) {
- c = db->cmp(key, node->key, db->maxlen);
- if (c == 0) {
- data = node->data;
- break;
- }
- if (c < 0)
- node = node->left;
- else
- node = node->right;
- }
- db_free_unlock(db);
- return data;
-}
-
-/**
- * Get the data of the entries matched by <code>match</code>.
- * It puts a maximum of <code>max</code> entries into <code>buf</code>.
- * If <code>buf</code> is NULL, it only counts the matches.
- * Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
- * first <code>max</code> entries found are put into the buffer.
- * @param self Interface of the database
- * @param buf Buffer to put the data of the matched entries
- * @param max Maximum number of data entries to be put into buf
- * @param match Function that matches the database entries
- * @param ... Extra arguments for match
- * @return The number of entries that matched
- * @protected
- * @see common\db.h#DBInterface
- * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args)
- * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
- */
-static unsigned int db_obj_vgetall(DBInterface self, void **buf, unsigned int max, DBMatcher match, va_list args)
-{
- Database db = (Database)self;
- unsigned int i;
- DBNode node;
- DBNode parent;
- unsigned int ret = 0;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_vgetall != (unsigned int)~0) stats.db_vgetall++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return 0; // nullpo candidate
- if (match == NULL) return 0; // nullpo candidate
-
- db_free_lock(db);
- for (i = 0; i < HASH_SIZE; i++) {
- // Match in the order: current node, left tree, right tree
- node = db->ht[i];
- while (node) {
- parent = node->parent;
- if (!(node->deleted) && match(node->key, node->data, args) == 0) {
- if (buf && ret < max)
- buf[ret] = node->data;
- ret++;
- }
- if (node->left) {
- node = node->left;
- continue;
- }
- if (node->right) {
- node = node->right;
- continue;
- }
- while (node) {
- parent = node->parent;
- if (parent && parent->right && parent->left == node) {
- node = parent->right;
- break;
- }
- node = parent;
- }
- }
- }
- db_free_unlock(db);
- return ret;
-}
-
-/**
- * Just calls {@link common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}.
- * Get the data of the entries matched by <code>match</code>.
- * It puts a maximum of <code>max</code> entries into <code>buf</code>.
- * If <code>buf</code> is NULL, it only counts the matches.
- * Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
- * first <code>max</code> entries found are put into the buffer.
- * @param self Interface of the database
- * @param buf Buffer to put the data of the matched entries
- * @param max Maximum number of data entries to be put into buf
- * @param match Function that matches the database entries
- * @param ... Extra arguments for match
- * @return The number of entries that matched
- * @protected
- * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args)
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
- * @see common\db.h\DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
- */
-static unsigned int db_obj_getall(DBInterface self, void **buf, unsigned int max, DBMatcher match, ...)
-{
- va_list args;
- unsigned int ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_getall != (unsigned int)~0) stats.db_getall++;
-#endif /* DB_ENABLE_STATS */
- if (self == NULL) return 0; // nullpo candidate
-
- va_start(args, match);
- ret = self->vgetall(self, buf, max, match, args);
- va_end(args);
- return ret;
-}
-
-/**
- * Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
- * <code>create</code>.
- * @param self Interface of the database
- * @param key Key that identifies the entry
- * @param create Function used to create the data if the entry doesn't exist
- * @param args Extra arguments for create
- * @return Data of the entry
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBCreateData
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
- */
-static void *db_obj_vensure(DBInterface self, DBKey key, DBCreateData create, va_list args)
-{
- Database db = (Database)self;
- DBNode node;
- DBNode parent = NULL;
- unsigned int hash;
- int c = 0;
- void *data = NULL;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_vensure != (unsigned int)~0) stats.db_vensure++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return NULL; // nullpo candidate
- if (create == NULL) {
- ShowError("db_ensure: Create function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
- if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
- ShowError("db_ensure: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
-
- db_free_lock(db);
- hash = db->hash(key, db->maxlen)%HASH_SIZE;
- node = db->ht[hash];
- while (node) {
- c = db->cmp(key, node->key, db->maxlen);
- if (c == 0) {
- break;
- }
- parent = node;
- if (c < 0)
- node = node->left;
- else
- node = node->right;
- }
- // Create node if necessary
- if (node == NULL) {
- if (db->item_count == (unsigned int)~0) {
- ShowError("db_vensure: item_count overflow, aborting item insertion.\n"
- "Database allocated at %s:%d",
- db->alloc_file, db->alloc_line);
- return NULL;
- }
-#ifdef DB_ENABLE_STATS
- if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++;
-#endif /* DB_ENABLE_STATS */
- node = ers_alloc(db->nodes, struct dbn);
- node->left = NULL;
- node->right = NULL;
- node->deleted = 0;
- db->item_count++;
- if (c == 0) { // hash entry is empty
- node->color = BLACK;
- node->parent = NULL;
- db->ht[hash] = node;
- } else {
- node->color = RED;
- if (c < 0) { // put at the left
- parent->left = node;
- node->parent = parent;
- } else { // put at the right
- parent->right = node;
- node->parent = parent;
- }
- if (parent->color == RED) // two consecutive RED nodes, must rebalance
- db_rebalance(node, &db->ht[hash]);
- }
- // put key and data in the node
- if (db->options&DB_OPT_DUP_KEY) {
- node->key = db_dup_key(db, key);
- if (db->options&DB_OPT_RELEASE_KEY)
- db->release(key, data, DB_RELEASE_KEY);
- } else {
- node->key = key;
- }
- node->data = create(key, args);
- }
- data = node->data;
- db_free_unlock(db);
- return data;
-}
-
-/**
- * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}.
- * Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
- * <code>create</code>.
- * @param self Interface of the database
- * @param key Key that identifies the entry
- * @param create Function used to create the data if the entry doesn't exist
- * @param ... Extra arguments for create
- * @return Data of the entry
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBCreateData
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
- * @see common\db.h\DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
- */
-static void *db_obj_ensure(DBInterface self, DBKey key, DBCreateData create, ...)
-{
- va_list args;
- void *ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_ensure != (unsigned int)~0) stats.db_ensure++;
-#endif /* DB_ENABLE_STATS */
- if (self == NULL) return 0; // nullpo candidate
-
- va_start(args, create);
- ret = self->vensure(self, key, create, args);
- va_end(args);
- return ret;
-}
-
-/**
- * Put the data identified by the key in the database.
- * Returns the previous data if the entry exists or NULL.
- * NOTE: Uses the new key, the old one is released.
- * @param self Interface of the database
- * @param key Key that identifies the data
- * @param data Data to be put in the database
- * @return The previous data if the entry exists or NULL
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBInterface
- * @see #db_malloc_dbn(void)
- * @see common\db.h\DBInterface#put(DBInterface,DBKey,void *)
- */
-static void *db_obj_put(DBInterface self, DBKey key, void *data)
-{
- Database db = (Database)self;
- DBNode node;
- DBNode parent = NULL;
- int c = 0;
- unsigned int hash;
- void *old_data = NULL;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_put != (unsigned int)~0) stats.db_put++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return NULL; // nullpo candidate
- if (db->global_lock) {
- ShowError("db_put: Database is being destroyed, aborting entry insertion.\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
- if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
- ShowError("db_put: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
- if (!(data || db->options&DB_OPT_ALLOW_NULL_DATA)) {
- ShowError("db_put: Attempted to use non-allowed NULL data for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
-
- if (db->item_count == (unsigned int)~0) {
- ShowError("db_put: item_count overflow, aborting item insertion.\n"
- "Database allocated at %s:%d",
- db->alloc_file, db->alloc_line);
- return NULL;
- }
- // search for an equal node
- db_free_lock(db);
- hash = db->hash(key, db->maxlen)%HASH_SIZE;
- for (node = db->ht[hash]; node; ) {
- c = db->cmp(key, node->key, db->maxlen);
- if (c == 0) { // equal entry, replace
- if (node->deleted) {
- db_free_remove(db, node);
- } else {
- db->release(node->key, node->data, DB_RELEASE_BOTH);
- }
- old_data = node->data;
- break;
- }
- parent = node;
- if (c < 0) {
- node = node->left;
- } else {
- node = node->right;
- }
- }
- // allocate a new node if necessary
- if (node == NULL) {
-#ifdef DB_ENABLE_STATS
- if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++;
-#endif /* DB_ENABLE_STATS */
- node = ers_alloc(db->nodes, struct dbn);
- node->left = NULL;
- node->right = NULL;
- node->deleted = 0;
- db->item_count++;
- if (c == 0) { // hash entry is empty
- node->color = BLACK;
- node->parent = NULL;
- db->ht[hash] = node;
- } else {
- node->color = RED;
- if (c < 0) { // put at the left
- parent->left = node;
- node->parent = parent;
- } else { // put at the right
- parent->right = node;
- node->parent = parent;
- }
- if (parent->color == RED) // two consecutive RED nodes, must rebalance
- db_rebalance(node, &db->ht[hash]);
- }
- }
- // put key and data in the node
- if (db->options&DB_OPT_DUP_KEY) {
- node->key = db_dup_key(db, key);
- if (db->options&DB_OPT_RELEASE_KEY)
- db->release(key, data, DB_RELEASE_KEY);
- } else {
- node->key = key;
- }
- node->data = data;
- db_free_unlock(db);
- return old_data;
-}
-
-/**
- * Remove an entry from the database.
- * Returns the data of the entry.
- * NOTE: The key (of the database) is released in {@link #db_free_add(Database,DBNode,DBNode *)}.
- * @param self Interface of the database
- * @param key Key that identifies the entry
- * @return The data of the entry or NULL if not found
- * @protected
- * @see common\db.h#DBKey
- * @see common\db.h#DBInterface
- * @see #db_free_add(Database,DBNode,DBNode *)
- * @see common\db.h\DBInterface#remove(DBInterface,DBKey)
- */
-static void *db_obj_remove(DBInterface self, DBKey key)
-{
- Database db = (Database)self;
- void *data = NULL;
- DBNode node;
- unsigned int hash;
- int c = 0;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_remove != (unsigned int)~0) stats.db_remove++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return NULL; // nullpo candidate
- if (db->global_lock) {
- ShowError("db_remove: Database is being destroyed. Aborting entry deletion.\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
- if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
- ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return NULL; // nullpo candidate
- }
-
- db_free_lock(db);
- hash = db->hash(key, db->maxlen)%HASH_SIZE;
- for(node = db->ht[hash]; node; ){
- c = db->cmp(key, node->key, db->maxlen);
- if (c == 0) {
- if (!(node->deleted)) {
- data = node->data;
- db->release(node->key, node->data, DB_RELEASE_DATA);
- db_free_add(db, node, &db->ht[hash]);
- }
- break;
- }
- if (c < 0)
- node = node->left;
- else
- node = node->right;
- }
- db_free_unlock(db);
- return data;
-}
-
-/**
- * Apply <code>func</code> to every entry in the database.
- * Returns the sum of values returned by func.
- * @param self Interface of the database
- * @param func Function to be applyed
- * @param args Extra arguments for func
- * @return Sum of the values returned by func
- * @protected
- * @see common\db.h#DBInterface
- * @see common\db.h#DBApply(DBKey,void *,va_list)
- * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)
- */
-static int db_obj_vforeach(DBInterface self, DBApply func, va_list args)
-{
- Database db = (Database)self;
- unsigned int i;
- int sum = 0;
- DBNode node;
- DBNode parent;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_vforeach != (unsigned int)~0) stats.db_vforeach++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return 0; // nullpo candidate
- if (func == NULL) {
- ShowError("db_foreach: Passed function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
- return 0; // nullpo candidate
- }
-
- db_free_lock(db);
- for (i = 0; i < HASH_SIZE; i++) {
- // Apply func in the order: current node, left node, right node
- node = db->ht[i];
- while (node) {
- parent = node->parent;
- if (!(node->deleted))
- sum += func(node->key, node->data, args);
- if (node->left) {
- node = node->left;
- continue;
- }
- if (node->right) {
- node = node->right;
- continue;
- }
- while (node) {
- parent = node->parent;
- if (parent && parent->right && parent->left == node) {
- node = parent->right;
- break;
- }
- node = parent;
- }
- }
- }
- db_free_unlock(db);
- return sum;
-}
-
-/**
- * Just calls {@link common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)}.
- * Apply <code>func</code> to every entry in the database.
- * Returns the sum of values returned by func.
- * @param self Interface of the database
- * @param func Function to be applyed
- * @param ... Extra arguments for func
- * @return Sum of the values returned by func
- * @protected
- * @see common\db.h#DBInterface
- * @see common\db.h#DBApply(DBKey,void *,va_list)
- * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)
- * @see common\db.h\DBInterface#foreach(DBInterface,DBApply,...)
- */
-static int db_obj_foreach(DBInterface self, DBApply func, ...)
-{
- va_list args;
- int ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_foreach != (unsigned int)~0) stats.db_foreach++;
-#endif /* DB_ENABLE_STATS */
- if (self == NULL) return 0; // nullpo candidate
-
- va_start(args, func);
- ret = self->vforeach(self, func, args);
- va_end(args);
- return ret;
-}
-
-/**
- * Removes all entries from the database.
- * Before deleting an entry, func is applyed to it.
- * Releases the key and the data.
- * Returns the sum of values returned by func, if it exists.
- * @param self Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param args Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see common\db.h#DBApply(DBKey,void *,va_list)
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)
- */
-static int db_obj_vclear(DBInterface self, DBApply func, va_list args)
-{
- Database db = (Database)self;
- int sum = 0;
- unsigned int i;
- DBNode node;
- DBNode parent;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_vclear != (unsigned int)~0) stats.db_vclear++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return 0; // nullpo candidate
-
- db_free_lock(db);
- for (i = 0; i < HASH_SIZE; i++) {
- // Apply the func and delete in the order: left tree, right tree, current node
- node = db->ht[i];
- db->ht[i] = NULL;
- while (node) {
- parent = node->parent;
- if (node->left) {
- node = node->left;
- continue;
- }
- if (node->right) {
- node = node->right;
- continue;
- }
- if (node->deleted) {
- db_dup_key_free(db, node->key);
- } else {
- if (func)
- sum += func(node->key, node->data, args);
- db->release(node->key, node->data, DB_RELEASE_BOTH);
- node->deleted = 1;
- }
-#ifdef DB_ENABLE_STATS
- if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++;
-#endif /* DB_ENABLE_STATS */
- ers_free(db->nodes, node);
- if (parent) {
- if (parent->left == node)
- parent->left = NULL;
- else
- parent->right = NULL;
- }
- node = parent;
- }
- db->ht[i] = NULL;
- }
- db->free_count = 0;
- db->item_count = 0;
- db_free_unlock(db);
- return sum;
-}
-
-/**
- * Just calls {@link common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)}.
- * Removes all entries from the database.
- * Before deleting an entry, func is applyed to it.
- * Releases the key and the data.
- * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
- * a database entry will give an error and be aborted (except for clearing).
- * @param self Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param ... Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see common\db.h#DBApply(DBKey,void *,va_list)
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)
- * @see common\db.h\DBInterface#clear(DBInterface,DBApply,...)
- */
-static int db_obj_clear(DBInterface self, DBApply func, ...)
-{
- va_list args;
- int ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_clear != (unsigned int)~0) stats.db_clear++;
-#endif /* DB_ENABLE_STATS */
- if (self == NULL) return 0; // nullpo candidate
-
- va_start(args, func);
- ret = self->vclear(self, func, args);
- va_end(args);
- return ret;
-}
-
-/**
- * Finalize the database, feeing all the memory it uses.
- * Before deleting an entry, func is applyed to it.
- * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
- * a database entry will give an error and be aborted (except for clearing).
- * @param self Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param args Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see common\db.h#DBApply(DBKey,void *,va_list)
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list)
- */
-static int db_obj_vdestroy(DBInterface self, DBApply func, va_list args)
-{
- Database db = (Database)self;
- int sum;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_vdestroy != (unsigned int)~0) stats.db_vdestroy++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return 0; // nullpo candidate
- if (db->global_lock) {
- ShowError("db_vdestroy: Database is already locked for destruction. Aborting second database destruction.\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line);
- return 0;
- }
- if (db->free_lock)
- ShowWarning("db_vdestroy: Database is still in use, %u lock(s) left. Continuing database destruction.\n"
- "Database allocated at %s:%d\n",
- db->alloc_file, db->alloc_line, db->free_lock);
-
-#ifdef DB_ENABLE_STATS
- switch (db->type) {
- case DB_INT:
- stats.db_int_destroy++;
- break;
- case DB_UINT:
- stats.db_uint_destroy++;
- break;
- case DB_STRING:
- stats.db_string_destroy++;
- break;
- case DB_ISTRING:
- stats.db_istring_destroy++;
- break;
- }
-#endif /* DB_ENABLE_STATS */
- db_free_lock(db);
- db->global_lock = 1;
- sum = self->vclear(self, func, args);
- aFree(db->free_list);
- db->free_list = NULL;
- db->free_max = 0;
- ers_destroy(db->nodes);
- db_free_unlock(db);
- aFree(db);
- return sum;
-}
-
-/**
- * Just calls {@link common\db.h\DBInterface#db_vdestroy(DBInterface,DBApply,va_list)}.
- * Finalize the database, feeing all the memory it uses.
- * Before deleting an entry, func is applyed to it.
- * Releases the key and the data.
- * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
- * a database entry will give an error and be aborted.
- * @param self Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param ... Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see common\db.h#DBApply(DBKey,void *,va_list)
- * @see common\db.h#DBInterface
- * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list)
- * @see common\db.h\DBInterface#destroy(DBInterface,DBApply,...)
- */
-static int db_obj_destroy(DBInterface self, DBApply func, ...)
-{
- va_list args;
- int ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_destroy != (unsigned int)~0) stats.db_destroy++;
-#endif /* DB_ENABLE_STATS */
- if (self == NULL) return 0; // nullpo candidate
-
- va_start(args, func);
- ret = self->vdestroy(self, func, args);
- va_end(args);
- return ret;
-}
-
-/**
- * Return the size of the database (number of items in the database).
- * @param self Interface of the database
- * @return Size of the database
- * @protected
- * @see common\db.h#DBInterface
- * @see Database#item_count
- * @see common\db.h\DBInterface#size(DBInterface)
- */
-static unsigned int db_obj_size(DBInterface self)
-{
- Database db = (Database)self;
- unsigned int item_count;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_size != (unsigned int)~0) stats.db_size++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return 0; // nullpo candidate
-
- db_free_lock(db);
- item_count = db->item_count;
- db_free_unlock(db);
-
- return item_count;
-}
-
-/**
- * Return the type of database.
- * @param self Interface of the database
- * @return Type of the database
- * @protected
- * @see common\db.h#DBType
- * @see common\db.h#DBInterface
- * @see Database#type
- * @see common\db.h\DBInterface#type(DBInterface)
- */
-static DBType db_obj_type(DBInterface self)
-{
- Database db = (Database)self;
- DBType type;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_type != (unsigned int)~0) stats.db_type++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return -1; // nullpo candidate - TODO what should this return?
-
- db_free_lock(db);
- type = db->type;
- db_free_unlock(db);
-
- return type;
-}
-
-/**
- * Return the options of the database.
- * @param self Interface of the database
- * @return Options of the database
- * @protected
- * @see common\db.h#DBOptions
- * @see common\db.h#DBInterface
- * @see Database#options
- * @see common\db.h\DBInterface#options(DBInterface)
- */
-static DBOptions db_obj_options(DBInterface self)
-{
- Database db = (Database)self;
- DBOptions options;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_options != (unsigned int)~0) stats.db_options++;
-#endif /* DB_ENABLE_STATS */
- if (db == NULL) return DB_OPT_BASE; // nullpo candidate - TODO what should this return?
-
- db_free_lock(db);
- options = db->options;
- db_free_unlock(db);
-
- return options;
-}
-
-/*****************************************************************************\
- * (5) Section with public functions. *
- * db_fix_options - Apply database type restrictions to the options. *
- * db_default_cmp - Get the default comparator for a type of database. *
- * db_default_hash - Get the default hasher for a type of database. *
- * db_default_release - Get the default releaser for a type of database *
- * with the specified options. *
- * db_custom_release - Get a releaser that behaves a certains way. *
- * db_alloc - Allocate a new database. *
- * db_i2key - Manual cast from 'int' to 'DBKey'. *
- * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
- * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
- * db_init - Initialize the database system. *
- * db_final - Finalize the database system. *
-\*****************************************************************************/
-
-/**
- * Returns the fixed options according to the database type.
- * Sets required options and unsets unsupported options.
- * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset.
- * @param type Type of the database
- * @param options Original options of the database
- * @return Fixed options of the database
- * @private
- * @see common\db.h#DBType
- * @see common\db.h#DBOptions
- * @see #db_default_release(DBType,DBOptions)
- * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
- * @see common\db.h#db_fix_options(DBType,DBOptions)
- */
-DBOptions db_fix_options(DBType type, DBOptions options)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_fix_options != (unsigned int)~0) stats.db_fix_options++;
-#endif /* DB_ENABLE_STATS */
- switch (type) {
- case DB_INT:
- case DB_UINT: // Numeric database, do nothing with the keys
- return options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY);
-
- default:
- ShowError("db_fix_options: Unknown database type %u with options %x\n", type, options);
- case DB_STRING:
- case DB_ISTRING: // String databases, no fix required
- return options;
- }
-}
-
-/**
- * Returns the default comparator for the specified type of database.
- * @param type Type of database
- * @return Comparator for the type of database or NULL if unknown database
- * @public
- * @see common\db.h#DBType
- * @see #db_int_cmp(DBKey,DBKey,unsigned short)
- * @see #db_uint_cmp(DBKey,DBKey,unsigned short)
- * @see #db_string_cmp(DBKey,DBKey,unsigned short)
- * @see #db_istring_cmp(DBKey,DBKey,unsigned short)
- * @see common\db.h#db_default_cmp(DBType)
- */
-DBComparator db_default_cmp(DBType type)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_default_cmp != (unsigned int)~0) stats.db_default_cmp++;
-#endif /* DB_ENABLE_STATS */
- switch (type) {
- case DB_INT: return db_int_cmp;
- case DB_UINT: return db_uint_cmp;
- case DB_STRING: return db_string_cmp;
- case DB_ISTRING: return db_istring_cmp;
- default:
- ShowError("db_default_cmp: Unknown database type %u\n", type);
- return NULL;
- }
-}
-
-/**
- * Returns the default hasher for the specified type of database.
- * @param type Type of database
- * @return Hasher of the type of database or NULL if unknown database
- * @public
- * @see common\db.h#DBType
- * @see #db_int_hash(DBKey,unsigned short)
- * @see #db_uint_hash(DBKey,unsigned short)
- * @see #db_string_hash(DBKey,unsigned short)
- * @see #db_istring_hash(DBKey,unsigned short)
- * @see common\db.h#db_default_hash(DBType)
- */
-DBHasher db_default_hash(DBType type)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_default_hash != (unsigned int)~0) stats.db_default_hash++;
-#endif /* DB_ENABLE_STATS */
- switch (type) {
- case DB_INT: return db_int_hash;
- case DB_UINT: return db_uint_hash;
- case DB_STRING: return db_string_hash;
- case DB_ISTRING: return db_istring_hash;
- default:
- ShowError("db_default_hash: Unknown database type %u\n", type);
- return NULL;
- }
-}
-
-/**
- * Returns the default releaser for the specified type of database with the
- * specified options.
- * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)}
- * before choosing the releaser.
- * @param type Type of database
- * @param options Options of the database
- * @return Default releaser for the type of database with the specified options
- * @public
- * @see common\db.h#DBType
- * @see common\db.h#DBOptions
- * @see common\db.h#DBReleaser
- * @see #db_release_nothing(DBKey,void *,DBRelease)
- * @see #db_release_key(DBKey,void *,DBRelease)
- * @see #db_release_data(DBKey,void *,DBRelease)
- * @see #db_release_both(DBKey,void *,DBRelease)
- * @see #db_custom_release(DBRelease)
- * @see common\db.h#db_default_release(DBType,DBOptions)
- */
-DBReleaser db_default_release(DBType type, DBOptions options)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_default_release != (unsigned int)~0) stats.db_default_release++;
-#endif /* DB_ENABLE_STATS */
- options = db_fix_options(type, options);
- if (options&DB_OPT_RELEASE_DATA) { // Release data, what about the key?
- if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY))
- return db_release_both; // Release both key and data
- return db_release_data; // Only release data
- }
- if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY))
- return db_release_key; // Only release key
- return db_release_nothing; // Release nothing
-}
-
-/**
- * Returns the releaser that releases the specified release options.
- * @param which Options that specified what the releaser releases
- * @return Releaser for the specified release options
- * @public
- * @see common\db.h#DBRelease
- * @see common\db.h#DBReleaser
- * @see #db_release_nothing(DBKey,void *,DBRelease)
- * @see #db_release_key(DBKey,void *,DBRelease)
- * @see #db_release_data(DBKey,void *,DBRelease)
- * @see #db_release_both(DBKey,void *,DBRelease)
- * @see #db_default_release(DBType,DBOptions)
- * @see common\db.h#db_custom_release(DBRelease)
- */
-DBReleaser db_custom_release(DBRelease which)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_custom_release != (unsigned int)~0) stats.db_custom_release++;
-#endif /* DB_ENABLE_STATS */
- switch (which) {
- case DB_RELEASE_NOTHING: return db_release_nothing;
- case DB_RELEASE_KEY: return db_release_key;
- case DB_RELEASE_DATA: return db_release_data;
- case DB_RELEASE_BOTH: return db_release_both;
- default:
- ShowError("db_custom_release: Unknown release options %u\n", which);
- return NULL;
- }
-}
-
-/**
- * Allocate a new database of the specified type.
- * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
- * before creating the database.
- * @param file File where the database is being allocated
- * @param line Line of the file where the database is being allocated
- * @param type Type of database
- * @param options Options of the database
- * @param maxlen Maximum length of the string to be used as key in string
- * databases
- * @return The interface of the database
- * @public
- * @see common\db.h#DBType
- * @see common\db.h#DBInterface
- * @see #Database
- * @see #db_fix_options(DBType,DBOptions)
- * @see common\db.h#db_alloc(const char *,int,DBType,unsigned short)
- */
-DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen)
-{
- Database db;
- unsigned int i;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_alloc != (unsigned int)~0) stats.db_alloc++;
- switch (type) {
- case DB_INT:
- stats.db_int_alloc++;
- break;
- case DB_UINT:
- stats.db_uint_alloc++;
- break;
- case DB_STRING:
- stats.db_string_alloc++;
- break;
- case DB_ISTRING:
- stats.db_istring_alloc++;
- break;
- }
-#endif /* DB_ENABLE_STATS */
- CREATE(db, struct db, 1);
-
- options = db_fix_options(type, options);
- /* Interface of the database */
- db->dbi.get = db_obj_get;
- db->dbi.getall = db_obj_getall;
- db->dbi.vgetall = db_obj_vgetall;
- db->dbi.ensure = db_obj_ensure;
- db->dbi.vensure = db_obj_vensure;
- db->dbi.put = db_obj_put;
- db->dbi.remove = db_obj_remove;
- db->dbi.foreach = db_obj_foreach;
- db->dbi.vforeach = db_obj_vforeach;
- db->dbi.clear = db_obj_clear;
- db->dbi.vclear = db_obj_vclear;
- db->dbi.destroy = db_obj_destroy;
- db->dbi.vdestroy = db_obj_vdestroy;
- db->dbi.size = db_obj_size;
- db->dbi.type = db_obj_type;
- db->dbi.options = db_obj_options;
- /* File and line of allocation */
- db->alloc_file = file;
- db->alloc_line = line;
- /* Lock system */
- db->free_list = NULL;
- db->free_count = 0;
- db->free_max = 0;
- db->free_lock = 0;
- /* Other */
- db->nodes = ers_new((uint32)sizeof(struct dbn));
- db->cmp = db_default_cmp(type);
- db->hash = db_default_hash(type);
- db->release = db_default_release(type, options);
- for (i = 0; i < HASH_SIZE; i++)
- db->ht[i] = NULL;
- db->type = type;
- db->options = options;
- db->item_count = 0;
- db->maxlen = maxlen;
- db->global_lock = 0;
-
- return &db->dbi;
-}
-
-#ifdef DB_MANUAL_CAST_TO_UNION
-/**
- * Manual cast from 'int' to the union DBKey.
- * Created for compilers that don't support casting to unions.
- * @param key Key to be casted
- * @return The key as a DBKey union
- * @public
- * @see common\db.h#DB_MANUAL_CAST_TO_UNION
- * @see #db_ui2key(unsigned int)
- * @see #db_str2key(unsigned char *)
- * @see common\db.h#db_i2key(int)
- */
-DBKey db_i2key(int key)
-{
- DBKey ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_i2key != (unsigned int)~0) stats.db_i2key++;
-#endif /* DB_ENABLE_STATS */
- ret.i = key;
- return ret;
-}
-
-/**
- * Manual cast from 'unsigned int' to the union DBKey.
- * Created for compilers that don't support casting to unions.
- * @param key Key to be casted
- * @return The key as a DBKey union
- * @public
- * @see common\db.h#DB_MANUAL_CAST_TO_UNION
- * @see #db_i2key(int)
- * @see #db_str2key(unsigned char *)
- * @see common\db.h#db_ui2key(unsigned int)
- */
-DBKey db_ui2key(unsigned int key)
-{
- DBKey ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_ui2key != (unsigned int)~0) stats.db_ui2key++;
-#endif /* DB_ENABLE_STATS */
- ret.ui = key;
- return ret;
-}
-
-/**
- * Manual cast from 'unsigned char *' to the union DBKey.
- * Created for compilers that don't support casting to unions.
- * @param key Key to be casted
- * @return The key as a DBKey union
- * @public
- * @see common\db.h#DB_MANUAL_CAST_TO_UNION
- * @see #db_i2key(int)
- * @see #db_ui2key(unsigned int)
- * @see common\db.h#db_str2key(unsigned char *)
- */
-DBKey db_str2key(unsigned char *key)
-{
- DBKey ret;
-
-#ifdef DB_ENABLE_STATS
- if (stats.db_str2key != (unsigned int)~0) stats.db_str2key++;
-#endif /* DB_ENABLE_STATS */
- ret.str = key;
- return ret;
-}
-#endif /* DB_MANUAL_CAST_TO_UNION */
-
-/**
- * Initialize the database system.
- * @public
- * @see #db_final(void)
- * @see common\db.h#db_init(void)
- */
-void db_init(void)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_init != (unsigned int)~0) stats.db_init++;
-#endif /* DB_ENABLE_STATS */
-}
-
-/**
- * Finalize the database system.
- * Frees the memory used by the block reusage system.
- * @public
- * @see common\db.h#DB_FINAL_NODE_CHECK
- * @see #db_init(void)
- * @see common\db.h#db_final(void)
- */
-void db_final(void)
-{
-#ifdef DB_ENABLE_STATS
- if (stats.db_final != (unsigned int)~0)
- stats.db_final++;
- ShowInfo(CL_WHITE"Database nodes"CL_RESET":\n"
- "allocated %u, freed %u\n",
- stats.db_node_alloc, stats.db_node_free);
- ShowInfo(CL_WHITE"Database types"CL_RESET":\n"
- "DB_INT : allocated %10u, destroyed %10u\n"
- "DB_UINT : allocated %10u, destroyed %10u\n"
- "DB_STRING : allocated %10u, destroyed %10u\n"
- "DB_ISTRING : allocated %10u, destroyed %10u\n",
- stats.db_int_alloc, stats.db_int_destroy,
- stats.db_uint_alloc, stats.db_uint_destroy,
- stats.db_string_alloc, stats.db_string_destroy,
- stats.db_istring_alloc, stats.db_istring_destroy);
- ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n"
- "db_rotate_left %10u, db_rotate_right %10u,\n"
- "db_rebalance %10u, db_rebalance_erase %10u,\n"
- "db_is_key_null %10u,\n"
- "db_dup_key %10u, db_dup_key_free %10u,\n"
- "db_free_add %10u, db_free_remove %10u,\n"
- "db_free_lock %10u, db_free_unlock %10u,\n"
- "db_int_cmp %10u, db_uint_cmp %10u,\n"
- "db_string_cmp %10u, db_istring_cmp %10u,\n"
- "db_int_hash %10u, db_uint_hash %10u,\n"
- "db_string_hash %10u, db_istring_hash %10u,\n"
- "db_release_nothing %10u, db_release_key %10u,\n"
- "db_release_data %10u, db_release_both %10u,\n"
- "db_get %10u,\n"
- "db_getall %10u, db_vgetall %10u,\n"
- "db_ensure %10u, db_vensure %10u,\n"
- "db_put %10u, db_remove %10u,\n"
- "db_foreach %10u, db_vforeach %10u,\n"
- "db_clear %10u, db_vclear %10u,\n"
- "db_destroy %10u, db_vdestroy %10u,\n"
- "db_size %10u, db_type %10u,\n"
- "db_options %10u, db_fix_options %10u,\n"
- "db_default_cmp %10u, db_default_hash %10u,\n"
- "db_default_release %10u, db_custom_release %10u,\n"
- "db_alloc %10u, db_i2key %10u,\n"
- "db_ui2key %10u, db_str2key %10u,\n"
- "db_init %10u, db_final %10u\n",
- stats.db_rotate_left, stats.db_rotate_right,
- stats.db_rebalance, stats.db_rebalance_erase,
- stats.db_is_key_null,
- stats.db_dup_key, stats.db_dup_key_free,
- stats.db_free_add, stats.db_free_remove,
- stats.db_free_lock, stats.db_free_unlock,
- stats.db_int_cmp, stats.db_uint_cmp,
- stats.db_string_cmp, stats.db_istring_cmp,
- stats.db_int_hash, stats.db_uint_hash,
- stats.db_string_hash, stats.db_istring_hash,
- stats.db_release_nothing, stats.db_release_key,
- stats.db_release_data, stats.db_release_both,
- stats.db_get,
- stats.db_getall, stats.db_vgetall,
- stats.db_ensure, stats.db_vensure,
- stats.db_put, stats.db_remove,
- stats.db_foreach, stats.db_vforeach,
- stats.db_clear, stats.db_vclear,
- stats.db_destroy, stats.db_vdestroy,
- stats.db_size, stats.db_type,
- stats.db_options, stats.db_fix_options,
- stats.db_default_cmp, stats.db_default_hash,
- stats.db_default_release, stats.db_custom_release,
- stats.db_alloc, stats.db_i2key,
- stats.db_ui2key, stats.db_str2key,
- stats.db_init, stats.db_final);
-#endif /* DB_ENABLE_STATS */
-}
-
-// Link DB System - jAthena
-void linkdb_insert( struct linkdb_node** head, void *key, void* data) {
- struct linkdb_node *node;
- if( head == NULL ) return ;
- node = aMalloc( sizeof(struct linkdb_node) );
- if( *head == NULL ) {
- // first node
- *head = node;
- node->prev = NULL;
- node->next = NULL;
- } else {
- // link nodes
- node->next = *head;
- node->prev = (*head)->prev;
- (*head)->prev = node;
- (*head) = node;
- }
- node->key = key;
- node->data = data;
-}
-
-void* linkdb_search( struct linkdb_node** head, void *key) {
- int n = 0;
- struct linkdb_node *node;
- if( head == NULL ) return NULL;
- node = *head;
- while( node ) {
- if( node->key == key ) {
- if( node->prev && n > 5 ) {
- // 処理効率改善の為にheadに移動させる
- if(node->prev) node->prev->next = node->next;
- if(node->next) node->next->prev = node->prev;
- node->next = *head;
- node->prev = (*head)->prev;
- (*head)->prev = node;
- (*head) = node;
- }
- return node->data;
- }
- node = node->next;
- n++;
- }
- return NULL;
-}
-
-void* linkdb_erase( struct linkdb_node** head, void *key) {
- struct linkdb_node *node;
- if( head == NULL ) return NULL;
- node = *head;
- while( node ) {
- if( node->key == key ) {
- void *data = node->data;
- if( node->prev == NULL )
- *head = node->next;
- else
- node->prev->next = node->next;
- if( node->next )
- node->next->prev = node->prev;
- aFree( node );
- return data;
- }
- node = node->next;
- }
- return NULL;
-}
-
-void linkdb_replace( struct linkdb_node** head, void *key, void *data ) {
- int n = 0;
- struct linkdb_node *node;
- if( head == NULL ) return ;
- node = *head;
- while( node ) {
- if( node->key == key ) {
- if( node->prev && n > 5 ) {
- // 処理効率改善の為にheadに移動させる
- if(node->prev) node->prev->next = node->next;
- if(node->next) node->next->prev = node->prev;
- node->next = *head;
- node->prev = (*head)->prev;
- (*head)->prev = node;
- (*head) = node;
- }
- node->data = data;
- return ;
- }
- node = node->next;
- n++;
- }
- // 見つからないので挿入
- linkdb_insert( head, key, data );
-}
-
-void linkdb_final( struct linkdb_node** head ) {
- struct linkdb_node *node, *node2;
- if( head == NULL ) return ;
- node = *head;
- while( node ) {
- node2 = node->next;
- aFree( node );
- node = node2;
- }
- *head = NULL;
-}
-
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * This file is separated in five sections: *
+ * (1) Private typedefs, enums, structures, defines and gblobal variables *
+ * (2) Private functions *
+ * (3) Protected functions used internally *
+ * (4) Protected functions used in the interface of the database *
+ * (5) Public functions *
+ * *
+ * The databases are structured as a hashtable of RED-BLACK trees. *
+ * *
+ * <B>Properties of the RED-BLACK trees being used:</B> *
+ * 1. The value of any node is greater than the value of its left child and *
+ * less than the value of its right child. *
+ * 2. Every node is colored either RED or BLACK. *
+ * 3. Every red node that is not a leaf has only black children. *
+ * 4. Every path from the root to a leaf contains the same number of black *
+ * nodes. *
+ * 5. The root node is black. *
+ * An <code>n</code> node in a RED-BLACK tree has the property that its *
+ * height is <code>O(lg(n))</code>. *
+ * Another important property is that after adding a node to a RED-BLACK *
+ * tree, the tree can be readjusted in <code>O(lg(n))</code> time. *
+ * Similarly, after deleting a node from a RED-BLACK tree, the tree can be *
+ * readjusted in <code>O(lg(n))</code> time. *
+ * {@link http://www.cs.mcgill.ca/~cs251/OldCourses/1997/topic18/} *
+ * *
+ * <B>How to add new database types:</B> *
+ * 1. Add the identifier of the new database type to the enum DBType *
+ * 2. If not already there, add the data type of the key to the union DBKey *
+ * 3. If the key can be considered NULL, update the function db_is_key_null *
+ * 4. If the key can be duplicated, update the functions db_dup_key and *
+ * db_dup_key_free *
+ * 5. Create a comparator and update the function db_default_cmp *
+ * 6. Create a hasher and update the function db_default_hash *
+ * 7. If the new database type requires or does not support some options, *
+ * update the function db_fix_options *
+ * *
+ * TODO: *
+ * - create test cases to test the database system thoroughly *
+ * - make data an enumeration *
+ * - finish this header describing the database system *
+ * - create custom database allocator *
+ * - make the system thread friendly *
+ * - change the structure of the database to T-Trees *
+ * - create a db that organizes itself by splaying *
+ * *
+ * HISTORY: *
+ * 2.1 (Athena build #???#) - Portability fix *
+ * - Fixed the portability of casting to union and added the functions *
+ * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and *
+ * {@link DBInterface#clear(DBInterface,DBApply,...)}. *
+ * 2.0 (Athena build 4859) - Transition version *
+ * - Almost everything recoded with a strategy similar to objects, *
+ * database structure is maintained. *
+ * 1.0 (up to Athena build 4706) *
+ * - Previous database system. *
+ * *
+ * @version 2.1 (Athena build #???#) - Portability fix *
+ * @author (Athena build 4859) Flavio @ Amazon Project *
+ * @author (up to Athena build 4706) Athena Dev Teams *
+ * @encoding US-ASCII *
+ * @see common#db.h *
+\*****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "db.h"
+#include "../common/mmo.h"
+#include "../common/utils.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/ers.h"
+
+/*****************************************************************************\
+ * (1) Private typedefs, enums, structures, defines and global variables of *
+ * the database system. *
+ * DB_ENABLE_STATS - Define to enable database statistics. *
+ * HASH_SIZE - Define with the size of the hashtable. *
+ * DBNColor - Enumeration of colors of the nodes. *
+ * DBNode - Structure of a node in RED-BLACK trees. *
+ * struct db_free - Structure that holds a deleted node to be freed. *
+ * Database - Struture of the database. *
+ * stats - Statistics about the database system. *
+\*****************************************************************************/
+
+/**
+ * If defined statistics about database nodes, database creating/destruction
+ * and function usage are keept and displayed when finalizing the database
+ * system.
+ * WARNING: This adds overhead to every database operation (not shure how much).
+ * @private
+ * @see #DBStats
+ * @see #stats
+ * @see #db_final(void)
+ */
+//#define DB_ENABLE_STATS
+
+/**
+ * Size of the hashtable in the database.
+ * @private
+ * @see Database#ht
+ */
+#define HASH_SIZE (256+27)
+
+/**
+ * A node in a RED-BLACK tree of the database.
+ * @param parent Parent node
+ * @param left Left child node
+ * @param right Right child node
+ * @param key Key of this database entry
+ * @param data Data of this database entry
+ * @param deleted If the node is deleted
+ * @param color Color of the node
+ * @private
+ * @see Database#ht
+ */
+typedef struct dbn {
+ // Tree structure
+ struct dbn *parent;
+ struct dbn *left;
+ struct dbn *right;
+ // Node data
+ DBKey key;
+ void *data;
+ // Other
+ enum {RED, BLACK} color;
+ unsigned deleted : 1;
+} *DBNode;
+
+/**
+ * Structure that holds a deleted node.
+ * @param node Deleted node
+ * @param root Address to the root of the tree
+ * @private
+ * @see Database#free_list
+ */
+struct db_free {
+ DBNode node;
+ DBNode *root;
+};
+
+/**
+ * Complete database structure.
+ * @param dbi Interface of the database
+ * @param alloc_file File where the database was allocated
+ * @param alloc_line Line in the file where the database was allocated
+ * @param free_list Array of deleted nodes to be freed
+ * @param free_count Number of deleted nodes in free_list
+ * @param free_max Current maximum capacity of free_list
+ * @param free_lock Lock for freeing the nodes
+ * @param nodes Manager of reusable tree nodes
+ * @param cmp Comparator of the database
+ * @param hash Hasher of the database
+ * @param release Releaser of the database
+ * @param ht Hashtable of RED-BLACK trees
+ * @param type Type of the database
+ * @param options Options of the database
+ * @param item_count Number of items in the database
+ * @param maxlen Maximum length of strings in DB_STRING and DB_ISTRING databases
+ * @param global_lock Global lock of the database
+ * @private
+ * @see common\db.h#DBInterface
+ * @see #HASH_SIZE
+ * @see #DBNode
+ * @see #struct db_free
+ * @see common\db.h#DBComparator(void *,void *)
+ * @see common\db.h#DBHasher(void *)
+ * @see common\db.h#DBReleaser(void *,void *,DBRelease)
+ * @see common\db.h#DBOptions
+ * @see common\db.h#DBType
+ * @see #db_alloc(const char *,int,DBOptions,DBType,...)
+ */
+typedef struct db {
+ // Database interface
+ struct dbt dbi;
+ // File and line of allocation
+ const char *alloc_file;
+ int alloc_line;
+ // Lock system
+ struct db_free *free_list;
+ unsigned int free_count;
+ unsigned int free_max;
+ unsigned int free_lock;
+ // Other
+ ERInterface nodes;
+ DBComparator cmp;
+ DBHasher hash;
+ DBReleaser release;
+ DBNode ht[HASH_SIZE];
+ DBType type;
+ DBOptions options;
+ unsigned int item_count;
+ unsigned short maxlen;
+ unsigned global_lock : 1;
+} *Database;
+
+#ifdef DB_ENABLE_STATS
+/**
+ * Structure with what is counted when the database estatistics are enabled.
+ * @private
+ * @see #DB_ENABLE_STATS
+ * @see #stats
+ */
+static struct {
+ // Node alloc/free
+ unsigned int db_node_alloc;
+ unsigned int db_node_free;
+ // Database creating/destruction counters
+ unsigned int db_int_alloc;
+ unsigned int db_uint_alloc;
+ unsigned int db_string_alloc;
+ unsigned int db_istring_alloc;
+ unsigned int db_int_destroy;
+ unsigned int db_uint_destroy;
+ unsigned int db_string_destroy;
+ unsigned int db_istring_destroy;
+ // Function usage counters
+ unsigned int db_rotate_left;
+ unsigned int db_rotate_right;
+ unsigned int db_rebalance;
+ unsigned int db_rebalance_erase;
+ unsigned int db_is_key_null;
+ unsigned int db_dup_key;
+ unsigned int db_dup_key_free;
+ unsigned int db_free_add;
+ unsigned int db_free_remove;
+ unsigned int db_free_lock;
+ unsigned int db_free_unlock;
+ unsigned int db_int_cmp;
+ unsigned int db_uint_cmp;
+ unsigned int db_string_cmp;
+ unsigned int db_istring_cmp;
+ unsigned int db_int_hash;
+ unsigned int db_uint_hash;
+ unsigned int db_string_hash;
+ unsigned int db_istring_hash;
+ unsigned int db_release_nothing;
+ unsigned int db_release_key;
+ unsigned int db_release_data;
+ unsigned int db_release_both;
+ unsigned int db_get;
+ unsigned int db_getall;
+ unsigned int db_vgetall;
+ unsigned int db_ensure;
+ unsigned int db_vensure;
+ unsigned int db_put;
+ unsigned int db_remove;
+ unsigned int db_foreach;
+ unsigned int db_vforeach;
+ unsigned int db_clear;
+ unsigned int db_vclear;
+ unsigned int db_destroy;
+ unsigned int db_vdestroy;
+ unsigned int db_size;
+ unsigned int db_type;
+ unsigned int db_options;
+ unsigned int db_fix_options;
+ unsigned int db_default_cmp;
+ unsigned int db_default_hash;
+ unsigned int db_default_release;
+ unsigned int db_custom_release;
+ unsigned int db_alloc;
+ unsigned int db_i2key;
+ unsigned int db_ui2key;
+ unsigned int db_str2key;
+ unsigned int db_init;
+ unsigned int db_final;
+} stats = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif /* DB_ENABLE_STATS */
+
+/*****************************************************************************\
+ * (2) Section of private functions used by the database system. *
+ * db_rotate_left - Rotate a tree node to the left. *
+ * db_rotate_right - Rotate a tree node to the right. *
+ * db_rebalance - Rebalance the tree. *
+ * db_rebalance_erase - Rebalance the tree after a BLACK node was erased. *
+ * db_is_key_null - Returns not 0 if the key is considered NULL. *
+ * db_dup_key - Duplicate a key for internal use. *
+ * db_dup_key_free - Free the duplicated key. *
+ * db_free_add - Add a node to the free_list of a database. *
+ * db_free_remove - Remove a node from the free_list of a database. *
+ * db_free_lock - Increment the free_lock of a database. *
+ * db_free_unlock - Decrement the free_lock of a database. *
+ * If it was the last lock, frees the nodes in free_list. *
+ * NOTE: Keeps the database trees balanced. *
+\*****************************************************************************/
+
+/**
+ * Rotate a node to the left.
+ * @param node Node to be rotated
+ * @param root Pointer to the root of the tree
+ * @private
+ * @see #db_rebalance(DBNode,DBNode *)
+ * @see #db_rebalance_erase(DBNode,DBNode *)
+ */
+static void db_rotate_left(DBNode node, DBNode *root)
+{
+ DBNode y = node->right;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rotate_left != (unsigned int)~0) stats.db_rotate_left++;
+#endif /* DB_ENABLE_STATS */
+ // put the left of y at the right of node
+ node->right = y->left;
+ if (y->left)
+ y->left->parent = node;
+ y->parent = node->parent;
+ // link y and node's parent
+ if (node == *root) {
+ *root = y; // node was root
+ } else if (node == node->parent->left) {
+ node->parent->left = y; // node was at the left
+ } else {
+ node->parent->right = y; // node was at the right
+ }
+ // put node at the left of y
+ y->left = node;
+ node->parent = y;
+}
+
+/**
+ * Rotate a node to the right
+ * @param node Node to be rotated
+ * @param root Pointer to the root of the tree
+ * @private
+ * @see #db_rebalance(DBNode,DBNode *)
+ * @see #db_rebalance_erase(DBNode,DBNode *)
+ */
+static void db_rotate_right(DBNode node, DBNode *root)
+{
+ DBNode y = node->left;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rotate_right != (unsigned int)~0) stats.db_rotate_right++;
+#endif /* DB_ENABLE_STATS */
+ // put the right of y at the left of node
+ node->left = y->right;
+ if (y->right != 0)
+ y->right->parent = node;
+ y->parent = node->parent;
+ // link y and node's parent
+ if (node == *root) {
+ *root = y; // node was root
+ } else if (node == node->parent->right) {
+ node->parent->right = y; // node was at the right
+ } else {
+ node->parent->left = y; // node was at the left
+ }
+ // put node at the right of y
+ y->right = node;
+ node->parent = y;
+}
+
+/**
+ * Rebalance the RED-BLACK tree.
+ * Called when the node and it's parent are both RED.
+ * @param node Node to be rebalanced
+ * @param root Pointer to the root of the tree
+ * @private
+ * @see #db_rotate_left(DBNode,DBNode *)
+ * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_put(DBInterface,DBKey,void *)
+ */
+static void db_rebalance(DBNode node, DBNode *root)
+{
+ DBNode y;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rebalance != (unsigned int)~0) stats.db_rebalance++;
+#endif /* DB_ENABLE_STATS */
+ // Restore the RED-BLACK properties
+ node->color = RED;
+ while (node != *root && node->parent->color == RED) {
+ if (node->parent == node->parent->parent->left) {
+ // If node's parent is a left, y is node's right 'uncle'
+ y = node->parent->parent->right;
+ if (y && y->color == RED) { // case 1
+ // change the colors and move up the tree
+ node->parent->color = BLACK;
+ y->color = BLACK;
+ node->parent->parent->color = RED;
+ node = node->parent->parent;
+ } else {
+ if (node == node->parent->right) { // case 2
+ // move up and rotate
+ node = node->parent;
+ db_rotate_left(node, root);
+ }
+ // case 3
+ node->parent->color = BLACK;
+ node->parent->parent->color = RED;
+ db_rotate_right(node->parent->parent, root);
+ }
+ } else {
+ // If node's parent is a right, y is node's left 'uncle'
+ y = node->parent->parent->left;
+ if (y && y->color == RED) { // case 1
+ // change the colors and move up the tree
+ node->parent->color = BLACK;
+ y->color = BLACK;
+ node->parent->parent->color = RED;
+ node = node->parent->parent;
+ } else {
+ if (node == node->parent->left) { // case 2
+ // move up and rotate
+ node = node->parent;
+ db_rotate_right(node, root);
+ }
+ // case 3
+ node->parent->color = BLACK;
+ node->parent->parent->color = RED;
+ db_rotate_left(node->parent->parent, root);
+ }
+ }
+ }
+ (*root)->color = BLACK; // the root can and should always be black
+}
+
+/**
+ * Erase a node from the RED-BLACK tree, keeping the tree balanced.
+ * @param node Node to be erased from the tree
+ * @param root Root of the tree
+ * @private
+ * @see #db_rotate_left(DBNode,DBNode *)
+ * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_free_unlock(Database)
+ */
+static void db_rebalance_erase(DBNode node, DBNode *root)
+{
+ DBNode y = node;
+ DBNode x = NULL;
+ DBNode x_parent = NULL;
+ DBNode w;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rebalance_erase != (unsigned int)~0) stats.db_rebalance_erase++;
+#endif /* DB_ENABLE_STATS */
+ // Select where to change the tree
+ if (y->left == NULL) { // no left
+ x = y->right;
+ } else if (y->right == NULL) { // no right
+ x = y->left;
+ } else { // both exist, go to the leftmost node of the right sub-tree
+ y = y->right;
+ while (y->left != NULL)
+ y = y->left;
+ x = y->right;
+ }
+
+ // Remove the node from the tree
+ if (y != node) { // both childs existed
+ // put the left of 'node' in the left of 'y'
+ node->left->parent = y;
+ y->left = node->left;
+
+ // 'y' is not the direct child of 'node'
+ if (y != node->right) {
+ // put 'x' in the old position of 'y'
+ x_parent = y->parent;
+ if (x) x->parent = y->parent;
+ y->parent->left = x;
+ // put the right of 'node' in 'y'
+ y->right = node->right;
+ node->right->parent = y;
+ // 'y' is a direct child of 'node'
+ } else {
+ x_parent = y;
+ }
+
+ // link 'y' and the parent of 'node'
+ if (*root == node) {
+ *root = y; // 'node' was the root
+ } else if (node->parent->left == node) {
+ node->parent->left = y; // 'node' was at the left
+ } else {
+ node->parent->right = y; // 'node' was at the right
+ }
+ y->parent = node->parent;
+ // switch colors
+ {
+ int tmp = y->color;
+ y->color = node->color;
+ node->color = tmp;
+ }
+ y = node;
+ } else { // one child did not exist
+ // put x in node's position
+ x_parent = y->parent;
+ if (x) x->parent = y->parent;
+ // link x and node's parent
+ if (*root == node) {
+ *root = x; // node was the root
+ } else if (node->parent->left == node) {
+ node->parent->left = x; // node was at the left
+ } else {
+ node->parent->right = x; // node was at the right
+ }
+ }
+
+ // Restore the RED-BLACK properties
+ if (y->color != RED) {
+ while (x != *root && (x == NULL || x->color == BLACK)) {
+ if (x == x_parent->left) {
+ w = x_parent->right;
+ if (w->color == RED) {
+ w->color = BLACK;
+ x_parent->color = RED;
+ db_rotate_left(x_parent, root);
+ w = x_parent->right;
+ }
+ if ((w->left == NULL || w->left->color == BLACK) &&
+ (w->right == NULL || w->right->color == BLACK)) {
+ w->color = RED;
+ x = x_parent;
+ x_parent = x_parent->parent;
+ } else {
+ if (w->right == NULL || w->right->color == BLACK) {
+ if (w->left) w->left->color = BLACK;
+ w->color = RED;
+ db_rotate_right(w, root);
+ w = x_parent->right;
+ }
+ w->color = x_parent->color;
+ x_parent->color = BLACK;
+ if (w->right) w->right->color = BLACK;
+ db_rotate_left(x_parent, root);
+ break;
+ }
+ } else {
+ w = x_parent->left;
+ if (w->color == RED) {
+ w->color = BLACK;
+ x_parent->color = RED;
+ db_rotate_right(x_parent, root);
+ w = x_parent->left;
+ }
+ if ((w->right == NULL || w->right->color == BLACK) &&
+ (w->left == NULL || w->left->color == BLACK)) {
+ w->color = RED;
+ x = x_parent;
+ x_parent = x_parent->parent;
+ } else {
+ if (w->left == NULL || w->left->color == BLACK) {
+ if (w->right) w->right->color = BLACK;
+ w->color = RED;
+ db_rotate_left(w, root);
+ w = x_parent->left;
+ }
+ w->color = x_parent->color;
+ x_parent->color = BLACK;
+ if (w->left) w->left->color = BLACK;
+ db_rotate_right(x_parent, root);
+ break;
+ }
+ }
+ }
+ if (x) x->color = BLACK;
+ }
+}
+
+/**
+ * Returns not 0 if the key is considerd to be NULL.
+ * @param type Type of database
+ * @param key Key being tested
+ * @return not 0 if considered NULL, 0 otherwise
+ * @private
+ * @see common\db.h#DBType
+ * @see common\db.h#DBKey
+ * @see #db_get(DBInterface,DBKey)
+ * @see #db_put(DBInterface,DBKey,void *)
+ * @see #db_remove(DBInterface,DBKey)
+ */
+static int db_is_key_null(DBType type, DBKey key)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_is_key_null != (unsigned int)~0) stats.db_is_key_null++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_STRING:
+ case DB_ISTRING:
+ return (key.str == NULL);
+
+ default: // Not a pointer
+ return 0;
+ }
+}
+
+/**
+ * Duplicate the key used in the database.
+ * @param db Database the key is being used in
+ * @param key Key to be duplicated
+ * @param Duplicated key
+ * @private
+ * @see #db_free_add(Database,DBNode,DBNode *)
+ * @see #db_free_remove(Database,DBNode)
+ * @see #db_put(DBInterface,DBKey,void *)
+ * @see #db_dup_key_free(Database,DBKey)
+ */
+static DBKey db_dup_key(Database db, DBKey key)
+{
+ unsigned char *str;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_dup_key != (unsigned int)~0) stats.db_dup_key++;
+#endif /* DB_ENABLE_STATS */
+ switch (db->type) {
+ case DB_STRING:
+ case DB_ISTRING:
+ if (db->maxlen) {
+ CREATE(str, unsigned char, db->maxlen +1);
+ memcpy(str, key.str, db->maxlen);
+ str[db->maxlen] = '\0';
+ key.str = str;
+ } else {
+ key.str = (unsigned char *)aStrdup((const char *)key.str);
+ }
+ return key;
+
+ default:
+ return key;
+ }
+}
+
+/**
+ * Free a key duplicated by db_dup_key.
+ * @param db Database the key is being used in
+ * @param key Key to be freed
+ * @private
+ * @see #db_dup_key(Database,DBKey)
+ */
+static void db_dup_key_free(Database db, DBKey key)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_dup_key_free != (unsigned int)~0) stats.db_dup_key_free++;
+#endif /* DB_ENABLE_STATS */
+ switch (db->type) {
+ case DB_STRING:
+ case DB_ISTRING:
+ aFree(key.str);
+ return;
+
+ default:
+ return;
+ }
+}
+
+/**
+ * Add a node to the free_list of the database.
+ * Marks the node as deleted.
+ * If the key isn't duplicated, the key is duplicated and released.
+ * @param db Target database
+ * @param root Root of the tree from the node
+ * @param node Target node
+ * @private
+ * @see #struct db_free
+ * @see Database#free_list
+ * @see Database#free_count
+ * @see Database#free_max
+ * @see #db_remove(DBInterface,DBKey)
+ * @see #db_free_remove(Database,DBNode)
+ */
+static void db_free_add(Database db, DBNode node, DBNode *root)
+{
+ DBKey old_key;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_add != (unsigned int)~0) stats.db_free_add++;
+#endif /* DB_ENABLE_STATS */
+ if (db->free_lock == (unsigned int)~0) {
+ ShowFatalError("db_free_add: free_lock overflow\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ exit(EXIT_FAILURE);
+ }
+ if (!(db->options&DB_OPT_DUP_KEY)) { // Make shure we have a key until the node is freed
+ old_key = node->key;
+ node->key = db_dup_key(db, node->key);
+ db->release(old_key, node->data, DB_RELEASE_KEY);
+ }
+ if (db->free_count == db->free_max) { // No more space, expand free_list
+ db->free_max = (db->free_max<<2) +3; // = db->free_max*4 +3
+ if (db->free_max <= db->free_count) {
+ if (db->free_count == (unsigned int)~0) {
+ ShowFatalError("db_free_add: free_count overflow\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ exit(EXIT_FAILURE);
+ }
+ db->free_max = (unsigned int)~0;
+ }
+ RECREATE(db->free_list, struct db_free, db->free_max);
+ }
+ node->deleted = 1;
+ db->free_list[db->free_count].node = node;
+ db->free_list[db->free_count].root = root;
+ db->free_count++;
+ db->item_count--;
+}
+
+/**
+ * Remove a node from the free_list of the database.
+ * Marks the node as not deleted.
+ * NOTE: Frees the duplicated key of the node.
+ * @param db Target database
+ * @param node Node being removed from free_list
+ * @private
+ * @see #struct db_free
+ * @see Database#free_list
+ * @see Database#free_count
+ * @see #db_put(DBInterface,DBKey,void *)
+ * @see #db_free_add(Database,DBNode *,DBNode)
+ */
+static void db_free_remove(Database db, DBNode node)
+{
+ unsigned int i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_remove != (unsigned int)~0) stats.db_free_remove++;
+#endif /* DB_ENABLE_STATS */
+ for (i = 0; i < db->free_count; i++) {
+ if (db->free_list[i].node == node) {
+ if (i < db->free_count -1) // copy the last item to where the removed one was
+ memcpy(&db->free_list[i], &db->free_list[db->free_count -1], sizeof(struct db_free));
+ db_dup_key_free(db, node->key);
+ break;
+ }
+ }
+ node->deleted = 0;
+ if (i == db->free_count) {
+ ShowWarning("db_free_remove: node was not found - database allocated at %s:%d\n", db->alloc_file, db->alloc_line);
+ } else {
+ db->free_count--;
+ }
+ db->item_count++;
+}
+
+/**
+ * Increment the free_lock of the database.
+ * @param db Target database
+ * @private
+ * @see Database#free_lock
+ * @see #db_unlock(Database)
+ */
+static void db_free_lock(Database db)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_lock != (unsigned int)~0) stats.db_free_lock++;
+#endif /* DB_ENABLE_STATS */
+ if (db->free_lock == (unsigned int)~0) {
+ ShowFatalError("db_free_lock: free_lock overflow\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ exit(EXIT_FAILURE);
+ }
+ db->free_lock++;
+}
+
+/**
+ * Decrement the free_lock of the database.
+ * If it was the last lock, frees the nodes of the database.
+ * Keeps the tree balanced.
+ * NOTE: Frees the duplicated keys of the nodes
+ * @param db Target database
+ * @private
+ * @see Database#free_lock
+ * @see #db_free_dbn(DBNode)
+ * @see #db_lock(Database)
+ */
+static void db_free_unlock(Database db)
+{
+ unsigned int i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_unlock != (unsigned int)~0) stats.db_free_unlock++;
+#endif /* DB_ENABLE_STATS */
+ if (db->free_lock == 0) {
+ ShowWarning("db_free_unlock: free_lock was already 0\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ } else {
+ db->free_lock--;
+ }
+ if (db->free_lock)
+ return; // Not last lock
+
+ for (i = 0; i < db->free_count ; i++) {
+ db_rebalance_erase(db->free_list[i].node, db->free_list[i].root);
+ db_dup_key_free(db, db->free_list[i].node->key);
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++;
+#endif /* DB_ENABLE_STATS */
+ ers_free(db->nodes, db->free_list[i].node);
+ }
+ db->free_count = 0;
+}
+
+/*****************************************************************************\
+ * (3) Section of protected functions used internally. *
+ * NOTE: the protected functions used in the database interface are in the *
+ * next section. *
+ * db_int_cmp - Default comparator for DB_INT databases. *
+ * db_uint_cmp - Default comparator for DB_UINT databases. *
+ * db_string_cmp - Default comparator for DB_STRING databases. *
+ * db_istring_cmp - Default comparator for DB_ISTRING databases. *
+ * db_int_hash - Default hasher for DB_INT databases. *
+ * db_uint_hash - Default hasher for DB_UINT databases. *
+ * db_string_hash - Default hasher for DB_STRING databases. *
+ * db_istring_hash - Default hasher for DB_ISTRING databases. *
+ * db_release_nothing - Releaser that releases nothing. *
+ * db_release_key - Releaser that only releases the key. *
+ * db_release_data - Releaser that only releases the data. *
+ * db_release_both - Releaser that releases key and data. *
+\*****************************************************************************/
+
+/**
+ * Default comparator for DB_INT databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * <code>maxlen</code> is ignored.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_INT
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_int_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_int_cmp != (unsigned int)~0) stats.db_int_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (key1.i < key2.i) return -1;
+ if (key1.i > key2.i) return 1;
+ return 0;
+}
+
+/**
+ * Default comparator for DB_UINT databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * <code>maxlen</code> is ignored.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_UINT
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_uint_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_uint_cmp != (unsigned int)~0) stats.db_uint_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (key1.ui < key2.ui) return -1;
+ if (key1.ui > key2.ui) return 1;
+ return 0;
+}
+
+/**
+ * Default comparator for DB_STRING databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_STRING
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_string_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_string_cmp != (unsigned int)~0) stats.db_string_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0) maxlen = (unsigned short)~0;
+ return strncmp((const char *)key1.str, (const char *)key2.str, maxlen);
+}
+
+/**
+ * Default comparator for DB_ISTRING databases.
+ * Compares key1 to key2 case insensitively.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_ISTRING
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_istring_cmp != (unsigned int)~0) stats.db_istring_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0) maxlen = (unsigned short)~0;
+ return strncasecmp((const char *)key1.str, (const char *)key2.str, maxlen);
+}
+
+/**
+ * Default hasher for DB_INT databases.
+ * Returns the value of the key as an unsigned int.
+ * <code>maxlen</code> is ignored.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_INT
+ * @see common\db.h#DBHasher
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_int_hash(DBKey key, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_int_hash != (unsigned int)~0) stats.db_int_hash++;
+#endif /* DB_ENABLE_STATS */
+ return (unsigned int)key.i;
+}
+
+/**
+ * Default hasher for DB_UINT databases.
+ * Just returns the value of the key.
+ * <code>maxlen</code> is ignored.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_UINT
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_uint_hash(DBKey key, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_uint_hash != (unsigned int)~0) stats.db_uint_hash++;
+#endif /* DB_ENABLE_STATS */
+ return key.ui;
+}
+
+/**
+ * Default hasher for DB_STRING databases.
+ * If maxlen if 0, the maximum number of maxlen is used instead.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_STRING
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
+{
+ unsigned char *k = key.str;
+ unsigned int hash = 0;
+ unsigned short i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_string_hash != (unsigned int)~0) stats.db_string_hash++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0)
+ maxlen = (unsigned short)~0; // Maximum
+
+ for (i = 0; *k; i++) {
+ hash = (hash*33 + *k++)^(hash>>24);
+ if (i == maxlen)
+ break;
+ }
+
+ return hash;
+}
+
+/**
+ * Default hasher for DB_ISTRING databases.
+ * If maxlen if 0, the maximum number of maxlen is used instead.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_ISTRING
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_istring_hash(DBKey key, unsigned short maxlen)
+{
+ unsigned char *k = key.str;
+ unsigned int hash = 0;
+ unsigned short i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_istring_hash != (unsigned int)~0) stats.db_istring_hash++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0)
+ maxlen = (unsigned short)~0; // Maximum
+
+ for (i = 0; *k; i++) {
+ hash = (hash*33 + LOWER(*k))^(hash>>24);
+ k++;
+ if (i == maxlen)
+ break;
+ }
+
+ return hash;
+}
+
+/**
+ * Releaser that releases nothing.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_releaser(DBType,DBOptions)
+ */
+static void db_release_nothing(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_nothing != (unsigned int)~0) stats.db_release_nothing++;
+#endif /* DB_ENABLE_STATS */
+}
+
+/**
+ * Releaser that only releases the key.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ */
+static void db_release_key(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_key != (unsigned int)~0) stats.db_release_key++;
+#endif /* DB_ENABLE_STATS */
+ if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer
+}
+
+/**
+ * Releaser that only releases the data.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ */
+static void db_release_data(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_data != (unsigned int)~0) stats.db_release_data++;
+#endif /* DB_ENABLE_STATS */
+ if (which&DB_RELEASE_DATA) aFree(data);
+}
+
+/**
+ * Releaser that releases both key and data.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ */
+static void db_release_both(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_both != (unsigned int)~0) stats.db_release_both++;
+#endif /* DB_ENABLE_STATS */
+ if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer
+ if (which&DB_RELEASE_DATA) aFree(data);
+}
+
+/*****************************************************************************\
+ * (4) Section with protected functions used in the interface of the *
+ * database. *
+ * db_obj_get - Get the data identified by the key. *
+ * db_obj_vgetall - Get the data of the matched entries. *
+ * db_obj_getall - Get the data of the matched entries. *
+ * db_obj_vensure - Get the data identified by the key, creating if it *
+ * doesn't exist yet. *
+ * db_obj_ensure - Get the data identified by the key, creating if it *
+ * doesn't exist yet. *
+ * db_obj_put - Put data identified by the key in the database. *
+ * db_obj_remove - Remove an entry from the database. *
+ * db_obj_vforeach - Apply a function to every entry in the database. *
+ * db_obj_foreach - Apply a function to every entry in the database. *
+ * db_obj_vclear - Remove all entries from the database. *
+ * db_obj_clear - Remove all entries from the database. *
+ * db_obj_vdestroy - Destroy the database, freeing all the used memory. *
+ * db_obj_destroy - Destroy the database, freeing all the used memory. *
+ * db_obj_size - Return the size of the database. *
+ * db_obj_type - Return the type of the database. *
+ * db_obj_options - Return the options of the database. *
+\*****************************************************************************/
+
+/**
+ * Get the data of the entry identifid by the key.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @return Data of the entry or NULL if not found
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#get(DBInterface,DBKey)
+ */
+static void *db_obj_get(DBInterface self, DBKey key)
+{
+ Database db = (Database)self;
+ DBNode node;
+ int c;
+ void *data = NULL;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_get != (unsigned int)~0) stats.db_get++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_get: Attempted to retrieve non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE];
+ while (node) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ data = node->data;
+ break;
+ }
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ db_free_unlock(db);
+ return data;
+}
+
+/**
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param self Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args)
+ * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ */
+static unsigned int db_obj_vgetall(DBInterface self, void **buf, unsigned int max, DBMatcher match, va_list args)
+{
+ Database db = (Database)self;
+ unsigned int i;
+ DBNode node;
+ DBNode parent;
+ unsigned int ret = 0;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vgetall != (unsigned int)~0) stats.db_vgetall++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+ if (match == NULL) return 0; // nullpo candidate
+
+ db_free_lock(db);
+ for (i = 0; i < HASH_SIZE; i++) {
+ // Match in the order: current node, left tree, right tree
+ node = db->ht[i];
+ while (node) {
+ parent = node->parent;
+ if (!(node->deleted) && match(node->key, node->data, args) == 0) {
+ if (buf && ret < max)
+ buf[ret] = node->data;
+ ret++;
+ }
+ if (node->left) {
+ node = node->left;
+ continue;
+ }
+ if (node->right) {
+ node = node->right;
+ continue;
+ }
+ while (node) {
+ parent = node->parent;
+ if (parent && parent->right && parent->left == node) {
+ node = parent->right;
+ break;
+ }
+ node = parent;
+ }
+ }
+ }
+ db_free_unlock(db);
+ return ret;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}.
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param self Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ * @see common\db.h\DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
+ */
+static unsigned int db_obj_getall(DBInterface self, void **buf, unsigned int max, DBMatcher match, ...)
+{
+ va_list args;
+ unsigned int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_getall != (unsigned int)~0) stats.db_getall++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, match);
+ ret = self->vgetall(self, buf, max, match, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param args Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBCreateData
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ */
+static void *db_obj_vensure(DBInterface self, DBKey key, DBCreateData create, va_list args)
+{
+ Database db = (Database)self;
+ DBNode node;
+ DBNode parent = NULL;
+ unsigned int hash;
+ int c = 0;
+ void *data = NULL;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vensure != (unsigned int)~0) stats.db_vensure++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (create == NULL) {
+ ShowError("db_ensure: Create function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_ensure: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ hash = db->hash(key, db->maxlen)%HASH_SIZE;
+ node = db->ht[hash];
+ while (node) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ break;
+ }
+ parent = node;
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ // Create node if necessary
+ if (node == NULL) {
+ if (db->item_count == (unsigned int)~0) {
+ ShowError("db_vensure: item_count overflow, aborting item insertion.\n"
+ "Database allocated at %s:%d",
+ db->alloc_file, db->alloc_line);
+ return NULL;
+ }
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++;
+#endif /* DB_ENABLE_STATS */
+ node = ers_alloc(db->nodes, struct dbn);
+ node->left = NULL;
+ node->right = NULL;
+ node->deleted = 0;
+ db->item_count++;
+ if (c == 0) { // hash entry is empty
+ node->color = BLACK;
+ node->parent = NULL;
+ db->ht[hash] = node;
+ } else {
+ node->color = RED;
+ if (c < 0) { // put at the left
+ parent->left = node;
+ node->parent = parent;
+ } else { // put at the right
+ parent->right = node;
+ node->parent = parent;
+ }
+ if (parent->color == RED) // two consecutive RED nodes, must rebalance
+ db_rebalance(node, &db->ht[hash]);
+ }
+ // put key and data in the node
+ if (db->options&DB_OPT_DUP_KEY) {
+ node->key = db_dup_key(db, key);
+ if (db->options&DB_OPT_RELEASE_KEY)
+ db->release(key, data, DB_RELEASE_KEY);
+ } else {
+ node->key = key;
+ }
+ node->data = create(key, args);
+ }
+ data = node->data;
+ db_free_unlock(db);
+ return data;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}.
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param ... Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBCreateData
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ * @see common\db.h\DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
+ */
+static void *db_obj_ensure(DBInterface self, DBKey key, DBCreateData create, ...)
+{
+ va_list args;
+ void *ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_ensure != (unsigned int)~0) stats.db_ensure++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, create);
+ ret = self->vensure(self, key, create, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Put the data identified by the key in the database.
+ * Returns the previous data if the entry exists or NULL.
+ * NOTE: Uses the new key, the old one is released.
+ * @param self Interface of the database
+ * @param key Key that identifies the data
+ * @param data Data to be put in the database
+ * @return The previous data if the entry exists or NULL
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBInterface
+ * @see #db_malloc_dbn(void)
+ * @see common\db.h\DBInterface#put(DBInterface,DBKey,void *)
+ */
+static void *db_obj_put(DBInterface self, DBKey key, void *data)
+{
+ Database db = (Database)self;
+ DBNode node;
+ DBNode parent = NULL;
+ int c = 0;
+ unsigned int hash;
+ void *old_data = NULL;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_put != (unsigned int)~0) stats.db_put++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (db->global_lock) {
+ ShowError("db_put: Database is being destroyed, aborting entry insertion.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_put: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(data || db->options&DB_OPT_ALLOW_NULL_DATA)) {
+ ShowError("db_put: Attempted to use non-allowed NULL data for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ if (db->item_count == (unsigned int)~0) {
+ ShowError("db_put: item_count overflow, aborting item insertion.\n"
+ "Database allocated at %s:%d",
+ db->alloc_file, db->alloc_line);
+ return NULL;
+ }
+ // search for an equal node
+ db_free_lock(db);
+ hash = db->hash(key, db->maxlen)%HASH_SIZE;
+ for (node = db->ht[hash]; node; ) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) { // equal entry, replace
+ if (node->deleted) {
+ db_free_remove(db, node);
+ } else {
+ db->release(node->key, node->data, DB_RELEASE_BOTH);
+ }
+ old_data = node->data;
+ break;
+ }
+ parent = node;
+ if (c < 0) {
+ node = node->left;
+ } else {
+ node = node->right;
+ }
+ }
+ // allocate a new node if necessary
+ if (node == NULL) {
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++;
+#endif /* DB_ENABLE_STATS */
+ node = ers_alloc(db->nodes, struct dbn);
+ node->left = NULL;
+ node->right = NULL;
+ node->deleted = 0;
+ db->item_count++;
+ if (c == 0) { // hash entry is empty
+ node->color = BLACK;
+ node->parent = NULL;
+ db->ht[hash] = node;
+ } else {
+ node->color = RED;
+ if (c < 0) { // put at the left
+ parent->left = node;
+ node->parent = parent;
+ } else { // put at the right
+ parent->right = node;
+ node->parent = parent;
+ }
+ if (parent->color == RED) // two consecutive RED nodes, must rebalance
+ db_rebalance(node, &db->ht[hash]);
+ }
+ }
+ // put key and data in the node
+ if (db->options&DB_OPT_DUP_KEY) {
+ node->key = db_dup_key(db, key);
+ if (db->options&DB_OPT_RELEASE_KEY)
+ db->release(key, data, DB_RELEASE_KEY);
+ } else {
+ node->key = key;
+ }
+ node->data = data;
+ db_free_unlock(db);
+ return old_data;
+}
+
+/**
+ * Remove an entry from the database.
+ * Returns the data of the entry.
+ * NOTE: The key (of the database) is released in {@link #db_free_add(Database,DBNode,DBNode *)}.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @return The data of the entry or NULL if not found
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBInterface
+ * @see #db_free_add(Database,DBNode,DBNode *)
+ * @see common\db.h\DBInterface#remove(DBInterface,DBKey)
+ */
+static void *db_obj_remove(DBInterface self, DBKey key)
+{
+ Database db = (Database)self;
+ void *data = NULL;
+ DBNode node;
+ unsigned int hash;
+ int c = 0;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_remove != (unsigned int)~0) stats.db_remove++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (db->global_lock) {
+ ShowError("db_remove: Database is being destroyed. Aborting entry deletion.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ hash = db->hash(key, db->maxlen)%HASH_SIZE;
+ for(node = db->ht[hash]; node; ){
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ if (!(node->deleted)) {
+ data = node->data;
+ db->release(node->key, node->data, DB_RELEASE_DATA);
+ db_free_add(db, node, &db->ht[hash]);
+ }
+ break;
+ }
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ db_free_unlock(db);
+ return data;
+}
+
+/**
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param self Interface of the database
+ * @param func Function to be applyed
+ * @param args Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)
+ */
+static int db_obj_vforeach(DBInterface self, DBApply func, va_list args)
+{
+ Database db = (Database)self;
+ unsigned int i;
+ int sum = 0;
+ DBNode node;
+ DBNode parent;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vforeach != (unsigned int)~0) stats.db_vforeach++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+ if (func == NULL) {
+ ShowError("db_foreach: Passed function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return 0; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ for (i = 0; i < HASH_SIZE; i++) {
+ // Apply func in the order: current node, left node, right node
+ node = db->ht[i];
+ while (node) {
+ parent = node->parent;
+ if (!(node->deleted))
+ sum += func(node->key, node->data, args);
+ if (node->left) {
+ node = node->left;
+ continue;
+ }
+ if (node->right) {
+ node = node->right;
+ continue;
+ }
+ while (node) {
+ parent = node->parent;
+ if (parent && parent->right && parent->left == node) {
+ node = parent->right;
+ break;
+ }
+ node = parent;
+ }
+ }
+ }
+ db_free_unlock(db);
+ return sum;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)}.
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param self Interface of the database
+ * @param func Function to be applyed
+ * @param ... Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see common\db.h\DBInterface#foreach(DBInterface,DBApply,...)
+ */
+static int db_obj_foreach(DBInterface self, DBApply func, ...)
+{
+ va_list args;
+ int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_foreach != (unsigned int)~0) stats.db_foreach++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, func);
+ ret = self->vforeach(self, func, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)
+ */
+static int db_obj_vclear(DBInterface self, DBApply func, va_list args)
+{
+ Database db = (Database)self;
+ int sum = 0;
+ unsigned int i;
+ DBNode node;
+ DBNode parent;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vclear != (unsigned int)~0) stats.db_vclear++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+
+ db_free_lock(db);
+ for (i = 0; i < HASH_SIZE; i++) {
+ // Apply the func and delete in the order: left tree, right tree, current node
+ node = db->ht[i];
+ db->ht[i] = NULL;
+ while (node) {
+ parent = node->parent;
+ if (node->left) {
+ node = node->left;
+ continue;
+ }
+ if (node->right) {
+ node = node->right;
+ continue;
+ }
+ if (node->deleted) {
+ db_dup_key_free(db, node->key);
+ } else {
+ if (func)
+ sum += func(node->key, node->data, args);
+ db->release(node->key, node->data, DB_RELEASE_BOTH);
+ node->deleted = 1;
+ }
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++;
+#endif /* DB_ENABLE_STATS */
+ ers_free(db->nodes, node);
+ if (parent) {
+ if (parent->left == node)
+ parent->left = NULL;
+ else
+ parent->right = NULL;
+ }
+ node = parent;
+ }
+ db->ht[i] = NULL;
+ }
+ db->free_count = 0;
+ db->item_count = 0;
+ db_free_unlock(db);
+ return sum;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)}.
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)
+ * @see common\db.h\DBInterface#clear(DBInterface,DBApply,...)
+ */
+static int db_obj_clear(DBInterface self, DBApply func, ...)
+{
+ va_list args;
+ int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_clear != (unsigned int)~0) stats.db_clear++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, func);
+ ret = self->vclear(self, func, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ */
+static int db_obj_vdestroy(DBInterface self, DBApply func, va_list args)
+{
+ Database db = (Database)self;
+ int sum;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vdestroy != (unsigned int)~0) stats.db_vdestroy++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+ if (db->global_lock) {
+ ShowError("db_vdestroy: Database is already locked for destruction. Aborting second database destruction.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ return 0;
+ }
+ if (db->free_lock)
+ ShowWarning("db_vdestroy: Database is still in use, %u lock(s) left. Continuing database destruction.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line, db->free_lock);
+
+#ifdef DB_ENABLE_STATS
+ switch (db->type) {
+ case DB_INT:
+ stats.db_int_destroy++;
+ break;
+ case DB_UINT:
+ stats.db_uint_destroy++;
+ break;
+ case DB_STRING:
+ stats.db_string_destroy++;
+ break;
+ case DB_ISTRING:
+ stats.db_istring_destroy++;
+ break;
+ }
+#endif /* DB_ENABLE_STATS */
+ db_free_lock(db);
+ db->global_lock = 1;
+ sum = self->vclear(self, func, args);
+ aFree(db->free_list);
+ db->free_list = NULL;
+ db->free_max = 0;
+ ers_destroy(db->nodes);
+ db_free_unlock(db);
+ aFree(db);
+ return sum;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#db_vdestroy(DBInterface,DBApply,va_list)}.
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted.
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ * @see common\db.h\DBInterface#destroy(DBInterface,DBApply,...)
+ */
+static int db_obj_destroy(DBInterface self, DBApply func, ...)
+{
+ va_list args;
+ int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_destroy != (unsigned int)~0) stats.db_destroy++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, func);
+ ret = self->vdestroy(self, func, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Return the size of the database (number of items in the database).
+ * @param self Interface of the database
+ * @return Size of the database
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see Database#item_count
+ * @see common\db.h\DBInterface#size(DBInterface)
+ */
+static unsigned int db_obj_size(DBInterface self)
+{
+ Database db = (Database)self;
+ unsigned int item_count;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_size != (unsigned int)~0) stats.db_size++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+
+ db_free_lock(db);
+ item_count = db->item_count;
+ db_free_unlock(db);
+
+ return item_count;
+}
+
+/**
+ * Return the type of database.
+ * @param self Interface of the database
+ * @return Type of the database
+ * @protected
+ * @see common\db.h#DBType
+ * @see common\db.h#DBInterface
+ * @see Database#type
+ * @see common\db.h\DBInterface#type(DBInterface)
+ */
+static DBType db_obj_type(DBInterface self)
+{
+ Database db = (Database)self;
+ DBType type;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_type != (unsigned int)~0) stats.db_type++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return -1; // nullpo candidate - TODO what should this return?
+
+ db_free_lock(db);
+ type = db->type;
+ db_free_unlock(db);
+
+ return type;
+}
+
+/**
+ * Return the options of the database.
+ * @param self Interface of the database
+ * @return Options of the database
+ * @protected
+ * @see common\db.h#DBOptions
+ * @see common\db.h#DBInterface
+ * @see Database#options
+ * @see common\db.h\DBInterface#options(DBInterface)
+ */
+static DBOptions db_obj_options(DBInterface self)
+{
+ Database db = (Database)self;
+ DBOptions options;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_options != (unsigned int)~0) stats.db_options++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return DB_OPT_BASE; // nullpo candidate - TODO what should this return?
+
+ db_free_lock(db);
+ options = db->options;
+ db_free_unlock(db);
+
+ return options;
+}
+
+/*****************************************************************************\
+ * (5) Section with public functions. *
+ * db_fix_options - Apply database type restrictions to the options. *
+ * db_default_cmp - Get the default comparator for a type of database. *
+ * db_default_hash - Get the default hasher for a type of database. *
+ * db_default_release - Get the default releaser for a type of database *
+ * with the specified options. *
+ * db_custom_release - Get a releaser that behaves a certains way. *
+ * db_alloc - Allocate a new database. *
+ * db_i2key - Manual cast from 'int' to 'DBKey'. *
+ * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
+ * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
+ * db_init - Initialize the database system. *
+ * db_final - Finalize the database system. *
+\*****************************************************************************/
+
+/**
+ * Returns the fixed options according to the database type.
+ * Sets required options and unsets unsupported options.
+ * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset.
+ * @param type Type of the database
+ * @param options Original options of the database
+ * @return Fixed options of the database
+ * @private
+ * @see common\db.h#DBType
+ * @see common\db.h#DBOptions
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ * @see common\db.h#db_fix_options(DBType,DBOptions)
+ */
+DBOptions db_fix_options(DBType type, DBOptions options)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_fix_options != (unsigned int)~0) stats.db_fix_options++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_INT:
+ case DB_UINT: // Numeric database, do nothing with the keys
+ return options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY);
+
+ default:
+ ShowError("db_fix_options: Unknown database type %u with options %x\n", type, options);
+ case DB_STRING:
+ case DB_ISTRING: // String databases, no fix required
+ return options;
+ }
+}
+
+/**
+ * Returns the default comparator for the specified type of database.
+ * @param type Type of database
+ * @return Comparator for the type of database or NULL if unknown database
+ * @public
+ * @see common\db.h#DBType
+ * @see #db_int_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_uint_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_string_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_istring_cmp(DBKey,DBKey,unsigned short)
+ * @see common\db.h#db_default_cmp(DBType)
+ */
+DBComparator db_default_cmp(DBType type)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_default_cmp != (unsigned int)~0) stats.db_default_cmp++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_INT: return db_int_cmp;
+ case DB_UINT: return db_uint_cmp;
+ case DB_STRING: return db_string_cmp;
+ case DB_ISTRING: return db_istring_cmp;
+ default:
+ ShowError("db_default_cmp: Unknown database type %u\n", type);
+ return NULL;
+ }
+}
+
+/**
+ * Returns the default hasher for the specified type of database.
+ * @param type Type of database
+ * @return Hasher of the type of database or NULL if unknown database
+ * @public
+ * @see common\db.h#DBType
+ * @see #db_int_hash(DBKey,unsigned short)
+ * @see #db_uint_hash(DBKey,unsigned short)
+ * @see #db_string_hash(DBKey,unsigned short)
+ * @see #db_istring_hash(DBKey,unsigned short)
+ * @see common\db.h#db_default_hash(DBType)
+ */
+DBHasher db_default_hash(DBType type)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_default_hash != (unsigned int)~0) stats.db_default_hash++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_INT: return db_int_hash;
+ case DB_UINT: return db_uint_hash;
+ case DB_STRING: return db_string_hash;
+ case DB_ISTRING: return db_istring_hash;
+ default:
+ ShowError("db_default_hash: Unknown database type %u\n", type);
+ return NULL;
+ }
+}
+
+/**
+ * Returns the default releaser for the specified type of database with the
+ * specified options.
+ * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)}
+ * before choosing the releaser.
+ * @param type Type of database
+ * @param options Options of the database
+ * @return Default releaser for the type of database with the specified options
+ * @public
+ * @see common\db.h#DBType
+ * @see common\db.h#DBOptions
+ * @see common\db.h#DBReleaser
+ * @see #db_release_nothing(DBKey,void *,DBRelease)
+ * @see #db_release_key(DBKey,void *,DBRelease)
+ * @see #db_release_data(DBKey,void *,DBRelease)
+ * @see #db_release_both(DBKey,void *,DBRelease)
+ * @see #db_custom_release(DBRelease)
+ * @see common\db.h#db_default_release(DBType,DBOptions)
+ */
+DBReleaser db_default_release(DBType type, DBOptions options)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_default_release != (unsigned int)~0) stats.db_default_release++;
+#endif /* DB_ENABLE_STATS */
+ options = db_fix_options(type, options);
+ if (options&DB_OPT_RELEASE_DATA) { // Release data, what about the key?
+ if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY))
+ return db_release_both; // Release both key and data
+ return db_release_data; // Only release data
+ }
+ if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY))
+ return db_release_key; // Only release key
+ return db_release_nothing; // Release nothing
+}
+
+/**
+ * Returns the releaser that releases the specified release options.
+ * @param which Options that specified what the releaser releases
+ * @return Releaser for the specified release options
+ * @public
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_release_nothing(DBKey,void *,DBRelease)
+ * @see #db_release_key(DBKey,void *,DBRelease)
+ * @see #db_release_data(DBKey,void *,DBRelease)
+ * @see #db_release_both(DBKey,void *,DBRelease)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see common\db.h#db_custom_release(DBRelease)
+ */
+DBReleaser db_custom_release(DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_custom_release != (unsigned int)~0) stats.db_custom_release++;
+#endif /* DB_ENABLE_STATS */
+ switch (which) {
+ case DB_RELEASE_NOTHING: return db_release_nothing;
+ case DB_RELEASE_KEY: return db_release_key;
+ case DB_RELEASE_DATA: return db_release_data;
+ case DB_RELEASE_BOTH: return db_release_both;
+ default:
+ ShowError("db_custom_release: Unknown release options %u\n", which);
+ return NULL;
+ }
+}
+
+/**
+ * Allocate a new database of the specified type.
+ * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
+ * before creating the database.
+ * @param file File where the database is being allocated
+ * @param line Line of the file where the database is being allocated
+ * @param type Type of database
+ * @param options Options of the database
+ * @param maxlen Maximum length of the string to be used as key in string
+ * databases
+ * @return The interface of the database
+ * @public
+ * @see common\db.h#DBType
+ * @see common\db.h#DBInterface
+ * @see #Database
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see common\db.h#db_alloc(const char *,int,DBType,unsigned short)
+ */
+DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen)
+{
+ Database db;
+ unsigned int i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_alloc != (unsigned int)~0) stats.db_alloc++;
+ switch (type) {
+ case DB_INT:
+ stats.db_int_alloc++;
+ break;
+ case DB_UINT:
+ stats.db_uint_alloc++;
+ break;
+ case DB_STRING:
+ stats.db_string_alloc++;
+ break;
+ case DB_ISTRING:
+ stats.db_istring_alloc++;
+ break;
+ }
+#endif /* DB_ENABLE_STATS */
+ CREATE(db, struct db, 1);
+
+ options = db_fix_options(type, options);
+ /* Interface of the database */
+ db->dbi.get = db_obj_get;
+ db->dbi.getall = db_obj_getall;
+ db->dbi.vgetall = db_obj_vgetall;
+ db->dbi.ensure = db_obj_ensure;
+ db->dbi.vensure = db_obj_vensure;
+ db->dbi.put = db_obj_put;
+ db->dbi.remove = db_obj_remove;
+ db->dbi.foreach = db_obj_foreach;
+ db->dbi.vforeach = db_obj_vforeach;
+ db->dbi.clear = db_obj_clear;
+ db->dbi.vclear = db_obj_vclear;
+ db->dbi.destroy = db_obj_destroy;
+ db->dbi.vdestroy = db_obj_vdestroy;
+ db->dbi.size = db_obj_size;
+ db->dbi.type = db_obj_type;
+ db->dbi.options = db_obj_options;
+ /* File and line of allocation */
+ db->alloc_file = file;
+ db->alloc_line = line;
+ /* Lock system */
+ db->free_list = NULL;
+ db->free_count = 0;
+ db->free_max = 0;
+ db->free_lock = 0;
+ /* Other */
+ db->nodes = ers_new((uint32)sizeof(struct dbn));
+ db->cmp = db_default_cmp(type);
+ db->hash = db_default_hash(type);
+ db->release = db_default_release(type, options);
+ for (i = 0; i < HASH_SIZE; i++)
+ db->ht[i] = NULL;
+ db->type = type;
+ db->options = options;
+ db->item_count = 0;
+ db->maxlen = maxlen;
+ db->global_lock = 0;
+
+ return &db->dbi;
+}
+
+#ifdef DB_MANUAL_CAST_TO_UNION
+/**
+ * Manual cast from 'int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see common\db.h#DB_MANUAL_CAST_TO_UNION
+ * @see #db_ui2key(unsigned int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.h#db_i2key(int)
+ */
+DBKey db_i2key(int key)
+{
+ DBKey ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_i2key != (unsigned int)~0) stats.db_i2key++;
+#endif /* DB_ENABLE_STATS */
+ ret.i = key;
+ return ret;
+}
+
+/**
+ * Manual cast from 'unsigned int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see common\db.h#DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.h#db_ui2key(unsigned int)
+ */
+DBKey db_ui2key(unsigned int key)
+{
+ DBKey ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_ui2key != (unsigned int)~0) stats.db_ui2key++;
+#endif /* DB_ENABLE_STATS */
+ ret.ui = key;
+ return ret;
+}
+
+/**
+ * Manual cast from 'unsigned char *' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see common\db.h#DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_ui2key(unsigned int)
+ * @see common\db.h#db_str2key(unsigned char *)
+ */
+DBKey db_str2key(unsigned char *key)
+{
+ DBKey ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_str2key != (unsigned int)~0) stats.db_str2key++;
+#endif /* DB_ENABLE_STATS */
+ ret.str = key;
+ return ret;
+}
+#endif /* DB_MANUAL_CAST_TO_UNION */
+
+/**
+ * Initialize the database system.
+ * @public
+ * @see #db_final(void)
+ * @see common\db.h#db_init(void)
+ */
+void db_init(void)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_init != (unsigned int)~0) stats.db_init++;
+#endif /* DB_ENABLE_STATS */
+}
+
+/**
+ * Finalize the database system.
+ * Frees the memory used by the block reusage system.
+ * @public
+ * @see common\db.h#DB_FINAL_NODE_CHECK
+ * @see #db_init(void)
+ * @see common\db.h#db_final(void)
+ */
+void db_final(void)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_final != (unsigned int)~0)
+ stats.db_final++;
+ ShowInfo(CL_WHITE"Database nodes"CL_RESET":\n"
+ "allocated %u, freed %u\n",
+ stats.db_node_alloc, stats.db_node_free);
+ ShowInfo(CL_WHITE"Database types"CL_RESET":\n"
+ "DB_INT : allocated %10u, destroyed %10u\n"
+ "DB_UINT : allocated %10u, destroyed %10u\n"
+ "DB_STRING : allocated %10u, destroyed %10u\n"
+ "DB_ISTRING : allocated %10u, destroyed %10u\n",
+ stats.db_int_alloc, stats.db_int_destroy,
+ stats.db_uint_alloc, stats.db_uint_destroy,
+ stats.db_string_alloc, stats.db_string_destroy,
+ stats.db_istring_alloc, stats.db_istring_destroy);
+ ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n"
+ "db_rotate_left %10u, db_rotate_right %10u,\n"
+ "db_rebalance %10u, db_rebalance_erase %10u,\n"
+ "db_is_key_null %10u,\n"
+ "db_dup_key %10u, db_dup_key_free %10u,\n"
+ "db_free_add %10u, db_free_remove %10u,\n"
+ "db_free_lock %10u, db_free_unlock %10u,\n"
+ "db_int_cmp %10u, db_uint_cmp %10u,\n"
+ "db_string_cmp %10u, db_istring_cmp %10u,\n"
+ "db_int_hash %10u, db_uint_hash %10u,\n"
+ "db_string_hash %10u, db_istring_hash %10u,\n"
+ "db_release_nothing %10u, db_release_key %10u,\n"
+ "db_release_data %10u, db_release_both %10u,\n"
+ "db_get %10u,\n"
+ "db_getall %10u, db_vgetall %10u,\n"
+ "db_ensure %10u, db_vensure %10u,\n"
+ "db_put %10u, db_remove %10u,\n"
+ "db_foreach %10u, db_vforeach %10u,\n"
+ "db_clear %10u, db_vclear %10u,\n"
+ "db_destroy %10u, db_vdestroy %10u,\n"
+ "db_size %10u, db_type %10u,\n"
+ "db_options %10u, db_fix_options %10u,\n"
+ "db_default_cmp %10u, db_default_hash %10u,\n"
+ "db_default_release %10u, db_custom_release %10u,\n"
+ "db_alloc %10u, db_i2key %10u,\n"
+ "db_ui2key %10u, db_str2key %10u,\n"
+ "db_init %10u, db_final %10u\n",
+ stats.db_rotate_left, stats.db_rotate_right,
+ stats.db_rebalance, stats.db_rebalance_erase,
+ stats.db_is_key_null,
+ stats.db_dup_key, stats.db_dup_key_free,
+ stats.db_free_add, stats.db_free_remove,
+ stats.db_free_lock, stats.db_free_unlock,
+ stats.db_int_cmp, stats.db_uint_cmp,
+ stats.db_string_cmp, stats.db_istring_cmp,
+ stats.db_int_hash, stats.db_uint_hash,
+ stats.db_string_hash, stats.db_istring_hash,
+ stats.db_release_nothing, stats.db_release_key,
+ stats.db_release_data, stats.db_release_both,
+ stats.db_get,
+ stats.db_getall, stats.db_vgetall,
+ stats.db_ensure, stats.db_vensure,
+ stats.db_put, stats.db_remove,
+ stats.db_foreach, stats.db_vforeach,
+ stats.db_clear, stats.db_vclear,
+ stats.db_destroy, stats.db_vdestroy,
+ stats.db_size, stats.db_type,
+ stats.db_options, stats.db_fix_options,
+ stats.db_default_cmp, stats.db_default_hash,
+ stats.db_default_release, stats.db_custom_release,
+ stats.db_alloc, stats.db_i2key,
+ stats.db_ui2key, stats.db_str2key,
+ stats.db_init, stats.db_final);
+#endif /* DB_ENABLE_STATS */
+}
+
+// Link DB System - jAthena
+void linkdb_insert( struct linkdb_node** head, void *key, void* data) {
+ struct linkdb_node *node;
+ if( head == NULL ) return ;
+ node = aMalloc( sizeof(struct linkdb_node) );
+ if( *head == NULL ) {
+ // first node
+ *head = node;
+ node->prev = NULL;
+ node->next = NULL;
+ } else {
+ // link nodes
+ node->next = *head;
+ node->prev = (*head)->prev;
+ (*head)->prev = node;
+ (*head) = node;
+ }
+ node->key = key;
+ node->data = data;
+}
+
+void* linkdb_search( struct linkdb_node** head, void *key) {
+ int n = 0;
+ struct linkdb_node *node;
+ if( head == NULL ) return NULL;
+ node = *head;
+ while( node ) {
+ if( node->key == key ) {
+ if( node->prev && n > 5 ) {
+ // 処理効率改善の為にheadに移動させる
+ if(node->prev) node->prev->next = node->next;
+ if(node->next) node->next->prev = node->prev;
+ node->next = *head;
+ node->prev = (*head)->prev;
+ (*head)->prev = node;
+ (*head) = node;
+ }
+ return node->data;
+ }
+ node = node->next;
+ n++;
+ }
+ return NULL;
+}
+
+void* linkdb_erase( struct linkdb_node** head, void *key) {
+ struct linkdb_node *node;
+ if( head == NULL ) return NULL;
+ node = *head;
+ while( node ) {
+ if( node->key == key ) {
+ void *data = node->data;
+ if( node->prev == NULL )
+ *head = node->next;
+ else
+ node->prev->next = node->next;
+ if( node->next )
+ node->next->prev = node->prev;
+ aFree( node );
+ return data;
+ }
+ node = node->next;
+ }
+ return NULL;
+}
+
+void linkdb_replace( struct linkdb_node** head, void *key, void *data ) {
+ int n = 0;
+ struct linkdb_node *node;
+ if( head == NULL ) return ;
+ node = *head;
+ while( node ) {
+ if( node->key == key ) {
+ if( node->prev && n > 5 ) {
+ // 処理効率改善の為にheadに移動させる
+ if(node->prev) node->prev->next = node->next;
+ if(node->next) node->next->prev = node->prev;
+ node->next = *head;
+ node->prev = (*head)->prev;
+ (*head)->prev = node;
+ (*head) = node;
+ }
+ node->data = data;
+ return ;
+ }
+ node = node->next;
+ n++;
+ }
+ // 見つからないので挿入
+ linkdb_insert( head, key, data );
+}
+
+void linkdb_final( struct linkdb_node** head ) {
+ struct linkdb_node *node, *node2;
+ if( head == NULL ) return ;
+ node = *head;
+ while( node ) {
+ node2 = node->next;
+ aFree( node );
+ node = node2;
+ }
+ *head = NULL;
+}
+
diff --git a/src/common/db.h b/src/common/db.h
index 0b6fc08c5..b17ae27f9 100644
--- a/src/common/db.h
+++ b/src/common/db.h
@@ -1,748 +1,748 @@
-/*****************************************************************************\
- * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
- * For more information, see LICENCE in the main folder *
- * *
- * This file is separated in two sections: *
- * (1) public typedefs, enums, unions, structures and defines *
- * (2) public functions *
- * *
- * <B>Notes on the release system:</B> *
- * Whenever an entry is removed from the database both the key and the *
- * data are requested to be released. *
- * At least one entry is removed when replacing an entry, removing an *
- * entry, clearing the database or destroying the database. *
- * What is actually released is defined by the release function, the *
- * functions of the database only ask for the key and/or data to be *
- * released. *
- * *
- * TODO: *
- * - create an enum for the data (with int, unsigned int and void *) *
- * - create a custom database allocator *
- * - see what functions need or should be added to the database interface *
- * *
- * HISTORY: *
- * 2.1 (Athena build #???#) - Portability fix *
- * - Fixed the portability of casting to union and added the functions *
- * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and *
- * {@link DBInterface#clear(DBInterface,DBApply,...)}. *
- * 2.0 (Athena build 4859) - Transition version *
- * - Almost everything recoded with a strategy similar to objects, *
- * database structure is maintained. *
- * 1.0 (up to Athena build 4706) *
- * - Previous database system. *
- * *
- * @version 2.1 (Athena build #???#) - Portability fix *
- * @author (Athena build 4859) Flavio @ Amazon Project *
- * @author (up to Athena build 4706) Athena Dev Teams *
- * @encoding US-ASCII *
- * @see common#db.c *
-\*****************************************************************************/
-#ifndef _DB_H_
-#define _DB_H_
-
-#include <stdarg.h>
-
-/*****************************************************************************\
- * (1) Section with public typedefs, enums, unions, structures and defines. *
- * DB_MANUAL_CAST_TO_UNION - Define when the compiler doesn't allow casting *
- * to unions. *
- * DBRelease - Enumeration of release options. *
- * DBType - Enumeration of database types. *
- * DBOptions - Bitfield enumeration of database options. *
- * DBKey - Union of used key types. *
- * DBApply - Format of functions applyed to the databases. *
- * DBMatcher - Format of matchers used in DBInterface->getall. *
- * DBComparator - Format of the comparators used by the databases. *
- * DBHasher - Format of the hashers used by the databases. *
- * DBReleaser - Format of the releasers used by the databases. *
- * DBInterface - Structure of the interface of the database. *
-\*****************************************************************************/
-
-/**
- * Define this to enable the functions that cast to unions.
- * Required when the compiler doesn't support casting to unions.
- * NOTE: It is recommened that the conditional tests to determine if this
- * should be defined be located in a makefile or a header file specific for
- * of compatibility and portability issues.
- * @public
- * @see #db_i2key(int)
- * @see #db_ui2key(unsigned int)
- * @see #db_str2key(unsigned char *)
- */
-//#define DB_MANUAL_CAST_TO_UNION
-
-/**
- * Bitfield with what should be released by the releaser function (if the
- * function supports it).
- * @public
- * @see #DBReleaser
- * @see #db_custom_release(DBRelease)
- */
-typedef enum {
- DB_RELEASE_NOTHING = 0,
- DB_RELEASE_KEY = 1,
- DB_RELEASE_DATA = 2,
- DB_RELEASE_BOTH = 3
-} DBRelease;
-
-/**
- * Supported types of database.
- * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
- * types of databases.
- * @param DB_INT Uses int's for keys
- * @param DB_UINT Uses unsigned int's for keys
- * @param DB_STRING Uses strings for keys.
- * @param DB_ISTRING Uses case insensitive strings for keys.
- * @public
- * @see #DBOptions
- * @see #DBKey
- * @see #db_fix_options(DBType,DBOptions)
- * @see #db_default_cmp(DBType)
- * @see #db_default_hash(DBType)
- * @see #db_default_release(DBType,DBOptions)
- * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
- */
-typedef enum {
- DB_INT,
- DB_UINT,
- DB_STRING,
- DB_ISTRING
-} DBType;
-
-/**
- * Bitfield of options that define the behaviour of the database.
- * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
- * types of databases.
- * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing
- * and does not allow NULL keys or NULL data.
- * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY
- * is defined, the real key is freed as soon as the entry is added.
- * @param DB_OPT_RELEASE_KEY Releases the key.
- * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed
- * from the database.
- * WARNING: for funtions that return the data (like DBInterface->remove),
- * a dangling pointer will be returned.
- * @param DB_OPT_RELEASE_BOTH Releases both key and data.
- * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database.
- * @param DB_OPT_ALLOW_NULL_DATA Allow NULL data in the database.
- * @public
- * @see #db_fix_options(DBType,DBOptions)
- * @see #db_default_release(DBType,DBOptions)
- * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
- */
-typedef enum {
- DB_OPT_BASE = 0,
- DB_OPT_DUP_KEY = 1,
- DB_OPT_RELEASE_KEY = 2,
- DB_OPT_RELEASE_DATA = 4,
- DB_OPT_RELEASE_BOTH = 6,
- DB_OPT_ALLOW_NULL_KEY = 8,
- DB_OPT_ALLOW_NULL_DATA = 16,
-} DBOptions;
-
-/**
- * Union of key types used by the database.
- * @param i Type of key for DB_INT databases
- * @param ui Type of key for DB_UINT databases
- * @param str Type of key for DB_STRING and DB_ISTRING databases
- * @public
- * @see #DBType
- * @see #DBApply(DBKey,void *,va_list)
- * @see #DBMatcher(DBKey,void *,va_list)
- * @see #DBComparator(DBKey,DBKey,unsigned short)
- * @see #DBHasher(DBKey,unsigned short)
- * @see #DBReleaser(DBKey,void *,DBRelease)
- * @see DBInterface#get(DBInterface,DBKey)
- * @see DBInterface#put(DBInterface,DBKey,void *)
- * @see DBInterface#remove(DBInterface,DBKey)
- */
-typedef union {
- int i;
- unsigned int ui;
- unsigned char *str;
-} DBKey;
-
-/**
- * Format of funtions that create the data for the key when the entry doesn't
- * exist in the database yet.
- * @param key Key of the database entry
- * @param args Extra arguments of the funtion
- * @return Data identified by the key to be put in the database
- * @public
- * @see #DBKey
- * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
- * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
- */
-typedef void *(*DBCreateData)(DBKey key, va_list args);
-
-/**
- * Format of functions to be applyed to an unspecified quantity of entries of
- * a database.
- * Any function that applyes this function to the database will return the sum
- * of values returned by this function.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param args Extra arguments of the funtion
- * @return Value to be added up by the funtion that is applying this
- * @public
- * @see #DBKey
- * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
- * @see DBInterface#foreach(DBInterface,DBApply,...)
- * @see DBInterface#vdestroy(DBInterface,DBApply,va_list)
- * @see DBInterface#destroy(DBInterface,DBApply,...)
- */
-typedef int (*DBApply)(DBKey key, void *data, va_list args);
-
-/**
- * Format of functions that match database entries.
- * The purpose of the match depends on the function that is calling the matcher.
- * Returns 0 if it is a match, another number otherwise.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param args Extra arguments of the function
- * @return 0 if a match, another number otherwise
- * @public
- * @see #DBKey
- * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatcher,...)
- */
-typedef int (*DBMatcher)(DBKey key, void *data, va_list args);
-
-/**
- * Format of the comparators used internally by the database system.
- * Compares key1 to key2.
- * <code>maxlen</code> is the maximum number of character used in DB_STRING and
- * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K).
- * Returns 0 is equal, negative if lower and positive is higher.
- * @param key1 Key being compared
- * @param key2 Key we are comparing to
- * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING
- * databases.
- * @return 0 if equal, negative if lower and positive if higher
- * @public
- * @see #DBKey
- * @see #db_default_cmp(DBType)
- */
-typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen);
-
-/**
- * Format of the hashers used internally by the database system.
- * Creates the hash of the key.
- * <code>maxlen</code> is the maximum number of character used in DB_STRING and
- * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K).
- * @param key Key being hashed
- * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING
- * databases.
- * @return Hash of the key
- * @public
- * @see #DBKey
- * @see #db_default_hash(DBType)
- */
-typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen);
-
-/**
- * Format of the releaser used by the database system.
- * Releases nothing, the key, the data or both.
- * All standard releasers use aFree to release.
- * @param key Key of the database entry
- * @param data Data of the database entry
- * @param which What is being requested to be released
- * @public
- * @see #DBRelease
- * @see #DBKey
- * @see #db_default_releaser(DBType,DBOptions)
- * @see #db_custom_release(DBRelease)
- */
-typedef void (*DBReleaser)(DBKey key, void *data, DBRelease which);
-
-/**
- * Public interface of a database. Only contains funtions.
- * All the functions take the interface as the first argument.
- * @public
- * @see DBInterface#get(DBInterface,DBKey)
- * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
- * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
- * @see DBInterface#put(DBInterface,DBKey,void *)
- * @see DBInterface#remove(DBInterface,DBKey)
- * @see DBInterface#foreach(DBInterface,DBApply,...)
- * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
- * @see DBInterface#destroy(DBInterface,DBApply,...)
- * @see DBInterface#destroy(DBInterface,DBApply,va_list)
- * @see DBInterface#size(DBInterface)
- * @see DBInterface#type(DBInterface)
- * @see DBInterface#options(DBInterface)
- * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
- */
-typedef struct dbt {
-
- /**
- * Get the data of the entry identifid by the key.
- * @param dbi Interface of the database
- * @param key Key that identifies the entry
- * @return Data of the entry or NULL if not found
- * @protected
- * @see #DBKey
- * @see #DBInterface
- * @see common\db.c#db_get(DBInterface,DBKey)
- */
- void *(*get)(struct dbt *dbi, DBKey key);
-
- /**
- * Just calls {@link DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}.
- * Get the data of the entries matched by <code>match</code>.
- * It puts a maximum of <code>max</code> entries into <code>buf</code>.
- * If <code>buf</code> is NULL, it only counts the matches.
- * Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
- * first <code>max</code> entries found are put into the buffer.
- * @param dbi Interface of the database
- * @param buf Buffer to put the data of the matched entries
- * @param max Maximum number of data entries to be put into buf
- * @param match Function that matches the database entries
- * @param ... Extra arguments for match
- * @return The number of entries that matched
- * @protected
- * @see #DBMatcher(DBKey key, void *data, va_list args)
- * @see #DBInterface
- * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
- * @see common\db.c#db_getall(DBInterface,void **,unsigned int,DBMatch,...)
- */
- unsigned int (*getall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, ...);
-
- /**
- * Get the data of the entries matched by <code>match</code>.
- * It puts a maximum of <code>max</code> entries into <code>buf</code>.
- * If <code>buf</code> is NULL, it only counts the matches.
- * Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the
- * first <code>max</code> entries found are put into the buffer.
- * @param dbi Interface of the database
- * @param buf Buffer to put the data of the matched entries
- * @param max Maximum number of data entries to be put into buf
- * @param match Function that matches the database entries
- * @param ... Extra arguments for match
- * @return The number of entries that matched
- * @protected
- * @see #DBMatcher(DBKey key, void *data, va_list args)
- * @see #DBInterface
- * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
- * @see common\db.c#db_vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
- */
- unsigned int (*vgetall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, va_list args);
-
- /**
- * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}.
- * Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
- * <code>create</code>.
- * @param dbi Interface of the database
- * @param key Key that identifies the entry
- * @param create Function used to create the data if the entry doesn't exist
- * @param ... Extra arguments for create
- * @return Data of the entry
- * @protected
- * @see #DBKey
- * @see #DBCreateData
- * @see #DBInterface
- * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
- * @see common\db.c#db_ensure(DBInterface,DBKey,DBCreateData,...)
- */
- void *(*ensure)(struct dbt *dbi, DBKey key, DBCreateData create, ...);
-
- /**
- * Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by
- * <code>create</code>.
- * @param dbi Interface of the database
- * @param key Key that identifies the entry
- * @param create Function used to create the data if the entry doesn't exist
- * @param args Extra arguments for create
- * @return Data of the entry
- * @protected
- * @see #DBKey
- * @see #DBCreateData
- * @see #DBInterface
- * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
- * @see common\db.c#db_vensure(DBInterface,DBKey,DBCreateData,va_list)
- */
- void *(*vensure)(struct dbt *dbi, DBKey key, DBCreateData create, va_list args);
-
- /**
- * Put the data identified by the key in the database.
- * Returns the previous data if the entry exists or NULL.
- * NOTE: Uses the new key, the old one is released.
- * @param dbi Interface of the database
- * @param key Key that identifies the data
- * @param data Data to be put in the database
- * @return The previous data if the entry exists or NULL
- * @protected
- * @see #DBKey
- * @see #DBInterface
- * @see common\db.c#db_put(DBInterface,DBKey,void *)
- */
- void *(*put)(struct dbt *dbi, DBKey key, void *data);
-
- /**
- * Remove an entry from the database.
- * Returns the data of the entry.
- * NOTE: The key (of the database) is released.
- * @param dbi Interface of the database
- * @param key Key that identifies the entry
- * @return The data of the entry or NULL if not found
- * @protected
- * @see #DBKey
- * @see #DBInterface
- * @see common\db.c#db_remove(DBInterface,DBKey)
- */
- void *(*remove)(struct dbt *dbi, DBKey key);
-
- /**
- * Just calls {@link DBInterface#vforeach(DBInterface,DBApply,va_list)}.
- * Apply <code>func</code> to every entry in the database.
- * Returns the sum of values returned by func.
- * @param dbi Interface of the database
- * @param func Function to be applyed
- * @param ... Extra arguments for func
- * @return Sum of the values returned by func
- * @protected
- * @see #DBInterface
- * @see #DBApply(DBKey,void *,va_list)
- * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
- * @see common\db.c#db_foreach(DBInterface,DBApply,...)
- */
- int (*foreach)(struct dbt *dbi, DBApply func, ...);
-
- /**
- * Apply <code>func</code> to every entry in the database.
- * Returns the sum of values returned by func.
- * @param dbi Interface of the database
- * @param func Function to be applyed
- * @param args Extra arguments for func
- * @return Sum of the values returned by func
- * @protected
- * @see #DBApply(DBKey,void *,va_list)
- * @see #DBInterface
- * @see DBInterface#foreach(DBInterface,DBApply,...)
- * @see common\db.c#db_vforeach(DBInterface,DBApply,va_list)
- */
- int (*vforeach)(struct dbt *dbi, DBApply func, va_list args);
-
- /**
- * Just calls {@link DBInterface#vclear(DBInterface,DBApply,va_list)}.
- * Removes all entries from the database.
- * Before deleting an entry, func is applyed to it.
- * Releases the key and the data.
- * Returns the sum of values returned by func, if it exists.
- * @param dbi Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param ... Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see #DBApply(DBKey,void *,va_list)
- * @see #DBInterface
- * @see DBInterface#vclear(DBInterface,DBApply,va_list)
- * @see common\db.c#db_clear(DBInterface,DBApply,...)
- */
- int (*clear)(struct dbt *dbi, DBApply func, ...);
-
- /**
- * Removes all entries from the database.
- * Before deleting an entry, func is applyed to it.
- * Releases the key and the data.
- * Returns the sum of values returned by func, if it exists.
- * @param dbi Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param args Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see #DBApply(DBKey,void *,va_list)
- * @see #DBInterface
- * @see DBInterface#clear(DBInterface,DBApply,...)
- * @see common\db.c#vclear(DBInterface,DBApply,va_list)
- */
- int (*vclear)(struct dbt *dbi, DBApply func, va_list args);
-
- /**
- * Just calls {@link DBInterface#vdestroy(DBInterface,DBApply,va_list)}.
- * Finalize the database, feeing all the memory it uses.
- * Before deleting an entry, func is applyed to it.
- * Releases the key and the data.
- * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
- * a database entry will give an error and be aborted (except for clearing).
- * @param dbi Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param ... Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see #DBApply(DBKey,void *,va_list)
- * @see #DBInterface
- * @see DBInterface#vdestroy(DBInterface,DBApply,va_list)
- * @see common\db.c#db_destroy(DBInterface,DBApply,...)
- */
- int (*destroy)(struct dbt *dbi, DBApply func, ...);
-
- /**
- * Finalize the database, feeing all the memory it uses.
- * Before deleting an entry, func is applyed to it.
- * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove
- * a database entry will give an error and be aborted (except for clearing).
- * @param dbi Interface of the database
- * @param func Function to be applyed to every entry before deleting
- * @param args Extra arguments for func
- * @return Sum of values returned by func
- * @protected
- * @see #DBInterface
- * @see #DBApply(DBKey,void *,va_list)
- * @see DBInterface#destroy(DBInterface,DBApply,...)
- * @see common\db.c#db_vdestroy(DBInterface,DBApply,va_list)
- */
- int (*vdestroy)(struct dbt *dbi, DBApply func, va_list args);
-
- /**
- * Return the size of the database (number of items in the database).
- * @param dbi Interface of the database
- * @return Size of the database
- * @protected
- * @see #DBInterface
- * @see common\db.c#db_size(DBInterface)
- */
- unsigned int (*size)(struct dbt *dbi);
-
- /**
- * Return the type of the database.
- * @param dbi Interface of the database
- * @return Type of the database
- * @protected
- * @see #DBType
- * @see #DBInterface
- * @see common\db.c#db_type(DBInterface)
- */
- DBType (*type)(struct dbt *dbi);
-
- /**
- * Return the options of the database.
- * @param dbi Interface of the database
- * @return Options of the database
- * @protected
- * @see #DBOptions
- * @see #DBInterface
- * @see common\db.c#db_options(DBInterface)
- */
- DBOptions (*options)(struct dbt *dbi);
-
-} *DBInterface;
-
-//For easy access to the common functions.
-#ifdef DB_MANUAL_CAST_TO_UNION
-# define i2key db_i2key
-# define ui2key db_ui2key
-# define str2key db_str2key
-#else /* not DB_MANUAL_CAST_TO_UNION */
-# define i2key(k) ((DBKey)(int)(k))
-# define ui2key(k) ((DBKey)(unsigned int)(k))
-# define str2key(k) ((DBKey)(unsigned char *)(k))
-#endif /* DB_MANUAL_CAST_TO_UNION / not DB_MANUAL_CAST_TO_UNION */
-
-#define db_get(db,k) (db)->get((db),(k))
-#define idb_get(db,k) (db)->get((db),i2key(k))
-#define uidb_get(db,k) (db)->get((db),ui2key(k))
-#define strdb_get(db,k) (db)->get((db),str2key(k))
-
-#define db_put(db,k,d) (db)->put((db),(k),(d))
-#define idb_put(db,k,d) (db)->put((db),i2key(k),(d))
-#define uidb_put(db,k,d) (db)->put((db),ui2key(k),(d))
-#define strdb_put(db,k,d) (db)->put((db),str2key(k),(d))
-
-#define db_remove(db,k) (db)->remove((db),(k))
-#define idb_remove(db,k) (db)->remove((db),i2key(k))
-#define uidb_remove(db,k) (db)->remove((db),ui2key(k))
-#define strdb_remove(db,k) (db)->remove((db),str2key(k))
-
-//These are discarding the possible vargs you could send to the function, so those
-//that require vargs must not use these defines.
-#define db_ensure(db,k,f) (db)->ensure((db),(k),f)
-#define idb_ensure(db,k,f) (db)->ensure((db),i2key(k),f)
-#define uidb_ensure(db,k,f) (db)->ensure((db),ui2key(k),f)
-#define strdb_ensure(db,k,f) (db)->ensure((db),str2key(k),f)
-
-/*****************************************************************************\
- * (2) Section with public functions. *
- * db_fix_options - Fix the options for a type of database. *
- * db_default_cmp - Get the default comparator for a type of database. *
- * db_default_hash - Get the default hasher for a type of database. *
- * db_default_release - Get the default releaser for a type of database *
- * with the fixed options. *
- * db_custom_release - Get the releaser that behaves as specified. *
- * db_alloc - Allocate a new database. *
- * db_i2key - Manual cast from 'int' to 'DBKey'. *
- * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
- * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
- * db_init - Initialise the database system. *
- * db_final - Finalise the database system. *
-\*****************************************************************************/
-
-/**
- * Returns the fixed options according to the database type.
- * Sets required options and unsets unsupported options.
- * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset.
- * @param type Type of the database
- * @param options Original options of the database
- * @return Fixed options of the database
- * @private
- * @see #DBType
- * @see #DBOptions
- * @see #db_default_release(DBType,DBOptions)
- * @see common\db.c#db_fix_options(DBType,DBOptions)
- */
-DBOptions db_fix_options(DBType type, DBOptions options);
-
-/**
- * Returns the default comparator for the type of database.
- * @param type Type of database
- * @return Comparator for the type of database or NULL if unknown database
- * @public
- * @see #DBType
- * @see #DBComparator
- * @see common\db.c#db_default_cmp(DBType)
- */
-DBComparator db_default_cmp(DBType type);
-
-/**
- * Returns the default hasher for the specified type of database.
- * @param type Type of database
- * @return Hasher of the type of database or NULL if unknown database
- * @public
- * @see #DBType
- * @see #DBHasher
- * @see common\db.c#db_default_hash(DBType)
- */
-DBHasher db_default_hash(DBType type);
-
-/**
- * Returns the default releaser for the specified type of database with the
- * specified options.
- * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
- * before choosing the releaser
- * @param type Type of database
- * @param options Options of the database
- * @return Default releaser for the type of database with the fixed options
- * @public
- * @see #DBType
- * @see #DBOptions
- * @see #DBReleaser
- * @see #db_fix_options(DBType,DBOptions)
- * @see #db_custom_release(DBRelease)
- * @see common\db.c#db_default_release(DBType,DBOptions)
- */
-DBReleaser db_default_release(DBType type, DBOptions options);
-
-/**
- * Returns the releaser that behaves as <code>which</code> specifies.
- * @param which Defines what the releaser releases
- * @return Releaser for the specified release options
- * @public
- * @see #DBRelease
- * @see #DBReleaser
- * @see #db_default_release(DBType,DBOptions)
- * @see common\db.c#db_custom_release(DBRelease)
- */
-DBReleaser db_custom_release(DBRelease which);
-
-/**
- * Allocate a new database of the specified type.
- * It uses the default comparator, hasher and releaser of the specified
- * database type and fixed options.
- * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
- * before creating the database.
- * @param file File where the database is being allocated
- * @param line Line of the file where the database is being allocated
- * @param type Type of database
- * @param options Options of the database
- * @param maxlen Maximum length of the string to be used as key in string
- * databases
- * @return The interface of the database
- * @public
- * @see #DBType
- * @see #DBInterface
- * @see #db_default_cmp(DBType)
- * @see #db_default_hash(DBType)
- * @see #db_default_release(DBType,DBOptions)
- * @see #db_fix_options(DBType,DBOptions)
- * @see common\db.c#db_alloc(const char *,int,DBType,DBOptions,unsigned short)
- */
-DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen);
-
-#ifdef DB_MANUAL_CAST_TO_UNION
-/**
- * Manual cast from 'int' to the union DBKey.
- * Created for compilers that don't support casting to unions.
- * @param key Key to be casted
- * @return The key as a DBKey union
- * @public
- * @see #DB_MANUAL_CAST_TO_UNION
- * @see #db_ui2key(unsigned int)
- * @see #db_str2key(unsigned char *)
- * @see common\db.c#db_i2key(int)
- */
-DBKey db_i2key(int key);
-
-/**
- * Manual cast from 'unsigned int' to the union DBKey.
- * Created for compilers that don't support casting to unions.
- * @param key Key to be casted
- * @return The key as a DBKey union
- * @public
- * @see #DB_MANUAL_CAST_TO_UNION
- * @see #db_i2key(int)
- * @see #db_str2key(unsigned char *)
- * @see common\db.c#db_ui2key(unsigned int)
- */
-DBKey db_ui2key(unsigned int key);
-
-/**
- * Manual cast from 'unsigned char *' to the union DBKey.
- * Created for compilers that don't support casting to unions.
- * @param key Key to be casted
- * @return The key as a DBKey union
- * @public
- * @see #DB_MANUAL_CAST_TO_UNION
- * @see #db_i2key(int)
- * @see #db_ui2key(unsigned int)
- * @see common\db.c#db_str2key(unsigned char *)
- */
-DBKey db_str2key(unsigned char *key);
-#endif /* DB_MANUAL_CAST_TO_UNION */
-
-/**
- * Initialize the database system.
- * @public
- * @see #db_final(void)
- * @see common\db.c#db_init(void)
- */
-void db_init(void);
-
-/**
- * Finalize the database system.
- * Frees the memory used by the block reusage system.
- * @public
- * @see #db_init(void)
- * @see common\db.c#db_final(void)
- */
-void db_final(void);
-
-// Link DB System - From jAthena
-struct linkdb_node {
- struct linkdb_node *next;
- struct linkdb_node *prev;
- void *key;
- void *data;
-};
-
-void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない
-void linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する
-void* linkdb_search ( struct linkdb_node** head, void *key);
-void* linkdb_erase ( struct linkdb_node** head, void *key);
-void linkdb_final ( struct linkdb_node** head );
-
-#endif
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * This file is separated in two sections: *
+ * (1) public typedefs, enums, unions, structures and defines *
+ * (2) public functions *
+ * *
+ * <B>Notes on the release system:</B> *
+ * Whenever an entry is removed from the database both the key and the *
+ * data are requested to be released. *
+ * At least one entry is removed when replacing an entry, removing an *
+ * entry, clearing the database or destroying the database. *
+ * What is actually released is defined by the release function, the *
+ * functions of the database only ask for the key and/or data to be *
+ * released. *
+ * *
+ * TODO: *
+ * - create an enum for the data (with int, unsigned int and void *) *
+ * - create a custom database allocator *
+ * - see what functions need or should be added to the database interface *
+ * *
+ * HISTORY: *
+ * 2.1 (Athena build #???#) - Portability fix *
+ * - Fixed the portability of casting to union and added the functions *
+ * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and *
+ * {@link DBInterface#clear(DBInterface,DBApply,...)}. *
+ * 2.0 (Athena build 4859) - Transition version *
+ * - Almost everything recoded with a strategy similar to objects, *
+ * database structure is maintained. *
+ * 1.0 (up to Athena build 4706) *
+ * - Previous database system. *
+ * *
+ * @version 2.1 (Athena build #???#) - Portability fix *
+ * @author (Athena build 4859) Flavio @ Amazon Project *
+ * @author (up to Athena build 4706) Athena Dev Teams *
+ * @encoding US-ASCII *
+ * @see common#db.c *
+\*****************************************************************************/
+#ifndef _DB_H_
+#define _DB_H_
+
+#include <stdarg.h>
+
+/*****************************************************************************\
+ * (1) Section with public typedefs, enums, unions, structures and defines. *
+ * DB_MANUAL_CAST_TO_UNION - Define when the compiler doesn't allow casting *
+ * to unions. *
+ * DBRelease - Enumeration of release options. *
+ * DBType - Enumeration of database types. *
+ * DBOptions - Bitfield enumeration of database options. *
+ * DBKey - Union of used key types. *
+ * DBApply - Format of functions applyed to the databases. *
+ * DBMatcher - Format of matchers used in DBInterface->getall. *
+ * DBComparator - Format of the comparators used by the databases. *
+ * DBHasher - Format of the hashers used by the databases. *
+ * DBReleaser - Format of the releasers used by the databases. *
+ * DBInterface - Structure of the interface of the database. *
+\*****************************************************************************/
+
+/**
+ * Define this to enable the functions that cast to unions.
+ * Required when the compiler doesn't support casting to unions.
+ * NOTE: It is recommened that the conditional tests to determine if this
+ * should be defined be located in a makefile or a header file specific for
+ * of compatibility and portability issues.
+ * @public
+ * @see #db_i2key(int)
+ * @see #db_ui2key(unsigned int)
+ * @see #db_str2key(unsigned char *)
+ */
+//#define DB_MANUAL_CAST_TO_UNION
+
+/**
+ * Bitfield with what should be released by the releaser function (if the
+ * function supports it).
+ * @public
+ * @see #DBReleaser
+ * @see #db_custom_release(DBRelease)
+ */
+typedef enum {
+ DB_RELEASE_NOTHING = 0,
+ DB_RELEASE_KEY = 1,
+ DB_RELEASE_DATA = 2,
+ DB_RELEASE_BOTH = 3
+} DBRelease;
+
+/**
+ * Supported types of database.
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
+ * types of databases.
+ * @param DB_INT Uses int's for keys
+ * @param DB_UINT Uses unsigned int's for keys
+ * @param DB_STRING Uses strings for keys.
+ * @param DB_ISTRING Uses case insensitive strings for keys.
+ * @public
+ * @see #DBOptions
+ * @see #DBKey
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see #db_default_cmp(DBType)
+ * @see #db_default_hash(DBType)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+typedef enum {
+ DB_INT,
+ DB_UINT,
+ DB_STRING,
+ DB_ISTRING
+} DBType;
+
+/**
+ * Bitfield of options that define the behaviour of the database.
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
+ * types of databases.
+ * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing
+ * and does not allow NULL keys or NULL data.
+ * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY
+ * is defined, the real key is freed as soon as the entry is added.
+ * @param DB_OPT_RELEASE_KEY Releases the key.
+ * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed
+ * from the database.
+ * WARNING: for funtions that return the data (like DBInterface->remove),
+ * a dangling pointer will be returned.
+ * @param DB_OPT_RELEASE_BOTH Releases both key and data.
+ * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database.
+ * @param DB_OPT_ALLOW_NULL_DATA Allow NULL data in the database.
+ * @public
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+typedef enum {
+ DB_OPT_BASE = 0,
+ DB_OPT_DUP_KEY = 1,
+ DB_OPT_RELEASE_KEY = 2,
+ DB_OPT_RELEASE_DATA = 4,
+ DB_OPT_RELEASE_BOTH = 6,
+ DB_OPT_ALLOW_NULL_KEY = 8,
+ DB_OPT_ALLOW_NULL_DATA = 16,
+} DBOptions;
+
+/**
+ * Union of key types used by the database.
+ * @param i Type of key for DB_INT databases
+ * @param ui Type of key for DB_UINT databases
+ * @param str Type of key for DB_STRING and DB_ISTRING databases
+ * @public
+ * @see #DBType
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBMatcher(DBKey,void *,va_list)
+ * @see #DBComparator(DBKey,DBKey,unsigned short)
+ * @see #DBHasher(DBKey,unsigned short)
+ * @see #DBReleaser(DBKey,void *,DBRelease)
+ * @see DBInterface#get(DBInterface,DBKey)
+ * @see DBInterface#put(DBInterface,DBKey,void *)
+ * @see DBInterface#remove(DBInterface,DBKey)
+ */
+typedef union {
+ int i;
+ unsigned int ui;
+ unsigned char *str;
+} DBKey;
+
+/**
+ * Format of funtions that create the data for the key when the entry doesn't
+ * exist in the database yet.
+ * @param key Key of the database entry
+ * @param args Extra arguments of the funtion
+ * @return Data identified by the key to be put in the database
+ * @public
+ * @see #DBKey
+ * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
+ */
+typedef void *(*DBCreateData)(DBKey key, va_list args);
+
+/**
+ * Format of functions to be applyed to an unspecified quantity of entries of
+ * a database.
+ * Any function that applyes this function to the database will return the sum
+ * of values returned by this function.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param args Extra arguments of the funtion
+ * @return Value to be added up by the funtion that is applying this
+ * @public
+ * @see #DBKey
+ * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see DBInterface#foreach(DBInterface,DBApply,...)
+ * @see DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ * @see DBInterface#destroy(DBInterface,DBApply,...)
+ */
+typedef int (*DBApply)(DBKey key, void *data, va_list args);
+
+/**
+ * Format of functions that match database entries.
+ * The purpose of the match depends on the function that is calling the matcher.
+ * Returns 0 if it is a match, another number otherwise.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param args Extra arguments of the function
+ * @return 0 if a match, another number otherwise
+ * @public
+ * @see #DBKey
+ * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatcher,...)
+ */
+typedef int (*DBMatcher)(DBKey key, void *data, va_list args);
+
+/**
+ * Format of the comparators used internally by the database system.
+ * Compares key1 to key2.
+ * <code>maxlen</code> is the maximum number of character used in DB_STRING and
+ * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K).
+ * Returns 0 is equal, negative if lower and positive is higher.
+ * @param key1 Key being compared
+ * @param key2 Key we are comparing to
+ * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING
+ * databases.
+ * @return 0 if equal, negative if lower and positive if higher
+ * @public
+ * @see #DBKey
+ * @see #db_default_cmp(DBType)
+ */
+typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen);
+
+/**
+ * Format of the hashers used internally by the database system.
+ * Creates the hash of the key.
+ * <code>maxlen</code> is the maximum number of character used in DB_STRING and
+ * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K).
+ * @param key Key being hashed
+ * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING
+ * databases.
+ * @return Hash of the key
+ * @public
+ * @see #DBKey
+ * @see #db_default_hash(DBType)
+ */
+typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen);
+
+/**
+ * Format of the releaser used by the database system.
+ * Releases nothing, the key, the data or both.
+ * All standard releasers use aFree to release.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @public
+ * @see #DBRelease
+ * @see #DBKey
+ * @see #db_default_releaser(DBType,DBOptions)
+ * @see #db_custom_release(DBRelease)
+ */
+typedef void (*DBReleaser)(DBKey key, void *data, DBRelease which);
+
+/**
+ * Public interface of a database. Only contains funtions.
+ * All the functions take the interface as the first argument.
+ * @public
+ * @see DBInterface#get(DBInterface,DBKey)
+ * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
+ * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ * @see DBInterface#put(DBInterface,DBKey,void *)
+ * @see DBInterface#remove(DBInterface,DBKey)
+ * @see DBInterface#foreach(DBInterface,DBApply,...)
+ * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see DBInterface#destroy(DBInterface,DBApply,...)
+ * @see DBInterface#destroy(DBInterface,DBApply,va_list)
+ * @see DBInterface#size(DBInterface)
+ * @see DBInterface#type(DBInterface)
+ * @see DBInterface#options(DBInterface)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+typedef struct dbt {
+
+ /**
+ * Get the data of the entry identifid by the key.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @return Data of the entry or NULL if not found
+ * @protected
+ * @see #DBKey
+ * @see #DBInterface
+ * @see common\db.c#db_get(DBInterface,DBKey)
+ */
+ void *(*get)(struct dbt *dbi, DBKey key);
+
+ /**
+ * Just calls {@link DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}.
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param dbi Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see #DBMatcher(DBKey key, void *data, va_list args)
+ * @see #DBInterface
+ * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ * @see common\db.c#db_getall(DBInterface,void **,unsigned int,DBMatch,...)
+ */
+ unsigned int (*getall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, ...);
+
+ /**
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param dbi Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see #DBMatcher(DBKey key, void *data, va_list args)
+ * @see #DBInterface
+ * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
+ * @see common\db.c#db_vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ */
+ unsigned int (*vgetall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, va_list args);
+
+ /**
+ * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}.
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param ... Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see #DBKey
+ * @see #DBCreateData
+ * @see #DBInterface
+ * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ * @see common\db.c#db_ensure(DBInterface,DBKey,DBCreateData,...)
+ */
+ void *(*ensure)(struct dbt *dbi, DBKey key, DBCreateData create, ...);
+
+ /**
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param args Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see #DBKey
+ * @see #DBCreateData
+ * @see #DBInterface
+ * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
+ * @see common\db.c#db_vensure(DBInterface,DBKey,DBCreateData,va_list)
+ */
+ void *(*vensure)(struct dbt *dbi, DBKey key, DBCreateData create, va_list args);
+
+ /**
+ * Put the data identified by the key in the database.
+ * Returns the previous data if the entry exists or NULL.
+ * NOTE: Uses the new key, the old one is released.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the data
+ * @param data Data to be put in the database
+ * @return The previous data if the entry exists or NULL
+ * @protected
+ * @see #DBKey
+ * @see #DBInterface
+ * @see common\db.c#db_put(DBInterface,DBKey,void *)
+ */
+ void *(*put)(struct dbt *dbi, DBKey key, void *data);
+
+ /**
+ * Remove an entry from the database.
+ * Returns the data of the entry.
+ * NOTE: The key (of the database) is released.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @return The data of the entry or NULL if not found
+ * @protected
+ * @see #DBKey
+ * @see #DBInterface
+ * @see common\db.c#db_remove(DBInterface,DBKey)
+ */
+ void *(*remove)(struct dbt *dbi, DBKey key);
+
+ /**
+ * Just calls {@link DBInterface#vforeach(DBInterface,DBApply,va_list)}.
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed
+ * @param ... Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see #DBInterface
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see common\db.c#db_foreach(DBInterface,DBApply,...)
+ */
+ int (*foreach)(struct dbt *dbi, DBApply func, ...);
+
+ /**
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed
+ * @param args Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#foreach(DBInterface,DBApply,...)
+ * @see common\db.c#db_vforeach(DBInterface,DBApply,va_list)
+ */
+ int (*vforeach)(struct dbt *dbi, DBApply func, va_list args);
+
+ /**
+ * Just calls {@link DBInterface#vclear(DBInterface,DBApply,va_list)}.
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#vclear(DBInterface,DBApply,va_list)
+ * @see common\db.c#db_clear(DBInterface,DBApply,...)
+ */
+ int (*clear)(struct dbt *dbi, DBApply func, ...);
+
+ /**
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#clear(DBInterface,DBApply,...)
+ * @see common\db.c#vclear(DBInterface,DBApply,va_list)
+ */
+ int (*vclear)(struct dbt *dbi, DBApply func, va_list args);
+
+ /**
+ * Just calls {@link DBInterface#vdestroy(DBInterface,DBApply,va_list)}.
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ * @see common\db.c#db_destroy(DBInterface,DBApply,...)
+ */
+ int (*destroy)(struct dbt *dbi, DBApply func, ...);
+
+ /**
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBInterface
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see DBInterface#destroy(DBInterface,DBApply,...)
+ * @see common\db.c#db_vdestroy(DBInterface,DBApply,va_list)
+ */
+ int (*vdestroy)(struct dbt *dbi, DBApply func, va_list args);
+
+ /**
+ * Return the size of the database (number of items in the database).
+ * @param dbi Interface of the database
+ * @return Size of the database
+ * @protected
+ * @see #DBInterface
+ * @see common\db.c#db_size(DBInterface)
+ */
+ unsigned int (*size)(struct dbt *dbi);
+
+ /**
+ * Return the type of the database.
+ * @param dbi Interface of the database
+ * @return Type of the database
+ * @protected
+ * @see #DBType
+ * @see #DBInterface
+ * @see common\db.c#db_type(DBInterface)
+ */
+ DBType (*type)(struct dbt *dbi);
+
+ /**
+ * Return the options of the database.
+ * @param dbi Interface of the database
+ * @return Options of the database
+ * @protected
+ * @see #DBOptions
+ * @see #DBInterface
+ * @see common\db.c#db_options(DBInterface)
+ */
+ DBOptions (*options)(struct dbt *dbi);
+
+} *DBInterface;
+
+//For easy access to the common functions.
+#ifdef DB_MANUAL_CAST_TO_UNION
+# define i2key db_i2key
+# define ui2key db_ui2key
+# define str2key db_str2key
+#else /* not DB_MANUAL_CAST_TO_UNION */
+# define i2key(k) ((DBKey)(int)(k))
+# define ui2key(k) ((DBKey)(unsigned int)(k))
+# define str2key(k) ((DBKey)(unsigned char *)(k))
+#endif /* DB_MANUAL_CAST_TO_UNION / not DB_MANUAL_CAST_TO_UNION */
+
+#define db_get(db,k) (db)->get((db),(k))
+#define idb_get(db,k) (db)->get((db),i2key(k))
+#define uidb_get(db,k) (db)->get((db),ui2key(k))
+#define strdb_get(db,k) (db)->get((db),str2key(k))
+
+#define db_put(db,k,d) (db)->put((db),(k),(d))
+#define idb_put(db,k,d) (db)->put((db),i2key(k),(d))
+#define uidb_put(db,k,d) (db)->put((db),ui2key(k),(d))
+#define strdb_put(db,k,d) (db)->put((db),str2key(k),(d))
+
+#define db_remove(db,k) (db)->remove((db),(k))
+#define idb_remove(db,k) (db)->remove((db),i2key(k))
+#define uidb_remove(db,k) (db)->remove((db),ui2key(k))
+#define strdb_remove(db,k) (db)->remove((db),str2key(k))
+
+//These are discarding the possible vargs you could send to the function, so those
+//that require vargs must not use these defines.
+#define db_ensure(db,k,f) (db)->ensure((db),(k),f)
+#define idb_ensure(db,k,f) (db)->ensure((db),i2key(k),f)
+#define uidb_ensure(db,k,f) (db)->ensure((db),ui2key(k),f)
+#define strdb_ensure(db,k,f) (db)->ensure((db),str2key(k),f)
+
+/*****************************************************************************\
+ * (2) Section with public functions. *
+ * db_fix_options - Fix the options for a type of database. *
+ * db_default_cmp - Get the default comparator for a type of database. *
+ * db_default_hash - Get the default hasher for a type of database. *
+ * db_default_release - Get the default releaser for a type of database *
+ * with the fixed options. *
+ * db_custom_release - Get the releaser that behaves as specified. *
+ * db_alloc - Allocate a new database. *
+ * db_i2key - Manual cast from 'int' to 'DBKey'. *
+ * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
+ * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
+ * db_init - Initialise the database system. *
+ * db_final - Finalise the database system. *
+\*****************************************************************************/
+
+/**
+ * Returns the fixed options according to the database type.
+ * Sets required options and unsets unsupported options.
+ * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset.
+ * @param type Type of the database
+ * @param options Original options of the database
+ * @return Fixed options of the database
+ * @private
+ * @see #DBType
+ * @see #DBOptions
+ * @see #db_default_release(DBType,DBOptions)
+ * @see common\db.c#db_fix_options(DBType,DBOptions)
+ */
+DBOptions db_fix_options(DBType type, DBOptions options);
+
+/**
+ * Returns the default comparator for the type of database.
+ * @param type Type of database
+ * @return Comparator for the type of database or NULL if unknown database
+ * @public
+ * @see #DBType
+ * @see #DBComparator
+ * @see common\db.c#db_default_cmp(DBType)
+ */
+DBComparator db_default_cmp(DBType type);
+
+/**
+ * Returns the default hasher for the specified type of database.
+ * @param type Type of database
+ * @return Hasher of the type of database or NULL if unknown database
+ * @public
+ * @see #DBType
+ * @see #DBHasher
+ * @see common\db.c#db_default_hash(DBType)
+ */
+DBHasher db_default_hash(DBType type);
+
+/**
+ * Returns the default releaser for the specified type of database with the
+ * specified options.
+ * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
+ * before choosing the releaser
+ * @param type Type of database
+ * @param options Options of the database
+ * @return Default releaser for the type of database with the fixed options
+ * @public
+ * @see #DBType
+ * @see #DBOptions
+ * @see #DBReleaser
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see #db_custom_release(DBRelease)
+ * @see common\db.c#db_default_release(DBType,DBOptions)
+ */
+DBReleaser db_default_release(DBType type, DBOptions options);
+
+/**
+ * Returns the releaser that behaves as <code>which</code> specifies.
+ * @param which Defines what the releaser releases
+ * @return Releaser for the specified release options
+ * @public
+ * @see #DBRelease
+ * @see #DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ * @see common\db.c#db_custom_release(DBRelease)
+ */
+DBReleaser db_custom_release(DBRelease which);
+
+/**
+ * Allocate a new database of the specified type.
+ * It uses the default comparator, hasher and releaser of the specified
+ * database type and fixed options.
+ * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
+ * before creating the database.
+ * @param file File where the database is being allocated
+ * @param line Line of the file where the database is being allocated
+ * @param type Type of database
+ * @param options Options of the database
+ * @param maxlen Maximum length of the string to be used as key in string
+ * databases
+ * @return The interface of the database
+ * @public
+ * @see #DBType
+ * @see #DBInterface
+ * @see #db_default_cmp(DBType)
+ * @see #db_default_hash(DBType)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see common\db.c#db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen);
+
+#ifdef DB_MANUAL_CAST_TO_UNION
+/**
+ * Manual cast from 'int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see #DB_MANUAL_CAST_TO_UNION
+ * @see #db_ui2key(unsigned int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.c#db_i2key(int)
+ */
+DBKey db_i2key(int key);
+
+/**
+ * Manual cast from 'unsigned int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see #DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.c#db_ui2key(unsigned int)
+ */
+DBKey db_ui2key(unsigned int key);
+
+/**
+ * Manual cast from 'unsigned char *' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see #DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_ui2key(unsigned int)
+ * @see common\db.c#db_str2key(unsigned char *)
+ */
+DBKey db_str2key(unsigned char *key);
+#endif /* DB_MANUAL_CAST_TO_UNION */
+
+/**
+ * Initialize the database system.
+ * @public
+ * @see #db_final(void)
+ * @see common\db.c#db_init(void)
+ */
+void db_init(void);
+
+/**
+ * Finalize the database system.
+ * Frees the memory used by the block reusage system.
+ * @public
+ * @see #db_init(void)
+ * @see common\db.c#db_final(void)
+ */
+void db_final(void);
+
+// Link DB System - From jAthena
+struct linkdb_node {
+ struct linkdb_node *next;
+ struct linkdb_node *prev;
+ void *key;
+ void *data;
+};
+
+void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない
+void linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する
+void* linkdb_search ( struct linkdb_node** head, void *key);
+void* linkdb_erase ( struct linkdb_node** head, void *key);
+void linkdb_final ( struct linkdb_node** head );
+
+#endif
diff --git a/src/common/ers.h b/src/common/ers.h
index a512f6365..9b6b4b62d 100644
--- a/src/common/ers.h
+++ b/src/common/ers.h
@@ -1,193 +1,193 @@
-/*****************************************************************************\
- * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
- * For more information, see LICENCE in the main folder *
- * *
- * <H1>Entry Reusage System</H1> *
- * *
- * There are several root entry managers, each with a different entry size. *
- * Each manager will keep track of how many instances have been 'created'. *
- * They will only automatically destroy themselves after the last instance *
- * is destroyed. *
- * *
- * Entries can be allocated from the managers. *
- * If it has reusable entries (freed entry), it uses one. *
- * So no assumption should be made about the data of the entry. *
- * Entries should be freed in the manager they where allocated from. *
- * Failure to do so can lead to unexpected behaviours. *
- * *
- * <H2>Advantages:</H2> *
- * - The same manager is used for entries of the same size. *
- * So entries freed in one instance of the manager can be used by other *
- * instances of the manager. *
- * - Much less memory allocation/deallocation - program will be faster. *
- * - Avoids memory fragmentaion - program will run better for longer. *
- * *
- * <H2>Disavantages:</H2> *
- * - Unused entries are almost inevitable - memory being wasted. *
- * - A manager will only auto-destroy when all of its instances are *
- * destroyed so memory will usually only be recovered near the end. *
- * - Always wastes space for entries smaller than a pointer. *
- * *
- * WARNING: The system is not thread-safe at the moment. *
- * *
- * HISTORY: *
- * 0.1 - Initial version *
- * *
- * @version 0.1 - Initial version *
- * @author Flavio @ Amazon Project *
- * @encoding US-ASCII *
- * @see common#ers.c *
-\*****************************************************************************/
-#ifndef _ERS_H_
-#define _ERS_H_
-
-#include "../common/cbasetypes.h"
-
-/*****************************************************************************\
- * (1) All public parts of the Entry Reusage System. *
- * DISABLE_ERS - Define to disable this system. *
- * ERS_ALIGNED - Alignment of the entries in the blocks. *
- * ERInterface - Interface of the entry manager. *
- * ers_new - Allocate an instance of an entry manager. *
- * ers_report - Print a report about the current state. *
- * ers_force_destroy_all - Force the destruction of all the managers. *
-\*****************************************************************************/
-
-/**
- * Define this to disable the Entry Reusage System.
- * All code except the typedef of ERInterface will be disabled.
- * To allow a smooth transition,
- * @public
- */
-//#define DISABLE_ERS
-
-/**
- * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries.
- * By default it aligns to one byte, using the "natural order" of the entries.
- * This should NEVER be set to zero or less.
- * If greater than one, some memory can be wasted. This should never be needed
- * but is here just in case some aligment issues arise.
- * @public
- * @see #ers_new(uint32)
- */
-#ifndef ERS_ALIGNED
-# define ERS_ALIGNED 1
-#endif /* not ERS_ALIGN_ENTRY */
-
-/**
- * Public interface of the entry manager.
- * @param alloc Allocate an entry from this manager
- * @param free Free an entry allocated from this manager
- * @param entry_size Return the size of the entries of this manager
- * @param destroy Destroy this instance of the manager
- * @public
- * @see #ers_new(uint32)
- */
-typedef struct eri {
-
- /**
- * Allocate an entry from this entry manager.
- * If there are reusable entries available, it reuses one instead.
- * @param self Interface of the entry manager
- * @return An entry
- * @protected
- * @see #ERInterface
- * @see ERInterface#free(ERInterface,void *)
- */
- void *(*alloc)(struct eri *self);
-
- /**
- * Free an entry allocated from this manager.
- * WARNING: Does not check if the entry was allocated by this manager.
- * Freeing such an entry can lead to unexpected behaviour.
- * @param self Interface of the entry manager
- * @param entry Entry to be freed
- * @protected
- * @see #ERInterface
- * @see ERInterface#alloc(ERInterface)
- */
- void (*free)(struct eri *self, void *entry);
-
- /**
- * Return the size of the entries allocated from this manager.
- * @param self Interface of the entry manager
- * @return Size of the entries of this manager in bytes
- * @protected
- * @see #ERInterface
- */
- uint32 (*entry_size)(struct eri *self);
-
- /**
- * Destroy this instance of the manager.
- * The manager is actually only destroyed when all the instances are destroyed.
- * When destroying the manager a warning is shown if the manager has
- * missing/extra entries.
- * @param self Interface of the entry manager
- * @protected
- * @see #ERInterface
- * @see #ers_new(uint32)
- */
- void (*destroy)(struct eri *self);
-
-} *ERInterface;
-
-#ifdef DISABLE_ERS
-// Use memory manager to allocate/free and disable other interface functions
-# define ers_alloc(obj,type) (type *)aMalloc(sizeof(type))
-# define ers_free(obj,entry) aFree(entry)
-# define ers_entry_size(obj) (uint32)0
-# define ers_destroy(obj)
-// Disable the public functions
-# define ers_new(size) NULL
-# define ers_report()
-# define ers_force_destroy_all()
-#else /* not DISABLE_ERS */
-// These defines should be used to allow the code to keep working whenever
-// the system is disabled
-# define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
-# define ers_free(obj,entry) (obj)->free((obj),(entry))
-# define ers_entry_size(obj) (obj)->entry_size(obj)
-# define ers_destroy(obj) (obj)->destroy(obj)
-
-/**
- * Get a new instance of the manager that handles the specified entry size.
- * Size has to greater than 0.
- * If the specified size is smaller than a pointer, the size of a pointer is
- * used instead.
- * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
- * ERS_ALIGNED that is greater or equal to size is what's actually used.
- * @param The requested size of the entry in bytes
- * @return Interface of the object
- * @public
- * @see #ERS_ALIGNED
- * @see #ERInterface
- * @see ERInterface#destroy(ERInterface)
- * @see common\ers.c#ers_new(uint32)
- */
-ERInterface ers_new(uint32 size);
-
-/**
- * Print a report about the current state of the Entry Reusage System.
- * Shows information about the global system and each entry manager.
- * The number of entries are checked and a warning is shown if extra reusable
- * entries are found.
- * The extra entries are included in the count of reusable entries.
- * @public
- * @see common\ers.c#ers_report(void)
- */
-void ers_report(void);
-
-/**
- * Forcibly destroy all the entry managers, checking for nothing.
- * The system is left as if no instances or entries had ever been allocated.
- * All previous entries and instances of the managers become invalid.
- * The use of this is NOT recommended.
- * It should only be used in extreme situations to make shure all the memory
- * allocated by this system is released.
- * @public
- * @see common\ers.c#ers_force_destroy_all(void)
- */
-void ers_force_destroy_all(void);
-#endif /* DISABLE_ERS / not DISABLE_ERS */
-
-#endif /* _ERS_H_ */
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * <H1>Entry Reusage System</H1> *
+ * *
+ * There are several root entry managers, each with a different entry size. *
+ * Each manager will keep track of how many instances have been 'created'. *
+ * They will only automatically destroy themselves after the last instance *
+ * is destroyed. *
+ * *
+ * Entries can be allocated from the managers. *
+ * If it has reusable entries (freed entry), it uses one. *
+ * So no assumption should be made about the data of the entry. *
+ * Entries should be freed in the manager they where allocated from. *
+ * Failure to do so can lead to unexpected behaviours. *
+ * *
+ * <H2>Advantages:</H2> *
+ * - The same manager is used for entries of the same size. *
+ * So entries freed in one instance of the manager can be used by other *
+ * instances of the manager. *
+ * - Much less memory allocation/deallocation - program will be faster. *
+ * - Avoids memory fragmentaion - program will run better for longer. *
+ * *
+ * <H2>Disavantages:</H2> *
+ * - Unused entries are almost inevitable - memory being wasted. *
+ * - A manager will only auto-destroy when all of its instances are *
+ * destroyed so memory will usually only be recovered near the end. *
+ * - Always wastes space for entries smaller than a pointer. *
+ * *
+ * WARNING: The system is not thread-safe at the moment. *
+ * *
+ * HISTORY: *
+ * 0.1 - Initial version *
+ * *
+ * @version 0.1 - Initial version *
+ * @author Flavio @ Amazon Project *
+ * @encoding US-ASCII *
+ * @see common#ers.c *
+\*****************************************************************************/
+#ifndef _ERS_H_
+#define _ERS_H_
+
+#include "../common/cbasetypes.h"
+
+/*****************************************************************************\
+ * (1) All public parts of the Entry Reusage System. *
+ * DISABLE_ERS - Define to disable this system. *
+ * ERS_ALIGNED - Alignment of the entries in the blocks. *
+ * ERInterface - Interface of the entry manager. *
+ * ers_new - Allocate an instance of an entry manager. *
+ * ers_report - Print a report about the current state. *
+ * ers_force_destroy_all - Force the destruction of all the managers. *
+\*****************************************************************************/
+
+/**
+ * Define this to disable the Entry Reusage System.
+ * All code except the typedef of ERInterface will be disabled.
+ * To allow a smooth transition,
+ * @public
+ */
+//#define DISABLE_ERS
+
+/**
+ * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries.
+ * By default it aligns to one byte, using the "natural order" of the entries.
+ * This should NEVER be set to zero or less.
+ * If greater than one, some memory can be wasted. This should never be needed
+ * but is here just in case some aligment issues arise.
+ * @public
+ * @see #ers_new(uint32)
+ */
+#ifndef ERS_ALIGNED
+# define ERS_ALIGNED 1
+#endif /* not ERS_ALIGN_ENTRY */
+
+/**
+ * Public interface of the entry manager.
+ * @param alloc Allocate an entry from this manager
+ * @param free Free an entry allocated from this manager
+ * @param entry_size Return the size of the entries of this manager
+ * @param destroy Destroy this instance of the manager
+ * @public
+ * @see #ers_new(uint32)
+ */
+typedef struct eri {
+
+ /**
+ * Allocate an entry from this entry manager.
+ * If there are reusable entries available, it reuses one instead.
+ * @param self Interface of the entry manager
+ * @return An entry
+ * @protected
+ * @see #ERInterface
+ * @see ERInterface#free(ERInterface,void *)
+ */
+ void *(*alloc)(struct eri *self);
+
+ /**
+ * Free an entry allocated from this manager.
+ * WARNING: Does not check if the entry was allocated by this manager.
+ * Freeing such an entry can lead to unexpected behaviour.
+ * @param self Interface of the entry manager
+ * @param entry Entry to be freed
+ * @protected
+ * @see #ERInterface
+ * @see ERInterface#alloc(ERInterface)
+ */
+ void (*free)(struct eri *self, void *entry);
+
+ /**
+ * Return the size of the entries allocated from this manager.
+ * @param self Interface of the entry manager
+ * @return Size of the entries of this manager in bytes
+ * @protected
+ * @see #ERInterface
+ */
+ uint32 (*entry_size)(struct eri *self);
+
+ /**
+ * Destroy this instance of the manager.
+ * The manager is actually only destroyed when all the instances are destroyed.
+ * When destroying the manager a warning is shown if the manager has
+ * missing/extra entries.
+ * @param self Interface of the entry manager
+ * @protected
+ * @see #ERInterface
+ * @see #ers_new(uint32)
+ */
+ void (*destroy)(struct eri *self);
+
+} *ERInterface;
+
+#ifdef DISABLE_ERS
+// Use memory manager to allocate/free and disable other interface functions
+# define ers_alloc(obj,type) (type *)aMalloc(sizeof(type))
+# define ers_free(obj,entry) aFree(entry)
+# define ers_entry_size(obj) (uint32)0
+# define ers_destroy(obj)
+// Disable the public functions
+# define ers_new(size) NULL
+# define ers_report()
+# define ers_force_destroy_all()
+#else /* not DISABLE_ERS */
+// These defines should be used to allow the code to keep working whenever
+// the system is disabled
+# define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
+# define ers_free(obj,entry) (obj)->free((obj),(entry))
+# define ers_entry_size(obj) (obj)->entry_size(obj)
+# define ers_destroy(obj) (obj)->destroy(obj)
+
+/**
+ * Get a new instance of the manager that handles the specified entry size.
+ * Size has to greater than 0.
+ * If the specified size is smaller than a pointer, the size of a pointer is
+ * used instead.
+ * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
+ * ERS_ALIGNED that is greater or equal to size is what's actually used.
+ * @param The requested size of the entry in bytes
+ * @return Interface of the object
+ * @public
+ * @see #ERS_ALIGNED
+ * @see #ERInterface
+ * @see ERInterface#destroy(ERInterface)
+ * @see common\ers.c#ers_new(uint32)
+ */
+ERInterface ers_new(uint32 size);
+
+/**
+ * Print a report about the current state of the Entry Reusage System.
+ * Shows information about the global system and each entry manager.
+ * The number of entries are checked and a warning is shown if extra reusable
+ * entries are found.
+ * The extra entries are included in the count of reusable entries.
+ * @public
+ * @see common\ers.c#ers_report(void)
+ */
+void ers_report(void);
+
+/**
+ * Forcibly destroy all the entry managers, checking for nothing.
+ * The system is left as if no instances or entries had ever been allocated.
+ * All previous entries and instances of the managers become invalid.
+ * The use of this is NOT recommended.
+ * It should only be used in extreme situations to make shure all the memory
+ * allocated by this system is released.
+ * @public
+ * @see common\ers.c#ers_force_destroy_all(void)
+ */
+void ers_force_destroy_all(void);
+#endif /* DISABLE_ERS / not DISABLE_ERS */
+
+#endif /* _ERS_H_ */
diff --git a/src/common/graph.c b/src/common/graph.c
index 3602f511f..e56783816 100644
--- a/src/common/graph.c
+++ b/src/common/graph.c
@@ -1,318 +1,318 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-// graph creation is enabled
-// #define ENABLE_GRAPH
-
-#ifdef ENABLE_GRAPH
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifndef _WIN32
- #include <unistd.h>
-#endif
-#ifdef MINGW
- #include <io.h>
-#endif
-
-#include "../common/core.h"
-#include "../common/timer.h"
-#include "../common/grfio.h"
-#include "../common/malloc.h"
-#include "graph.h"
-
-struct graph {
- int width;
- int height;
- int pallet_count;
- int png_len;
- int png_dirty;
- unsigned char* raw_data;
- unsigned char* png_data;
- int * graph_value;
- int graph_max;
-};
-
-void graph_write_dword(unsigned char* p,unsigned int v) {
- p[0] = (unsigned char)((v >> 24) & 0xFF);
- p[1] = (unsigned char)((v >> 16) & 0xFF);
- p[2] = (unsigned char)((v >> 8) & 0xFF);
- p[3] = (unsigned char)(v & 0xFF);
-}
-
-struct graph* graph_create(unsigned int x,unsigned int y) {
- struct graph *g = (struct graph*)aCalloc(sizeof(struct graph),1);
- if(g == NULL) return NULL;
- // 256 * 3 : パレットデータ
- // x * y * 2 : イメージのバッファ
- // 256 : チャンクデータなどの予備
- g->png_data = (unsigned char *) aMalloc(4 * 256 + (x + 1) * y * 2);
- g->raw_data = (unsigned char *) aCalloc( (x + 1) * y , 1);
- memcpy(
- g->png_data,
- "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x03\x00\x00\x00\xFF\xFF\xFF"
- "\xFF\x00\x00\x00\x03\x50\x4C\x54\x45\xFF\xFF\xFF\xA7\xC4\x1B\xC8",0x30
- );
- graph_write_dword(g->png_data + 0x10,x);
- graph_write_dword(g->png_data + 0x14,y);
- graph_write_dword(g->png_data + 0x1D,grfio_crc32(g->png_data+0x0C,0x11));
- g->pallet_count = 1;
- g->width = x;
- g->height = y;
- g->png_dirty = 1;
- g->graph_value = (int *) aCalloc(x,sizeof(int));
- g->graph_max = 1;
- return g;
-}
-
-void graph_pallet(struct graph* g, int index,unsigned long c) {
- if(g == NULL || c >= 256) return;
-
- if(g->pallet_count <= index) {
- malloc_set(g->png_data + 0x29 + 3 * g->pallet_count,0,(index - g->pallet_count) * 3);
- g->pallet_count = index + 1;
- }
- g->png_data[0x29 + index * 3 ] = (unsigned char)((c >> 16) & 0xFF); // R
- g->png_data[0x29 + index * 3 + 1] = (unsigned char)((c >> 8) & 0xFF); // G
- g->png_data[0x29 + index * 3 + 2] = (unsigned char)( c & 0xFF); // B
- graph_write_dword(g->png_data + 0x21,g->pallet_count * 3);
- graph_write_dword(
- g->png_data + 0x29 + g->pallet_count * 3,
- grfio_crc32(g->png_data + 0x25,g->pallet_count * 3 + 4)
- );
- g->png_dirty = 1;
-}
-
-void graph_setpixel(struct graph* g,int x,int y,int color) {
- if(g == NULL || color >= 256) { return; }
- if(x < 0) x = 0;
- if(y < 0) y = 0;
- if(x >= g->width) { x = g->width - 1; }
- if(y >= g->height) { y = g->height - 1; }
- if(color >= g->pallet_count) { graph_pallet(g,color,graph_rgb(0,0,0)); }
-
- g->raw_data[y * (g->width + 1) + x + 1] = (unsigned char)color;
- g->png_dirty = 1;
-}
-
-int graph_getpixel(struct graph* g,int x,int y) {
- if(x < 0) x = 0;
- if(y < 0) y = 0;
- if(x >= g->width) { x = g->width - 1; }
- if(y >= g->height) { y = g->height - 1; }
- return g->raw_data[y * (g->width + 1) + x + 1];
-}
-
-const unsigned char* graph_output(struct graph* g,int *len) {
- unsigned long inflate_len;
- unsigned char *p;
-
- if(g == NULL) return NULL;
- if(g->png_dirty == 0) {
- *len = g->png_len;
- return g->png_data;
- }
-
- p = g->png_data + 0x2D + 3 * g->pallet_count;
- inflate_len = 2 * (g->width + 1) * g->height;
- memcpy(p + 4,"IDAT",4);
- encode_zip(p + 8,&inflate_len,g->raw_data,(g->width + 1) * g->height);
- graph_write_dword(p,inflate_len);
- graph_write_dword(p + 8 + inflate_len,grfio_crc32(p + 4, inflate_len + 4));
-
- p += 0x0C + inflate_len;
- memcpy(p,"\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82",0x0C);
- p += 0x0C;
- g->png_len = p - g->png_data;
- g->png_dirty = 0;
- *len = g->png_len;
- return g->png_data;
-}
-
-void graph_free(struct graph* g) {
- if(g != NULL) {
- aFree(g->png_data);
- aFree(g->raw_data);
- aFree(g->graph_value);
- aFree(g);
- }
-}
-
-// とりあえず不効率版。後ほど書き直し予定
-void graph_square(struct graph* g,int x,int y,int xe,int ye,int color) {
- int i,j;
- if(g == NULL) return;
- if(x < 0) { x = 0; }
- if(y < 0) { y = 0; }
- if(xe > g->width) { xe = g->width; }
- if(ye > g->height) { ye = g->height; }
- for(i = y;i < ye ; i++) {
- for(j = x; j < xe ; j++) {
- graph_setpixel(g,j,i,color);
- }
- }
-}
-
-// とりあえず不効率版。後ほど書き直し予定
-void graph_scroll(struct graph* g,int n,int color) {
- int x,y;
- if(g == NULL) return;
- for(y = 0; y < g->height; y++) {
- for(x = 0; x < g->width - n; x++) {
- graph_setpixel(g,x,y,graph_getpixel(g,x + n,y));
- }
- for( ; x < g->width; x++) {
- graph_setpixel(g,x,y,color);
- }
- }
-}
-
-void graph_data(struct graph* g,int value) {
- int i, j, start;
- if(g == NULL) return;
- memmove(&g->graph_value[0],&g->graph_value[1],sizeof(int) * (g->width - 1));
- g->graph_value[g->width - 1] = value;
- if(value > g->graph_max) {
- // 最大値を超えたので再描画
- g->graph_max = value;
- graph_square(g,0,0,g->width,g->height,0);
- start = 0;
- } else {
- // スクロールしてポイント打つ
- graph_scroll(g,1,0);
- start = g->width - 1;
- }
- for(i = start; i < g->width; i++) {
- int h0 = (i == 0 ? 0 : g->graph_value[i - 1]) * g->height / g->graph_max;
- int h1 = (g->graph_value[i] ) * g->height / g->graph_max;
- int h2 = (h0 < h1 ? 1 : -1);
- for(j = h0; j != h1; j += h2) {
- graph_setpixel(g,i,g->height - 1 - j,1);
- }
- graph_setpixel(g,i,g->height - 1 - h1,1);
- }
-}
-
-// 上の関数群を利用して、自動的にグラフを作成するタイマー群
-
-#define GRP_WIDTH 300 // グラフの幅
-#define GRP_HEIGHT 200 // グラフの高さ
-#define GRP_COLOR graph_rgb(0,0,255) // グラフの色
-#define GRP_INTERVEL 60*1000 // グラフの更新間隔
-
-#define GRP_PATH "httpd/"
-
-struct graph_sensor {
- struct graph* graph;
- char* str;
- char hash[32];
- int scanid;
- int drawid;
- int interval;
- unsigned int (*func)(void);
-};
-
-static struct graph_sensor *sensor;
-static int sensor_max;
-
-static int graph_scan_timer(int tid,unsigned int tick,int id,int data)
-{
- if(id >= 0 && id < sensor_max)
- graph_data(sensor[id].graph,sensor[id].func());
- return 0;
-}
-
-// modified by Celest -- i'm trying to separate it from httpd if possible ^^;
-static int graph_draw_timer(int tid,unsigned int tick,int id,int data)
-{
- char png_file[24];
- FILE *fp;
-
- // create/update the png file
- do {
- const char *png_data;
- int len;
- sprintf (png_file, GRP_PATH"%s.png", sensor[id].hash);
- fp = fopen(png_file, "w");
- // if another png of the same hash exists
- // (i.e 2nd login server with the same sensors)
- // this will fail = not good >.<
- if (fp == NULL)
- break;
- png_data = graph_output(sensor[id].graph, &len);
- fwrite(png_data,1,len,fp);
- fclose(fp);
- } while (0);
-
- // create/update text snippet
- do {
- char buf[8192], *p;
- p = buf;
- sprintf (png_file, GRP_PATH"%s.graph", sensor[id].hash);
- fp = fopen(png_file, "w");
- if (fp == NULL)
- break;
- p += sprintf(p,"<h2>%s</h2>\n\n",
- sensor[id].str);
- p += sprintf(p,"<p><img src=\"%s.png\" width=\"%d\" height=\"%d\"></p>\n",
- sensor[id].hash, GRP_WIDTH,GRP_HEIGHT);
- p += sprintf(p,"<p>Max: %d, Interval: %d sec</p>\n\n",
- sensor[id].graph->graph_max, sensor[id].interval / 1000);
- fprintf(fp, buf);
- fclose(fp);
- } while (0);
-
- return 0;
-}
-
-void graph_add_sensor(const unsigned char* string, int interval, unsigned int (*callback_func)(void))
-{
- int draw_interval = interval * 2;
- struct graph *g = graph_create(GRP_WIDTH,GRP_HEIGHT);
- graph_pallet(g,1,GRP_COLOR);
-
- sensor = (struct graph_sensor *) aRealloc(sensor, sizeof(struct graph_sensor) * (sensor_max + 1));
- sensor[sensor_max].graph = g;
- sensor[sensor_max].str = aStrdup(string);
- // create crc32 hash of the sensor's name
- sprintf (sensor[sensor_max].hash, "%lu%c", grfio_crc32(string,strlen(string)), 'a' + SERVER_TYPE);
- sensor[sensor_max].func = callback_func;
- sensor[sensor_max].scanid = add_timer_interval(gettick() + 500, graph_scan_timer, sensor_max, 0, interval);
- sensor[sensor_max].drawid = add_timer_interval(gettick() + 1000, graph_draw_timer, sensor_max, 0, draw_interval < 60000 ? 60000 : draw_interval);
- sensor[sensor_max].interval = interval;
- sensor_max++;
-
-}
-
-void graph_final (void)
-{
- int i;
- for(i = 0; i < sensor_max; i++) {
- char png_file[24];
- // remove the png and snippet file
- sprintf (png_file, GRP_PATH"%s.png", sensor[i].hash);
- unlink (png_file);
- sprintf (png_file, GRP_PATH"%s.graph", sensor[i].hash);
- unlink (png_file);
- graph_free(sensor[i].graph);
- aFree(sensor[i].str);
- //delete_timer(sensor[i].scanid,graph_scan_timer);
- //delete_timer(sensor[i].drawid,graph_draw_timer);
- }
- aFree(sensor);
- sensor_max = 0;
-}
-
-void graph_init (void)
-{
- graph_add_sensor ("Memory Usage", 1000, malloc_usage);
- add_timer_func_list(graph_scan_timer, "graph_scan_timer");
- add_timer_func_list(graph_draw_timer, "graph_draw_timer");
-}
-
-#else
-void graph_init (void) {}
-void graph_final (void) {}
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// graph creation is enabled
+// #define ENABLE_GRAPH
+
+#ifdef ENABLE_GRAPH
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+ #include <unistd.h>
+#endif
+#ifdef MINGW
+ #include <io.h>
+#endif
+
+#include "../common/core.h"
+#include "../common/timer.h"
+#include "../common/grfio.h"
+#include "../common/malloc.h"
+#include "graph.h"
+
+struct graph {
+ int width;
+ int height;
+ int pallet_count;
+ int png_len;
+ int png_dirty;
+ unsigned char* raw_data;
+ unsigned char* png_data;
+ int * graph_value;
+ int graph_max;
+};
+
+void graph_write_dword(unsigned char* p,unsigned int v) {
+ p[0] = (unsigned char)((v >> 24) & 0xFF);
+ p[1] = (unsigned char)((v >> 16) & 0xFF);
+ p[2] = (unsigned char)((v >> 8) & 0xFF);
+ p[3] = (unsigned char)(v & 0xFF);
+}
+
+struct graph* graph_create(unsigned int x,unsigned int y) {
+ struct graph *g = (struct graph*)aCalloc(sizeof(struct graph),1);
+ if(g == NULL) return NULL;
+ // 256 * 3 : パレットデータ
+ // x * y * 2 : イメージのバッファ
+ // 256 : チャンクデータなどの予備
+ g->png_data = (unsigned char *) aMalloc(4 * 256 + (x + 1) * y * 2);
+ g->raw_data = (unsigned char *) aCalloc( (x + 1) * y , 1);
+ memcpy(
+ g->png_data,
+ "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52"
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x03\x00\x00\x00\xFF\xFF\xFF"
+ "\xFF\x00\x00\x00\x03\x50\x4C\x54\x45\xFF\xFF\xFF\xA7\xC4\x1B\xC8",0x30
+ );
+ graph_write_dword(g->png_data + 0x10,x);
+ graph_write_dword(g->png_data + 0x14,y);
+ graph_write_dword(g->png_data + 0x1D,grfio_crc32(g->png_data+0x0C,0x11));
+ g->pallet_count = 1;
+ g->width = x;
+ g->height = y;
+ g->png_dirty = 1;
+ g->graph_value = (int *) aCalloc(x,sizeof(int));
+ g->graph_max = 1;
+ return g;
+}
+
+void graph_pallet(struct graph* g, int index,unsigned long c) {
+ if(g == NULL || c >= 256) return;
+
+ if(g->pallet_count <= index) {
+ malloc_set(g->png_data + 0x29 + 3 * g->pallet_count,0,(index - g->pallet_count) * 3);
+ g->pallet_count = index + 1;
+ }
+ g->png_data[0x29 + index * 3 ] = (unsigned char)((c >> 16) & 0xFF); // R
+ g->png_data[0x29 + index * 3 + 1] = (unsigned char)((c >> 8) & 0xFF); // G
+ g->png_data[0x29 + index * 3 + 2] = (unsigned char)( c & 0xFF); // B
+ graph_write_dword(g->png_data + 0x21,g->pallet_count * 3);
+ graph_write_dword(
+ g->png_data + 0x29 + g->pallet_count * 3,
+ grfio_crc32(g->png_data + 0x25,g->pallet_count * 3 + 4)
+ );
+ g->png_dirty = 1;
+}
+
+void graph_setpixel(struct graph* g,int x,int y,int color) {
+ if(g == NULL || color >= 256) { return; }
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+ if(x >= g->width) { x = g->width - 1; }
+ if(y >= g->height) { y = g->height - 1; }
+ if(color >= g->pallet_count) { graph_pallet(g,color,graph_rgb(0,0,0)); }
+
+ g->raw_data[y * (g->width + 1) + x + 1] = (unsigned char)color;
+ g->png_dirty = 1;
+}
+
+int graph_getpixel(struct graph* g,int x,int y) {
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+ if(x >= g->width) { x = g->width - 1; }
+ if(y >= g->height) { y = g->height - 1; }
+ return g->raw_data[y * (g->width + 1) + x + 1];
+}
+
+const unsigned char* graph_output(struct graph* g,int *len) {
+ unsigned long inflate_len;
+ unsigned char *p;
+
+ if(g == NULL) return NULL;
+ if(g->png_dirty == 0) {
+ *len = g->png_len;
+ return g->png_data;
+ }
+
+ p = g->png_data + 0x2D + 3 * g->pallet_count;
+ inflate_len = 2 * (g->width + 1) * g->height;
+ memcpy(p + 4,"IDAT",4);
+ encode_zip(p + 8,&inflate_len,g->raw_data,(g->width + 1) * g->height);
+ graph_write_dword(p,inflate_len);
+ graph_write_dword(p + 8 + inflate_len,grfio_crc32(p + 4, inflate_len + 4));
+
+ p += 0x0C + inflate_len;
+ memcpy(p,"\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82",0x0C);
+ p += 0x0C;
+ g->png_len = p - g->png_data;
+ g->png_dirty = 0;
+ *len = g->png_len;
+ return g->png_data;
+}
+
+void graph_free(struct graph* g) {
+ if(g != NULL) {
+ aFree(g->png_data);
+ aFree(g->raw_data);
+ aFree(g->graph_value);
+ aFree(g);
+ }
+}
+
+// とりあえず不効率版。後ほど書き直し予定
+void graph_square(struct graph* g,int x,int y,int xe,int ye,int color) {
+ int i,j;
+ if(g == NULL) return;
+ if(x < 0) { x = 0; }
+ if(y < 0) { y = 0; }
+ if(xe > g->width) { xe = g->width; }
+ if(ye > g->height) { ye = g->height; }
+ for(i = y;i < ye ; i++) {
+ for(j = x; j < xe ; j++) {
+ graph_setpixel(g,j,i,color);
+ }
+ }
+}
+
+// とりあえず不効率版。後ほど書き直し予定
+void graph_scroll(struct graph* g,int n,int color) {
+ int x,y;
+ if(g == NULL) return;
+ for(y = 0; y < g->height; y++) {
+ for(x = 0; x < g->width - n; x++) {
+ graph_setpixel(g,x,y,graph_getpixel(g,x + n,y));
+ }
+ for( ; x < g->width; x++) {
+ graph_setpixel(g,x,y,color);
+ }
+ }
+}
+
+void graph_data(struct graph* g,int value) {
+ int i, j, start;
+ if(g == NULL) return;
+ memmove(&g->graph_value[0],&g->graph_value[1],sizeof(int) * (g->width - 1));
+ g->graph_value[g->width - 1] = value;
+ if(value > g->graph_max) {
+ // 最大値を超えたので再描画
+ g->graph_max = value;
+ graph_square(g,0,0,g->width,g->height,0);
+ start = 0;
+ } else {
+ // スクロールしてポイント打つ
+ graph_scroll(g,1,0);
+ start = g->width - 1;
+ }
+ for(i = start; i < g->width; i++) {
+ int h0 = (i == 0 ? 0 : g->graph_value[i - 1]) * g->height / g->graph_max;
+ int h1 = (g->graph_value[i] ) * g->height / g->graph_max;
+ int h2 = (h0 < h1 ? 1 : -1);
+ for(j = h0; j != h1; j += h2) {
+ graph_setpixel(g,i,g->height - 1 - j,1);
+ }
+ graph_setpixel(g,i,g->height - 1 - h1,1);
+ }
+}
+
+// 上の関数群を利用して、自動的にグラフを作成するタイマー群
+
+#define GRP_WIDTH 300 // グラフの幅
+#define GRP_HEIGHT 200 // グラフの高さ
+#define GRP_COLOR graph_rgb(0,0,255) // グラフの色
+#define GRP_INTERVEL 60*1000 // グラフの更新間隔
+
+#define GRP_PATH "httpd/"
+
+struct graph_sensor {
+ struct graph* graph;
+ char* str;
+ char hash[32];
+ int scanid;
+ int drawid;
+ int interval;
+ unsigned int (*func)(void);
+};
+
+static struct graph_sensor *sensor;
+static int sensor_max;
+
+static int graph_scan_timer(int tid,unsigned int tick,int id,int data)
+{
+ if(id >= 0 && id < sensor_max)
+ graph_data(sensor[id].graph,sensor[id].func());
+ return 0;
+}
+
+// modified by Celest -- i'm trying to separate it from httpd if possible ^^;
+static int graph_draw_timer(int tid,unsigned int tick,int id,int data)
+{
+ char png_file[24];
+ FILE *fp;
+
+ // create/update the png file
+ do {
+ const char *png_data;
+ int len;
+ sprintf (png_file, GRP_PATH"%s.png", sensor[id].hash);
+ fp = fopen(png_file, "w");
+ // if another png of the same hash exists
+ // (i.e 2nd login server with the same sensors)
+ // this will fail = not good >.<
+ if (fp == NULL)
+ break;
+ png_data = graph_output(sensor[id].graph, &len);
+ fwrite(png_data,1,len,fp);
+ fclose(fp);
+ } while (0);
+
+ // create/update text snippet
+ do {
+ char buf[8192], *p;
+ p = buf;
+ sprintf (png_file, GRP_PATH"%s.graph", sensor[id].hash);
+ fp = fopen(png_file, "w");
+ if (fp == NULL)
+ break;
+ p += sprintf(p,"<h2>%s</h2>\n\n",
+ sensor[id].str);
+ p += sprintf(p,"<p><img src=\"%s.png\" width=\"%d\" height=\"%d\"></p>\n",
+ sensor[id].hash, GRP_WIDTH,GRP_HEIGHT);
+ p += sprintf(p,"<p>Max: %d, Interval: %d sec</p>\n\n",
+ sensor[id].graph->graph_max, sensor[id].interval / 1000);
+ fprintf(fp, buf);
+ fclose(fp);
+ } while (0);
+
+ return 0;
+}
+
+void graph_add_sensor(const unsigned char* string, int interval, unsigned int (*callback_func)(void))
+{
+ int draw_interval = interval * 2;
+ struct graph *g = graph_create(GRP_WIDTH,GRP_HEIGHT);
+ graph_pallet(g,1,GRP_COLOR);
+
+ sensor = (struct graph_sensor *) aRealloc(sensor, sizeof(struct graph_sensor) * (sensor_max + 1));
+ sensor[sensor_max].graph = g;
+ sensor[sensor_max].str = aStrdup(string);
+ // create crc32 hash of the sensor's name
+ sprintf (sensor[sensor_max].hash, "%lu%c", grfio_crc32(string,strlen(string)), 'a' + SERVER_TYPE);
+ sensor[sensor_max].func = callback_func;
+ sensor[sensor_max].scanid = add_timer_interval(gettick() + 500, graph_scan_timer, sensor_max, 0, interval);
+ sensor[sensor_max].drawid = add_timer_interval(gettick() + 1000, graph_draw_timer, sensor_max, 0, draw_interval < 60000 ? 60000 : draw_interval);
+ sensor[sensor_max].interval = interval;
+ sensor_max++;
+
+}
+
+void graph_final (void)
+{
+ int i;
+ for(i = 0; i < sensor_max; i++) {
+ char png_file[24];
+ // remove the png and snippet file
+ sprintf (png_file, GRP_PATH"%s.png", sensor[i].hash);
+ unlink (png_file);
+ sprintf (png_file, GRP_PATH"%s.graph", sensor[i].hash);
+ unlink (png_file);
+ graph_free(sensor[i].graph);
+ aFree(sensor[i].str);
+ //delete_timer(sensor[i].scanid,graph_scan_timer);
+ //delete_timer(sensor[i].drawid,graph_draw_timer);
+ }
+ aFree(sensor);
+ sensor_max = 0;
+}
+
+void graph_init (void)
+{
+ graph_add_sensor ("Memory Usage", 1000, malloc_usage);
+ add_timer_func_list(graph_scan_timer, "graph_scan_timer");
+ add_timer_func_list(graph_draw_timer, "graph_draw_timer");
+}
+
+#else
+void graph_init (void) {}
+void graph_final (void) {}
+#endif
diff --git a/src/common/graph.h b/src/common/graph.h
index 6c80dd41c..9c8b73580 100644
--- a/src/common/graph.h
+++ b/src/common/graph.h
@@ -1,27 +1,27 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _GRAPH_H_
-#define _GRAPH_H_
-
-void graph_init (void);
-void graph_final (void);
-
-struct graph* graph_create(unsigned int x,unsigned int y);
-void graph_pallet(struct graph* g, int index,unsigned long c);
-const unsigned char* graph_output(struct graph* g,int *len);
-void graph_setpixel(struct graph* g,int x,int y,int color);
-void graph_scroll(struct graph* g,int n,int color);
-void graph_square(struct graph* g,int x,int y,int xe,int ye,int color);
-
-// athenaの状態を調査するセンサーを追加する。
-// string : センサーの名称(Login Users など)
-// inetrval : センサーの値を所得する間隔(msec)
-// callback_func : センサーの値を返す関数( unsigned int login_users(void); など)
-
-void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void));
-
-#define graph_rgb(r,g,b) (((r) << 16) | ((g) << 8) | (b))
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GRAPH_H_
+#define _GRAPH_H_
+
+void graph_init (void);
+void graph_final (void);
+
+struct graph* graph_create(unsigned int x,unsigned int y);
+void graph_pallet(struct graph* g, int index,unsigned long c);
+const unsigned char* graph_output(struct graph* g,int *len);
+void graph_setpixel(struct graph* g,int x,int y,int color);
+void graph_scroll(struct graph* g,int n,int color);
+void graph_square(struct graph* g,int x,int y,int xe,int ye,int color);
+
+// athenaの状態を調査するセンサーを追加する。
+// string : センサーの名称(Login Users など)
+// inetrval : センサーの値を所得する間隔(msec)
+// callback_func : センサーの値を返す関数( unsigned int login_users(void); など)
+
+void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void));
+
+#define graph_rgb(r,g,b) (((r) << 16) | ((g) << 8) | (b))
+
+#endif
+
diff --git a/src/common/grfio.c b/src/common/grfio.c
index 5597177c5..a821ee269 100644
--- a/src/common/grfio.c
+++ b/src/common/grfio.c
@@ -1,1031 +1,1031 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-/*********************************************************************
- *
- * Ragnarok Online Emulator : grfio.c -- grf file I/O Module
- *--------------------------------------------------------------------
- * special need library : zlib
- *********************************************************************
- * $Id: grfio.c,v 1.2 2004/09/29 17:31:49 kalaspuff Exp $
- *
- * 2002/12/18... the original edition
- * 2003/01/23 ... Code correction
- * 2003/02/01 ... An addition and decryption processing are improved for LocalFile and two or more GRF(s) check processing.
- * 2003/02/02 ... Even if there is no grf it does not stop -- as -- correction
- * 2003/02/02... grf reading specification can be added later -- as -- correction (grfio_add function addition)
- * 2003/02 / 03... at the time of grfio_resourcecheck processing the entry addition processing method -- correction
- * 2003/02/05... change of the processing in grfio_init
- * 2003/02/23... a local file check -- GRFIO_LOCAL -- switch (Defoe -- Function Off)
- * 2003/10/21 ... The data of alpha client was read.
- * 2003/11/10 ... Ready new grf format.
- * 2003/11/11 ... version check fix & bug fix
- * 2006/04/16 ... fixed crash grfio_find_file when file is not found.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <sys/stat.h>
-
-#include "grfio.h"
-#include "../common/mmo.h"
-#include "../common/showmsg.h"
-#include "../common/malloc.h"
-#include "../zlib/unzip.h"
-
-#define CHUNK 16384
-
-#ifdef __WIN32
- #include "../zlib/zlib.h"
- #include "../zlib/iowin32.h"
-#else
- #ifndef __FREEBSD__
- #include <zlib.h>
- #endif
-#endif
-
-typedef unsigned char BYTE;
-typedef unsigned short WORD;
-typedef unsigned long DWORD;
-
-//static char data_file[1024] = ""; // "data.grf";
-//static char sdata_file[1024] = ""; // "sdata.grf";
-//static char adata_file[1024] = ""; // "adata.grf";
-static char data_dir[1024] = ""; // "../";
-
-//----------------------------
-// file entry table struct
-//----------------------------
-typedef struct {
- int srclen; // compressed size
- int srclen_aligned; //
- int declen; // original size
- int srcpos;
- short next;
- int cycle;
- char type;
- char fn[128-4*5]; // file name
- char *fnd;
- signed char gentry; // read grf file select
-} FILELIST;
-//gentry ... 0 : It acquires from a local file.
-// It acquires from the resource file of 1>=:gentry_table[gentry-1].
-// 1<=: Check a local file.
-// If it is, after re-setting to 0, it acquires from a local file.
-// If there is nothing, mark reversal will be carried out, and it will re-set, and will acquire from a resource file as well as 1>=.
-
-//Since char defines *FILELIST.gentry, the maximum which can be added by grfio_add becomes by 127 pieces.
-
-#define GENTRY_LIMIT 512
-#define FILELIST_LIMIT 1048576 // temporary maximum, and a theory top maximum are 2G.
-
-static FILELIST *filelist = NULL;
-static int filelist_entrys = 0;
-static int filelist_maxentry = 0;
-
-static char **gentry_table = NULL;
-static int gentry_entrys = 0;
-static int gentry_maxentry = 0;
-
-//----------------------------
-// file list hash table
-//----------------------------
-static int filelist_hash[256];
-
-//----------------------------
-// grf decode data table
-//----------------------------
-static unsigned char BitMaskTable[8] = {
- 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
-};
-
-static char BitSwapTable1[64] = {
- 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
- 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
- 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
- 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
-};
-static char BitSwapTable2[64] = {
- 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
- 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
- 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
- 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
-};
-static char BitSwapTable3[32] = {
- 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
- 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
-};
-
-static unsigned char NibbleData[4][64]={
- {
- 0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e,
- 0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba, 0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b, 0x7a, 0x85,
- 0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21, 0xda, 0x43, 0x64, 0x9f, 0x2d, 0x14, 0xb1, 0x72,
- 0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec, 0x39, 0xa0, 0xa3, 0x05, 0x52, 0x6e, 0x0f, 0xd9,
- }, {
- 0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95, 0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a, 0xa3,
- 0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec, 0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe, 0x8f, 0x19,
- 0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06, 0x8c, 0x6a, 0xfb, 0x91, 0x37, 0x8d, 0x0d, 0x78,
- 0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b, 0x55, 0xbc, 0xa2, 0x57, 0xe8, 0x22, 0x74, 0xce,
- }, {
- 0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2, 0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68, 0x15,
- 0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae, 0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83, 0x9b, 0x68,
- 0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c, 0xa2, 0x19, 0xd8, 0xe5, 0x7c, 0x2f, 0x83, 0xda,
- 0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97, 0x61, 0xa6, 0x3d, 0x40, 0x0b, 0x58, 0xe6, 0x3d,
- }, {
- 0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78, 0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1, 0xa4,
- 0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb, 0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89, 0x17, 0x62,
- 0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87, 0xc9, 0x14, 0x3c, 0x4a, 0x7e, 0xa8, 0xe2, 0x7d,
- 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb,
- }
-};
-/*-----------------
- * long data get
- */
-static unsigned int getlong(unsigned char *p)
-{
-// return *p+p[1]*256+(p[2]+p[3]*256)*65536;
- return p[0]
- | p[1] << 0x08
- | p[2] << 0x10
- | p[3] << 0x18; // Shinomori
-}
-
-/*==========================================
- * Grf data decode : Subs
- *------------------------------------------
- */
-static void NibbleSwap(BYTE *Src, int len)
-{
- for(;0<len;len--,Src++) {
- *Src = (*Src>>4) | (*Src<<4);
- }
-}
-
-static void BitConvert(BYTE *Src,char *BitSwapTable)
-{
- int lop,prm;
- BYTE tmp[8];
-// *(DWORD*)tmp=*(DWORD*)(tmp+4)=0;
- malloc_tsetdword(tmp,0,8);
- for(lop=0;lop!=64;lop++) {
- prm = BitSwapTable[lop]-1;
- if (Src[(prm >> 3) & 7] & BitMaskTable[prm & 7]) {
- tmp[(lop >> 3) & 7] |= BitMaskTable[lop & 7];
- }
- }
-// *(DWORD*)Src = *(DWORD*)tmp;
-// *(DWORD*)(Src+4) = *(DWORD*)(tmp+4);
- memcpy(Src,tmp,8);
-}
-
-static void BitConvert4(BYTE *Src)
-{
- int lop,prm;
- BYTE tmp[8];
- tmp[0] = ((Src[7]<<5) | (Src[4]>>3)) & 0x3f; // ..0 vutsr
- tmp[1] = ((Src[4]<<1) | (Src[5]>>7)) & 0x3f; // ..srqpo n
- tmp[2] = ((Src[4]<<5) | (Src[5]>>3)) & 0x3f; // ..o nmlkj
- tmp[3] = ((Src[5]<<1) | (Src[6]>>7)) & 0x3f; // ..kjihg f
- tmp[4] = ((Src[5]<<5) | (Src[6]>>3)) & 0x3f; // ..g fedcb
- tmp[5] = ((Src[6]<<1) | (Src[7]>>7)) & 0x3f; // ..cba98 7
- tmp[6] = ((Src[6]<<5) | (Src[7]>>3)) & 0x3f; // ..8 76543
- tmp[7] = ((Src[7]<<1) | (Src[4]>>7)) & 0x3f; // ..43210 v
-
- for(lop=0;lop!=4;lop++) {
- tmp[lop] = (NibbleData[lop][tmp[lop*2]] & 0xf0)
- | (NibbleData[lop][tmp[lop*2+1]] & 0x0f);
- }
-
- *(DWORD*)(tmp+4)=0;
- for(lop=0;lop!=32;lop++) {
- prm = BitSwapTable3[lop]-1;
- if (tmp[prm >> 3] & BitMaskTable[prm & 7]) {
- tmp[(lop >> 3) + 4] |= BitMaskTable[lop & 7];
- }
- }
-// *(DWORD*)Src ^= *(DWORD*)(tmp+4);
- Src[0] ^= tmp[4];
- Src[1] ^= tmp[5];
- Src[2] ^= tmp[6];
- Src[3] ^= tmp[7];
-}
-
-static void decode_des_etc(BYTE *buf,int len,int type,int cycle)
-{
- int lop,cnt=0;
- if(cycle<3) cycle=3;
- else if(cycle<5) cycle++;
- else if(cycle<7) cycle+=9;
- else cycle+=15;
-
- for(lop=0;lop*8<len;lop++,buf+=8) {
- if(lop<20 || (type==0 && lop%cycle==0)){ // des
- BitConvert(buf,BitSwapTable1);
- BitConvert4(buf);
- BitConvert(buf,BitSwapTable2);
- } else {
- if(cnt==7 && type==0){
- int a;
- BYTE tmp[8];
- *(DWORD*)tmp = *(DWORD*)buf;
- *(DWORD*)(tmp+4) = *(DWORD*)(buf+4);
- cnt=0;
- buf[0]=tmp[3];
- buf[1]=tmp[4];
- buf[2]=tmp[6];
- buf[3]=tmp[0];
- buf[4]=tmp[1];
- buf[5]=tmp[2];
- buf[6]=tmp[5];
- a=tmp[7];
- if(a==0x00) a=0x2b;
- else if(a==0x2b) a=0x00;
- else if(a==0x01) a=0x68;
- else if(a==0x68) a=0x01;
- else if(a==0x48) a=0x77;
- else if(a==0x77) a=0x48;
- else if(a==0x60) a=0xff;
- else if(a==0xff) a=0x60;
- else if(a==0x6c) a=0x80;
- else if(a==0x80) a=0x6c;
- else if(a==0xb9) a=0xc0;
- else if(a==0xc0) a=0xb9;
- else if(a==0xeb) a=0xfe;
- else if(a==0xfe) a=0xeb;
- buf[7]=a;
- }
- cnt++;
- }
- }
-}
-/*==========================================
- * Grf data decode sub : zip
- *------------------------------------------
- */
-int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen)
-{
- z_stream stream;
- int err;
-
- stream.next_in = (Bytef*)source;
- stream.avail_in = (uInt)sourceLen;
- /* Check for source > 64K on 16-bit machine: */
- if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
-
- stream.next_out = (Bytef*) dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)0;
- stream.zfree = (free_func)0;
-
- err = inflateInit(&stream);
- if (err != Z_OK) return err;
-
- err = inflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- inflateEnd(&stream);
- return err == Z_OK ? Z_BUF_ERROR : err;
- }
- *destLen = stream.total_out;
-
- err = inflateEnd(&stream);
- return err;
-}
-
-int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen) {
- z_stream stream;
- int err;
- malloc_tsetdword(&stream, 0, sizeof(stream));
- stream.next_in = (Bytef*)source;
- stream.avail_in = (uInt)sourceLen;
- /* Check for source > 64K on 16-bit machine: */
- if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
-
- stream.next_out = (Bytef*) dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)0;
- stream.zfree = (free_func)0;
-
- err = deflateInit(&stream,Z_DEFAULT_COMPRESSION);
- if (err != Z_OK) return err;
-
- err = deflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- inflateEnd(&stream);
- return err == Z_OK ? Z_BUF_ERROR : err;
- }
- *destLen = stream.total_out;
-
- err = deflateEnd(&stream);
- return err;
-}
-
-/* ===================================
-* Unzips a file. 1: success, 0: error
-* Adapted from miniunz.c [Celest]
-* Version 1.01b, May 30th, 2004
-* Copyright (C) 1998-2004 Gilles Vollant
-* -------------------------------------
-*/
-int deflate_file (const char *source, const char *filename)
-{
-#ifdef _WIN32
- zlib_filefunc_def ffunc;
-#endif
- unzFile uf = NULL;
- int err = UNZ_OK;
- uInt size_buf = 8192;
- FILE *fout = NULL;
- void *buf;
-
-#ifdef _WIN32
- fill_win32_filefunc(&ffunc);
- uf = unzOpen2(source, &ffunc);
-#else
- uf = unzOpen(source);
-#endif
-
- if (uf == NULL) {
- //printf("Cannot open %s\n", source);
- return 0;
- }
- //printf("%s opened\n", source);
-
- if (unzLocateFile(uf, filename, 0) != UNZ_OK) {
- //printf("file %s not found in the zipfile\n", filename);
- return 0;
- }
-
- err = unzOpenCurrentFilePassword(uf, NULL);
- //if (err != UNZ_OK)
- // printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
-
- fout = fopen(filename,"wb");
- if (fout == NULL) {
- //printf("error opening %s\n", filename);
- return 0;
- }
-
- buf = (void *)aMalloc(size_buf);
- do {
- err = unzReadCurrentFile(uf, buf, size_buf);
- if (err < 0) {
- //printf("error %d with zipfile in unzReadCurrentFile\n", err);
- break;
- }
- if (err > 0 &&
- fwrite(buf, err, 1, fout)!=1)
- {
- //printf("error in writing extracted file\n");
- err = UNZ_ERRNO;
- break;
- }
- } while (err > 0);
-
- if (fout) fclose(fout);
-
- if (err == UNZ_OK) {
- err = unzCloseCurrentFile (uf);
- //if (err != UNZ_OK)
- // printf("error %d with zipfile in unzCloseCurrentFile\n", err);
- aFree(buf);
- return (err == UNZ_OK);
- }
-
- unzCloseCurrentFile(uf); /* don't lose the error */
-
- return 0;
-}
-
-unsigned long grfio_crc32 (const unsigned char *buf, unsigned int len)
-{
- return crc32(crc32(0L, Z_NULL, 0), buf, len);
-}
-
-/***********************************************************
- *** File List Subroutines ***
- ***********************************************************/
-
-/*==========================================
- * File List : Hash make
- *------------------------------------------
- */
-static int filehash(unsigned char *fname)
-{
- unsigned int hash=0;
- while(*fname) {
- hash = ((hash<<1)+(hash>>7)*9+tolower(*fname));
- fname++;
- }
- return hash & 255;
-}
-
-/*==========================================
- * File List : Hash initalize
- *------------------------------------------
- */
-static void hashinit(void)
-{
- int lop;
- for (lop = 0; lop < 256; lop++)
- filelist_hash[lop] = -1;
-}
-
-/*==========================================
- * File List : File find
- *------------------------------------------
- */
-static FILELIST *filelist_find(char *fname)
-{
- int hash;
-
- if (!filelist)
- return NULL;
-
- for (hash = filelist_hash[filehash((unsigned char *) fname)]; hash >= 0; hash = filelist[hash].next) {
- if(strcmpi(filelist[hash].fn, fname) == 0)
- break;
- }
-
- return (hash >= 0) ? &filelist[hash] : NULL;
-}
-
-char *grfio_find_file(char *fname){
- FILELIST *filelist = filelist_find(fname);
- if (!filelist) return NULL;
- return (!filelist->fnd?filelist->fn:filelist->fnd);
-}
-
-/*==========================================
- * File List : Filelist add
- *------------------------------------------
- */
-#define FILELIST_ADDS 1024 // number increment of file lists `
-
-static FILELIST* filelist_add(FILELIST *entry)
-{
- int hash;
-
- if (filelist_entrys >= FILELIST_LIMIT) {
- ShowFatalError("GRF filelist limit reached (filelist_add)!\n");
- exit(1);
- }
-
- if (filelist_entrys >= filelist_maxentry) {
- filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST));
- malloc_tsetdword(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST));
- filelist_maxentry += FILELIST_ADDS;
- }
-
- memcpy (&filelist[filelist_entrys], entry, sizeof(FILELIST));
-
- hash = filehash((unsigned char *) entry->fn);
- filelist[filelist_entrys].next = filelist_hash[hash];
- filelist_hash[hash] = filelist_entrys;
-
- filelist_entrys++;
-
- return &filelist[filelist_entrys - 1];
-}
-
-static FILELIST* filelist_modify(FILELIST *entry)
-{
- FILELIST *fentry;
- if ((fentry = filelist_find(entry->fn)) != NULL) {
- int tmp = fentry->next;
- memcpy(fentry, entry, sizeof(FILELIST));
- fentry->next = tmp;
- } else {
- fentry = filelist_add(entry);
- }
- return fentry;
-}
-
-/*==========================================
- * File List : filelist size adjust
- *------------------------------------------
- */
-static void filelist_adjust(void)
-{
- if (filelist != NULL) {
- if (filelist_maxentry > filelist_entrys) {
- filelist = (FILELIST *)aRealloc(
- filelist, filelist_entrys * sizeof(FILELIST));
- filelist_maxentry = filelist_entrys;
- }
- }
-}
-
-/***********************************************************
- *** Grfio Sobroutines ***
- ***********************************************************/
-
-/*==========================================
- * Grfio : Resource file size get
- *------------------------------------------
- */
-int grfio_size(char *fname)
-{
- FILELIST *entry;
-
- entry = filelist_find(fname);
-
- if (entry == NULL || entry->gentry < 0) { // LocalFileCheck
- char lfname[256], *p;
- FILELIST lentry;
- struct stat st;
-
- sprintf(lfname, "%s%s", data_dir, fname);
-
- for (p = &lfname[0]; *p != 0; p++)
- if (*p=='\\') *p = '/'; // * At the time of Unix
-
- if (stat(lfname, &st) == 0) {
- strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1);
- lentry.fnd = NULL;
- lentry.declen = st.st_size;
- lentry.gentry = 0; // 0:LocalFile
- entry = filelist_modify(&lentry);
- } else if (entry == NULL) {
- ShowError("%s not found (grfio_size)\n", fname);
- //exit(1);
- return -1;
- }
- }
- return entry->declen;
-}
-
-/*==========================================
- * Grfio : Resource file read & size get
- *------------------------------------------
- */
-void* grfio_reads(char *fname, int *size)
-{
- FILE *in;
- FILELIST *entry;
- unsigned char *buf2 = NULL;
-
- entry = filelist_find(fname);
-
- if (entry == NULL || entry->gentry <= 0) { // LocalFileCheck
- char lfname[256], *p;
- FILELIST lentry;
-
- sprintf(lfname, "%s%s", data_dir, fname);
-
- for (p = &lfname[0]; *p != 0; p++)
- if (*p == '\\') *p = '/'; // * At the time of Unix
-
- in = fopen(lfname, "rb");
- if (in != NULL) {
- if (entry != NULL && entry->gentry == 0) {
- lentry.declen = entry->declen;
- } else {
- fseek(in,0,2); // SEEK_END
- lentry.declen = ftell(in);
- }
- fseek(in,0,0); // SEEK_SET
- buf2 = (unsigned char *)aMallocA(lentry.declen + 1024);
- fread(buf2, 1, lentry.declen, in);
- fclose(in);
- strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1);
- lentry.fnd = NULL;
- lentry.gentry = 0; // 0:LocalFile
- entry = filelist_modify(&lentry);
- } else {
- if (entry != NULL && entry->gentry < 0) {
- entry->gentry = -entry->gentry; // local file checked
- } else {
- ShowError("%s not found (grfio_reads - local file %s)\n", fname, lfname);
- return NULL;
- }
- }
- }
- if (entry != NULL && entry->gentry > 0) { // Archive[GRF] File Read
- char *gfname = gentry_table[entry->gentry - 1];
- in = fopen(gfname, "rb");
- if(in != NULL) {
- unsigned char *buf = (unsigned char *)aMallocA(entry->srclen_aligned + 1024);
- fseek(in, entry->srcpos, 0);
- fread(buf, 1, entry->srclen_aligned, in);
- fclose(in);
- buf2 = (unsigned char *)aMallocA(entry->declen + 1024);
- if (entry->type == 1 || entry->type == 3 || entry->type == 5) {
- uLongf len;
- if (entry->cycle >= 0)
- decode_des_etc(buf, entry->srclen_aligned, entry->cycle == 0, entry->cycle);
- len = entry->declen;
- decode_zip(buf2, &len, buf, entry->srclen);
- if (len != entry->declen) {
- ShowError("decode_zip size miss match err: %d != %d\n", (int)len, entry->declen);
- aFree(buf);
- aFree(buf2);
- return NULL;
- }
- } else {
- memcpy(buf2, buf, entry->declen);
- }
- aFree(buf);
- } else {
- ShowError("%s not found (grfio_reads - grf file %s)\n", fname, gfname);
- return NULL;
- }
- }
- if (size != NULL && entry != NULL)
- *size = entry->declen;
-
- return buf2;
-}
-
-/*==========================================
- * Resource filename decode
- *------------------------------------------
- */
-static char * decode_filename(unsigned char *buf,int len)
-{
- int lop;
- for(lop=0;lop<len;lop+=8) {
- NibbleSwap(&buf[lop],8);
- BitConvert(&buf[lop],BitSwapTable1);
- BitConvert4(&buf[lop]);
- BitConvert(&buf[lop],BitSwapTable2);
- }
- return (char*)buf;
-}
-
-/*==========================================
- * Grfio : Entry table read
- *------------------------------------------
- */
-static int grfio_entryread(char *gfname,int gentry)
-{
- FILE *fp;
- long grf_size,list_size;
- unsigned char grf_header[0x2e];
- int lop,entry,entrys,ofs,grf_version;
- char *fname;
- unsigned char *grf_filelist;
-
- fp = fopen(gfname, "rb");
- if (fp == NULL) {
- ShowWarning("GRF Data File not found: '"CL_WHITE"%s"CL_RESET"'.\n",gfname);
- return 1; // 1:not found error
- }
-
- fseek(fp,0,2); // SEEK_END
- grf_size = ftell(fp);
- fseek(fp,0,0); // SEEK_SET
- fread(grf_header,1,0x2e,fp);
- if (strcmp((const char *) grf_header,"Master of Magic") ||
- fseek(fp,getlong(grf_header+0x1e),1)) // SEEK_CUR
- {
- fclose(fp);
- ShowError("GRF %s read error\n",gfname);
- return 2; // 2:file format error
- }
-
- grf_version = getlong(grf_header+0x2a) >> 8;
-
- if (grf_version == 0x01) { //****** Grf version 01xx ******
- list_size = grf_size - ftell(fp);
- grf_filelist = (unsigned char *) aMallocA(list_size);
- /*if (grf_filelist == NULL){
- fclose(fp);
- ShowError("out of memory : grf_filelist\n");
- return 3; // 3:memory alloc error
- }*/
- fread(grf_filelist,1,list_size,fp);
- fclose(fp);
-
- entrys = getlong(grf_header+0x26) - getlong(grf_header+0x22) - 7;
-
- // Get an entry
- for (entry = 0,ofs = 0; entry < entrys; entry++) {
- int ofs2, srclen, srccount, type;
- char *period_ptr;
- FILELIST aentry;
-
- ofs2 = ofs+getlong(grf_filelist+ofs)+4;
- type = grf_filelist[ofs2+12];
- if (type != 0) { // Directory Index ... skip
- fname = decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6);
- if (strlen(fname) > sizeof(aentry.fn) - 1) {
- ShowFatalError("GRF file name %s is too long\n", fname);
- aFree(grf_filelist);
- exit(1);
- }
- srclen = 0;
- if ((period_ptr = strrchr(fname, '.')) != NULL) {
- for(lop = 0; lop < 4; lop++) {
- if (strcmpi(period_ptr, ".gnd\0.gat\0.act\0.str"+lop*5) == 0)
- break;
- }
- srclen = getlong(grf_filelist+ofs2) - getlong(grf_filelist+ofs2+8) - 715;
- if(lop == 4) {
- for(lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++);
- } else {
- srccount = 0;
- }
- } else {
- srccount = 0;
- }
-
- aentry.srclen = srclen;
- aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579;
- aentry.declen = getlong(grf_filelist+ofs2+8);
- aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
- aentry.cycle = srccount;
- aentry.type = type;
- strncpy(aentry.fn, fname,sizeof(aentry.fn)-1);
- aentry.fnd = NULL;
-#ifdef GRFIO_LOCAL
- aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
-#else
- aentry.gentry = gentry+1; // With no first time LocalFileCheck
-#endif
- filelist_modify(&aentry);
- }
- ofs = ofs2 + 17;
- }
- aFree(grf_filelist);
-
- } else if (grf_version == 0x02) { //****** Grf version 02xx ******
- unsigned char eheader[8];
- unsigned char *rBuf;
- uLongf rSize, eSize;
-
- fread(eheader,1,8,fp);
- rSize = getlong(eheader); // Read Size
- eSize = getlong(eheader+4); // Extend Size
-
- if ((long)rSize > grf_size-ftell(fp)) { // Warning fix [Lance]
- fclose(fp);
- ShowError("Illegal data format : grf compress entry size\n");
- return 4;
- }
-
- rBuf = (unsigned char *)aMallocA(rSize); // Get a Read Size
- /*if (rBuf==NULL) {
- fclose(fp);
- ShowError("out of memory : grf compress entry table buffer\n");
- return 3;
- }*/
- grf_filelist = (unsigned char *)aMallocA(eSize); // Get a Extend Size
- /*if (grf_filelist==NULL) {
- aFree(rBuf);
- fclose(fp);
- ShowError("out of memory : grf extract entry table buffer\n");
- return 3;
- }*/
- fread(rBuf,1,rSize,fp);
- fclose(fp);
- decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function
- list_size = eSize;
- aFree(rBuf);
-
- entrys = getlong(grf_header+0x26) - 7;
-
- // Get an entry
- for(entry = 0, ofs = 0; entry < entrys; entry++){
- int ofs2, srclen, srccount, type;
- FILELIST aentry;
-
- fname = (char*)(grf_filelist+ofs);
- if (strlen(fname) > sizeof(aentry.fn)-1) {
- ShowFatalError("GRF file name %s is too long\n", fname);
- aFree(grf_filelist);
- exit(1);
- }
- //ofs2 = ofs+strlen((char*)(grf_filelist+ofs))+1;
- ofs2 = ofs + strlen(fname)+1;
- type = grf_filelist[ofs2+12];
- if (type == 1 || type == 3 || type == 5) {
- srclen = getlong(grf_filelist+ofs2);
- if (grf_filelist[ofs2+12] == 3) {
- for (lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++);
- } else if (grf_filelist[ofs2+12] == 5) {
- srccount = 0;
- } else { // if (grf_filelist[ofs2+12]==1) {
- srccount = -1;
- }
-
- aentry.srclen = srclen;
- aentry.srclen_aligned = getlong(grf_filelist+ofs2+4);
- aentry.declen = getlong(grf_filelist+ofs2+8);
- aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
- aentry.cycle = srccount;
- aentry.type = type;
- strncpy(aentry.fn,fname,sizeof(aentry.fn)-1);
- aentry.fnd = NULL;
-#ifdef GRFIO_LOCAL
- aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
-#else
- aentry.gentry = gentry+1; // With no first time LocalFileCheck
-#endif
- filelist_modify(&aentry);
- }
- ofs = ofs2 + 17;
- }
- aFree(grf_filelist);
-
- } else { //****** Grf Other version ******
- fclose(fp);
- ShowError("GRF version %04x not supported\n",getlong(grf_header+0x2a));
- return 4;
- }
-
- filelist_adjust(); // Unnecessary area release of filelist
-
- return 0; // 0:no error
-}
-
-/*==========================================
- * Grfio : Resource file check
- *------------------------------------------
- */
-static void grfio_resourcecheck(void)
-{
- char w1[256], w2[256], src[256], dst[256], restable[256], line[256];
- char *ptr, *buf;
- FILELIST *entry;
- int size, i = 0;
- FILE *fp;
-
- // read resnametable from data directory and return if successful
- sprintf(restable, "%sdata\\resnametable.txt", data_dir);
- for (ptr = &restable[0]; *ptr != 0; ptr++)
- if (*ptr == '\\') *ptr = '/';
-
- fp = fopen(restable,"rb");
- if (fp) {
- while (fgets(line, sizeof(line) - 1, fp)) {
- if (sscanf(line, "%[^#]#%[^#]#", w1, w2) == 2 &&
- // we only need the map names and text files
- (strstr(w2, ".gat") || strstr(w2, ".txt")))
- {
- sprintf(src, "data\\%s", w1);
- sprintf(dst, "data\\%s", w2);
- entry = filelist_find(dst);
- // create new entries reusing the original's info
- if (entry != NULL) {
- FILELIST fentry;
- memcpy(&fentry, entry, sizeof(FILELIST));
- strncpy(fentry.fn, src, sizeof(fentry.fn) - 1);
- fentry.fnd = grfio_alloc_ptr(dst);
- filelist_modify(&fentry);
- i++;
- }
- }
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "resnametable.txt");
- return; // we're done here!
- }
-
- // 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) {
- buf[size] = 0;
- ptr = buf;
-
- while (ptr - buf < size) {
- if (sscanf(ptr, "%[^#]#%[^#]#", w1, w2) == 2 &&
- (strstr(w2, ".gat") || strstr(w2, ".txt")))
- {
- sprintf(src, "data\\%s", w1);
- sprintf(dst, "data\\%s", w2);
- entry = filelist_find(dst);
- if (entry != NULL) {
- FILELIST fentry;
- memcpy(&fentry, entry, sizeof(FILELIST));
- strncpy(fentry.fn, src, sizeof(fentry.fn) - 1);
- fentry.fnd = grfio_alloc_ptr(dst);
- filelist_modify(&fentry);
- i++;
- }
- }
- ptr = strchr(ptr,'\n'); // Next line
- if (!ptr) break;
- ptr++;
- }
- aFree(buf);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "data\\resnametable.txt");
- return;
- }
-
- //ShowWarning("GRF: No resnametable found! Panic?\n");
-}
-
-/*==========================================
- * Grfio : Resource add
- *------------------------------------------
- */
-#define GENTRY_ADDS 4 // The number increment of gentry_table entries
-
-static int grfio_add(char *fname)
-{
- grfio_alloc_ptr(fname);
-
- return grfio_entryread(fname, gentry_entrys - 1);
-}
-
-char *grfio_alloc_ptr(char *fname)
-{
- int len;
- char *buf;
-
- if (gentry_entrys >= GENTRY_LIMIT) {
- ShowFatalError("gentrys limit : grfio_add\n");
- exit(1);
- }
-
- if (gentry_entrys >= gentry_maxentry) {
- gentry_maxentry += GENTRY_ADDS;
- gentry_table = (char**)aRealloc(gentry_table, gentry_maxentry * sizeof(char*));
- malloc_tsetdword(gentry_table + (gentry_maxentry - GENTRY_ADDS), 0, sizeof(char*) * GENTRY_ADDS);
- }
- len = strlen( fname );
- buf = (char*)aMallocA(len + 1);
- strcpy(buf, fname);
- gentry_table[gentry_entrys++] = buf;
-
- return buf;
-}
-
-/*==========================================
- * Grfio : Finalize
- *------------------------------------------
- */
-void grfio_final(void)
-{
- if (filelist != NULL)
- aFree(filelist);
-
- filelist_entrys = filelist_maxentry = 0;
-
- if (gentry_table != NULL) {
- int lop;
- for (lop = 0; lop < gentry_entrys; lop++) {
- if (gentry_table[lop] != NULL)
- aFree(gentry_table[lop]);
- }
- aFree(gentry_table);
- }
- gentry_table = NULL;
- gentry_entrys = gentry_maxentry = 0;
-}
-
-/*==========================================
- * Grfio : Initialize
- *------------------------------------------
- */
-void grfio_init(char *fname)
-{
- FILE *data_conf;
- char line[1024], w1[1024], w2[1024];
- int result = 0;
-
- hashinit(); // hash table initialization
-
- data_conf = fopen(fname, "r");
- // It will read, if there is grf-files.txt.
- if (data_conf) {
- while(fgets(line, sizeof(line) - 1, data_conf)) {
- if (line[0] == '/' && line[1] == '/')
- continue;
- if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
- continue;
- // Entry table reading
- if(strcmp(w1, "grf") == 0 ||
- strcmp(w1, "data") == 0 || // Primary data file
- strcmp(w1, "sdata") == 0 || // Sakray data file
- strcmp(w1, "adata") == 0) // Alpha version data file
- // increment if successfully loaded
- result += (grfio_add(w2) == 0);
- else if(strcmp(w1,"data_dir") == 0) // Data directory
- strcpy(data_dir, w2);
- }
-
- fclose(data_conf);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", fname);
- } // end of reading grf-files.txt
-
- if (result == 0) {
- ShowInfo("No grf's loaded.. using default data directory\n");
- //exit(1); // It ends, if a resource cannot read one.
- }
-
- // Unnecessary area release of filelist
- filelist_adjust();
- // Resource check
- grfio_resourcecheck();
-
- return;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+/*********************************************************************
+ *
+ * Ragnarok Online Emulator : grfio.c -- grf file I/O Module
+ *--------------------------------------------------------------------
+ * special need library : zlib
+ *********************************************************************
+ * $Id: grfio.c,v 1.2 2004/09/29 17:31:49 kalaspuff Exp $
+ *
+ * 2002/12/18... the original edition
+ * 2003/01/23 ... Code correction
+ * 2003/02/01 ... An addition and decryption processing are improved for LocalFile and two or more GRF(s) check processing.
+ * 2003/02/02 ... Even if there is no grf it does not stop -- as -- correction
+ * 2003/02/02... grf reading specification can be added later -- as -- correction (grfio_add function addition)
+ * 2003/02 / 03... at the time of grfio_resourcecheck processing the entry addition processing method -- correction
+ * 2003/02/05... change of the processing in grfio_init
+ * 2003/02/23... a local file check -- GRFIO_LOCAL -- switch (Defoe -- Function Off)
+ * 2003/10/21 ... The data of alpha client was read.
+ * 2003/11/10 ... Ready new grf format.
+ * 2003/11/11 ... version check fix & bug fix
+ * 2006/04/16 ... fixed crash grfio_find_file when file is not found.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "grfio.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+#include "../zlib/unzip.h"
+
+#define CHUNK 16384
+
+#ifdef __WIN32
+ #include "../zlib/zlib.h"
+ #include "../zlib/iowin32.h"
+#else
+ #ifndef __FREEBSD__
+ #include <zlib.h>
+ #endif
+#endif
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+
+//static char data_file[1024] = ""; // "data.grf";
+//static char sdata_file[1024] = ""; // "sdata.grf";
+//static char adata_file[1024] = ""; // "adata.grf";
+static char data_dir[1024] = ""; // "../";
+
+//----------------------------
+// file entry table struct
+//----------------------------
+typedef struct {
+ int srclen; // compressed size
+ int srclen_aligned; //
+ int declen; // original size
+ int srcpos;
+ short next;
+ int cycle;
+ char type;
+ char fn[128-4*5]; // file name
+ char *fnd;
+ signed char gentry; // read grf file select
+} FILELIST;
+//gentry ... 0 : It acquires from a local file.
+// It acquires from the resource file of 1>=:gentry_table[gentry-1].
+// 1<=: Check a local file.
+// If it is, after re-setting to 0, it acquires from a local file.
+// If there is nothing, mark reversal will be carried out, and it will re-set, and will acquire from a resource file as well as 1>=.
+
+//Since char defines *FILELIST.gentry, the maximum which can be added by grfio_add becomes by 127 pieces.
+
+#define GENTRY_LIMIT 512
+#define FILELIST_LIMIT 1048576 // temporary maximum, and a theory top maximum are 2G.
+
+static FILELIST *filelist = NULL;
+static int filelist_entrys = 0;
+static int filelist_maxentry = 0;
+
+static char **gentry_table = NULL;
+static int gentry_entrys = 0;
+static int gentry_maxentry = 0;
+
+//----------------------------
+// file list hash table
+//----------------------------
+static int filelist_hash[256];
+
+//----------------------------
+// grf decode data table
+//----------------------------
+static unsigned char BitMaskTable[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static char BitSwapTable1[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
+};
+static char BitSwapTable2[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
+};
+static char BitSwapTable3[32] = {
+ 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
+ 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
+};
+
+static unsigned char NibbleData[4][64]={
+ {
+ 0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e,
+ 0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba, 0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b, 0x7a, 0x85,
+ 0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21, 0xda, 0x43, 0x64, 0x9f, 0x2d, 0x14, 0xb1, 0x72,
+ 0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec, 0x39, 0xa0, 0xa3, 0x05, 0x52, 0x6e, 0x0f, 0xd9,
+ }, {
+ 0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95, 0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a, 0xa3,
+ 0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec, 0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe, 0x8f, 0x19,
+ 0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06, 0x8c, 0x6a, 0xfb, 0x91, 0x37, 0x8d, 0x0d, 0x78,
+ 0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b, 0x55, 0xbc, 0xa2, 0x57, 0xe8, 0x22, 0x74, 0xce,
+ }, {
+ 0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2, 0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68, 0x15,
+ 0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae, 0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83, 0x9b, 0x68,
+ 0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c, 0xa2, 0x19, 0xd8, 0xe5, 0x7c, 0x2f, 0x83, 0xda,
+ 0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97, 0x61, 0xa6, 0x3d, 0x40, 0x0b, 0x58, 0xe6, 0x3d,
+ }, {
+ 0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78, 0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1, 0xa4,
+ 0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb, 0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89, 0x17, 0x62,
+ 0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87, 0xc9, 0x14, 0x3c, 0x4a, 0x7e, 0xa8, 0xe2, 0x7d,
+ 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb,
+ }
+};
+/*-----------------
+ * long data get
+ */
+static unsigned int getlong(unsigned char *p)
+{
+// return *p+p[1]*256+(p[2]+p[3]*256)*65536;
+ return p[0]
+ | p[1] << 0x08
+ | p[2] << 0x10
+ | p[3] << 0x18; // Shinomori
+}
+
+/*==========================================
+ * Grf data decode : Subs
+ *------------------------------------------
+ */
+static void NibbleSwap(BYTE *Src, int len)
+{
+ for(;0<len;len--,Src++) {
+ *Src = (*Src>>4) | (*Src<<4);
+ }
+}
+
+static void BitConvert(BYTE *Src,char *BitSwapTable)
+{
+ int lop,prm;
+ BYTE tmp[8];
+// *(DWORD*)tmp=*(DWORD*)(tmp+4)=0;
+ malloc_tsetdword(tmp,0,8);
+ for(lop=0;lop!=64;lop++) {
+ prm = BitSwapTable[lop]-1;
+ if (Src[(prm >> 3) & 7] & BitMaskTable[prm & 7]) {
+ tmp[(lop >> 3) & 7] |= BitMaskTable[lop & 7];
+ }
+ }
+// *(DWORD*)Src = *(DWORD*)tmp;
+// *(DWORD*)(Src+4) = *(DWORD*)(tmp+4);
+ memcpy(Src,tmp,8);
+}
+
+static void BitConvert4(BYTE *Src)
+{
+ int lop,prm;
+ BYTE tmp[8];
+ tmp[0] = ((Src[7]<<5) | (Src[4]>>3)) & 0x3f; // ..0 vutsr
+ tmp[1] = ((Src[4]<<1) | (Src[5]>>7)) & 0x3f; // ..srqpo n
+ tmp[2] = ((Src[4]<<5) | (Src[5]>>3)) & 0x3f; // ..o nmlkj
+ tmp[3] = ((Src[5]<<1) | (Src[6]>>7)) & 0x3f; // ..kjihg f
+ tmp[4] = ((Src[5]<<5) | (Src[6]>>3)) & 0x3f; // ..g fedcb
+ tmp[5] = ((Src[6]<<1) | (Src[7]>>7)) & 0x3f; // ..cba98 7
+ tmp[6] = ((Src[6]<<5) | (Src[7]>>3)) & 0x3f; // ..8 76543
+ tmp[7] = ((Src[7]<<1) | (Src[4]>>7)) & 0x3f; // ..43210 v
+
+ for(lop=0;lop!=4;lop++) {
+ tmp[lop] = (NibbleData[lop][tmp[lop*2]] & 0xf0)
+ | (NibbleData[lop][tmp[lop*2+1]] & 0x0f);
+ }
+
+ *(DWORD*)(tmp+4)=0;
+ for(lop=0;lop!=32;lop++) {
+ prm = BitSwapTable3[lop]-1;
+ if (tmp[prm >> 3] & BitMaskTable[prm & 7]) {
+ tmp[(lop >> 3) + 4] |= BitMaskTable[lop & 7];
+ }
+ }
+// *(DWORD*)Src ^= *(DWORD*)(tmp+4);
+ Src[0] ^= tmp[4];
+ Src[1] ^= tmp[5];
+ Src[2] ^= tmp[6];
+ Src[3] ^= tmp[7];
+}
+
+static void decode_des_etc(BYTE *buf,int len,int type,int cycle)
+{
+ int lop,cnt=0;
+ if(cycle<3) cycle=3;
+ else if(cycle<5) cycle++;
+ else if(cycle<7) cycle+=9;
+ else cycle+=15;
+
+ for(lop=0;lop*8<len;lop++,buf+=8) {
+ if(lop<20 || (type==0 && lop%cycle==0)){ // des
+ BitConvert(buf,BitSwapTable1);
+ BitConvert4(buf);
+ BitConvert(buf,BitSwapTable2);
+ } else {
+ if(cnt==7 && type==0){
+ int a;
+ BYTE tmp[8];
+ *(DWORD*)tmp = *(DWORD*)buf;
+ *(DWORD*)(tmp+4) = *(DWORD*)(buf+4);
+ cnt=0;
+ buf[0]=tmp[3];
+ buf[1]=tmp[4];
+ buf[2]=tmp[6];
+ buf[3]=tmp[0];
+ buf[4]=tmp[1];
+ buf[5]=tmp[2];
+ buf[6]=tmp[5];
+ a=tmp[7];
+ if(a==0x00) a=0x2b;
+ else if(a==0x2b) a=0x00;
+ else if(a==0x01) a=0x68;
+ else if(a==0x68) a=0x01;
+ else if(a==0x48) a=0x77;
+ else if(a==0x77) a=0x48;
+ else if(a==0x60) a=0xff;
+ else if(a==0xff) a=0x60;
+ else if(a==0x6c) a=0x80;
+ else if(a==0x80) a=0x6c;
+ else if(a==0xb9) a=0xc0;
+ else if(a==0xc0) a=0xb9;
+ else if(a==0xeb) a=0xfe;
+ else if(a==0xfe) a=0xeb;
+ buf[7]=a;
+ }
+ cnt++;
+ }
+ }
+}
+/*==========================================
+ * Grf data decode sub : zip
+ *------------------------------------------
+ */
+int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen)
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = (Bytef*) dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
+
+int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen) {
+ z_stream stream;
+ int err;
+ malloc_tsetdword(&stream, 0, sizeof(stream));
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = (Bytef*) dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = deflateInit(&stream,Z_DEFAULT_COMPRESSION);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===================================
+* Unzips a file. 1: success, 0: error
+* Adapted from miniunz.c [Celest]
+* Version 1.01b, May 30th, 2004
+* Copyright (C) 1998-2004 Gilles Vollant
+* -------------------------------------
+*/
+int deflate_file (const char *source, const char *filename)
+{
+#ifdef _WIN32
+ zlib_filefunc_def ffunc;
+#endif
+ unzFile uf = NULL;
+ int err = UNZ_OK;
+ uInt size_buf = 8192;
+ FILE *fout = NULL;
+ void *buf;
+
+#ifdef _WIN32
+ fill_win32_filefunc(&ffunc);
+ uf = unzOpen2(source, &ffunc);
+#else
+ uf = unzOpen(source);
+#endif
+
+ if (uf == NULL) {
+ //printf("Cannot open %s\n", source);
+ return 0;
+ }
+ //printf("%s opened\n", source);
+
+ if (unzLocateFile(uf, filename, 0) != UNZ_OK) {
+ //printf("file %s not found in the zipfile\n", filename);
+ return 0;
+ }
+
+ err = unzOpenCurrentFilePassword(uf, NULL);
+ //if (err != UNZ_OK)
+ // printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
+
+ fout = fopen(filename,"wb");
+ if (fout == NULL) {
+ //printf("error opening %s\n", filename);
+ return 0;
+ }
+
+ buf = (void *)aMalloc(size_buf);
+ do {
+ err = unzReadCurrentFile(uf, buf, size_buf);
+ if (err < 0) {
+ //printf("error %d with zipfile in unzReadCurrentFile\n", err);
+ break;
+ }
+ if (err > 0 &&
+ fwrite(buf, err, 1, fout)!=1)
+ {
+ //printf("error in writing extracted file\n");
+ err = UNZ_ERRNO;
+ break;
+ }
+ } while (err > 0);
+
+ if (fout) fclose(fout);
+
+ if (err == UNZ_OK) {
+ err = unzCloseCurrentFile (uf);
+ //if (err != UNZ_OK)
+ // printf("error %d with zipfile in unzCloseCurrentFile\n", err);
+ aFree(buf);
+ return (err == UNZ_OK);
+ }
+
+ unzCloseCurrentFile(uf); /* don't lose the error */
+
+ return 0;
+}
+
+unsigned long grfio_crc32 (const unsigned char *buf, unsigned int len)
+{
+ return crc32(crc32(0L, Z_NULL, 0), buf, len);
+}
+
+/***********************************************************
+ *** File List Subroutines ***
+ ***********************************************************/
+
+/*==========================================
+ * File List : Hash make
+ *------------------------------------------
+ */
+static int filehash(unsigned char *fname)
+{
+ unsigned int hash=0;
+ while(*fname) {
+ hash = ((hash<<1)+(hash>>7)*9+tolower(*fname));
+ fname++;
+ }
+ return hash & 255;
+}
+
+/*==========================================
+ * File List : Hash initalize
+ *------------------------------------------
+ */
+static void hashinit(void)
+{
+ int lop;
+ for (lop = 0; lop < 256; lop++)
+ filelist_hash[lop] = -1;
+}
+
+/*==========================================
+ * File List : File find
+ *------------------------------------------
+ */
+static FILELIST *filelist_find(char *fname)
+{
+ int hash;
+
+ if (!filelist)
+ return NULL;
+
+ for (hash = filelist_hash[filehash((unsigned char *) fname)]; hash >= 0; hash = filelist[hash].next) {
+ if(strcmpi(filelist[hash].fn, fname) == 0)
+ break;
+ }
+
+ return (hash >= 0) ? &filelist[hash] : NULL;
+}
+
+char *grfio_find_file(char *fname){
+ FILELIST *filelist = filelist_find(fname);
+ if (!filelist) return NULL;
+ return (!filelist->fnd?filelist->fn:filelist->fnd);
+}
+
+/*==========================================
+ * File List : Filelist add
+ *------------------------------------------
+ */
+#define FILELIST_ADDS 1024 // number increment of file lists `
+
+static FILELIST* filelist_add(FILELIST *entry)
+{
+ int hash;
+
+ if (filelist_entrys >= FILELIST_LIMIT) {
+ ShowFatalError("GRF filelist limit reached (filelist_add)!\n");
+ exit(1);
+ }
+
+ if (filelist_entrys >= filelist_maxentry) {
+ filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST));
+ malloc_tsetdword(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST));
+ filelist_maxentry += FILELIST_ADDS;
+ }
+
+ memcpy (&filelist[filelist_entrys], entry, sizeof(FILELIST));
+
+ hash = filehash((unsigned char *) entry->fn);
+ filelist[filelist_entrys].next = filelist_hash[hash];
+ filelist_hash[hash] = filelist_entrys;
+
+ filelist_entrys++;
+
+ return &filelist[filelist_entrys - 1];
+}
+
+static FILELIST* filelist_modify(FILELIST *entry)
+{
+ FILELIST *fentry;
+ if ((fentry = filelist_find(entry->fn)) != NULL) {
+ int tmp = fentry->next;
+ memcpy(fentry, entry, sizeof(FILELIST));
+ fentry->next = tmp;
+ } else {
+ fentry = filelist_add(entry);
+ }
+ return fentry;
+}
+
+/*==========================================
+ * File List : filelist size adjust
+ *------------------------------------------
+ */
+static void filelist_adjust(void)
+{
+ if (filelist != NULL) {
+ if (filelist_maxentry > filelist_entrys) {
+ filelist = (FILELIST *)aRealloc(
+ filelist, filelist_entrys * sizeof(FILELIST));
+ filelist_maxentry = filelist_entrys;
+ }
+ }
+}
+
+/***********************************************************
+ *** Grfio Sobroutines ***
+ ***********************************************************/
+
+/*==========================================
+ * Grfio : Resource file size get
+ *------------------------------------------
+ */
+int grfio_size(char *fname)
+{
+ FILELIST *entry;
+
+ entry = filelist_find(fname);
+
+ if (entry == NULL || entry->gentry < 0) { // LocalFileCheck
+ char lfname[256], *p;
+ FILELIST lentry;
+ struct stat st;
+
+ sprintf(lfname, "%s%s", data_dir, fname);
+
+ for (p = &lfname[0]; *p != 0; p++)
+ if (*p=='\\') *p = '/'; // * At the time of Unix
+
+ if (stat(lfname, &st) == 0) {
+ strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1);
+ lentry.fnd = NULL;
+ lentry.declen = st.st_size;
+ lentry.gentry = 0; // 0:LocalFile
+ entry = filelist_modify(&lentry);
+ } else if (entry == NULL) {
+ ShowError("%s not found (grfio_size)\n", fname);
+ //exit(1);
+ return -1;
+ }
+ }
+ return entry->declen;
+}
+
+/*==========================================
+ * Grfio : Resource file read & size get
+ *------------------------------------------
+ */
+void* grfio_reads(char *fname, int *size)
+{
+ FILE *in;
+ FILELIST *entry;
+ unsigned char *buf2 = NULL;
+
+ entry = filelist_find(fname);
+
+ if (entry == NULL || entry->gentry <= 0) { // LocalFileCheck
+ char lfname[256], *p;
+ FILELIST lentry;
+
+ sprintf(lfname, "%s%s", data_dir, fname);
+
+ for (p = &lfname[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/'; // * At the time of Unix
+
+ in = fopen(lfname, "rb");
+ if (in != NULL) {
+ if (entry != NULL && entry->gentry == 0) {
+ lentry.declen = entry->declen;
+ } else {
+ fseek(in,0,2); // SEEK_END
+ lentry.declen = ftell(in);
+ }
+ fseek(in,0,0); // SEEK_SET
+ buf2 = (unsigned char *)aMallocA(lentry.declen + 1024);
+ fread(buf2, 1, lentry.declen, in);
+ fclose(in);
+ strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1);
+ lentry.fnd = NULL;
+ lentry.gentry = 0; // 0:LocalFile
+ entry = filelist_modify(&lentry);
+ } else {
+ if (entry != NULL && entry->gentry < 0) {
+ entry->gentry = -entry->gentry; // local file checked
+ } else {
+ ShowError("%s not found (grfio_reads - local file %s)\n", fname, lfname);
+ return NULL;
+ }
+ }
+ }
+ if (entry != NULL && entry->gentry > 0) { // Archive[GRF] File Read
+ char *gfname = gentry_table[entry->gentry - 1];
+ in = fopen(gfname, "rb");
+ if(in != NULL) {
+ unsigned char *buf = (unsigned char *)aMallocA(entry->srclen_aligned + 1024);
+ fseek(in, entry->srcpos, 0);
+ fread(buf, 1, entry->srclen_aligned, in);
+ fclose(in);
+ buf2 = (unsigned char *)aMallocA(entry->declen + 1024);
+ if (entry->type == 1 || entry->type == 3 || entry->type == 5) {
+ uLongf len;
+ if (entry->cycle >= 0)
+ decode_des_etc(buf, entry->srclen_aligned, entry->cycle == 0, entry->cycle);
+ len = entry->declen;
+ decode_zip(buf2, &len, buf, entry->srclen);
+ if (len != entry->declen) {
+ ShowError("decode_zip size miss match err: %d != %d\n", (int)len, entry->declen);
+ aFree(buf);
+ aFree(buf2);
+ return NULL;
+ }
+ } else {
+ memcpy(buf2, buf, entry->declen);
+ }
+ aFree(buf);
+ } else {
+ ShowError("%s not found (grfio_reads - grf file %s)\n", fname, gfname);
+ return NULL;
+ }
+ }
+ if (size != NULL && entry != NULL)
+ *size = entry->declen;
+
+ return buf2;
+}
+
+/*==========================================
+ * Resource filename decode
+ *------------------------------------------
+ */
+static char * decode_filename(unsigned char *buf,int len)
+{
+ int lop;
+ for(lop=0;lop<len;lop+=8) {
+ NibbleSwap(&buf[lop],8);
+ BitConvert(&buf[lop],BitSwapTable1);
+ BitConvert4(&buf[lop]);
+ BitConvert(&buf[lop],BitSwapTable2);
+ }
+ return (char*)buf;
+}
+
+/*==========================================
+ * Grfio : Entry table read
+ *------------------------------------------
+ */
+static int grfio_entryread(char *gfname,int gentry)
+{
+ FILE *fp;
+ long grf_size,list_size;
+ unsigned char grf_header[0x2e];
+ int lop,entry,entrys,ofs,grf_version;
+ char *fname;
+ unsigned char *grf_filelist;
+
+ fp = fopen(gfname, "rb");
+ if (fp == NULL) {
+ ShowWarning("GRF Data File not found: '"CL_WHITE"%s"CL_RESET"'.\n",gfname);
+ return 1; // 1:not found error
+ }
+
+ fseek(fp,0,2); // SEEK_END
+ grf_size = ftell(fp);
+ fseek(fp,0,0); // SEEK_SET
+ fread(grf_header,1,0x2e,fp);
+ if (strcmp((const char *) grf_header,"Master of Magic") ||
+ fseek(fp,getlong(grf_header+0x1e),1)) // SEEK_CUR
+ {
+ fclose(fp);
+ ShowError("GRF %s read error\n",gfname);
+ return 2; // 2:file format error
+ }
+
+ grf_version = getlong(grf_header+0x2a) >> 8;
+
+ if (grf_version == 0x01) { //****** Grf version 01xx ******
+ list_size = grf_size - ftell(fp);
+ grf_filelist = (unsigned char *) aMallocA(list_size);
+ /*if (grf_filelist == NULL){
+ fclose(fp);
+ ShowError("out of memory : grf_filelist\n");
+ return 3; // 3:memory alloc error
+ }*/
+ fread(grf_filelist,1,list_size,fp);
+ fclose(fp);
+
+ entrys = getlong(grf_header+0x26) - getlong(grf_header+0x22) - 7;
+
+ // Get an entry
+ for (entry = 0,ofs = 0; entry < entrys; entry++) {
+ int ofs2, srclen, srccount, type;
+ char *period_ptr;
+ FILELIST aentry;
+
+ ofs2 = ofs+getlong(grf_filelist+ofs)+4;
+ type = grf_filelist[ofs2+12];
+ if (type != 0) { // Directory Index ... skip
+ fname = decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6);
+ if (strlen(fname) > sizeof(aentry.fn) - 1) {
+ ShowFatalError("GRF file name %s is too long\n", fname);
+ aFree(grf_filelist);
+ exit(1);
+ }
+ srclen = 0;
+ if ((period_ptr = strrchr(fname, '.')) != NULL) {
+ for(lop = 0; lop < 4; lop++) {
+ if (strcmpi(period_ptr, ".gnd\0.gat\0.act\0.str"+lop*5) == 0)
+ break;
+ }
+ srclen = getlong(grf_filelist+ofs2) - getlong(grf_filelist+ofs2+8) - 715;
+ if(lop == 4) {
+ for(lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++);
+ } else {
+ srccount = 0;
+ }
+ } else {
+ srccount = 0;
+ }
+
+ aentry.srclen = srclen;
+ aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579;
+ aentry.declen = getlong(grf_filelist+ofs2+8);
+ aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
+ aentry.cycle = srccount;
+ aentry.type = type;
+ strncpy(aentry.fn, fname,sizeof(aentry.fn)-1);
+ aentry.fnd = NULL;
+#ifdef GRFIO_LOCAL
+ aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
+#else
+ aentry.gentry = gentry+1; // With no first time LocalFileCheck
+#endif
+ filelist_modify(&aentry);
+ }
+ ofs = ofs2 + 17;
+ }
+ aFree(grf_filelist);
+
+ } else if (grf_version == 0x02) { //****** Grf version 02xx ******
+ unsigned char eheader[8];
+ unsigned char *rBuf;
+ uLongf rSize, eSize;
+
+ fread(eheader,1,8,fp);
+ rSize = getlong(eheader); // Read Size
+ eSize = getlong(eheader+4); // Extend Size
+
+ if ((long)rSize > grf_size-ftell(fp)) { // Warning fix [Lance]
+ fclose(fp);
+ ShowError("Illegal data format : grf compress entry size\n");
+ return 4;
+ }
+
+ rBuf = (unsigned char *)aMallocA(rSize); // Get a Read Size
+ /*if (rBuf==NULL) {
+ fclose(fp);
+ ShowError("out of memory : grf compress entry table buffer\n");
+ return 3;
+ }*/
+ grf_filelist = (unsigned char *)aMallocA(eSize); // Get a Extend Size
+ /*if (grf_filelist==NULL) {
+ aFree(rBuf);
+ fclose(fp);
+ ShowError("out of memory : grf extract entry table buffer\n");
+ return 3;
+ }*/
+ fread(rBuf,1,rSize,fp);
+ fclose(fp);
+ decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function
+ list_size = eSize;
+ aFree(rBuf);
+
+ entrys = getlong(grf_header+0x26) - 7;
+
+ // Get an entry
+ for(entry = 0, ofs = 0; entry < entrys; entry++){
+ int ofs2, srclen, srccount, type;
+ FILELIST aentry;
+
+ fname = (char*)(grf_filelist+ofs);
+ if (strlen(fname) > sizeof(aentry.fn)-1) {
+ ShowFatalError("GRF file name %s is too long\n", fname);
+ aFree(grf_filelist);
+ exit(1);
+ }
+ //ofs2 = ofs+strlen((char*)(grf_filelist+ofs))+1;
+ ofs2 = ofs + strlen(fname)+1;
+ type = grf_filelist[ofs2+12];
+ if (type == 1 || type == 3 || type == 5) {
+ srclen = getlong(grf_filelist+ofs2);
+ if (grf_filelist[ofs2+12] == 3) {
+ for (lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++);
+ } else if (grf_filelist[ofs2+12] == 5) {
+ srccount = 0;
+ } else { // if (grf_filelist[ofs2+12]==1) {
+ srccount = -1;
+ }
+
+ aentry.srclen = srclen;
+ aentry.srclen_aligned = getlong(grf_filelist+ofs2+4);
+ aentry.declen = getlong(grf_filelist+ofs2+8);
+ aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
+ aentry.cycle = srccount;
+ aentry.type = type;
+ strncpy(aentry.fn,fname,sizeof(aentry.fn)-1);
+ aentry.fnd = NULL;
+#ifdef GRFIO_LOCAL
+ aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
+#else
+ aentry.gentry = gentry+1; // With no first time LocalFileCheck
+#endif
+ filelist_modify(&aentry);
+ }
+ ofs = ofs2 + 17;
+ }
+ aFree(grf_filelist);
+
+ } else { //****** Grf Other version ******
+ fclose(fp);
+ ShowError("GRF version %04x not supported\n",getlong(grf_header+0x2a));
+ return 4;
+ }
+
+ filelist_adjust(); // Unnecessary area release of filelist
+
+ return 0; // 0:no error
+}
+
+/*==========================================
+ * Grfio : Resource file check
+ *------------------------------------------
+ */
+static void grfio_resourcecheck(void)
+{
+ char w1[256], w2[256], src[256], dst[256], restable[256], line[256];
+ char *ptr, *buf;
+ FILELIST *entry;
+ int size, i = 0;
+ FILE *fp;
+
+ // read resnametable from data directory and return if successful
+ sprintf(restable, "%sdata\\resnametable.txt", data_dir);
+ for (ptr = &restable[0]; *ptr != 0; ptr++)
+ if (*ptr == '\\') *ptr = '/';
+
+ fp = fopen(restable,"rb");
+ if (fp) {
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (sscanf(line, "%[^#]#%[^#]#", w1, w2) == 2 &&
+ // we only need the map names and text files
+ (strstr(w2, ".gat") || strstr(w2, ".txt")))
+ {
+ sprintf(src, "data\\%s", w1);
+ sprintf(dst, "data\\%s", w2);
+ entry = filelist_find(dst);
+ // create new entries reusing the original's info
+ if (entry != NULL) {
+ FILELIST fentry;
+ memcpy(&fentry, entry, sizeof(FILELIST));
+ strncpy(fentry.fn, src, sizeof(fentry.fn) - 1);
+ fentry.fnd = grfio_alloc_ptr(dst);
+ filelist_modify(&fentry);
+ i++;
+ }
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "resnametable.txt");
+ return; // we're done here!
+ }
+
+ // 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) {
+ buf[size] = 0;
+ ptr = buf;
+
+ while (ptr - buf < size) {
+ if (sscanf(ptr, "%[^#]#%[^#]#", w1, w2) == 2 &&
+ (strstr(w2, ".gat") || strstr(w2, ".txt")))
+ {
+ sprintf(src, "data\\%s", w1);
+ sprintf(dst, "data\\%s", w2);
+ entry = filelist_find(dst);
+ if (entry != NULL) {
+ FILELIST fentry;
+ memcpy(&fentry, entry, sizeof(FILELIST));
+ strncpy(fentry.fn, src, sizeof(fentry.fn) - 1);
+ fentry.fnd = grfio_alloc_ptr(dst);
+ filelist_modify(&fentry);
+ i++;
+ }
+ }
+ ptr = strchr(ptr,'\n'); // Next line
+ if (!ptr) break;
+ ptr++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "data\\resnametable.txt");
+ return;
+ }
+
+ //ShowWarning("GRF: No resnametable found! Panic?\n");
+}
+
+/*==========================================
+ * Grfio : Resource add
+ *------------------------------------------
+ */
+#define GENTRY_ADDS 4 // The number increment of gentry_table entries
+
+static int grfio_add(char *fname)
+{
+ grfio_alloc_ptr(fname);
+
+ return grfio_entryread(fname, gentry_entrys - 1);
+}
+
+char *grfio_alloc_ptr(char *fname)
+{
+ int len;
+ char *buf;
+
+ if (gentry_entrys >= GENTRY_LIMIT) {
+ ShowFatalError("gentrys limit : grfio_add\n");
+ exit(1);
+ }
+
+ if (gentry_entrys >= gentry_maxentry) {
+ gentry_maxentry += GENTRY_ADDS;
+ gentry_table = (char**)aRealloc(gentry_table, gentry_maxentry * sizeof(char*));
+ malloc_tsetdword(gentry_table + (gentry_maxentry - GENTRY_ADDS), 0, sizeof(char*) * GENTRY_ADDS);
+ }
+ len = strlen( fname );
+ buf = (char*)aMallocA(len + 1);
+ strcpy(buf, fname);
+ gentry_table[gentry_entrys++] = buf;
+
+ return buf;
+}
+
+/*==========================================
+ * Grfio : Finalize
+ *------------------------------------------
+ */
+void grfio_final(void)
+{
+ if (filelist != NULL)
+ aFree(filelist);
+
+ filelist_entrys = filelist_maxentry = 0;
+
+ if (gentry_table != NULL) {
+ int lop;
+ for (lop = 0; lop < gentry_entrys; lop++) {
+ if (gentry_table[lop] != NULL)
+ aFree(gentry_table[lop]);
+ }
+ aFree(gentry_table);
+ }
+ gentry_table = NULL;
+ gentry_entrys = gentry_maxentry = 0;
+}
+
+/*==========================================
+ * Grfio : Initialize
+ *------------------------------------------
+ */
+void grfio_init(char *fname)
+{
+ FILE *data_conf;
+ char line[1024], w1[1024], w2[1024];
+ int result = 0;
+
+ hashinit(); // hash table initialization
+
+ data_conf = fopen(fname, "r");
+ // It will read, if there is grf-files.txt.
+ if (data_conf) {
+ while(fgets(line, sizeof(line) - 1, data_conf)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+ // Entry table reading
+ if(strcmp(w1, "grf") == 0 ||
+ strcmp(w1, "data") == 0 || // Primary data file
+ strcmp(w1, "sdata") == 0 || // Sakray data file
+ strcmp(w1, "adata") == 0) // Alpha version data file
+ // increment if successfully loaded
+ result += (grfio_add(w2) == 0);
+ else if(strcmp(w1,"data_dir") == 0) // Data directory
+ strcpy(data_dir, w2);
+ }
+
+ fclose(data_conf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", fname);
+ } // end of reading grf-files.txt
+
+ if (result == 0) {
+ ShowInfo("No grf's loaded.. using default data directory\n");
+ //exit(1); // It ends, if a resource cannot read one.
+ }
+
+ // Unnecessary area release of filelist
+ filelist_adjust();
+ // Resource check
+ grfio_resourcecheck();
+
+ return;
+}
diff --git a/src/common/grfio.h b/src/common/grfio.h
index 4ccdd00c7..572258b04 100644
--- a/src/common/grfio.h
+++ b/src/common/grfio.h
@@ -1,22 +1,22 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _GRFIO_H_
-#define _GRFIO_H_
-
-void grfio_init(char*); // GRFIO Initialize
-void grfio_final(void); // GRFIO Finalize
-void* grfio_reads(char*,int*); // GRFIO data file read & size get
-char *grfio_find_file(char *fname);
-char *grfio_alloc_ptr(char *fname);
-
-#define grfio_read(fn) grfio_reads(fn, NULL)
-
-int grfio_size(char*); // GRFIO data file size get
-unsigned long grfio_crc32(const unsigned char *buf, unsigned int len);
-
-int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen);
-int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen);
-int deflate_file (const char *source, const char *filename);
-
-#endif // _GRFIO_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GRFIO_H_
+#define _GRFIO_H_
+
+void grfio_init(char*); // GRFIO Initialize
+void grfio_final(void); // GRFIO Finalize
+void* grfio_reads(char*,int*); // GRFIO data file read & size get
+char *grfio_find_file(char *fname);
+char *grfio_alloc_ptr(char *fname);
+
+#define grfio_read(fn) grfio_reads(fn, NULL)
+
+int grfio_size(char*); // GRFIO data file size get
+unsigned long grfio_crc32(const unsigned char *buf, unsigned int len);
+
+int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen);
+int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen);
+int deflate_file (const char *source, const char *filename);
+
+#endif // _GRFIO_H_
diff --git a/src/common/lock.c b/src/common/lock.c
index c7bf623e5..18091820d 100644
--- a/src/common/lock.c
+++ b/src/common/lock.c
@@ -1,71 +1,71 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#ifndef WIN32
-#include <unistd.h>
-#else
-#include <windows.h>
-#define F_OK 0x0
-#define R_OK 0x4
-#endif
-#include "lock.h"
-#include "showmsg.h"
-
-#ifndef _WIN32
- #define exists(filename) (!access(filename, F_OK))
-#else
-// could be speed up maybe?
-int exists(char *file) {
- FILE *fp;
- if ((fp = fopen(file,"r")) && fclose(fp) == 0) return 1;
- return 0;
-}
-#endif
-
-// 書き込みファイルの保護処理
-// (書き込みが終わるまで、旧ファイルを保管しておく)
-
-// 新しいファイルの書き込み開始
-FILE* lock_fopen (const char* filename, int *info) {
- char newfile[512];
- FILE *fp;
- int no = 0;
-
- // 安全なファイル名を得る(手抜き)
- do {
- sprintf(newfile, "%s_%04d.tmp", filename, ++no);
- } while((fp = fopen(newfile,"r")) && (fclose(fp), no < 9999));
- *info = no;
- return fopen(newfile,"w");
-}
-
-// 旧ファイルを削除&新ファイルをリネーム
-int lock_fclose (FILE *fp, const char* filename, int *info) {
- int ret = 1;
- char newfile[512];
- char oldfile[512];
- if (fp != NULL) {
- ret = fclose(fp);
- sprintf(newfile, "%s_%04d.tmp", filename, *info);
- sprintf(oldfile, "%s.bak", filename); // old backup file
-
- if (exists(oldfile)) remove(oldfile); // remove backup file if it already exists
- rename (filename, oldfile); // backup our older data instead of deleting it
-
- // このタイミングで落ちると最悪。
- if ((ret = rename(newfile,filename)) != 0) { // rename our temporary file to its correct name
-#if defined(__NETBSD__) || defined(_WIN32) || defined(sun) || defined (_sun) || defined (__sun__)
- ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror(errno), newfile);
-#else
- char ebuf[255];
- ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror_r(errno, ebuf, sizeof(ebuf)), newfile);
-#endif
- }
- }
-
- return ret;
-}
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <windows.h>
+#define F_OK 0x0
+#define R_OK 0x4
+#endif
+#include "lock.h"
+#include "showmsg.h"
+
+#ifndef _WIN32
+ #define exists(filename) (!access(filename, F_OK))
+#else
+// could be speed up maybe?
+int exists(char *file) {
+ FILE *fp;
+ if ((fp = fopen(file,"r")) && fclose(fp) == 0) return 1;
+ return 0;
+}
+#endif
+
+// 書き込みファイルの保護処理
+// (書き込みが終わるまで、旧ファイルを保管しておく)
+
+// 新しいファイルの書き込み開始
+FILE* lock_fopen (const char* filename, int *info) {
+ char newfile[512];
+ FILE *fp;
+ int no = 0;
+
+ // 安全なファイル名を得る(手抜き)
+ do {
+ sprintf(newfile, "%s_%04d.tmp", filename, ++no);
+ } while((fp = fopen(newfile,"r")) && (fclose(fp), no < 9999));
+ *info = no;
+ return fopen(newfile,"w");
+}
+
+// 旧ファイルを削除&新ファイルをリネーム
+int lock_fclose (FILE *fp, const char* filename, int *info) {
+ int ret = 1;
+ char newfile[512];
+ char oldfile[512];
+ if (fp != NULL) {
+ ret = fclose(fp);
+ sprintf(newfile, "%s_%04d.tmp", filename, *info);
+ sprintf(oldfile, "%s.bak", filename); // old backup file
+
+ if (exists(oldfile)) remove(oldfile); // remove backup file if it already exists
+ rename (filename, oldfile); // backup our older data instead of deleting it
+
+ // このタイミングで落ちると最悪。
+ if ((ret = rename(newfile,filename)) != 0) { // rename our temporary file to its correct name
+#if defined(__NETBSD__) || defined(_WIN32) || defined(sun) || defined (_sun) || defined (__sun__)
+ ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror(errno), newfile);
+#else
+ char ebuf[255];
+ ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror_r(errno, ebuf, sizeof(ebuf)), newfile);
+#endif
+ }
+ }
+
+ return ret;
+}
+
diff --git a/src/common/lock.h b/src/common/lock.h
index 5c846eb73..e2247fc68 100644
--- a/src/common/lock.h
+++ b/src/common/lock.h
@@ -1,11 +1,11 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _LOCK_H_
-#define _LOCK_H_
-
-FILE* lock_fopen(const char* filename,int *info);
-int lock_fclose(FILE *fp,const char* filename,int *info);
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _LOCK_H_
+#define _LOCK_H_
+
+FILE* lock_fopen(const char* filename,int *info);
+int lock_fclose(FILE *fp,const char* filename,int *info);
+
+#endif
+
diff --git a/src/common/malloc.c b/src/common/malloc.c
index 1a4729fa2..d4dda5b4c 100644
--- a/src/common/malloc.c
+++ b/src/common/malloc.c
@@ -1,733 +1,733 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "../common/malloc.h"
-#include "../common/core.h"
-#include "../common/showmsg.h"
-
-#ifdef MINICORE
- #undef LOG_MEMMGR
-#endif
-
-void* aMalloc_(size_t size, const char *file, int line, const char *func)
-{
- void *ret = MALLOC(size, file, line, func);
- // ShowMessage("%s:%d: in func %s: aMalloc %d\n",file,line,func,size);
- if (ret == NULL){
- ShowFatalError("%s:%d: in func %s: aMalloc error out of memory!\n",file,line,func);
- exit(1);
- }
-
- return ret;
-}
-void* aMallocA_(size_t size, const char *file, int line, const char *func)
-{
- void *ret = MALLOCA(size, file, line, func);
- // ShowMessage("%s:%d: in func %s: aMallocA %d\n",file,line,func,size);
- if (ret == NULL){
- ShowFatalError("%s:%d: in func %s: aMallocA error out of memory!\n",file,line,func);
- exit(1);
- }
-
- return ret;
-}
-void* aCalloc_(size_t num, size_t size, const char *file, int line, const char *func)
-{
- void *ret = CALLOC(num, size, file, line, func);
- // ShowMessage("%s:%d: in func %s: aCalloc %d %d\n",file,line,func,num,size);
- if (ret == NULL){
- ShowFatalError("%s:%d: in func %s: aCalloc error out of memory!\n", file, line, func);
- exit(1);
- }
- return ret;
-}
-void* aCallocA_(size_t num, size_t size, const char *file, int line, const char *func)
-{
- void *ret = CALLOCA(num, size, file, line, func);
- // ShowMessage("%s:%d: in func %s: aCallocA %d %d\n",file,line,func,num,size);
- if (ret == NULL){
- ShowFatalError("%s:%d: in func %s: aCallocA error out of memory!\n",file,line,func);
- exit(1);
- }
- return ret;
-}
-void* aRealloc_(void *p, size_t size, const char *file, int line, const char *func)
-{
- void *ret = REALLOC(p, size, file, line, func);
- // ShowMessage("%s:%d: in func %s: aRealloc %p %d\n",file,line,func,p,size);
- if (ret == NULL){
- ShowFatalError("%s:%d: in func %s: aRealloc error out of memory!\n",file,line,func);
- exit(1);
- }
- return ret;
-}
-char* aStrdup_(const char *p, const char *file, int line, const char *func)
-{
- char *ret = STRDUP(p, file, line, func);
- // ShowMessage("%s:%d: in func %s: aStrdup %p\n",file,line,func,p);
- if (ret == NULL){
- ShowFatalError("%s:%d: in func %s: aStrdup error out of memory!\n", file, line, func);
- exit(1);
- }
- return ret;
-}
-void aFree_(void *p, const char *file, int line, const char *func)
-{
- // ShowMessage("%s:%d: in func %s: aFree %p\n",file,line,func,p);
- if (p)
- FREE(p, file, line, func);
-
- p = NULL;
-}
-
-#ifdef GCOLLECT
-
-void* _bcallocA(size_t size, size_t cnt)
-{
- void *ret = MALLOCA(size * cnt);
- if (ret) //malloc_set(ret, 0, size * cnt);
- malloc_set(ret, 0, size*cnt);
- return ret;
-}
-void* _bcalloc(size_t size, size_t cnt)
-{
- void *ret = MALLOC(size * cnt);
- if (ret) //malloc_set(ret, 0, size * cnt);
- malloc_set(ret, 0, size*cnt);
- return ret;
-}
-char* _bstrdup(const char *chr)
-{
- int len = strlen(chr);
- char *ret = (char*)MALLOC(len + 1);
- if (ret) memcpy(ret, chr, len + 1);
- return ret;
-}
-
-#endif
-
-#ifdef USE_MEMMGR
-
-/* USE_MEMMGR */
-
-/*
- * メモリマネージャ
- * malloc , free の処理を効率的に出来るようにしたもの。
- * 複雑な処理を行っているので、若干重くなるかもしれません。
- *
- * データ構造など(説明下手ですいません^^; )
- * ・メモリを複数の「ブロック」に分けて、さらにブロックを複数の「ユニット」
- * に分けています。ユニットのサイズは、1ブロックの容量を複数個に均等配分
- * したものです。たとえば、1ユニット32KBの場合、ブロック1つは32Byteのユ
- * ニットが、1024個集まって出来ていたり、64Byteのユニットが 512個集まって
- * 出来ていたりします。(padding,unit_head を除く)
- *
- * ・ブロック同士はリンクリスト(block_prev,block_next) でつながり、同じサイ
- * ズを持つブロック同士もリンクリスト(samesize_prev,samesize_nect) でつな
- * がっています。それにより、不要となったメモリの再利用が効率的に行えます。
- */
-
-/* ブロックに入るデータ量 */
-#define BLOCK_DATA_SIZE 80*1024
-
-/* 一度に確保するブロックの数。 */
-#define BLOCK_ALLOC 32
-
-/* ブロックのアライメント */
-#define BLOCK_ALIGNMENT 64
-
-/* ブロック */
-struct block {
- int block_no; /* ブロック番号 */
- struct block* block_prev; /* 前に確保した領域 */
- struct block* block_next; /* 次に確保した領域 */
- int samesize_no; /* 同じサイズの番号 */
- struct block* samesize_prev; /* 同じサイズの前の領域 */
- struct block* samesize_next; /* 同じサイズの次の領域 */
- size_t unit_size; /* ユニットのバイト数 0=未使用 */
- size_t unit_hash; /* ユニットのハッシュ */
- int unit_count; /* ユニットの数 */
- int unit_used; /* 使用済みユニット */
- char data[BLOCK_DATA_SIZE];
-};
-
-struct unit_head {
- struct block* block;
- size_t size;
- const char* file;
- int line;
- unsigned int checksum;
-};
-
-struct chunk {
- char *block;
- struct chunk *next;
-};
-
-static struct block* block_first = NULL;
-static struct block* block_last = NULL;
-static struct block* block_unused = NULL;
-
-/* ユニットへのハッシュ。80KB/64Byte = 1280個 */
-static struct block* unit_first[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最初 */
-static struct block* unit_unfill[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 埋まってない */
-static struct block* unit_last[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最後 */
-
-/* メモリを使い回せない領域用のデータ */
-struct unit_head_large {
- struct unit_head_large* prev;
- struct unit_head_large* next;
- struct unit_head unit_head;
-};
-static struct unit_head_large *unit_head_large_first = NULL;
-
-static struct chunk *chunk_first = NULL;
-
-static struct block* block_malloc(void);
-static void block_free(struct block* p);
-static void memmgr_info(void);
-static unsigned int memmgr_usage_bytes = 0;
-
-void* _mmalloc(size_t size, const char *file, int line, const char *func ) {
- int i;
- struct block *block;
- size_t size_hash;
-
- if (((long) size) < 0) {
- printf("_mmalloc: %d\n", size);
- return 0;
- }
-
- size_hash = (size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT;
- if(size == 0) {
- return NULL;
- }
- memmgr_usage_bytes += size;
-
- /* ブロック長を超える領域の確保には、malloc() を用いる */
- /* その際、unit_head.block に NULL を代入して区別する */
- if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) {
- struct unit_head_large* p = (struct unit_head_large*)MALLOC(sizeof(struct unit_head_large)+size,file,line,func);
- if(p != NULL) {
- p->unit_head.block = NULL;
- p->unit_head.size = size;
- p->unit_head.file = file;
- p->unit_head.line = line;
- p->prev = NULL;
- if (unit_head_large_first == NULL)
- p->next = NULL;
- else {
- unit_head_large_first->prev = p;
- p->next = unit_head_large_first;
- }
- unit_head_large_first = p;
- *(int*)((char*)p + sizeof(struct unit_head_large) - sizeof(int) + size) = 0xdeadbeaf;
- return (char *)p + sizeof(struct unit_head_large) - sizeof(int);
- } else {
- ShowFatalError("Memory manager::memmgr_alloc failed (allocating %d+%d bytes at %s:%d).\n", sizeof(struct unit_head_large), size, file, line);
- exit(1);
- }
- }
-
- /* 同一サイズのブロックが確保されていない時、新たに確保する */
- if(unit_unfill[size_hash] == NULL) {
- block = block_malloc();
- if(unit_first[size_hash] == NULL) {
- /* 初回確保 */
- unit_first[size_hash] = block;
- unit_last[size_hash] = block;
- block->samesize_no = 0;
- block->samesize_prev = NULL;
- block->samesize_next = NULL;
- } else {
- /* 連結作業 */
- unit_last[size_hash]->samesize_next = block;
- block->samesize_no = unit_last[size_hash]->samesize_no + 1;
- block->samesize_prev = unit_last[size_hash];
- block->samesize_next = NULL;
- unit_last[size_hash] = block;
- }
- unit_unfill[size_hash] = block;
- block->unit_size = size_hash * BLOCK_ALIGNMENT + sizeof(struct unit_head);
- block->unit_count = (int)(BLOCK_DATA_SIZE / block->unit_size);
- block->unit_used = 0;
- block->unit_hash = size_hash;
- /* 未使用Flagを立てる */
- for(i=0;i<block->unit_count;i++) {
- ((struct unit_head*)(&block->data[block->unit_size * i]))->block = NULL;
- }
- }
- /* ユニット使用個数加算 */
- block = unit_unfill[size_hash];
- block->unit_used++;
-
- /* ユニット内を全て使い果たした */
- if(block->unit_count == block->unit_used) {
- do {
- unit_unfill[size_hash] = unit_unfill[size_hash]->samesize_next;
- } while(
- unit_unfill[size_hash] != NULL &&
- unit_unfill[size_hash]->unit_count == unit_unfill[size_hash]->unit_used
- );
- }
-
- /* ブロックの中の空きユニット捜索 */
- for(i=0;i<block->unit_count;i++) {
- struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]);
- if(head->block == NULL) {
- head->block = block;
- head->size = size;
- head->line = line;
- head->file = file;
- *(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + size) = 0xdeadbeaf;
- return (char *)head + sizeof(struct unit_head) - sizeof(int);
- }
- }
- // ここに来てはいけない。
- ShowFatalError("Memory manager::memmgr_malloc() serious error (allocating %d+%d bytes at %s:%d)\n", sizeof(struct unit_head_large), size, file, line);
- memmgr_info();
- exit(1);
- return NULL;
-};
-
-void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func ) {
- void *p = _mmalloc(num * size,file,line,func);
- //malloc_set(p,0,num * size);
- malloc_set(p,0,num*size);
- return p;
-}
-
-void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func ) {
- size_t old_size;
- if(memblock == NULL) {
- return _mmalloc(size,file,line,func);
- }
-
- old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(int)))->size;
- if(old_size > size) {
- // サイズ縮小 -> そのまま返す(手抜き)
- return memblock;
- } else {
- // サイズ拡大
- void *p = _mmalloc(size,file,line,func);
- if(p != NULL) {
- memcpy(p,memblock,old_size);
- }
- _mfree(memblock,file,line,func);
- return p;
- }
-}
-
-char* _mstrdup(const char *p, const char *file, int line, const char *func ) {
- if(p == NULL) {
- return NULL;
- } else {
- size_t len = strlen(p);
- char *string = (char *)_mmalloc(len + 1,file,line,func);
- memcpy(string,p,len+1);
- return string;
- }
-}
-
-void _mfree(void *ptr, const char *file, int line, const char *func ) {
- struct unit_head *head;
- size_t size_hash;
-
- if (ptr == NULL)
- return;
-
- head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(int));
- size_hash = (head->size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT;
-
- if(head->block == NULL) {
- if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) {
- /* malloc() で直に確保された領域 */
- struct unit_head_large *head_large = (struct unit_head_large *)((char *)ptr - sizeof(struct unit_head_large) + sizeof(int));
- if(
- *(int*)((char*)head_large + sizeof(struct unit_head_large) - sizeof(int) + head->size)
- != 0xdeadbeaf)
- {
- ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line);
- }
- if(head_large->prev) {
- head_large->prev->next = head_large->next;
- } else {
- unit_head_large_first = head_large->next;
- }
- if(head_large->next) {
- head_large->next->prev = head_large->prev;
- }
- head->block = NULL;
- memmgr_usage_bytes -= head->size;
- FREE(head_large,file,line,func);
- } else {
- ShowError("Memory manager: args of aFree is freed pointer %s:%d@%s\n", file, line, func);
- }
- ptr = NULL;
- return;
- } else {
- /* ユニット解放 */
- struct block *block = head->block;
- if((unsigned long)block % sizeof(struct block) != 0) {
- ShowError("Memory manager: args of aFree is not valid pointer %s line %d\n", file, line);
- } else if(*(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + head->size) != 0xdeadbeaf) {
- ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line);
- } else {
- head->block = NULL;
- memmgr_usage_bytes -= head->size;
- if(--block->unit_used == 0) {
- /* ブロックの解放 */
- if(unit_unfill[block->unit_hash] == block) {
- /* 空きユニットに指定されている */
- do {
- unit_unfill[block->unit_hash] = unit_unfill[block->unit_hash]->samesize_next;
- } while(
- unit_unfill[block->unit_hash] != NULL &&
- unit_unfill[block->unit_hash]->unit_count == unit_unfill[block->unit_hash]->unit_used
- );
- }
- if(block->samesize_prev == NULL && block->samesize_next == NULL) {
- /* 独立ブロックの解放 */
- unit_first[block->unit_hash] = NULL;
- unit_last[block->unit_hash] = NULL;
- unit_unfill[block->unit_hash] = NULL;
- } else if(block->samesize_prev == NULL) {
- /* 先頭ブロックの解放 */
- unit_first[block->unit_hash] = block->samesize_next;
- (block->samesize_next)->samesize_prev = NULL;
- } else if(block->samesize_next == NULL) {
- /* 末端ブロックの解放 */
- unit_last[block->unit_hash] = block->samesize_prev;
- (block->samesize_prev)->samesize_next = NULL;
- } else {
- /* 中間ブロックの解放 */
- (block->samesize_next)->samesize_prev = block->samesize_prev;
- (block->samesize_prev)->samesize_next = block->samesize_next;
- }
- block_free(block);
- } else {
- /* 空きユニットの再設定 */
- if(
- unit_unfill[block->unit_hash] == NULL ||
- unit_unfill[block->unit_hash]->samesize_no > block->samesize_no
- ) {
- unit_unfill[block->unit_hash] = block;
- }
- }
- ptr = NULL;
- }
- }
-}
-
-/* 現在の状況を表示する */
-static void memmgr_info(void) {
- int i;
- struct block *p;
- ShowInfo("** Memory Manager Information **\n");
- if(block_first == NULL) {
- ShowMessage("Uninitialized.\n");
- return;
- }
- ShowMessage(
- "Blocks: %04u , BlockSize: %06u Byte , Used: %08uKB\n",
- block_last->block_no+1,sizeof(struct block),
- (block_last->block_no+1) * sizeof(struct block) / 1024
- );
- p = block_first;
- for(i=0;i<=block_last->block_no;i++) {
- ShowMessage(" Block #%04u : ",p->block_no);
- if(p->unit_size == 0) {
- ShowMessage("unused.\n");
- } else {
- ShowMessage(
- "size: %05u byte. used: %04u/%04u prev:",
- p->unit_size - sizeof(struct unit_head),p->unit_used,p->unit_count
- );
- if(p->samesize_prev == NULL) {
- ShowMessage("NULL");
- } else {
- ShowMessage("%04u",(p->samesize_prev)->block_no);
- }
- ShowMessage(" next:");
- if(p->samesize_next == NULL) {
- ShowMessage("NULL");
- } else {
- ShowMessage("%04u",(p->samesize_next)->block_no);
- }
- ShowMessage("\n");
- }
- p = p->block_next;
- }
-}
-
-/* ブロックを確保する */
-static struct block* block_malloc(void) {
- if(block_unused != NULL) {
- /* ブロック用の領域は確保済み */
- struct block* ret = block_unused;
- do {
- block_unused = block_unused->block_next;
- } while(block_unused != NULL && block_unused->unit_size != 0);
- return ret;
- } else {
- /* ブロック用の領域を新たに確保する */
- int i;
- int block_no;
- struct block* p;
- struct chunk* chunk;
- char *pb = (char *)CALLOC(sizeof(struct block),BLOCK_ALLOC+1,file,line,func);
- if(pb == NULL) {
- ShowFatalError("Memory manager::block_alloc failed.\n");
- exit(1);
- }
-
- // store original block address in chunk
- chunk = (struct chunk *)MALLOC(sizeof(struct chunk),file,line,func);
- if (chunk == NULL) {
- ShowFatalError("Memory manager::block_alloc failed.\n");
- exit(1);
- }
- chunk->block = pb;
- chunk->next = (chunk_first) ? chunk_first : NULL;
- chunk_first = chunk;
-
- // ブロックのポインタの先頭をsizeof(block) アライメントに揃える
- // このアドレスをfree() することはないので、直接ポインタを変更している。
- pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block));
- p = (struct block*)pb;
- if(block_first == NULL) {
- /* 初回確保 */
- block_no = 0;
- block_first = p;
- } else {
- block_no = block_last->block_no + 1;
- block_last->block_next = p;
- p->block_prev = block_last;
- }
- block_last = &p[BLOCK_ALLOC - 1];
- /* ブロックを連結させる */
- for(i=0;i<BLOCK_ALLOC;i++) {
- if(i != 0) {
- p[i].block_prev = &p[i-1];
- }
- if(i != BLOCK_ALLOC -1) {
- p[i].block_next = &p[i+1];
- }
- p[i].block_no = block_no + i;
- }
-
- /* 未使用ブロックへのポインタを更新 */
- block_unused = &p[1];
- p->unit_size = 1;
- return p;
- }
-}
-
-static void block_free(struct block* p) {
- /* free() せずに、未使用フラグを付けるだけ */
- p->unit_size = 0;
- /* 未使用ポインターを更新する */
- if(block_unused == NULL) {
- block_unused = p;
- } else if(block_unused->block_no > p->block_no) {
- block_unused = p;
- }
-}
-
-unsigned int memmgr_usage (void)
-{
- return memmgr_usage_bytes / 1024;
-}
-
-#ifdef LOG_MEMMGR
-static char memmer_logfile[128];
-static FILE *log_fp;
-
-static void memmgr_log (char *buf)
-{
- if (!log_fp) {
- log_fp = fopen(memmer_logfile,"w");
- if (!log_fp) log_fp = stdout;
- fprintf(log_fp, "Memory manager: Memory leaks found (Revision %s).\n", get_svn_revision());
- }
- fprintf(log_fp, buf);
- return;
-}
-#endif
-
-static void memmgr_final (void)
-{
- struct block *block = block_first;
- struct chunk *chunk = chunk_first, *chunk2;
- struct unit_head_large *large = unit_head_large_first, *large2;
- int i;
-
-#ifdef LOG_MEMMGR
- int count = 0;
- char buf[128];
-#endif
-
- while (block) {
- if (block->unit_size) {
- for (i = 0; i < block->unit_count; i++) {
- struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]);
- if(head->block != NULL)
- {
- #ifdef LOG_MEMMGR
- sprintf (buf,
- "%04d : %s line %d size %d\n", ++count,
- head->file, head->line, head->size);
- memmgr_log (buf);
- #endif
- // get block pointer and free it [celest]
- _mfree ((char *)head + sizeof(struct unit_head) - sizeof(int), ALC_MARK);
- }
- }
- }
- //if (block->block_no >= block2->block_no + BLOCK_ALLOC - 1) {
- // reached a new block array
- //block = block->block_next;
-
- /* Okay wise guys... this is how block2 was allocated: [Skotlex]
- struct block* p;
- char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1);
- pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block));
- p = (struct block*)pb;
-
- The reason we get an invalid pointer is that we allocated pb, not p.
- So how do you get pb when you only have p?
- The answer is, you can't, because the original pointer was lost when
- memory-aligning the block. So we either forget this FREE or use a
- self-reference...
- Since we are already quitting, it might be ok to just not free the block
- as it is.
- */
- // didn't realise that before o.o -- block chunks are now freed below [celest]
- // FREE(block2);
- //block2 = block;
- //continue;
- //}
- block = block->block_next;
- }
-
- // free the allocated block chunks
- chunk = chunk_first;
- while (chunk) {
- chunk2 = chunk->next;
- FREE(chunk->block,file,line,func);
- FREE(chunk,file,line,func);
- chunk = chunk2;
- }
-
- while(large) {
- large2 = large->next;
- #ifdef LOG_MEMMGR
- sprintf (buf,
- "%04d : %s line %d size %d\n", ++count,
- large->unit_head.file, large->unit_head.line, large->unit_head.size);
- memmgr_log (buf);
- #endif
- FREE(large,file,line,func);
- large = large2;
- }
-#ifdef LOG_MEMMGR
- if(count == 0) {
- ShowInfo("Memory manager: No memory leaks found.\n");
- } else {
- ShowWarning("Memory manager: Memory leaks found and fixed.\n");
- fclose(log_fp);
- }
-#endif
- return;
-}
-
-static void memmgr_init (void)
-{
- #ifdef LOG_MEMMGR
- sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME);
- ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile);
- #endif
- return;
-}
-#endif
-
-#if defined(MEMSET_TURBO) && defined(_WIN32)
- void malloc_set(void *dest, int value, int count){
- _asm
- {
- mov eax, value
- mov ecx, count
- mov ebx, ecx
- mov edi, dest
- shr ecx, 2
- test ecx, ecx
- jz ByteOp
- shl ecx, 2
- sub ebx, ecx
- shr ecx, 2
- rep stosd
- test ebx, ebx
- jz Done
- ByteOp:
- mov ecx, ebx
- rep stosb
- Done:
- }
- }
- // Sets 32-bit aligned memory.
- void malloc_tsetdword(void *dest, int value, int count){
- _asm
- {
- mov edi, dest
- mov ecx, count
- shr ecx, 2
- mov eax, value
- rep stosd
- }
- }
-
- // Sets 16-bit aligned memory.
- void malloc_tsetword(void *dest, short value, int count){
- _asm
- {
- mov edi, dest
- mov ecx, count
- shr ecx, 1
- mov ax, value
- rep stosw
- }
- }
-#endif
-
-/*======================================
- * Initialise
- *--------------------------------------
- */
-
-unsigned int malloc_usage (void)
-{
-#ifdef USE_MEMMGR
- return memmgr_usage ();
-#else
- return 0;
-#endif
-}
-
-void malloc_final (void)
-{
-#ifdef USE_MEMMGR
- memmgr_final ();
-#endif
- return;
-}
-
-void malloc_init (void)
-{
-#ifdef USE_MEMMGR
- memmgr_init ();
-#endif
- return;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../common/malloc.h"
+#include "../common/core.h"
+#include "../common/showmsg.h"
+
+#ifdef MINICORE
+ #undef LOG_MEMMGR
+#endif
+
+void* aMalloc_(size_t size, const char *file, int line, const char *func)
+{
+ void *ret = MALLOC(size, file, line, func);
+ // ShowMessage("%s:%d: in func %s: aMalloc %d\n",file,line,func,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: aMalloc error out of memory!\n",file,line,func);
+ exit(1);
+ }
+
+ return ret;
+}
+void* aMallocA_(size_t size, const char *file, int line, const char *func)
+{
+ void *ret = MALLOCA(size, file, line, func);
+ // ShowMessage("%s:%d: in func %s: aMallocA %d\n",file,line,func,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: aMallocA error out of memory!\n",file,line,func);
+ exit(1);
+ }
+
+ return ret;
+}
+void* aCalloc_(size_t num, size_t size, const char *file, int line, const char *func)
+{
+ void *ret = CALLOC(num, size, file, line, func);
+ // ShowMessage("%s:%d: in func %s: aCalloc %d %d\n",file,line,func,num,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: aCalloc error out of memory!\n", file, line, func);
+ exit(1);
+ }
+ return ret;
+}
+void* aCallocA_(size_t num, size_t size, const char *file, int line, const char *func)
+{
+ void *ret = CALLOCA(num, size, file, line, func);
+ // ShowMessage("%s:%d: in func %s: aCallocA %d %d\n",file,line,func,num,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: aCallocA error out of memory!\n",file,line,func);
+ exit(1);
+ }
+ return ret;
+}
+void* aRealloc_(void *p, size_t size, const char *file, int line, const char *func)
+{
+ void *ret = REALLOC(p, size, file, line, func);
+ // ShowMessage("%s:%d: in func %s: aRealloc %p %d\n",file,line,func,p,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: aRealloc error out of memory!\n",file,line,func);
+ exit(1);
+ }
+ return ret;
+}
+char* aStrdup_(const char *p, const char *file, int line, const char *func)
+{
+ char *ret = STRDUP(p, file, line, func);
+ // ShowMessage("%s:%d: in func %s: aStrdup %p\n",file,line,func,p);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: aStrdup error out of memory!\n", file, line, func);
+ exit(1);
+ }
+ return ret;
+}
+void aFree_(void *p, const char *file, int line, const char *func)
+{
+ // ShowMessage("%s:%d: in func %s: aFree %p\n",file,line,func,p);
+ if (p)
+ FREE(p, file, line, func);
+
+ p = NULL;
+}
+
+#ifdef GCOLLECT
+
+void* _bcallocA(size_t size, size_t cnt)
+{
+ void *ret = MALLOCA(size * cnt);
+ if (ret) //malloc_set(ret, 0, size * cnt);
+ malloc_set(ret, 0, size*cnt);
+ return ret;
+}
+void* _bcalloc(size_t size, size_t cnt)
+{
+ void *ret = MALLOC(size * cnt);
+ if (ret) //malloc_set(ret, 0, size * cnt);
+ malloc_set(ret, 0, size*cnt);
+ return ret;
+}
+char* _bstrdup(const char *chr)
+{
+ int len = strlen(chr);
+ char *ret = (char*)MALLOC(len + 1);
+ if (ret) memcpy(ret, chr, len + 1);
+ return ret;
+}
+
+#endif
+
+#ifdef USE_MEMMGR
+
+/* USE_MEMMGR */
+
+/*
+ * メモリマネージャ
+ * malloc , free の処理を効率的に出来るようにしたもの。
+ * 複雑な処理を行っているので、若干重くなるかもしれません。
+ *
+ * データ構造など(説明下手ですいません^^; )
+ * ・メモリを複数の「ブロック」に分けて、さらにブロックを複数の「ユニット」
+ * に分けています。ユニットのサイズは、1ブロックの容量を複数個に均等配分
+ * したものです。たとえば、1ユニット32KBの場合、ブロック1つは32Byteのユ
+ * ニットが、1024個集まって出来ていたり、64Byteのユニットが 512個集まって
+ * 出来ていたりします。(padding,unit_head を除く)
+ *
+ * ・ブロック同士はリンクリスト(block_prev,block_next) でつながり、同じサイ
+ * ズを持つブロック同士もリンクリスト(samesize_prev,samesize_nect) でつな
+ * がっています。それにより、不要となったメモリの再利用が効率的に行えます。
+ */
+
+/* ブロックに入るデータ量 */
+#define BLOCK_DATA_SIZE 80*1024
+
+/* 一度に確保するブロックの数。 */
+#define BLOCK_ALLOC 32
+
+/* ブロックのアライメント */
+#define BLOCK_ALIGNMENT 64
+
+/* ブロック */
+struct block {
+ int block_no; /* ブロック番号 */
+ struct block* block_prev; /* 前に確保した領域 */
+ struct block* block_next; /* 次に確保した領域 */
+ int samesize_no; /* 同じサイズの番号 */
+ struct block* samesize_prev; /* 同じサイズの前の領域 */
+ struct block* samesize_next; /* 同じサイズの次の領域 */
+ size_t unit_size; /* ユニットのバイト数 0=未使用 */
+ size_t unit_hash; /* ユニットのハッシュ */
+ int unit_count; /* ユニットの数 */
+ int unit_used; /* 使用済みユニット */
+ char data[BLOCK_DATA_SIZE];
+};
+
+struct unit_head {
+ struct block* block;
+ size_t size;
+ const char* file;
+ int line;
+ unsigned int checksum;
+};
+
+struct chunk {
+ char *block;
+ struct chunk *next;
+};
+
+static struct block* block_first = NULL;
+static struct block* block_last = NULL;
+static struct block* block_unused = NULL;
+
+/* ユニットへのハッシュ。80KB/64Byte = 1280個 */
+static struct block* unit_first[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最初 */
+static struct block* unit_unfill[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 埋まってない */
+static struct block* unit_last[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最後 */
+
+/* メモリを使い回せない領域用のデータ */
+struct unit_head_large {
+ struct unit_head_large* prev;
+ struct unit_head_large* next;
+ struct unit_head unit_head;
+};
+static struct unit_head_large *unit_head_large_first = NULL;
+
+static struct chunk *chunk_first = NULL;
+
+static struct block* block_malloc(void);
+static void block_free(struct block* p);
+static void memmgr_info(void);
+static unsigned int memmgr_usage_bytes = 0;
+
+void* _mmalloc(size_t size, const char *file, int line, const char *func ) {
+ int i;
+ struct block *block;
+ size_t size_hash;
+
+ if (((long) size) < 0) {
+ printf("_mmalloc: %d\n", size);
+ return 0;
+ }
+
+ size_hash = (size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT;
+ if(size == 0) {
+ return NULL;
+ }
+ memmgr_usage_bytes += size;
+
+ /* ブロック長を超える領域の確保には、malloc() を用いる */
+ /* その際、unit_head.block に NULL を代入して区別する */
+ if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) {
+ struct unit_head_large* p = (struct unit_head_large*)MALLOC(sizeof(struct unit_head_large)+size,file,line,func);
+ if(p != NULL) {
+ p->unit_head.block = NULL;
+ p->unit_head.size = size;
+ p->unit_head.file = file;
+ p->unit_head.line = line;
+ p->prev = NULL;
+ if (unit_head_large_first == NULL)
+ p->next = NULL;
+ else {
+ unit_head_large_first->prev = p;
+ p->next = unit_head_large_first;
+ }
+ unit_head_large_first = p;
+ *(int*)((char*)p + sizeof(struct unit_head_large) - sizeof(int) + size) = 0xdeadbeaf;
+ return (char *)p + sizeof(struct unit_head_large) - sizeof(int);
+ } else {
+ ShowFatalError("Memory manager::memmgr_alloc failed (allocating %d+%d bytes at %s:%d).\n", sizeof(struct unit_head_large), size, file, line);
+ exit(1);
+ }
+ }
+
+ /* 同一サイズのブロックが確保されていない時、新たに確保する */
+ if(unit_unfill[size_hash] == NULL) {
+ block = block_malloc();
+ if(unit_first[size_hash] == NULL) {
+ /* 初回確保 */
+ unit_first[size_hash] = block;
+ unit_last[size_hash] = block;
+ block->samesize_no = 0;
+ block->samesize_prev = NULL;
+ block->samesize_next = NULL;
+ } else {
+ /* 連結作業 */
+ unit_last[size_hash]->samesize_next = block;
+ block->samesize_no = unit_last[size_hash]->samesize_no + 1;
+ block->samesize_prev = unit_last[size_hash];
+ block->samesize_next = NULL;
+ unit_last[size_hash] = block;
+ }
+ unit_unfill[size_hash] = block;
+ block->unit_size = size_hash * BLOCK_ALIGNMENT + sizeof(struct unit_head);
+ block->unit_count = (int)(BLOCK_DATA_SIZE / block->unit_size);
+ block->unit_used = 0;
+ block->unit_hash = size_hash;
+ /* 未使用Flagを立てる */
+ for(i=0;i<block->unit_count;i++) {
+ ((struct unit_head*)(&block->data[block->unit_size * i]))->block = NULL;
+ }
+ }
+ /* ユニット使用個数加算 */
+ block = unit_unfill[size_hash];
+ block->unit_used++;
+
+ /* ユニット内を全て使い果たした */
+ if(block->unit_count == block->unit_used) {
+ do {
+ unit_unfill[size_hash] = unit_unfill[size_hash]->samesize_next;
+ } while(
+ unit_unfill[size_hash] != NULL &&
+ unit_unfill[size_hash]->unit_count == unit_unfill[size_hash]->unit_used
+ );
+ }
+
+ /* ブロックの中の空きユニット捜索 */
+ for(i=0;i<block->unit_count;i++) {
+ struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]);
+ if(head->block == NULL) {
+ head->block = block;
+ head->size = size;
+ head->line = line;
+ head->file = file;
+ *(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + size) = 0xdeadbeaf;
+ return (char *)head + sizeof(struct unit_head) - sizeof(int);
+ }
+ }
+ // ここに来てはいけない。
+ ShowFatalError("Memory manager::memmgr_malloc() serious error (allocating %d+%d bytes at %s:%d)\n", sizeof(struct unit_head_large), size, file, line);
+ memmgr_info();
+ exit(1);
+ return NULL;
+};
+
+void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func ) {
+ void *p = _mmalloc(num * size,file,line,func);
+ //malloc_set(p,0,num * size);
+ malloc_set(p,0,num*size);
+ return p;
+}
+
+void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func ) {
+ size_t old_size;
+ if(memblock == NULL) {
+ return _mmalloc(size,file,line,func);
+ }
+
+ old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(int)))->size;
+ if(old_size > size) {
+ // サイズ縮小 -> そのまま返す(手抜き)
+ return memblock;
+ } else {
+ // サイズ拡大
+ void *p = _mmalloc(size,file,line,func);
+ if(p != NULL) {
+ memcpy(p,memblock,old_size);
+ }
+ _mfree(memblock,file,line,func);
+ return p;
+ }
+}
+
+char* _mstrdup(const char *p, const char *file, int line, const char *func ) {
+ if(p == NULL) {
+ return NULL;
+ } else {
+ size_t len = strlen(p);
+ char *string = (char *)_mmalloc(len + 1,file,line,func);
+ memcpy(string,p,len+1);
+ return string;
+ }
+}
+
+void _mfree(void *ptr, const char *file, int line, const char *func ) {
+ struct unit_head *head;
+ size_t size_hash;
+
+ if (ptr == NULL)
+ return;
+
+ head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(int));
+ size_hash = (head->size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT;
+
+ if(head->block == NULL) {
+ if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) {
+ /* malloc() で直に確保された領域 */
+ struct unit_head_large *head_large = (struct unit_head_large *)((char *)ptr - sizeof(struct unit_head_large) + sizeof(int));
+ if(
+ *(int*)((char*)head_large + sizeof(struct unit_head_large) - sizeof(int) + head->size)
+ != 0xdeadbeaf)
+ {
+ ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line);
+ }
+ if(head_large->prev) {
+ head_large->prev->next = head_large->next;
+ } else {
+ unit_head_large_first = head_large->next;
+ }
+ if(head_large->next) {
+ head_large->next->prev = head_large->prev;
+ }
+ head->block = NULL;
+ memmgr_usage_bytes -= head->size;
+ FREE(head_large,file,line,func);
+ } else {
+ ShowError("Memory manager: args of aFree is freed pointer %s:%d@%s\n", file, line, func);
+ }
+ ptr = NULL;
+ return;
+ } else {
+ /* ユニット解放 */
+ struct block *block = head->block;
+ if((unsigned long)block % sizeof(struct block) != 0) {
+ ShowError("Memory manager: args of aFree is not valid pointer %s line %d\n", file, line);
+ } else if(*(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + head->size) != 0xdeadbeaf) {
+ ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line);
+ } else {
+ head->block = NULL;
+ memmgr_usage_bytes -= head->size;
+ if(--block->unit_used == 0) {
+ /* ブロックの解放 */
+ if(unit_unfill[block->unit_hash] == block) {
+ /* 空きユニットに指定されている */
+ do {
+ unit_unfill[block->unit_hash] = unit_unfill[block->unit_hash]->samesize_next;
+ } while(
+ unit_unfill[block->unit_hash] != NULL &&
+ unit_unfill[block->unit_hash]->unit_count == unit_unfill[block->unit_hash]->unit_used
+ );
+ }
+ if(block->samesize_prev == NULL && block->samesize_next == NULL) {
+ /* 独立ブロックの解放 */
+ unit_first[block->unit_hash] = NULL;
+ unit_last[block->unit_hash] = NULL;
+ unit_unfill[block->unit_hash] = NULL;
+ } else if(block->samesize_prev == NULL) {
+ /* 先頭ブロックの解放 */
+ unit_first[block->unit_hash] = block->samesize_next;
+ (block->samesize_next)->samesize_prev = NULL;
+ } else if(block->samesize_next == NULL) {
+ /* 末端ブロックの解放 */
+ unit_last[block->unit_hash] = block->samesize_prev;
+ (block->samesize_prev)->samesize_next = NULL;
+ } else {
+ /* 中間ブロックの解放 */
+ (block->samesize_next)->samesize_prev = block->samesize_prev;
+ (block->samesize_prev)->samesize_next = block->samesize_next;
+ }
+ block_free(block);
+ } else {
+ /* 空きユニットの再設定 */
+ if(
+ unit_unfill[block->unit_hash] == NULL ||
+ unit_unfill[block->unit_hash]->samesize_no > block->samesize_no
+ ) {
+ unit_unfill[block->unit_hash] = block;
+ }
+ }
+ ptr = NULL;
+ }
+ }
+}
+
+/* 現在の状況を表示する */
+static void memmgr_info(void) {
+ int i;
+ struct block *p;
+ ShowInfo("** Memory Manager Information **\n");
+ if(block_first == NULL) {
+ ShowMessage("Uninitialized.\n");
+ return;
+ }
+ ShowMessage(
+ "Blocks: %04u , BlockSize: %06u Byte , Used: %08uKB\n",
+ block_last->block_no+1,sizeof(struct block),
+ (block_last->block_no+1) * sizeof(struct block) / 1024
+ );
+ p = block_first;
+ for(i=0;i<=block_last->block_no;i++) {
+ ShowMessage(" Block #%04u : ",p->block_no);
+ if(p->unit_size == 0) {
+ ShowMessage("unused.\n");
+ } else {
+ ShowMessage(
+ "size: %05u byte. used: %04u/%04u prev:",
+ p->unit_size - sizeof(struct unit_head),p->unit_used,p->unit_count
+ );
+ if(p->samesize_prev == NULL) {
+ ShowMessage("NULL");
+ } else {
+ ShowMessage("%04u",(p->samesize_prev)->block_no);
+ }
+ ShowMessage(" next:");
+ if(p->samesize_next == NULL) {
+ ShowMessage("NULL");
+ } else {
+ ShowMessage("%04u",(p->samesize_next)->block_no);
+ }
+ ShowMessage("\n");
+ }
+ p = p->block_next;
+ }
+}
+
+/* ブロックを確保する */
+static struct block* block_malloc(void) {
+ if(block_unused != NULL) {
+ /* ブロック用の領域は確保済み */
+ struct block* ret = block_unused;
+ do {
+ block_unused = block_unused->block_next;
+ } while(block_unused != NULL && block_unused->unit_size != 0);
+ return ret;
+ } else {
+ /* ブロック用の領域を新たに確保する */
+ int i;
+ int block_no;
+ struct block* p;
+ struct chunk* chunk;
+ char *pb = (char *)CALLOC(sizeof(struct block),BLOCK_ALLOC+1,file,line,func);
+ if(pb == NULL) {
+ ShowFatalError("Memory manager::block_alloc failed.\n");
+ exit(1);
+ }
+
+ // store original block address in chunk
+ chunk = (struct chunk *)MALLOC(sizeof(struct chunk),file,line,func);
+ if (chunk == NULL) {
+ ShowFatalError("Memory manager::block_alloc failed.\n");
+ exit(1);
+ }
+ chunk->block = pb;
+ chunk->next = (chunk_first) ? chunk_first : NULL;
+ chunk_first = chunk;
+
+ // ブロックのポインタの先頭をsizeof(block) アライメントに揃える
+ // このアドレスをfree() することはないので、直接ポインタを変更している。
+ pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block));
+ p = (struct block*)pb;
+ if(block_first == NULL) {
+ /* 初回確保 */
+ block_no = 0;
+ block_first = p;
+ } else {
+ block_no = block_last->block_no + 1;
+ block_last->block_next = p;
+ p->block_prev = block_last;
+ }
+ block_last = &p[BLOCK_ALLOC - 1];
+ /* ブロックを連結させる */
+ for(i=0;i<BLOCK_ALLOC;i++) {
+ if(i != 0) {
+ p[i].block_prev = &p[i-1];
+ }
+ if(i != BLOCK_ALLOC -1) {
+ p[i].block_next = &p[i+1];
+ }
+ p[i].block_no = block_no + i;
+ }
+
+ /* 未使用ブロックへのポインタを更新 */
+ block_unused = &p[1];
+ p->unit_size = 1;
+ return p;
+ }
+}
+
+static void block_free(struct block* p) {
+ /* free() せずに、未使用フラグを付けるだけ */
+ p->unit_size = 0;
+ /* 未使用ポインターを更新する */
+ if(block_unused == NULL) {
+ block_unused = p;
+ } else if(block_unused->block_no > p->block_no) {
+ block_unused = p;
+ }
+}
+
+unsigned int memmgr_usage (void)
+{
+ return memmgr_usage_bytes / 1024;
+}
+
+#ifdef LOG_MEMMGR
+static char memmer_logfile[128];
+static FILE *log_fp;
+
+static void memmgr_log (char *buf)
+{
+ if (!log_fp) {
+ log_fp = fopen(memmer_logfile,"w");
+ if (!log_fp) log_fp = stdout;
+ fprintf(log_fp, "Memory manager: Memory leaks found (Revision %s).\n", get_svn_revision());
+ }
+ fprintf(log_fp, buf);
+ return;
+}
+#endif
+
+static void memmgr_final (void)
+{
+ struct block *block = block_first;
+ struct chunk *chunk = chunk_first, *chunk2;
+ struct unit_head_large *large = unit_head_large_first, *large2;
+ int i;
+
+#ifdef LOG_MEMMGR
+ int count = 0;
+ char buf[128];
+#endif
+
+ while (block) {
+ if (block->unit_size) {
+ for (i = 0; i < block->unit_count; i++) {
+ struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]);
+ if(head->block != NULL)
+ {
+ #ifdef LOG_MEMMGR
+ sprintf (buf,
+ "%04d : %s line %d size %d\n", ++count,
+ head->file, head->line, head->size);
+ memmgr_log (buf);
+ #endif
+ // get block pointer and free it [celest]
+ _mfree ((char *)head + sizeof(struct unit_head) - sizeof(int), ALC_MARK);
+ }
+ }
+ }
+ //if (block->block_no >= block2->block_no + BLOCK_ALLOC - 1) {
+ // reached a new block array
+ //block = block->block_next;
+
+ /* Okay wise guys... this is how block2 was allocated: [Skotlex]
+ struct block* p;
+ char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1);
+ pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block));
+ p = (struct block*)pb;
+
+ The reason we get an invalid pointer is that we allocated pb, not p.
+ So how do you get pb when you only have p?
+ The answer is, you can't, because the original pointer was lost when
+ memory-aligning the block. So we either forget this FREE or use a
+ self-reference...
+ Since we are already quitting, it might be ok to just not free the block
+ as it is.
+ */
+ // didn't realise that before o.o -- block chunks are now freed below [celest]
+ // FREE(block2);
+ //block2 = block;
+ //continue;
+ //}
+ block = block->block_next;
+ }
+
+ // free the allocated block chunks
+ chunk = chunk_first;
+ while (chunk) {
+ chunk2 = chunk->next;
+ FREE(chunk->block,file,line,func);
+ FREE(chunk,file,line,func);
+ chunk = chunk2;
+ }
+
+ while(large) {
+ large2 = large->next;
+ #ifdef LOG_MEMMGR
+ sprintf (buf,
+ "%04d : %s line %d size %d\n", ++count,
+ large->unit_head.file, large->unit_head.line, large->unit_head.size);
+ memmgr_log (buf);
+ #endif
+ FREE(large,file,line,func);
+ large = large2;
+ }
+#ifdef LOG_MEMMGR
+ if(count == 0) {
+ ShowInfo("Memory manager: No memory leaks found.\n");
+ } else {
+ ShowWarning("Memory manager: Memory leaks found and fixed.\n");
+ fclose(log_fp);
+ }
+#endif
+ return;
+}
+
+static void memmgr_init (void)
+{
+ #ifdef LOG_MEMMGR
+ sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME);
+ ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile);
+ #endif
+ return;
+}
+#endif
+
+#if defined(MEMSET_TURBO) && defined(_WIN32)
+ void malloc_set(void *dest, int value, int count){
+ _asm
+ {
+ mov eax, value
+ mov ecx, count
+ mov ebx, ecx
+ mov edi, dest
+ shr ecx, 2
+ test ecx, ecx
+ jz ByteOp
+ shl ecx, 2
+ sub ebx, ecx
+ shr ecx, 2
+ rep stosd
+ test ebx, ebx
+ jz Done
+ ByteOp:
+ mov ecx, ebx
+ rep stosb
+ Done:
+ }
+ }
+ // Sets 32-bit aligned memory.
+ void malloc_tsetdword(void *dest, int value, int count){
+ _asm
+ {
+ mov edi, dest
+ mov ecx, count
+ shr ecx, 2
+ mov eax, value
+ rep stosd
+ }
+ }
+
+ // Sets 16-bit aligned memory.
+ void malloc_tsetword(void *dest, short value, int count){
+ _asm
+ {
+ mov edi, dest
+ mov ecx, count
+ shr ecx, 1
+ mov ax, value
+ rep stosw
+ }
+ }
+#endif
+
+/*======================================
+ * Initialise
+ *--------------------------------------
+ */
+
+unsigned int malloc_usage (void)
+{
+#ifdef USE_MEMMGR
+ return memmgr_usage ();
+#else
+ return 0;
+#endif
+}
+
+void malloc_final (void)
+{
+#ifdef USE_MEMMGR
+ memmgr_final ();
+#endif
+ return;
+}
+
+void malloc_init (void)
+{
+#ifdef USE_MEMMGR
+ memmgr_init ();
+#endif
+ return;
+}
diff --git a/src/common/malloc.h b/src/common/malloc.h
index f65593eb1..503eba75b 100644
--- a/src/common/malloc.h
+++ b/src/common/malloc.h
@@ -1,178 +1,178 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _MALLOC_H_
-#define _MALLOC_H_
-// Q: What are the 'a'-variant allocation functions?
-// A: They allocate memory from the stack, which is automatically
-// freed when the invoking function returns.
-// But it's not portable (http://c-faq.com/malloc/alloca.html)
-// and I have doubts our implementation works.
-// -> They should NOT be used, period.
-
-#define MEMSET_TURBO
-
-#ifndef __NETBSD__
-#if __STDC_VERSION__ < 199901L
-# if __GNUC__ >= 2
-# define __func__ __FUNCTION__
-# else
-# define __func__ ""
-# endif
-#endif
-#endif
-#define ALC_MARK __FILE__, __LINE__, __func__
-
-//////////////////////////////////////////////////////////////////////
-// Whether to use Athena's built-in Memory Manager (enabled by default)
-// To disable just comment the following line
-#if !defined(DMALLOC) && !defined(BCHECK)
- #define USE_MEMMGR
-#endif
-// Whether to enable Memory Manager's logging
-#define LOG_MEMMGR
-
-#ifdef USE_MEMMGR
-
-# define aMalloc(n) _mmalloc(n,ALC_MARK)
-# define aMallocA(n) _mmalloc(n,ALC_MARK)
-# define aCalloc(m,n) _mcalloc(m,n,ALC_MARK)
-# define aCallocA(m,n) _mcalloc(m,n,ALC_MARK)
-# define aRealloc(p,n) _mrealloc(p,n,ALC_MARK)
-# define aStrdup(p) _mstrdup(p,ALC_MARK)
-# define aFree(p) _mfree(p,ALC_MARK)
-
- void* _mmalloc (size_t size, const char *file, int line, const char *func);
- void* _mcalloc (size_t num, size_t size, const char *file, int line, const char *func);
- void* _mrealloc (void *p, size_t size, const char *file, int line, const char *func);
- char* _mstrdup (const char *p, const char *file, int line, const char *func);
- void _mfree (void *p, const char *file, int line, const char *func);
-
-#else
-
-# define aMalloc(n) aMalloc_((n),ALC_MARK)
-# define aMallocA(n) aMallocA_((n),ALC_MARK)
-# define aCalloc(m,n) aCalloc_((m),(n),ALC_MARK)
-# define aCallocA(m,n) aCallocA_(m,n,ALC_MARK)
-# define aRealloc(p,n) aRealloc_(p,n,ALC_MARK)
-# define aStrdup(p) aStrdup_(p,ALC_MARK)
-# define aFree(p) aFree_(p,ALC_MARK)
-
- void* aMalloc_ (size_t size, const char *file, int line, const char *func);
- void* aMallocA_ (size_t size, const char *file, int line, const char *func);
- void* aCalloc_ (size_t num, size_t size, const char *file, int line, const char *func);
- void* aCallocA_ (size_t num, size_t size, const char *file, int line, const char *func);
- void* aRealloc_ (void *p, size_t size, const char *file, int line, const char *func);
- char* aStrdup_ (const char *p, const char *file, int line, const char *func);
- void aFree_ (void *p, const char *file, int line, const char *func);
-
-#endif
-
-////////////// Memory Managers //////////////////
-
-#ifdef MEMWATCH
-
-# include "memwatch.h"
-# define MALLOC(n,file,line,func) mwMalloc((n),(file),(line))
-# define MALLOCA(n,file,line,func) mwMalloc((n),(file),(line))
-# define CALLOC(m,n,file,line,func) mwCalloc((m),(n),(file),(line))
-# define CALLOCA(m,n,file,line,func) mwCalloc((m),(n),(file),(line))
-# define REALLOC(p,n,file,line,func) mwRealloc((p),(n),(file),(line))
-# define STRDUP(p,file,line,func) mwStrdup((p),(file),(line))
-# define FREE(p,file,line,func) mwFree((p),(file),(line))
-
-#elif defined(DMALLOC)
-
-# include "dmalloc.h"
-# define MALLOC(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0)
-# define MALLOCA(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0)
-# define CALLOC(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0)
-# define CALLOCA(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0)
-# define REALLOC(p,n,file,line,func) dmalloc_realloc((file),(line),(p),(n),DMALLOC_FUNC_REALLOC,0)
-# define STRDUP(p,file,line,func) strdup(p)
-# define FREE(p,file,line,func) free(p)
-
-#elif defined(GCOLLECT)
-
-# include "gc.h"
-# define MALLOC(n,file,line,func) GC_MALLOC(n)
-# define MALLOCA(n,file,line,func) GC_MALLOC_ATOMIC(n)
-# define CALLOC(m,n,file,line,func) _bcalloc((m),(n))
-# define CALLOCA(m,n,file,line,func) _bcallocA((m),(n))
-# define REALLOC(p,n,file,line,func) GC_REALLOC((p),(n))
-# define STRDUP(p,file,line,func) _bstrdup(p)
-# define FREE(p,file,line,func) GC_FREE(p)
-
- void * _bcalloc(size_t, size_t);
- void * _bcallocA(size_t, size_t);
- char * _bstrdup(const char *);
-
-/* FIXME Why is this the same as #else? [FlavioJS]
-#elif defined(BCHECK)
-
-# define MALLOC(n,file,line,func) malloc(n)
-# define MALLOCA(n,file,line,func) malloc(n)
-# define CALLOC(m,n,file,line,func) calloc((m),(n))
-# define CALLOCA(m,n,file,line,func) calloc((m),(n))
-# define REALLOC(p,n,file,line,func) realloc((p),(n))
-# define STRDUP(p,file,line,func) strdup(p)
-# define FREE(p,file,line,func) free(p)
-*/
-#else
-
-# define MALLOC(n,file,line,func) malloc(n)
-# define MALLOCA(n,file,line,func) malloc(n)
-# define CALLOC(m,n,file,line,func) calloc((m),(n))
-# define CALLOCA(m,n,file,line,func) calloc((m),(n))
-# define REALLOC(p,n,file,line,func) realloc((p),(n))
-# define STRDUP(p,file,line,func) strdup(p)
-# define FREE(p,file,line,func) free(p)
-
-#endif
-
-/////////////// Buffer Creation /////////////////
-// Full credit for this goes to Shinomori [Ajarn]
-
-#ifdef __GNUC__ // GCC has variable length arrays
-
- #define CREATE_BUFFER(name, type, size) type name[size]
- #define DELETE_BUFFER(name)
-
-#else // others don't, so we emulate them
-
- #define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc (size, sizeof(type))
- #define DELETE_BUFFER(name) aFree(name)
-
-#endif
-
-////////////// Others //////////////////////////
-// should be merged with any of above later
-#define CREATE(result, type, number) (result) = (type *) aCalloc ((number), sizeof(type));
-
-#define CREATE_A(result, type, number) (result) = (type *) aCallocA ((number), sizeof(type));
-
-#define RECREATE(result, type, number) (result) = (type *) aRealloc ((result), sizeof(type) * (number));
-
-////////////////////////////////////////////////
-
-unsigned int malloc_usage (void);
-#ifndef INLINE
- #ifdef _WIN32
- #define INLINE
- #else
- #define INLINE inline
- #endif
-#endif
-#if defined(MEMSET_TURBO) && defined(_WIN32)
- INLINE void malloc_set(void *, int, int);
- INLINE void malloc_tsetdword(void *, int, int);
- INLINE void malloc_tsetword(void *, short, int);
-#else
- #define malloc_set(x,y,z) memset(x,y,z)
- #define malloc_tsetdword(x,y,z) memset(x,y,z)
- #define malloc_tsetword(x,y,z) memset(x,y,z)
-#endif
-void malloc_init (void);
-void malloc_final (void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MALLOC_H_
+#define _MALLOC_H_
+// Q: What are the 'a'-variant allocation functions?
+// A: They allocate memory from the stack, which is automatically
+// freed when the invoking function returns.
+// But it's not portable (http://c-faq.com/malloc/alloca.html)
+// and I have doubts our implementation works.
+// -> They should NOT be used, period.
+
+#define MEMSET_TURBO
+
+#ifndef __NETBSD__
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ ""
+# endif
+#endif
+#endif
+#define ALC_MARK __FILE__, __LINE__, __func__
+
+//////////////////////////////////////////////////////////////////////
+// Whether to use Athena's built-in Memory Manager (enabled by default)
+// To disable just comment the following line
+#if !defined(DMALLOC) && !defined(BCHECK)
+ #define USE_MEMMGR
+#endif
+// Whether to enable Memory Manager's logging
+#define LOG_MEMMGR
+
+#ifdef USE_MEMMGR
+
+# define aMalloc(n) _mmalloc(n,ALC_MARK)
+# define aMallocA(n) _mmalloc(n,ALC_MARK)
+# define aCalloc(m,n) _mcalloc(m,n,ALC_MARK)
+# define aCallocA(m,n) _mcalloc(m,n,ALC_MARK)
+# define aRealloc(p,n) _mrealloc(p,n,ALC_MARK)
+# define aStrdup(p) _mstrdup(p,ALC_MARK)
+# define aFree(p) _mfree(p,ALC_MARK)
+
+ void* _mmalloc (size_t size, const char *file, int line, const char *func);
+ void* _mcalloc (size_t num, size_t size, const char *file, int line, const char *func);
+ void* _mrealloc (void *p, size_t size, const char *file, int line, const char *func);
+ char* _mstrdup (const char *p, const char *file, int line, const char *func);
+ void _mfree (void *p, const char *file, int line, const char *func);
+
+#else
+
+# define aMalloc(n) aMalloc_((n),ALC_MARK)
+# define aMallocA(n) aMallocA_((n),ALC_MARK)
+# define aCalloc(m,n) aCalloc_((m),(n),ALC_MARK)
+# define aCallocA(m,n) aCallocA_(m,n,ALC_MARK)
+# define aRealloc(p,n) aRealloc_(p,n,ALC_MARK)
+# define aStrdup(p) aStrdup_(p,ALC_MARK)
+# define aFree(p) aFree_(p,ALC_MARK)
+
+ void* aMalloc_ (size_t size, const char *file, int line, const char *func);
+ void* aMallocA_ (size_t size, const char *file, int line, const char *func);
+ void* aCalloc_ (size_t num, size_t size, const char *file, int line, const char *func);
+ void* aCallocA_ (size_t num, size_t size, const char *file, int line, const char *func);
+ void* aRealloc_ (void *p, size_t size, const char *file, int line, const char *func);
+ char* aStrdup_ (const char *p, const char *file, int line, const char *func);
+ void aFree_ (void *p, const char *file, int line, const char *func);
+
+#endif
+
+////////////// Memory Managers //////////////////
+
+#ifdef MEMWATCH
+
+# include "memwatch.h"
+# define MALLOC(n,file,line,func) mwMalloc((n),(file),(line))
+# define MALLOCA(n,file,line,func) mwMalloc((n),(file),(line))
+# define CALLOC(m,n,file,line,func) mwCalloc((m),(n),(file),(line))
+# define CALLOCA(m,n,file,line,func) mwCalloc((m),(n),(file),(line))
+# define REALLOC(p,n,file,line,func) mwRealloc((p),(n),(file),(line))
+# define STRDUP(p,file,line,func) mwStrdup((p),(file),(line))
+# define FREE(p,file,line,func) mwFree((p),(file),(line))
+
+#elif defined(DMALLOC)
+
+# include "dmalloc.h"
+# define MALLOC(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0)
+# define MALLOCA(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0)
+# define CALLOC(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0)
+# define CALLOCA(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0)
+# define REALLOC(p,n,file,line,func) dmalloc_realloc((file),(line),(p),(n),DMALLOC_FUNC_REALLOC,0)
+# define STRDUP(p,file,line,func) strdup(p)
+# define FREE(p,file,line,func) free(p)
+
+#elif defined(GCOLLECT)
+
+# include "gc.h"
+# define MALLOC(n,file,line,func) GC_MALLOC(n)
+# define MALLOCA(n,file,line,func) GC_MALLOC_ATOMIC(n)
+# define CALLOC(m,n,file,line,func) _bcalloc((m),(n))
+# define CALLOCA(m,n,file,line,func) _bcallocA((m),(n))
+# define REALLOC(p,n,file,line,func) GC_REALLOC((p),(n))
+# define STRDUP(p,file,line,func) _bstrdup(p)
+# define FREE(p,file,line,func) GC_FREE(p)
+
+ void * _bcalloc(size_t, size_t);
+ void * _bcallocA(size_t, size_t);
+ char * _bstrdup(const char *);
+
+/* FIXME Why is this the same as #else? [FlavioJS]
+#elif defined(BCHECK)
+
+# define MALLOC(n,file,line,func) malloc(n)
+# define MALLOCA(n,file,line,func) malloc(n)
+# define CALLOC(m,n,file,line,func) calloc((m),(n))
+# define CALLOCA(m,n,file,line,func) calloc((m),(n))
+# define REALLOC(p,n,file,line,func) realloc((p),(n))
+# define STRDUP(p,file,line,func) strdup(p)
+# define FREE(p,file,line,func) free(p)
+*/
+#else
+
+# define MALLOC(n,file,line,func) malloc(n)
+# define MALLOCA(n,file,line,func) malloc(n)
+# define CALLOC(m,n,file,line,func) calloc((m),(n))
+# define CALLOCA(m,n,file,line,func) calloc((m),(n))
+# define REALLOC(p,n,file,line,func) realloc((p),(n))
+# define STRDUP(p,file,line,func) strdup(p)
+# define FREE(p,file,line,func) free(p)
+
+#endif
+
+/////////////// Buffer Creation /////////////////
+// Full credit for this goes to Shinomori [Ajarn]
+
+#ifdef __GNUC__ // GCC has variable length arrays
+
+ #define CREATE_BUFFER(name, type, size) type name[size]
+ #define DELETE_BUFFER(name)
+
+#else // others don't, so we emulate them
+
+ #define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc (size, sizeof(type))
+ #define DELETE_BUFFER(name) aFree(name)
+
+#endif
+
+////////////// Others //////////////////////////
+// should be merged with any of above later
+#define CREATE(result, type, number) (result) = (type *) aCalloc ((number), sizeof(type));
+
+#define CREATE_A(result, type, number) (result) = (type *) aCallocA ((number), sizeof(type));
+
+#define RECREATE(result, type, number) (result) = (type *) aRealloc ((result), sizeof(type) * (number));
+
+////////////////////////////////////////////////
+
+unsigned int malloc_usage (void);
+#ifndef INLINE
+ #ifdef _WIN32
+ #define INLINE
+ #else
+ #define INLINE inline
+ #endif
+#endif
+#if defined(MEMSET_TURBO) && defined(_WIN32)
+ INLINE void malloc_set(void *, int, int);
+ INLINE void malloc_tsetdword(void *, int, int);
+ INLINE void malloc_tsetword(void *, short, int);
+#else
+ #define malloc_set(x,y,z) memset(x,y,z)
+ #define malloc_tsetdword(x,y,z) memset(x,y,z)
+ #define malloc_tsetword(x,y,z) memset(x,y,z)
+#endif
+void malloc_init (void);
+void malloc_final (void);
+
+#endif
diff --git a/src/common/nullpo.c b/src/common/nullpo.c
index 8508f1333..931846bd6 100644
--- a/src/common/nullpo.c
+++ b/src/common/nullpo.c
@@ -1,94 +1,94 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include "nullpo.h"
-#include "../common/showmsg.h"
-// #include "logs.h" // 布石してみる
-
-static void nullpo_info_core(const char *file, int line, const char *func,
- const char *fmt, va_list ap);
-
-/*======================================
- * Nullチェック 及び 情報出力
- *--------------------------------------
- */
-int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
- const char *fmt, ...)
-{
- va_list ap;
-
- if (target != NULL)
- return 0;
-
- va_start(ap, fmt);
- nullpo_info_core(file, line, func, fmt, ap);
- va_end(ap);
- return 1;
-}
-
-int nullpo_chk(const char *file, int line, const char *func, const void *target)
-{
- if (target != NULL)
- return 0;
-
- nullpo_info_core(file, line, func, NULL, NULL);
- return 1;
-}
-
-
-/*======================================
- * nullpo情報出力(外部呼出し向けラッパ)
- *--------------------------------------
- */
-void nullpo_info_f(const char *file, int line, const char *func,
- const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- nullpo_info_core(file, line, func, fmt, ap);
- va_end(ap);
-}
-
-void nullpo_info(const char *file, int line, const char *func)
-{
- nullpo_info_core(file, line, func, NULL, NULL);
-}
-
-
-/*======================================
- * nullpo情報出力(Main)
- *--------------------------------------
- */
-static void nullpo_info_core(const char *file, int line, const char *func,
- const char *fmt, va_list ap)
-{
- if (file == NULL)
- file = "??";
-
- func =
- func == NULL ? "unknown":
- func[0] == '\0' ? "unknown":
- func;
-
- ShowMessage("--- nullpo info --------------------------------------------\n");
- ShowMessage("%s:%d: in func `%s'\n", file, line, func);
- if (fmt != NULL)
- {
- if (fmt[0] != '\0')
- {
- vprintf(fmt, ap);
-
- // 最後に改行したか確認
- if (fmt[strlen(fmt)-1] != '\n')
- ShowMessage("\n");
- }
- }
- ShowMessage("--- end nullpo info ----------------------------------------\n");
-
- // ここらでnullpoログをファイルに書き出せたら
- // まとめて提出できるなと思っていたり。
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "nullpo.h"
+#include "../common/showmsg.h"
+// #include "logs.h" // 布石してみる
+
+static void nullpo_info_core(const char *file, int line, const char *func,
+ const char *fmt, va_list ap);
+
+/*======================================
+ * Nullチェック 及び 情報出力
+ *--------------------------------------
+ */
+int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (target != NULL)
+ return 0;
+
+ va_start(ap, fmt);
+ nullpo_info_core(file, line, func, fmt, ap);
+ va_end(ap);
+ return 1;
+}
+
+int nullpo_chk(const char *file, int line, const char *func, const void *target)
+{
+ if (target != NULL)
+ return 0;
+
+ nullpo_info_core(file, line, func, NULL, NULL);
+ return 1;
+}
+
+
+/*======================================
+ * nullpo情報出力(外部呼出し向けラッパ)
+ *--------------------------------------
+ */
+void nullpo_info_f(const char *file, int line, const char *func,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ nullpo_info_core(file, line, func, fmt, ap);
+ va_end(ap);
+}
+
+void nullpo_info(const char *file, int line, const char *func)
+{
+ nullpo_info_core(file, line, func, NULL, NULL);
+}
+
+
+/*======================================
+ * nullpo情報出力(Main)
+ *--------------------------------------
+ */
+static void nullpo_info_core(const char *file, int line, const char *func,
+ const char *fmt, va_list ap)
+{
+ if (file == NULL)
+ file = "??";
+
+ func =
+ func == NULL ? "unknown":
+ func[0] == '\0' ? "unknown":
+ func;
+
+ ShowMessage("--- nullpo info --------------------------------------------\n");
+ ShowMessage("%s:%d: in func `%s'\n", file, line, func);
+ if (fmt != NULL)
+ {
+ if (fmt[0] != '\0')
+ {
+ vprintf(fmt, ap);
+
+ // 最後に改行したか確認
+ if (fmt[strlen(fmt)-1] != '\n')
+ ShowMessage("\n");
+ }
+ }
+ ShowMessage("--- end nullpo info ----------------------------------------\n");
+
+ // ここらでnullpoログをファイルに書き出せたら
+ // まとめて提出できるなと思っていたり。
+}
diff --git a/src/common/nullpo.h b/src/common/nullpo.h
index 66d984224..d5ccb8f95 100644
--- a/src/common/nullpo.h
+++ b/src/common/nullpo.h
@@ -1,237 +1,237 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _NULLPO_H_
-#define _NULLPO_H_
-
-
-#define NULLPO_CHECK 1
- // 全体のスイッチを宣言しているヘッダがあれば
- // そこに移動していただけると
-
-#ifndef __NETBSD__
-#if __STDC_VERSION__ < 199901L
-# if __GNUC__ >= 2
-# define __func__ __FUNCTION__
-# else
-# define __func__ ""
-# endif
-#endif
-#endif
-
-#ifdef _WIN32
-#define __attribute__(x) /* nothing */
-#endif
-
-
-#define NLP_MARK __FILE__, __LINE__, __func__
-
-/*----------------------------------------------------------------------------
- * Macros
- *----------------------------------------------------------------------------
- */
-/*======================================
- * Nullチェック 及び 情報出力後 return
- *・展開するとifとかreturn等が出るので
- * 一行単体で使ってください。
- *・nullpo_ret(x = func());
- * のような使用法も想定しています。
- *--------------------------------------
- * nullpo_ret(t)
- * 戻り値 0固定
- * [引数]
- * t チェック対象
- *--------------------------------------
- * nullpo_retv(t)
- * 戻り値 なし
- * [引数]
- * t チェック対象
- *--------------------------------------
- * nullpo_retr(ret, t)
- * 戻り値 指定
- * [引数]
- * ret return(ret);
- * t チェック対象
- *--------------------------------------
- * nullpo_ret_f(t, fmt, ...)
- * 詳細情報出力用
- * 戻り値 0
- * [引数]
- * t チェック対象
- * fmt ... vprintfに渡される
- * 備考や関係変数の書き出しなどに
- *--------------------------------------
- * nullpo_retv_f(t, fmt, ...)
- * 詳細情報出力用
- * 戻り値 なし
- * [引数]
- * t チェック対象
- * fmt ... vprintfに渡される
- * 備考や関係変数の書き出しなどに
- *--------------------------------------
- * nullpo_retr_f(ret, t, fmt, ...)
- * 詳細情報出力用
- * 戻り値 指定
- * [引数]
- * ret return(ret);
- * t チェック対象
- * fmt ... vprintfに渡される
- * 備考や関係変数の書き出しなどに
- *--------------------------------------
- */
-
-#if NULLPO_CHECK
-
-#define nullpo_ret(t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {return(0);}
-
-#define nullpo_retv(t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {return;}
-
-#define nullpo_retr(ret, t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {return(ret);}
-
-#define nullpo_retb(t) \
- if (nullpo_chk(NLP_MARK, (void *)(t))) {break;}
-
-// 可変引数マクロに関する条件コンパイル
-#if __STDC_VERSION__ >= 199901L
-/* C99に対応 */
-#define nullpo_ret_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(0);}
-
-#define nullpo_retv_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return;}
-
-#define nullpo_retr_f(ret, t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(ret);}
-
-#define nullpo_retb_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {break;}
-
-#elif __GNUC__ >= 2
-/* GCC用 */
-#define nullpo_ret_f(t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(0);}
-
-#define nullpo_retv_f(t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return;}
-
-#define nullpo_retr_f(ret, t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(ret);}
-
-#define nullpo_retb_f(t, fmt, args...) \
- if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {break;}
-
-#else
-
-/* その他の場合・・・ orz */
-
-#endif
-
-#else /* NULLPO_CHECK */
-/* No Nullpo check */
-
-// if((t)){;}
-// 良い方法が思いつかなかったので・・・苦肉の策です。
-// 一応ワーニングは出ないはず
-
-#define nullpo_ret(t) if((t)){;}
-#define nullpo_retv(t) if((t)){;}
-#define nullpo_retr(ret, t) if((t)){;}
-#define nullpo_retb(t) if((t)){;}
-
-// 可変引数マクロに関する条件コンパイル
-#if __STDC_VERSION__ >= 199901L
-/* C99に対応 */
-#define nullpo_ret_f(t, fmt, ...) if((t)){;}
-#define nullpo_retv_f(t, fmt, ...) if((t)){;}
-#define nullpo_retr_f(ret, t, fmt, ...) if((t)){;}
-#define nullpo_retb_f(t, fmt, ...) if((t)){;}
-
-#elif __GNUC__ >= 2
-/* GCC用 */
-#define nullpo_ret_f(t, fmt, args...) if((t)){;}
-#define nullpo_retv_f(t, fmt, args...) if((t)){;}
-#define nullpo_retr_f(ret, t, fmt, args...) if((t)){;}
-#define nullpo_retb_f(t, fmt, args...) if((t)){;}
-
-#else
-/* その他の場合・・・ orz */
-#endif
-
-#endif /* NULLPO_CHECK */
-
-/*----------------------------------------------------------------------------
- * Functions
- *----------------------------------------------------------------------------
- */
-/*======================================
- * nullpo_chk
- * Nullチェック 及び 情報出力
- * [引数]
- * file __FILE__
- * line __LINE__
- * func __func__ (関数名)
- * これらには NLP_MARK を使うとよい
- * target チェック対象
- * [返り値]
- * 0 OK
- * 1 NULL
- *--------------------------------------
- */
-int nullpo_chk(const char *file, int line, const char *func, const void *target);
-
-
-/*======================================
- * nullpo_chk_f
- * Nullチェック 及び 詳細な情報出力
- * [引数]
- * file __FILE__
- * line __LINE__
- * func __func__ (関数名)
- * これらには NLP_MARK を使うとよい
- * target チェック対象
- * fmt ... vprintfに渡される
- * 備考や関係変数の書き出しなどに
- * [返り値]
- * 0 OK
- * 1 NULL
- *--------------------------------------
- */
-int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
- const char *fmt, ...)
- __attribute__((format(printf,5,6)));
-
-
-/*======================================
- * nullpo_info
- * nullpo情報出力
- * [引数]
- * file __FILE__
- * line __LINE__
- * func __func__ (関数名)
- * これらには NLP_MARK を使うとよい
- *--------------------------------------
- */
-void nullpo_info(const char *file, int line, const char *func);
-
-
-/*======================================
- * nullpo_info_f
- * nullpo詳細情報出力
- * [引数]
- * file __FILE__
- * line __LINE__
- * func __func__ (関数名)
- * これらには NLP_MARK を使うとよい
- * fmt ... vprintfに渡される
- * 備考や関係変数の書き出しなどに
- *--------------------------------------
- */
-void nullpo_info_f(const char *file, int line, const char *func,
- const char *fmt, ...)
- __attribute__((format(printf,4,5)));
-
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _NULLPO_H_
+#define _NULLPO_H_
+
+
+#define NULLPO_CHECK 1
+ // 全体のスイッチを宣言しているヘッダがあれば
+ // そこに移動していただけると
+
+#ifndef __NETBSD__
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ ""
+# endif
+#endif
+#endif
+
+#ifdef _WIN32
+#define __attribute__(x) /* nothing */
+#endif
+
+
+#define NLP_MARK __FILE__, __LINE__, __func__
+
+/*----------------------------------------------------------------------------
+ * Macros
+ *----------------------------------------------------------------------------
+ */
+/*======================================
+ * Nullチェック 及び 情報出力後 return
+ *・展開するとifとかreturn等が出るので
+ * 一行単体で使ってください。
+ *・nullpo_ret(x = func());
+ * のような使用法も想定しています。
+ *--------------------------------------
+ * nullpo_ret(t)
+ * 戻り値 0固定
+ * [引数]
+ * t チェック対象
+ *--------------------------------------
+ * nullpo_retv(t)
+ * 戻り値 なし
+ * [引数]
+ * t チェック対象
+ *--------------------------------------
+ * nullpo_retr(ret, t)
+ * 戻り値 指定
+ * [引数]
+ * ret return(ret);
+ * t チェック対象
+ *--------------------------------------
+ * nullpo_ret_f(t, fmt, ...)
+ * 詳細情報出力用
+ * 戻り値 0
+ * [引数]
+ * t チェック対象
+ * fmt ... vprintfに渡される
+ * 備考や関係変数の書き出しなどに
+ *--------------------------------------
+ * nullpo_retv_f(t, fmt, ...)
+ * 詳細情報出力用
+ * 戻り値 なし
+ * [引数]
+ * t チェック対象
+ * fmt ... vprintfに渡される
+ * 備考や関係変数の書き出しなどに
+ *--------------------------------------
+ * nullpo_retr_f(ret, t, fmt, ...)
+ * 詳細情報出力用
+ * 戻り値 指定
+ * [引数]
+ * ret return(ret);
+ * t チェック対象
+ * fmt ... vprintfに渡される
+ * 備考や関係変数の書き出しなどに
+ *--------------------------------------
+ */
+
+#if NULLPO_CHECK
+
+#define nullpo_ret(t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {return(0);}
+
+#define nullpo_retv(t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {return;}
+
+#define nullpo_retr(ret, t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {return(ret);}
+
+#define nullpo_retb(t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {break;}
+
+// 可変引数マクロに関する条件コンパイル
+#if __STDC_VERSION__ >= 199901L
+/* C99に対応 */
+#define nullpo_ret_f(t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(0);}
+
+#define nullpo_retv_f(t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return;}
+
+#define nullpo_retr_f(ret, t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(ret);}
+
+#define nullpo_retb_f(t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {break;}
+
+#elif __GNUC__ >= 2
+/* GCC用 */
+#define nullpo_ret_f(t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(0);}
+
+#define nullpo_retv_f(t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return;}
+
+#define nullpo_retr_f(ret, t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(ret);}
+
+#define nullpo_retb_f(t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {break;}
+
+#else
+
+/* その他の場合・・・ orz */
+
+#endif
+
+#else /* NULLPO_CHECK */
+/* No Nullpo check */
+
+// if((t)){;}
+// 良い方法が思いつかなかったので・・・苦肉の策です。
+// 一応ワーニングは出ないはず
+
+#define nullpo_ret(t) if((t)){;}
+#define nullpo_retv(t) if((t)){;}
+#define nullpo_retr(ret, t) if((t)){;}
+#define nullpo_retb(t) if((t)){;}
+
+// 可変引数マクロに関する条件コンパイル
+#if __STDC_VERSION__ >= 199901L
+/* C99に対応 */
+#define nullpo_ret_f(t, fmt, ...) if((t)){;}
+#define nullpo_retv_f(t, fmt, ...) if((t)){;}
+#define nullpo_retr_f(ret, t, fmt, ...) if((t)){;}
+#define nullpo_retb_f(t, fmt, ...) if((t)){;}
+
+#elif __GNUC__ >= 2
+/* GCC用 */
+#define nullpo_ret_f(t, fmt, args...) if((t)){;}
+#define nullpo_retv_f(t, fmt, args...) if((t)){;}
+#define nullpo_retr_f(ret, t, fmt, args...) if((t)){;}
+#define nullpo_retb_f(t, fmt, args...) if((t)){;}
+
+#else
+/* その他の場合・・・ orz */
+#endif
+
+#endif /* NULLPO_CHECK */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ *----------------------------------------------------------------------------
+ */
+/*======================================
+ * nullpo_chk
+ * Nullチェック 及び 情報出力
+ * [引数]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (関数名)
+ * これらには NLP_MARK を使うとよい
+ * target チェック対象
+ * [返り値]
+ * 0 OK
+ * 1 NULL
+ *--------------------------------------
+ */
+int nullpo_chk(const char *file, int line, const char *func, const void *target);
+
+
+/*======================================
+ * nullpo_chk_f
+ * Nullチェック 及び 詳細な情報出力
+ * [引数]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (関数名)
+ * これらには NLP_MARK を使うとよい
+ * target チェック対象
+ * fmt ... vprintfに渡される
+ * 備考や関係変数の書き出しなどに
+ * [返り値]
+ * 0 OK
+ * 1 NULL
+ *--------------------------------------
+ */
+int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
+ const char *fmt, ...)
+ __attribute__((format(printf,5,6)));
+
+
+/*======================================
+ * nullpo_info
+ * nullpo情報出力
+ * [引数]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (関数名)
+ * これらには NLP_MARK を使うとよい
+ *--------------------------------------
+ */
+void nullpo_info(const char *file, int line, const char *func);
+
+
+/*======================================
+ * nullpo_info_f
+ * nullpo詳細情報出力
+ * [引数]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (関数名)
+ * これらには NLP_MARK を使うとよい
+ * fmt ... vprintfに渡される
+ * 備考や関係変数の書き出しなどに
+ *--------------------------------------
+ */
+void nullpo_info_f(const char *file, int line, const char *func,
+ const char *fmt, ...)
+ __attribute__((format(printf,4,5)));
+
+
+#endif
diff --git a/src/common/plugin.h b/src/common/plugin.h
index 2ccefb6bd..402636b1d 100644
--- a/src/common/plugin.h
+++ b/src/common/plugin.h
@@ -1,40 +1,40 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _PLUGIN_H_
-#define _PLUGIN_H_
-
-////// Plugin functions ///////////////
-
-#define PLUGIN_VERSION "1.02"
-
-typedef struct _Plugin_Info {
- char *name;
- char type;
- char *version;
- char *req_version;
- char *description;
-} Plugin_Info;
-
-typedef struct _Plugin_Event_Table {
- char *func_name;
- char *event_name;
-} Plugin_Event_Table;
-
-////// Plugin Export functions /////////////
-
-#define PLUGIN_ALL 0
-#define PLUGIN_LOGIN 1
-#define PLUGIN_CHAR 2
-#define PLUGIN_MAP 8
-#define PLUGIN_CORE 16
-
-#define IMPORT_SYMBOL(s,n) (s) = plugin_call_table[n]
-
-////// Global Plugin variables /////////////
-
-#define PLUGIN_INFO struct _Plugin_Info plugin_info
-#define PLUGIN_EVENTS_TABLE struct _Plugin_Event_Table plugin_event_table[]
-void **plugin_call_table;
-
-#endif // _PLUGIN_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PLUGIN_H_
+#define _PLUGIN_H_
+
+////// Plugin functions ///////////////
+
+#define PLUGIN_VERSION "1.02"
+
+typedef struct _Plugin_Info {
+ char *name;
+ char type;
+ char *version;
+ char *req_version;
+ char *description;
+} Plugin_Info;
+
+typedef struct _Plugin_Event_Table {
+ char *func_name;
+ char *event_name;
+} Plugin_Event_Table;
+
+////// Plugin Export functions /////////////
+
+#define PLUGIN_ALL 0
+#define PLUGIN_LOGIN 1
+#define PLUGIN_CHAR 2
+#define PLUGIN_MAP 8
+#define PLUGIN_CORE 16
+
+#define IMPORT_SYMBOL(s,n) (s) = plugin_call_table[n]
+
+////// Global Plugin variables /////////////
+
+#define PLUGIN_INFO struct _Plugin_Info plugin_info
+#define PLUGIN_EVENTS_TABLE struct _Plugin_Event_Table plugin_event_table[]
+void **plugin_call_table;
+
+#endif // _PLUGIN_H_
diff --git a/src/common/plugins.c b/src/common/plugins.c
index a057190b9..5951885a1 100644
--- a/src/common/plugins.c
+++ b/src/common/plugins.c
@@ -1,367 +1,367 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-#include "plugin.h"
-#include "plugins.h"
-#include "../common/mmo.h"
-#include "../common/core.h"
-#include "../common/timer.h"
-#include "../common/utils.h"
-#include "../common/socket.h"
-#include "../common/malloc.h"
-#include "../common/version.h"
-#include "../common/showmsg.h"
-
-//////////////////////////////////////////////
-
-typedef struct _Plugin_Event {
- void (*func)(void);
- struct _Plugin_Event *next;
-} Plugin_Event;
-
-typedef struct _Plugin_Event_List {
- char *name;
- struct _Plugin_Event_List *next;
- struct _Plugin_Event *events;
-} Plugin_Event_List;
-
-static int auto_search = 1;
-static int load_priority = 0;
-Plugin_Event_List *event_head = NULL;
-Plugin *plugin_head = NULL;
-
-Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" };
-
-static size_t call_table_size = 0;
-static size_t max_call_table = 0;
-
-////// Plugin Events Functions //////////////////
-
-int register_plugin_func (char *name)
-{
- Plugin_Event_List *evl;
- if (name) {
- evl = (Plugin_Event_List *) aMalloc(sizeof(Plugin_Event_List));
- evl->name = (char *) aMalloc (strlen(name) + 1);
-
- evl->next = event_head;
- strcpy(evl->name, name);
- evl->events = NULL;
- event_head = evl;
- }
- return 0;
-}
-
-Plugin_Event_List *search_plugin_func (char *name)
-{
- Plugin_Event_List *evl = event_head;
- while (evl) {
- if (strcmpi(evl->name, name) == 0)
- return evl;
- evl = evl->next;
- }
- return NULL;
-}
-
-int register_plugin_event (void (*func)(void), char* name)
-{
- Plugin_Event_List *evl = search_plugin_func(name);
- if (!evl) {
- // register event if it doesn't exist already
- register_plugin_func(name);
- // relocate the new event list
- evl = search_plugin_func(name);
- }
- if (evl) {
- Plugin_Event *ev;
-
- ev = (Plugin_Event *) aMalloc(sizeof(Plugin_Event));
- ev->func = func;
- ev->next = NULL;
-
- if (evl->events == NULL)
- evl->events = ev;
- else {
- Plugin_Event *ev2 = evl->events;
- while (ev2) {
- if (ev2->next == NULL) {
- ev2->next = ev;
- break;
- }
- ev2 = ev2->next;
- }
- }
- }
- return 0;
-}
-
-int plugin_event_trigger (char *name)
-{
- int c = 0;
- Plugin_Event_List *evl = search_plugin_func(name);
- if (evl) {
- Plugin_Event *ev = evl->events;
- while (ev) {
- ev->func();
- ev = ev->next;
- c++;
- }
- }
- return c;
-}
-
-////// Plugins Call Table Functions /////////
-
-int export_symbol (void *var, int offset)
-{
- //printf ("0x%x\n", var);
-
- // add to the end of the list
- if (offset < 0)
- offset = call_table_size;
-
- // realloc if not large enough
- if ((size_t)offset >= max_call_table) {
- max_call_table = 1 + offset;
- plugin_call_table = (void**)aRealloc(plugin_call_table, max_call_table*sizeof(void*));
-
- // clear the new alloced block
- malloc_tsetdword(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*));
- }
-
- // the new table size is delimited by the new element at the end
- if ((size_t)offset >= call_table_size)
- call_table_size = offset+1;
-
- // put the pointer at the selected place
- plugin_call_table[offset] = var;
- return 0;
-}
-
-////// Plugins Core /////////////////////////
-
-Plugin *plugin_open (const char *filename)
-{
- Plugin *plugin;
- Plugin_Info *info;
- Plugin_Event_Table *events;
- void **procs;
- int init_flag = 1;
-
- //printf ("loading %s\n", filename);
-
- // Check if the plugin has been loaded before
- plugin = plugin_head;
- while (plugin) {
- // returns handle to the already loaded plugin
- if (plugin->state && strcmpi(plugin->filename, filename) == 0) {
- //printf ("not loaded (duplicate) : %s\n", filename);
- return plugin;
- }
- plugin = plugin->next;
- }
-
- plugin = (Plugin *)aCalloc(1, sizeof(Plugin));
- plugin->state = -1; // not loaded
-
- plugin->dll = DLL_OPEN(filename);
- if (!plugin->dll) {
- //printf ("not loaded (invalid file) : %s\n", filename);
- plugin_unload(plugin);
- return NULL;
- }
-
- // Retrieve plugin information
- plugin->state = 0; // initialising
- DLL_SYM (info, plugin->dll, "plugin_info");
- // For high priority plugins (those that are explicitly loaded from the conf file)
- // we'll ignore them even (could be a 3rd party dll file)
- if ((!info && load_priority == 0) ||
- (info && ((atof(info->req_version) < atof(PLUGIN_VERSION)) || // plugin is based on older code
- (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || // plugin is not for this server
- (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP))))
- {
- //printf ("not loaded (incompatible) : %s\n", filename);
- plugin_unload(plugin);
- return NULL;
- }
- plugin->info = (info) ? info : &default_info;
-
- plugin->filename = (char *) aMalloc (strlen(filename) + 1);
- strcpy(plugin->filename, filename);
-
- // Initialise plugin call table (For exporting procedures)
- DLL_SYM (procs, plugin->dll, "plugin_call_table");
- if (procs) *procs = plugin_call_table;
-
- // Register plugin events
- DLL_SYM (events, plugin->dll, "plugin_event_table");
- if (events) {
- int i = 0;
- while (events[i].func_name) {
- if (strcmpi(events[i].event_name, "Plugin_Test") == 0) {
- int (*test_func)(void);
- DLL_SYM (test_func, plugin->dll, events[i].func_name);
- if (test_func && test_func() == 0) {
- // plugin has failed test, disabling
- //printf ("disabled (failed test) : %s\n", filename);
- init_flag = 0;
- }
- } else {
- void (*func)(void);
- DLL_SYM (func, plugin->dll, events[i].func_name);
- if (func) register_plugin_event (func, events[i].event_name);
- }
- i++;
- }
- }
-
- plugin->next = plugin_head;
- plugin_head = plugin;
-
- plugin->state = init_flag; // fully loaded
- ShowStatus ("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename);
-
- return plugin;
-}
-
-void plugin_load (const char *filename)
-{
- plugin_open(filename);
-}
-
-void plugin_unload (Plugin *plugin)
-{
- if (plugin == NULL)
- return;
- if (plugin->filename) aFree(plugin->filename);
- if (plugin->dll) DLL_CLOSE(plugin->dll);
- aFree(plugin);
-}
-
-#ifdef _WIN32
-char *DLL_ERROR(void)
-{
- static char dllbuf[80];
- DWORD dw = GetLastError();
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL);
- return dllbuf;
-}
-#endif
-
-////// Initialize/Finalize ////////////////////
-
-int plugins_config_read(const char *cfgName)
-{
- char line[1024], w1[1024], w2[1024];
- FILE *fp;
-
- fp = fopen(cfgName, "r");
- if (fp == NULL) {
- ShowError("File not found: %s\n", cfgName);
- return 1;
- }
- while (fgets(line, 1020, fp)) {
- if(line[0] == '/' && line[1] == '/')
- continue;
- if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2)
- continue;
-
- if (strcmpi(w1, "auto_search") == 0) {
- if(strcmpi(w2, "yes")==0)
- auto_search = 1;
- else if(strcmpi(w2, "no")==0)
- auto_search = 0;
- else auto_search = atoi(w2);
- } else if (strcmpi(w1, "plugin") == 0) {
- char filename[128];
- sprintf (filename, "plugins/%s%s", w2, DLL_EXT);
- plugin_load(filename);
- } else if (strcmpi(w1, "import") == 0)
- plugins_config_read(w2);
- }
- fclose(fp);
- return 0;
-}
-
-void plugins_init (void)
-{
- char *PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf";
- register_plugin_func("Plugin_Init");
- register_plugin_func("Plugin_Final");
- register_plugin_func("Athena_Init");
- register_plugin_func("Athena_Final");
-
- // networking
- export_symbol (func_parse_table, 18);
- export_symbol (RFIFOSKIP, 17);
- export_symbol (WFIFOSET, 16);
- export_symbol (delete_session, 15);
- export_symbol (session, 14);
- export_symbol (&fd_max, 13);
- export_symbol (addr_, 12);
- // timers
- export_symbol (get_uptime, 11);
- export_symbol (delete_timer, 10);
- export_symbol (add_timer_func_list, 9);
- export_symbol (add_timer_interval, 8);
- export_symbol (add_timer, 7);
- export_symbol ((void *)get_svn_revision, 6);
- export_symbol (gettick, 5);
- // core
- export_symbol (&runflag, 4);
- export_symbol (arg_v, 3);
- export_symbol (&arg_c, 2);
- export_symbol (SERVER_NAME, 1);
- export_symbol (&SERVER_TYPE, 0);
-
- load_priority = 1;
- plugins_config_read (PLUGIN_CONF_FILENAME);
- load_priority = 0;
-
- if (auto_search)
- findfile("plugins", DLL_EXT, plugin_load);
-
- plugin_event_trigger("Plugin_Init");
-
- return;
-}
-
-void plugins_final (void)
-{
- Plugin *plugin = plugin_head, *plugin2;
- Plugin_Event_List *evl = event_head, *evl2;
- Plugin_Event *ev, *ev2;
-
- plugin_event_trigger("Plugin_Final");
-
- while (plugin) {
- plugin2 = plugin->next;
- plugin_unload(plugin);
- plugin = plugin2;
- }
-
- while (evl) {
- ev = evl->events;
- while (ev) {
- ev2 = ev->next;
- aFree(ev);
- ev = ev2;
- }
- evl2 = evl->next;
- aFree(evl->name);
- aFree(evl);
- evl = evl2;
- }
-
- aFree(plugin_call_table);
-
- return;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "plugin.h"
+#include "plugins.h"
+#include "../common/mmo.h"
+#include "../common/core.h"
+#include "../common/timer.h"
+#include "../common/utils.h"
+#include "../common/socket.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/showmsg.h"
+
+//////////////////////////////////////////////
+
+typedef struct _Plugin_Event {
+ void (*func)(void);
+ struct _Plugin_Event *next;
+} Plugin_Event;
+
+typedef struct _Plugin_Event_List {
+ char *name;
+ struct _Plugin_Event_List *next;
+ struct _Plugin_Event *events;
+} Plugin_Event_List;
+
+static int auto_search = 1;
+static int load_priority = 0;
+Plugin_Event_List *event_head = NULL;
+Plugin *plugin_head = NULL;
+
+Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" };
+
+static size_t call_table_size = 0;
+static size_t max_call_table = 0;
+
+////// Plugin Events Functions //////////////////
+
+int register_plugin_func (char *name)
+{
+ Plugin_Event_List *evl;
+ if (name) {
+ evl = (Plugin_Event_List *) aMalloc(sizeof(Plugin_Event_List));
+ evl->name = (char *) aMalloc (strlen(name) + 1);
+
+ evl->next = event_head;
+ strcpy(evl->name, name);
+ evl->events = NULL;
+ event_head = evl;
+ }
+ return 0;
+}
+
+Plugin_Event_List *search_plugin_func (char *name)
+{
+ Plugin_Event_List *evl = event_head;
+ while (evl) {
+ if (strcmpi(evl->name, name) == 0)
+ return evl;
+ evl = evl->next;
+ }
+ return NULL;
+}
+
+int register_plugin_event (void (*func)(void), char* name)
+{
+ Plugin_Event_List *evl = search_plugin_func(name);
+ if (!evl) {
+ // register event if it doesn't exist already
+ register_plugin_func(name);
+ // relocate the new event list
+ evl = search_plugin_func(name);
+ }
+ if (evl) {
+ Plugin_Event *ev;
+
+ ev = (Plugin_Event *) aMalloc(sizeof(Plugin_Event));
+ ev->func = func;
+ ev->next = NULL;
+
+ if (evl->events == NULL)
+ evl->events = ev;
+ else {
+ Plugin_Event *ev2 = evl->events;
+ while (ev2) {
+ if (ev2->next == NULL) {
+ ev2->next = ev;
+ break;
+ }
+ ev2 = ev2->next;
+ }
+ }
+ }
+ return 0;
+}
+
+int plugin_event_trigger (char *name)
+{
+ int c = 0;
+ Plugin_Event_List *evl = search_plugin_func(name);
+ if (evl) {
+ Plugin_Event *ev = evl->events;
+ while (ev) {
+ ev->func();
+ ev = ev->next;
+ c++;
+ }
+ }
+ return c;
+}
+
+////// Plugins Call Table Functions /////////
+
+int export_symbol (void *var, int offset)
+{
+ //printf ("0x%x\n", var);
+
+ // add to the end of the list
+ if (offset < 0)
+ offset = call_table_size;
+
+ // realloc if not large enough
+ if ((size_t)offset >= max_call_table) {
+ max_call_table = 1 + offset;
+ plugin_call_table = (void**)aRealloc(plugin_call_table, max_call_table*sizeof(void*));
+
+ // clear the new alloced block
+ malloc_tsetdword(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*));
+ }
+
+ // the new table size is delimited by the new element at the end
+ if ((size_t)offset >= call_table_size)
+ call_table_size = offset+1;
+
+ // put the pointer at the selected place
+ plugin_call_table[offset] = var;
+ return 0;
+}
+
+////// Plugins Core /////////////////////////
+
+Plugin *plugin_open (const char *filename)
+{
+ Plugin *plugin;
+ Plugin_Info *info;
+ Plugin_Event_Table *events;
+ void **procs;
+ int init_flag = 1;
+
+ //printf ("loading %s\n", filename);
+
+ // Check if the plugin has been loaded before
+ plugin = plugin_head;
+ while (plugin) {
+ // returns handle to the already loaded plugin
+ if (plugin->state && strcmpi(plugin->filename, filename) == 0) {
+ //printf ("not loaded (duplicate) : %s\n", filename);
+ return plugin;
+ }
+ plugin = plugin->next;
+ }
+
+ plugin = (Plugin *)aCalloc(1, sizeof(Plugin));
+ plugin->state = -1; // not loaded
+
+ plugin->dll = DLL_OPEN(filename);
+ if (!plugin->dll) {
+ //printf ("not loaded (invalid file) : %s\n", filename);
+ plugin_unload(plugin);
+ return NULL;
+ }
+
+ // Retrieve plugin information
+ plugin->state = 0; // initialising
+ DLL_SYM (info, plugin->dll, "plugin_info");
+ // For high priority plugins (those that are explicitly loaded from the conf file)
+ // we'll ignore them even (could be a 3rd party dll file)
+ if ((!info && load_priority == 0) ||
+ (info && ((atof(info->req_version) < atof(PLUGIN_VERSION)) || // plugin is based on older code
+ (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || // plugin is not for this server
+ (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP))))
+ {
+ //printf ("not loaded (incompatible) : %s\n", filename);
+ plugin_unload(plugin);
+ return NULL;
+ }
+ plugin->info = (info) ? info : &default_info;
+
+ plugin->filename = (char *) aMalloc (strlen(filename) + 1);
+ strcpy(plugin->filename, filename);
+
+ // Initialise plugin call table (For exporting procedures)
+ DLL_SYM (procs, plugin->dll, "plugin_call_table");
+ if (procs) *procs = plugin_call_table;
+
+ // Register plugin events
+ DLL_SYM (events, plugin->dll, "plugin_event_table");
+ if (events) {
+ int i = 0;
+ while (events[i].func_name) {
+ if (strcmpi(events[i].event_name, "Plugin_Test") == 0) {
+ int (*test_func)(void);
+ DLL_SYM (test_func, plugin->dll, events[i].func_name);
+ if (test_func && test_func() == 0) {
+ // plugin has failed test, disabling
+ //printf ("disabled (failed test) : %s\n", filename);
+ init_flag = 0;
+ }
+ } else {
+ void (*func)(void);
+ DLL_SYM (func, plugin->dll, events[i].func_name);
+ if (func) register_plugin_event (func, events[i].event_name);
+ }
+ i++;
+ }
+ }
+
+ plugin->next = plugin_head;
+ plugin_head = plugin;
+
+ plugin->state = init_flag; // fully loaded
+ ShowStatus ("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename);
+
+ return plugin;
+}
+
+void plugin_load (const char *filename)
+{
+ plugin_open(filename);
+}
+
+void plugin_unload (Plugin *plugin)
+{
+ if (plugin == NULL)
+ return;
+ if (plugin->filename) aFree(plugin->filename);
+ if (plugin->dll) DLL_CLOSE(plugin->dll);
+ aFree(plugin);
+}
+
+#ifdef _WIN32
+char *DLL_ERROR(void)
+{
+ static char dllbuf[80];
+ DWORD dw = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL);
+ return dllbuf;
+}
+#endif
+
+////// Initialize/Finalize ////////////////////
+
+int plugins_config_read(const char *cfgName)
+{
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ fp = fopen(cfgName, "r");
+ if (fp == NULL) {
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while (fgets(line, 1020, fp)) {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+
+ if (strcmpi(w1, "auto_search") == 0) {
+ if(strcmpi(w2, "yes")==0)
+ auto_search = 1;
+ else if(strcmpi(w2, "no")==0)
+ auto_search = 0;
+ else auto_search = atoi(w2);
+ } else if (strcmpi(w1, "plugin") == 0) {
+ char filename[128];
+ sprintf (filename, "plugins/%s%s", w2, DLL_EXT);
+ plugin_load(filename);
+ } else if (strcmpi(w1, "import") == 0)
+ plugins_config_read(w2);
+ }
+ fclose(fp);
+ return 0;
+}
+
+void plugins_init (void)
+{
+ char *PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf";
+ register_plugin_func("Plugin_Init");
+ register_plugin_func("Plugin_Final");
+ register_plugin_func("Athena_Init");
+ register_plugin_func("Athena_Final");
+
+ // networking
+ export_symbol (func_parse_table, 18);
+ export_symbol (RFIFOSKIP, 17);
+ export_symbol (WFIFOSET, 16);
+ export_symbol (delete_session, 15);
+ export_symbol (session, 14);
+ export_symbol (&fd_max, 13);
+ export_symbol (addr_, 12);
+ // timers
+ export_symbol (get_uptime, 11);
+ export_symbol (delete_timer, 10);
+ export_symbol (add_timer_func_list, 9);
+ export_symbol (add_timer_interval, 8);
+ export_symbol (add_timer, 7);
+ export_symbol ((void *)get_svn_revision, 6);
+ export_symbol (gettick, 5);
+ // core
+ export_symbol (&runflag, 4);
+ export_symbol (arg_v, 3);
+ export_symbol (&arg_c, 2);
+ export_symbol (SERVER_NAME, 1);
+ export_symbol (&SERVER_TYPE, 0);
+
+ load_priority = 1;
+ plugins_config_read (PLUGIN_CONF_FILENAME);
+ load_priority = 0;
+
+ if (auto_search)
+ findfile("plugins", DLL_EXT, plugin_load);
+
+ plugin_event_trigger("Plugin_Init");
+
+ return;
+}
+
+void plugins_final (void)
+{
+ Plugin *plugin = plugin_head, *plugin2;
+ Plugin_Event_List *evl = event_head, *evl2;
+ Plugin_Event *ev, *ev2;
+
+ plugin_event_trigger("Plugin_Final");
+
+ while (plugin) {
+ plugin2 = plugin->next;
+ plugin_unload(plugin);
+ plugin = plugin2;
+ }
+
+ while (evl) {
+ ev = evl->events;
+ while (ev) {
+ ev2 = ev->next;
+ aFree(ev);
+ ev = ev2;
+ }
+ evl2 = evl->next;
+ aFree(evl->name);
+ aFree(evl);
+ evl = evl2;
+ }
+
+ aFree(plugin_call_table);
+
+ return;
+}
diff --git a/src/common/plugins.h b/src/common/plugins.h
index d642b5965..34fd18a64 100644
--- a/src/common/plugins.h
+++ b/src/common/plugins.h
@@ -1,61 +1,61 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _PLUGINS_H_
-#define _PLUGINS_H_
-
-////// Dynamic Link Library functions ///////////////
-
-#ifdef _WIN32
-
- #include <windows.h>
- #define DLL_OPEN(x) LoadLibrary(x)
- #define DLL_SYM(x,y,z) (FARPROC)(x) = GetProcAddress(y,z)
- #define DLL_CLOSE(x) FreeLibrary(x)
- #define DLL_EXT ".dll"
- #define DLL HINSTANCE
- char *DLL_ERROR(void);
-
-#else
-
- #include <dlfcn.h>
- #define DLL_OPEN(x) dlopen(x,RTLD_NOW)
- #define DLL_SYM(x,y,z) (x) = (void *)dlsym(y,z)
- #define DLL_CLOSE(x) dlclose(x)
- #define DLL_ERROR dlerror
-
- #ifdef CYGWIN
- #define DLL_EXT ".dll"
- #else
- #define DLL_EXT ".so"
- #endif
- #define DLL void *
-
-#endif
-
-////// Plugin Definitions ///////////////////
-
-typedef struct _Plugin {
- DLL dll;
- char state;
- char *filename;
- struct _Plugin_Info *info;
- struct _Plugin *next;
-} Plugin;
-
-/////////////////////////////////////////////
-
-int register_plugin_func (char *);
-int register_plugin_event (void (*)(void), char *);
-int plugin_event_trigger (char *);
-
-int export_symbol (void *, int);
-#define EXPORT_SYMBOL(s) export_symbol((s), -1);
-
-Plugin *plugin_open (const char *);
-void plugin_load (const char *);
-void plugin_unload (Plugin *);
-void plugins_init (void);
-void plugins_final (void);
-
-#endif // _PLUGINS_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PLUGINS_H_
+#define _PLUGINS_H_
+
+////// Dynamic Link Library functions ///////////////
+
+#ifdef _WIN32
+
+ #include <windows.h>
+ #define DLL_OPEN(x) LoadLibrary(x)
+ #define DLL_SYM(x,y,z) (FARPROC)(x) = GetProcAddress(y,z)
+ #define DLL_CLOSE(x) FreeLibrary(x)
+ #define DLL_EXT ".dll"
+ #define DLL HINSTANCE
+ char *DLL_ERROR(void);
+
+#else
+
+ #include <dlfcn.h>
+ #define DLL_OPEN(x) dlopen(x,RTLD_NOW)
+ #define DLL_SYM(x,y,z) (x) = (void *)dlsym(y,z)
+ #define DLL_CLOSE(x) dlclose(x)
+ #define DLL_ERROR dlerror
+
+ #ifdef CYGWIN
+ #define DLL_EXT ".dll"
+ #else
+ #define DLL_EXT ".so"
+ #endif
+ #define DLL void *
+
+#endif
+
+////// Plugin Definitions ///////////////////
+
+typedef struct _Plugin {
+ DLL dll;
+ char state;
+ char *filename;
+ struct _Plugin_Info *info;
+ struct _Plugin *next;
+} Plugin;
+
+/////////////////////////////////////////////
+
+int register_plugin_func (char *);
+int register_plugin_event (void (*)(void), char *);
+int plugin_event_trigger (char *);
+
+int export_symbol (void *, int);
+#define EXPORT_SYMBOL(s) export_symbol((s), -1);
+
+Plugin *plugin_open (const char *);
+void plugin_load (const char *);
+void plugin_unload (Plugin *);
+void plugins_init (void);
+void plugins_final (void);
+
+#endif // _PLUGINS_H_
diff --git a/src/common/showmsg.c b/src/common/showmsg.c
index b0f1d1d8f..fd376f100 100644
--- a/src/common/showmsg.c
+++ b/src/common/showmsg.c
@@ -1,826 +1,826 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-#include <stdlib.h> // atexit
-#include "../common/cbasetypes.h"
-#include "showmsg.h"
-
-#ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
-
- #ifdef DEBUGLOGMAP
- #define DEBUGLOGPATH "log\\map-server.log"
- #else
- #ifdef DEBUGLOGCHAR
- #define DEBUGLOGPATH "log\\char-server.log"
- #else
- #ifdef DEBUGLOGLOGIN
- #define DEBUGLOGPATH "log\\login-server.log"
- #endif
- #endif
- #endif
-#else
- #include <unistd.h>
- #include <ctype.h>
-
- #ifdef DEBUGLOGMAP
- #define DEBUGLOGPATH "log/map-server.log"
- #else
- #ifdef DEBUGLOGCHAR
- #define DEBUGLOGPATH "log/char-server.log"
- #else
- #ifdef DEBUGLOGLOGIN
- #define DEBUGLOGPATH "log/login-server.log"
- #endif
- #endif
- #endif
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-/// behavioral parameter.
-/// when true, prints ansi sequences also when redirecting outputs to file
-/// otherwise remove them
-int stdout_with_ansisequence = 1;
-
-int msg_silent; //Specifies how silent the console is.
-
-///////////////////////////////////////////////////////////////////////////////
-/// small reallocating temporary printer buffer
-static char *tempbuf = NULL;
-static size_t sz = 0;
-#define tempbuf_size() (sz)
-static void tempbuf_free(void){ free(tempbuf); }
-static void tempbuf_alloc(void){ sz = 256; tempbuf = (char *)malloc(sz); atexit(tempbuf_free); }
-static void tempbuf_realloc(void){ sz <<= 1; tempbuf = (char *)realloc(tempbuf,sz); }
-
-///////////////////////////////////////////////////////////////////////////////
-#ifdef _WIN32
-// XXX adapted from eApp (comments are left untouched) [flaviojs]
-
-///////////////////////////////////////////////////////////////////////////////
-// ansi compatible printf with control sequence parser for windows
-// fast hack, handle with care, not everything implemented
-//
-// \033[#;...;#m - Set Graphics Rendition (SGR)
-//
-// printf("\x1b[1;31;40m"); // Bright red on black
-// printf("\x1b[3;33;45m"); // Blinking yellow on magenta (blink not implemented)
-// printf("\x1b[1;30;47m"); // Bright black (grey) on dim white
-//
-// Style Foreground Background
-// 1st Digit 2nd Digit 3rd Digit RGB
-// 0 - Reset 30 - Black 40 - Black 000
-// 1 - FG Bright 31 - Red 41 - Red 100
-// 2 - Unknown 32 - Green 42 - Green 010
-// 3 - Blink 33 - Yellow 43 - Yellow 110
-// 4 - Underline 34 - Blue 44 - Blue 001
-// 5 - BG Bright 35 - Magenta 45 - Magenta 101
-// 6 - Unknown 36 - Cyan 46 - Cyan 011
-// 7 - Reverse 37 - White 47 - White 111
-// 8 - Concealed (invisible)
-//
-// \033[#A - Cursor Up (CUU)
-// Moves the cursor up by the specified number of lines without changing columns.
-// If the cursor is already on the top line, this sequence is ignored. \e[A is equivalent to \e[1A.
-//
-// \033[#B - Cursor Down (CUD)
-// Moves the cursor down by the specified number of lines without changing columns.
-// If the cursor is already on the bottom line, this sequence is ignored. \e[B is equivalent to \e[1B.
-//
-// \033[#C - Cursor Forward (CUF)
-// Moves the cursor forward by the specified number of columns without changing lines.
-// If the cursor is already in the rightmost column, this sequence is ignored. \e[C is equivalent to \e[1C.
-//
-// \033[#D - Cursor Backward (CUB)
-// Moves the cursor back by the specified number of columns without changing lines.
-// If the cursor is already in the leftmost column, this sequence is ignored. \e[D is equivalent to \e[1D.
-//
-// \033[#E - Cursor Next Line (CNL)
-// Moves the cursor down the indicated # of rows, to column 1. \e[E is equivalent to \e[1E.
-//
-// \033[#F - Cursor Preceding Line (CPL)
-// Moves the cursor up the indicated # of rows, to column 1. \e[F is equivalent to \e[1F.
-//
-// \033[#G - Cursor Horizontal Absolute (CHA)
-// Moves the cursor to indicated column in current row. \e[G is equivalent to \e[1G.
-//
-// \033[#;#H - Cursor Position (CUP)
-// Moves the cursor to the specified position. The first # specifies the line number,
-// the second # specifies the column. If you do not specify a position, the cursor moves to the home position:
-// the upper-left corner of the screen (line 1, column 1).
-//
-// \033[#;#f - Horizontal & Vertical Position
-// (same as \033[#;#H)
-//
-// \033[s - Save Cursor Position (SCP)
-// The current cursor position is saved.
-//
-// \033[u - Restore cursor position (RCP)
-// Restores the cursor position saved with the (SCP) sequence \033[s.
-// (addition, restore to 0,0 if nothinh was saved before)
-//
-
-// \033[#J - Erase Display (ED)
-// Clears the screen and moves to the home position
-// \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. (default)
-// \033[1J - Clears the screen from start to cursor. The cursor position is unchanged.
-// \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1).
-//
-// \033[#K - Erase Line (EL)
-// Clears the current line from the cursor position
-// \033[0K - Clears all characters from the cursor position to the end of the line (including the character at the cursor position). The cursor position is unchanged. (default)
-// \033[1K - Clears all characters from start of line to the cursor position. (including the character at the cursor position). The cursor position is unchanged.
-// \033[2K - Clears all characters of the whole line. The cursor position is unchanged.
-
-
-/*
-not implemented
-
-\033[#L
-IL: Insert Lines: The cursor line and all lines below it move down # lines, leaving blank space. The cursor position is unchanged. The bottommost # lines are lost. \e[L is equivalent to \e[1L.
-\033[#M
-DL: Delete Line: The block of # lines at and below the cursor are deleted; all lines below them move up # lines to fill in the gap, leaving # blank lines at the bottom of the screen. The cursor position is unchanged. \e[M is equivalent to \e[1M.
-\033[#\@
-ICH: Insert CHaracter: The cursor character and all characters to the right of it move right # columns, leaving behind blank space. The cursor position is unchanged. The rightmost # characters on the line are lost. \e[\@ is equivalent to \e[1\@.
-\033[#P
-DCH: Delete CHaracter: The block of # characters at and to the right of the cursor are deleted; all characters to the right of it move left # columns, leaving behind blank space. The cursor position is unchanged. \e[P is equivalent to \e[1P.
-
-Escape sequences for Select Character Set
-*/
-
-#define is_console(handle) (FILE_TYPE_CHAR==GetFileType(handle))
-
-///////////////////////////////////////////////////////////////////////////////
-int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
-{
- /////////////////////////////////////////////////////////////////
- /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs]
- static COORD saveposition = {0,0};
- */
-
- /////////////////////////////////////////////////////////////////
- unsigned long written;
- char *p, *q;
-
- if(!fmt || !*fmt)
- return 0;
-
- if(tempbuf == NULL)
- tempbuf_alloc();
- for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc());
- // vsnprintf returns -1 in case of insufficient buffer size
- // tempbuf_realloc doubles the size of the buffer in this case
-
- if( !is_console(handle) && stdout_with_ansisequence )
- {
- WriteFile(handle,tempbuf, strlen(tempbuf), &written, 0);
- return 0;
- }
-
- // start with processing
- p = tempbuf;
- while ((q = strchr(p, 0x1b)) != NULL)
- { // find the escape character
- if( 0==WriteConsole(handle, p, q-p, &written, 0) ) // write up to the escape
- WriteFile(handle, p, q-p, &written, 0);
-
- if( q[1]!='[' )
- { // write the escape char (whatever purpose it has)
- if(0==WriteConsole(handle, q, 1, &written, 0) )
- WriteFile(handle,q, 1, &written, 0);
- p=q+1; //and start searching again
- }
- else
- { // from here, we will skip the '\033['
- // we break at the first unprocessible position
- // assuming regular text is starting there
- uchar numbers[16], numpoint=0;
- CONSOLE_SCREEN_BUFFER_INFO info;
-
- // initialize
- GetConsoleScreenBufferInfo(handle, &info);
- memset(numbers,0,sizeof(numbers));
-
- // skip escape and bracket
- q=q+2;
- while(1)
- {
- if( isdigit((int)((unsigned char)*q)) )
- { // add number to number array, only accept 2digits, shift out the rest
- // so // \033[123456789m will become \033[89m
- numbers[numpoint] = (numbers[numpoint]<<4) | (*q-'0');
- ++q;
- // and next character
- continue;
- }
- else if( *q == ';' )
- { // delimiter
- if(numpoint<sizeof(numbers)/sizeof(*numbers))
- { // go to next array position
- numpoint++;
- }
- else
- { // array is full, so we 'forget' the first value
- memmove(numbers,numbers+1,sizeof(numbers)/sizeof(*numbers)-1);
- numbers[sizeof(numbers)/sizeof(*numbers)-1]=0;
- }
- ++q;
- // and next number
- continue;
- }
- else if( *q == 'm' )
- { // \033[#;...;#m - Set Graphics Rendition (SGR)
- uint i;
- for(i=0; i<= numpoint; ++i)
- {
- if( 0x00 == (0xF0 & numbers[i]) )
- { // upper nibble 0
- if( 0 == numbers[i] )
- { // reset
- info.wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
- }
- else if( 1==numbers[i] )
- { // set foreground intensity
- info.wAttributes |= FOREGROUND_INTENSITY;
- }
- else if( 5==numbers[i] )
- { // set background intensity
- info.wAttributes |= BACKGROUND_INTENSITY;
- }
- else if( 7==numbers[i] )
- { // reverse colors (just xor them)
- info.wAttributes ^= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
- BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
- }
- //case '2': // not existing
- //case '3': // blinking (not implemented)
- //case '4': // unterline (not implemented)
- //case '6': // not existing
- //case '8': // concealed (not implemented)
- //case '9': // not existing
- }
- else if( 0x20 == (0xF0 & numbers[i]) )
- { // off
-
- if( 1==numbers[i] )
- { // set foreground intensity off
- info.wAttributes &= ~FOREGROUND_INTENSITY;
- }
- else if( 5==numbers[i] )
- { // set background intensity off
- info.wAttributes &= ~BACKGROUND_INTENSITY;
- }
- else if( 7==numbers[i] )
- { // reverse colors (just xor them)
- info.wAttributes ^= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
- BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
- }
- }
- else if( 0x30 == (0xF0 & numbers[i]) )
- { // foreground
- uint num = numbers[i]&0x0F;
- if(num==9) info.wAttributes |= FOREGROUND_INTENSITY;
- if(num>7) num=7; // set white for 37, 38 and 39
- info.wAttributes &= ~(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
- if( (num & 0x01)>0 ) // lowest bit set = red
- info.wAttributes |= FOREGROUND_RED;
- if( (num & 0x02)>0 ) // second bit set = green
- info.wAttributes |= FOREGROUND_GREEN;
- if( (num & 0x04)>0 ) // third bit set = blue
- info.wAttributes |= FOREGROUND_BLUE;
- }
- else if( 0x40 == (0xF0 & numbers[i]) )
- { // background
- uint num = numbers[i]&0x0F;
- if(num==9) info.wAttributes |= BACKGROUND_INTENSITY;
- if(num>7) num=7; // set white for 47, 48 and 49
- info.wAttributes &= ~(BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE);
- if( (num & 0x01)>0 ) // lowest bit set = red
- info.wAttributes |= BACKGROUND_RED;
- if( (num & 0x02)>0 ) // second bit set = green
- info.wAttributes |= BACKGROUND_GREEN;
- if( (num & 0x04)>0 ) // third bit set = blue
- info.wAttributes |= BACKGROUND_BLUE;
- }
- }
- // set the attributes
- SetConsoleTextAttribute(handle, info.wAttributes);
- }
- else if( *q=='J' )
- { // \033[#J - Erase Display (ED)
- // \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged.
- // \033[1J - Clears the screen from start to cursor. The cursor position is unchanged.
- // \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1).
- uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F);
- int cnt;
- COORD origin = {0,0};
- if(num==1)
- { // chars from start up to and including cursor
- cnt = info.dwSize.X * info.dwCursorPosition.Y + info.dwCursorPosition.X + 1;
- }
- else if(num==2)
- { // Number of chars on screen.
- cnt = info.dwSize.X * info.dwSize.Y;
- SetConsoleCursorPosition(handle, origin);
- }
- else// 0 and default
- { // number of chars from cursor to end
- origin = info.dwCursorPosition;
- cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X;
- }
- FillConsoleOutputAttribute(handle,info.wAttributes,cnt,origin,NULL);
- FillConsoleOutputCharacter(handle,' ', cnt,origin,NULL);
- }
- else if( *q=='K' )
- { // \033[K : clear line from actual position to end of the line
- // \033[0K - Clears all characters from the cursor position to the end of the line.
- // \033[1K - Clears all characters from start of line to the cursor position.
- // \033[2K - Clears all characters of the whole line.
-
- uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F);
- COORD origin = {0,info.dwCursorPosition.Y};
- SHORT cnt;
- if(num==1)
- {
- cnt = info.dwCursorPosition.X + 1;
- }
- else if(num==2)
- {
- cnt = info.dwSize.X;
- }
- else// 0 and default
- {
- origin = info.dwCursorPosition;
- cnt = info.dwSize.X - info.dwCursorPosition.X; // how many spaces until line is full
- }
- FillConsoleOutputAttribute(handle, info.wAttributes, cnt, origin, NULL);
- FillConsoleOutputCharacter(handle, ' ', cnt, origin, NULL);
- }
- else if( *q == 'H' || *q == 'f' )
- { // \033[#;#H - Cursor Position (CUP)
- // \033[#;#f - Horizontal & Vertical Position
- // The first # specifies the line number, the second # specifies the column.
- // The default for both is 1
- info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0;
- info.dwCursorPosition.Y = (numpoint && numbers[numpoint-1])?(numbers[numpoint-1]>>4)*10+(numbers[numpoint-1]&0x0F-1):0;
-
- if( info.dwCursorPosition.X >= info.dwSize.X ) info.dwCursorPosition.Y = info.dwSize.X-1;
- if( info.dwCursorPosition.Y >= info.dwSize.Y ) info.dwCursorPosition.Y = info.dwSize.Y-1;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q=='s' )
- { // \033[s - Save Cursor Position (SCP)
- /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs]
- CONSOLE_SCREEN_BUFFER_INFO info;
- GetConsoleScreenBufferInfo(handle, &info);
- saveposition = info.dwCursorPosition;
- */
- }
- else if( *q=='u' )
- { // \033[u - Restore cursor position (RCP)
- /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs]
- SetConsoleCursorPosition(handle, saveposition);
- */
- }
- else if( *q == 'A' )
- { // \033[#A - Cursor Up (CUU)
- // Moves the cursor UP # number of lines
- info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
-
- if( info.dwCursorPosition.Y < 0 )
- info.dwCursorPosition.Y = 0;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'B' )
- { // \033[#B - Cursor Down (CUD)
- // Moves the cursor DOWN # number of lines
- info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
-
- if( info.dwCursorPosition.Y >= info.dwSize.Y )
- info.dwCursorPosition.Y = info.dwSize.Y-1;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'C' )
- { // \033[#C - Cursor Forward (CUF)
- // Moves the cursor RIGHT # number of columns
- info.dwCursorPosition.X += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
-
- if( info.dwCursorPosition.X >= info.dwSize.X )
- info.dwCursorPosition.X = info.dwSize.X-1;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'D' )
- { // \033[#D - Cursor Backward (CUB)
- // Moves the cursor LEFT # number of columns
- info.dwCursorPosition.X -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
-
- if( info.dwCursorPosition.X < 0 )
- info.dwCursorPosition.X = 0;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'E' )
- { // \033[#E - Cursor Next Line (CNL)
- // Moves the cursor down the indicated # of rows, to column 1
- info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
- info.dwCursorPosition.X = 0;
-
- if( info.dwCursorPosition.Y >= info.dwSize.Y )
- info.dwCursorPosition.Y = info.dwSize.Y-1;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'F' )
- { // \033[#F - Cursor Preceding Line (CPL)
- // Moves the cursor up the indicated # of rows, to column 1.
- info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
- info.dwCursorPosition.X = 0;
-
- if( info.dwCursorPosition.Y < 0 )
- info.dwCursorPosition.Y = 0;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'G' )
- { // \033[#G - Cursor Horizontal Absolute (CHA)
- // Moves the cursor to indicated column in current row.
- info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0;
-
- if( info.dwCursorPosition.X >= info.dwSize.X )
- info.dwCursorPosition.X = info.dwSize.X-1;
- SetConsoleCursorPosition(handle, info.dwCursorPosition);
- }
- else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P')
- { // not implemented, just skip
- }
- else
- { // no number nor valid sequencer
- // something is fishy, we break and give the current char free
- --q;
- }
- // skip the sequencer and search again
- p = q+1;
- break;
- }// end while
- }
- }
- if (*p) // write the rest of the buffer
- if( 0==WriteConsole(handle, p, strlen(p), &written, 0) )
- WriteFile(handle,p, strlen(p), &written, 0);
- return 0;
-}
-
-int FPRINTF(HANDLE handle, const char *fmt, ...)
-{
- int ret;
- va_list argptr;
- va_start(argptr, fmt);
- ret = VFPRINTF(handle,fmt,argptr);
- va_end(argptr);
- return ret;
-}
-
-#define FFLUSH(handle)
-
-#define STDOUT GetStdHandle(STD_OUTPUT_HANDLE)
-#define STDERR GetStdHandle(STD_ERROR_HANDLE)
-
-#else // not _WIN32
-
-
-//#define VPRINTF vprintf
-//#define PRINTF printf
-
-#define is_console(file) (0!=isatty(fileno(file)))
-
-//vprintf_without_ansiformats
-int VFPRINTF(FILE *file, const char *fmt, va_list argptr)
-{
- char *p, *q;
-
- if(!fmt || !*fmt)
- return 0;
-
- if( is_console(file) || stdout_with_ansisequence )
- {
- vfprintf(file, fmt, argptr);
- return 0;
- }
-
- if(tempbuf == NULL)
- tempbuf_alloc();
- for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc());
- // vsnprintf returns -1 in case of insufficient buffer size
- // tempbuf.realloc doubles the size of the buffer in this case
-
- // start with processing
- p = tempbuf;
- while ((q = strchr(p, 0x1b)) != NULL)
- { // find the escape character
- fprintf(file, "%.*s", (int)(q-p), p); // write up to the escape
- if( q[1]!='[' )
- { // write the escape char (whatever purpose it has)
- fprintf(file, "%.*s", 1, q);
- p=q+1; //and start searching again
- }
- else
- { // from here, we will skip the '\033['
- // we break at the first unprocessible position
- // assuming regular text is starting there
-
- // skip escape and bracket
- q=q+2;
- while(1)
- {
- if( isdigit((int)((unsigned char)*q)) )
- {
- ++q;
- // and next character
- continue;
- }
- else if( *q == ';' )
- { // delimiter
- ++q;
- // and next number
- continue;
- }
- else if( *q == 'm' )
- { // \033[#;...;#m - Set Graphics Rendition (SGR)
- // set the attributes
- }
- else if( *q=='J' )
- { // \033[#J - Erase Display (ED)
- }
- else if( *q=='K' )
- { // \033[K : clear line from actual position to end of the line
- }
- else if( *q == 'H' || *q == 'f' )
- { // \033[#;#H - Cursor Position (CUP)
- // \033[#;#f - Horizontal & Vertical Position
- }
- else if( *q=='s' )
- { // \033[s - Save Cursor Position (SCP)
- }
- else if( *q=='u' )
- { // \033[u - Restore cursor position (RCP)
- }
- else if( *q == 'A' )
- { // \033[#A - Cursor Up (CUU)
- // Moves the cursor UP # number of lines
- }
- else if( *q == 'B' )
- { // \033[#B - Cursor Down (CUD)
- // Moves the cursor DOWN # number of lines
- }
- else if( *q == 'C' )
- { // \033[#C - Cursor Forward (CUF)
- // Moves the cursor RIGHT # number of columns
- }
- else if( *q == 'D' )
- { // \033[#D - Cursor Backward (CUB)
- // Moves the cursor LEFT # number of columns
- }
- else if( *q == 'E' )
- { // \033[#E - Cursor Next Line (CNL)
- // Moves the cursor down the indicated # of rows, to column 1
- }
- else if( *q == 'F' )
- { // \033[#F - Cursor Preceding Line (CPL)
- // Moves the cursor up the indicated # of rows, to column 1.
- }
- else if( *q == 'G' )
- { // \033[#G - Cursor Horizontal Absolute (CHA)
- // Moves the cursor to indicated column in current row.
- }
- else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P')
- { // not implemented, just skip
- }
- else
- { // no number nor valid sequencer
- // something is fishy, we break and give the current char free
- --q;
- }
- // skip the sequencer and search again
- p = q+1;
- break;
- }// end while
- }
- }
- if (*p) // write the rest of the buffer
- fprintf(file, "%s", p);
- return 0;
-}
-int FPRINTF(FILE *file, const char *fmt, ...)
-{
- int ret;
- va_list argptr;
- va_start(argptr, fmt);
- ret = VFPRINTF(file,fmt,argptr);
- va_end(argptr);
- return ret;
-}
-
-#define FFLUSH fflush
-
-#define STDOUT stdout
-#define STDERR stderr
-
-#endif// not _WIN32
-
-
-
-
-
-
-
-
-
-
-char timestamp_format[20] = ""; //For displaying Timestamps
-
-// by MC Cameri
-int _vShowMessage(enum msg_type flag, const char *string, va_list ap)
-{
- // _ShowMessage MUST be used instead of printf as of 10/24/2004.
- // Return: 0 = Successful, 1 = Failed.
-// int ret = 0;
- char prefix[100];
-#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN)
- FILE *fp;
-#endif
-
- if (!string || *string == '\0') {
- ShowError("Empty string passed to _vShowMessage().\n");
- return 1;
- }
- if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) ||
- (flag == MSG_INFORMATION && msg_silent&1) ||
- (flag == MSG_STATUS && msg_silent&2) ||
- (flag == MSG_NOTICE && msg_silent&4) ||
- (flag == MSG_WARNING && msg_silent&8) ||
- (flag == MSG_ERROR && msg_silent&16) ||
- (flag == MSG_SQL && msg_silent&16))
- return 0; //Do not print it.
-
- if (timestamp_format[0])
- { //Display time format. [Skotlex]
- time_t t = time(NULL);
- strftime(prefix, 80, timestamp_format, localtime(&t));
- } else prefix[0]='\0';
-
- switch (flag) {
- case MSG_NONE: // direct printf replacement
- break;
- case MSG_STATUS: //Bright Green (To inform about good things)
- strcat(prefix,CL_GREEN"[Status]"CL_RESET":");
- break;
- case MSG_SQL: //Bright Violet (For dumping out anything related with SQL) <- Actually, this is mostly used for SQL errors with the database, as successes can as well just be anything else... [Skotlex]
- strcat(prefix,CL_MAGENTA"[SQL]"CL_RESET":");
- break;
- case MSG_INFORMATION: //Bright White (Variable information)
- strcat(prefix,CL_WHITE"[Info]"CL_RESET":");
- break;
- case MSG_NOTICE: //Bright White (Less than a warning)
- strcat(prefix,CL_WHITE"[Notice]"CL_RESET":");
- break;
- case MSG_WARNING: //Bright Yellow
- strcat(prefix,CL_YELLOW"[Warning]"CL_RESET":");
- break;
- case MSG_DEBUG: //Bright Cyan, important stuff!
- strcat(prefix,CL_CYAN"[Debug]"CL_RESET":");
- break;
- case MSG_ERROR: //Bright Red (Regular errors)
- strcat(prefix,CL_RED"[Error]"CL_RESET":");
- break;
- case MSG_FATALERROR: //Bright Red (Fatal errors, abort(); if possible)
- strcat(prefix,CL_RED"[Fatal Error]"CL_RESET":");
- break;
- default:
- ShowError("In function _vShowMessage() -> Invalid flag passed.\n");
- return 1;
- }
-
- if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL)
- { //Send Errors to StdErr [Skotlex]
- FPRINTF(STDERR, "%s ", prefix);
- VFPRINTF(STDERR, string, ap);
- FFLUSH(STDERR);
- } else {
- if (flag != MSG_NONE)
- FPRINTF(STDOUT, "%s ", prefix);
- VFPRINTF(STDOUT, string, ap);
- FFLUSH(STDOUT);
- }
-
-#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN)
- if(strlen(DEBUGLOGPATH) > 0) {
- fp=fopen(DEBUGLOGPATH,"a");
- if (fp == NULL) {
- FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n", DEBUGLOGPATH);
- FFLUSH(STDERR);
- } else {
- fprintf(fp,"%s ", prefix);
- vfprintf(fp,string,ap);
- fclose(fp);
- }
- } else {
- FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n");
- FFLUSH(STDERR);
- }
-#endif
-
- va_end(ap);
- return 0;
-}
-
-void ClearScreen(void)
-{
-#ifndef _WIN32
- ShowMessage(CL_CLS); // to prevent empty string passed messages
-#endif
-}
-int _ShowMessage(enum msg_type flag, const char *string, ...)
-{
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(flag, string, ap);
- va_end(ap);
- return ret;
-}
-
-// direct printf replacement
-int ShowMessage(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_NONE, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowStatus(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_STATUS, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowSQL(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_SQL, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowInfo(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_INFORMATION, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowNotice(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_NOTICE, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowWarning(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_WARNING, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowDebug(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_DEBUG, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowError(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_ERROR, string, ap);
- va_end(ap);
- return ret;
-}
-int ShowFatalError(const char *string, ...) {
- int ret;
- va_list ap;
- va_start(ap, string);
- ret = _vShowMessage(MSG_FATALERROR, string, ap);
- va_end(ap);
- return ret;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <stdlib.h> // atexit
+#include "../common/cbasetypes.h"
+#include "showmsg.h"
+
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+
+ #ifdef DEBUGLOGMAP
+ #define DEBUGLOGPATH "log\\map-server.log"
+ #else
+ #ifdef DEBUGLOGCHAR
+ #define DEBUGLOGPATH "log\\char-server.log"
+ #else
+ #ifdef DEBUGLOGLOGIN
+ #define DEBUGLOGPATH "log\\login-server.log"
+ #endif
+ #endif
+ #endif
+#else
+ #include <unistd.h>
+ #include <ctype.h>
+
+ #ifdef DEBUGLOGMAP
+ #define DEBUGLOGPATH "log/map-server.log"
+ #else
+ #ifdef DEBUGLOGCHAR
+ #define DEBUGLOGPATH "log/char-server.log"
+ #else
+ #ifdef DEBUGLOGLOGIN
+ #define DEBUGLOGPATH "log/login-server.log"
+ #endif
+ #endif
+ #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+/// behavioral parameter.
+/// when true, prints ansi sequences also when redirecting outputs to file
+/// otherwise remove them
+int stdout_with_ansisequence = 1;
+
+int msg_silent; //Specifies how silent the console is.
+
+///////////////////////////////////////////////////////////////////////////////
+/// small reallocating temporary printer buffer
+static char *tempbuf = NULL;
+static size_t sz = 0;
+#define tempbuf_size() (sz)
+static void tempbuf_free(void){ free(tempbuf); }
+static void tempbuf_alloc(void){ sz = 256; tempbuf = (char *)malloc(sz); atexit(tempbuf_free); }
+static void tempbuf_realloc(void){ sz <<= 1; tempbuf = (char *)realloc(tempbuf,sz); }
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef _WIN32
+// XXX adapted from eApp (comments are left untouched) [flaviojs]
+
+///////////////////////////////////////////////////////////////////////////////
+// ansi compatible printf with control sequence parser for windows
+// fast hack, handle with care, not everything implemented
+//
+// \033[#;...;#m - Set Graphics Rendition (SGR)
+//
+// printf("\x1b[1;31;40m"); // Bright red on black
+// printf("\x1b[3;33;45m"); // Blinking yellow on magenta (blink not implemented)
+// printf("\x1b[1;30;47m"); // Bright black (grey) on dim white
+//
+// Style Foreground Background
+// 1st Digit 2nd Digit 3rd Digit RGB
+// 0 - Reset 30 - Black 40 - Black 000
+// 1 - FG Bright 31 - Red 41 - Red 100
+// 2 - Unknown 32 - Green 42 - Green 010
+// 3 - Blink 33 - Yellow 43 - Yellow 110
+// 4 - Underline 34 - Blue 44 - Blue 001
+// 5 - BG Bright 35 - Magenta 45 - Magenta 101
+// 6 - Unknown 36 - Cyan 46 - Cyan 011
+// 7 - Reverse 37 - White 47 - White 111
+// 8 - Concealed (invisible)
+//
+// \033[#A - Cursor Up (CUU)
+// Moves the cursor up by the specified number of lines without changing columns.
+// If the cursor is already on the top line, this sequence is ignored. \e[A is equivalent to \e[1A.
+//
+// \033[#B - Cursor Down (CUD)
+// Moves the cursor down by the specified number of lines without changing columns.
+// If the cursor is already on the bottom line, this sequence is ignored. \e[B is equivalent to \e[1B.
+//
+// \033[#C - Cursor Forward (CUF)
+// Moves the cursor forward by the specified number of columns without changing lines.
+// If the cursor is already in the rightmost column, this sequence is ignored. \e[C is equivalent to \e[1C.
+//
+// \033[#D - Cursor Backward (CUB)
+// Moves the cursor back by the specified number of columns without changing lines.
+// If the cursor is already in the leftmost column, this sequence is ignored. \e[D is equivalent to \e[1D.
+//
+// \033[#E - Cursor Next Line (CNL)
+// Moves the cursor down the indicated # of rows, to column 1. \e[E is equivalent to \e[1E.
+//
+// \033[#F - Cursor Preceding Line (CPL)
+// Moves the cursor up the indicated # of rows, to column 1. \e[F is equivalent to \e[1F.
+//
+// \033[#G - Cursor Horizontal Absolute (CHA)
+// Moves the cursor to indicated column in current row. \e[G is equivalent to \e[1G.
+//
+// \033[#;#H - Cursor Position (CUP)
+// Moves the cursor to the specified position. The first # specifies the line number,
+// the second # specifies the column. If you do not specify a position, the cursor moves to the home position:
+// the upper-left corner of the screen (line 1, column 1).
+//
+// \033[#;#f - Horizontal & Vertical Position
+// (same as \033[#;#H)
+//
+// \033[s - Save Cursor Position (SCP)
+// The current cursor position is saved.
+//
+// \033[u - Restore cursor position (RCP)
+// Restores the cursor position saved with the (SCP) sequence \033[s.
+// (addition, restore to 0,0 if nothinh was saved before)
+//
+
+// \033[#J - Erase Display (ED)
+// Clears the screen and moves to the home position
+// \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. (default)
+// \033[1J - Clears the screen from start to cursor. The cursor position is unchanged.
+// \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1).
+//
+// \033[#K - Erase Line (EL)
+// Clears the current line from the cursor position
+// \033[0K - Clears all characters from the cursor position to the end of the line (including the character at the cursor position). The cursor position is unchanged. (default)
+// \033[1K - Clears all characters from start of line to the cursor position. (including the character at the cursor position). The cursor position is unchanged.
+// \033[2K - Clears all characters of the whole line. The cursor position is unchanged.
+
+
+/*
+not implemented
+
+\033[#L
+IL: Insert Lines: The cursor line and all lines below it move down # lines, leaving blank space. The cursor position is unchanged. The bottommost # lines are lost. \e[L is equivalent to \e[1L.
+\033[#M
+DL: Delete Line: The block of # lines at and below the cursor are deleted; all lines below them move up # lines to fill in the gap, leaving # blank lines at the bottom of the screen. The cursor position is unchanged. \e[M is equivalent to \e[1M.
+\033[#\@
+ICH: Insert CHaracter: The cursor character and all characters to the right of it move right # columns, leaving behind blank space. The cursor position is unchanged. The rightmost # characters on the line are lost. \e[\@ is equivalent to \e[1\@.
+\033[#P
+DCH: Delete CHaracter: The block of # characters at and to the right of the cursor are deleted; all characters to the right of it move left # columns, leaving behind blank space. The cursor position is unchanged. \e[P is equivalent to \e[1P.
+
+Escape sequences for Select Character Set
+*/
+
+#define is_console(handle) (FILE_TYPE_CHAR==GetFileType(handle))
+
+///////////////////////////////////////////////////////////////////////////////
+int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr)
+{
+ /////////////////////////////////////////////////////////////////
+ /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs]
+ static COORD saveposition = {0,0};
+ */
+
+ /////////////////////////////////////////////////////////////////
+ unsigned long written;
+ char *p, *q;
+
+ if(!fmt || !*fmt)
+ return 0;
+
+ if(tempbuf == NULL)
+ tempbuf_alloc();
+ for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc());
+ // vsnprintf returns -1 in case of insufficient buffer size
+ // tempbuf_realloc doubles the size of the buffer in this case
+
+ if( !is_console(handle) && stdout_with_ansisequence )
+ {
+ WriteFile(handle,tempbuf, strlen(tempbuf), &written, 0);
+ return 0;
+ }
+
+ // start with processing
+ p = tempbuf;
+ while ((q = strchr(p, 0x1b)) != NULL)
+ { // find the escape character
+ if( 0==WriteConsole(handle, p, q-p, &written, 0) ) // write up to the escape
+ WriteFile(handle, p, q-p, &written, 0);
+
+ if( q[1]!='[' )
+ { // write the escape char (whatever purpose it has)
+ if(0==WriteConsole(handle, q, 1, &written, 0) )
+ WriteFile(handle,q, 1, &written, 0);
+ p=q+1; //and start searching again
+ }
+ else
+ { // from here, we will skip the '\033['
+ // we break at the first unprocessible position
+ // assuming regular text is starting there
+ uchar numbers[16], numpoint=0;
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ // initialize
+ GetConsoleScreenBufferInfo(handle, &info);
+ memset(numbers,0,sizeof(numbers));
+
+ // skip escape and bracket
+ q=q+2;
+ while(1)
+ {
+ if( isdigit((int)((unsigned char)*q)) )
+ { // add number to number array, only accept 2digits, shift out the rest
+ // so // \033[123456789m will become \033[89m
+ numbers[numpoint] = (numbers[numpoint]<<4) | (*q-'0');
+ ++q;
+ // and next character
+ continue;
+ }
+ else if( *q == ';' )
+ { // delimiter
+ if(numpoint<sizeof(numbers)/sizeof(*numbers))
+ { // go to next array position
+ numpoint++;
+ }
+ else
+ { // array is full, so we 'forget' the first value
+ memmove(numbers,numbers+1,sizeof(numbers)/sizeof(*numbers)-1);
+ numbers[sizeof(numbers)/sizeof(*numbers)-1]=0;
+ }
+ ++q;
+ // and next number
+ continue;
+ }
+ else if( *q == 'm' )
+ { // \033[#;...;#m - Set Graphics Rendition (SGR)
+ uint i;
+ for(i=0; i<= numpoint; ++i)
+ {
+ if( 0x00 == (0xF0 & numbers[i]) )
+ { // upper nibble 0
+ if( 0 == numbers[i] )
+ { // reset
+ info.wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ }
+ else if( 1==numbers[i] )
+ { // set foreground intensity
+ info.wAttributes |= FOREGROUND_INTENSITY;
+ }
+ else if( 5==numbers[i] )
+ { // set background intensity
+ info.wAttributes |= BACKGROUND_INTENSITY;
+ }
+ else if( 7==numbers[i] )
+ { // reverse colors (just xor them)
+ info.wAttributes ^= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
+ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
+ }
+ //case '2': // not existing
+ //case '3': // blinking (not implemented)
+ //case '4': // unterline (not implemented)
+ //case '6': // not existing
+ //case '8': // concealed (not implemented)
+ //case '9': // not existing
+ }
+ else if( 0x20 == (0xF0 & numbers[i]) )
+ { // off
+
+ if( 1==numbers[i] )
+ { // set foreground intensity off
+ info.wAttributes &= ~FOREGROUND_INTENSITY;
+ }
+ else if( 5==numbers[i] )
+ { // set background intensity off
+ info.wAttributes &= ~BACKGROUND_INTENSITY;
+ }
+ else if( 7==numbers[i] )
+ { // reverse colors (just xor them)
+ info.wAttributes ^= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
+ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
+ }
+ }
+ else if( 0x30 == (0xF0 & numbers[i]) )
+ { // foreground
+ uint num = numbers[i]&0x0F;
+ if(num==9) info.wAttributes |= FOREGROUND_INTENSITY;
+ if(num>7) num=7; // set white for 37, 38 and 39
+ info.wAttributes &= ~(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
+ if( (num & 0x01)>0 ) // lowest bit set = red
+ info.wAttributes |= FOREGROUND_RED;
+ if( (num & 0x02)>0 ) // second bit set = green
+ info.wAttributes |= FOREGROUND_GREEN;
+ if( (num & 0x04)>0 ) // third bit set = blue
+ info.wAttributes |= FOREGROUND_BLUE;
+ }
+ else if( 0x40 == (0xF0 & numbers[i]) )
+ { // background
+ uint num = numbers[i]&0x0F;
+ if(num==9) info.wAttributes |= BACKGROUND_INTENSITY;
+ if(num>7) num=7; // set white for 47, 48 and 49
+ info.wAttributes &= ~(BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE);
+ if( (num & 0x01)>0 ) // lowest bit set = red
+ info.wAttributes |= BACKGROUND_RED;
+ if( (num & 0x02)>0 ) // second bit set = green
+ info.wAttributes |= BACKGROUND_GREEN;
+ if( (num & 0x04)>0 ) // third bit set = blue
+ info.wAttributes |= BACKGROUND_BLUE;
+ }
+ }
+ // set the attributes
+ SetConsoleTextAttribute(handle, info.wAttributes);
+ }
+ else if( *q=='J' )
+ { // \033[#J - Erase Display (ED)
+ // \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged.
+ // \033[1J - Clears the screen from start to cursor. The cursor position is unchanged.
+ // \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1).
+ uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F);
+ int cnt;
+ COORD origin = {0,0};
+ if(num==1)
+ { // chars from start up to and including cursor
+ cnt = info.dwSize.X * info.dwCursorPosition.Y + info.dwCursorPosition.X + 1;
+ }
+ else if(num==2)
+ { // Number of chars on screen.
+ cnt = info.dwSize.X * info.dwSize.Y;
+ SetConsoleCursorPosition(handle, origin);
+ }
+ else// 0 and default
+ { // number of chars from cursor to end
+ origin = info.dwCursorPosition;
+ cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X;
+ }
+ FillConsoleOutputAttribute(handle,info.wAttributes,cnt,origin,NULL);
+ FillConsoleOutputCharacter(handle,' ', cnt,origin,NULL);
+ }
+ else if( *q=='K' )
+ { // \033[K : clear line from actual position to end of the line
+ // \033[0K - Clears all characters from the cursor position to the end of the line.
+ // \033[1K - Clears all characters from start of line to the cursor position.
+ // \033[2K - Clears all characters of the whole line.
+
+ uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F);
+ COORD origin = {0,info.dwCursorPosition.Y};
+ SHORT cnt;
+ if(num==1)
+ {
+ cnt = info.dwCursorPosition.X + 1;
+ }
+ else if(num==2)
+ {
+ cnt = info.dwSize.X;
+ }
+ else// 0 and default
+ {
+ origin = info.dwCursorPosition;
+ cnt = info.dwSize.X - info.dwCursorPosition.X; // how many spaces until line is full
+ }
+ FillConsoleOutputAttribute(handle, info.wAttributes, cnt, origin, NULL);
+ FillConsoleOutputCharacter(handle, ' ', cnt, origin, NULL);
+ }
+ else if( *q == 'H' || *q == 'f' )
+ { // \033[#;#H - Cursor Position (CUP)
+ // \033[#;#f - Horizontal & Vertical Position
+ // The first # specifies the line number, the second # specifies the column.
+ // The default for both is 1
+ info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0;
+ info.dwCursorPosition.Y = (numpoint && numbers[numpoint-1])?(numbers[numpoint-1]>>4)*10+(numbers[numpoint-1]&0x0F-1):0;
+
+ if( info.dwCursorPosition.X >= info.dwSize.X ) info.dwCursorPosition.Y = info.dwSize.X-1;
+ if( info.dwCursorPosition.Y >= info.dwSize.Y ) info.dwCursorPosition.Y = info.dwSize.Y-1;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q=='s' )
+ { // \033[s - Save Cursor Position (SCP)
+ /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs]
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo(handle, &info);
+ saveposition = info.dwCursorPosition;
+ */
+ }
+ else if( *q=='u' )
+ { // \033[u - Restore cursor position (RCP)
+ /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs]
+ SetConsoleCursorPosition(handle, saveposition);
+ */
+ }
+ else if( *q == 'A' )
+ { // \033[#A - Cursor Up (CUU)
+ // Moves the cursor UP # number of lines
+ info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
+
+ if( info.dwCursorPosition.Y < 0 )
+ info.dwCursorPosition.Y = 0;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'B' )
+ { // \033[#B - Cursor Down (CUD)
+ // Moves the cursor DOWN # number of lines
+ info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
+
+ if( info.dwCursorPosition.Y >= info.dwSize.Y )
+ info.dwCursorPosition.Y = info.dwSize.Y-1;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'C' )
+ { // \033[#C - Cursor Forward (CUF)
+ // Moves the cursor RIGHT # number of columns
+ info.dwCursorPosition.X += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
+
+ if( info.dwCursorPosition.X >= info.dwSize.X )
+ info.dwCursorPosition.X = info.dwSize.X-1;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'D' )
+ { // \033[#D - Cursor Backward (CUB)
+ // Moves the cursor LEFT # number of columns
+ info.dwCursorPosition.X -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
+
+ if( info.dwCursorPosition.X < 0 )
+ info.dwCursorPosition.X = 0;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'E' )
+ { // \033[#E - Cursor Next Line (CNL)
+ // Moves the cursor down the indicated # of rows, to column 1
+ info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
+ info.dwCursorPosition.X = 0;
+
+ if( info.dwCursorPosition.Y >= info.dwSize.Y )
+ info.dwCursorPosition.Y = info.dwSize.Y-1;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'F' )
+ { // \033[#F - Cursor Preceding Line (CPL)
+ // Moves the cursor up the indicated # of rows, to column 1.
+ info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1;
+ info.dwCursorPosition.X = 0;
+
+ if( info.dwCursorPosition.Y < 0 )
+ info.dwCursorPosition.Y = 0;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'G' )
+ { // \033[#G - Cursor Horizontal Absolute (CHA)
+ // Moves the cursor to indicated column in current row.
+ info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0;
+
+ if( info.dwCursorPosition.X >= info.dwSize.X )
+ info.dwCursorPosition.X = info.dwSize.X-1;
+ SetConsoleCursorPosition(handle, info.dwCursorPosition);
+ }
+ else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P')
+ { // not implemented, just skip
+ }
+ else
+ { // no number nor valid sequencer
+ // something is fishy, we break and give the current char free
+ --q;
+ }
+ // skip the sequencer and search again
+ p = q+1;
+ break;
+ }// end while
+ }
+ }
+ if (*p) // write the rest of the buffer
+ if( 0==WriteConsole(handle, p, strlen(p), &written, 0) )
+ WriteFile(handle,p, strlen(p), &written, 0);
+ return 0;
+}
+
+int FPRINTF(HANDLE handle, const char *fmt, ...)
+{
+ int ret;
+ va_list argptr;
+ va_start(argptr, fmt);
+ ret = VFPRINTF(handle,fmt,argptr);
+ va_end(argptr);
+ return ret;
+}
+
+#define FFLUSH(handle)
+
+#define STDOUT GetStdHandle(STD_OUTPUT_HANDLE)
+#define STDERR GetStdHandle(STD_ERROR_HANDLE)
+
+#else // not _WIN32
+
+
+//#define VPRINTF vprintf
+//#define PRINTF printf
+
+#define is_console(file) (0!=isatty(fileno(file)))
+
+//vprintf_without_ansiformats
+int VFPRINTF(FILE *file, const char *fmt, va_list argptr)
+{
+ char *p, *q;
+
+ if(!fmt || !*fmt)
+ return 0;
+
+ if( is_console(file) || stdout_with_ansisequence )
+ {
+ vfprintf(file, fmt, argptr);
+ return 0;
+ }
+
+ if(tempbuf == NULL)
+ tempbuf_alloc();
+ for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc());
+ // vsnprintf returns -1 in case of insufficient buffer size
+ // tempbuf.realloc doubles the size of the buffer in this case
+
+ // start with processing
+ p = tempbuf;
+ while ((q = strchr(p, 0x1b)) != NULL)
+ { // find the escape character
+ fprintf(file, "%.*s", (int)(q-p), p); // write up to the escape
+ if( q[1]!='[' )
+ { // write the escape char (whatever purpose it has)
+ fprintf(file, "%.*s", 1, q);
+ p=q+1; //and start searching again
+ }
+ else
+ { // from here, we will skip the '\033['
+ // we break at the first unprocessible position
+ // assuming regular text is starting there
+
+ // skip escape and bracket
+ q=q+2;
+ while(1)
+ {
+ if( isdigit((int)((unsigned char)*q)) )
+ {
+ ++q;
+ // and next character
+ continue;
+ }
+ else if( *q == ';' )
+ { // delimiter
+ ++q;
+ // and next number
+ continue;
+ }
+ else if( *q == 'm' )
+ { // \033[#;...;#m - Set Graphics Rendition (SGR)
+ // set the attributes
+ }
+ else if( *q=='J' )
+ { // \033[#J - Erase Display (ED)
+ }
+ else if( *q=='K' )
+ { // \033[K : clear line from actual position to end of the line
+ }
+ else if( *q == 'H' || *q == 'f' )
+ { // \033[#;#H - Cursor Position (CUP)
+ // \033[#;#f - Horizontal & Vertical Position
+ }
+ else if( *q=='s' )
+ { // \033[s - Save Cursor Position (SCP)
+ }
+ else if( *q=='u' )
+ { // \033[u - Restore cursor position (RCP)
+ }
+ else if( *q == 'A' )
+ { // \033[#A - Cursor Up (CUU)
+ // Moves the cursor UP # number of lines
+ }
+ else if( *q == 'B' )
+ { // \033[#B - Cursor Down (CUD)
+ // Moves the cursor DOWN # number of lines
+ }
+ else if( *q == 'C' )
+ { // \033[#C - Cursor Forward (CUF)
+ // Moves the cursor RIGHT # number of columns
+ }
+ else if( *q == 'D' )
+ { // \033[#D - Cursor Backward (CUB)
+ // Moves the cursor LEFT # number of columns
+ }
+ else if( *q == 'E' )
+ { // \033[#E - Cursor Next Line (CNL)
+ // Moves the cursor down the indicated # of rows, to column 1
+ }
+ else if( *q == 'F' )
+ { // \033[#F - Cursor Preceding Line (CPL)
+ // Moves the cursor up the indicated # of rows, to column 1.
+ }
+ else if( *q == 'G' )
+ { // \033[#G - Cursor Horizontal Absolute (CHA)
+ // Moves the cursor to indicated column in current row.
+ }
+ else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P')
+ { // not implemented, just skip
+ }
+ else
+ { // no number nor valid sequencer
+ // something is fishy, we break and give the current char free
+ --q;
+ }
+ // skip the sequencer and search again
+ p = q+1;
+ break;
+ }// end while
+ }
+ }
+ if (*p) // write the rest of the buffer
+ fprintf(file, "%s", p);
+ return 0;
+}
+int FPRINTF(FILE *file, const char *fmt, ...)
+{
+ int ret;
+ va_list argptr;
+ va_start(argptr, fmt);
+ ret = VFPRINTF(file,fmt,argptr);
+ va_end(argptr);
+ return ret;
+}
+
+#define FFLUSH fflush
+
+#define STDOUT stdout
+#define STDERR stderr
+
+#endif// not _WIN32
+
+
+
+
+
+
+
+
+
+
+char timestamp_format[20] = ""; //For displaying Timestamps
+
+// by MC Cameri
+int _vShowMessage(enum msg_type flag, const char *string, va_list ap)
+{
+ // _ShowMessage MUST be used instead of printf as of 10/24/2004.
+ // Return: 0 = Successful, 1 = Failed.
+// int ret = 0;
+ char prefix[100];
+#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN)
+ FILE *fp;
+#endif
+
+ if (!string || *string == '\0') {
+ ShowError("Empty string passed to _vShowMessage().\n");
+ return 1;
+ }
+ if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) ||
+ (flag == MSG_INFORMATION && msg_silent&1) ||
+ (flag == MSG_STATUS && msg_silent&2) ||
+ (flag == MSG_NOTICE && msg_silent&4) ||
+ (flag == MSG_WARNING && msg_silent&8) ||
+ (flag == MSG_ERROR && msg_silent&16) ||
+ (flag == MSG_SQL && msg_silent&16))
+ return 0; //Do not print it.
+
+ if (timestamp_format[0])
+ { //Display time format. [Skotlex]
+ time_t t = time(NULL);
+ strftime(prefix, 80, timestamp_format, localtime(&t));
+ } else prefix[0]='\0';
+
+ switch (flag) {
+ case MSG_NONE: // direct printf replacement
+ break;
+ case MSG_STATUS: //Bright Green (To inform about good things)
+ strcat(prefix,CL_GREEN"[Status]"CL_RESET":");
+ break;
+ case MSG_SQL: //Bright Violet (For dumping out anything related with SQL) <- Actually, this is mostly used for SQL errors with the database, as successes can as well just be anything else... [Skotlex]
+ strcat(prefix,CL_MAGENTA"[SQL]"CL_RESET":");
+ break;
+ case MSG_INFORMATION: //Bright White (Variable information)
+ strcat(prefix,CL_WHITE"[Info]"CL_RESET":");
+ break;
+ case MSG_NOTICE: //Bright White (Less than a warning)
+ strcat(prefix,CL_WHITE"[Notice]"CL_RESET":");
+ break;
+ case MSG_WARNING: //Bright Yellow
+ strcat(prefix,CL_YELLOW"[Warning]"CL_RESET":");
+ break;
+ case MSG_DEBUG: //Bright Cyan, important stuff!
+ strcat(prefix,CL_CYAN"[Debug]"CL_RESET":");
+ break;
+ case MSG_ERROR: //Bright Red (Regular errors)
+ strcat(prefix,CL_RED"[Error]"CL_RESET":");
+ break;
+ case MSG_FATALERROR: //Bright Red (Fatal errors, abort(); if possible)
+ strcat(prefix,CL_RED"[Fatal Error]"CL_RESET":");
+ break;
+ default:
+ ShowError("In function _vShowMessage() -> Invalid flag passed.\n");
+ return 1;
+ }
+
+ if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL)
+ { //Send Errors to StdErr [Skotlex]
+ FPRINTF(STDERR, "%s ", prefix);
+ VFPRINTF(STDERR, string, ap);
+ FFLUSH(STDERR);
+ } else {
+ if (flag != MSG_NONE)
+ FPRINTF(STDOUT, "%s ", prefix);
+ VFPRINTF(STDOUT, string, ap);
+ FFLUSH(STDOUT);
+ }
+
+#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN)
+ if(strlen(DEBUGLOGPATH) > 0) {
+ fp=fopen(DEBUGLOGPATH,"a");
+ if (fp == NULL) {
+ FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n", DEBUGLOGPATH);
+ FFLUSH(STDERR);
+ } else {
+ fprintf(fp,"%s ", prefix);
+ vfprintf(fp,string,ap);
+ fclose(fp);
+ }
+ } else {
+ FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n");
+ FFLUSH(STDERR);
+ }
+#endif
+
+ va_end(ap);
+ return 0;
+}
+
+void ClearScreen(void)
+{
+#ifndef _WIN32
+ ShowMessage(CL_CLS); // to prevent empty string passed messages
+#endif
+}
+int _ShowMessage(enum msg_type flag, const char *string, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(flag, string, ap);
+ va_end(ap);
+ return ret;
+}
+
+// direct printf replacement
+int ShowMessage(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_NONE, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowStatus(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_STATUS, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowSQL(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_SQL, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowInfo(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_INFORMATION, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowNotice(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_NOTICE, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowWarning(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_WARNING, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowDebug(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_DEBUG, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowError(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_ERROR, string, ap);
+ va_end(ap);
+ return ret;
+}
+int ShowFatalError(const char *string, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, string);
+ ret = _vShowMessage(MSG_FATALERROR, string, ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/src/common/showmsg.h b/src/common/showmsg.h
index f947b34e3..cf9c16d23 100644
--- a/src/common/showmsg.h
+++ b/src/common/showmsg.h
@@ -1,96 +1,96 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _SHOWMSG_H_
-#define _SHOWMSG_H_
-
-#define SHOW_DEBUG_MSG 1
-// for help with the console colors look here:
-// http://www.edoceo.com/liberum/?doc=printf-with-color
-// some code explanation (used here):
-// \033[2J : clear screen and go up/left (0, 0 position)
-// \033[K : clear line from actual position to end of the line
-// \033[0m : reset color parameter
-// \033[1m : use bold for font
-
-#define CL_RESET "\033[0m"
-#define CL_CLS "\033[2J"
-#define CL_CLL "\033[K"
-
-// font settings
-#define CL_BOLD "\033[1m"
-#define CL_NORM CL_RESET
-#define CL_NORMAL CL_RESET
-#define CL_NONE CL_RESET
-// foreground color and bold font (bright color on windows)
-#define CL_WHITE "\033[1;37m"
-#define CL_GRAY "\033[1;30m"
-#define CL_RED "\033[1;31m"
-#define CL_GREEN "\033[1;32m"
-#define CL_YELLOW "\033[1;33m"
-#define CL_BLUE "\033[1;34m"
-#define CL_MAGENTA "\033[1;35m"
-#define CL_CYAN "\033[1;36m"
-
-// background color
-#define CL_BG_BLACK "\033[40m"
-#define CL_BG_RED "\033[41m"
-#define CL_BG_GREEN "\033[42m"
-#define CL_BG_YELLOW "\033[43m"
-#define CL_BG_BLUE "\033[44m"
-#define CL_BG_MAGENTA "\033[45m"
-#define CL_BG_CYAN "\033[46m"
-#define CL_BG_WHITE "\033[47m"
-// foreground color and normal font (normal color on windows)
-#define CL_LT_BLACK "\033[0;30m"
-#define CL_LT_RED "\033[0;31m"
-#define CL_LT_GREEN "\033[0;32m"
-#define CL_LT_YELLOW "\033[0;33m"
-#define CL_LT_BLUE "\033[0;34m"
-#define CL_LT_MAGENTA "\033[0;35m"
-#define CL_LT_CYAN "\033[0;36m"
-#define CL_LT_WHITE "\033[0;37m"
-// foreground color and bold font (bright color on windows)
-#define CL_BT_BLACK "\033[1;30m"
-#define CL_BT_RED "\033[1;31m"
-#define CL_BT_GREEN "\033[1;32m"
-#define CL_BT_YELLOW "\033[1;33m"
-#define CL_BT_BLUE "\033[1;34m"
-#define CL_BT_MAGENTA "\033[1;35m"
-#define CL_BT_CYAN "\033[1;36m"
-#define CL_BT_WHITE "\033[1;37m"
-
-#define CL_WTBL "\033[37;44m" // white on blue
-#define CL_XXBL "\033[0;44m" // default on blue
-#define CL_PASS "\033[0;32;42m" // green on green
-
-#define CL_SPACE " " // space aquivalent of the print messages
-
-extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs]
-extern int msg_silent; //Specifies how silent the console is. [Skotlex]
-extern char timestamp_format[20]; //For displaying Timestamps [Skotlex]
-
-enum msg_type {
- MSG_NONE,
- MSG_STATUS,
- MSG_SQL,
- MSG_INFORMATION,
- MSG_NOTICE,
- MSG_WARNING,
- MSG_DEBUG,
- MSG_ERROR,
- MSG_FATALERROR
-};
-
-extern void ClearScreen(void);
-extern int ShowMessage(const char *, ...);
-extern int ShowStatus(const char *, ...);
-extern int ShowSQL(const char *, ...);
-extern int ShowInfo(const char *, ...);
-extern int ShowNotice(const char *, ...);
-extern int ShowWarning(const char *, ...);
-extern int ShowDebug(const char *, ...);
-extern int ShowError(const char *, ...);
-extern int ShowFatalError(const char *, ...);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SHOWMSG_H_
+#define _SHOWMSG_H_
+
+#define SHOW_DEBUG_MSG 1
+// for help with the console colors look here:
+// http://www.edoceo.com/liberum/?doc=printf-with-color
+// some code explanation (used here):
+// \033[2J : clear screen and go up/left (0, 0 position)
+// \033[K : clear line from actual position to end of the line
+// \033[0m : reset color parameter
+// \033[1m : use bold for font
+
+#define CL_RESET "\033[0m"
+#define CL_CLS "\033[2J"
+#define CL_CLL "\033[K"
+
+// font settings
+#define CL_BOLD "\033[1m"
+#define CL_NORM CL_RESET
+#define CL_NORMAL CL_RESET
+#define CL_NONE CL_RESET
+// foreground color and bold font (bright color on windows)
+#define CL_WHITE "\033[1;37m"
+#define CL_GRAY "\033[1;30m"
+#define CL_RED "\033[1;31m"
+#define CL_GREEN "\033[1;32m"
+#define CL_YELLOW "\033[1;33m"
+#define CL_BLUE "\033[1;34m"
+#define CL_MAGENTA "\033[1;35m"
+#define CL_CYAN "\033[1;36m"
+
+// background color
+#define CL_BG_BLACK "\033[40m"
+#define CL_BG_RED "\033[41m"
+#define CL_BG_GREEN "\033[42m"
+#define CL_BG_YELLOW "\033[43m"
+#define CL_BG_BLUE "\033[44m"
+#define CL_BG_MAGENTA "\033[45m"
+#define CL_BG_CYAN "\033[46m"
+#define CL_BG_WHITE "\033[47m"
+// foreground color and normal font (normal color on windows)
+#define CL_LT_BLACK "\033[0;30m"
+#define CL_LT_RED "\033[0;31m"
+#define CL_LT_GREEN "\033[0;32m"
+#define CL_LT_YELLOW "\033[0;33m"
+#define CL_LT_BLUE "\033[0;34m"
+#define CL_LT_MAGENTA "\033[0;35m"
+#define CL_LT_CYAN "\033[0;36m"
+#define CL_LT_WHITE "\033[0;37m"
+// foreground color and bold font (bright color on windows)
+#define CL_BT_BLACK "\033[1;30m"
+#define CL_BT_RED "\033[1;31m"
+#define CL_BT_GREEN "\033[1;32m"
+#define CL_BT_YELLOW "\033[1;33m"
+#define CL_BT_BLUE "\033[1;34m"
+#define CL_BT_MAGENTA "\033[1;35m"
+#define CL_BT_CYAN "\033[1;36m"
+#define CL_BT_WHITE "\033[1;37m"
+
+#define CL_WTBL "\033[37;44m" // white on blue
+#define CL_XXBL "\033[0;44m" // default on blue
+#define CL_PASS "\033[0;32;42m" // green on green
+
+#define CL_SPACE " " // space aquivalent of the print messages
+
+extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs]
+extern int msg_silent; //Specifies how silent the console is. [Skotlex]
+extern char timestamp_format[20]; //For displaying Timestamps [Skotlex]
+
+enum msg_type {
+ MSG_NONE,
+ MSG_STATUS,
+ MSG_SQL,
+ MSG_INFORMATION,
+ MSG_NOTICE,
+ MSG_WARNING,
+ MSG_DEBUG,
+ MSG_ERROR,
+ MSG_FATALERROR
+};
+
+extern void ClearScreen(void);
+extern int ShowMessage(const char *, ...);
+extern int ShowStatus(const char *, ...);
+extern int ShowSQL(const char *, ...);
+extern int ShowInfo(const char *, ...);
+extern int ShowNotice(const char *, ...);
+extern int ShowWarning(const char *, ...);
+extern int ShowDebug(const char *, ...);
+extern int ShowError(const char *, ...);
+extern int ShowFatalError(const char *, ...);
+
+#endif
diff --git a/src/common/strlib.h b/src/common/strlib.h
index 225228c74..d3b9a1dec 100644
--- a/src/common/strlib.h
+++ b/src/common/strlib.h
@@ -1,24 +1,24 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _J_STR_LIB_H_
-#define _J_STR_LIB_H_
-#define J_MAX_MALLOC_SIZE 65535
-// String function library.
-// code by Jioh L. Jung (ziozzang@4wish.net)
-// This code is under license "BSD"
-char* jstrescape (char* pt);
-char* jstrescapecpy (char* pt,char* spt);
-int jmemescapecpy (char* pt,char* spt, int size);
-
-#ifdef __WIN32
-#define HAVE_STRTOK_R
-#define strtok_r(s,delim,save_ptr) _strtok_r((s),(delim),(save_ptr))
-char *_strtok_r(char *s1, const char *s2, char **lasts);
-#endif
-
-// custom functions
-int remove_control_chars(unsigned char *);
-char *trim(char *str, const char *delim);
-const char *stristr(const char *haystack, const char *needle);
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _J_STR_LIB_H_
+#define _J_STR_LIB_H_
+#define J_MAX_MALLOC_SIZE 65535
+// String function library.
+// code by Jioh L. Jung (ziozzang@4wish.net)
+// This code is under license "BSD"
+char* jstrescape (char* pt);
+char* jstrescapecpy (char* pt,char* spt);
+int jmemescapecpy (char* pt,char* spt, int size);
+
+#ifdef __WIN32
+#define HAVE_STRTOK_R
+#define strtok_r(s,delim,save_ptr) _strtok_r((s),(delim),(save_ptr))
+char *_strtok_r(char *s1, const char *s2, char **lasts);
+#endif
+
+// custom functions
+int remove_control_chars(unsigned char *);
+char *trim(char *str, const char *delim);
+const char *stristr(const char *haystack, const char *needle);
+#endif
diff --git a/src/common/timer.c b/src/common/timer.c
index a848266ac..77d88d17d 100644
--- a/src/common/timer.c
+++ b/src/common/timer.c
@@ -1,436 +1,436 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <sys/types.h>
-
-#ifdef __WIN32
-#define __USE_W32_SOCKETS
-// Well, this won't last another 30++ years (where conversion will truncate).
-//#define _USE_32BIT_TIME_T // use 32 bit time variables on 64bit windows
-#include <windows.h>
-#else
-#include <sys/socket.h>
-#include <sys/time.h>
-#endif
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "timer.h"
-#include "malloc.h"
-#include "showmsg.h"
-
-// タイマー間隔の最小値。モンスターの大量召還時、多数のクライアント接続時に
-// サーバーが反応しなくなる場合は、TIMER_MIN_INTERVAL を増やしてください。
-
-// If the server shows no reaction when processing thousands of monsters
-// or connected by many clients, please increase TIMER_MIN_INTERVAL.
-
-#define TIMER_MIN_INTERVAL 50
-
-static struct TimerData* timer_data = NULL;
-static int timer_data_max = 0;
-static int timer_data_num = 0;
-
-static int* free_timer_list = NULL;
-static int free_timer_list_max = 0;
-static int free_timer_list_pos = 0;
-
-static int timer_heap_num = 0;
-static int timer_heap_max = 0;
-static int* timer_heap = NULL;
-
-static int fix_heap_flag =0; //Flag for fixing the stack only once per tick loop. May not be the best way, but it's all I can think of currently :X [Skotlex]
-
-// for debug
-struct timer_func_list {
- int (*func)(int,unsigned int,int,int);
- struct timer_func_list* next;
- char* name;
-};
-static struct timer_func_list* tfl_root;
-
-time_t start_time;
-
-#ifdef __WIN32
-/* Modified struct timezone to void - we pass NULL anyway */
-void gettimeofday (struct timeval *t, void *dummy)
-{
- DWORD millisec = GetTickCount();
-
- t->tv_sec = (int) (millisec / 1000);
- t->tv_usec = (millisec % 1000) * 1000;
-}
-#endif
-
-//
-int add_timer_func_list(int (*func)(int,unsigned int,int,int), char* name)
-{
- struct timer_func_list* tfl;
-
- if (name) {
- tfl = (struct timer_func_list*) aCalloc (sizeof(struct timer_func_list), 1);
- tfl->name = (char *) aMalloc (strlen(name) + 1);
-
- tfl->next = tfl_root;
- tfl->func = func;
- strcpy(tfl->name, name);
- tfl_root = tfl;
- }
- return 0;
-}
-
-char* search_timer_func_list(int (*func)(int,unsigned int,int,int))
-{
- struct timer_func_list* tfl = tfl_root;
- while (tfl) {
- if (func == tfl->func)
- return tfl->name;
- tfl = tfl->next;
- }
-
- return "unknown timer function";
-}
-
-/*----------------------------
- * Get tick time
- *----------------------------*/
-static unsigned int gettick_cache;
-static int gettick_count;
-
-unsigned int gettick_nocache(void)
-{
- struct timeval tval;
-
- gettimeofday(&tval, NULL);
- gettick_count = 256;
-
- return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000;
-}
-
-unsigned int gettick(void)
-{
- gettick_count--;
- if (gettick_count < 0)
- return gettick_nocache();
-
- return gettick_cache;
-}
-
-/*======================================
- * CORE : Timer Heap
- *--------------------------------------
- */
-static void push_timer_heap(int index)
-{
- int i, j;
- int min, max, pivot; // for sorting
-
- // check number of element
- if (timer_heap_num >= timer_heap_max) {
- if (timer_heap_max == 0) {
- timer_heap_max = 256;
- timer_heap = (int *) aCalloc( sizeof(int) , 256);
- } else {
- timer_heap_max += 256;
- timer_heap = (int *) aRealloc( timer_heap, sizeof(int) * timer_heap_max);
- malloc_tsetdword(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256);
- }
- }
-
- // do a sorting from higher to lower
- j = timer_data[index].tick; // speed up
- // with less than 4 values, it's speeder to use simple loop
- if (timer_heap_num < 4) {
- for(i = timer_heap_num; i > 0; i--)
-// if (j < timer_data[timer_heap[i - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
- if (DIFF_TICK(j, timer_data[timer_heap[i - 1]].tick) < 0)
- break;
- else
- timer_heap[i] = timer_heap[i - 1];
- timer_heap[i] = index;
- // searching by dichotomie
- } else {
- // if lower actual item is higher than new
-// if (j < timer_data[timer_heap[timer_heap_num - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
- if (DIFF_TICK(j, timer_data[timer_heap[timer_heap_num - 1]].tick) < 0)
- timer_heap[timer_heap_num] = index;
- else {
- // searching position
- min = 0;
- max = timer_heap_num - 1;
- while (min < max) {
- pivot = (min + max) / 2;
-// if (j < timer_data[timer_heap[pivot]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
- if (DIFF_TICK(j, timer_data[timer_heap[pivot]].tick) < 0)
- min = pivot + 1;
- else
- max = pivot;
- }
- // move elements - do loop if there are a little number of elements to move
- if (timer_heap_num - min < 5) {
- for(i = timer_heap_num; i > min; i--)
- timer_heap[i] = timer_heap[i - 1];
- // move elements - else use memmove (speeder for a lot of elements)
- } else
- memmove(&timer_heap[min + 1], &timer_heap[min], sizeof(int) * (timer_heap_num - min));
- // save new element
- timer_heap[min] = index;
- }
- }
-
- timer_heap_num++;
-}
-
-/*==========================
- * Timer Management
- *--------------------------
- */
-
-int acquire_timer (void)
-{
- int i;
-
- if (free_timer_list_pos) {
- do {
- i = free_timer_list[--free_timer_list_pos];
- } while(i >= timer_data_num && free_timer_list_pos > 0);
- } else
- i = timer_data_num;
-
- if (i >= timer_data_num)
- for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++);
- if (i >= timer_data_num && i >= timer_data_max) {
- if (timer_data_max == 0) {
- timer_data_max = 256;
- timer_data = (struct TimerData*) aCalloc( sizeof(struct TimerData) , timer_data_max);
- } else {
- timer_data_max += 256;
- timer_data = (struct TimerData *) aRealloc( timer_data, sizeof(struct TimerData) * timer_data_max);
- malloc_tsetdword(timer_data + (timer_data_max - 256), 0, sizeof(struct TimerData) * 256);
- }
- }
-
- return i;
-}
-
-int add_timer(unsigned int tick,int (*func)(int,unsigned int,int,int), int id, int data)
-{
- int tid = acquire_timer();
-
- timer_data[tid].tick = tick;
- timer_data[tid].func = func;
- timer_data[tid].id = id;
- timer_data[tid].data = data;
- timer_data[tid].type = TIMER_ONCE_AUTODEL;
- timer_data[tid].interval = 1000;
- push_timer_heap(tid);
-
- if (tid >= timer_data_num)
- timer_data_num = tid + 1;
-
- return tid;
-}
-
-int add_timer_interval(unsigned int tick, int (*func)(int,unsigned int,int,int), int id, int data, int interval)
-{
- int tid;
-
- if (interval < 1) {
- ShowError("add_timer_interval : function %08x(%s) has invalid interval %d!\n",
- (int)func, search_timer_func_list(func), interval);
- return -1;
- }
-
- tid = acquire_timer();
- timer_data[tid].tick = tick;
- timer_data[tid].func = func;
- timer_data[tid].id = id;
- timer_data[tid].data = data;
- timer_data[tid].type = TIMER_INTERVAL;
- timer_data[tid].interval = interval;
- push_timer_heap(tid);
-
- if (tid >= timer_data_num)
- timer_data_num = tid + 1;
-
- return tid;
-}
-
-int delete_timer(int id, int (*func)(int,unsigned int,int,int))
-{
- if (id <= 0 || id >= timer_data_num) {
- ShowError("delete_timer error : no such timer %d (%08x(%s))\n", id, (int)func, search_timer_func_list(func));
- return -1;
- }
- if (timer_data[id].func != func) {
- ShowError("delete_timer error : function mismatch %08x(%s) != %08x(%s)\n",
- (int)timer_data[id].func, search_timer_func_list(timer_data[id].func),
- (int)func, search_timer_func_list(func));
- return -2;
- }
- // そのうち消えるにまかせる
- timer_data[id].func = NULL;
- timer_data[id].type = TIMER_ONCE_AUTODEL;
-
- return 0;
-}
-
-int addtick_timer(int tid, unsigned int tick)
-{
- return timer_data[tid].tick += tick;
-}
-
-//Sets the tick at which the timer triggers directly (meant as a replacement of delete_timer + add_timer) [Skotlex]
-//FIXME: DON'T use this function yet, it is not correctly reorganizing the timer stack causing unexpected problems later on!
-int settick_timer(int tid, unsigned int tick)
-{
- int i,j;
- if (timer_data[tid].tick == tick)
- return tick;
-
- //FIXME: This search is not all that effective... there doesn't seems to be a better way to locate an element in the heap.
- for(i = timer_heap_num-1; i >= 0 && timer_heap[i] != tid; i--);
-
- if (i < 0)
- return -1; //Sort of impossible, isn't it?
- if (DIFF_TICK(timer_data[tid].tick, tick) > 0)
- { //Timer is accelerated, shift timer near the end of the heap.
- if (i == timer_heap_num-1) //Nothing to shift.
- j = timer_heap_num-1;
- else {
- for (j = i+1; j < timer_heap_num && DIFF_TICK(timer_data[j].tick, tick) > 0; j++);
- j--;
- memmove(&timer_heap[i], &timer_heap[i+1], (j-i)*sizeof(int));
- }
- } else { //Timer is delayed, shift timer near the beginning of the heap.
- if (i == 0) //Nothing to shift.
- j = 0;
- else {
- for (j = i-1; j >= 0 && DIFF_TICK(timer_data[j].tick, tick) < 0; j--);
- j++;
- memmove(&timer_heap[j+1], &timer_heap[j], (i-j)*sizeof(int));
- }
- }
- timer_heap[j] = tid;
- timer_data[tid].tick = tick;
- return tick;
-}
-
-struct TimerData* get_timer(int tid)
-{
- return &timer_data[tid];
-}
-
-//Correcting the heap when the tick overflows is an idea taken from jA to
-//prevent timer problems. Thanks to [End of Exam] for providing the required data. [Skotlex]
-//This funtion will rearrange the heap and assign new tick values.
-static void fix_timer_heap(unsigned int tick)
-{
- if (timer_heap_num >= 0 && tick < 0x00010000 && timer_data[timer_heap[0]].tick > 0xf0000000)
- { //The last timer is way too far into the future, and the current tick is too close to 0, overflow was very likely
- //(not perfect, but will work as long as the timer is not expected to happen 50 or so days into the future)
- int i;
- int *tmp_heap;
- for (i=0; i < timer_heap_num && timer_data[timer_heap[i]].tick > 0xf0000000; i++)
- { //All functions with high tick value should had been executed already...
- timer_data[timer_heap[i]].tick = 0;
- }
- //Move elements to readjust the heap.
- tmp_heap = aCalloc(sizeof(int), i);
- memmove(&tmp_heap[0], &timer_heap[0], i*sizeof(int));
- memmove(&timer_heap[0], &timer_heap[i], (timer_heap_num-i)*sizeof(int));
- memmove(&timer_heap[timer_heap_num-i], &tmp_heap[0], i*sizeof(int));
- aFree(tmp_heap);
- }
-}
-
-int do_timer(unsigned int tick)
-{
- int i, nextmin = 1000;
-
- if (tick < 0x010000 && fix_heap_flag)
- {
- fix_timer_heap(tick);
- fix_heap_flag = 0;
- }
-
- while(timer_heap_num) {
- i = timer_heap[timer_heap_num - 1]; // next shorter element
- if ((nextmin = DIFF_TICK(timer_data[i].tick, tick)) > 0)
- break;
- if (timer_heap_num > 0) // suppress the actual element from the table
- timer_heap_num--;
- timer_data[i].type |= TIMER_REMOVE_HEAP;
- if (timer_data[i].func) {
- if (nextmin < -1000) {
- // 1秒以上の大幅な遅延が発生しているので、
- // timer処理タイミングを現在値とする事で
- // 呼び出し時タイミング(引数のtick)相対で処理してる
- // timer関数の次回処理タイミングを遅らせる
- timer_data[i].func(i, tick, timer_data[i].id, timer_data[i].data);
- } else {
- timer_data[i].func(i, timer_data[i].tick, timer_data[i].id, timer_data[i].data);
- }
- }
- if (timer_data[i].type & TIMER_REMOVE_HEAP) {
- switch(timer_data[i].type & ~TIMER_REMOVE_HEAP) {
- case TIMER_ONCE_AUTODEL:
- timer_data[i].type = 0;
- if (free_timer_list_pos >= free_timer_list_max) {
- free_timer_list_max += 256;
- free_timer_list = (int *) aRealloc(free_timer_list, sizeof(int) * free_timer_list_max);
- malloc_tsetdword(free_timer_list + (free_timer_list_max - 256), 0, 256 * sizeof(int));
- }
- free_timer_list[free_timer_list_pos++] = i;
- break;
- case TIMER_INTERVAL:
- if (DIFF_TICK(timer_data[i].tick , tick) < -1000) {
- timer_data[i].tick = tick + timer_data[i].interval;
- } else {
- timer_data[i].tick += timer_data[i].interval;
- }
- timer_data[i].type &= ~TIMER_REMOVE_HEAP;
- push_timer_heap(i);
- break;
- }
- }
- }
-
- if (nextmin < TIMER_MIN_INTERVAL)
- nextmin = TIMER_MIN_INTERVAL;
-
- if ((unsigned int)(tick + nextmin) < tick) //Tick will loop, rearrange the heap on the next iteration.
- fix_heap_flag = 1;
- return nextmin;
-}
-
-unsigned long get_uptime (void)
-{
- return (unsigned long) difftime (time(NULL), start_time);
-}
-
-void timer_init(void)
-{
- time(&start_time);
-}
-
-void timer_final(void)
-{
- struct timer_func_list* tfl = tfl_root, *tfl2;
-
- while (tfl) {
- tfl2 = tfl->next; // copy next pointer
- aFree(tfl->name); // free structures
- aFree(tfl);
- tfl = tfl2; // use copied pointer for next cycle
- }
-
- if (timer_data) aFree(timer_data);
- if (timer_heap) aFree(timer_heap);
- if (free_timer_list) aFree(free_timer_list);
-}
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <sys/types.h>
+
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+// Well, this won't last another 30++ years (where conversion will truncate).
+//#define _USE_32BIT_TIME_T // use 32 bit time variables on 64bit windows
+#include <windows.h>
+#else
+#include <sys/socket.h>
+#include <sys/time.h>
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "timer.h"
+#include "malloc.h"
+#include "showmsg.h"
+
+// タイマー間隔の最小値。モンスターの大量召還時、多数のクライアント接続時に
+// サーバーが反応しなくなる場合は、TIMER_MIN_INTERVAL を増やしてください。
+
+// If the server shows no reaction when processing thousands of monsters
+// or connected by many clients, please increase TIMER_MIN_INTERVAL.
+
+#define TIMER_MIN_INTERVAL 50
+
+static struct TimerData* timer_data = NULL;
+static int timer_data_max = 0;
+static int timer_data_num = 0;
+
+static int* free_timer_list = NULL;
+static int free_timer_list_max = 0;
+static int free_timer_list_pos = 0;
+
+static int timer_heap_num = 0;
+static int timer_heap_max = 0;
+static int* timer_heap = NULL;
+
+static int fix_heap_flag =0; //Flag for fixing the stack only once per tick loop. May not be the best way, but it's all I can think of currently :X [Skotlex]
+
+// for debug
+struct timer_func_list {
+ int (*func)(int,unsigned int,int,int);
+ struct timer_func_list* next;
+ char* name;
+};
+static struct timer_func_list* tfl_root;
+
+time_t start_time;
+
+#ifdef __WIN32
+/* Modified struct timezone to void - we pass NULL anyway */
+void gettimeofday (struct timeval *t, void *dummy)
+{
+ DWORD millisec = GetTickCount();
+
+ t->tv_sec = (int) (millisec / 1000);
+ t->tv_usec = (millisec % 1000) * 1000;
+}
+#endif
+
+//
+int add_timer_func_list(int (*func)(int,unsigned int,int,int), char* name)
+{
+ struct timer_func_list* tfl;
+
+ if (name) {
+ tfl = (struct timer_func_list*) aCalloc (sizeof(struct timer_func_list), 1);
+ tfl->name = (char *) aMalloc (strlen(name) + 1);
+
+ tfl->next = tfl_root;
+ tfl->func = func;
+ strcpy(tfl->name, name);
+ tfl_root = tfl;
+ }
+ return 0;
+}
+
+char* search_timer_func_list(int (*func)(int,unsigned int,int,int))
+{
+ struct timer_func_list* tfl = tfl_root;
+ while (tfl) {
+ if (func == tfl->func)
+ return tfl->name;
+ tfl = tfl->next;
+ }
+
+ return "unknown timer function";
+}
+
+/*----------------------------
+ * Get tick time
+ *----------------------------*/
+static unsigned int gettick_cache;
+static int gettick_count;
+
+unsigned int gettick_nocache(void)
+{
+ struct timeval tval;
+
+ gettimeofday(&tval, NULL);
+ gettick_count = 256;
+
+ return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000;
+}
+
+unsigned int gettick(void)
+{
+ gettick_count--;
+ if (gettick_count < 0)
+ return gettick_nocache();
+
+ return gettick_cache;
+}
+
+/*======================================
+ * CORE : Timer Heap
+ *--------------------------------------
+ */
+static void push_timer_heap(int index)
+{
+ int i, j;
+ int min, max, pivot; // for sorting
+
+ // check number of element
+ if (timer_heap_num >= timer_heap_max) {
+ if (timer_heap_max == 0) {
+ timer_heap_max = 256;
+ timer_heap = (int *) aCalloc( sizeof(int) , 256);
+ } else {
+ timer_heap_max += 256;
+ timer_heap = (int *) aRealloc( timer_heap, sizeof(int) * timer_heap_max);
+ malloc_tsetdword(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256);
+ }
+ }
+
+ // do a sorting from higher to lower
+ j = timer_data[index].tick; // speed up
+ // with less than 4 values, it's speeder to use simple loop
+ if (timer_heap_num < 4) {
+ for(i = timer_heap_num; i > 0; i--)
+// if (j < timer_data[timer_heap[i - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
+ if (DIFF_TICK(j, timer_data[timer_heap[i - 1]].tick) < 0)
+ break;
+ else
+ timer_heap[i] = timer_heap[i - 1];
+ timer_heap[i] = index;
+ // searching by dichotomie
+ } else {
+ // if lower actual item is higher than new
+// if (j < timer_data[timer_heap[timer_heap_num - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
+ if (DIFF_TICK(j, timer_data[timer_heap[timer_heap_num - 1]].tick) < 0)
+ timer_heap[timer_heap_num] = index;
+ else {
+ // searching position
+ min = 0;
+ max = timer_heap_num - 1;
+ while (min < max) {
+ pivot = (min + max) / 2;
+// if (j < timer_data[timer_heap[pivot]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
+ if (DIFF_TICK(j, timer_data[timer_heap[pivot]].tick) < 0)
+ min = pivot + 1;
+ else
+ max = pivot;
+ }
+ // move elements - do loop if there are a little number of elements to move
+ if (timer_heap_num - min < 5) {
+ for(i = timer_heap_num; i > min; i--)
+ timer_heap[i] = timer_heap[i - 1];
+ // move elements - else use memmove (speeder for a lot of elements)
+ } else
+ memmove(&timer_heap[min + 1], &timer_heap[min], sizeof(int) * (timer_heap_num - min));
+ // save new element
+ timer_heap[min] = index;
+ }
+ }
+
+ timer_heap_num++;
+}
+
+/*==========================
+ * Timer Management
+ *--------------------------
+ */
+
+int acquire_timer (void)
+{
+ int i;
+
+ if (free_timer_list_pos) {
+ do {
+ i = free_timer_list[--free_timer_list_pos];
+ } while(i >= timer_data_num && free_timer_list_pos > 0);
+ } else
+ i = timer_data_num;
+
+ if (i >= timer_data_num)
+ for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++);
+ if (i >= timer_data_num && i >= timer_data_max) {
+ if (timer_data_max == 0) {
+ timer_data_max = 256;
+ timer_data = (struct TimerData*) aCalloc( sizeof(struct TimerData) , timer_data_max);
+ } else {
+ timer_data_max += 256;
+ timer_data = (struct TimerData *) aRealloc( timer_data, sizeof(struct TimerData) * timer_data_max);
+ malloc_tsetdword(timer_data + (timer_data_max - 256), 0, sizeof(struct TimerData) * 256);
+ }
+ }
+
+ return i;
+}
+
+int add_timer(unsigned int tick,int (*func)(int,unsigned int,int,int), int id, int data)
+{
+ int tid = acquire_timer();
+
+ timer_data[tid].tick = tick;
+ timer_data[tid].func = func;
+ timer_data[tid].id = id;
+ timer_data[tid].data = data;
+ timer_data[tid].type = TIMER_ONCE_AUTODEL;
+ timer_data[tid].interval = 1000;
+ push_timer_heap(tid);
+
+ if (tid >= timer_data_num)
+ timer_data_num = tid + 1;
+
+ return tid;
+}
+
+int add_timer_interval(unsigned int tick, int (*func)(int,unsigned int,int,int), int id, int data, int interval)
+{
+ int tid;
+
+ if (interval < 1) {
+ ShowError("add_timer_interval : function %08x(%s) has invalid interval %d!\n",
+ (int)func, search_timer_func_list(func), interval);
+ return -1;
+ }
+
+ tid = acquire_timer();
+ timer_data[tid].tick = tick;
+ timer_data[tid].func = func;
+ timer_data[tid].id = id;
+ timer_data[tid].data = data;
+ timer_data[tid].type = TIMER_INTERVAL;
+ timer_data[tid].interval = interval;
+ push_timer_heap(tid);
+
+ if (tid >= timer_data_num)
+ timer_data_num = tid + 1;
+
+ return tid;
+}
+
+int delete_timer(int id, int (*func)(int,unsigned int,int,int))
+{
+ if (id <= 0 || id >= timer_data_num) {
+ ShowError("delete_timer error : no such timer %d (%08x(%s))\n", id, (int)func, search_timer_func_list(func));
+ return -1;
+ }
+ if (timer_data[id].func != func) {
+ ShowError("delete_timer error : function mismatch %08x(%s) != %08x(%s)\n",
+ (int)timer_data[id].func, search_timer_func_list(timer_data[id].func),
+ (int)func, search_timer_func_list(func));
+ return -2;
+ }
+ // そのうち消えるにまかせる
+ timer_data[id].func = NULL;
+ timer_data[id].type = TIMER_ONCE_AUTODEL;
+
+ return 0;
+}
+
+int addtick_timer(int tid, unsigned int tick)
+{
+ return timer_data[tid].tick += tick;
+}
+
+//Sets the tick at which the timer triggers directly (meant as a replacement of delete_timer + add_timer) [Skotlex]
+//FIXME: DON'T use this function yet, it is not correctly reorganizing the timer stack causing unexpected problems later on!
+int settick_timer(int tid, unsigned int tick)
+{
+ int i,j;
+ if (timer_data[tid].tick == tick)
+ return tick;
+
+ //FIXME: This search is not all that effective... there doesn't seems to be a better way to locate an element in the heap.
+ for(i = timer_heap_num-1; i >= 0 && timer_heap[i] != tid; i--);
+
+ if (i < 0)
+ return -1; //Sort of impossible, isn't it?
+ if (DIFF_TICK(timer_data[tid].tick, tick) > 0)
+ { //Timer is accelerated, shift timer near the end of the heap.
+ if (i == timer_heap_num-1) //Nothing to shift.
+ j = timer_heap_num-1;
+ else {
+ for (j = i+1; j < timer_heap_num && DIFF_TICK(timer_data[j].tick, tick) > 0; j++);
+ j--;
+ memmove(&timer_heap[i], &timer_heap[i+1], (j-i)*sizeof(int));
+ }
+ } else { //Timer is delayed, shift timer near the beginning of the heap.
+ if (i == 0) //Nothing to shift.
+ j = 0;
+ else {
+ for (j = i-1; j >= 0 && DIFF_TICK(timer_data[j].tick, tick) < 0; j--);
+ j++;
+ memmove(&timer_heap[j+1], &timer_heap[j], (i-j)*sizeof(int));
+ }
+ }
+ timer_heap[j] = tid;
+ timer_data[tid].tick = tick;
+ return tick;
+}
+
+struct TimerData* get_timer(int tid)
+{
+ return &timer_data[tid];
+}
+
+//Correcting the heap when the tick overflows is an idea taken from jA to
+//prevent timer problems. Thanks to [End of Exam] for providing the required data. [Skotlex]
+//This funtion will rearrange the heap and assign new tick values.
+static void fix_timer_heap(unsigned int tick)
+{
+ if (timer_heap_num >= 0 && tick < 0x00010000 && timer_data[timer_heap[0]].tick > 0xf0000000)
+ { //The last timer is way too far into the future, and the current tick is too close to 0, overflow was very likely
+ //(not perfect, but will work as long as the timer is not expected to happen 50 or so days into the future)
+ int i;
+ int *tmp_heap;
+ for (i=0; i < timer_heap_num && timer_data[timer_heap[i]].tick > 0xf0000000; i++)
+ { //All functions with high tick value should had been executed already...
+ timer_data[timer_heap[i]].tick = 0;
+ }
+ //Move elements to readjust the heap.
+ tmp_heap = aCalloc(sizeof(int), i);
+ memmove(&tmp_heap[0], &timer_heap[0], i*sizeof(int));
+ memmove(&timer_heap[0], &timer_heap[i], (timer_heap_num-i)*sizeof(int));
+ memmove(&timer_heap[timer_heap_num-i], &tmp_heap[0], i*sizeof(int));
+ aFree(tmp_heap);
+ }
+}
+
+int do_timer(unsigned int tick)
+{
+ int i, nextmin = 1000;
+
+ if (tick < 0x010000 && fix_heap_flag)
+ {
+ fix_timer_heap(tick);
+ fix_heap_flag = 0;
+ }
+
+ while(timer_heap_num) {
+ i = timer_heap[timer_heap_num - 1]; // next shorter element
+ if ((nextmin = DIFF_TICK(timer_data[i].tick, tick)) > 0)
+ break;
+ if (timer_heap_num > 0) // suppress the actual element from the table
+ timer_heap_num--;
+ timer_data[i].type |= TIMER_REMOVE_HEAP;
+ if (timer_data[i].func) {
+ if (nextmin < -1000) {
+ // 1秒以上の大幅な遅延が発生しているので、
+ // timer処理タイミングを現在値とする事で
+ // 呼び出し時タイミング(引数のtick)相対で処理してる
+ // timer関数の次回処理タイミングを遅らせる
+ timer_data[i].func(i, tick, timer_data[i].id, timer_data[i].data);
+ } else {
+ timer_data[i].func(i, timer_data[i].tick, timer_data[i].id, timer_data[i].data);
+ }
+ }
+ if (timer_data[i].type & TIMER_REMOVE_HEAP) {
+ switch(timer_data[i].type & ~TIMER_REMOVE_HEAP) {
+ case TIMER_ONCE_AUTODEL:
+ timer_data[i].type = 0;
+ if (free_timer_list_pos >= free_timer_list_max) {
+ free_timer_list_max += 256;
+ free_timer_list = (int *) aRealloc(free_timer_list, sizeof(int) * free_timer_list_max);
+ malloc_tsetdword(free_timer_list + (free_timer_list_max - 256), 0, 256 * sizeof(int));
+ }
+ free_timer_list[free_timer_list_pos++] = i;
+ break;
+ case TIMER_INTERVAL:
+ if (DIFF_TICK(timer_data[i].tick , tick) < -1000) {
+ timer_data[i].tick = tick + timer_data[i].interval;
+ } else {
+ timer_data[i].tick += timer_data[i].interval;
+ }
+ timer_data[i].type &= ~TIMER_REMOVE_HEAP;
+ push_timer_heap(i);
+ break;
+ }
+ }
+ }
+
+ if (nextmin < TIMER_MIN_INTERVAL)
+ nextmin = TIMER_MIN_INTERVAL;
+
+ if ((unsigned int)(tick + nextmin) < tick) //Tick will loop, rearrange the heap on the next iteration.
+ fix_heap_flag = 1;
+ return nextmin;
+}
+
+unsigned long get_uptime (void)
+{
+ return (unsigned long) difftime (time(NULL), start_time);
+}
+
+void timer_init(void)
+{
+ time(&start_time);
+}
+
+void timer_final(void)
+{
+ struct timer_func_list* tfl = tfl_root, *tfl2;
+
+ while (tfl) {
+ tfl2 = tfl->next; // copy next pointer
+ aFree(tfl->name); // free structures
+ aFree(tfl);
+ tfl = tfl2; // use copied pointer for next cycle
+ }
+
+ if (timer_data) aFree(timer_data);
+ if (timer_heap) aFree(timer_heap);
+ if (free_timer_list) aFree(free_timer_list);
+}
+
diff --git a/src/common/timer.h b/src/common/timer.h
index aafefd1e2..9acc7c640 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -1,60 +1,60 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _TIMER_H_
-#define _TIMER_H_
-
-#ifdef __WIN32
-/* We need winsock lib to have timeval struct - windows is weirdo */
-#define __USE_W32_SOCKETS
-#include <windows.h>
-#endif
-
-#define BASE_TICK 5
-
-#define TIMER_ONCE_AUTODEL 1
-#define TIMER_INTERVAL 2
-#define TIMER_REMOVE_HEAP 16
-
-#define DIFF_TICK(a,b) ((int)((a)-(b)))
-
-// Struct declaration
-
-struct TimerData {
- unsigned int tick;
- int (*func)(int,unsigned int,int,int);
- int id;
- int data;
- int type;
- int interval;
- int heap_pos;
-};
-
-// Function prototype declaration
-
-#ifdef __WIN32
-void gettimeofday(struct timeval *t, void *dummy);
-#endif
-
-unsigned int gettick_nocache(void);
-unsigned int gettick(void);
-
-int add_timer(unsigned int,int (*)(int,unsigned int,int,int),int,int);
-int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int);
-int delete_timer(int,int (*)(int,unsigned int,int,int));
-
-int addtick_timer(int tid,unsigned int tick);
-int settick_timer(int tid,unsigned int tick);
-struct TimerData *get_timer(int tid);
-
-int do_timer(unsigned int tick);
-
-int add_timer_func_list(int (*)(int,unsigned int,int,int),char*);
-char* search_timer_func_list(int (*)(int,unsigned int,int,int));
-
-unsigned long get_uptime(void);
-
-void timer_init(void);
-void timer_final(void);
-
-#endif // _TIMER_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#ifdef __WIN32
+/* We need winsock lib to have timeval struct - windows is weirdo */
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#endif
+
+#define BASE_TICK 5
+
+#define TIMER_ONCE_AUTODEL 1
+#define TIMER_INTERVAL 2
+#define TIMER_REMOVE_HEAP 16
+
+#define DIFF_TICK(a,b) ((int)((a)-(b)))
+
+// Struct declaration
+
+struct TimerData {
+ unsigned int tick;
+ int (*func)(int,unsigned int,int,int);
+ int id;
+ int data;
+ int type;
+ int interval;
+ int heap_pos;
+};
+
+// Function prototype declaration
+
+#ifdef __WIN32
+void gettimeofday(struct timeval *t, void *dummy);
+#endif
+
+unsigned int gettick_nocache(void);
+unsigned int gettick(void);
+
+int add_timer(unsigned int,int (*)(int,unsigned int,int,int),int,int);
+int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int);
+int delete_timer(int,int (*)(int,unsigned int,int,int));
+
+int addtick_timer(int tid,unsigned int tick);
+int settick_timer(int tid,unsigned int tick);
+struct TimerData *get_timer(int tid);
+
+int do_timer(unsigned int tick);
+
+int add_timer_func_list(int (*)(int,unsigned int,int,int),char*);
+char* search_timer_func_list(int (*)(int,unsigned int,int,int));
+
+unsigned long get_uptime(void);
+
+void timer_init(void);
+void timer_final(void);
+
+#endif // _TIMER_H_
diff --git a/src/common/utils.c b/src/common/utils.c
index bbf9f8398..3172e89d9 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -1,384 +1,384 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef WIN32
- #include <windows.h>
- #define PATHSEP '\\'
-#else
- #include <unistd.h>
- #include <dirent.h>
- #include <sys/stat.h>
- #define PATHSEP '/'
-#endif
-
-#include "utils.h"
-#include "../common/mmo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-
-void dump(unsigned char *buffer, int num)
-{
- int icnt,jcnt;
-
- printf(" Hex ASCII\n");
- printf(" ----------------------------------------------- ----------------");
-
- for (icnt=0;icnt<num;icnt+=16) {
- printf("\n%p ",&buffer[icnt]);
- for (jcnt=icnt;jcnt<icnt+16;++jcnt) {
- if (jcnt < num) {
- printf("%02hX ",buffer[jcnt]);
- } else
- printf(" ");
- }
-
- printf(" | ");
-
- for (jcnt=icnt;jcnt<icnt+16;++jcnt) {
- if (jcnt < num) {
- if (buffer[jcnt] > 31 && buffer[jcnt] < 127)
- printf("%c",buffer[jcnt]);
- else
- printf(".");
- } else
- printf(" ");
- }
- }
- printf("\n");
-}
-
-//NOTE: There is no need to use this function as the standard sqrt is plenty fast as it is. [Skotlex]
-int newt_sqrt(int input)
-{
- int new_value, value = input/2, count = 0;
- if (!value) //Division by zero fix, pointed out by Shinomori. [Skotlex]
- return input;
- do
- {
- new_value = (value + input/value)>>1;
- if (abs(value - new_value) <= 1)
- return new_value;
- value = new_value;
- }
- while (count++ < 25);
- return new_value;
-}
-
-#if defined(_WIN32) && !defined(MINGW)
-char *rindex(char *str, char c)
-{
- char *sptr;
-
- sptr = str;
- while(*sptr)
- ++sptr;
- if (c == '\0')
- return(sptr);
- while(str != sptr)
- if (*sptr-- == c)
- return(++sptr);
- return(NULL);
-}
-
-int strcasecmp(const char *arg1, const char *arg2)
-{
- int chk, i;
-
- if (arg1 == NULL || arg2 == NULL) {
- ShowError("strcasecmp: received a NULL pointer, %p or %p.\n", arg1, arg2);
- return (0);
- }
-
- for (i = 0; arg1[i] || arg2[i]; i++)
- if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
- return (chk); /* not equal */
-
- return (0);
-}
-
-int strncasecmp(const char *arg1, const char *arg2, size_t n)
-{
- int chk, i;
-
- if (arg1 == NULL || arg2 == NULL) {
- ShowError("strncasecmp(): received a NULL pointer, %p or %p.\n", arg1, arg2);
- return (0);
- }
-
- for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--)
- if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
- return (chk); /* not equal */
-
- return (0);
-}
-
-void str_upper(char *name)
-{
-
- int len = (int)strlen(name);
- while (len--) {
- if (*name >= 'a' && *name <= 'z')
- *name -= ('a' - 'A');
- name++;
- }
-}
-
-void str_lower(char *name)
-{
- int len = (int)strlen(name);
-
- while (len--) {
- if (*name >= 'A' && *name <= 'Z')
- *name += ('a' - 'A');
- name++;
- }
-}
-
-#endif
-
-// Allocate a StringBuf [MouseJstr]
-struct StringBuf * StringBuf_Malloc()
-{
- struct StringBuf * ret = (struct StringBuf *) aMallocA(sizeof(struct StringBuf));
- StringBuf_Init(ret);
- return ret;
-}
-
-// Initialize a previously allocated StringBuf [MouseJstr]
-void StringBuf_Init(struct StringBuf * sbuf) {
- sbuf->max_ = 1024;
- sbuf->ptr_ = sbuf->buf_ = (char *) aMallocA(sbuf->max_ + 1);
-}
-
-// printf into a StringBuf, moving the pointer [MouseJstr]
-int StringBuf_Printf(struct StringBuf *sbuf,const char *fmt,...)
-{
- va_list ap;
- int n, size, off;
-
- while (1) {
- /* Try to print in the allocated space. */
- va_start(ap, fmt);
- size = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_);
- n = vsnprintf (sbuf->ptr_, size, fmt, ap);
- va_end(ap);
- /* If that worked, return the length. */
- if (n > -1 && n < size) {
- sbuf->ptr_ += n;
- return (int)(sbuf->ptr_ - sbuf->buf_);
- }
- /* Else try again with more space. */
- sbuf->max_ *= 2; // twice the old size
- off = (int)(sbuf->ptr_ - sbuf->buf_);
- sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1);
- sbuf->ptr_ = sbuf->buf_ + off;
- }
-}
-
-// Append buf2 onto the end of buf1 [MouseJstr]
-int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2)
-{
- int buf1_avail = buf1->max_ - (buf1->ptr_ - buf1->buf_);
- int size2 = (int)(buf2->ptr_ - buf2->buf_);
-
- if (size2 >= buf1_avail) {
- int off = (int)(buf1->ptr_ - buf1->buf_);
- buf1->max_ += size2;
- buf1->buf_ = (char *) aRealloc(buf1->buf_, buf1->max_ + 1);
- buf1->ptr_ = buf1->buf_ + off;
- }
-
- memcpy(buf1->ptr_, buf2->buf_, size2);
- buf1->ptr_ += size2;
- return (int)(buf1->ptr_ - buf1->buf_);
-}
-
-// Destroy a StringBuf [MouseJstr]
-void StringBuf_Destroy(struct StringBuf *sbuf)
-{
- aFree(sbuf->buf_);
- sbuf->ptr_ = sbuf->buf_ = 0;
-}
-
-// Free a StringBuf returned by StringBuf_Malloc [MouseJstr]
-void StringBuf_Free(struct StringBuf *sbuf)
-{
- StringBuf_Destroy(sbuf);
- aFree(sbuf);
-}
-
-// Return the built string from the StringBuf [MouseJstr]
-char * StringBuf_Value(struct StringBuf *sbuf)
-{
- *sbuf->ptr_ = '\0';
- return sbuf->buf_;
-}
-
-#ifdef WIN32
-
-char* checkpath(char *path, const char *srcpath)
-{ // just make sure the char*path is not const
- char *p=path;
- if(NULL!=path && NULL!=srcpath)
- while(*srcpath) {
- if (*srcpath=='/') {
- *p++ = '\\';
- srcpath++;
- }
- else
- *p++ = *srcpath++;
- }
- *p = *srcpath; //EOS
- return path;
-}
-
-void findfile(const char *p, const char *pat, void (func)(const char*))
-{
- WIN32_FIND_DATA FindFileData;
- HANDLE hFind;
- char tmppath[MAX_PATH+1];
-
- const char *path = (p ==NULL)? "." : p;
- const char *pattern = (pat==NULL)? "" : pat;
-
- checkpath(tmppath,path);
- if( PATHSEP != tmppath[strlen(tmppath)-1])
- strcat(tmppath, "\\*");
- else
- strcat(tmppath, "*");
-
- hFind = FindFirstFile(tmppath, &FindFileData);
- if (hFind != INVALID_HANDLE_VALUE)
- {
- do
- {
- if (strcmp(FindFileData.cFileName, ".") == 0)
- continue;
- if (strcmp(FindFileData.cFileName, "..") == 0)
- continue;
-
- sprintf(tmppath,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
-
- if (FindFileData.cFileName && strstr(FindFileData.cFileName, pattern)) {
- func( tmppath );
- }
-
-
- if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
- {
- findfile(tmppath, pat, func);
- }
- }while (FindNextFile(hFind, &FindFileData) != 0);
- FindClose(hFind);
- }
- return;
-}
-#else
-
-#define MAX_DIR_PATH 2048
-
-char* checkpath(char *path, const char*srcpath)
-{ // just make sure the char*path is not const
- char *p=path;
- if(NULL!=path && NULL!=srcpath)
- while(*srcpath) {
- if (*srcpath=='\\') {
- *p++ = '/';
- srcpath++;
- }
- else
- *p++ = *srcpath++;
- }
- *p = *srcpath; //EOS
- return path;
-}
-
-void findfile(const char *p, const char *pat, void (func)(const char*))
-{
- DIR* dir; // pointer to the scanned directory.
- struct dirent* entry; // pointer to one directory entry.
- struct stat dir_stat; // used by stat().
- char tmppath[MAX_DIR_PATH+1];
- char path[MAX_DIR_PATH+1]= ".";
- const char *pattern = (pat==NULL)? "" : pat;
- if(p!=NULL) strcpy(path,p);
-
- // open the directory for reading
- dir = opendir( checkpath(path, path) );
- if (!dir) {
- ShowError("Cannot read directory '%s'\n", path);
- return;
- }
-
- // scan the directory, traversing each sub-directory
- // matching the pattern for each file name.
- while ((entry = readdir(dir))) {
- // skip the "." and ".." entries.
- if (strcmp(entry->d_name, ".") == 0)
- continue;
- if (strcmp(entry->d_name, "..") == 0)
- continue;
-
- sprintf(tmppath,"%s%c%s",path, PATHSEP, entry->d_name);
-
- // check if the pattern matchs.
- if (entry->d_name && strstr(entry->d_name, pattern)) {
- func( tmppath );
- }
- // check if it is a directory.
- if (stat(tmppath, &dir_stat) == -1) {
- ShowError("stat error %s\n': ", tmppath);
- continue;
- }
- // is this a directory?
- if (S_ISDIR(dir_stat.st_mode)) {
- // decent recursivly
- findfile(tmppath, pat, func);
- }
- }//end while
-}
-#endif
-
-unsigned char GetByte(unsigned long val, size_t num)
-{
- switch(num)
- {
- case 0:
- return (unsigned char)((val & 0x000000FF) );
- case 1:
- return (unsigned char)((val & 0x0000FF00)>>0x08);
- case 2:
- return (unsigned char)((val & 0x00FF0000)>>0x10);
- case 3:
- return (unsigned char)((val & 0xFF000000)>>0x18);
- default:
- return 0; //better throw something here
- }
-}
-unsigned short GetWord(unsigned long val, size_t num)
-{
- switch(num)
- {
- case 0:
- return (unsigned short)((val & 0x0000FFFF) );
- case 1:
- return (unsigned short)((val & 0xFFFF0000)>>0x10);
- default:
- return 0; //better throw something here
- }
-}
-unsigned short MakeWord(unsigned char byte0, unsigned char byte1)
-{
- return byte0 | (byte1<<0x08);
-}
-unsigned long MakeDWord(unsigned short word0, unsigned short word1)
-{
- return ((unsigned long)word0)
- | ((unsigned long)word1<<0x10);
-}
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WIN32
+ #include <windows.h>
+ #define PATHSEP '\\'
+#else
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #define PATHSEP '/'
+#endif
+
+#include "utils.h"
+#include "../common/mmo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+void dump(unsigned char *buffer, int num)
+{
+ int icnt,jcnt;
+
+ printf(" Hex ASCII\n");
+ printf(" ----------------------------------------------- ----------------");
+
+ for (icnt=0;icnt<num;icnt+=16) {
+ printf("\n%p ",&buffer[icnt]);
+ for (jcnt=icnt;jcnt<icnt+16;++jcnt) {
+ if (jcnt < num) {
+ printf("%02hX ",buffer[jcnt]);
+ } else
+ printf(" ");
+ }
+
+ printf(" | ");
+
+ for (jcnt=icnt;jcnt<icnt+16;++jcnt) {
+ if (jcnt < num) {
+ if (buffer[jcnt] > 31 && buffer[jcnt] < 127)
+ printf("%c",buffer[jcnt]);
+ else
+ printf(".");
+ } else
+ printf(" ");
+ }
+ }
+ printf("\n");
+}
+
+//NOTE: There is no need to use this function as the standard sqrt is plenty fast as it is. [Skotlex]
+int newt_sqrt(int input)
+{
+ int new_value, value = input/2, count = 0;
+ if (!value) //Division by zero fix, pointed out by Shinomori. [Skotlex]
+ return input;
+ do
+ {
+ new_value = (value + input/value)>>1;
+ if (abs(value - new_value) <= 1)
+ return new_value;
+ value = new_value;
+ }
+ while (count++ < 25);
+ return new_value;
+}
+
+#if defined(_WIN32) && !defined(MINGW)
+char *rindex(char *str, char c)
+{
+ char *sptr;
+
+ sptr = str;
+ while(*sptr)
+ ++sptr;
+ if (c == '\0')
+ return(sptr);
+ while(str != sptr)
+ if (*sptr-- == c)
+ return(++sptr);
+ return(NULL);
+}
+
+int strcasecmp(const char *arg1, const char *arg2)
+{
+ int chk, i;
+
+ if (arg1 == NULL || arg2 == NULL) {
+ ShowError("strcasecmp: received a NULL pointer, %p or %p.\n", arg1, arg2);
+ return (0);
+ }
+
+ for (i = 0; arg1[i] || arg2[i]; i++)
+ if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
+ return (chk); /* not equal */
+
+ return (0);
+}
+
+int strncasecmp(const char *arg1, const char *arg2, size_t n)
+{
+ int chk, i;
+
+ if (arg1 == NULL || arg2 == NULL) {
+ ShowError("strncasecmp(): received a NULL pointer, %p or %p.\n", arg1, arg2);
+ return (0);
+ }
+
+ for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--)
+ if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
+ return (chk); /* not equal */
+
+ return (0);
+}
+
+void str_upper(char *name)
+{
+
+ int len = (int)strlen(name);
+ while (len--) {
+ if (*name >= 'a' && *name <= 'z')
+ *name -= ('a' - 'A');
+ name++;
+ }
+}
+
+void str_lower(char *name)
+{
+ int len = (int)strlen(name);
+
+ while (len--) {
+ if (*name >= 'A' && *name <= 'Z')
+ *name += ('a' - 'A');
+ name++;
+ }
+}
+
+#endif
+
+// Allocate a StringBuf [MouseJstr]
+struct StringBuf * StringBuf_Malloc()
+{
+ struct StringBuf * ret = (struct StringBuf *) aMallocA(sizeof(struct StringBuf));
+ StringBuf_Init(ret);
+ return ret;
+}
+
+// Initialize a previously allocated StringBuf [MouseJstr]
+void StringBuf_Init(struct StringBuf * sbuf) {
+ sbuf->max_ = 1024;
+ sbuf->ptr_ = sbuf->buf_ = (char *) aMallocA(sbuf->max_ + 1);
+}
+
+// printf into a StringBuf, moving the pointer [MouseJstr]
+int StringBuf_Printf(struct StringBuf *sbuf,const char *fmt,...)
+{
+ va_list ap;
+ int n, size, off;
+
+ while (1) {
+ /* Try to print in the allocated space. */
+ va_start(ap, fmt);
+ size = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_);
+ n = vsnprintf (sbuf->ptr_, size, fmt, ap);
+ va_end(ap);
+ /* If that worked, return the length. */
+ if (n > -1 && n < size) {
+ sbuf->ptr_ += n;
+ return (int)(sbuf->ptr_ - sbuf->buf_);
+ }
+ /* Else try again with more space. */
+ sbuf->max_ *= 2; // twice the old size
+ off = (int)(sbuf->ptr_ - sbuf->buf_);
+ sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1);
+ sbuf->ptr_ = sbuf->buf_ + off;
+ }
+}
+
+// Append buf2 onto the end of buf1 [MouseJstr]
+int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2)
+{
+ int buf1_avail = buf1->max_ - (buf1->ptr_ - buf1->buf_);
+ int size2 = (int)(buf2->ptr_ - buf2->buf_);
+
+ if (size2 >= buf1_avail) {
+ int off = (int)(buf1->ptr_ - buf1->buf_);
+ buf1->max_ += size2;
+ buf1->buf_ = (char *) aRealloc(buf1->buf_, buf1->max_ + 1);
+ buf1->ptr_ = buf1->buf_ + off;
+ }
+
+ memcpy(buf1->ptr_, buf2->buf_, size2);
+ buf1->ptr_ += size2;
+ return (int)(buf1->ptr_ - buf1->buf_);
+}
+
+// Destroy a StringBuf [MouseJstr]
+void StringBuf_Destroy(struct StringBuf *sbuf)
+{
+ aFree(sbuf->buf_);
+ sbuf->ptr_ = sbuf->buf_ = 0;
+}
+
+// Free a StringBuf returned by StringBuf_Malloc [MouseJstr]
+void StringBuf_Free(struct StringBuf *sbuf)
+{
+ StringBuf_Destroy(sbuf);
+ aFree(sbuf);
+}
+
+// Return the built string from the StringBuf [MouseJstr]
+char * StringBuf_Value(struct StringBuf *sbuf)
+{
+ *sbuf->ptr_ = '\0';
+ return sbuf->buf_;
+}
+
+#ifdef WIN32
+
+char* checkpath(char *path, const char *srcpath)
+{ // just make sure the char*path is not const
+ char *p=path;
+ if(NULL!=path && NULL!=srcpath)
+ while(*srcpath) {
+ if (*srcpath=='/') {
+ *p++ = '\\';
+ srcpath++;
+ }
+ else
+ *p++ = *srcpath++;
+ }
+ *p = *srcpath; //EOS
+ return path;
+}
+
+void findfile(const char *p, const char *pat, void (func)(const char*))
+{
+ WIN32_FIND_DATA FindFileData;
+ HANDLE hFind;
+ char tmppath[MAX_PATH+1];
+
+ const char *path = (p ==NULL)? "." : p;
+ const char *pattern = (pat==NULL)? "" : pat;
+
+ checkpath(tmppath,path);
+ if( PATHSEP != tmppath[strlen(tmppath)-1])
+ strcat(tmppath, "\\*");
+ else
+ strcat(tmppath, "*");
+
+ hFind = FindFirstFile(tmppath, &FindFileData);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (strcmp(FindFileData.cFileName, ".") == 0)
+ continue;
+ if (strcmp(FindFileData.cFileName, "..") == 0)
+ continue;
+
+ sprintf(tmppath,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
+
+ if (FindFileData.cFileName && strstr(FindFileData.cFileName, pattern)) {
+ func( tmppath );
+ }
+
+
+ if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ findfile(tmppath, pat, func);
+ }
+ }while (FindNextFile(hFind, &FindFileData) != 0);
+ FindClose(hFind);
+ }
+ return;
+}
+#else
+
+#define MAX_DIR_PATH 2048
+
+char* checkpath(char *path, const char*srcpath)
+{ // just make sure the char*path is not const
+ char *p=path;
+ if(NULL!=path && NULL!=srcpath)
+ while(*srcpath) {
+ if (*srcpath=='\\') {
+ *p++ = '/';
+ srcpath++;
+ }
+ else
+ *p++ = *srcpath++;
+ }
+ *p = *srcpath; //EOS
+ return path;
+}
+
+void findfile(const char *p, const char *pat, void (func)(const char*))
+{
+ DIR* dir; // pointer to the scanned directory.
+ struct dirent* entry; // pointer to one directory entry.
+ struct stat dir_stat; // used by stat().
+ char tmppath[MAX_DIR_PATH+1];
+ char path[MAX_DIR_PATH+1]= ".";
+ const char *pattern = (pat==NULL)? "" : pat;
+ if(p!=NULL) strcpy(path,p);
+
+ // open the directory for reading
+ dir = opendir( checkpath(path, path) );
+ if (!dir) {
+ ShowError("Cannot read directory '%s'\n", path);
+ return;
+ }
+
+ // scan the directory, traversing each sub-directory
+ // matching the pattern for each file name.
+ while ((entry = readdir(dir))) {
+ // skip the "." and ".." entries.
+ if (strcmp(entry->d_name, ".") == 0)
+ continue;
+ if (strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ sprintf(tmppath,"%s%c%s",path, PATHSEP, entry->d_name);
+
+ // check if the pattern matchs.
+ if (entry->d_name && strstr(entry->d_name, pattern)) {
+ func( tmppath );
+ }
+ // check if it is a directory.
+ if (stat(tmppath, &dir_stat) == -1) {
+ ShowError("stat error %s\n': ", tmppath);
+ continue;
+ }
+ // is this a directory?
+ if (S_ISDIR(dir_stat.st_mode)) {
+ // decent recursivly
+ findfile(tmppath, pat, func);
+ }
+ }//end while
+}
+#endif
+
+unsigned char GetByte(unsigned long val, size_t num)
+{
+ switch(num)
+ {
+ case 0:
+ return (unsigned char)((val & 0x000000FF) );
+ case 1:
+ return (unsigned char)((val & 0x0000FF00)>>0x08);
+ case 2:
+ return (unsigned char)((val & 0x00FF0000)>>0x10);
+ case 3:
+ return (unsigned char)((val & 0xFF000000)>>0x18);
+ default:
+ return 0; //better throw something here
+ }
+}
+unsigned short GetWord(unsigned long val, size_t num)
+{
+ switch(num)
+ {
+ case 0:
+ return (unsigned short)((val & 0x0000FFFF) );
+ case 1:
+ return (unsigned short)((val & 0xFFFF0000)>>0x10);
+ default:
+ return 0; //better throw something here
+ }
+}
+unsigned short MakeWord(unsigned char byte0, unsigned char byte1)
+{
+ return byte0 | (byte1<<0x08);
+}
+unsigned long MakeDWord(unsigned short word0, unsigned short word1)
+{
+ return ((unsigned long)word0)
+ | ((unsigned long)word1<<0x10);
+}
+
diff --git a/src/common/utils.h b/src/common/utils.h
index 7c3c1f40c..1f7eeba5c 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -1,52 +1,52 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef COMMON_UTILS_H
-#define COMMON_UTILS_H
-
-
-#ifndef NULL
-#define NULL (void *)0
-#endif
-
-#define LOWER(c) (((c)>='A' && (c) <= 'Z') ? ((c)+('a'-'A')) : (c))
-#define UPPER(c) (((c)>='a' && (c) <= 'z') ? ((c)+('A'-'a')) : (c) )
-
-/* strcasecmp -> stricmp -> str_cmp */
-#if defined(_WIN32) && !defined(MINGW)
- int strcasecmp(const char *arg1, const char *arg2);
- int strncasecmp(const char *arg1, const char *arg2, size_t n);
- void str_upper(char *name);
- void str_lower(char *name);
- char *rindex(char *str, char c);
-#endif
-
-void dump(unsigned char *buffer, int num);
-int newt_sqrt(int value); //Newton aproximation for getting a fast sqrt.
-
-struct StringBuf {
- char *buf_;
- char *ptr_;
- unsigned int max_;
-};
-
-struct StringBuf * StringBuf_Malloc(void);
-void StringBuf_Init(struct StringBuf *);
-int StringBuf_Printf(struct StringBuf *,const char *,...);
-int StringBuf_Append(struct StringBuf *,const struct StringBuf *);
-char * StringBuf_Value(struct StringBuf *);
-void StringBuf_Destroy(struct StringBuf *);
-void StringBuf_Free(struct StringBuf *);
-
-void findfile(const char *p, const char *pat, void (func)(const char*));
-
-//////////////////////////////////////////////////////////////////////////
-// byte word dword access [Shinomori]
-//////////////////////////////////////////////////////////////////////////
-
-extern unsigned char GetByte(unsigned long val, size_t num);
-extern unsigned short GetWord(unsigned long val, size_t num);
-extern unsigned short MakeWord(unsigned char byte0, unsigned char byte1);
-extern unsigned long MakeDWord(unsigned short word0, unsigned short word1);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef COMMON_UTILS_H
+#define COMMON_UTILS_H
+
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#define LOWER(c) (((c)>='A' && (c) <= 'Z') ? ((c)+('a'-'A')) : (c))
+#define UPPER(c) (((c)>='a' && (c) <= 'z') ? ((c)+('A'-'a')) : (c) )
+
+/* strcasecmp -> stricmp -> str_cmp */
+#if defined(_WIN32) && !defined(MINGW)
+ int strcasecmp(const char *arg1, const char *arg2);
+ int strncasecmp(const char *arg1, const char *arg2, size_t n);
+ void str_upper(char *name);
+ void str_lower(char *name);
+ char *rindex(char *str, char c);
+#endif
+
+void dump(unsigned char *buffer, int num);
+int newt_sqrt(int value); //Newton aproximation for getting a fast sqrt.
+
+struct StringBuf {
+ char *buf_;
+ char *ptr_;
+ unsigned int max_;
+};
+
+struct StringBuf * StringBuf_Malloc(void);
+void StringBuf_Init(struct StringBuf *);
+int StringBuf_Printf(struct StringBuf *,const char *,...);
+int StringBuf_Append(struct StringBuf *,const struct StringBuf *);
+char * StringBuf_Value(struct StringBuf *);
+void StringBuf_Destroy(struct StringBuf *);
+void StringBuf_Free(struct StringBuf *);
+
+void findfile(const char *p, const char *pat, void (func)(const char*));
+
+//////////////////////////////////////////////////////////////////////////
+// byte word dword access [Shinomori]
+//////////////////////////////////////////////////////////////////////////
+
+extern unsigned char GetByte(unsigned long val, size_t num);
+extern unsigned short GetWord(unsigned long val, size_t num);
+extern unsigned short MakeWord(unsigned char byte0, unsigned char byte1);
+extern unsigned long MakeDWord(unsigned short word0, unsigned short word1);
+
+#endif
diff --git a/src/common/version.h b/src/common/version.h
index 1c7961ee1..2560c1019 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -1,30 +1,30 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _VERSION_H_
-#define _VERSION_H_
-
-#define ATHENA_MAJOR_VERSION 1 // Major Version
-#define ATHENA_MINOR_VERSION 0 // Minor Version
-#define ATHENA_REVISION 0 // Revision
-
-#define ATHENA_RELEASE_FLAG 1 // 1=Develop,0=Stable
-#define ATHENA_OFFICIAL_FLAG 1 // 1=Mod,0=Official
-
-#define ATHENA_SERVER_NONE 0 // not defined
-#define ATHENA_SERVER_LOGIN 1 // login server
-#define ATHENA_SERVER_CHAR 2 // char server
-#define ATHENA_SERVER_INTER 4 // inter server
-#define ATHENA_SERVER_MAP 8 // map server
-
-// ATHENA_MOD_VERSIONはパッチ番号です。
-// これは無理に変えなくても気が向いたら変える程度の扱いで。
-// (毎回アップロードの度に変更するのも面倒と思われるし、そもそも
-//  この項目を参照する人がいるかどうかで疑問だから。)
-// その程度の扱いなので、サーバーに問い合わせる側も、あくまで目安程度の扱いで
-// あんまり信用しないこと。
-// 鯖snapshotの時や、大きな変更があった場合は設定してほしいです。
-// C言語の仕様上、最初に0を付けると8進数になるので間違えないで下さい。
-#define ATHENA_MOD_VERSION 1249 // mod version (patch No.)
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _VERSION_H_
+#define _VERSION_H_
+
+#define ATHENA_MAJOR_VERSION 1 // Major Version
+#define ATHENA_MINOR_VERSION 0 // Minor Version
+#define ATHENA_REVISION 0 // Revision
+
+#define ATHENA_RELEASE_FLAG 1 // 1=Develop,0=Stable
+#define ATHENA_OFFICIAL_FLAG 1 // 1=Mod,0=Official
+
+#define ATHENA_SERVER_NONE 0 // not defined
+#define ATHENA_SERVER_LOGIN 1 // login server
+#define ATHENA_SERVER_CHAR 2 // char server
+#define ATHENA_SERVER_INTER 4 // inter server
+#define ATHENA_SERVER_MAP 8 // map server
+
+// ATHENA_MOD_VERSIONはパッチ番号です。
+// これは無理に変えなくても気が向いたら変える程度の扱いで。
+// (毎回アップロードの度に変更するのも面倒と思われるし、そもそも
+//  この項目を参照する人がいるかどうかで疑問だから。)
+// その程度の扱いなので、サーバーに問い合わせる側も、あくまで目安程度の扱いで
+// あんまり信用しないこと。
+// 鯖snapshotの時や、大きな変更があった場合は設定してほしいです。
+// C言語の仕様上、最初に0を付けると8進数になるので間違えないで下さい。
+#define ATHENA_MOD_VERSION 1249 // mod version (patch No.)
+
+#endif