mirror of
https://github.com/rehlds/rehlds.git
synced 2025-04-08 10:39:05 +03:00
621 lines
12 KiB
C++
621 lines
12 KiB
C++
/*
|
|
*
|
|
* 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 the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* In addition, as a special exception, the author gives permission to
|
|
* link the code of this program with the Half-Life Game Engine ("HL
|
|
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
|
|
* L.L.C ("Valve"). You must obey the GNU General Public License in all
|
|
* respects for all of the code used other than the HL Engine and MODs
|
|
* from Valve. If you modify this file, you may extend this exception
|
|
* to your version of the file, but you are not obligated to do so. If
|
|
* you do not wish to do so, delete this exception statement from your
|
|
* version.
|
|
*
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
// NOTE: This file contains a lot of fixes that are not covered by REHLDS_FIXES define.
|
|
// TODO: Most of the Info_ functions can be speedup via removing unneded copy of key and values.
|
|
|
|
/*
|
|
===============
|
|
Info_ValueForKey
|
|
|
|
Searches the string for the given
|
|
key and returns the associated value, or an empty string.
|
|
===============
|
|
*/
|
|
/* <40d86> ../engine/info.c:23 */
|
|
const char* EXT_FUNC Info_ValueForKey(const char *s, const char *key)
|
|
{
|
|
// use few (two?) buffers so compares work without stomping on each other
|
|
static char value[INFO_MAX_BUFFER_VALUES][MAX_KV_LEN];
|
|
static int valueindex;
|
|
char pkey[MAX_KV_LEN];
|
|
char *c;
|
|
int nCount;
|
|
|
|
while (*s)
|
|
{
|
|
if (*s == '\\')
|
|
{
|
|
s++; // skip the slash
|
|
}
|
|
|
|
// Copy a key
|
|
nCount = 0;
|
|
c = pkey;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
return ""; // key should end with a \, not a NULL, but suppose its value as absent
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized key chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
s++; // skip the slash
|
|
|
|
// Copy a value
|
|
nCount = 0;
|
|
c = value[valueindex];
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // allow value to be ended with NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized value chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
|
|
if (!Q_strcmp(key, pkey))
|
|
{
|
|
c = value[valueindex];
|
|
valueindex = (valueindex + 1) % INFO_MAX_BUFFER_VALUES;
|
|
return c;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/* <40e38> ../engine/info.c:72 */
|
|
void Info_RemoveKey(char *s, const char *key)
|
|
{
|
|
char pkey[MAX_KV_LEN];
|
|
char value[MAX_KV_LEN];
|
|
char *start;
|
|
char *c;
|
|
int cmpsize;
|
|
int nCount;
|
|
|
|
if (Q_strstr(key, "\\"))
|
|
{
|
|
Con_Printf("Can't use a key with a \\\n");
|
|
return;
|
|
}
|
|
|
|
cmpsize = Q_strlen(key);
|
|
if (cmpsize > MAX_KV_LEN - 1)
|
|
cmpsize = MAX_KV_LEN - 1;
|
|
|
|
while (*s)
|
|
{
|
|
start = s;
|
|
|
|
if (*s == '\\')
|
|
{
|
|
s++; // skip the slash
|
|
}
|
|
|
|
// Copy a key
|
|
nCount = 0;
|
|
c = pkey;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // key should end with a \, not a NULL, but allow to remove it
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized key chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
if (*s)
|
|
s++; // skip the slash
|
|
|
|
// Copy a value
|
|
nCount = 0;
|
|
c = value;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // allow value to be ended with NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized value chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
|
|
// Compare keys
|
|
if (!Q_strncmp(key, pkey, cmpsize))
|
|
{
|
|
strcpy_safe(start, s); // remove this part
|
|
s = start; // continue searching
|
|
}
|
|
}
|
|
}
|
|
|
|
/* <40ecf> ../engine/info.c:136 */
|
|
void Info_RemovePrefixedKeys(char *s, const char prefix)
|
|
{
|
|
char pkey[MAX_KV_LEN];
|
|
char value[MAX_KV_LEN];
|
|
char *start;
|
|
char *c;
|
|
int nCount;
|
|
|
|
while (*s)
|
|
{
|
|
start = s;
|
|
|
|
if (*s == '\\')
|
|
{
|
|
s++; // skip the slash
|
|
}
|
|
|
|
// Copy a key
|
|
nCount = 0;
|
|
c = pkey;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // key should end with a \, not a NULL, but allow to remove it
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized key chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
if (*s)
|
|
s++; // skip the slash
|
|
|
|
// Copy a value
|
|
nCount = 0;
|
|
c = value;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // allow value to be ended with NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized value chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
|
|
if (pkey[0] == prefix)
|
|
{
|
|
strcpy_safe(start, s); // remove this part
|
|
s = start; // continue searching
|
|
}
|
|
}
|
|
}
|
|
|
|
/* <40d4a> ../engine/info.c:188 */
|
|
qboolean Info_IsKeyImportant(const char *key)
|
|
{
|
|
if (key[0] == '*')
|
|
return true;
|
|
if (!Q_strcmp(key, "name"))
|
|
return true;
|
|
if (!Q_strcmp(key, "model"))
|
|
return true;
|
|
if (!Q_strcmp(key, "rate"))
|
|
return true;
|
|
if (!Q_strcmp(key, "topcolor"))
|
|
return true;
|
|
if (!Q_strcmp(key, "bottomcolor"))
|
|
return true;
|
|
if (!Q_strcmp(key, "cl_updaterate"))
|
|
return true;
|
|
if (!Q_strcmp(key, "cl_lw"))
|
|
return true;
|
|
if (!Q_strcmp(key, "cl_lc"))
|
|
return true;
|
|
#ifndef REHLDS_FIXES
|
|
// keys starts from '*' already checked
|
|
if (!Q_strcmp(key, "*hltv"))
|
|
return true;
|
|
if (!Q_strcmp(key, "*sid"))
|
|
return true;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
/* <40f88> ../engine/info.c:216 */
|
|
char *Info_FindLargestKey(char *s, int maxsize)
|
|
{
|
|
static char largest_key[MAX_KV_LEN];
|
|
char key[MAX_KV_LEN];
|
|
char value[MAX_KV_LEN];
|
|
char *c;
|
|
int nCount;
|
|
int largest_size = 0;
|
|
|
|
largest_key[0] = 0;
|
|
|
|
while (*s)
|
|
{
|
|
if (*s == '\\')
|
|
{
|
|
s++; // skip the slash
|
|
}
|
|
|
|
// Copy a key
|
|
nCount = 0;
|
|
c = key;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s) // key should end with a \, not a NULL, return this key, so it will be deleted as wrong
|
|
{
|
|
*c = 0;
|
|
Q_strcpy(largest_key, key);
|
|
return largest_key;
|
|
}
|
|
if (nCount >= MAX_KV_LEN) // oversized key, return this key, so it will be deleted as wrong
|
|
{
|
|
*c = 0;
|
|
Q_strcpy(largest_key, key);
|
|
return largest_key;
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
s++; // skip the slash
|
|
|
|
// Get length
|
|
int size = c - key;
|
|
|
|
// Copy a value
|
|
nCount = 0;
|
|
c = value;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // allow value to be ended with NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN) // oversized value, return this key, so it will be deleted as wrong
|
|
{
|
|
*c = 0;
|
|
Q_strcpy(largest_key, key);
|
|
return largest_key;
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
|
|
// Add length
|
|
size += c - value;
|
|
|
|
if (size > largest_size && !Info_IsKeyImportant(key))
|
|
{
|
|
largest_size = size;
|
|
Q_strcpy(largest_key, key);
|
|
}
|
|
}
|
|
|
|
return largest_key;
|
|
}
|
|
|
|
/* <41063> ../engine/info.c:275 */
|
|
void Info_SetValueForStarKey(char *s, const char *key, const char *value, int maxsize)
|
|
{
|
|
char newArray[MAX_INFO_STRING];
|
|
char *v;
|
|
int c;
|
|
|
|
if (!key || !value)
|
|
{
|
|
Con_Printf("Keys and values can't be null\n");
|
|
return;
|
|
}
|
|
|
|
if (key[0] == 0)
|
|
{
|
|
Con_Printf("Keys can't be an empty string\n");
|
|
return;
|
|
}
|
|
|
|
if (Q_strstr(key, "\\") || Q_strstr(value, "\\"))
|
|
{
|
|
Con_Printf("Can't use keys or values with a \\\n");
|
|
return;
|
|
}
|
|
|
|
if (Q_strstr(key, "..") || Q_strstr(value, ".."))
|
|
{
|
|
// TODO: Why silently return?
|
|
//Con_Printf("Can't use keys or values with a ..\n");
|
|
return;
|
|
}
|
|
|
|
if (Q_strstr(key, "\"") || Q_strstr(value, "\""))
|
|
{
|
|
Con_Printf("Can't use keys or values with a \"\n");
|
|
return;
|
|
}
|
|
|
|
int keyLen = Q_strlen(key);
|
|
int valueLan = Q_strlen(value);
|
|
|
|
if (keyLen >= MAX_KV_LEN || valueLan >= MAX_KV_LEN)
|
|
{
|
|
Con_Printf("Keys and values must be < %i characters\n", MAX_KV_LEN);
|
|
return;
|
|
}
|
|
|
|
if (!Q_UnicodeValidate(key) || !Q_UnicodeValidate(value))
|
|
{
|
|
Con_Printf("Keys and values must be valid utf8 text\n");
|
|
return;
|
|
}
|
|
|
|
// Remove current key/value and return if we doesn't specified to set a value
|
|
Info_RemoveKey(s, key);
|
|
if (value[0] == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Create key/value pair
|
|
Q_snprintf(newArray, MAX_INFO_STRING - 1, "\\%s\\%s", key, value);
|
|
newArray[MAX_INFO_STRING - 1] = 0;
|
|
|
|
int neededLength = Q_strlen(newArray);
|
|
if ((int)Q_strlen(s) + neededLength >= maxsize)
|
|
{
|
|
// no more room in the buffer to add key/value
|
|
if (!Info_IsKeyImportant(key))
|
|
{
|
|
// no room to add setting
|
|
Con_Printf("Info string length exceeded\n");
|
|
return;
|
|
}
|
|
|
|
// keep removing the largest key/values until we have a room
|
|
char *largekey;
|
|
do
|
|
{
|
|
largekey = Info_FindLargestKey(s, maxsize);
|
|
if (largekey[0] == 0)
|
|
{
|
|
// no room to add setting
|
|
Con_Printf("Info string length exceeded\n");
|
|
return;
|
|
}
|
|
Info_RemoveKey(s, largekey);
|
|
} while ((int)Q_strlen(s) + neededLength >= maxsize);
|
|
}
|
|
|
|
// auto lowercase team
|
|
bool lowerCaseValue = Q_stricmp(key, "team") == 0;
|
|
s += Q_strlen(s);
|
|
v = newArray;
|
|
while (*v)
|
|
{
|
|
c = (unsigned char)*v++;
|
|
if (lowerCaseValue)
|
|
{
|
|
c = tolower(c);
|
|
}
|
|
*s++ = c;
|
|
}
|
|
*s = 0;
|
|
}
|
|
|
|
/* <4113e> ../engine/info.c:361 */
|
|
void Info_SetValueForKey(char *s, const char *key, const char *value, int maxsize)
|
|
{
|
|
if (key[0] == '*')
|
|
{
|
|
Con_Printf("Can't set * keys\n");
|
|
return;
|
|
}
|
|
|
|
Info_SetValueForStarKey(s, key, value, maxsize);
|
|
}
|
|
|
|
/* <41193> ../engine/info.c:372 */
|
|
void Info_Print(const char *s)
|
|
{
|
|
char key[MAX_KV_LEN];
|
|
char value[MAX_KV_LEN];
|
|
char *c;
|
|
int l;
|
|
int nCount;
|
|
|
|
while (*s)
|
|
{
|
|
if (*s == '\\')
|
|
{
|
|
s++; // skip the slash
|
|
}
|
|
|
|
// Copy a key
|
|
nCount = 0;
|
|
c = key;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // key should end with a \, not a NULL, but allow to print it
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized key chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
if (*s)
|
|
s++; // skip the slash
|
|
|
|
// Pad and print a key
|
|
l = c - key;
|
|
if (l < 20)
|
|
{
|
|
Q_memset(c, ' ', 20 - l);
|
|
key[20] = 0;
|
|
}
|
|
Con_Printf("%s", key);
|
|
|
|
if (!*s)
|
|
{
|
|
Con_Printf("MISSING VALUE\n");
|
|
return;
|
|
}
|
|
|
|
// Copy a value
|
|
nCount = 0;
|
|
c = value;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // allow value to be ended with NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
s++;
|
|
continue; // skip oversized value chars till the slash or EOL
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
|
|
Con_Printf("%s\n", value);
|
|
}
|
|
}
|
|
|
|
/* <4120e> ../engine/info.c:426 */
|
|
qboolean Info_IsValid(const char *s)
|
|
{
|
|
char key[MAX_KV_LEN];
|
|
char value[MAX_KV_LEN];
|
|
char *c;
|
|
int nCount;
|
|
|
|
while (*s)
|
|
{
|
|
if (*s == '\\')
|
|
{
|
|
s++; // skip the slash
|
|
}
|
|
|
|
// Copy a key
|
|
nCount = 0;
|
|
c = key;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
return FALSE; // key should end with a \, not a NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
return FALSE; // key length should be less then MAX_KV_LEN
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
s++; // skip the slash
|
|
|
|
// Copy a value
|
|
nCount = 0;
|
|
c = value;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s)
|
|
{
|
|
break; // allow value to be ended with NULL
|
|
}
|
|
if (nCount >= MAX_KV_LEN)
|
|
{
|
|
return FALSE; // value length should be less then MAX_KV_LEN
|
|
}
|
|
*c++ = *s++;
|
|
nCount++;
|
|
}
|
|
*c = 0;
|
|
|
|
if (value[0] == 0)
|
|
{
|
|
return FALSE; // empty values are not valid
|
|
}
|
|
|
|
if (!*s)
|
|
{
|
|
return TRUE; // EOL, info is valid
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|