Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
SecureHash.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
6#include "Containers/Map.h"
7#include "Containers/StringConv.h"
8#include "Containers/StringFwd.h"
9#include "Containers/StringView.h"
10#include "Containers/UnrealString.h"
11#include "CoreTypes.h"
12#include "HAL/PlatformCrt.h"
13#include "HAL/PreprocessorHelpers.h"
14#include "HAL/UnrealMemory.h"
15#include "Misc/AssertionMacros.h"
16#include "Misc/CString.h"
17#include "Misc/Guid.h"
18#include "Serialization/Archive.h"
19#include "Serialization/BufferReader.h"
20#include "Serialization/MemoryLayout.h"
21#include "Stats/Stats.h"
22#include "Stats/Stats2.h"
23#include "String/BytesToHex.h"
24#include "String/HexToBytes.h"
25#include "Templates/UnrealTemplate.h"
26
27#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
28#include "Async/AsyncWork.h"
29#endif
30
31
32class FCbFieldView;
33class FCbWriter;
37class FSHA1;
38struct FMD5Hash;
39
40/*-----------------------------------------------------------------------------
41 MD5 functions.
42-----------------------------------------------------------------------------*/
43
44/** @name MD5 functions */
45//@{
46//
47// MD5 Context.
48//
49
50
51//
52// MD5 functions.
53//!!it would be cool if these were implemented as subclasses of
54// FArchive.
55//
56// void appMD5Init( FMD5Context* context );
57// void appMD5Update( FMD5Context* context, uint8* input, int32 inputLen );
58// void appMD5Final( uint8* digest, FMD5Context* context );
59// void appMD5Transform( uint32* state, uint8* block );
60// void appMD5Encode( uint8* output, uint32* input, int32 len );
61// void appMD5Decode( uint32* output, uint8* input, int32 len );
62
63class FMD5
64{
65public:
67 ~FMD5();
68
69 /**
70 * MD5 block update operation. Continues an MD5 message-digest operation,
71 * processing another message block, and updating the context.
72 *
73 * @param input input data
74 * @param inputLen length of the input data in bytes
75 **/
76 void Update(const uint8* input, uint64 inputLen);
77
78 /**
79 * MD5 finalization. Ends an MD5 message-digest operation, writing the
80 * the message digest and zeroizing the context.
81 * Digest is 16 BYTEs.
82 *
83 * @param digest pointer to a buffer where the digest should be stored ( must have at least 16 bytes )
84 **/
85 void Final(uint8* digest);
86
87 /**
88 * Helper to perform the very common case of hashing an ASCII string into a hex representation.
89 *
90 * @param String hex representation of the hash (32 lower-case hex digits)
91 **/
92 static FString HashAnsiString(const TCHAR* String)
93 {
94 return HashBytes((unsigned char*)TCHAR_TO_ANSI(String), FCString::Strlen(String));
95 }
96
97 /**
98 * Helper to perform the very common case of hashing an in-memory array of bytes into a hex representation
99 *
100 * @param String hex representation of the hash (32 lower-case hex digits)
101 **/
102 static FString HashBytes(const uint8* input, uint64 inputLen)
103 {
104 uint8 Digest[16];
105
106 FMD5 Md5Gen;
107
108 Md5Gen.Update(input, inputLen);
109 Md5Gen.Final(Digest);
110
111 FString MD5;
112 for (int32 i = 0; i < 16; i++)
113 {
114 MD5 += FString::Printf(TEXT("%02x"), Digest[i]);
115 }
116 return MD5;
117 }
118
119private:
120 struct FContext
121 {
122 uint32 state[4];
123 uint32 count[2];
124 uint8 buffer[64];
125 };
126
127 void Transform( uint32* state, const uint8* block );
128 void Encode( uint8* output, const uint32* input, int32 len );
129 void Decode( uint32* output, const uint8* input, int32 len );
130
132};
133//@}
134
135struct FMD5Hash;
136
137/** Simple helper struct to ease the caching of MD5 hashes */
139{
140 /** Default constructor */
141 FMD5Hash() : bIsValid(false) {}
142
143 /** Check whether this has hash is valid or not */
144 bool IsValid() const { return bIsValid; }
145
146 /** Set up the MD5 hash from a container */
147 void Set(FMD5& MD5)
148 {
149 MD5.Final(Bytes);
150 bIsValid = true;
151 }
152
153 /** Compare one hash with another */
154 friend bool operator==(const FMD5Hash& LHS, const FMD5Hash& RHS)
155 {
156 return LHS.bIsValid == RHS.bIsValid && (!LHS.bIsValid || FMemory::Memcmp(LHS.Bytes, RHS.Bytes, 16) == 0);
157 }
158
159 /** Compare one hash with another */
160 friend bool operator!=(const FMD5Hash& LHS, const FMD5Hash& RHS)
161 {
162 return LHS.bIsValid != RHS.bIsValid || (LHS.bIsValid && FMemory::Memcmp(LHS.Bytes, RHS.Bytes, 16) != 0);
163 }
164
165 /** Serialise this hash */
166 friend FArchive& operator<<(FArchive& Ar, FMD5Hash& Hash)
167 {
168 Ar << Hash.bIsValid;
169 if (Hash.bIsValid)
170 {
171 Ar.Serialize(Hash.Bytes, 16);
172 }
173
174 return Ar;
175 }
176
177 /** Hash the specified file contents (using the optionally supplied scratch buffer) */
178 static FMD5Hash HashFile(const TCHAR* InFilename, TArray<uint8>* Buffer = nullptr);
179 static FMD5Hash HashFileFromArchive(FArchive* Ar, TArray<uint8>* ScratchPad = nullptr);
180
181 const uint8* GetBytes() const { return Bytes; }
182 const int32 GetSize() const { return sizeof(Bytes); }
183
184private:
185 /** Whether this hash is valid or not */
187
188 /** The bytes this hash comprises */
189 uint8 Bytes[16];
190
191 friend inline FCbWriter& operator<<(FCbWriter& Writer, const FMD5Hash& Hash) // Hidden friends must be inlined to be performant
192 {
193 return Hash.WriteCompactBinary(Writer);
194 }
196 friend bool LoadFromCompactBinary(FCbFieldView Field, FMD5Hash& OutHash); // inlining not available because we don't want FCbFieldView defined
198 friend void LexFromString(FMD5Hash& Hash, const TCHAR*);
199};
200
201/**
202 * Construct a FGuid from a MD5Hash. This means that calling ToString on the resulting FGuid will not result in the
203 * expected MD5 hash string, due to how FGuid outputs the string; LexToString should be used in that case.
204 */
205inline FGuid MD5HashToGuid(const FMD5Hash& Hash)
206{
207 FGuid Result;
208 FMemory::Memcpy(&Result, Hash.GetBytes(), sizeof(FGuid));
209 return Result;
210}
211
212/*-----------------------------------------------------------------------------
213 SHA-1 functions.
214-----------------------------------------------------------------------------*/
215
216/*
217 * NOTE:
218 * 100% free public domain implementation of the SHA-1 algorithm
219 * by Dominik Reichl <dominik.reichl@t-online.de>
220 * Web: http://www.dominik-reichl.de/
221 */
222
223/** This divider string is beween full file hashes and script hashes */
224#define HASHES_SHA_DIVIDER "+++"
225
226/** Stores an SHA hash generated by FSHA1. */
228{
229public:
230 alignas(uint32) uint8 Hash[20];
231
233 {
235 }
236
237 static constexpr int32 GetStringLen() { return UE_ARRAY_COUNT(Hash) * 2; }
238
239 inline void AppendString(FString& Out) const
240 {
241 BytesToHex((const uint8*)Hash, sizeof(Hash), Out);
242 }
243
244 inline FString ToString() const
245 {
246 return BytesToHex((const uint8*)Hash, sizeof(Hash));
247 }
248
249 inline void ToString(TCHAR* Dest, bool bNullTerminate) const
250 {
251 constexpr auto Count = UE_ARRAY_COUNT(Hash);
252 for (int i = 0; i < Count; ++i)
253 {
254 uint8 Val = Hash[i];
255 Dest[i * 2] = NibbleToTChar(Val >> 4);
256 Dest[i * 2 + 1] = NibbleToTChar(Val & 15);
257 }
258
259 if (bNullTerminate)
260 {
261 Dest[Count * 2] = TEXT('\0');
262 }
263 }
264
265 inline void FromString(const FStringView& Src)
266 {
267 check(Src.Len() == GetStringLen());
268 UE::String::HexToBytes(Src, Hash);
269 }
270
271 friend bool operator==(const FSHAHash& X, const FSHAHash& Y)
272 {
273 return FMemory::Memcmp(&X.Hash, &Y.Hash, sizeof(X.Hash)) == 0;
274 }
275
276 friend bool operator!=(const FSHAHash& X, const FSHAHash& Y)
277 {
278 return FMemory::Memcmp(&X.Hash, &Y.Hash, sizeof(X.Hash)) != 0;
279 }
280
281 friend bool operator<(const FSHAHash& X, const FSHAHash& Y)
282 {
283 return FMemory::Memcmp(&X.Hash, &Y.Hash, sizeof(X.Hash)) < 0;
284 }
285
286 friend FArchive& operator<<( FArchive& Ar, FSHAHash& G );
287
288 friend uint32 GetTypeHash(const FSHAHash& InKey)
289 {
290 return *reinterpret_cast<const uint32*>(InKey.Hash);
291 }
292
294 friend void LexFromString(FSHAHash& Hash, const TCHAR*);
295 friend inline FStringBuilderBase& operator<<(FStringBuilderBase& Builder, const FSHAHash& InHash) { UE::String::BytesToHex(InHash.Hash, Builder); return Builder; }
296 friend inline FAnsiStringBuilderBase& operator<<(FAnsiStringBuilderBase& Builder, const FSHAHash& InHash) { UE::String::BytesToHex(InHash.Hash, Builder); return Builder; }
297 friend FCbWriter& operator<<(FCbWriter& Writer, const FSHAHash& Hash);
298 friend bool LoadFromCompactBinary(FCbFieldView Field, FSHAHash& OutHash);
299};
300
301namespace Freeze
302{
303 void IntrinsicToString(const FSHAHash& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext);
304}
305
307
308class FSHA1
309{
310public:
311
312 enum {DigestSize=20};
313 // Constructor and Destructor
316
317 uint32 m_state[5];
318 uint64 m_count;
319 uint32 __reserved1[1];
320 uint8 m_buffer[64];
321 uint8 m_digest[20];
322 uint32 __reserved2[3];
323
324 void Reset();
325
326 // Update the hash value
327 void Update(const uint8 *data, uint64 len);
328
329 // Update the hash value with string
330 void UpdateWithString(const TCHAR *data, uint32 len);
331
332 // Finalize hash and report
333 void Final();
334
335 // Finalize hash and return it
337 {
338 Final();
339 FSHAHash Digest;
340 GetHash(reinterpret_cast<uint8*>(&Digest));
341 return Digest;
342 }
343
344 // Report functions: as pre-formatted and raw data
345 void GetHash(uint8 *puDest);
346
347 /**
348 * Calculate the hash on a single block and return it
349 *
350 * @param Data Input data to hash
351 * @param DataSize Size of the Data block
352 * @param OutHash Resulting hash value (20 byte buffer)
353 */
354 static void HashBuffer(const void* Data, uint64 DataSize, uint8* OutHash);
355
356 /**
357 * Calculate the hash on a single block and return it
358 *
359 * @param Data Input data to hash
360 * @param DataSize Size of the Data block
361 * @return Resulting digest
362 */
363 static FSHAHash HashBuffer(const void* Data, uint64 DataSize)
364 {
365 FSHAHash Hash;
366 HashBuffer(Data, DataSize, Hash.Hash);
367 return Hash;
368 }
369
370 /**
371 * Generate the HMAC (Hash-based Message Authentication Code) for a block of data.
372 * https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
373 *
374 * @param Key The secret key to be used when generating the HMAC
375 * @param KeySize The size of the key
376 * @param Data Input data to hash
377 * @param DataSize Size of the Data block
378 * @param OutHash Resulting hash value (20 byte buffer)
379 */
380 static void HMACBuffer(const void* Key, uint32 KeySize, const void* Data, uint64 DataSize, uint8* OutHash);
381
382 /**
383 * Shared hashes.sha reading code (each platform gets a buffer to the data,
384 * then passes it to this function for processing)
385 *
386 * @param Buffer Contents of hashes.sha (probably loaded from an a section in the executable)
387 * @param BufferSize Size of Buffer
388 * @param bDuplicateKeyMemory If Buffer is not always loaded, pass true so that the 20 byte hashes are duplicated
389 */
390 static void InitializeFileHashesFromBuffer(uint8* Buffer, uint64 BufferSize, bool bDuplicateKeyMemory=false);
391
392 /**
393 * Gets the stored SHA hash from the platform, if it exists. This function
394 * must be able to be called from any thread.
395 *
396 * @param Pathname Pathname to the file to get the SHA for
397 * @param Hash 20 byte array that receives the hash
398 * @param bIsFullPackageHash true if we are looking for a full package hash, instead of a script code only hash
399 *
400 * @return true if the hash was found, false otherwise
401 */
402 static bool GetFileSHAHash(const TCHAR* Pathname, uint8 Hash[20], bool bIsFullPackageHash=true);
403
404private:
405 // Private SHA-1 transformation
406 void Transform(const uint8* buffer, uint64 len);
407
408 /** Global map of filename to hash value, filled out in InitializeFileHashesFromBuffer */
410
411 /** Global map of filename to hash value, but for script-only SHA hashes */
413};
414
415
416/**
417 * Asynchronous SHA verification
418 */
420{
421protected:
422 /** Buffer to run the has on. This class can take ownership of the buffer is bShouldDeleteBuffer is true */
423 void* Buffer;
424
425 /** Size of Buffer */
427
428 /** Hash to compare against */
429 uint8 Hash[20];
430
431 /** Filename to lookup hash value (can be empty if hash was passed to constructor) */
433
434 /** If this is true, and looking up the hash by filename fails, this will abort execution */
436
437 /** Should this class delete the buffer memory when verification is complete? */
439
440public:
441
442 /**
443 * Constructor.
444 *
445 * @param InBuffer Buffer of data to calculate has on. MUST be valid until this task completes (use Counter or pass ownership via bInShouldDeleteBuffer)
446 * @param InBufferSize Size of InBuffer
447 * @param bInShouldDeleteBuffer true if this task should FMemory::Free InBuffer on completion of the verification. Useful for a fire & forget verification
448 * NOTE: If you pass ownership to the task MAKE SURE you are done using the buffer as it could go away at ANY TIME
449 * @param Pathname Pathname to use to have the platform lookup the hash value
450 * @param bInIsUnfoundHashAnError true if failing to lookup the hash value results in a fail (only for Shipping PC)
451 */
453 void* InBuffer,
454 uint64 InBufferSize,
455 bool bInShouldDeleteBuffer,
456 const TCHAR* InPathname,
457 bool bInIsUnfoundHashAnError)
458 : Buffer(InBuffer)
459 , BufferSize(InBufferSize)
461 , bIsUnfoundHashAnError(bInIsUnfoundHashAnError)
462 , bShouldDeleteBuffer(bInShouldDeleteBuffer)
463 {
464 }
465
466 /**
467 * Performs the async hash verification
468 */
469 void DoWork();
470
471 /**
472 * Task API, return true to indicate that we can abandon
473 */
475 {
476 return true;
477 }
478
479 /**
480 * Abandon task, deletes the buffer if that is what was requested
481 */
482 void Abandon()
483 {
485 {
487 Buffer = 0;
488 }
489 }
490
492 {
493 RETURN_QUICK_DECLARE_CYCLE_STAT(FAsyncSHAVerify, STATGROUP_ThreadPoolAsyncTasks);
494 }
495};
496
497/**
498 * Callback that is called if the asynchronous SHA verification fails
499 * This will be called from a pooled thread.
500 *
501 * NOTE: Each platform is expected to implement this!
502 *
503 * @param FailedPathname Pathname of file that failed to verify
504 * @param bFailedDueToMissingHash true if the reason for the failure was that the hash was missing, and that was set as being an error condition
505 */
506void appOnFailSHAVerification(const TCHAR* FailedPathname, bool bFailedDueToMissingHash);
507
508/**
509 * Similar to FBufferReader, but will verify the contents of the buffer on close (on close to that
510 * we know we don't need the data anymore)
511 */
512class FBufferReaderWithSHA final : public FBufferReaderBase
513{
514public:
515 /**
516 * Constructor
517 *
518 * @param Data Buffer to use as the source data to read from
519 * @param Size Size of Data
520 * @param bInFreeOnClose If true, Data will be FMemory::Free'd when this archive is closed
521 * @param SHASourcePathname Path to the file to use to lookup the SHA hash value
522 * @param bIsPersistent Uses this value for SetIsPersistent()
523 * @param bInIsUnfoundHashAnError true if failing to lookup the hash should trigger an error (only in ShippingPC)
524 */
526 void* Data,
527 int64 Size,
528 bool bInFreeOnClose,
529 const TCHAR* SHASourcePathname,
530 bool bIsPersistent=false,
531 bool bInIsUnfoundHashAnError=false
532 )
533 // we force the base class to NOT free buffer on close, as we will let the SHA task do it if needed
534 : FBufferReaderBase(Data, Size, bInFreeOnClose, bIsPersistent)
536 , bIsUnfoundHashAnError(bInIsUnfoundHashAnError)
537 {
538 }
539
541 {
542 Close();
543 }
544
545 bool Close() override;
546
547 /**
548 * Returns the name of the Archive. Useful for getting the name of the package a struct or object
549 * is in when a loading error occurs.
550 *
551 * This is overridden for the specific Archive Types
552 **/
553 virtual FString GetArchiveName() const { return TEXT("FBufferReaderWithSHA"); }
554
555protected:
556 /** Path to the file to use to lookup the SHA hash value */
558 /** true if failing to lookup the hash should trigger an error */
560};
#define check(expr)
#define DECLARE_INTRINSIC_TYPE_LAYOUT(T)
#define TEXT(x)
Definition Platform.h:1108
#define FORCEINLINE
Definition Platform.h:644
FGuid MD5HashToGuid(const FMD5Hash &Hash)
Definition SecureHash.h:205
void appOnFailSHAVerification(const TCHAR *FailedPathname, bool bFailedDueToMissingHash)
#define TCHAR_TO_ANSI(str)
Definition StringConv.h:960
UE_NODISCARD FString BytesToHex(const uint8 *Bytes, int32 NumBytes)
void BytesToHex(const uint8 *Bytes, int32 NumBytes, FString &Out)
#define UE_ARRAY_COUNT(array)
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1569
bool bIsUnfoundHashAnError
Definition SecureHash.h:435
uint8 Hash[20]
Definition SecureHash.h:429
FAsyncSHAVerify(void *InBuffer, uint64 InBufferSize, bool bInShouldDeleteBuffer, const TCHAR *InPathname, bool bInIsUnfoundHashAnError)
Definition SecureHash.h:452
FORCEINLINE TStatId GetStatId() const
Definition SecureHash.h:491
bool bShouldDeleteBuffer
Definition SecureHash.h:438
FBufferReaderBase(void *Data, int64 Size, bool bInFreeOnClose, bool bIsPersistent=false)
~FBufferReaderWithSHA() override
Definition SecureHash.h:540
bool Close() override
FBufferReaderWithSHA(void *Data, int64 Size, bool bInFreeOnClose, const TCHAR *SHASourcePathname, bool bIsPersistent=false, bool bInIsUnfoundHashAnError=false)
Definition SecureHash.h:525
virtual FString GetArchiveName() const
Definition SecureHash.h:553
!it would be cool if these were implemented as subclasses of
Definition SecureHash.h:64
FContext Context
Definition SecureHash.h:131
static FString HashBytes(const uint8 *input, uint64 inputLen)
Definition SecureHash.h:102
static FString HashAnsiString(const TCHAR *String)
Definition SecureHash.h:92
void Transform(uint32 *state, const uint8 *block)
void Final(uint8 *digest)
void Encode(uint8 *output, const uint32 *input, int32 len)
void Decode(uint32 *output, const uint8 *input, int32 len)
void Update(const uint8 *input, uint64 inputLen)
@ DigestSize
Definition SecureHash.h:312
void GetHash(uint8 *puDest)
void Update(const uint8 *data, uint64 len)
FSHAHash Finalize()
Definition SecureHash.h:336
static void HashBuffer(const void *Data, uint64 DataSize, uint8 *OutHash)
uint32 __reserved1[1]
Definition SecureHash.h:319
static TMap< FString, uint8 * > ScriptSHAHashMap
Definition SecureHash.h:412
static TMap< FString, uint8 * > FullFileSHAHashMap
Definition SecureHash.h:409
void UpdateWithString(const TCHAR *data, uint32 len)
uint64 m_count
Definition SecureHash.h:318
static void HMACBuffer(const void *Key, uint32 KeySize, const void *Data, uint64 DataSize, uint8 *OutHash)
uint32 m_state[5]
Definition SecureHash.h:317
static FSHAHash HashBuffer(const void *Data, uint64 DataSize)
Definition SecureHash.h:363
void Reset()
static bool GetFileSHAHash(const TCHAR *Pathname, uint8 Hash[20], bool bIsFullPackageHash=true)
uint8 m_buffer[64]
Definition SecureHash.h:320
static void InitializeFileHashesFromBuffer(uint8 *Buffer, uint64 BufferSize, bool bDuplicateKeyMemory=false)
void Transform(const uint8 *buffer, uint64 len)
uint8 m_digest[20]
Definition SecureHash.h:321
uint32 __reserved2[3]
Definition SecureHash.h:322
void Final()
friend uint32 GetTypeHash(const FSHAHash &InKey)
Definition SecureHash.h:288
friend bool LoadFromCompactBinary(FCbFieldView Field, FSHAHash &OutHash)
FString ToString() const
Definition SecureHash.h:244
friend void LexFromString(FSHAHash &Hash, const TCHAR *)
void FromString(const FStringView &Src)
Definition SecureHash.h:265
void ToString(TCHAR *Dest, bool bNullTerminate) const
Definition SecureHash.h:249
static constexpr int32 GetStringLen()
Definition SecureHash.h:237
friend bool operator<(const FSHAHash &X, const FSHAHash &Y)
Definition SecureHash.h:281
friend bool operator==(const FSHAHash &X, const FSHAHash &Y)
Definition SecureHash.h:271
friend FString LexToString(const FSHAHash &)
friend bool operator!=(const FSHAHash &X, const FSHAHash &Y)
Definition SecureHash.h:276
uint8 Hash[20]
Definition SecureHash.h:230
void AppendString(FString &Out) const
Definition SecureHash.h:239
void IntrinsicToString(const FSHAHash &Object, const FTypeLayoutDesc &TypeDesc, const FPlatformTypeLayoutParameters &LayoutParams, FMemoryToStringContext &OutContext)
Definition Vector.h:40
Definition Guid.h:108
uint32 state[4]
Definition SecureHash.h:122
uint32 count[2]
Definition SecureHash.h:123
uint8 buffer[64]
Definition SecureHash.h:124
friend void LexFromString(FMD5Hash &Hash, const TCHAR *)
const uint8 * GetBytes() const
Definition SecureHash.h:181
const int32 GetSize() const
Definition SecureHash.h:182
static FMD5Hash HashFileFromArchive(FArchive *Ar, TArray< uint8 > *ScratchPad=nullptr)
friend FString LexToString(const FMD5Hash &)
friend bool operator!=(const FMD5Hash &LHS, const FMD5Hash &RHS)
Definition SecureHash.h:160
friend bool operator==(const FMD5Hash &LHS, const FMD5Hash &RHS)
Definition SecureHash.h:154
bool IsValid() const
Definition SecureHash.h:144
void Set(FMD5 &MD5)
Definition SecureHash.h:147
FCbWriter & WriteCompactBinary(FCbWriter &Writer) const
friend bool LoadFromCompactBinary(FCbFieldView Field, FMD5Hash &OutHash)
static FMD5Hash HashFile(const TCHAR *InFilename, TArray< uint8 > *Buffer=nullptr)
uint8 Bytes[16]
Definition SecureHash.h:189
bool bIsValid
Definition SecureHash.h:186
static void Free(void *Original)
static FORCEINLINE void * Memset(void *Dest, uint8 Char, SIZE_T Count)
static FORCEINLINE int32 Memcmp(const void *Buf1, const void *Buf2, SIZE_T Count)
static FORCEINLINE void * Memcpy(void *Dest, const void *Src, SIZE_T Count)