diff options
author | FlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2006-12-05 13:23:07 +0000 |
---|---|---|
committer | FlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2006-12-05 13:23:07 +0000 |
commit | 288490094a7fe9167747dc78d416940759a31197 (patch) | |
tree | 53dc4f5c2375f4b688b53ca8841630ddec5e1f88 /src/common | |
parent | 8ec1c47aed09c90343949d57c92760ba84738a46 (diff) | |
download | hercules-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.c | 606 | ||||
-rw-r--r-- | src/common/core.h | 44 | ||||
-rw-r--r-- | src/common/db.c | 4896 | ||||
-rw-r--r-- | src/common/db.h | 1496 | ||||
-rw-r--r-- | src/common/ers.h | 386 | ||||
-rw-r--r-- | src/common/graph.c | 636 | ||||
-rw-r--r-- | src/common/graph.h | 54 | ||||
-rw-r--r-- | src/common/grfio.c | 2062 | ||||
-rw-r--r-- | src/common/grfio.h | 44 | ||||
-rw-r--r-- | src/common/lock.c | 142 | ||||
-rw-r--r-- | src/common/lock.h | 22 | ||||
-rw-r--r-- | src/common/malloc.c | 1466 | ||||
-rw-r--r-- | src/common/malloc.h | 356 | ||||
-rw-r--r-- | src/common/nullpo.c | 188 | ||||
-rw-r--r-- | src/common/nullpo.h | 474 | ||||
-rw-r--r-- | src/common/plugin.h | 80 | ||||
-rw-r--r-- | src/common/plugins.c | 734 | ||||
-rw-r--r-- | src/common/plugins.h | 122 | ||||
-rw-r--r-- | src/common/showmsg.c | 1652 | ||||
-rw-r--r-- | src/common/showmsg.h | 192 | ||||
-rw-r--r-- | src/common/strlib.h | 48 | ||||
-rw-r--r-- | src/common/timer.c | 872 | ||||
-rw-r--r-- | src/common/timer.h | 120 | ||||
-rw-r--r-- | src/common/utils.c | 768 | ||||
-rw-r--r-- | src/common/utils.h | 104 | ||||
-rw-r--r-- | src/common/version.h | 60 |
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 |