mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 16:10:53 +00:00
3fffa26fb4
With any luck it should actually work this time. Added the getimpulse command so that GIB scripts can check if an impulse command is pending before sending their own. Fixed all the memory leaks I could find. QuakeC and GIB seem to be clean except for maybe one or two sneaky leaks I can't track down.
457 lines
11 KiB
C
457 lines
11 KiB
C
/*
|
|
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 {
|
|
char * key;
|
|
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,d;
|
|
|
|
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 {
|
|
for(d=0;d<MAX_SH_VALUES;d++) {
|
|
// free key
|
|
free(sh->elements[i]->key);
|
|
// free values
|
|
if(sh->elements[i]->values[d] != NULL) {
|
|
free(sh->elements[i]->values[d]);
|
|
}
|
|
}
|
|
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
|
|
// strdup() because strings can dissappear
|
|
sh->elements[i]->values[val_id] = strdup(val);
|
|
} else {
|
|
// when using strdup(), we have to free the stuff properly
|
|
free(sh->elements[i]->values[val_id]);
|
|
sh->elements[i]->values[val_id] = strdup(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 = strdup(key);
|
|
sh->elements[sh->cnt_elements]->values[val_id] = strdup(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;
|
|
}
|
|
|
|
if(sh->elements[idx]->values[val_id] != NULL) {
|
|
free(sh->elements[idx]->values[val_id]);
|
|
}
|
|
sh->elements[idx]->values[val_id] = strdup(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,n;
|
|
|
|
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]->key); // Free the key
|
|
for (n = 0; n < MAX_SH_VALUES; n++) // Free all values
|
|
if (res->hashes[i]->elements[d]->values[n])
|
|
free(res->hashes[i]->elements[d]->values[n]);
|
|
free(res->hashes[i]->elements[d]); // Free the element itself
|
|
}
|
|
free(res->hashes[i]->elements);
|
|
free(res->hashes[i]);
|
|
res->hashes[i] = 0;
|
|
}
|
|
}
|
|
free (res->hashes);
|
|
res->hashes = 0;
|
|
res->cnt_hashes = 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.
|
|
*/
|