/* =========================================================================== Copyright (C) 2015 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code 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. OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "q_shared.h" #include "qcommon.h" #include "alias.h" static AliasList_t *Aliases; static AliasListNode_t *foundlist[ 64 ]; AliasListNode_t *AliasListNode_New() { AliasListNode_t *node = Z_TagMalloc( sizeof( AliasListNode_t ), TAG_TIKI ); memset( node, 0, sizeof( AliasListNode_t ) ); node->weight = 1.0f; node->next = NULL; return node; } AliasList_t *AliasList_New( const char *name ) { AliasList_t *list = Z_TagMalloc( sizeof( AliasList_t ), TAG_TIKI ); list->num_in_list = 0; list->sorted_list = NULL; list->data_list = NULL; list->dirty = qfalse; if( name ) { strncpy( list->name, name, sizeof( list->name ) ); } else { list->name[ 0 ] = 0; } return list; } void Alias_ListClear( AliasList_t *list ) { AliasListNode_t *node; AliasListNode_t *next; if( list->sorted_list ) { Z_Free( list->sorted_list ); } for( node = list->data_list; node != NULL; node = next ) { next = node->next; Z_Free( node ); } list->num_in_list = 0; list->dirty = qfalse; list->sorted_list = NULL; list->data_list = NULL; } void Alias_ListDelete( AliasList_t *list ) { Alias_ListClear( list ); Z_Free( list ); } float getTokenFloat( const char **parameters, char *parmName, AliasListNode_t *node ) { const char *token; float temp_float = 0.0f; token = COM_GetToken( parameters, 1 ); if( token && *token ) { temp_float = atof( token ); if( temp_float < 0.0f ) { Com_Printf( "ERROR getTokenFloat: %s value out of range in %s alias\n", parmName, node->alias_name ); } return temp_float; } else { Com_Printf( "ERROR getTokenFloat: %s value is hosed or wasn't found for %s\n", parmName, node->alias_name ); } return 0.0f; } int S_ChannelNameToNum( const char *pszName ) { if( !Q_stricmp( pszName, "auto" ) ) { return CHAN_AUTO; } else if( !Q_stricmp( pszName, "local" ) ) { return CHAN_LOCAL; } else if( !Q_stricmp( pszName, "weapon" ) ) { return CHAN_WEAPON; } else if( !Q_stricmp( pszName, "voice" ) ) { return CHAN_VOICE; } else if( !Q_stricmp( pszName, "item" ) ) { return CHAN_ITEM; } else if( !Q_stricmp( pszName, "body" ) ) { return CHAN_BODY; } else if( !Q_stricmp( pszName, "dialog" ) ) { return CHAN_DIALOG; } else if( !Q_stricmp( pszName, "dialog_secondary" ) ) { return CHAN_DIALOG_SECONDARY; } else if( !Q_stricmp( pszName, "weaponidle" ) ) { return CHAN_WEAPONIDLE; } else if( !Q_stricmp( pszName, "menu" ) ) { return CHAN_MENU; } else { return -1; } } const char *S_ChannelNumToName( int iChannel ) { switch( iChannel ) { case CHAN_AUTO: return "auto"; case CHAN_LOCAL: return "local"; case CHAN_WEAPON: return "weapon"; case CHAN_VOICE: return "voice"; case CHAN_ITEM: return "item"; case CHAN_BODY: return "body"; case CHAN_DIALOG: return "dialog"; case CHAN_DIALOG_SECONDARY: return "dialog_secondary"; case CHAN_WEAPONIDLE: return "weaponidle"; case CHAN_MENU: return "menu"; default: return NULL; } } void Alias_ListAddParms( AliasListNode_t *node, const char *parameters ) { const char *token; float temp_float; const char *nptr; if( !node || !parameters ) { return; } while( 1 ) { while( 1 ) { while( 1 ) { while( 1 ) { while( 1 ) { while( 1 ) { while( 1 ) { while( 1 ) { token = nptr = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { return; } if( Q_stricmp( nptr, "weight" ) ) { break; } node->stop_flag = 1; } if( Q_stricmp( nptr, "weight" ) ) { break; } token = COM_GetToken( ¶meters, 1 ); if( !token || !*token ) { return; } temp_float = atof( token ); if( temp_float < 0.0f ) { Com_Printf( "Weight value out of range in %s alias\n", node->alias_name ); continue; } node->weight = temp_float; } if( Q_stricmp( nptr, "soundparms" ) ) { break; } node->volume = getTokenFloat( ¶meters, "volume", node ); node->volumeMod = getTokenFloat( ¶meters, "volumeMod", node ); node->pitch = getTokenFloat( ¶meters, "pitch", node ); node->pitchMod = getTokenFloat( ¶meters, "pitchMod", node ); node->dist = getTokenFloat( ¶meters, "dist", node ); node->maxDist = getTokenFloat( ¶meters, "maxDist", node ); token = COM_GetToken( ¶meters, 1 ); if( !token || !*token ) { return; } node->channel = S_ChannelNameToNum( token ); if( node->channel == -1 ) { Com_Printf( "%s is not avalid channel on %s alias.\n", token, node->alias_name ); node->channel = 0; } token = COM_GetToken( ¶meters, 1 ); if( !token || !*token ) { Com_Printf( "ERROR: defaulting %s to not streamed - please setup alias properly.\n", node->alias_name ); return; } if( !Q_stricmp( token, "streamed" ) ) { node->streamed = 1; } else if( !Q_stricmp( token, "loaded" ) ) { node->streamed = 0; } else { Com_Printf( "ERROR: Expecting streamed or loaded in ubersound and got %s on alias %s\n", token, node->alias_name ); continue; } } if( Q_stricmp( nptr, "subtitle" ) ) { break; } token = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { Com_Printf( "NULL subtitle on %s alias.\n", node->alias_name ); return; } node->subtitle = Z_TagMalloc( strlen( token ) + 1, TAG_TIKI ); strcpy( node->subtitle, token ); } if( Q_stricmp( nptr, "pitch" ) ) { break; } token = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { return; } temp_float = atof( token ); if( temp_float < 0.0f ) { Com_Printf( "Pitch value out of range in %s alias\n", node->alias_name ); continue; } node->pitch = temp_float; } if( Q_stricmp( nptr, "pitchmod" ) ) { break; } token = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { return; } temp_float = atof( token ); if( temp_float < 0.0f ) { Com_Printf( "PitchMod value out of range in %s alias\n", node->alias_name ); continue; } node->pitchMod = temp_float; } if( Q_stricmp( nptr, "volumemod" ) ) { break; } token = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { return; } temp_float = atof( token ); if( temp_float < 0.0f ) { Com_Printf( "volumeMod value out of range in %s alias\n", node->alias_name ); continue; } node->volumeMod = temp_float; } if( !Q_stricmp( nptr, "dist" ) ) { token = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { Com_Printf( "\n\n BAD DISTANCE\n\n", token ); return; } temp_float = atof( token ); if( temp_float < 0.0f ) { Com_Printf( "dist value out of range in %s alias\n" ); continue; } node->dist = temp_float; } else if( !Q_stricmp( nptr, "channel" ) ) { token = COM_GetToken( ¶meters, qtrue ); if( !token || !*token ) { Com_Printf( "\n\n BAD DISTANCE\n\n", token ); return; } node->channel = S_ChannelNameToNum( token ); if( node->channel == -1 ) { Com_Printf( "%s is not a valid channel on %s alias.\n", token ); node->channel = CHAN_AUTO; } } else { Com_Printf( "Unknown parameter for %s alias\n", node->alias_name ); continue; } } // FIXME: stub } qboolean Alias_ListAdd( AliasList_t *list, const char *alias, const char *name, const char *parameters ) { AliasListNode_t *ptr = NULL; int i; AliasListNode_t *node; if( !list ) { return qfalse; } if( list->dirty ) { for( node = list->data_list; node != NULL; node = node->next ) { if( !strcmp( node->alias_name, alias ) ) { Com_Printf( "DUPLICATE ALIASES: %s and %s\n", node->alias_name, alias ); return qtrue; } } } else { ptr = Alias_ListFindNode( list, alias ); } if( !ptr ) { node = AliasListNode_New(); strncpy( node->alias_name, alias, sizeof( node->alias_name ) ); for( i = 0; i < MAX_ALIASLIST_NAME_LENGTH; i++ ) { node->alias_name[ i ] = tolower( node->alias_name[ i ] ); } strncpy( node->real_name, name, sizeof( node->real_name ) ); node->stop_flag = 0; Alias_ListAddParms( node, parameters ); list->dirty = qtrue; list->num_in_list++; node->next = list->data_list; list->data_list = node; } else if( strcmp( name, ptr->real_name ) ) { Com_DPrintf( "Duplicate Aliases for %s in list %s.\n", alias, name ); } return qtrue; } void Alias_ListSort( AliasList_t *list ) { int num; int size; int i; AliasListNode_t *root; AliasListNode_t *ptr; AliasListNode_t *smallest = NULL; AliasListNode_t *prev; AliasListNode_t *smallprev; AliasListNode_t *last = NULL; if( !list->dirty ) { return; } num = 0; root = list->data_list; list->data_list = NULL; if( list->sorted_list ) Z_Free( list->sorted_list ); size = list->num_in_list * sizeof( AliasListNode_t * ); list->sorted_list = Z_TagMalloc( size, TAG_TIKI ); memset( list->sorted_list, 0, size ); for( i = 0; i < list->num_in_list; i++ ) { prev = root; smallprev = root; for( ptr = root->next; ptr != NULL; ptr = ptr->next ) { if( strcmp( ptr->alias_name, smallprev->alias_name ) < 0 ) { smallest = prev; smallprev = ptr; } prev = ptr; } if( smallprev == root ) { root = smallprev->next; } else { smallest->next = smallprev->next; } if( !last || strcmp( last->alias_name, smallprev->alias_name ) ) { list->sorted_list[ num++ ] = smallprev; smallprev->next = list->data_list; list->data_list = smallprev; last = smallprev; } else { if( strcmp( last->real_name, smallprev->real_name ) ) { Com_DPrintf( "Duplicate Aliases for %s in list %s Last: %x.\n", last->alias_name, list->name, last ); } Z_Free( smallprev ); } } list->dirty = 0; list->num_in_list = num; } const char *Alias_ListFind( AliasList_t *list, const char *alias ) { AliasListNode_t *node = Alias_ListFindNode( list, alias ); if( node ) { return node->real_name; } else { return NULL; } } void Alias_ListUpdateDialog( AliasList_t *list, const char *alias ) { int min_index; int max_index; float total_weight; Alias_ListFindRandomRange( list, alias, &min_index, &max_index, &total_weight ); } AliasListNode_t *Alias_ListFindNode( AliasList_t *list, const char *alias ) { char convalias[ 40 ]; int index; int l; int r; int diff; size_t length; int __res; if( list->dirty ) { Alias_ListSort( list ); } length = strlen( alias ); for( index = 0; index < length; index++ ) { convalias[ index ] = tolower( alias[ index ] ); } convalias[ length ] = 0; r = 0; l = list->num_in_list - 1; for( index = l; index >= 0; index-- ) { __res = ( l + r ) >> 1; diff = strcmp( convalias, list->sorted_list[ index ]->alias_name ); if( diff == 0 ) { return list->sorted_list[ index ]; } else if( diff > 0 ) { r = __res + 1; } else { l = __res - 1; } } return NULL; } float randweight() { return ( ( float )( rand() & 0x7fff ) ) / ( ( float )0x8000 ); } const char *Alias_ListFindRandom( AliasList_t *list, const char *alias, AliasListNode_t **ret ) { int min_index; int max_index; float total_weight; int i; int numfound; float weight; float num; if( !*alias ) { return NULL; } Alias_ListFindRandomRange( list, alias, &min_index, &max_index, &total_weight ); if( min_index == -1 || max_index == -1 ) { return NULL; } num = 0.0f; numfound = max_index - min_index + 1; weight = randweight() * total_weight; for( i = 0; i < numfound; i++ ) { num += list->sorted_list[ i + min_index ]->weight; if( num > weight ) { if( ret ) { *ret = list->sorted_list[ i + min_index ]; } return list->sorted_list[ i + min_index ]->real_name; } } return NULL; } void Alias_ListFindRandomRange( AliasList_t *list, const char *alias, int *min_index, int *max_index, float *total_weight ) { char convalias[ MAX_ALIASLIST_NAME_LENGTH ]; int index; int l; int r; int diff; int i; size_t length; int numfound; float totalfoundweight = 0.0f; AliasListNode_t **ptr; *min_index = -1; *max_index = -1; if( !*alias ) return; if( list->dirty ) Alias_ListSort( list ); length = strlen( alias ); if( length > MAX_ALIASLIST_NAME_LENGTH ) { return; } for( index = 0; index < length; index++ ) { convalias[ index ] = tolower( alias[ index ] ); } convalias[ length ] = 0; l = 0; r = list->num_in_list - 1; if (r >= 0) { do { index = (l + r) >> 1; diff = strncmp( convalias, list->sorted_list[index]->alias_name, length ); if( diff == 0 ) { break; } else if( diff > 0 ) { l = index + 1; } else { r = index - 1; } } while (r >= l); if(l >= r) { numfound = 0; *min_index = index; *max_index = index; totalfoundweight = 0.f; ptr = &list->sorted_list[index]; for(i = 0; i < index; i++, ptr--) { if (strncmp(convalias, (*ptr)->alias_name, length)) { break; } if( numfound <= 63 && ( *ptr )->alias_name[ length ] != '_' ) { foundlist[numfound++] = *ptr; totalfoundweight += (*ptr)->weight; *min_index = i; } } ptr = &list->sorted_list[index + 1]; for(i = index + 1; i < list->num_in_list; i++, ptr++) { if (strncmp(convalias, (*ptr)->alias_name, length)) { break; } if( numfound <= 63 && ( *ptr )->alias_name[ length ] != '_' ) { foundlist[ numfound++ ] = *ptr; totalfoundweight += ( *ptr )->weight; *max_index = i; } } if( numfound ) { *total_weight = totalfoundweight; } else { *min_index = -1; *max_index = -1; } } } } void Alias_ListDump( AliasList_t *list ) { AliasListNode_t *ptr; int i; if( list->dirty ) Alias_ListSort( list ); Com_DPrintf( "Alias List: %s\n", ( char )list ); Com_DPrintf( "Alias Name / Weight / Real Name\n" ); for( i = 0; i < list->num_in_list; ++i ) { ptr = list->sorted_list[ i ]; Com_DPrintf( "%32s %3.2f %s\n", ptr->alias_name ); } Com_DPrintf( "%d total aliases\n", list->num_in_list ); } const char *Alias_Find( const char *alias ) { if( !Aliases ) Aliases = AliasList_New( "Global" ); return Alias_ListFind( Aliases, alias ); } qboolean Alias_Add( const char *alias, const char *name, const char *parameters ) { if( !Aliases ) Aliases = AliasList_New( "Global" ); return Alias_ListAdd( Aliases, alias, name, parameters ); } qboolean Alias_Delete( const char *alias ) { return qtrue; } const char *Alias_FindRandom( const char *alias, AliasListNode_t **ret ) { if( !Aliases ) Aliases = AliasList_New( "Global" ); return Alias_ListFindRandom( Aliases, alias, ret ); } void Alias_Dump() { if( !Aliases ) Aliases = AliasList_New( "Global" ); Alias_ListDump( Aliases ); } void Alias_Clear() { if( !Aliases ) Aliases = AliasList_New( "Global" ); Alias_ListClear( Aliases ); } AliasList_t *Alias_GetGlobalList() { return Aliases; }