mirror of
https://github.com/s1lentq/ReGameDLL_CS.git
synced 2024-12-27 07:05:38 +03:00
1108 lines
27 KiB
C++
1108 lines
27 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"
|
|
|
|
TYPEDESCRIPTION g_EntvarsDescription[] =
|
|
{
|
|
DEFINE_ENTITY_FIELD(classname, FIELD_STRING),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(globalname, FIELD_STRING),
|
|
DEFINE_ENTITY_FIELD(origin, FIELD_POSITION_VECTOR),
|
|
DEFINE_ENTITY_FIELD(oldorigin, FIELD_POSITION_VECTOR),
|
|
DEFINE_ENTITY_FIELD(velocity, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(basevelocity, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(movedir, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(angles, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(avelocity, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(punchangle, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(v_angle, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(fixangle, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(idealpitch, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(pitch_speed, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(ideal_yaw, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(yaw_speed, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(modelindex, FIELD_INTEGER),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(model, FIELD_MODELNAME),
|
|
DEFINE_ENTITY_FIELD(viewmodel, FIELD_MODELNAME),
|
|
DEFINE_ENTITY_FIELD(weaponmodel, FIELD_MODELNAME),
|
|
DEFINE_ENTITY_FIELD(absmin, FIELD_POSITION_VECTOR),
|
|
DEFINE_ENTITY_FIELD(absmax, FIELD_POSITION_VECTOR),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(mins, FIELD_VECTOR),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(maxs, FIELD_VECTOR),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(size, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(ltime, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(nextthink, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(solid, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(movetype, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(skin, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(body, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(effects, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(gravity, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(friction, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(light_level, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(frame, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(scale, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(sequence, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(animtime, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(framerate, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(controller, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(blending, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(rendermode, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(renderamt, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(rendercolor, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(renderfx, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(health, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(frags, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(weapons, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(takedamage, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(deadflag, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(view_ofs, FIELD_VECTOR),
|
|
DEFINE_ENTITY_FIELD(button, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(impulse, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(chain, FIELD_EDICT),
|
|
DEFINE_ENTITY_FIELD(dmg_inflictor, FIELD_EDICT),
|
|
DEFINE_ENTITY_FIELD(enemy, FIELD_EDICT),
|
|
DEFINE_ENTITY_FIELD(aiment, FIELD_EDICT),
|
|
DEFINE_ENTITY_FIELD(owner, FIELD_EDICT),
|
|
DEFINE_ENTITY_FIELD(groundentity, FIELD_EDICT),
|
|
DEFINE_ENTITY_FIELD(spawnflags, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(flags, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(colormap, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(team, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(max_health, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(teleport_time, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(armortype, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(armorvalue, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(waterlevel, FIELD_INTEGER),
|
|
DEFINE_ENTITY_FIELD(watertype, FIELD_INTEGER),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(target, FIELD_STRING),
|
|
DEFINE_ENTITY_GLOBAL_FIELD(targetname, FIELD_STRING),
|
|
DEFINE_ENTITY_FIELD(netname, FIELD_STRING),
|
|
DEFINE_ENTITY_FIELD(message, FIELD_STRING),
|
|
DEFINE_ENTITY_FIELD(dmg_take, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(dmg_save, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(dmg, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(dmgtime, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(noise, FIELD_SOUNDNAME),
|
|
DEFINE_ENTITY_FIELD(noise1, FIELD_SOUNDNAME),
|
|
DEFINE_ENTITY_FIELD(noise2, FIELD_SOUNDNAME),
|
|
DEFINE_ENTITY_FIELD(noise3, FIELD_SOUNDNAME),
|
|
DEFINE_ENTITY_FIELD(speed, FIELD_FLOAT),
|
|
DEFINE_ENTITY_FIELD(air_finished, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(pain_finished, FIELD_TIME),
|
|
DEFINE_ENTITY_FIELD(radsuit_finished, FIELD_TIME),
|
|
};
|
|
|
|
void EntvarsKeyvalue(entvars_t *pev, KeyValueData *pkvd)
|
|
{
|
|
for (int i = 0; i < ARRAYSIZE(g_EntvarsDescription); i++)
|
|
{
|
|
TYPEDESCRIPTION *pField = &g_EntvarsDescription[i];
|
|
|
|
if (!Q_stricmp(pField->fieldName, pkvd->szKeyName))
|
|
{
|
|
switch (pField->fieldType)
|
|
{
|
|
case FIELD_STRING:
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
*(string_t *)((char *)pev + pField->fieldOffset) = ALLOC_STRING(pkvd->szValue);
|
|
break;
|
|
case FIELD_FLOAT:
|
|
case FIELD_TIME:
|
|
*(float *)((char *)pev + pField->fieldOffset) = Q_atof(pkvd->szValue);
|
|
break;
|
|
case FIELD_INTEGER:
|
|
*(string_t *)((char *)pev + pField->fieldOffset) = Q_atoi(pkvd->szValue);
|
|
break;
|
|
case FIELD_VECTOR:
|
|
case FIELD_POSITION_VECTOR:
|
|
UTIL_StringToVector((float *)((char *)pev + pField->fieldOffset), pkvd->szValue);
|
|
break;
|
|
|
|
default:
|
|
case FIELD_EVARS:
|
|
case FIELD_CLASSPTR:
|
|
case FIELD_EDICT:
|
|
case FIELD_ENTITY:
|
|
case FIELD_POINTER:
|
|
ALERT(at_error, "Bad field in entity!!\n");
|
|
break;
|
|
}
|
|
pkvd->fHandled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const int CSaveRestoreBuffer::m_Sizes[] =
|
|
{
|
|
sizeof(float), // FIELD_FLOAT
|
|
sizeof(int), // FIELD_STRING
|
|
sizeof(int), // FIELD_ENTITY
|
|
sizeof(int), // FIELD_CLASSPTR
|
|
sizeof(int), // FIELD_EHANDLE
|
|
sizeof(int), // FIELD_entvars_t
|
|
sizeof(int), // FIELD_EDICT
|
|
sizeof(float) * 3, // FIELD_VECTOR
|
|
sizeof(float) * 3, // FIELD_POSITION_VECTOR
|
|
sizeof(int *), // FIELD_POINTER
|
|
sizeof(int), // FIELD_INTEGER
|
|
sizeof(int *), // FIELD_FUNCTION
|
|
sizeof(int), // FIELD_BOOLEAN
|
|
sizeof(short), // FIELD_SHORT
|
|
sizeof(char), // FIELD_CHARACTER
|
|
sizeof(float), // FIELD_TIME
|
|
sizeof(int), // FIELD_MODELNAME
|
|
sizeof(int), // FIELD_SOUNDNAME
|
|
};
|
|
|
|
CSaveRestoreBuffer::CSaveRestoreBuffer()
|
|
{
|
|
m_pData = nullptr;
|
|
}
|
|
|
|
CSaveRestoreBuffer::CSaveRestoreBuffer(SAVERESTOREDATA *pdata)
|
|
{
|
|
m_pData = pdata;
|
|
}
|
|
|
|
CSaveRestoreBuffer::~CSaveRestoreBuffer()
|
|
{
|
|
;
|
|
}
|
|
|
|
int CSaveRestoreBuffer::EntityIndex(CBaseEntity *pEntity)
|
|
{
|
|
if (!pEntity)
|
|
return -1;
|
|
|
|
return EntityIndex(pEntity->pev);
|
|
}
|
|
|
|
int CSaveRestoreBuffer::EntityIndex(entvars_t *pevLookup)
|
|
{
|
|
if (!pevLookup)
|
|
return -1;
|
|
|
|
return EntityIndex(ENT(pevLookup));
|
|
}
|
|
|
|
int CSaveRestoreBuffer::EntityIndex(EOFFSET eoLookup)
|
|
{
|
|
return EntityIndex(ENT(eoLookup));
|
|
}
|
|
|
|
int CSaveRestoreBuffer::EntityIndex(edict_t *pentLookup)
|
|
{
|
|
if (!m_pData || !pentLookup)
|
|
return -1;
|
|
|
|
for (int i = 0; i < m_pData->tableCount; i++)
|
|
{
|
|
ENTITYTABLE *pTable = &m_pData->pTable[i];
|
|
if (pTable->pent == pentLookup)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
edict_t *CSaveRestoreBuffer::EntityFromIndex(int entityIndex)
|
|
{
|
|
if (!m_pData || entityIndex < 0)
|
|
return nullptr;
|
|
|
|
for (int i = 0; i < m_pData->tableCount; i++)
|
|
{
|
|
ENTITYTABLE *pTable = &m_pData->pTable[i];
|
|
if (pTable->id == entityIndex)
|
|
return pTable->pent;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int CSaveRestoreBuffer::EntityFlagsSet(int entityIndex, int flags)
|
|
{
|
|
if (!m_pData || entityIndex < 0)
|
|
return 0;
|
|
|
|
if (!m_pData || entityIndex < 0 || entityIndex > m_pData->tableCount)
|
|
return 0;
|
|
|
|
m_pData->pTable[entityIndex].flags |= flags;
|
|
return m_pData->pTable[entityIndex].flags;
|
|
}
|
|
|
|
int CSaveRestoreBuffer::EntityFlags(int entityIndex, int flags)
|
|
{
|
|
return EntityFlagsSet(entityIndex, flags);
|
|
}
|
|
|
|
void CSaveRestoreBuffer::BufferRewind(int size)
|
|
{
|
|
if (!m_pData)
|
|
return;
|
|
|
|
if (m_pData->size < size)
|
|
size = m_pData->size;
|
|
|
|
m_pData->pCurrentData -= size;
|
|
m_pData->size -= size;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
extern "C"
|
|
{
|
|
inline unsigned _rotr(unsigned val, int shift)
|
|
{
|
|
unsigned lobit;
|
|
unsigned num = val;
|
|
|
|
shift &= 0x1f;
|
|
|
|
while (shift--)
|
|
{
|
|
lobit = num & 1;
|
|
num >>= 1;
|
|
if (lobit)
|
|
num |= 0x80000000;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
}
|
|
#endif // _WIN32
|
|
|
|
unsigned int CSaveRestoreBuffer::HashString(const char *pszToken)
|
|
{
|
|
unsigned int hash = 0;
|
|
while (*pszToken)
|
|
hash = _rotr(hash, 4) ^ *pszToken++;
|
|
|
|
return hash;
|
|
}
|
|
|
|
unsigned short CSaveRestoreBuffer::TokenHash(const char *pszToken)
|
|
{
|
|
unsigned short hash = (unsigned short)(HashString(pszToken) % (unsigned)m_pData->tokenCount);
|
|
for (int i = 0; i < m_pData->tokenCount; i++)
|
|
{
|
|
int index = hash + i;
|
|
if (index >= m_pData->tokenCount)
|
|
index -= m_pData->tokenCount;
|
|
|
|
if (!m_pData->pTokens[index] || !Q_strcmp(pszToken, m_pData->pTokens[index]))
|
|
{
|
|
m_pData->pTokens[index] = (char *)pszToken;
|
|
return index;
|
|
}
|
|
}
|
|
|
|
ALERT(at_error, "CSaveRestoreBuffer :: TokenHash() is COMPLETELY FULL!");
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CSave::WriteData(const char *pname, int size, const char *pdata)
|
|
{
|
|
BufferField(pname, size, pdata);
|
|
}
|
|
|
|
NOXREF void CSave::WriteShort(const char *pname, const short *data, int count)
|
|
{
|
|
BufferField(pname, sizeof(short) * count, (const char *)data);
|
|
}
|
|
|
|
void CSave::WriteInt(const char *pname, const int *data, int count)
|
|
{
|
|
BufferField(pname, sizeof(int) * count, (const char *)data);
|
|
}
|
|
|
|
void CSave::WriteFloat(const char *pname, const float *data, int count)
|
|
{
|
|
BufferField(pname, sizeof(float) * count, (const char *)data);
|
|
}
|
|
|
|
void CSave::WriteTime(const char *pname, const float *data, int count)
|
|
{
|
|
BufferHeader(pname, sizeof(float) * count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
float tmp = data[0];
|
|
if (m_pData) {
|
|
tmp -= m_pData->time;
|
|
}
|
|
|
|
BufferData((const char *)&tmp, sizeof(float));
|
|
data++;
|
|
}
|
|
}
|
|
|
|
NOXREF void CSave::WriteString(const char *pname, const char *pdata)
|
|
{
|
|
BufferField(pname, Q_strlen(pdata) + 1, pdata);
|
|
}
|
|
|
|
void CSave::WriteString(const char *pname, const int *stringId, int count)
|
|
{
|
|
int i;
|
|
int size = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
size += Q_strlen(STRING(stringId[i])) + 1;
|
|
}
|
|
|
|
BufferHeader(pname, size);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
const char *pString = STRING(stringId[i]);
|
|
BufferData(pString, Q_strlen(pString) + 1);
|
|
}
|
|
}
|
|
|
|
void CSave::WriteVector(const char *pname, const Vector &value)
|
|
{
|
|
WriteVector(pname, &value.x, 1);
|
|
}
|
|
|
|
void CSave::WriteVector(const char *pname, const float *value, int count)
|
|
{
|
|
BufferHeader(pname, sizeof(float) * 3 * count);
|
|
BufferData((const char *)value, sizeof(float) * 3 * count);
|
|
}
|
|
|
|
NOXREF void CSave::WritePositionVector(const char *pname, const Vector &value)
|
|
{
|
|
if (m_pData && m_pData->fUseLandmark)
|
|
{
|
|
Vector tmp = value - m_pData->vecLandmarkOffset;
|
|
WriteVector(pname, tmp);
|
|
}
|
|
WriteVector(pname, value);
|
|
}
|
|
|
|
void CSave::WritePositionVector(const char *pname, const float *value, int count)
|
|
{
|
|
BufferHeader(pname, sizeof(float) * 3 * count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Vector tmp(value[0], value[1], value[2]);
|
|
if (m_pData && m_pData->fUseLandmark) {
|
|
tmp -= m_pData->vecLandmarkOffset;
|
|
}
|
|
|
|
BufferData((const char *)&tmp.x, sizeof(float) * 3);
|
|
value += 3;
|
|
}
|
|
}
|
|
|
|
void CSave::WriteFunction(const char *pname, void **data, int count)
|
|
{
|
|
const char *functionName = NAME_FOR_FUNCTION((uint32)*data);
|
|
|
|
if (functionName)
|
|
BufferField(pname, Q_strlen(functionName) + 1, functionName);
|
|
else
|
|
ALERT(at_error, "Invalid function pointer in entity!");
|
|
}
|
|
|
|
int CSave::WriteEntVars(const char *pname, entvars_t *pev)
|
|
{
|
|
return WriteFields(pname, pev, g_EntvarsDescription, ARRAYSIZE(g_EntvarsDescription));
|
|
}
|
|
|
|
int CSave::WriteFields(const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount)
|
|
{
|
|
int i;
|
|
int emptyCount = 0;
|
|
|
|
for (i = 0; i < fieldCount; i++)
|
|
{
|
|
void *pOutputData = ((char *)pBaseData + pFields[i].fieldOffset);
|
|
if (DataEmpty((const char *)pOutputData, pFields[i].fieldSize * m_Sizes[pFields[i].fieldType]))
|
|
emptyCount++;
|
|
}
|
|
|
|
int entityArray[MAX_ENTITY_ARRAY];
|
|
int actualCount = fieldCount - emptyCount;
|
|
|
|
WriteInt(pname, &actualCount, 1);
|
|
for (i = 0; i < fieldCount; i++)
|
|
{
|
|
TYPEDESCRIPTION *pTest = &pFields[i];
|
|
void *pOutputData = ((char *)pBaseData + pTest->fieldOffset);
|
|
|
|
if (DataEmpty((const char *)pOutputData, pTest->fieldSize * m_Sizes[pTest->fieldType]))
|
|
continue;
|
|
|
|
switch (pTest->fieldType)
|
|
{
|
|
case FIELD_FLOAT:
|
|
WriteFloat(pTest->fieldName, (float *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
|
|
case FIELD_TIME:
|
|
WriteTime(pTest->fieldName, (float *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
case FIELD_STRING:
|
|
WriteString(pTest->fieldName, (int *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
|
|
case FIELD_CLASSPTR:
|
|
case FIELD_EVARS:
|
|
case FIELD_EDICT:
|
|
case FIELD_ENTITY:
|
|
case FIELD_EHANDLE:
|
|
{
|
|
if (pTest->fieldSize > MAX_ENTITY_ARRAY)
|
|
ALERT(at_error, "Can't save more than %d entities in an array!!!\n", MAX_ENTITY_ARRAY);
|
|
|
|
for (int j = 0; j < pTest->fieldSize; j++)
|
|
{
|
|
switch (pTest->fieldType)
|
|
{
|
|
case FIELD_EVARS:
|
|
entityArray[j] = EntityIndex(((entvars_t **)pOutputData)[j]);
|
|
break;
|
|
case FIELD_CLASSPTR:
|
|
entityArray[j] = EntityIndex(((CBaseEntity **)pOutputData)[j]);
|
|
break;
|
|
case FIELD_EDICT:
|
|
entityArray[j] = EntityIndex(((edict_t **)pOutputData)[j]);
|
|
break;
|
|
case FIELD_ENTITY:
|
|
entityArray[j] = EntityIndex(((EOFFSET *)pOutputData)[j]);
|
|
break;
|
|
case FIELD_EHANDLE:
|
|
entityArray[j] = EntityIndex((CBaseEntity *)(((EHANDLE *)pOutputData)[j]));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
WriteInt(pTest->fieldName, entityArray, pTest->fieldSize);
|
|
break;
|
|
}
|
|
case FIELD_POSITION_VECTOR:
|
|
WritePositionVector(pTest->fieldName, (float *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
case FIELD_VECTOR:
|
|
WriteVector(pTest->fieldName, (float *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
case FIELD_BOOLEAN:
|
|
case FIELD_INTEGER:
|
|
WriteInt(pTest->fieldName, (int *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
case FIELD_SHORT:
|
|
WriteData(pTest->fieldName, 2 * pTest->fieldSize, ((char *)pOutputData));
|
|
break;
|
|
case FIELD_CHARACTER:
|
|
WriteData(pTest->fieldName, pTest->fieldSize, ((char *)pOutputData));
|
|
break;
|
|
case FIELD_POINTER:
|
|
WriteInt(pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize);
|
|
break;
|
|
case FIELD_FUNCTION:
|
|
WriteFunction(pTest->fieldName, &pOutputData, pTest->fieldSize);
|
|
break;
|
|
default:
|
|
ALERT(at_error, "Bad field type\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
NOXREF void CSave::BufferString(char *pdata, int len)
|
|
{
|
|
char c = 0;
|
|
BufferData(pdata, len);
|
|
BufferData(&c, 1);
|
|
}
|
|
|
|
int CSave::DataEmpty(const char *pdata, int size)
|
|
{
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
if (pdata[i])
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CSave::BufferField(const char *pname, int size, const char *pdata)
|
|
{
|
|
BufferHeader(pname, size);
|
|
BufferData(pdata, size);
|
|
}
|
|
|
|
void CSave::BufferHeader(const char *pname, int size)
|
|
{
|
|
short hashvalue = TokenHash(pname);
|
|
if (size > (1 << (sizeof(short) * 8)))
|
|
ALERT(at_error, "CSave :: BufferHeader() size parameter exceeds 'short'!");
|
|
|
|
BufferData((const char *)&size, sizeof(short));
|
|
BufferData((const char *)&hashvalue, sizeof(short));
|
|
}
|
|
|
|
void CSave::BufferData(const char *pdata, int size)
|
|
{
|
|
if (!m_pData)
|
|
return;
|
|
|
|
if (m_pData->size + size > m_pData->bufferSize)
|
|
{
|
|
ALERT(at_error, "Save/Restore overflow!");
|
|
m_pData->size = m_pData->bufferSize;
|
|
return;
|
|
}
|
|
|
|
Q_memcpy(m_pData->pCurrentData, pdata, size);
|
|
m_pData->pCurrentData += size;
|
|
m_pData->size += size;
|
|
}
|
|
|
|
int CRestore::ReadField(void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData)
|
|
{
|
|
float time = 0.0f;
|
|
Vector position(0, 0, 0);
|
|
|
|
if (m_pData)
|
|
{
|
|
time = m_pData->time;
|
|
if (m_pData->fUseLandmark) {
|
|
position = m_pData->vecLandmarkOffset;
|
|
}
|
|
}
|
|
for (int i = 0; i < fieldCount; i++)
|
|
{
|
|
int fieldNumber = (i + startField) % fieldCount;
|
|
TYPEDESCRIPTION *pTest = &pFields[fieldNumber];
|
|
|
|
if (!Q_stricmp(pTest->fieldName, pName))
|
|
{
|
|
if (!m_global || !(pTest->flags & FTYPEDESC_GLOBAL))
|
|
{
|
|
for (int j = 0; j < pTest->fieldSize; j++)
|
|
{
|
|
void *pOutputData = ((char *)pBaseData + pTest->fieldOffset + (j * m_Sizes[pTest->fieldType]));
|
|
void *pInputData = (char *)pData + j * m_Sizes[pTest->fieldType];
|
|
|
|
switch (pTest->fieldType)
|
|
{
|
|
case FIELD_TIME:
|
|
{
|
|
float timeData = *(float *)pInputData;
|
|
timeData += time;
|
|
*((float *)pOutputData) = timeData;
|
|
break;
|
|
}
|
|
case FIELD_FLOAT: *((float *)pOutputData) = *(float *)pInputData; break;
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
case FIELD_STRING:
|
|
{
|
|
char *pString = (char *)pData;
|
|
for (int stringCount = 0; stringCount < j; stringCount++)
|
|
{
|
|
while (*pString)
|
|
pString++;
|
|
|
|
pString++;
|
|
}
|
|
|
|
pInputData = pString;
|
|
if (!Q_strlen((char *)pInputData))
|
|
*((int *)pOutputData) = 0;
|
|
else
|
|
{
|
|
int string = ALLOC_STRING((char *)pInputData);
|
|
*((int *)pOutputData) = string;
|
|
|
|
if (!FStringNull(string) && m_precache)
|
|
{
|
|
if (pTest->fieldType == FIELD_MODELNAME)
|
|
PRECACHE_MODEL((char *)STRING(string));
|
|
else if (pTest->fieldType == FIELD_SOUNDNAME)
|
|
PRECACHE_SOUND((char *)STRING(string));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case FIELD_EVARS:
|
|
{
|
|
int entityIndex = *(int *)pInputData;
|
|
edict_t *pent = EntityFromIndex(entityIndex);
|
|
|
|
if (pent)
|
|
*((entvars_t **)pOutputData) = VARS(pent);
|
|
else
|
|
*((entvars_t **)pOutputData) = nullptr;
|
|
|
|
break;
|
|
}
|
|
case FIELD_CLASSPTR:
|
|
{
|
|
int entityIndex = *(int *)pInputData;
|
|
edict_t *pent = EntityFromIndex(entityIndex);
|
|
|
|
if (pent)
|
|
*((CBaseEntity **)pOutputData) = CBaseEntity::Instance(pent);
|
|
else
|
|
*((CBaseEntity **)pOutputData) = nullptr;
|
|
|
|
break;
|
|
}
|
|
case FIELD_EDICT:
|
|
{
|
|
int entityIndex = *(int *)pInputData;
|
|
edict_t *pent = EntityFromIndex(entityIndex);
|
|
*((edict_t **)pOutputData) = pent;
|
|
break;
|
|
}
|
|
case FIELD_EHANDLE:
|
|
{
|
|
pOutputData = (char *)pOutputData + j * (sizeof(EHANDLE) - m_Sizes[pTest->fieldType]);
|
|
int entityIndex = *(int *)pInputData;
|
|
edict_t *pent = EntityFromIndex(entityIndex);
|
|
|
|
if (pent)
|
|
*((EHANDLE *)pOutputData) = CBaseEntity::Instance(pent);
|
|
else
|
|
*((EHANDLE *)pOutputData) = nullptr;
|
|
|
|
break;
|
|
}
|
|
case FIELD_ENTITY:
|
|
{
|
|
int entityIndex = *(int *)pInputData;
|
|
edict_t *pent = EntityFromIndex(entityIndex);
|
|
|
|
if (pent)
|
|
*((EOFFSET *)pOutputData) = OFFSET(pent);
|
|
else
|
|
*((EOFFSET *)pOutputData) = 0;
|
|
|
|
break;
|
|
}
|
|
case FIELD_VECTOR:
|
|
{
|
|
((float *)pOutputData)[0] = ((float *)pInputData)[0];
|
|
((float *)pOutputData)[1] = ((float *)pInputData)[1];
|
|
((float *)pOutputData)[2] = ((float *)pInputData)[2];
|
|
break;
|
|
}
|
|
case FIELD_POSITION_VECTOR:
|
|
{
|
|
((float *)pOutputData)[0] = ((float *)pInputData)[0] + position.x;
|
|
((float *)pOutputData)[1] = ((float *)pInputData)[1] + position.y;
|
|
((float *)pOutputData)[2] = ((float *)pInputData)[2] + position.z;
|
|
break;
|
|
}
|
|
case FIELD_BOOLEAN:
|
|
case FIELD_INTEGER:
|
|
*((int *)pOutputData) = *(int *)pInputData;
|
|
break;
|
|
case FIELD_SHORT:
|
|
*((short *)pOutputData) = *(short *)pInputData;
|
|
break;
|
|
case FIELD_CHARACTER:
|
|
*((char *)pOutputData) = *(char *)pInputData;
|
|
break;
|
|
case FIELD_POINTER:
|
|
*((int *)pOutputData) = *(int *)pInputData;
|
|
break;
|
|
case FIELD_FUNCTION:
|
|
{
|
|
if (!Q_strlen((char *)pInputData))
|
|
*((int *)pOutputData) = 0;
|
|
else
|
|
*((int *)pOutputData) = FUNCTION_FROM_NAME((char *)pInputData);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
ALERT(at_error, "Bad field type\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return fieldNumber;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int CRestore::ReadEntVars(const char *pname, entvars_t *pev)
|
|
{
|
|
return ReadFields(pname, pev, g_EntvarsDescription, ARRAYSIZE(g_EntvarsDescription));
|
|
}
|
|
|
|
int CRestore::ReadFields(const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount)
|
|
{
|
|
unsigned short i = ReadShort();
|
|
unsigned short token = ReadShort();
|
|
|
|
if (token != TokenHash(pname))
|
|
{
|
|
BufferRewind(2 * sizeof(short));
|
|
return 0;
|
|
}
|
|
|
|
int fileCount = ReadInt();
|
|
int lastField = 0;
|
|
|
|
for (i = 0; i < fieldCount; i++)
|
|
{
|
|
if (!m_global || !(pFields[i].flags & FTYPEDESC_GLOBAL))
|
|
Q_memset(((char *)pBaseData + pFields[i].fieldOffset), 0, pFields[i].fieldSize * m_Sizes[pFields[i].fieldType]);
|
|
}
|
|
|
|
for (i = 0; i < fileCount; i++)
|
|
{
|
|
HEADER header;
|
|
BufferReadHeader(&header);
|
|
|
|
lastField = ReadField(pBaseData, pFields, fieldCount, lastField, header.size, m_pData->pTokens[header.token], header.pData);
|
|
lastField++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CRestore::BufferReadHeader(HEADER *pheader)
|
|
{
|
|
pheader->size = ReadShort();
|
|
pheader->token = ReadShort();
|
|
pheader->pData = BufferPointer();
|
|
|
|
BufferSkipBytes(pheader->size);
|
|
}
|
|
|
|
short CRestore::ReadShort()
|
|
{
|
|
short tmp = 0;
|
|
BufferReadBytes((char *)&tmp, sizeof(short));
|
|
return tmp;
|
|
}
|
|
|
|
int CRestore::ReadInt()
|
|
{
|
|
int tmp = 0;
|
|
BufferReadBytes((char *)&tmp, sizeof(int));
|
|
return tmp;
|
|
}
|
|
|
|
NOXREF int CRestore::ReadNamedInt(const char *pName)
|
|
{
|
|
HEADER header;
|
|
BufferReadHeader(&header);
|
|
return ((int *)header.pData)[0];
|
|
}
|
|
|
|
NOXREF char *CRestore::ReadNamedString(const char *pName)
|
|
{
|
|
HEADER header;
|
|
BufferReadHeader(&header);
|
|
return (char *)header.pData;
|
|
}
|
|
|
|
char *CRestore::BufferPointer()
|
|
{
|
|
if (!m_pData)
|
|
return nullptr;
|
|
|
|
return m_pData->pCurrentData;
|
|
}
|
|
|
|
void CRestore::BufferReadBytes(char *pOutput, int size)
|
|
{
|
|
if (!m_pData || Empty())
|
|
return;
|
|
|
|
if ((m_pData->size + size) > m_pData->bufferSize)
|
|
{
|
|
ALERT(at_error, "Restore overflow!");
|
|
m_pData->size = m_pData->bufferSize;
|
|
return;
|
|
}
|
|
|
|
if (pOutput)
|
|
Q_memcpy(pOutput, m_pData->pCurrentData, size);
|
|
|
|
m_pData->pCurrentData += size;
|
|
m_pData->size += size;
|
|
}
|
|
|
|
void CRestore::BufferSkipBytes(int bytes)
|
|
{
|
|
BufferReadBytes(nullptr, bytes);
|
|
}
|
|
|
|
NOXREF int CRestore::BufferSkipZString()
|
|
{
|
|
if (!m_pData)
|
|
return 0;
|
|
|
|
int maxLen = m_pData->bufferSize - m_pData->size;
|
|
int len = 0;
|
|
char *pszSearch = m_pData->pCurrentData;
|
|
|
|
while (*pszSearch++ && len < maxLen)
|
|
len++;
|
|
|
|
len++;
|
|
BufferSkipBytes(len);
|
|
return len;
|
|
}
|
|
|
|
NOXREF int CRestore::BufferCheckZString(const char *string)
|
|
{
|
|
if (!m_pData)
|
|
return 0;
|
|
|
|
int maxLen = m_pData->bufferSize - m_pData->size;
|
|
int len = Q_strlen(string);
|
|
|
|
if (len <= maxLen)
|
|
{
|
|
if (!Q_strncmp(string, m_pData->pCurrentData, len))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CGlobalState::CGlobalState()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CGlobalState::Reset()
|
|
{
|
|
m_pList = nullptr;
|
|
m_listCount = 0;
|
|
}
|
|
|
|
globalentity_t *CGlobalState::Find(string_t globalname)
|
|
{
|
|
if (!globalname)
|
|
return nullptr;
|
|
|
|
globalentity_t *pTest = m_pList;
|
|
const char *pEntityName = STRING(globalname);
|
|
|
|
while (pTest)
|
|
{
|
|
if (!Q_strcmp(pEntityName, pTest->name))
|
|
break;
|
|
|
|
pTest = pTest->pNext;
|
|
}
|
|
|
|
return pTest;
|
|
}
|
|
|
|
// This is available all the time now on impulse 104, remove later
|
|
void CGlobalState::DumpGlobals()
|
|
{
|
|
static char *estates[] = { "Off", "On", "Dead" };
|
|
globalentity_t *pTest;
|
|
|
|
ALERT(at_console, "-- Globals --\n");
|
|
pTest = m_pList;
|
|
|
|
while (pTest)
|
|
{
|
|
ALERT(at_console, "%s: %s (%s)\n", pTest->name, pTest->levelName, estates[pTest->state]);
|
|
pTest = pTest->pNext;
|
|
}
|
|
}
|
|
|
|
void CGlobalState::EntityAdd(string_t globalname, string_t mapName, GLOBALESTATE state)
|
|
{
|
|
DbgAssert(!Find(globalname));
|
|
|
|
globalentity_t *pNewEntity = (globalentity_t *)calloc(sizeof(globalentity_t), 1);
|
|
DbgAssert(pNewEntity != nullptr);
|
|
|
|
pNewEntity->pNext = m_pList;
|
|
m_pList = pNewEntity;
|
|
Q_strcpy(pNewEntity->name, STRING(globalname));
|
|
Q_strcpy(pNewEntity->levelName, STRING(mapName));
|
|
pNewEntity->state = state;
|
|
|
|
m_listCount++;
|
|
}
|
|
|
|
void CGlobalState::EntitySetState(string_t globalname, GLOBALESTATE state)
|
|
{
|
|
globalentity_t *pEnt = Find(globalname);
|
|
if (pEnt)
|
|
{
|
|
pEnt->state = state;
|
|
}
|
|
}
|
|
|
|
const globalentity_t *CGlobalState::EntityFromTable(string_t globalname)
|
|
{
|
|
globalentity_t *pEnt = Find(globalname);
|
|
|
|
return pEnt;
|
|
}
|
|
|
|
GLOBALESTATE CGlobalState::EntityGetState(string_t globalname)
|
|
{
|
|
globalentity_t *pEnt = Find(globalname);
|
|
if (pEnt)
|
|
{
|
|
return pEnt->state;
|
|
}
|
|
|
|
return GLOBAL_OFF;
|
|
}
|
|
|
|
TYPEDESCRIPTION CGlobalState::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD(CGlobalState, m_listCount, FIELD_INTEGER)
|
|
};
|
|
|
|
TYPEDESCRIPTION CGlobalState::m_GlobalEntitySaveData[] =
|
|
{
|
|
DEFINE_ARRAY(globalentity_t, name, FIELD_CHARACTER, 64),
|
|
DEFINE_ARRAY(globalentity_t, levelName, FIELD_CHARACTER, MAX_MAPNAME_LENGHT),
|
|
DEFINE_FIELD(globalentity_t, state, FIELD_INTEGER)
|
|
};
|
|
|
|
int CGlobalState::Save(CSave &save)
|
|
{
|
|
int i;
|
|
globalentity_t *pEntity;
|
|
|
|
if (!save.WriteFields("GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
pEntity = m_pList;
|
|
for (i = 0; i < m_listCount && pEntity; i++)
|
|
{
|
|
if (!save.WriteFields("GENT", pEntity, m_GlobalEntitySaveData, ARRAYSIZE(m_GlobalEntitySaveData)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
pEntity = pEntity->pNext;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CGlobalState::Restore(CRestore &restore)
|
|
{
|
|
int i, listCount;
|
|
globalentity_t tmpEntity;
|
|
|
|
ClearStates();
|
|
|
|
if (!restore.ReadFields("GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Get new list count
|
|
listCount = m_listCount;
|
|
|
|
// Clear loaded data
|
|
m_listCount = 0;
|
|
|
|
for (i = 0; i < listCount; i++)
|
|
{
|
|
if (!restore.ReadFields("GENT", &tmpEntity, m_GlobalEntitySaveData, ARRAYSIZE(m_GlobalEntitySaveData)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
EntityAdd(MAKE_STRING(tmpEntity.name), MAKE_STRING(tmpEntity.levelName), tmpEntity.state);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CGlobalState::EntityUpdate(string_t globalname, string_t mapname)
|
|
{
|
|
globalentity_t *pEnt = Find(globalname);
|
|
if (pEnt)
|
|
{
|
|
Q_strcpy(pEnt->levelName, STRING(mapname));
|
|
}
|
|
}
|
|
|
|
void CGlobalState::ClearStates()
|
|
{
|
|
globalentity_t *pFree = m_pList;
|
|
while (pFree)
|
|
{
|
|
globalentity_t *pNext = pFree->pNext;
|
|
|
|
free(pFree);
|
|
pFree = pNext;
|
|
}
|
|
|
|
Reset();
|
|
}
|
|
|
|
void EXT_FUNC SaveGlobalState(SAVERESTOREDATA *pSaveData)
|
|
{
|
|
CSave saveHelper(pSaveData);
|
|
gGlobalState.Save(saveHelper);
|
|
}
|
|
|
|
void EXT_FUNC RestoreGlobalState(SAVERESTOREDATA *pSaveData)
|
|
{
|
|
CRestore restoreHelper(pSaveData);
|
|
gGlobalState.Restore(restoreHelper);
|
|
}
|
|
|
|
void EXT_FUNC ResetGlobalState()
|
|
{
|
|
gGlobalState.ClearStates();
|
|
|
|
// Init the HUD on a new game / load game
|
|
gInitHUD = TRUE;
|
|
}
|