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. ;)
This commit is contained in:
Robin Redeker 2002-03-16 15:52:17 +00:00
parent 51805ebd92
commit 757556022b
10 changed files with 806 additions and 108 deletions

View file

@ -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 $<

View file

@ -1,3 +1,34 @@
/*
options.qc
Options menu
Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>
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 ();
};

View file

@ -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

View file

@ -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 ();

View file

@ -12,7 +12,6 @@ CB_ Input callback
MENU_ Menu making function
DRAW_ Draw
Comment functions like this:
/*
[PREFIX_]FunctionName

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -0,0 +1,440 @@
/*
bi_inputline.c
CSQC string hashes builtins
Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>
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 <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#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.
*/