mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-25 13:21:36 +00:00
4d25c073ec
fix crash from qwplayers with invalid modelindexes rework allow_skybox a little. now applies immediately. try to fix the appears-outside-of-map bug, again. dir *.wav now shows a little extra info on mouse-over. try loading music file extensions that we expect to be able to play via ffmpeg, not just the ones that are directly supported. rework the hidden fps_presets, show them with tab completion. fix a possible crash with r_temporalscenecache fix lightmap updates on submodels not happening properly with the scenecache. fix the serious memory leak with scenecache. add r_glsl_pbr cvar to force use of pbr pathways in our glsl. fix bug in alsa output not supporting float output properly. preliminary work to have the mixer use floating point mixing. disabled for now. try to update sys_register_file_associations on linux, still needs work though. try to work around nquake's config quirks, so config files don't get overwritten. repackage quake's conchars in order to add padding. this should avoid extra junk on the outside of glyphs. give fteqcc(commandline version) some extra package extration/creation support. our paks should be more end-user-friendly, and our spanned pk3s are awesome. write rune state to legacy saved games, so we don't ever have the missing-runes bug ever again... git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5780 fc73d0e0-1445-4013-8a0c-d673dee63da5
391 lines
7.6 KiB
C
391 lines
7.6 KiB
C
#if _MSC_VER >= 1300
|
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
#ifndef _CRT_NONSTDC_NO_WARNINGS
|
|
#define _CRT_NONSTDC_NO_WARNINGS
|
|
#endif
|
|
#endif
|
|
|
|
#include "hash.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifndef _WIN32
|
|
#ifndef stricmp
|
|
#define stricmp strcasecmp
|
|
#endif
|
|
#endif
|
|
|
|
// hash init assumes we get clean memory
|
|
void Hash_InitTable(hashtable_t *table, unsigned int numbucks, void *mem)
|
|
{
|
|
table->numbuckets = numbucks;
|
|
table->bucket = (bucket_t **)mem;
|
|
}
|
|
|
|
void *Hash_Enumerate(hashtable_t *table, void (*callback) (void *ctx, void *data), void *ctx)
|
|
{
|
|
unsigned int bucknum;
|
|
bucket_t *buck;
|
|
void *data;
|
|
|
|
for (bucknum = 0; bucknum < table->numbuckets; bucknum++)
|
|
{
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
data = buck->data;
|
|
buck = buck->next;
|
|
|
|
//now that we don't care about backlinks etc, we can call the callback and it can safely nuke it (Hash_RemoveData or even destroy the bucket if the hash table is going to die).
|
|
callback(ctx, data);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int Hash_Key(const char *name, unsigned int modulus)
|
|
{ //fixme: optimize.
|
|
unsigned int key;
|
|
for (key=0;*name; name++)
|
|
key += ((key<<3) + (key>>28) + *name);
|
|
|
|
return (key%modulus);
|
|
}
|
|
unsigned int Hash_KeyInsensitive(const char *name, unsigned int modulus)
|
|
{ //fixme: optimize.
|
|
unsigned int key;
|
|
for (key=0;*name; name++)
|
|
{
|
|
if (*name >= 'A' && *name <= 'Z')
|
|
key += ((key<<3) + (key>>28) + (*name-'A'+'a'));
|
|
else
|
|
key += ((key<<3) + (key>>28) + *name);
|
|
}
|
|
|
|
return (key%modulus);
|
|
}
|
|
|
|
void *Hash_GetIdx(hashtable_t *table, unsigned int idx)
|
|
{
|
|
unsigned int bucknum;
|
|
bucket_t *buck;
|
|
|
|
for (bucknum = 0; bucknum < table->numbuckets; bucknum++)
|
|
{
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (!idx--)
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void *Hash_Get(hashtable_t *table, const char *name)
|
|
{
|
|
unsigned int bucknum = Hash_Key(name, table->numbuckets);
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (!STRCMP(name, buck->key.string))
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
void *Hash_GetInsensitive(hashtable_t *table, const char *name)
|
|
{
|
|
unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (!stricmp(name, buck->key.string))
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
void *Hash_GetInsensitiveBucket(hashtable_t *table, const char *name)
|
|
{
|
|
unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (!stricmp(name, buck->key.string))
|
|
return buck;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
void *Hash_GetKey(hashtable_t *table, unsigned int key)
|
|
{
|
|
unsigned int bucknum = key%table->numbuckets;
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (buck->key.value == key)
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
/*Does _NOT_ support items that are added with two names*/
|
|
void *Hash_GetNextKey(hashtable_t *table, unsigned int key, void *old)
|
|
{
|
|
unsigned int bucknum = key%table->numbuckets;
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (buck->data == old) //found the old one
|
|
break;
|
|
buck = buck->next;
|
|
}
|
|
if (!buck)
|
|
return NULL;
|
|
|
|
buck = buck->next;//don't return old
|
|
while(buck)
|
|
{
|
|
if (buck->key.value == key)
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
/*Does _NOT_ support items that are added with two names*/
|
|
void *Hash_GetNext(hashtable_t *table, const char *name, void *old)
|
|
{
|
|
unsigned int bucknum = Hash_Key(name, table->numbuckets);
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (buck->data == old) //found the old one
|
|
// if (!STRCMP(name, buck->key.string))
|
|
break;
|
|
buck = buck->next;
|
|
}
|
|
if (!buck)
|
|
return NULL;
|
|
|
|
buck = buck->next;//don't return old
|
|
while(buck)
|
|
{
|
|
if (!STRCMP(name, buck->key.string))
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
/*Does _NOT_ support items that are added with two names*/
|
|
void *Hash_GetNextInsensitive(hashtable_t *table, const char *name, void *old)
|
|
{
|
|
unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
while(buck)
|
|
{
|
|
if (buck->data == old) //found the old one
|
|
{
|
|
// if (!stricmp(name, buck->key.string))
|
|
break;
|
|
}
|
|
|
|
buck = buck->next;
|
|
}
|
|
if (!buck)
|
|
return NULL;
|
|
|
|
buck = buck->next;//don't return old
|
|
while(buck)
|
|
{
|
|
if (!stricmp(name, buck->key.string))
|
|
return buck->data;
|
|
|
|
buck = buck->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck)
|
|
{
|
|
unsigned int bucknum = Hash_Key(name, table->numbuckets);
|
|
|
|
buck->data = data;
|
|
buck->key.string = name;
|
|
buck->next = table->bucket[bucknum];
|
|
table->bucket[bucknum] = buck;
|
|
|
|
return buck;
|
|
}
|
|
void *Hash_AddInsensitive(hashtable_t *table, const char *name, void *data, bucket_t *buck)
|
|
{
|
|
unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);
|
|
|
|
buck->data = data;
|
|
buck->key.string = name;
|
|
buck->next = table->bucket[bucknum];
|
|
table->bucket[bucknum] = buck;
|
|
|
|
return buck;
|
|
}
|
|
void *Hash_AddKey(hashtable_t *table, unsigned int key, void *data, bucket_t *buck)
|
|
{
|
|
unsigned int bucknum = key%table->numbuckets;
|
|
|
|
buck->data = data;
|
|
buck->key.value = key;
|
|
buck->next = table->bucket[bucknum];
|
|
table->bucket[bucknum] = buck;
|
|
|
|
return buck;
|
|
}
|
|
|
|
void Hash_Remove(hashtable_t *table, const char *name)
|
|
{
|
|
unsigned int bucknum = Hash_Key(name, table->numbuckets);
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
if (!STRCMP(name, buck->key.string))
|
|
{
|
|
table->bucket[bucknum] = buck->next;
|
|
return;
|
|
}
|
|
|
|
|
|
while(buck->next)
|
|
{
|
|
if (!STRCMP(name, buck->next->key.string))
|
|
{
|
|
buck->next = buck->next->next;
|
|
return;
|
|
}
|
|
|
|
buck = buck->next;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void Hash_RemoveDataInsensitive(hashtable_t *table, const char *name, void *data)
|
|
{
|
|
unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);
|
|
bucket_t **link, *buck;
|
|
|
|
for (link = &table->bucket[bucknum]; *link; link = &(*link)->next)
|
|
{
|
|
buck = *link;
|
|
if (buck->data == data && !stricmp(name, buck->key.string))
|
|
{
|
|
*link = buck->next;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
void Hash_RemoveData(hashtable_t *table, const char *name, void *data)
|
|
{
|
|
unsigned int bucknum = Hash_Key(name, table->numbuckets);
|
|
bucket_t **link, *buck;
|
|
|
|
for (link = &table->bucket[bucknum]; *link; link = &(*link)->next)
|
|
{
|
|
buck = *link;
|
|
if (buck->data == data && !stricmp(name, buck->key.string))
|
|
{
|
|
*link = buck->next;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
void Hash_RemoveBucket(hashtable_t *table, const char *name, bucket_t *data)
|
|
{
|
|
unsigned int bucknum = Hash_Key(name, table->numbuckets);
|
|
bucket_t **link, *buck;
|
|
|
|
for (link = &table->bucket[bucknum]; *link; link = &(*link)->next)
|
|
{
|
|
buck = *link;
|
|
if (buck == data && !stricmp(name, buck->key.string))
|
|
{
|
|
*link = buck->next;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void Hash_RemoveDataKey(hashtable_t *table, unsigned int key, void *data)
|
|
{
|
|
unsigned int bucknum = key%table->numbuckets;
|
|
bucket_t **link, *buck;
|
|
|
|
for (link = &table->bucket[bucknum]; *link; link = &(*link)->next)
|
|
{
|
|
buck = *link;
|
|
if (buck->data == data && buck->key.value == key)
|
|
{
|
|
*link = buck->next;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
void Hash_RemoveKey(hashtable_t *table, unsigned int key)
|
|
{
|
|
unsigned int bucknum = key%table->numbuckets;
|
|
bucket_t *buck;
|
|
|
|
buck = table->bucket[bucknum];
|
|
|
|
if (buck->key.value == key)
|
|
{
|
|
table->bucket[bucknum] = buck->next;
|
|
return;
|
|
}
|
|
|
|
|
|
while(buck->next)
|
|
{
|
|
if (buck->next->key.value == key)
|
|
{
|
|
buck->next = buck->next->next;
|
|
return;
|
|
}
|
|
|
|
buck = buck->next;
|
|
}
|
|
return;
|
|
}
|