From 757556022be4ef5643aaeb9e5060ad82e692cda2 Mon Sep 17 00:00:00 2001 From: Robin Redeker Date: Sat, 16 Mar 2002 15:52:17 +0000 Subject: [PATCH] 1. general menu code cleanup 2. added lots of comments to my menu code 3. wrote a string hash api (key -> value assignment list) Hm, nothing else afaik. ;) --- cs-code/Makefile.am | 2 +- cs-code/controls_o.qc | 452 ++++++++++++++++++++++------ cs-code/menu.src.in | 1 + cs-code/options.qc | 13 +- doc/QCMenuCondingStyle | 1 - include/QF/csqc.h | 1 + libs/console/menu.c | 1 + libs/gamecode/builtins/Makefile.am | 2 +- libs/gamecode/builtins/bi_init.c | 1 + libs/gamecode/builtins/bi_strhash.c | 440 +++++++++++++++++++++++++++ 10 files changed, 806 insertions(+), 108 deletions(-) create mode 100644 libs/gamecode/builtins/bi_strhash.c diff --git a/cs-code/Makefile.am b/cs-code/Makefile.am index 4a29c7766..97e77f5b8 100644 --- a/cs-code/Makefile.am +++ b/cs-code/Makefile.am @@ -11,7 +11,7 @@ QCPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include pkgdata_DATA= menu.dat -menu_src= menu.qc servlist.qc options.qc cbuf_def.qc cvar_def.qc draw_def.qc file_def.qc game_def.qc inputline_def.qc key_defs.qc menu_def.qc options_util.qc string_def.qc controls_o.qc +menu_src= menu.qc servlist.qc options.qc cbuf_def.qc cvar_def.qc draw_def.qc file_def.qc game_def.qc inputline_def.qc key_defs.qc menu_def.qc options_util.qc string_def.qc controls_o.qc stringh_def.qc menu.dat: menu.src $(menu_src) $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -P $< diff --git a/cs-code/controls_o.qc b/cs-code/controls_o.qc index 9a59d54e4..7bea77ac3 100644 --- a/cs-code/controls_o.qc +++ b/cs-code/controls_o.qc @@ -1,3 +1,34 @@ +/* + options.qc + + Options menu + + Copyright (C) 2002 Robin Redeker + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + + +/* + This is a array of the commands, which + can be binded by menu. +*/ #define NUM_BINDED_KEYS 34 string [NUM_BINDED_KEYS] key_bindings = { @@ -25,7 +56,6 @@ string [NUM_BINDED_KEYS] key_bindings = "messagemode", "screenshot", #define NUM_MISC_KEYS NUM_BASIC_KEYS + 4 - "impulse 0", "impulse 1", "impulse 2", "impulse 3", @@ -36,11 +66,107 @@ string [NUM_BINDED_KEYS] key_bindings = "impulse 8" #define NUM_WEAPON_KEYS NUM_MISC_KEYS + 9 }; +// this array holds readable information about the binded keys string [NUM_BINDED_KEYS] key_bindings_desc; -integer set_key_flag; +integer set_key_flag; // holds flag for the key-setting + +// three global hashes for the main binding groups +integer basic_binding_hash; +integer misc_binding_hash; +integer weapon_binding_hash; -string (integer key, integer bindnum) make_key_desc = +/* + init_binding_hash + + this function initializes the hashes for the binding menus +*/ +void () +init_binding_hash = +{ + /* + DESIGN NOTES for the Menu: + + binding config is loaded into hashes. + the key of the hash is the string, which will + be displayed as binding description. + The first value of the key is the command, which + is binded. + The second valu (loaded later) of the hash + will be the keyname. (see get_hash_keys()) + */ + + // Basic keys + basic_binding_hash = StringHash_Create(); + StringHash_Set(basic_binding_hash, "Attack", "+attack", 0); + StringHash_Set(basic_binding_hash, "Next weapon", "impulse 10", 0); + StringHash_Set(basic_binding_hash, "Jump/Swin up", "+jump", 0); + StringHash_Set(basic_binding_hash, "Walk forward", "+forward", 0); + StringHash_Set(basic_binding_hash, "Backpedal", "+back", 0); + StringHash_Set(basic_binding_hash, "Turn left", "+left", 0); + StringHash_Set(basic_binding_hash, "Turn right", "+right", 0); + StringHash_Set(basic_binding_hash, "Run", "+speed", 0); + StringHash_Set(basic_binding_hash, "Step left", "+moveleft", 0); + StringHash_Set(basic_binding_hash, "Step right", "+moveright", 0); + StringHash_Set(basic_binding_hash, "Sidestep", "+strafe", 0); + StringHash_Set(basic_binding_hash, "Look up", "+lookup", 0); + StringHash_Set(basic_binding_hash, "Look down", "+lookdown", 0); + StringHash_Set(basic_binding_hash, "Center view", "centerview", 0); + StringHash_Set(basic_binding_hash, "Mouse look", "+mlook", 0); + StringHash_Set(basic_binding_hash, "Keyboard look", "+klook", 0); + StringHash_Set(basic_binding_hash, "Swim up", "+moveup", 0); + StringHash_Set(basic_binding_hash, "Swim down", "+movedown", 0); + + // Misc keys + misc_binding_hash = StringHash_Create(); + StringHash_Set(misc_binding_hash, "Pause game", "pause", 0); + StringHash_Set(misc_binding_hash, "Tog. m.-grab", "toggle in_grab", 0); + StringHash_Set(misc_binding_hash, "Messagemode", "messagemode", 0); + StringHash_Set(misc_binding_hash, "Screenshot", "screenshot", 0); + + // Weapon keys + weapon_binding_hash = StringHash_Create(); + StringHash_Set(weapon_binding_hash, "Axe", "impulse 1", 0); + StringHash_Set(weapon_binding_hash, "Shotgun", "impulse 2", 0); + StringHash_Set(weapon_binding_hash, "Super Shotgun", "impulse 3", 0); + StringHash_Set(weapon_binding_hash, "Nailgun", "impulse 4", 0); + StringHash_Set(weapon_binding_hash, "Super Nailgun", "impulse 5", 0); + StringHash_Set(weapon_binding_hash, "Rocket L. ", "impulse 6", 0); + StringHash_Set(weapon_binding_hash, "Grenade L. ", "impulse 7", 0); + StringHash_Set(weapon_binding_hash, "Thunderbolt", "impulse 8", 0); + +}; + +/* + get_keyname + + Gets the string of the key, which is binded + to a special binding. + bindnum is the number of the binding. + As a command/binding can be binded to many keys, + you can get the second, third, etc. key by giving + the bindnum. +*/ +string (string binding, integer bindnum) +get_keyname = +{ + local integer keynum; + local string keyname; + + keynum = Key_LookupBinding(IMT_0, bindnum, binding); + if(keynum == -1) { + keyname = ""; + } else { + keyname = Key_KeynumToString(keynum); + // cut away the "K_", thats maybe enough as description for now + keyname = String_Cut(0, 2, keyname); + } + return keyname; +}; + +//XXX: make_key_desc will be removed soon +string (integer key, integer bindnum) +make_key_desc = { local integer keynum; local string keyname; @@ -56,35 +182,79 @@ string (integer key, integer bindnum) make_key_desc = return keyname; }; -void () load_keybindings = +/* + get_hash_keys + + gets the keys for a keybinding-hash +*/ +void (integer hash_id) +get_hash_keys = { - local integer i; - local string nddesc = ""; + local integer i,hlen; + local string binding, desc1 = "", desc2 = ""; - for(i = 0;i < NUM_BINDED_KEYS; i++) { - key_bindings_desc[i] = make_key_desc(i, 1); - nddesc = make_key_desc(i, 2); - if(nddesc != "") { - key_bindings_desc[i] += ", " + nddesc; + hlen = StringHash_Length(hash_id); + for(i = 0;i < hlen; i++) { + binding = StringHash_GetIdx(hash_id, i, 0); + desc1 = get_keyname(binding, 1); // first key binded to + desc2 = get_keyname(binding, 2); // second key binded to + + if(desc2 != "") { + desc1 += ", " + desc2; } + StringHash_SetIdx(hash_id, i, desc1, 1); } }; -//**** BINDINGS OPTIONS - -integer (string text, integer key) control_bind_f = +/* + load_keybindings + + Loads the kername for into the hashes +*/ +void () +load_keybindings = { - local string binding; - local integer retval = 0, bindcnt = 0; + get_hash_keys(basic_binding_hash); + get_hash_keys(misc_binding_hash); + get_hash_keys(weapon_binding_hash); +}; - binding = key_bindings[stoi(text)]; + +/******************* + * BINDINGS OPTIONS + * Binding settings + *******************/ + +/* + DESIGN NOTE (by elmex): + + Every sub-menu for control bindings + has its own hash for holding the keybindings. + Thats why there are three different functions, which + shadow the CB_MAIN_control_binding() function. + They get the binding from the correct hash. +*/ + + +/* + CB_MAIN_control_binding + + The core function of all control_binding function. + Its taking the binding as argument. + This function is called by the real callbacks. +*/ +integer (string binding, integer key) +CB_MAIN_control_binding = +{ + local integer retval = 0, bindcnt = 0; if(set_key_flag) { bindcnt = Key_CountBinding(IMT_0, binding); + /* we are not binding keys for more than one command + by the menu (maybe extended later) */ if(bindcnt < 2) { Key_SetBinding (IMT_0, key, binding); - } - // else: not bind + } // else: not bind the key set_key_flag = 0; retval = 1; @@ -104,133 +274,215 @@ integer (string text, integer key) control_bind_f = return retval; }; -integer () basic_control_bind_draw = +/* + CB_basic_control_binding + + Callback for the basic control bindings menu +*/ +integer (string text, integer key) +CB_basic_control_binding = +{ + local string binding = StringHash_GetIdx(basic_binding_hash, stoi(text), 0); + return CB_MAIN_control_binding(binding, key); +}; + + +/* + DRAW_basic_control_binding + + Draws the menu for the basic control bindins +*/ +integer () +DRAW_basic_control_binding = { local integer cursor_pad = 40, bind_desc_pad; + local integer i, hl; bind_desc_pad = 120; Draw_String (20, 10, "Backspace/Delete: Del binding"); Draw_String (20, 20, "Enter: New binding"); - draw_val_item (20, 40, bind_desc_pad, "Attack", key_bindings_desc[0]); - draw_val_item (20, 50, bind_desc_pad, "Next weapon", key_bindings_desc[1]); - draw_val_item (20, 60, bind_desc_pad, "Jump/Swin up", key_bindings_desc[2]); - draw_val_item (20, 70, bind_desc_pad, "Walf forward", key_bindings_desc[3]); - draw_val_item (20, 80, bind_desc_pad, "Backpedal", key_bindings_desc[4]); - draw_val_item (20, 90, bind_desc_pad, "Turn left", key_bindings_desc[5]); - draw_val_item (20, 100, bind_desc_pad, "Turn right", key_bindings_desc[6]); - draw_val_item (20, 110, bind_desc_pad, "Run", key_bindings_desc[7]); - draw_val_item (20, 120, bind_desc_pad, "Step left", key_bindings_desc[8]); - draw_val_item (20, 130, bind_desc_pad, "Step right", key_bindings_desc[9]); - draw_val_item (20, 140, bind_desc_pad, "Sidestep", key_bindings_desc[10]); - draw_val_item (20, 150, bind_desc_pad, "Look up", key_bindings_desc[11]); - draw_val_item (20, 160, bind_desc_pad, "Look down", key_bindings_desc[12]); - draw_val_item (20, 170, bind_desc_pad, "Center view", key_bindings_desc[13]); - draw_val_item (20, 180, bind_desc_pad, "Mouse look", key_bindings_desc[14]); - draw_val_item (20, 190, bind_desc_pad, "Keyboard look", key_bindings_desc[15]); - draw_val_item (20, 200, bind_desc_pad, "Swim up", key_bindings_desc[16]); - draw_val_item (20, 210, bind_desc_pad, "Swim down", key_bindings_desc[17]); + + hl = StringHash_Length(basic_binding_hash); + for(i=0;i < hl; i++) { + draw_val_item (20, 40+(i*10), bind_desc_pad, + StringHash_GetIdx(basic_binding_hash, i, -1), + StringHash_GetIdx(basic_binding_hash, i, 1)); + } opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad); return 1; }; -integer () misc_control_bind_draw = +/* + MENU_basic_control_binding + + Menu making function for the control bindings +*/ +void () +MENU_basic_control_binding = { - local integer cursor_pad = 40, bind_desc_pad; - - bind_desc_pad = 120; - - Draw_String (20, 10, "Backspace/Delete: Del binding"); - Draw_String (20, 20, "Enter: New binding"); - - draw_val_item (20, 40, bind_desc_pad, "Pause game", key_bindings_desc[NUM_BASIC_KEYS]); - draw_val_item (20, 50, bind_desc_pad, "Tog. m.-grab", key_bindings_desc[NUM_BASIC_KEYS + 1]); - draw_val_item (20, 60, bind_desc_pad, "Messagemode", key_bindings_desc[NUM_BASIC_KEYS + 2]); - draw_val_item (20, 70, bind_desc_pad, "Screenshot", key_bindings_desc[NUM_BASIC_KEYS + 3]); - - opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad); - - return 1; -}; - -integer () weapon_control_bind_draw = -{ - local integer cursor_pad = 40, bind_desc_pad; - - bind_desc_pad = 120; - - Draw_String (20, 10, "Backspace/Delete: Del binding"); - Draw_String (20, 20, "Enter: New binding"); - - draw_val_item (20, 40, bind_desc_pad, "Axe", key_bindings_desc[NUM_MISC_KEYS]); - draw_val_item (20, 50, bind_desc_pad, "Shotgun", key_bindings_desc[NUM_MISC_KEYS + 1]); - draw_val_item (20, 60, bind_desc_pad, "Super Shotgun", key_bindings_desc[NUM_MISC_KEYS + 2]); - draw_val_item (20, 70, bind_desc_pad, "Nailgun", key_bindings_desc[NUM_MISC_KEYS + 3]); - draw_val_item (20, 80, bind_desc_pad, "Super Nailgun", key_bindings_desc[NUM_MISC_KEYS + 4]); - draw_val_item (20, 90, bind_desc_pad, "Rocket L. ", key_bindings_desc[NUM_MISC_KEYS + 5]); - draw_val_item (20, 100, bind_desc_pad, "Grenade L. ", key_bindings_desc[NUM_MISC_KEYS + 6]); - draw_val_item (20, 110, bind_desc_pad, "Thunderbolt", key_bindings_desc[NUM_MISC_KEYS + 7]); - - opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad); - - return 1; -}; - - -void () basic_control_bind_menu = -{ - local integer i; + local integer i,hl; Menu_Begin (54, 40, "Basic bindings"); Menu_FadeScreen (1); - Menu_Draw (basic_control_bind_draw); + Menu_Draw (DRAW_basic_control_binding); - for (i = 0; i < NUM_BASIC_KEYS; i++) { - Menu_Item (20, 40 + i*10, itos(i), control_bind_f, 1); + hl = StringHash_Length(basic_binding_hash); + for (i = 0; i < hl; i++) { + Menu_Item (20, 40 + i*10, itos(i), CB_basic_control_binding, 1); } Menu_End (); }; -void () misc_control_bind_menu = + +/* + CB_misc_control_binding + + Callback for misc control bindings. +*/ +integer (string text, integer key) +CB_misc_control_binding = { - local integer i; + local string binding = StringHash_GetIdx(misc_binding_hash, stoi(text), 0); + return CB_MAIN_control_binding(binding, key); +}; + +/* + DRAW_misc_control_binding + + Draw the bindings for the misc controls +*/ +integer () +DRAW_misc_control_binding = +{ + local integer cursor_pad = 40, bind_desc_pad; + local integer i, hl; + + bind_desc_pad = 120; + + Draw_String (20, 10, "Backspace/Delete: Del binding"); + Draw_String (20, 20, "Enter: New binding"); + + hl = StringHash_Length(misc_binding_hash); + for(i=0;i < hl; i++) { + draw_val_item (20, 40+(i*10), bind_desc_pad, + StringHash_GetIdx(misc_binding_hash, i, -1), + StringHash_GetIdx(misc_binding_hash, i, 1)); + } + + opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad); + return 1; +}; + + + +/* + MENU_misc_control_binding + + Menu maker function for the misc control binding +*/ +void () +MENU_misc_control_binding = +{ + local integer i,hl; Menu_Begin (54, 50, "Misc bindings"); Menu_FadeScreen (1); - Menu_Draw (misc_control_bind_draw); + Menu_Draw (DRAW_misc_control_binding); - for (i = NUM_BASIC_KEYS; i < NUM_MISC_KEYS; i++) { - Menu_Item (20, 40 + i*10, itos(i), control_bind_f, 1); + hl = StringHash_Length(basic_binding_hash); + for (i = 0; i < hl; i++) { + Menu_Item (20, 40 + i*10, itos(i), CB_misc_control_binding, 1); } Menu_End (); }; -void () weapon_control_bind_menu = +/* + CB_weapon_control_binding + + Callback function for the weapons control bindings +*/ +integer (string text, integer key) +CB_weapon_control_binding = { - local integer i; + local string binding = StringHash_GetIdx(weapon_binding_hash, stoi(text),0); + return CB_MAIN_control_binding(binding, key); +}; + +/* + DRAW_weapon_control_binding + + Draw the weapon binding menu +*/ +integer () +DRAW_weapon_control_binding = +{ + local integer cursor_pad = 40, bind_desc_pad; + local integer i,hl; + + bind_desc_pad = 120; + + Draw_String (20, 10, "Backspace/Delete: Del binding"); + Draw_String (20, 20, "Enter: New binding"); + + hl = StringHash_Length(weapon_binding_hash); + for(i=0;i < hl; i++) { + draw_val_item (20, 40+(i*10), bind_desc_pad, + StringHash_GetIdx(weapon_binding_hash, i, -1), + StringHash_GetIdx(weapon_binding_hash, i, 1)); + } + + opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad); + + return 1; +}; + + +/* + MENU_weapon_control_binding + + Menu maker for the weapons menu +*/ +void () +MENU_weapon_control_binding = +{ + local integer i,hl; Menu_Begin (54, 60, "Weapon bindings"); Menu_FadeScreen (1); - Menu_Draw (weapon_control_bind_draw); + Menu_Draw (DRAW_weapon_control_binding); - for (i = NUM_MISC_KEYS; i < NUM_WEAPON_KEYS; i++) { - Menu_Item (20, 40 + i*10, itos(i), control_bind_f, 1); + hl = StringHash_Length(basic_binding_hash); + for (i = 0; i < hl; i++) { + Menu_Item (20, 40 + i*10, itos(i), CB_weapon_control_binding, 1); } Menu_End (); }; -void () control_bind_menu = +/* + MENU_control_binding + + Main controls menu, for selecting the sub control menus +*/ +void () +MENU_control_binding = { + init_binding_hash (); // init the keybinding hashes + load_keybindings (); // load the keybindings into hashes + Menu_Begin (54, 60, "Bindings"); Menu_Pic (16, 4, "gfx/qplaque.lmp"); Menu_CenterPic (160, 4, "gfx/p_option.lmp"); + Menu_FadeScreen (1); - basic_control_bind_menu (); - misc_control_bind_menu (); - weapon_control_bind_menu (); + + MENU_basic_control_binding (); + MENU_misc_control_binding (); + MENU_weapon_control_binding(); + Menu_End (); }; diff --git a/cs-code/menu.src.in b/cs-code/menu.src.in index 5533e7653..6e7ca00a9 100644 --- a/cs-code/menu.src.in +++ b/cs-code/menu.src.in @@ -8,6 +8,7 @@ menu.dat @srcdir@/key_defs.qc @srcdir@/menu_def.qc @srcdir@/string_def.qc +@srcdir@/stringh_def.qc @srcdir@/options_util.qc diff --git a/cs-code/options.qc b/cs-code/options.qc index a666ef8ad..e983411ed 100644 --- a/cs-code/options.qc +++ b/cs-code/options.qc @@ -180,7 +180,8 @@ CB_audio_options = local float volume; volume = cvar("volume"); - volume = min_max_cnt(MIN_VOLUME, MAX_VOLUME, VOLUME_STEP, volume, (key == QFK_RIGHT) && (key != QFK_LEFT)); + volume = min_max_cnt(MIN_VOLUME, MAX_VOLUME, VOLUME_STEP, volume, + (key == QFK_RIGHT) && (key != QFK_LEFT)); cvar_set("volume", ftos(volume)); return 0; @@ -247,8 +248,6 @@ CB_control_options = { local float val; - load_keybindings(); - switch (text) { case "in_grab": Cbuf_AddText ("toggle in_grab\n"); @@ -344,8 +343,11 @@ MENU_control_options = Menu_Begin (54, 40, "Controls"); Menu_FadeScreen (1); Menu_CenterPic (160, 4, "gfx/p_option.lmp"); + Menu_Draw (DRAW_control_options); - control_bind_menu (); + + MENU_control_binding (); + Menu_Item (54, 70, "in_grab", CB_control_options, 0); Menu_Item (54, 80, "autorun", CB_control_options, 0); Menu_Item (54, 90, "m_pitch", CB_control_options, 0); @@ -353,6 +355,7 @@ MENU_control_options = Menu_Item (54, 110, "freelook", CB_control_options, 0); Menu_Item (54, 120, "lookspring", CB_control_options, 0); Menu_Item (54, 130, "lookstrafe", CB_control_options, 0); + Menu_End (); }; @@ -435,7 +438,6 @@ integer (integer key, integer unicode, integer down) CB_options = { // pre-loading of the bindings and set_key_flag == 0 - load_keybindings(); set_key_flag = 0; return 0; @@ -457,6 +459,7 @@ MENU_options = Menu_Pic (16, 4, "gfx/qplaque.lmp"); Menu_CenterPic (160, 4, "gfx/p_option.lmp"); Menu_KeyEvent (CB_options); + MENU_control_options (); MENU_video_options (); MENU_audio_options (); diff --git a/doc/QCMenuCondingStyle b/doc/QCMenuCondingStyle index fceb961a3..1f524763b 100644 --- a/doc/QCMenuCondingStyle +++ b/doc/QCMenuCondingStyle @@ -12,7 +12,6 @@ CB_ Input callback MENU_ Menu making function DRAW_ Draw - Comment functions like this: /* [PREFIX_]FunctionName diff --git a/include/QF/csqc.h b/include/QF/csqc.h index fe26e2fce..f00be1a4a 100644 --- a/include/QF/csqc.h +++ b/include/QF/csqc.h @@ -40,6 +40,7 @@ void Cbuf_Progs_Init (struct progs_s *pr); void File_Progs_Init (struct progs_s *pr); void InputLine_Progs_Init (struct progs_s *pr); void String_Progs_Init (struct progs_s *pr); +void StringHash_Progs_Init (struct progs_s *pr); void Cvar_Progs_Init (struct progs_s *pr); void Key_Progs_Init (struct progs_s *pr); diff --git a/libs/console/menu.c b/libs/console/menu.c index cf2c5af41..0f643551b 100644 --- a/libs/console/menu.c +++ b/libs/console/menu.c @@ -361,6 +361,7 @@ Menu_Init (void) File_Progs_Init (&menu_pr_state); InputLine_Progs_Init (&menu_pr_state); String_Progs_Init (&menu_pr_state); + StringHash_Progs_Init (&menu_pr_state); Cvar_Progs_Init (&menu_pr_state); Key_Progs_Init (&menu_pr_state); PR_Cmds_Init (&menu_pr_state); diff --git a/libs/gamecode/builtins/Makefile.am b/libs/gamecode/builtins/Makefile.am index 46ca7afcd..912dbdc28 100644 --- a/libs/gamecode/builtins/Makefile.am +++ b/libs/gamecode/builtins/Makefile.am @@ -8,4 +8,4 @@ libQFgamecode_builtins_la_LDFLAGS= -version-info 1:0:0 libQFgamecode_builtins_la_SOURCES= pr_cmds.c libQFcsqc_la_LDFLAGS= -version-info 1:0:0 -libQFcsqc_la_SOURCES= bi_init.c bi_cbuf.c bi_file.c bi_inputline.c bi_string.c bi_cvar.c bi_keys.c +libQFcsqc_la_SOURCES= bi_init.c bi_cbuf.c bi_file.c bi_inputline.c bi_string.c bi_cvar.c bi_keys.c bi_strhash.c diff --git a/libs/gamecode/builtins/bi_init.c b/libs/gamecode/builtins/bi_init.c index 09661c5da..7cf7be214 100644 --- a/libs/gamecode/builtins/bi_init.c +++ b/libs/gamecode/builtins/bi_init.c @@ -37,6 +37,7 @@ static void (*const cbuf_progs_init)(struct progs_s *) = Cbuf_Progs_Init; static void (*const file_progs_init)(struct progs_s *) = File_Progs_Init; static void (*const inputline_progs_init)(struct progs_s *) = InputLine_Progs_Init; static void (*const string_progs_init)(struct progs_s *) = String_Progs_Init; +static void (*const stringhashe_progs_init)(struct progs_s *) = StringHash_Progs_Init; static void (*const cvar_progs_init)(struct progs_s *) = Cvar_Progs_Init; static void (*const key_progs_init)(struct progs_s *) = Key_Progs_Init; diff --git a/libs/gamecode/builtins/bi_strhash.c b/libs/gamecode/builtins/bi_strhash.c new file mode 100644 index 000000000..bbdbc14b0 --- /dev/null +++ b/libs/gamecode/builtins/bi_strhash.c @@ -0,0 +1,440 @@ +/* + bi_inputline.c + + CSQC string hashes builtins + + Copyright (C) 2002 Robin Redeker + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +static const char rcsid[] = + "$Id$"; + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/console.h" +#include "QF/draw.h" +#include "QF/progs.h" +#include "QF/zone.h" + +#define MAX_SH_VALUES 16 + +/* + DESIGN NOTE (by elmex): + + This file contains code for QuakeC hashes. + The hases are designed to have around 10 sub-values for + one key. + Hashes are stored as hash-ids in the QC-environment. + + The key->value pairs in the hashes are stored in the + order they are set. So its easy to make array-hashes. + The function which gets the Lenght of a hash is specialy + made for array purposes. + + TODO: Check the FIXME's below in the code. + (please taniwha ;) + (about memory leaks) +*/ + +// a hash element +typedef struct { + const char * key; + const char * values[MAX_SH_VALUES]; +} str_hash_elem; + +// a structure of a hash +typedef struct { + str_hash_elem **elements; + int cnt_elements; +} str_hash; + +// a list structure of hashes +typedef struct { + str_hash **hashes; + int cnt_hashes; +} strh_resources_t; + +/* + bi_StringHash_Create + + Creates a hash structure and gives back the hash-id to the + QC-environment +*/ +static void +bi_StringHash_Create (progs_t *pr) +{ + strh_resources_t *res = PR_Resources_Find (pr, "StringHash"); + int i; + int hash_id=-1; + + // check if there is a empty hash in the array + for(i = 0; i < res->cnt_hashes; i++) { + if(res->hashes[i]->cnt_elements == 0) { + // we found a empty already allocated hash + hash_id = i; + break; + } + } + if(hash_id == -1) { + /* allocate a new hash struct, if + there is no old one */ + if(res->hashes == NULL) { // allocate a new list of hashes + res->hashes = (str_hash **) malloc( + sizeof(str_hash*) * (res->cnt_hashes + 1)); + } else { // reallocate the list of hashes + res->hashes = (str_hash **) realloc(res->hashes, + sizeof(str_hash*) * (res->cnt_hashes + 1)); + } + hash_id = res->cnt_hashes; + + res->hashes[hash_id] = + (str_hash*) malloc(sizeof(str_hash)); + // dont forge to clean the hash + memset(res->hashes[hash_id],0,sizeof(str_hash)); + + res->cnt_hashes++; // increase cnt of allocated hashes + } + G_INT (pr, OFS_RETURN) = hash_id; +} + +/* + bi_StringHash_Destroy + + Destroys a hash +*/ +static void +bi_StringHash_Destroy (progs_t *pr) +{ + strh_resources_t* res = PR_Resources_Find (pr, "StringHash"); + int hash_id = G_INT (pr, OFS_PARM0); + str_hash *sh = NULL; + int i; + + if(hash_id >= res->cnt_hashes || hash_id < 0) { + G_INT (pr, OFS_RETURN) = 0; + return; + } + sh = res->hashes[hash_id]; + + /* we dont really destroy the allocated hash, + but we free the elements of the hash + and mark it for reuseing */ + + for(i = 0; i < sh->cnt_elements; i++) { + if(sh->elements[i] != NULL) { + /* they should never be NULL, + buy, who knows? */ + PR_Error(pr, "NULL hash-element found -> not supposed!"); + } else { + free(sh->elements[i]); // free str_hash_elem structs + } + /* + FIXME: HELP: taniwha??? do i have to FREE + the strings ?? + */ + } + free(sh->elements); // free the list pointer + sh->elements = NULL; + sh->cnt_elements = 0; + G_INT (pr, OFS_RETURN) = 1; +} + + +/* + bi_StringHash_Set + + Sets a the key-value (with a special id) in a hash + to a value. + If a non existing key is given a element if generated for it. + FIXME: look if this functions does mem-leak +*/ +static void +bi_StringHash_Set (progs_t *pr) +{ + strh_resources_t* res = PR_Resources_Find (pr, "StringHash"); + int hash_id = G_INT (pr, OFS_PARM0); + const char *key = G_STRING (pr, OFS_PARM1); + const char *val = G_STRING (pr, OFS_PARM2); + int val_id = G_INT (pr, OFS_PARM3); + str_hash *sh = NULL; + int i,found_fl=0; + + // validate the hash ID + if(res->hashes == NULL || + (hash_id >= res->cnt_hashes || hash_id < 0) || + (val_id < 0 || val_id >= MAX_SH_VALUES)) + { + G_INT (pr, OFS_RETURN) = 0; + return; + } + sh = res->hashes[hash_id]; + + // first search for existing key + for(i = 0; i < sh->cnt_elements; i++) { + if(strcmp(sh->elements[i]->key, key) == 0) { + // found already a element with that key + if(sh->elements[i]->values[val_id] == NULL) { // empty val + sh->elements[i]->values[val_id] = val; + } else { + /* FIXME: taniwha, do i have to free it? + * this is a potential memory leak, maybe, bu + * i dont know how Hunk_Alloc* stuff works + * ITS NOT DOCUMENTED! + */ + sh->elements[i]->values[val_id] = val; + } + found_fl = 1; + } + } + if(!found_fl) { // add a new element + if(sh->elements == NULL) { // alloc new elements list pointer + sh->elements = (str_hash_elem**) malloc(sizeof(str_hash_elem*)); + sh->cnt_elements = 0; // 0 because usage as index here + } else { + sh->elements = (str_hash_elem**) realloc(sh->elements, + sizeof(str_hash_elem*) * (sh->cnt_elements+1)); + } + sh->elements[sh->cnt_elements] = malloc(sizeof(str_hash_elem)); + memset(sh->elements[sh->cnt_elements],0,sizeof(str_hash_elem)); + + sh->elements[sh->cnt_elements]->key = key; + sh->elements[sh->cnt_elements]->values[val_id] = val; + + sh->cnt_elements++; + } + G_INT (pr, OFS_RETURN) = 1; + return; +} + + +/* + bi_StringHash_SetIdx + + Sets a the key-value (with a special id) in a hash + to a value. This function works by index of the element. + A element in hash is NOT generated automatically. + FIXME: look if this functions does mem-leak +*/ +static void +bi_StringHash_SetIdx (progs_t *pr) +{ + strh_resources_t* res = PR_Resources_Find (pr, "StringHash"); + int hash_id = G_INT (pr, OFS_PARM0); + int idx = G_INT (pr, OFS_PARM1); + const char *val = G_STRING (pr, OFS_PARM2); + int val_id = G_INT (pr, OFS_PARM3); + str_hash *sh = NULL; + + // validate the hash ID + if(res->hashes == NULL || + (hash_id >= res->cnt_hashes || hash_id < 0) || + (val_id < 0 || val_id >= MAX_SH_VALUES)) + { + G_INT (pr, OFS_RETURN) = 0; + return; + } + sh = res->hashes[hash_id]; + + if(idx < 0 || idx >= sh->cnt_elements || sh->elements[idx] == NULL) { + if(sh->elements[idx] == NULL) + PR_Error(pr, "NULL hash-element found -> not supposed!"); + + G_INT (pr, OFS_RETURN) = 0; + return; + } + + sh->elements[idx]->values[val_id] = val; + + G_INT (pr, OFS_RETURN) = 1; + return; +} + +/* + bi_StringHash_Get + + Gets the value of a key and its id in a hash +*/ +static void +bi_StringHash_Get (progs_t *pr) +{ + strh_resources_t* res = PR_Resources_Find (pr, "StringHash"); + int hash_id = G_INT (pr, OFS_PARM0); + const char *key = G_STRING (pr, OFS_PARM1); + int val_id = G_INT (pr, OFS_PARM2); + str_hash *sh = NULL; + int i,found_fl=0; + const char *retstr = NULL; + + // validate the hash ID + if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 || + val_id >= MAX_SH_VALUES) + { + retstr = ""; + RETURN_STRING(pr, retstr); + return; + } + sh = res->hashes[hash_id]; + + // first search for existing key + for(i = 0; i < sh->cnt_elements; i++) { + if(strcmp(sh->elements[i]->key, key) == 0) { + if(sh->elements[i]->values[val_id] != NULL) { + retstr = sh->elements[i]->values[val_id]; + } else { + retstr = ""; + } + found_fl = 1; + } + } + if(!found_fl) { + retstr = ""; + } + RETURN_STRING(pr, retstr); +} + +/* + bi_StringHash_Length + + Gets the count of the elements in a hash +*/ +static void +bi_StringHash_Length (progs_t *pr) +{ + strh_resources_t* res = PR_Resources_Find (pr, "StringHash"); + int hash_id = G_INT (pr, OFS_PARM0); + str_hash *sh = NULL; + + // validate the hash ID + if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) { + G_INT (pr, OFS_RETURN) = 0; + return; + } + sh = res->hashes[hash_id]; + + G_INT (pr, OFS_RETURN) = sh->cnt_elements; +} + +/* + bi_StringHash_GetIdx + + Gets a hash elment by its index + special: if the val_id is -1 the key of the element will + be returned +*/ +static void +bi_StringHash_GetIdx (progs_t *pr) +{ + strh_resources_t* res = PR_Resources_Find (pr, "StringHash"); + int hash_id = G_INT (pr, OFS_PARM0); + int idx = G_INT (pr, OFS_PARM1); + int val_id = G_INT (pr, OFS_PARM2); + str_hash *sh = NULL; + const char *retstr = NULL; + + // validate the hash ID + if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) { + retstr = NULL; + } + sh = res->hashes[hash_id]; + + if(idx < 0 || idx >= sh->cnt_elements || + (val_id < -1 || val_id >= MAX_SH_VALUES)) + { + retstr = NULL; + } else { + if(val_id == -1) { + retstr = sh->elements[idx]->key; + } else { + retstr = sh->elements[idx]->values[val_id]; + } + } + if(retstr == NULL) { retstr = ""; } + + RETURN_STRING(pr, retstr); +} + +/* + bi_strh_clear + + Free the dynamic allocated memory + XXX: taniwha: i dont know what to free + exactly, could you validate this code? +*/ +static void +bi_strh_clear (progs_t *pr, void *data) +{ + strh_resources_t *res = (strh_resources_t *)data; + int i,d; + + for (i = 0; i < res->cnt_hashes; i++) { + if (res->hashes[i]) { + for(d = 0; d < res->hashes[i]->cnt_elements; d++) { + free(res->hashes[i]->elements[d]); + } + free(res->hashes[i]->elements); + free(res->hashes[i]); + res->hashes[i] = 0; + } + } +} + + +/* + StringHash_Progs_Init + + Inits the Progs-system with StringHash resources and + functions. +*/ +void +StringHash_Progs_Init (progs_t *pr) +{ + strh_resources_t *res = malloc (sizeof (strh_resources_t)); + res->cnt_hashes = 0; + res->hashes = NULL; + + PR_Resources_Register (pr, "StringHash", res, bi_strh_clear); + PR_AddBuiltin (pr, "StringHash_Create", bi_StringHash_Create, -1); + PR_AddBuiltin (pr, "StringHash_Destroy", bi_StringHash_Destroy, -1); + PR_AddBuiltin (pr, "StringHash_Set", bi_StringHash_Set, -1); + PR_AddBuiltin (pr, "StringHash_Get", bi_StringHash_Get, -1); + PR_AddBuiltin (pr, "StringHash_Length", bi_StringHash_Length, -1); + PR_AddBuiltin (pr, "StringHash_SetIdx", bi_StringHash_SetIdx, -1); + PR_AddBuiltin (pr, "StringHash_GetIdx", bi_StringHash_GetIdx, -1); +} + +/* + XXX NOTE by elmex: + A file, decripted like this is what + i want to see everywhere in qf-cvs =) + No excuse for undocumented code and design without + a reason for it. + We/I want to know why something was designed how it is. +*/