2010-11-20 10:37:14 +00:00
|
|
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
2010-05-29 07:25:38 +00:00
|
|
|
#include "snapshot.h"
|
|
|
|
#include "compression.h"
|
2017-05-21 23:07:13 +00:00
|
|
|
#include "uuid_manager.h"
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2022-02-11 15:59:27 +00:00
|
|
|
#include <climits>
|
2022-06-16 15:23:36 +00:00
|
|
|
#include <cstdlib>
|
2022-02-11 15:59:27 +00:00
|
|
|
|
2022-12-30 12:57:57 +00:00
|
|
|
#include <base/math.h>
|
2022-05-29 12:03:16 +00:00
|
|
|
#include <base/system.h>
|
2022-12-30 12:57:57 +00:00
|
|
|
|
2020-06-12 19:22:54 +00:00
|
|
|
#include <game/generated/protocolglue.h>
|
2020-03-29 02:36:38 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// CSnapshot
|
|
|
|
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *CSnapshot::GetItem(int Index) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2022-10-09 10:59:30 +00:00
|
|
|
return (const CSnapshotItem *)(DataStart() + Offsets()[Index]);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2023-09-26 18:03:45 +00:00
|
|
|
const CSnapshot CSnapshot::ms_EmptySnapshot;
|
|
|
|
|
2021-02-08 21:26:26 +00:00
|
|
|
int CSnapshot::GetItemSize(int Index) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(Index == m_NumItems - 1)
|
2011-04-13 18:37:12 +00:00
|
|
|
return (m_DataSize - Offsets()[Index]) - sizeof(CSnapshotItem);
|
2020-09-26 19:41:58 +00:00
|
|
|
return (Offsets()[Index + 1] - Offsets()[Index]) - sizeof(CSnapshotItem);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2021-02-08 21:26:26 +00:00
|
|
|
int CSnapshot::GetItemType(int Index) const
|
2017-05-21 23:07:13 +00:00
|
|
|
{
|
|
|
|
int InternalType = GetItem(Index)->Type();
|
2022-06-21 15:34:56 +00:00
|
|
|
return GetExternalItemType(InternalType);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSnapshot::GetExternalItemType(int InternalType) const
|
|
|
|
{
|
2017-05-21 23:07:13 +00:00
|
|
|
if(InternalType < OFFSET_UUID_TYPE)
|
|
|
|
{
|
|
|
|
return InternalType;
|
|
|
|
}
|
|
|
|
|
2022-10-30 13:10:52 +00:00
|
|
|
int TypeItemIndex = GetItemIndex(InternalType); // NETOBJTYPE_EX
|
2017-05-21 23:07:13 +00:00
|
|
|
if(TypeItemIndex == -1 || GetItemSize(TypeItemIndex) < (int)sizeof(CUuid))
|
|
|
|
{
|
|
|
|
return InternalType;
|
|
|
|
}
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pTypeItem = GetItem(TypeItemIndex);
|
2017-05-21 23:07:13 +00:00
|
|
|
CUuid Uuid;
|
2023-02-04 00:22:49 +00:00
|
|
|
for(size_t i = 0; i < sizeof(CUuid) / sizeof(int32_t); i++)
|
|
|
|
uint_to_bytes_be(&Uuid.m_aData[i * sizeof(int32_t)], pTypeItem->Data()[i]);
|
2017-05-21 23:07:13 +00:00
|
|
|
|
|
|
|
return g_UuidManager.LookupUuid(Uuid);
|
|
|
|
}
|
|
|
|
|
2021-02-08 21:26:26 +00:00
|
|
|
int CSnapshot::GetItemIndex(int Key) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-04-13 18:37:12 +00:00
|
|
|
// TODO: OPT: this should not be a linear search. very bad
|
|
|
|
for(int i = 0; i < m_NumItems; i++)
|
|
|
|
{
|
|
|
|
if(GetItem(i)->Key() == Key)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 10:59:30 +00:00
|
|
|
const void *CSnapshot::FindItem(int Type, int ID) const
|
2021-11-07 00:02:20 +00:00
|
|
|
{
|
|
|
|
int InternalType = Type;
|
|
|
|
if(Type >= OFFSET_UUID)
|
|
|
|
{
|
|
|
|
CUuid TypeUuid = g_UuidManager.GetUuid(Type);
|
2023-02-04 00:22:49 +00:00
|
|
|
int aTypeUuidItem[sizeof(CUuid) / sizeof(int32_t)];
|
|
|
|
for(size_t i = 0; i < sizeof(CUuid) / sizeof(int32_t); i++)
|
|
|
|
aTypeUuidItem[i] = bytes_be_to_uint(&TypeUuid.m_aData[i * sizeof(int32_t)]);
|
2021-11-08 19:21:02 +00:00
|
|
|
|
2021-11-07 00:02:20 +00:00
|
|
|
bool Found = false;
|
|
|
|
for(int i = 0; i < m_NumItems; i++)
|
|
|
|
{
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pItem = GetItem(i);
|
2021-11-07 00:02:20 +00:00
|
|
|
if(pItem->Type() == 0 && pItem->ID() >= OFFSET_UUID_TYPE) // NETOBJTYPE_EX
|
|
|
|
{
|
|
|
|
if(mem_comp(pItem->Data(), aTypeUuidItem, sizeof(CUuid)) == 0)
|
|
|
|
{
|
|
|
|
InternalType = pItem->ID();
|
|
|
|
Found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!Found)
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int Index = GetItemIndex((InternalType << 16) | ID);
|
|
|
|
return Index < 0 ? nullptr : GetItem(Index)->Data();
|
|
|
|
}
|
|
|
|
|
2020-10-10 10:30:02 +00:00
|
|
|
unsigned CSnapshot::Crc()
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-10-10 10:30:02 +00:00
|
|
|
unsigned int Crc = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int i = 0; i < m_NumItems; i++)
|
|
|
|
{
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pItem = GetItem(i);
|
2010-05-29 07:25:38 +00:00
|
|
|
int Size = GetItemSize(i);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2023-02-04 00:22:49 +00:00
|
|
|
for(size_t b = 0; b < Size / sizeof(int32_t); b++)
|
2010-05-29 07:25:38 +00:00
|
|
|
Crc += pItem->Data()[b];
|
|
|
|
}
|
|
|
|
return Crc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSnapshot::DebugDump()
|
|
|
|
{
|
|
|
|
dbg_msg("snapshot", "data_size=%d num_items=%d", m_DataSize, m_NumItems);
|
|
|
|
for(int i = 0; i < m_NumItems; i++)
|
|
|
|
{
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pItem = GetItem(i);
|
2010-05-29 07:25:38 +00:00
|
|
|
int Size = GetItemSize(i);
|
|
|
|
dbg_msg("snapshot", "\ttype=%d id=%d", pItem->Type(), pItem->ID());
|
2023-02-04 00:22:49 +00:00
|
|
|
for(size_t b = 0; b < Size / sizeof(int32_t); b++)
|
|
|
|
dbg_msg("snapshot", "\t\t%3d %12d\t%08x", (int)b, pItem->Data()[b], pItem->Data()[b]);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Validate snapshot size and member variables and demo snapshots
Add `CSnapshot::IsValid` to check if a snapshot unpacked from a snapshot delta or demo is valid:
- ensure number of items and data size are not negative
- ensure that the actual size of the snapshot matches the size derived from its member variables
- ensure item offsets are within the valid range
- ensure item sizes are not negative
Add `CSnapshot::TotalSize` and `CSnapshot::OffsetSize` utility functions.
Minor improvements to related error messages.
Fixes buffer overflow:
```
==47744==ERROR: AddressSanitizer: global-buffer-overflow on address 0x558618e3767f at pc 0x558614b9bdfb bp 0x7ffe58a32cd0 sp 0x7ffe58a32cc0
READ of size 4 at 0x558618e3767f thread T0
0x558614b9bdfa in CSnapshotItem::Type() const src/engine/shared/snapshot.h:16
0x558615c3c911 in CSnapshot::GetItemType(int) const src/engine/shared/snapshot.cpp:29
0x558614aebaba in CClient::UnpackAndValidateSnapshot(CSnapshot*, CSnapshot*) src/engine/client/client.cpp:2264
0x558614af87cb in CClient::OnDemoPlayerSnapshot(void*, int) src/engine/client/client.cpp:2598
0x558615b9db1a in CDemoPlayer::DoTick() src/engine/shared/demo.cpp:659
0x558615babd3f in CDemoPlayer::Update(bool) src/engine/shared/demo.cpp:1007
0x558614afb08b in CClient::Update() src/engine/client/client.cpp:2686
0x558614b1d9eb in CClient::Run() src/engine/client/client.cpp:3296
0x558614b8e64f in main src/engine/client/client.cpp:4761
```
And fixes a buffer overflow that manifests itself as an internal ASan error:
```
=================================================================
==4755==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_descriptions.cc:79 "((0 && "Address is not in memory and not in shadow?")) != (0)" (0x0, 0x0)
0x7f0bf5f368be in AsanCheckFailed ../../../../src/libsanitizer/asan/asan_rtl.cc:72
0x7f0bf5f54eee in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_termination.cc:77
0x7f0bf5e4cb6f in GetShadowKind ../../../../src/libsanitizer/asan/asan_descriptions.cc:79
0x7f0bf5e4cb6f in __asan::GetShadowAddressInformation(unsigned long, __asan::ShadowAddressDescription*) ../../../../src/libsanitizer/asan/asan_descriptions.cc:95
0x7f0bf5e4cb6f in __asan::GetShadowAddressInformation(unsigned long, __asan::ShadowAddressDescription*) ../../../../src/libsanitizer/asan/asan_descriptions.cc:92
0x7f0bf5e4e386 in __asan::AddressDescription::AddressDescription(unsigned long, unsigned long, bool) ../../../../src/libsanitizer/asan/asan_descriptions.cc:440
0x7f0bf5e50e94 in __asan::ErrorGeneric::ErrorGeneric(unsigned int, unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long) ../../../../src/libsanitizer/asan/asan_errors.cc:380
0x7f0bf5f35f4d in __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) ../../../../src/libsanitizer/asan/asan_report.cc:460
0x7f0bf5e86f5e in __interceptor_memset ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:762
0x558234873f1d in mem_zero src/base/system.cpp:213
0x55823481fc27 in CSnapshotBuilder::NewItem(int, int, int) src/engine/shared/snapshot.cpp:675
0x55823481be65 in CSnapshotDelta::UnpackDelta(CSnapshot*, CSnapshot*, void const*, int) src/engine/shared/snapshot.cpp:380
0x558234776641 in CDemoPlayer::DoTick() src/engine/shared/demo.cpp:631
0x5582347861a9 in CDemoPlayer::Update(bool) src/engine/shared/demo.cpp:1007
0x5582336d4c7d in CClient::Update() src/engine/client/client.cpp:2695
0x5582336f75dd in CClient::Run() src/engine/client/client.cpp:3305
0x558233768241 in main src/engine/client/client.cpp:4770
```
2022-07-22 22:01:46 +00:00
|
|
|
bool CSnapshot::IsValid(size_t ActualSize) const
|
|
|
|
{
|
|
|
|
// validate total size
|
|
|
|
if(ActualSize < sizeof(CSnapshot) || m_NumItems < 0 || m_DataSize < 0 || ActualSize != TotalSize())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// validate item offsets
|
|
|
|
const int *pOffsets = Offsets();
|
|
|
|
for(int Index = 0; Index < m_NumItems; Index++)
|
|
|
|
if(pOffsets[Index] < 0 || pOffsets[Index] > m_DataSize)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// validate item sizes
|
|
|
|
for(int Index = 0; Index < m_NumItems; Index++)
|
|
|
|
if(GetItemSize(Index) < 0) // the offsets must be validated before using this
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// CSnapshotDelta
|
|
|
|
|
2022-08-08 20:31:48 +00:00
|
|
|
enum
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2022-08-08 20:31:48 +00:00
|
|
|
HASHLIST_SIZE = 256,
|
|
|
|
HASHLIST_BUCKET_SIZE = 64,
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
|
2022-08-08 20:31:48 +00:00
|
|
|
struct CItemList
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2022-08-08 20:31:48 +00:00
|
|
|
int m_Num;
|
|
|
|
int m_aKeys[HASHLIST_BUCKET_SIZE];
|
|
|
|
int m_aIndex[HASHLIST_BUCKET_SIZE];
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
|
2022-08-08 20:30:14 +00:00
|
|
|
inline size_t CalcHashID(int Key)
|
|
|
|
{
|
2022-08-08 21:21:45 +00:00
|
|
|
// djb2 (http://www.cse.yorku.ca/~oz/hash.html)
|
|
|
|
unsigned Hash = 5381;
|
|
|
|
for(unsigned Shift = 0; Shift < sizeof(int); Shift++)
|
|
|
|
Hash = ((Hash << 5) + Hash) + ((Key >> (Shift * 8)) & 0xFF);
|
|
|
|
return Hash % HASHLIST_SIZE;
|
2022-08-08 20:30:14 +00:00
|
|
|
}
|
|
|
|
|
2023-09-26 18:03:45 +00:00
|
|
|
static void GenerateHash(CItemList *pHashlist, const CSnapshot *pSnapshot)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
for(int i = 0; i < HASHLIST_SIZE; i++)
|
|
|
|
pHashlist[i].m_Num = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int i = 0; i < pSnapshot->NumItems(); i++)
|
|
|
|
{
|
|
|
|
int Key = pSnapshot->GetItem(i)->Key();
|
2022-08-08 20:30:14 +00:00
|
|
|
size_t HashID = CalcHashID(Key);
|
2022-08-08 20:31:48 +00:00
|
|
|
if(pHashlist[HashID].m_Num < HASHLIST_BUCKET_SIZE)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
pHashlist[HashID].m_aIndex[pHashlist[HashID].m_Num] = i;
|
|
|
|
pHashlist[HashID].m_aKeys[pHashlist[HashID].m_Num] = Key;
|
|
|
|
pHashlist[HashID].m_Num++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int GetItemIndexHashed(int Key, const CItemList *pHashlist)
|
|
|
|
{
|
2022-08-08 20:30:14 +00:00
|
|
|
size_t HashID = CalcHashID(Key);
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int i = 0; i < pHashlist[HashID].m_Num; i++)
|
|
|
|
{
|
|
|
|
if(pHashlist[HashID].m_aKeys[i] == Key)
|
|
|
|
return pHashlist[HashID].m_aIndex[i];
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-10-09 10:59:30 +00:00
|
|
|
int CSnapshotDelta::DiffItem(const int *pPast, const int *pCurrent, int *pOut, int Size)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
int Needed = 0;
|
|
|
|
while(Size)
|
|
|
|
{
|
Fix undefined behavior in `CSnapshotDelta::DiffItem`
Cast `int`s to `unsigned` before subtracting to ensure that integer wrapping is being used instead of causing undefined behavior. Same as in `UndiffItem`.
```
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/master/src/base/math.h:16:40 in
src/master/src/engine/shared/snapshot.cpp:206:21: runtime error: signed integer overflow: 256 - -2147483648 cannot be represented in type 'int'
0 0x7650b7 in CSnapshotDelta::DiffItem(int const*, int const*, int*, int) src/master/src/engine/shared/snapshot.cpp:206:21
1 0x765cea in CSnapshotDelta::CreateDelta(CSnapshot*, CSnapshot*, void*) src/master/src/engine/shared/snapshot.cpp:323:7
2 0x51a0e2 in CServer::DoSnapshot() src/master/src/engine/server/server.cpp:964:36
3 0x537486 in CServer::Run() src/master/src/engine/server/server.cpp:2818:6
4 0x4feeb7 in main src/master/src/engine/server/main.cpp:190:21
5 0x7fc51ec27d09 in __libc_start_main csu/../csu/libc-start.c:308:16
6 0x4c3819 in _start (servers/DDNet-Server-ubsan+0x4c3819)
src/master/src/engine/shared/snapshot.cpp:206:21: runtime error: signed integer overflow: 1645289600 - -2139062144 cannot be represented in type 'int'
0 0x7650b7 in CSnapshotDelta::DiffItem(int const*, int const*, int*, int) src/master/src/engine/shared/snapshot.cpp:206:21
1 0x765cea in CSnapshotDelta::CreateDelta(CSnapshot*, CSnapshot*, void*) src/master/src/engine/shared/snapshot.cpp:323:7
2 0x51a0e2 in CServer::DoSnapshot() src/master/src/engine/server/server.cpp:964:36
3 0x537486 in CServer::Run() src/master/src/engine/server/server.cpp:2818:6
4 0x4feeb7 in main src/master/src/engine/server/main.cpp:190:21
5 0x7efd50c4ed09 in __libc_start_main csu/../csu/libc-start.c:308:16
6 0x4c3819 in _start (servers/DDNet-Server-ubsan+0x4c3819)
```
See #6650.
2023-05-27 18:57:02 +00:00
|
|
|
// subtraction with wrapping by casting to unsigned
|
|
|
|
*pOut = (unsigned)*pCurrent - (unsigned)*pPast;
|
2010-05-29 07:25:38 +00:00
|
|
|
Needed |= *pOut;
|
|
|
|
pOut++;
|
|
|
|
pPast++;
|
|
|
|
pCurrent++;
|
|
|
|
Size--;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
return Needed;
|
|
|
|
}
|
|
|
|
|
2023-09-26 18:03:45 +00:00
|
|
|
void CSnapshotDelta::UndiffItem(const int *pPast, const int *pDiff, int *pOut, int Size, int *pDataRate)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
while(Size)
|
|
|
|
{
|
2023-01-15 12:23:59 +00:00
|
|
|
// addition with wrapping by casting to unsigned
|
|
|
|
*pOut = (unsigned)*pPast + (unsigned)*pDiff;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(*pDiff == 0)
|
2022-01-14 16:52:23 +00:00
|
|
|
*pDataRate += 1;
|
2010-05-29 07:25:38 +00:00
|
|
|
else
|
|
|
|
{
|
2021-12-27 22:35:46 +00:00
|
|
|
unsigned char aBuf[CVariableInt::MAX_BYTES_PACKED];
|
2022-03-03 22:09:36 +00:00
|
|
|
unsigned char *pEnd = CVariableInt::Pack(aBuf, *pDiff, sizeof(aBuf));
|
2022-01-14 16:52:23 +00:00
|
|
|
*pDataRate += (int)(pEnd - (unsigned char *)aBuf) * 8;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
pOut++;
|
|
|
|
pPast++;
|
|
|
|
pDiff++;
|
|
|
|
Size--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CSnapshotDelta::CSnapshotDelta()
|
|
|
|
{
|
|
|
|
mem_zero(m_aItemSizes, sizeof(m_aItemSizes));
|
|
|
|
mem_zero(m_aSnapshotDataRate, sizeof(m_aSnapshotDataRate));
|
|
|
|
mem_zero(m_aSnapshotDataUpdates, sizeof(m_aSnapshotDataUpdates));
|
|
|
|
mem_zero(&m_Empty, sizeof(m_Empty));
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
CSnapshotDelta::CSnapshotDelta(const CSnapshotDelta &Old)
|
2019-05-31 18:42:28 +00:00
|
|
|
{
|
2020-10-27 17:57:14 +00:00
|
|
|
mem_copy(m_aItemSizes, Old.m_aItemSizes, sizeof(m_aItemSizes));
|
|
|
|
mem_copy(m_aSnapshotDataRate, Old.m_aSnapshotDataRate, sizeof(m_aSnapshotDataRate));
|
|
|
|
mem_copy(m_aSnapshotDataUpdates, Old.m_aSnapshotDataUpdates, sizeof(m_aSnapshotDataUpdates));
|
2022-01-14 16:52:23 +00:00
|
|
|
mem_zero(&m_Empty, sizeof(m_Empty));
|
2019-05-31 18:42:28 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CSnapshotDelta::SetStaticsize(int ItemType, int Size)
|
|
|
|
{
|
2020-10-11 16:14:12 +00:00
|
|
|
if(ItemType < 0 || ItemType >= MAX_NETOBJSIZES)
|
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_aItemSizes[ItemType] = Size;
|
|
|
|
}
|
|
|
|
|
2022-01-14 16:52:23 +00:00
|
|
|
const CSnapshotDelta::CData *CSnapshotDelta::EmptyDelta() const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
return &m_Empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: OPT: this should be made much faster
|
2023-09-26 18:03:45 +00:00
|
|
|
int CSnapshotDelta::CreateDelta(const CSnapshot *pFrom, CSnapshot *pTo, void *pDstData)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CData *pDelta = (CData *)pDstData;
|
2020-10-27 17:57:14 +00:00
|
|
|
int *pData = (int *)pDelta->m_aData;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
pDelta->m_NumDeletedItems = 0;
|
|
|
|
pDelta->m_NumUpdateItems = 0;
|
|
|
|
pDelta->m_NumTempItems = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
CItemList aHashlist[HASHLIST_SIZE];
|
|
|
|
GenerateHash(aHashlist, pTo);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// pack deleted stuff
|
2022-01-14 16:52:23 +00:00
|
|
|
for(int i = 0; i < pFrom->NumItems(); i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
const CSnapshotItem *pFromItem = pFrom->GetItem(i);
|
2020-10-27 17:57:14 +00:00
|
|
|
if(GetItemIndexHashed(pFromItem->Key(), aHashlist) == -1)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
// deleted
|
|
|
|
pDelta->m_NumDeletedItems++;
|
|
|
|
*pData = pFromItem->Key();
|
|
|
|
pData++;
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
GenerateHash(aHashlist, pFrom);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// fetch previous indices
|
|
|
|
// we do this as a separate pass because it helps the cache
|
2022-01-14 16:52:23 +00:00
|
|
|
int aPastIndices[CSnapshot::MAX_ITEMS];
|
2012-08-17 16:32:56 +00:00
|
|
|
const int NumItems = pTo->NumItems();
|
2022-01-14 16:52:23 +00:00
|
|
|
for(int i = 0; i < NumItems; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
const CSnapshotItem *pCurItem = pTo->GetItem(i); // O(1) .. O(n)
|
2020-10-27 17:57:14 +00:00
|
|
|
aPastIndices[i] = GetItemIndexHashed(pCurItem->Key(), aHashlist); // O(n) .. O(n^n)
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-01-14 16:52:23 +00:00
|
|
|
for(int i = 0; i < NumItems; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
// do delta
|
2022-01-14 16:52:23 +00:00
|
|
|
const int ItemSize = pTo->GetItemSize(i); // O(1) .. O(n)
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pCurItem = pTo->GetItem(i); // O(1) .. O(n)
|
2022-01-14 16:52:23 +00:00
|
|
|
const int PastIndex = aPastIndices[i];
|
|
|
|
const bool IncludeSize = pCurItem->Type() >= MAX_NETOBJSIZES || !m_aItemSizes[pCurItem->Type()];
|
2020-10-11 16:14:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(PastIndex != -1)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int *pItemDataDst = pData + 3;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pPastItem = pFrom->GetItem(PastIndex);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-10-11 16:14:12 +00:00
|
|
|
if(!IncludeSize)
|
2020-09-26 19:41:58 +00:00
|
|
|
pItemDataDst = pData + 2;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2023-02-04 00:22:49 +00:00
|
|
|
if(DiffItem(pPastItem->Data(), pCurItem->Data(), pItemDataDst, ItemSize / sizeof(int32_t)))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
*pData++ = pCurItem->Type();
|
|
|
|
*pData++ = pCurItem->ID();
|
2020-10-11 16:14:12 +00:00
|
|
|
if(IncludeSize)
|
2023-02-04 00:22:49 +00:00
|
|
|
*pData++ = ItemSize / sizeof(int32_t);
|
|
|
|
pData += ItemSize / sizeof(int32_t);
|
2010-05-29 07:25:38 +00:00
|
|
|
pDelta->m_NumUpdateItems++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*pData++ = pCurItem->Type();
|
|
|
|
*pData++ = pCurItem->ID();
|
2020-10-11 16:14:12 +00:00
|
|
|
if(IncludeSize)
|
2023-02-04 00:22:49 +00:00
|
|
|
*pData++ = ItemSize / sizeof(int32_t);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_copy(pData, pCurItem->Data(), ItemSize);
|
2023-02-04 00:22:49 +00:00
|
|
|
pData += ItemSize / sizeof(int32_t);
|
2010-05-29 07:25:38 +00:00
|
|
|
pDelta->m_NumUpdateItems++;
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(!pDelta->m_NumDeletedItems && !pDelta->m_NumUpdateItems && !pDelta->m_NumTempItems)
|
|
|
|
return 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
return (int)((char *)pData - (char *)pDstData);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2023-09-26 18:03:45 +00:00
|
|
|
int CSnapshotDelta::UnpackDelta(const CSnapshot *pFrom, CSnapshot *pTo, const void *pSrcData, int DataSize)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CData *pDelta = (CData *)pSrcData;
|
2020-10-27 17:57:14 +00:00
|
|
|
int *pData = (int *)pDelta->m_aData;
|
2010-05-29 07:25:38 +00:00
|
|
|
int *pEnd = (int *)(((char *)pSrcData + DataSize));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-01-14 16:52:23 +00:00
|
|
|
CSnapshotBuilder Builder;
|
2010-05-29 07:25:38 +00:00
|
|
|
Builder.Init();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// unpack deleted stuff
|
2022-01-14 16:52:23 +00:00
|
|
|
int *pDeleted = pData;
|
2020-06-07 10:01:03 +00:00
|
|
|
if(pDelta->m_NumDeletedItems < 0)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -201;
|
2010-05-29 07:25:38 +00:00
|
|
|
pData += pDelta->m_NumDeletedItems;
|
|
|
|
if(pData > pEnd)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -101;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// copy all non deleted stuff
|
|
|
|
for(int i = 0; i < pFrom->NumItems(); i++)
|
|
|
|
{
|
2022-10-09 10:59:30 +00:00
|
|
|
const CSnapshotItem *pFromItem = pFrom->GetItem(i);
|
2022-01-14 16:52:23 +00:00
|
|
|
const int ItemSize = pFrom->GetItemSize(i);
|
|
|
|
bool Keep = true;
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int d = 0; d < pDelta->m_NumDeletedItems; d++)
|
|
|
|
{
|
|
|
|
if(pDeleted[d] == pFromItem->Key())
|
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
Keep = false;
|
2010-05-29 07:25:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(Keep)
|
|
|
|
{
|
2021-01-05 18:25:58 +00:00
|
|
|
void *pObj = Builder.NewItem(pFromItem->Type(), pFromItem->ID(), ItemSize);
|
|
|
|
if(!pObj)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -301;
|
2021-01-05 18:25:58 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// keep it
|
2021-01-05 18:25:58 +00:00
|
|
|
mem_copy(pObj, pFromItem->Data(), ItemSize);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// unpack updated stuff
|
|
|
|
for(int i = 0; i < pDelta->m_NumUpdateItems; i++)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pData + 2 > pEnd)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -102;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-01-14 16:52:23 +00:00
|
|
|
const int Type = *pData++;
|
|
|
|
if(Type < 0 || Type > CSnapshot::MAX_TYPE)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -202;
|
2022-01-14 16:52:23 +00:00
|
|
|
|
|
|
|
const int ID = *pData++;
|
|
|
|
if(ID < 0 || ID > CSnapshot::MAX_ID)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -203;
|
2022-01-14 16:52:23 +00:00
|
|
|
|
|
|
|
int ItemSize;
|
2020-10-11 16:14:12 +00:00
|
|
|
if(Type < MAX_NETOBJSIZES && m_aItemSizes[Type])
|
2010-05-29 07:25:38 +00:00
|
|
|
ItemSize = m_aItemSizes[Type];
|
|
|
|
else
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pData + 1 > pEnd)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -103;
|
2023-02-04 00:22:49 +00:00
|
|
|
if(*pData < 0 || (size_t)*pData > INT_MAX / sizeof(int32_t))
|
2023-01-08 13:30:52 +00:00
|
|
|
return -204;
|
2023-02-04 00:22:49 +00:00
|
|
|
ItemSize = (*pData++) * sizeof(int32_t);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2023-06-21 18:50:28 +00:00
|
|
|
if(ItemSize < 0 || (const char *)pEnd - (const char *)pData < ItemSize)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -205;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-01-14 16:52:23 +00:00
|
|
|
const int Key = (Type << 16) | ID;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// create the item if needed
|
2022-01-14 16:52:23 +00:00
|
|
|
int *pNewData = Builder.GetItemData(Key);
|
2010-05-29 07:25:38 +00:00
|
|
|
if(!pNewData)
|
2022-01-14 16:52:23 +00:00
|
|
|
pNewData = (int *)Builder.NewItem(Type, ID, ItemSize);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2021-01-05 18:25:58 +00:00
|
|
|
if(!pNewData)
|
2023-01-08 13:30:52 +00:00
|
|
|
return -302;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-01-14 16:52:23 +00:00
|
|
|
const int FromIndex = pFrom->GetItemIndex(Key);
|
2010-05-29 07:25:38 +00:00
|
|
|
if(FromIndex != -1)
|
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
// we got an update so we need to apply the diff
|
2023-02-04 00:22:49 +00:00
|
|
|
UndiffItem(pFrom->GetItem(FromIndex)->Data(), pData, pNewData, ItemSize / sizeof(int32_t), &m_aSnapshotDataRate[Type]);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else // no previous, just copy the pData
|
|
|
|
{
|
|
|
|
mem_copy(pNewData, pData, ItemSize);
|
2022-01-14 16:52:23 +00:00
|
|
|
m_aSnapshotDataRate[Type] += ItemSize * 8;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2022-01-14 16:52:23 +00:00
|
|
|
m_aSnapshotDataUpdates[Type]++;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2023-02-04 00:22:49 +00:00
|
|
|
pData += ItemSize / sizeof(int32_t);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// finish up
|
|
|
|
return Builder.Finish(pTo);
|
|
|
|
}
|
|
|
|
|
|
|
|
// CSnapshotStorage
|
|
|
|
|
|
|
|
void CSnapshotStorage::Init()
|
|
|
|
{
|
2022-10-09 15:57:43 +00:00
|
|
|
m_pFirst = nullptr;
|
|
|
|
m_pLast = nullptr;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSnapshotStorage::PurgeAll()
|
|
|
|
{
|
|
|
|
CHolder *pHolder = m_pFirst;
|
|
|
|
|
|
|
|
while(pHolder)
|
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
CHolder *pNext = pHolder->m_pNext;
|
2018-04-09 09:56:39 +00:00
|
|
|
free(pHolder);
|
2010-05-29 07:25:38 +00:00
|
|
|
pHolder = pNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
// no more snapshots in storage
|
2022-10-09 15:57:43 +00:00
|
|
|
m_pFirst = nullptr;
|
|
|
|
m_pLast = nullptr;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSnapshotStorage::PurgeUntil(int Tick)
|
|
|
|
{
|
|
|
|
CHolder *pHolder = m_pFirst;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
while(pHolder)
|
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
CHolder *pNext = pHolder->m_pNext;
|
2010-05-29 07:25:38 +00:00
|
|
|
if(pHolder->m_Tick >= Tick)
|
|
|
|
return; // no more to remove
|
2018-04-09 09:56:39 +00:00
|
|
|
free(pHolder);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// did we come to the end of the list?
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!pNext)
|
2011-04-13 18:37:12 +00:00
|
|
|
break;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
m_pFirst = pNext;
|
2022-10-09 15:57:43 +00:00
|
|
|
pNext->m_pPrev = nullptr;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
pHolder = pNext;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// no more snapshots in storage
|
2022-10-09 15:57:43 +00:00
|
|
|
m_pFirst = nullptr;
|
|
|
|
m_pLast = nullptr;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2023-09-26 18:03:45 +00:00
|
|
|
void CSnapshotStorage::Add(int Tick, int64_t Tagtime, int DataSize, const void *pData, int AltDataSize, const void *pAltData)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
// allocate memory for holder + snapshot_data
|
2020-09-26 19:41:58 +00:00
|
|
|
int TotalSize = sizeof(CHolder) + DataSize;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-06-22 21:46:25 +00:00
|
|
|
if(AltDataSize > 0)
|
|
|
|
{
|
|
|
|
TotalSize += AltDataSize;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-04-09 09:56:39 +00:00
|
|
|
CHolder *pHolder = (CHolder *)malloc(TotalSize);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// set data
|
|
|
|
pHolder->m_Tick = Tick;
|
|
|
|
pHolder->m_Tagtime = Tagtime;
|
|
|
|
pHolder->m_SnapSize = DataSize;
|
2020-09-26 19:41:58 +00:00
|
|
|
pHolder->m_pSnap = (CSnapshot *)(pHolder + 1);
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_copy(pHolder->m_pSnap, pData, DataSize);
|
|
|
|
|
2022-06-22 21:46:25 +00:00
|
|
|
if(AltDataSize > 0) // create alternative if wanted
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
pHolder->m_pAltSnap = (CSnapshot *)(((char *)pHolder->m_pSnap) + DataSize);
|
2022-06-22 21:46:25 +00:00
|
|
|
mem_copy(pHolder->m_pAltSnap, pAltData, AltDataSize);
|
|
|
|
pHolder->m_AltSnapSize = AltDataSize;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else
|
2022-06-22 21:46:25 +00:00
|
|
|
{
|
2022-10-09 15:57:43 +00:00
|
|
|
pHolder->m_pAltSnap = nullptr;
|
2022-06-22 21:46:25 +00:00
|
|
|
pHolder->m_AltSnapSize = 0;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// link
|
2022-10-09 15:57:43 +00:00
|
|
|
pHolder->m_pNext = nullptr;
|
2010-05-29 07:25:38 +00:00
|
|
|
pHolder->m_pPrev = m_pLast;
|
|
|
|
if(m_pLast)
|
|
|
|
m_pLast->m_pNext = pHolder;
|
|
|
|
else
|
|
|
|
m_pFirst = pHolder;
|
|
|
|
m_pLast = pHolder;
|
|
|
|
}
|
|
|
|
|
2023-09-26 18:03:45 +00:00
|
|
|
int CSnapshotStorage::Get(int Tick, int64_t *pTagtime, const CSnapshot **ppData, const CSnapshot **ppAltData)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CHolder *pHolder = m_pFirst;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
while(pHolder)
|
|
|
|
{
|
|
|
|
if(pHolder->m_Tick == Tick)
|
|
|
|
{
|
|
|
|
if(pTagtime)
|
|
|
|
*pTagtime = pHolder->m_Tagtime;
|
|
|
|
if(ppData)
|
|
|
|
*ppData = pHolder->m_pSnap;
|
|
|
|
if(ppAltData)
|
2012-08-17 16:32:56 +00:00
|
|
|
*ppAltData = pHolder->m_pAltSnap;
|
2010-05-29 07:25:38 +00:00
|
|
|
return pHolder->m_SnapSize;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
pHolder = pHolder->m_pNext;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// CSnapshotBuilder
|
2017-05-21 23:07:13 +00:00
|
|
|
CSnapshotBuilder::CSnapshotBuilder()
|
|
|
|
{
|
|
|
|
m_NumExtendedItemTypes = 0;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2020-03-29 02:36:38 +00:00
|
|
|
void CSnapshotBuilder::Init(bool Sixup)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
m_DataSize = 0;
|
|
|
|
m_NumItems = 0;
|
2020-03-29 02:36:38 +00:00
|
|
|
m_Sixup = Sixup;
|
2017-05-21 23:07:13 +00:00
|
|
|
|
|
|
|
for(int i = 0; i < m_NumExtendedItemTypes; i++)
|
|
|
|
{
|
|
|
|
AddExtendedItemType(i);
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
CSnapshotItem *CSnapshotBuilder::GetItem(int Index)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
return (CSnapshotItem *)&(m_aData[m_aOffsets[Index]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int *CSnapshotBuilder::GetItemData(int Key)
|
|
|
|
{
|
2022-01-14 16:52:23 +00:00
|
|
|
for(int i = 0; i < m_NumItems; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
if(GetItem(i)->Key() == Key)
|
2019-04-11 10:21:42 +00:00
|
|
|
return GetItem(i)->Data();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2022-10-09 15:57:43 +00:00
|
|
|
return nullptr;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
int CSnapshotBuilder::Finish(void *pSnapData)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2022-07-23 13:00:10 +00:00
|
|
|
// flatten and make the snapshot
|
2020-10-27 17:57:14 +00:00
|
|
|
CSnapshot *pSnap = (CSnapshot *)pSnapData;
|
2010-05-29 07:25:38 +00:00
|
|
|
pSnap->m_DataSize = m_DataSize;
|
|
|
|
pSnap->m_NumItems = m_NumItems;
|
2022-07-23 13:00:10 +00:00
|
|
|
mem_copy(pSnap->Offsets(), m_aOffsets, pSnap->OffsetSize());
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_copy(pSnap->DataStart(), m_aData, m_DataSize);
|
2022-07-23 13:00:10 +00:00
|
|
|
return pSnap->TotalSize();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2022-07-02 12:25:16 +00:00
|
|
|
int CSnapshotBuilder::GetTypeFromIndex(int Index)
|
2017-05-21 23:07:13 +00:00
|
|
|
{
|
|
|
|
return CSnapshot::MAX_TYPE - Index;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSnapshotBuilder::AddExtendedItemType(int Index)
|
|
|
|
{
|
|
|
|
dbg_assert(0 <= Index && Index < m_NumExtendedItemTypes, "index out of range");
|
|
|
|
int TypeID = m_aExtendedItemTypes[Index];
|
|
|
|
CUuid Uuid = g_UuidManager.GetUuid(TypeID);
|
|
|
|
int *pUuidItem = (int *)NewItem(0, GetTypeFromIndex(Index), sizeof(Uuid)); // NETOBJTYPE_EX
|
2021-01-05 18:25:58 +00:00
|
|
|
if(pUuidItem)
|
2017-05-21 23:07:13 +00:00
|
|
|
{
|
2023-02-04 00:22:49 +00:00
|
|
|
for(size_t i = 0; i < sizeof(CUuid) / sizeof(int32_t); i++)
|
|
|
|
pUuidItem[i] = bytes_be_to_uint(&Uuid.m_aData[i * sizeof(int32_t)]);
|
2017-05-21 23:07:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSnapshotBuilder::GetExtendedItemTypeIndex(int TypeID)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < m_NumExtendedItemTypes; i++)
|
|
|
|
{
|
|
|
|
if(m_aExtendedItemTypes[i] == TypeID)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dbg_assert(m_NumExtendedItemTypes < MAX_EXTENDED_ITEM_TYPES, "too many extended item types");
|
|
|
|
int Index = m_NumExtendedItemTypes;
|
|
|
|
m_aExtendedItemTypes[Index] = TypeID;
|
|
|
|
m_NumExtendedItemTypes++;
|
|
|
|
return Index;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void *CSnapshotBuilder::NewItem(int Type, int ID, int Size)
|
|
|
|
{
|
2022-01-04 23:11:42 +00:00
|
|
|
if(ID == -1)
|
|
|
|
{
|
2022-10-09 15:57:43 +00:00
|
|
|
return nullptr;
|
2022-01-04 23:11:42 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 17:09:09 +00:00
|
|
|
if(m_DataSize + sizeof(CSnapshotItem) + Size >= CSnapshot::MAX_SIZE ||
|
2022-01-14 16:52:23 +00:00
|
|
|
m_NumItems + 1 >= CSnapshot::MAX_ITEMS)
|
2011-01-18 17:09:09 +00:00
|
|
|
{
|
|
|
|
dbg_assert(m_DataSize < CSnapshot::MAX_SIZE, "too much data");
|
2022-01-14 16:52:23 +00:00
|
|
|
dbg_assert(m_NumItems < CSnapshot::MAX_ITEMS, "too many items");
|
2022-10-09 15:57:43 +00:00
|
|
|
return nullptr;
|
2011-01-18 17:09:09 +00:00
|
|
|
}
|
|
|
|
|
2020-08-04 17:09:37 +00:00
|
|
|
bool Extended = false;
|
2017-05-21 23:07:13 +00:00
|
|
|
if(Type >= OFFSET_UUID)
|
|
|
|
{
|
2020-08-04 17:09:37 +00:00
|
|
|
Extended = true;
|
2017-05-21 23:07:13 +00:00
|
|
|
Type = GetTypeFromIndex(GetExtendedItemTypeIndex(Type));
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize);
|
|
|
|
|
2020-08-04 17:09:37 +00:00
|
|
|
if(m_Sixup && !Extended)
|
2020-03-29 02:36:38 +00:00
|
|
|
{
|
2020-06-12 19:22:54 +00:00
|
|
|
if(Type >= 0)
|
|
|
|
Type = Obj_SixToSeven(Type);
|
|
|
|
else
|
|
|
|
Type *= -1;
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(Type < 0)
|
|
|
|
return pObj;
|
2020-03-29 02:36:38 +00:00
|
|
|
}
|
2022-12-30 12:54:37 +00:00
|
|
|
else if(Type < 0)
|
|
|
|
return nullptr;
|
2020-03-29 02:36:38 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_zero(pObj, sizeof(CSnapshotItem) + Size);
|
2020-09-26 19:41:58 +00:00
|
|
|
pObj->m_TypeAndID = (Type << 16) | ID;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_aOffsets[m_NumItems] = m_DataSize;
|
|
|
|
m_DataSize += sizeof(CSnapshotItem) + Size;
|
|
|
|
m_NumItems++;
|
|
|
|
|
|
|
|
return pObj->Data();
|
|
|
|
}
|