diff --git a/r5dev/core/stdafx.h b/r5dev/core/stdafx.h
index 1637be75..ee52eac0 100644
--- a/r5dev/core/stdafx.h
+++ b/r5dev/core/stdafx.h
@@ -36,6 +36,11 @@
#include "thirdparty/imgui/include/imgui_impl_win32.h"
#endif // !DEDICATED
+#if !defined(SDKLAUNCHER)
+#include "thirdparty/lzham/include/lzham_types.h"
+#include "thirdparty/lzham/include/lzham.h"
+#endif // !SDKLAUNCHER
+
#include "thirdparty/spdlog/include/spdlog.h"
#include "thirdparty/spdlog/include/sinks/basic_file_sink.h"
#include "thirdparty/spdlog/include/sinks/stdout_sinks.h"
@@ -57,4 +62,4 @@ namespace
MODULE g_mRadAudioDecoderDll = MODULE("binkawin64.dll");
MODULE g_mRadAudioSystemDll = MODULE("mileswin64.dll");
}
-#endif // SDKLAUNCHER
+#endif // !SDKLAUNCHER
diff --git a/r5dev/dedicated.vcxproj b/r5dev/dedicated.vcxproj
index 30414412..017a8898 100644
--- a/r5dev/dedicated.vcxproj
+++ b/r5dev/dedicated.vcxproj
@@ -135,7 +135,7 @@
Console
true
- lzhamlib_x64D.lib;lzhamcomp_x64D.lib;lzhamdecomp_x64D.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
+ d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
del "..\..\..\$(ProjectName)" && copy /Y "$(TargetPath)" "..\..\..\
@@ -162,7 +162,7 @@
true
true
true
- lzhamlib_x64.lib;lzhamcomp_x64.lib;lzhamdecomp_x64.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
+ d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
del "..\..\..\$(ProjectName)" && copy /Y "$(TargetPath)" "..\..\..\
@@ -215,7 +215,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -360,6 +387,10 @@
+
+ NotUsing
+ NotUsing
+
NotUsing
NotUsing
@@ -372,6 +403,86 @@
NotUsing
NotUsing
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
diff --git a/r5dev/dedicated.vcxproj.filters b/r5dev/dedicated.vcxproj.filters
index 3bee9887..c110b5a1 100644
--- a/r5dev/dedicated.vcxproj.filters
+++ b/r5dev/dedicated.vcxproj.filters
@@ -97,6 +97,18 @@
{8288ba1a-7609-42ef-af3b-850727635a99}
+
+ {8736d047-b4af-4c17-99ee-454cc96ec1ba}
+
+
+ {e84ad150-2358-4146-971a-02c5f045437c}
+
+
+ {eb98cd2b-4508-43a0-95e1-feacc7c83a8d}
+
+
+ {463e0739-1e5f-47a0-94d1-6cf5b6bf3ea6}
+
@@ -540,12 +552,6 @@
sdk\public\include
-
- thirdparty\lzham\include
-
-
- thirdparty\lzham\include
-
sdk\mathlib
@@ -564,6 +570,93 @@
windows
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\lzhamcomp\include
+
+
+ thirdparty\lzham\lzhamcomp\include
+
+
+ thirdparty\lzham\lzhamdecomp\include
+
+
+ thirdparty\lzham\lzhamdecomp\include
+
@@ -701,6 +794,69 @@
thirdparty\detours
+
+ thirdparty\detours
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham\lzhamcomp
+
+
+ thirdparty\lzham\lzhamcomp
+
+
+ thirdparty\lzham\lzhamcomp
+
+
+ thirdparty\lzham\lzhamdecomp
+
+
+ thirdparty\lzham\lzhamdecomp
+
diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj
index dcab8624..8d942be3 100644
--- a/r5dev/r5dev.vcxproj
+++ b/r5dev/r5dev.vcxproj
@@ -58,6 +58,10 @@
+
+ NotUsing
+ NotUsing
+
NotUsing
NotUsing
@@ -106,6 +110,86 @@
NotUsing
NotUsing
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
@@ -192,7 +276,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -452,7 +563,7 @@
true
false
r5dev.def
- lzhamlib_x64D.lib;lzhamcomp_x64D.lib;lzhamdecomp_x64D.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
+ d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
copy /Y "$(TargetPath)" "..\..\..\bin\$(TargetFileName)"
@@ -488,7 +599,7 @@
true
false
r5dev.def
- lzhamlib_x64.lib;lzhamcomp_x64.lib;lzhamdecomp_x64.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
+ d3d11.lib;bcrypt.lib;%(AdditionalDependencies)
copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)" && del "..\..\..\r5apexsdkd64.dll" && rename "..\..\..\$(TargetFileName)" "r5apexsdkd64.dll"
diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters
index 4bd5cd1c..818da70f 100644
--- a/r5dev/r5dev.vcxproj.filters
+++ b/r5dev/r5dev.vcxproj.filters
@@ -127,6 +127,18 @@
{f52dfb17-f5bd-4258-91a2-500587bee708}
+
+ {f450ee50-7010-49e2-9f91-05a74fcb6a8b}
+
+
+ {11645361-fd70-462f-ab8b-8a78283a5fc7}
+
+
+ {785353c2-6417-4213-b55f-3007a0b79801}
+
+
+ {5beb12b5-0422-4337-9be6-2e6c0a05a69b}
+
@@ -321,6 +333,69 @@
thirdparty\detours
+
+ thirdparty\detours
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham
+
+
+ thirdparty\lzham\lzhamcomp
+
+
+ thirdparty\lzham\lzhamcomp
+
+
+ thirdparty\lzham\lzhamcomp
+
+
+ thirdparty\lzham\lzhamdecomp
+
+
+ thirdparty\lzham\lzhamdecomp
+
@@ -836,12 +911,6 @@
sdk\public\include
-
- thirdparty\lzham\include
-
-
- thirdparty\lzham\include
-
sdk\mathlib
@@ -872,6 +941,93 @@
sdk\milessdk
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\include
+
+
+ thirdparty\lzham\lzhamcomp\include
+
+
+ thirdparty\lzham\lzhamcomp\include
+
+
+ thirdparty\lzham\lzhamdecomp\include
+
+
+ thirdparty\lzham\lzhamdecomp\include
+
diff --git a/r5dev/sdklauncher.vcxproj b/r5dev/sdklauncher.vcxproj
index 99e6e9fb..d5260590 100644
--- a/r5dev/sdklauncher.vcxproj
+++ b/r5dev/sdklauncher.vcxproj
@@ -140,7 +140,7 @@
Console
true
- detours.lib;%(AdditionalDependencies)
+ %(AdditionalDependencies)
del "..\..\..\r5reloaded.exe" && copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)"
@@ -174,7 +174,7 @@
true
true
true
- detours.lib;%(AdditionalDependencies)
+ %(AdditionalDependencies)
del "..\..\..\r5reloaded.exe" && copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)"
@@ -186,6 +186,22 @@
Create
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
@@ -198,6 +214,9 @@
+
+
+
diff --git a/r5dev/sdklauncher.vcxproj.filters b/r5dev/sdklauncher.vcxproj.filters
index 5a7f2de0..70394cc2 100644
--- a/r5dev/sdklauncher.vcxproj.filters
+++ b/r5dev/sdklauncher.vcxproj.filters
@@ -13,6 +13,12 @@
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+ {82b18787-373d-42ce-8d8d-1e3adba8d3a0}
+
+
+ {dc968871-7ca2-452b-a5b1-350a12dd54aa}
+
@@ -21,6 +27,18 @@
Source Files
+
+ Detours Files
+
+
+ Detours Files
+
+
+ Detours Files
+
+
+ Detours Files
+
@@ -37,6 +55,15 @@
Header Files
+
+ Detours Files\include
+
+
+ Detours Files\include
+
+
+ Detours Files\include
+
diff --git a/r5dev/thirdparty/detours/src/creatwth.cpp b/r5dev/thirdparty/detours/src/creatwth.cpp
new file mode 100644
index 00000000..f6720d7b
--- /dev/null
+++ b/r5dev/thirdparty/detours/src/creatwth.cpp
@@ -0,0 +1,1783 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// Create a process with a DLL (creatwth.cpp of detours.lib)
+//
+// Microsoft Research Detours Package, Version 4.0.1
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+
+// #define DETOUR_DEBUG 1
+#define DETOURS_INTERNAL
+#include "../include/detours.h"
+#include
+
+#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
+#error detours.h version mismatch
+#endif
+
+#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
+#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
+#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
+#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
+
+//////////////////////////////////////////////////////////////////////////////
+//
+const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */
+ 0xea0251b9, 0x5cde, 0x41b5,
+ { 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Enumerate through modules in the target process.
+//
+static PVOID LoadNtHeaderFromProcess(_In_ HANDLE hProcess,
+ _In_ HMODULE hModule,
+ _Out_ PIMAGE_NT_HEADERS32 pNtHeader)
+{
+ ZeroMemory(pNtHeader, sizeof(*pNtHeader));
+ PBYTE pbModule = (PBYTE)hModule;
+
+ if (pbModule == NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ MEMORY_BASIC_INFORMATION mbi;
+ ZeroMemory(&mbi, sizeof(mbi));
+
+ if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) {
+ return NULL;
+ }
+
+ IMAGE_DOS_HEADER idh;
+ if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
+ pbModule, pbModule + sizeof(idh), GetLastError()));
+ return NULL;
+ }
+
+ if (idh.e_magic != IMAGE_DOS_SIGNATURE ||
+ (DWORD)idh.e_lfanew > mbi.RegionSize ||
+ (DWORD)idh.e_lfanew < sizeof(idh)) {
+
+ SetLastError(ERROR_BAD_EXE_FORMAT);
+ return NULL;
+ }
+
+ if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew,
+ pNtHeader, sizeof(*pNtHeader), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %lu\n",
+ pbModule + idh.e_lfanew,
+ pbModule + idh.e_lfanew + sizeof(*pNtHeader),
+ pbModule,
+ GetLastError()));
+ return NULL;
+ }
+
+ if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
+ SetLastError(ERROR_BAD_EXE_FORMAT);
+ return NULL;
+ }
+
+ return pbModule + idh.e_lfanew;
+}
+
+static HMODULE EnumerateModulesInProcess(_In_ HANDLE hProcess,
+ _In_opt_ HMODULE hModuleLast,
+ _Out_ PIMAGE_NT_HEADERS32 pNtHeader,
+ _Out_opt_ PVOID *pRemoteNtHeader)
+{
+ ZeroMemory(pNtHeader, sizeof(*pNtHeader));
+ if (pRemoteNtHeader) {
+ *pRemoteNtHeader = NULL;
+ }
+
+ PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY;
+
+ MEMORY_BASIC_INFORMATION mbi;
+ ZeroMemory(&mbi, sizeof(mbi));
+
+ // Find the next memory region that contains a mapped PE image.
+ //
+
+ for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
+ if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {
+ break;
+ }
+
+ // Usermode address space has such an unaligned region size always at the
+ // end and only at the end.
+ //
+ if ((mbi.RegionSize & 0xfff) == 0xfff) {
+ break;
+ }
+ if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) {
+ break;
+ }
+
+ // Skip uncommitted regions and guard pages.
+ //
+ if ((mbi.State != MEM_COMMIT) ||
+ ((mbi.Protect & 0xff) == PAGE_NOACCESS) ||
+ (mbi.Protect & PAGE_GUARD)) {
+ continue;
+ }
+
+ PVOID remoteHeader
+ = LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader);
+ if (remoteHeader) {
+ if (pRemoteNtHeader) {
+ *pRemoteNtHeader = remoteHeader;
+ }
+
+ return (HMODULE)pbLast;
+ }
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Find payloads in target process.
+//
+
+static PVOID FindDetourSectionInRemoteModule(_In_ HANDLE hProcess,
+ _In_ HMODULE hModule,
+ _In_ const IMAGE_NT_HEADERS32 *pNtHeader,
+ _In_ PVOID pRemoteNtHeader)
+{
+ if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
+ SetLastError(ERROR_EXE_MARKED_INVALID);
+ return NULL;
+ }
+
+ PIMAGE_SECTION_HEADER pRemoteSectionHeaders
+ = (PIMAGE_SECTION_HEADER)((PBYTE)pRemoteNtHeader
+ + sizeof(pNtHeader->Signature)
+ + sizeof(pNtHeader->FileHeader)
+ + pNtHeader->FileHeader.SizeOfOptionalHeader);
+
+ IMAGE_SECTION_HEADER header;
+ for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; ++n) {
+ if (!ReadProcessMemory(hProcess, pRemoteSectionHeaders + n, &header, sizeof(header), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
+ pRemoteSectionHeaders + n,
+ (PBYTE)(pRemoteSectionHeaders + n) + sizeof(header),
+ GetLastError()));
+
+ return NULL;
+ }
+
+ if (strcmp((PCHAR)header.Name, ".detour") == 0) {
+ if (header.VirtualAddress == 0 ||
+ header.SizeOfRawData == 0) {
+
+ break;
+ }
+
+ SetLastError(NO_ERROR);
+ return (PBYTE)hModule + header.VirtualAddress;
+ }
+ }
+
+ SetLastError(ERROR_EXE_MARKED_INVALID);
+ return NULL;
+}
+
+static PVOID FindPayloadInRemoteDetourSection(_In_ HANDLE hProcess,
+ _In_ REFGUID rguid,
+ _Out_opt_ DWORD *pcbData,
+ _In_ PVOID pvRemoteDetoursSection)
+{
+ if (pcbData) {
+ *pcbData = 0;
+ }
+
+ PBYTE pbData = (PBYTE)pvRemoteDetoursSection;
+
+ DETOUR_SECTION_HEADER header;
+ if (!ReadProcessMemory(hProcess, pbData, &header, sizeof(header), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(dsh@%p..%p) failed: %lu\n",
+ pbData,
+ pbData + sizeof(header),
+ GetLastError()));
+ return NULL;
+ }
+
+ if (header.cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||
+ header.nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {
+ SetLastError(ERROR_EXE_MARKED_INVALID);
+ return NULL;
+ }
+
+ if (header.nDataOffset == 0) {
+ header.nDataOffset = header.cbHeaderSize;
+ }
+
+ for (PVOID pvSection = pbData + header.nDataOffset; pvSection < pbData + header.cbDataSize;) {
+ DETOUR_SECTION_RECORD section;
+ if (!ReadProcessMemory(hProcess, pvSection, §ion, sizeof(section), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(dsr@%p..%p) failed: %lu\n",
+ pvSection,
+ (PBYTE)pvSection + sizeof(section),
+ GetLastError()));
+ return NULL;
+ }
+
+ if (DetourAreSameGuid(section.guid, rguid)) {
+ if (pcbData) {
+ *pcbData = section.cbBytes - sizeof(section);
+ }
+ SetLastError(NO_ERROR);
+ return (DETOUR_SECTION_RECORD *)pvSection + 1;
+ }
+
+ pvSection = (PBYTE)pvSection + section.cbBytes;
+ }
+
+ return NULL;
+}
+
+_Success_(return != NULL)
+PVOID WINAPI DetourFindRemotePayload(_In_ HANDLE hProcess,
+ _In_ REFGUID rguid,
+ _Out_opt_ DWORD *pcbData)
+{
+ if (hProcess == NULL) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+
+ IMAGE_NT_HEADERS32 header;
+ PVOID pvRemoteHeader;
+ for (HMODULE hMod = NULL; (hMod = EnumerateModulesInProcess(hProcess, hMod, &header, &pvRemoteHeader)) != NULL;) {
+ PVOID pvData = FindDetourSectionInRemoteModule(hProcess, hMod, &header, pvRemoteHeader);
+ if (pvData != NULL) {
+ pvData = FindPayloadInRemoteDetourSection(hProcess, rguid, pcbData, pvData);
+ if (pvData != NULL) {
+ return pvData;
+ }
+ }
+ }
+
+ SetLastError(ERROR_MOD_NOT_FOUND);
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Find a region of memory in which we can create a replacement import table.
+//
+static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbModule, PBYTE pbBase, DWORD cbAlloc)
+{
+ MEMORY_BASIC_INFORMATION mbi;
+ ZeroMemory(&mbi, sizeof(mbi));
+
+ PBYTE pbLast = pbBase;
+ for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
+
+ ZeroMemory(&mbi, sizeof(mbi));
+ if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {
+ if (GetLastError() == ERROR_INVALID_PARAMETER) {
+ break;
+ }
+ DETOUR_TRACE(("VirtualQueryEx(%p) failed: %lu\n",
+ pbLast, GetLastError()));
+ break;
+ }
+ // Usermode address space has such an unaligned region size always at the
+ // end and only at the end.
+ //
+ if ((mbi.RegionSize & 0xfff) == 0xfff) {
+ break;
+ }
+
+ // Skip anything other than a pure free region.
+ //
+ if (mbi.State != MEM_FREE) {
+ continue;
+ }
+
+ // Use the max of mbi.BaseAddress and pbBase, in case mbi.BaseAddress < pbBase.
+ PBYTE pbAddress = (PBYTE)mbi.BaseAddress > pbBase ? (PBYTE)mbi.BaseAddress : pbBase;
+
+ // Round pbAddress up to the nearest MM allocation boundary.
+ const DWORD_PTR mmGranularityMinusOne = (DWORD_PTR)(MM_ALLOCATION_GRANULARITY -1);
+ pbAddress = (PBYTE)(((DWORD_PTR)pbAddress + mmGranularityMinusOne) & ~mmGranularityMinusOne);
+
+#ifdef _WIN64
+ // The offset from pbModule to any replacement import must fit into 32 bits.
+ // For simplicity, we check that the offset to the last byte fits into 32 bits,
+ // instead of the largest offset we'll actually use. The values are very similar.
+ const size_t GB4 = ((((size_t)1) << 32) - 1);
+ if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) {
+ DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress));
+ return NULL;
+ }
+#else
+ UNREFERENCED_PARAMETER(pbModule);
+#endif
+
+ DETOUR_TRACE(("Free region %p..%p\n",
+ mbi.BaseAddress,
+ (PBYTE)mbi.BaseAddress + mbi.RegionSize));
+
+ for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) {
+ PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc,
+ MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ if (pbAlloc == NULL) {
+ DETOUR_TRACE(("VirtualAllocEx(%p) failed: %lu\n", pbAddress, GetLastError()));
+ continue;
+ }
+#ifdef _WIN64
+ // The offset from pbModule to any replacement import must fit into 32 bits.
+ if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) {
+ DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress));
+ return NULL;
+ }
+#endif
+ DETOUR_TRACE(("[%p..%p] Allocated for import table.\n",
+ pbAlloc, pbAlloc + cbAlloc));
+ return pbAlloc;
+ }
+ }
+ return NULL;
+}
+
+static inline DWORD PadToDword(DWORD dw)
+{
+ return (dw + 3) & ~3u;
+}
+
+static inline DWORD PadToDwordPtr(DWORD dw)
+{
+ return (dw + 7) & ~7u;
+}
+
+static inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest,
+ _In_ size_t cchDest,
+ _In_z_ LPCSTR pszSize)
+{
+ if (cchDest == 0 || pszDest == NULL || pszSize == NULL ||
+ pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') {
+
+ // can not write into empty buffer or with string other than two chars.
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ for (; cchDest >= 2; cchDest--, pszDest++) {
+ if (pszDest[0] == '?' && pszDest[1] == '?') {
+ pszDest[0] = pszSize[0];
+ pszDest[1] = pszSize[1];
+ break;
+ }
+ }
+
+ return S_OK;
+}
+
+static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der)
+{
+ // Save the various headers for DetourRestoreAfterWith.
+ ZeroMemory(&der, sizeof(der));
+ der.cb = sizeof(der);
+
+ der.pidh = (PBYTE)hModule;
+ der.cbidh = sizeof(der.idh);
+ if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
+ der.pidh, der.pidh + der.cbidh, GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh));
+
+ // We read the NT header in two passes to get the full size.
+ // First we read just the Signature and FileHeader.
+ der.pinh = der.pidh + der.idh.e_lfanew;
+ der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader);
+ if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
+ der.pinh, der.pinh + der.cbinh, GetLastError()));
+ return FALSE;
+ }
+
+ // Second we read the OptionalHeader and Section headers.
+ der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
+ der.inh.FileHeader.SizeOfOptionalHeader +
+ der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
+
+ if (der.cbinh > sizeof(der.raw)) {
+ return FALSE;
+ }
+
+ if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
+ der.pinh, der.pinh + der.cbinh, GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh));
+
+ // Third, we read the CLR header
+
+ if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 &&
+ der.inh32.CLR_DIRECTORY.Size != 0) {
+
+ DETOUR_TRACE(("CLR32.VirtAddr=%08lx, CLR.Size=%lu\n",
+ der.inh32.CLR_DIRECTORY.VirtualAddress,
+ der.inh32.CLR_DIRECTORY.Size));
+
+ der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress;
+ }
+ }
+ else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 &&
+ der.inh64.CLR_DIRECTORY.Size != 0) {
+
+ DETOUR_TRACE(("CLR64.VirtAddr=%08lx, CLR.Size=%lu\n",
+ der.inh64.CLR_DIRECTORY.VirtualAddress,
+ der.inh64.CLR_DIRECTORY.Size));
+
+ der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress;
+ }
+ }
+
+ if (der.pclr != 0) {
+ der.cbclr = sizeof(der.clr);
+ if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %lu\n",
+ der.pclr, der.pclr + der.cbclr, GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
+ }
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+#if DETOURS_32BIT
+#define DWORD_XX DWORD32
+#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32
+#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC
+#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32
+#define IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA32
+#define UPDATE_IMPORTS_XX UpdateImports32
+#define DETOURS_BITS_XX 32
+#include "uimports.cpp"
+#undef DETOUR_EXE_RESTORE_FIELD_XX
+#undef DWORD_XX
+#undef IMAGE_NT_HEADERS_XX
+#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX
+#undef IMAGE_ORDINAL_FLAG_XX
+#undef UPDATE_IMPORTS_XX
+#endif // DETOURS_32BIT
+
+#if DETOURS_64BIT
+#define DWORD_XX DWORD64
+#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64
+#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC
+#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64
+#define IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA64
+#define UPDATE_IMPORTS_XX UpdateImports64
+#define DETOURS_BITS_XX 64
+#include "uimports.cpp"
+#undef DETOUR_EXE_RESTORE_FIELD_XX
+#undef DWORD_XX
+#undef IMAGE_NT_HEADERS_XX
+#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX
+#undef IMAGE_ORDINAL_FLAG_XX
+#undef UPDATE_IMPORTS_XX
+#endif // DETOURS_64BIT
+
+//////////////////////////////////////////////////////////////////////////////
+//
+#if DETOURS_64BIT
+
+C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16);
+
+static BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine,
+ DETOUR_EXE_RESTORE& der)
+{
+ IMAGE_DOS_HEADER idh;
+ IMAGE_NT_HEADERS32 inh32;
+ IMAGE_NT_HEADERS64 inh64;
+ IMAGE_SECTION_HEADER sects[32];
+ PBYTE pbModule = (PBYTE)hModule;
+ DWORD n;
+
+ ZeroMemory(&inh32, sizeof(inh32));
+ ZeroMemory(&inh64, sizeof(inh64));
+ ZeroMemory(sects, sizeof(sects));
+
+ DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine));
+ //////////////////////////////////////////////////////// Read old headers.
+ //
+ if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
+ pbModule, pbModule + sizeof(idh), GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n",
+ pbModule, pbModule + sizeof(idh)));
+
+ PBYTE pnh = pbModule + idh.e_lfanew;
+ if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
+ pnh, pnh + sizeof(inh32), GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32)));
+
+ if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) {
+ return FALSE;
+ }
+
+ PBYTE psects = pnh +
+ FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
+ inh32.FileHeader.SizeOfOptionalHeader;
+ ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
+ psects, psects + cb, GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb));
+
+ ////////////////////////////////////////////////////////// Convert header.
+ //
+ inh64.Signature = inh32.Signature;
+ inh64.FileHeader = inh32.FileHeader;
+ inh64.FileHeader.Machine = machine;
+ inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
+
+ inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
+ inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion;
+ inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion;
+ inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode;
+ inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData;
+ inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData;
+ inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint;
+ inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode;
+ inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase;
+ inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment;
+ inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment;
+ inh64.OptionalHeader.MajorOperatingSystemVersion
+ = inh32.OptionalHeader.MajorOperatingSystemVersion;
+ inh64.OptionalHeader.MinorOperatingSystemVersion
+ = inh32.OptionalHeader.MinorOperatingSystemVersion;
+ inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion;
+ inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion;
+ inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion;
+ inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion;
+ inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue;
+ inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage;
+ inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders;
+ inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum;
+ inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem;
+ inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics;
+ inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve;
+ inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit;
+ inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve;
+ inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit;
+ inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags;
+ inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes;
+ for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) {
+ inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n];
+ }
+
+ /////////////////////////////////////////////////////// Write new headers.
+ //
+ DWORD dwProtect = 0;
+ if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,
+ PAGE_EXECUTE_READWRITE, &dwProtect)) {
+ return FALSE;
+ }
+
+ if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n",
+ pnh, pnh + sizeof(inh64), GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64)));
+
+ psects = pnh +
+ FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
+ inh64.FileHeader.SizeOfOptionalHeader;
+ cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %lu\n",
+ psects, psects + cb, GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb));
+
+ // Record the updated headers.
+ if (!RecordExeRestore(hProcess, hModule, der)) {
+ return FALSE;
+ }
+
+ // Remove the import table.
+ if (der.pclr != NULL && (der.clr.Flags & COMIMAGE_FLAGS_ILONLY)) {
+ inh64.IMPORT_DIRECTORY.VirtualAddress = 0;
+ inh64.IMPORT_DIRECTORY.Size = 0;
+
+ if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n",
+ pnh, pnh + sizeof(inh64), GetLastError()));
+ return FALSE;
+ }
+ }
+
+ DWORD dwOld = 0;
+ if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,
+ dwProtect, &dwOld)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif // DETOURS_64BIT
+
+typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
+
+static BOOL IsWow64ProcessHelper(HANDLE hProcess,
+ PBOOL Wow64Process)
+{
+#ifdef _X86_
+ if (Wow64Process == NULL) {
+ return FALSE;
+ }
+
+ // IsWow64Process is not available on all supported versions of Windows.
+ //
+ HMODULE hKernel32 = LoadLibraryW(L"KERNEL32.DLL");
+ if (hKernel32 == NULL) {
+ DETOUR_TRACE(("LoadLibraryW failed: %lu\n", GetLastError()));
+ return FALSE;
+ }
+
+ LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
+ hKernel32, "IsWow64Process");
+
+ if (pfnIsWow64Process == NULL) {
+ DETOUR_TRACE(("GetProcAddress failed: %lu\n", GetLastError()));
+ return FALSE;
+ }
+ return pfnIsWow64Process(hProcess, Wow64Process);
+#else
+ return IsWow64Process(hProcess, Wow64Process);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,
+ _In_reads_(nDlls) LPCSTR *rlpDlls,
+ _In_ DWORD nDlls)
+{
+ // Find the next memory region that contains a mapped PE image.
+ //
+ BOOL bIs32BitProcess;
+ BOOL bIs64BitOS = FALSE;
+ HMODULE hModule = NULL;
+ HMODULE hLast = NULL;
+
+ DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%lu)\n", hProcess, nDlls));
+
+ for (;;) {
+ IMAGE_NT_HEADERS32 inh;
+
+ if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh, NULL)) == NULL) {
+ break;
+ }
+
+ DETOUR_TRACE(("%p machine=%04x magic=%04x\n",
+ hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic));
+
+ if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
+ hModule = hLast;
+ DETOUR_TRACE(("%p Found EXE\n", hLast));
+ }
+ }
+
+ if (hModule == NULL) {
+ SetLastError(ERROR_INVALID_OPERATION);
+ return FALSE;
+ }
+
+ // Determine if the target process is 32bit or 64bit. This is a two-stop process:
+ //
+ // 1. First, determine if we're running on a 64bit operating system.
+ // - If we're running 64bit code (i.e. _WIN64 is defined), this is trivially true.
+ // - If we're running 32bit code (i.e. _WIN64 is not defined), test if
+ // we're running under Wow64. If so, it implies that the operating system
+ // is 64bit.
+ //
+#ifdef _WIN64
+ bIs64BitOS = TRUE;
+#else
+ if (!IsWow64ProcessHelper(GetCurrentProcess(), &bIs64BitOS)) {
+ return FALSE;
+ }
+#endif
+
+ // 2. With the operating system bitness known, we can now consider the target process:
+ // - If we're running on a 64bit OS, the target process is 32bit in case
+ // it is running under Wow64. Otherwise, it's 64bit, running natively
+ // (without Wow64).
+ // - If we're running on a 32bit OS, the target process must be 32bit, too.
+ //
+ if (bIs64BitOS) {
+ if (!IsWow64ProcessHelper(hProcess, &bIs32BitProcess)) {
+ return FALSE;
+ }
+ } else {
+ bIs32BitProcess = TRUE;
+ }
+
+ DETOUR_TRACE((" 32BitProcess=%d\n", bIs32BitProcess));
+
+ return DetourUpdateProcessWithDllEx(hProcess,
+ hModule,
+ bIs32BitProcess,
+ rlpDlls,
+ nDlls);
+}
+
+BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,
+ _In_ HMODULE hModule,
+ _In_ BOOL bIs32BitProcess,
+ _In_reads_(nDlls) LPCSTR *rlpDlls,
+ _In_ DWORD nDlls)
+{
+ // Find the next memory region that contains a mapped PE image.
+ //
+ BOOL bIs32BitExe = FALSE;
+
+ DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%lu)\n", hProcess, hModule, nDlls));
+
+ IMAGE_NT_HEADERS32 inh;
+
+ if (hModule == NULL || !LoadNtHeaderFromProcess(hProcess, hModule, &inh)) {
+ SetLastError(ERROR_INVALID_OPERATION);
+ return FALSE;
+ }
+
+ if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
+ && inh.FileHeader.Machine != 0) {
+
+ bIs32BitExe = TRUE;
+ }
+
+ DETOUR_TRACE((" 32BitExe=%d\n", bIs32BitExe));
+
+ if (hModule == NULL) {
+ SetLastError(ERROR_INVALID_OPERATION);
+ return FALSE;
+ }
+
+ // Save the various headers for DetourRestoreAfterWith.
+ //
+ DETOUR_EXE_RESTORE der;
+
+ if (!RecordExeRestore(hProcess, hModule, der)) {
+ return FALSE;
+ }
+
+#if defined(DETOURS_64BIT)
+ // Try to convert a neutral 32-bit managed binary to a 64-bit managed binary.
+ if (bIs32BitExe && !bIs32BitProcess) {
+ if (!der.pclr // Native binary
+ || (der.clr.Flags & COMIMAGE_FLAGS_ILONLY) == 0 // Or mixed-mode MSIL
+ || (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) != 0) { // Or 32BIT Required MSIL
+
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ if (!UpdateFrom32To64(hProcess, hModule,
+#if defined(DETOURS_X64)
+ IMAGE_FILE_MACHINE_AMD64,
+#elif defined(DETOURS_IA64)
+ IMAGE_FILE_MACHINE_IA64,
+#elif defined(DETOURS_ARM64)
+ IMAGE_FILE_MACHINE_ARM64,
+#else
+#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit.
+#endif
+ der)) {
+ return FALSE;
+ }
+ bIs32BitExe = FALSE;
+ }
+#endif // DETOURS_64BIT
+
+ // Now decide if we can insert the detour.
+
+#if defined(DETOURS_32BIT)
+ if (bIs32BitProcess) {
+ // 32-bit native or 32-bit managed process on any platform.
+ if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) {
+ return FALSE;
+ }
+ }
+ else {
+ // 64-bit native or 64-bit managed process.
+ //
+ // Can't detour a 64-bit process with 32-bit code.
+ // Note: This happens for 32-bit PE binaries containing only
+ // manage code that have been marked as 64-bit ready.
+ //
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+#elif defined(DETOURS_64BIT)
+ if (bIs32BitProcess || bIs32BitExe) {
+ // Can't detour a 32-bit process with 64-bit code.
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ else {
+ // 64-bit native or 64-bit managed process on any platform.
+ if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) {
+ return FALSE;
+ }
+ }
+#else
+#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT.
+#endif // DETOURS_64BIT
+
+ /////////////////////////////////////////////////// Update the CLR header.
+ //
+ if (der.pclr != NULL) {
+ DETOUR_CLR_HEADER clr;
+ CopyMemory(&clr, &der.clr, sizeof(clr));
+ clr.Flags &= ~COMIMAGE_FLAGS_ILONLY; // Clear the IL_ONLY flag.
+
+ DWORD dwProtect;
+ if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) {
+ DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %lu\n", GetLastError()));
+ return FALSE;
+ }
+
+ if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(clr) failed: %lu\n", GetLastError()));
+ return FALSE;
+ }
+
+ if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) {
+ DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %lu\n", GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
+
+#if DETOURS_64BIT
+ if (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) { // Is the 32BIT Required Flag set?
+ // X64 never gets here because the process appears as a WOW64 process.
+ // However, on IA64, it doesn't appear to be a WOW process.
+ DETOUR_TRACE(("CLR Requires 32-bit\n"));
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+#endif // DETOURS_64BIT
+ }
+
+ //////////////////////////////// Save the undo data to the target process.
+ //
+ if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) {
+ DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName,
+ _Inout_opt_ LPSTR lpCommandLine,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ _In_ BOOL bInheritHandles,
+ _In_ DWORD dwCreationFlags,
+ _In_opt_ LPVOID lpEnvironment,
+ _In_opt_ LPCSTR lpCurrentDirectory,
+ _In_ LPSTARTUPINFOA lpStartupInfo,
+ _Out_ LPPROCESS_INFORMATION lpProcessInformation,
+ _In_ LPCSTR lpDllName,
+ _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
+{
+ DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);
+ PROCESS_INFORMATION pi;
+ BOOL fResult = FALSE;
+
+ if (pfCreateProcessA == NULL) {
+ pfCreateProcessA = CreateProcessA;
+ }
+
+ fResult = pfCreateProcessA(lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwMyCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ &pi);
+
+ if (lpProcessInformation != NULL) {
+ CopyMemory(lpProcessInformation, &pi, sizeof(pi));
+ }
+
+ if (!fResult) {
+ return FALSE;
+ }
+
+ LPCSTR rlpDlls[2];
+ DWORD nDlls = 0;
+ if (lpDllName != NULL) {
+ rlpDlls[nDlls++] = lpDllName;
+ }
+
+ if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {
+ TerminateProcess(pi.hProcess, ~0u);
+ return FALSE;
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED)) {
+ ResumeThread(pi.hThread);
+ }
+ return TRUE;
+}
+
+
+BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName,
+ _Inout_opt_ LPWSTR lpCommandLine,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ _In_ BOOL bInheritHandles,
+ _In_ DWORD dwCreationFlags,
+ _In_opt_ LPVOID lpEnvironment,
+ _In_opt_ LPCWSTR lpCurrentDirectory,
+ _In_ LPSTARTUPINFOW lpStartupInfo,
+ _Out_ LPPROCESS_INFORMATION lpProcessInformation,
+ _In_ LPCSTR lpDllName,
+ _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
+{
+ DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);
+ PROCESS_INFORMATION pi;
+
+ if (pfCreateProcessW == NULL) {
+ pfCreateProcessW = CreateProcessW;
+ }
+
+ BOOL fResult = pfCreateProcessW(lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwMyCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ &pi);
+
+ if (lpProcessInformation) {
+ CopyMemory(lpProcessInformation, &pi, sizeof(pi));
+ }
+
+ if (!fResult) {
+ return FALSE;
+ }
+
+ LPCSTR rlpDlls[2];
+ DWORD nDlls = 0;
+ if (lpDllName != NULL) {
+ rlpDlls[nDlls++] = lpDllName;
+ }
+
+ if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {
+ TerminateProcess(pi.hProcess, ~0u);
+ return FALSE;
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED)) {
+ ResumeThread(pi.hThread);
+ }
+ return TRUE;
+}
+
+BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess,
+ _In_ REFGUID rguid,
+ _In_reads_bytes_(cbData) LPCVOID pvData,
+ _In_ DWORD cbData)
+{
+ return DetourCopyPayloadToProcessEx(hProcess, rguid, pvData, cbData) != NULL;
+}
+
+_Success_(return != NULL)
+PVOID WINAPI DetourCopyPayloadToProcessEx(_In_ HANDLE hProcess,
+ _In_ REFGUID rguid,
+ _In_reads_bytes_(cbData) LPCVOID pvData,
+ _In_ DWORD cbData)
+{
+ if (hProcess == NULL) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+
+ DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) +
+ sizeof(IMAGE_NT_HEADERS) +
+ sizeof(IMAGE_SECTION_HEADER) +
+ sizeof(DETOUR_SECTION_HEADER) +
+ sizeof(DETOUR_SECTION_RECORD) +
+ cbData);
+
+ PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (pbBase == NULL) {
+ DETOUR_TRACE(("VirtualAllocEx(%lu) failed: %lu\n", cbTotal, GetLastError()));
+ return NULL;
+ }
+
+ // As you can see in the following code,
+ // the memory layout of the payload range "[pbBase, pbBase+cbTotal]" is a PE executable file,
+ // so DetourFreePayload can use "DetourGetContainingModule(Payload pointer)" to get the above "pbBase" pointer,
+ // pbBase: the memory block allocated by VirtualAllocEx will be released in DetourFreePayload by VirtualFree.
+
+ PBYTE pbTarget = pbBase;
+ IMAGE_DOS_HEADER idh;
+ IMAGE_NT_HEADERS inh;
+ IMAGE_SECTION_HEADER ish;
+ DETOUR_SECTION_HEADER dsh;
+ DETOUR_SECTION_RECORD dsr;
+ SIZE_T cbWrote = 0;
+
+ ZeroMemory(&idh, sizeof(idh));
+ idh.e_magic = IMAGE_DOS_SIGNATURE;
+ idh.e_lfanew = sizeof(idh);
+ if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) ||
+ cbWrote != sizeof(idh)) {
+ DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError()));
+ return NULL;
+ }
+ pbTarget += sizeof(idh);
+
+ ZeroMemory(&inh, sizeof(inh));
+ inh.Signature = IMAGE_NT_SIGNATURE;
+ inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader);
+ inh.FileHeader.Characteristics = IMAGE_FILE_DLL;
+ inh.FileHeader.NumberOfSections = 1;
+ inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;
+ if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) ||
+ cbWrote != sizeof(inh)) {
+ return NULL;
+ }
+ pbTarget += sizeof(inh);
+
+ ZeroMemory(&ish, sizeof(ish));
+ memcpy(ish.Name, ".detour", sizeof(ish.Name));
+ ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase);
+ ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) +
+ sizeof(DETOUR_SECTION_RECORD) +
+ cbData);
+ if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) ||
+ cbWrote != sizeof(ish)) {
+ return NULL;
+ }
+ pbTarget += sizeof(ish);
+
+ ZeroMemory(&dsh, sizeof(dsh));
+ dsh.cbHeaderSize = sizeof(dsh);
+ dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;
+ dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER);
+ dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) +
+ sizeof(DETOUR_SECTION_RECORD) +
+ cbData);
+ if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) ||
+ cbWrote != sizeof(dsh)) {
+ return NULL;
+ }
+ pbTarget += sizeof(dsh);
+
+ ZeroMemory(&dsr, sizeof(dsr));
+ dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD);
+ dsr.nReserved = 0;
+ dsr.guid = rguid;
+ if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) ||
+ cbWrote != sizeof(dsr)) {
+ return NULL;
+ }
+ pbTarget += sizeof(dsr);
+
+ if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) ||
+ cbWrote != cbData) {
+ return NULL;
+ }
+
+ DETOUR_TRACE(("Copied %lu byte payload into target process at %p\n",
+ cbData, pbTarget));
+
+ SetLastError(NO_ERROR);
+ return pbTarget;
+}
+
+static BOOL s_fSearchedForHelper = FALSE;
+static PDETOUR_EXE_HELPER s_pHelper = NULL;
+
+VOID CALLBACK DetourFinishHelperProcess(_In_ HWND,
+ _In_ HINSTANCE,
+ _In_ LPSTR,
+ _In_ INT)
+{
+ LPCSTR * rlpDlls = NULL;
+ DWORD Result = 9900;
+ DWORD cOffset = 0;
+ DWORD cSize = 0;
+ HANDLE hProcess = NULL;
+
+ if (s_pHelper == NULL) {
+ DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n"));
+ Result = 9905;
+ goto Cleanup;
+ }
+
+ hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid);
+ if (hProcess == NULL) {
+ DETOUR_TRACE(("OpenProcess(pid=%lu) failed: %lu\n",
+ s_pHelper->pid, GetLastError()));
+ Result = 9901;
+ goto Cleanup;
+ }
+
+ rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls];
+ cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER);
+ for (DWORD n = 0; n < s_pHelper->nDlls; n++) {
+ size_t cchDest = 0;
+ HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest);
+ if (!SUCCEEDED(hr)) {
+ Result = 9902;
+ goto Cleanup;
+ }
+
+ rlpDlls[n] = &s_pHelper->rDlls[cOffset];
+ cOffset += (DWORD)cchDest + 1;
+ }
+
+ if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) {
+ DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%lu) failed: %lu\n",
+ s_pHelper->pid, GetLastError()));
+ Result = 9903;
+ goto Cleanup;
+ }
+ Result = 0;
+
+ Cleanup:
+ if (rlpDlls != NULL) {
+ delete[] rlpDlls;
+ rlpDlls = NULL;
+ }
+
+ // Note: s_pHelper is allocated as part of injecting the payload in DetourCopyPayloadToProcess(..),
+ // it's a fake section and not data allocated by the system PE loader.
+
+ // Delete the payload after execution to release the memory occupied by it
+ if (s_pHelper != NULL) {
+ DetourFreePayload(s_pHelper);
+ s_pHelper = NULL;
+ }
+
+ ExitProcess(Result);
+}
+
+BOOL WINAPI DetourIsHelperProcess(VOID)
+{
+ PVOID pvData;
+ DWORD cbData;
+
+ if (s_fSearchedForHelper) {
+ return (s_pHelper != NULL);
+ }
+
+ s_fSearchedForHelper = TRUE;
+ pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData);
+
+ if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) {
+ return FALSE;
+ }
+
+ s_pHelper = (PDETOUR_EXE_HELPER)pvData;
+ if (s_pHelper->cb < sizeof(*s_pHelper)) {
+ s_pHelper = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static
+BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper,
+ _In_ DWORD dwTargetPid,
+ _In_ DWORD nDlls,
+ _In_reads_(nDlls) LPCSTR *rlpDlls)
+{
+ PDETOUR_EXE_HELPER Helper = NULL;
+ BOOL Result = FALSE;
+ _Field_range_(0, cSize - 4) DWORD cOffset = 0;
+ DWORD cSize = 4;
+
+ if (pHelper == NULL) {
+ goto Cleanup;
+ }
+ *pHelper = NULL;
+
+ if (nDlls < 1 || nDlls > 4096) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto Cleanup;
+ }
+
+ for (DWORD n = 0; n < nDlls; n++) {
+ HRESULT hr;
+ size_t cchDest = 0;
+
+ hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest);
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+ cSize += (DWORD)cchDest + 1;
+ }
+
+ Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize];
+ if (Helper == NULL) {
+ goto Cleanup;
+ }
+
+ Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize;
+ Helper->pid = dwTargetPid;
+ Helper->nDlls = nDlls;
+
+ for (DWORD n = 0; n < nDlls; n++) {
+ HRESULT hr;
+ size_t cchDest = 0;
+
+ if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) {
+ goto Cleanup;
+ }
+
+ if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) {
+ goto Cleanup;
+ }
+
+ _Analysis_assume_(cOffset + 1 < cSize);
+ _Analysis_assume_(cOffset < 0x10000);
+ _Analysis_assume_(cSize < 0x10000);
+
+ PCHAR psz = &Helper->rDlls[cOffset];
+
+ hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]);
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call.
+// REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program.
+#pragma warning(suppress:28020 28313)
+ hr = StringCchLengthA(psz, cSize - cOffset, &cchDest);
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+ // Replace "32." with "64." or "64." with "32."
+
+ for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) {
+#if DETOURS_32BIT
+ if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') {
+ psz[c - 3] = '6'; psz[c - 2] = '4';
+ break;
+ }
+#else
+ if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') {
+ psz[c - 3] = '3'; psz[c - 2] = '2';
+ break;
+ }
+#endif
+ }
+
+ cOffset += (DWORD)cchDest + 1;
+ }
+
+ *pHelper = Helper;
+ Helper = NULL;
+ Result = TRUE;
+
+ Cleanup:
+ if (Helper != NULL) {
+ delete[] (PBYTE)Helper;
+ Helper = NULL;
+ }
+ return Result;
+}
+
+static
+VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper)
+{
+ if (*pHelper != NULL) {
+ delete[] (PBYTE)*pHelper;
+ *pHelper = NULL;
+ }
+}
+
+BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid,
+ _In_ LPCSTR lpDllName,
+ _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
+{
+ return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA);
+}
+
+
+BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,
+ _In_ DWORD nDlls,
+ _In_reads_(nDlls) LPCSTR *rlpDlls,
+ _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
+{
+ BOOL Result = FALSE;
+ PROCESS_INFORMATION pi;
+ STARTUPINFOA si;
+ CHAR szExe[MAX_PATH];
+ CHAR szCommand[MAX_PATH];
+ PDETOUR_EXE_HELPER helper = NULL;
+ HRESULT hr;
+ DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe));
+
+ DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls));
+ if (nDlls < 1 || nDlls > 4096) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto Cleanup;
+ }
+ if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {
+ goto Cleanup;
+ }
+
+ if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {
+ goto Cleanup;
+ }
+
+#if DETOURS_OPTION_BITS
+#if DETOURS_32BIT
+ hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe");
+#else // !DETOURS_32BIT
+ hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe");
+#endif // !DETOURS_32BIT
+#else // DETOURS_OPTIONS_BITS
+ hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe");
+#endif // DETOURS_OPTIONS_BITS
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+ //for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP"));
+ //so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before
+ hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand),
+ "rundll32.exe \"%s\",#1", &helper->rDlls[0]);
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand));
+ if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi)) {
+
+ if (!DetourCopyPayloadToProcess(pi.hProcess,
+ DETOUR_EXE_HELPER_GUID,
+ helper, helper->cb)) {
+ DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
+ TerminateProcess(pi.hProcess, ~0u);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ goto Cleanup;
+ }
+
+ ResumeThread(pi.hThread);
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ DWORD dwResult = 500;
+ GetExitCodeProcess(pi.hProcess, &dwResult);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ if (dwResult != 0) {
+ DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult));
+ goto Cleanup;
+ }
+ Result = TRUE;
+ }
+ else {
+ DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError()));
+ goto Cleanup;
+ }
+
+ Cleanup:
+ FreeExeHelper(&helper);
+ return Result;
+}
+
+BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid,
+ _In_ LPCSTR lpDllName,
+ _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
+{
+ return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW);
+}
+
+BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid,
+ _In_ DWORD nDlls,
+ _In_reads_(nDlls) LPCSTR *rlpDlls,
+ _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
+{
+ BOOL Result = FALSE;
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+ WCHAR szExe[MAX_PATH];
+ WCHAR szCommand[MAX_PATH];
+ PDETOUR_EXE_HELPER helper = NULL;
+ HRESULT hr;
+ WCHAR szDllName[MAX_PATH];
+ int cchWrittenWideChar;
+ DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe));
+
+ DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls));
+ if (nDlls < 1 || nDlls > 4096) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto Cleanup;
+ }
+ if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {
+ goto Cleanup;
+ }
+
+ if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {
+ goto Cleanup;
+ }
+
+#if DETOURS_OPTION_BITS
+#if DETOURS_32BIT
+ hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe");
+#else // !DETOURS_32BIT
+ hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe");
+#endif // !DETOURS_32BIT
+#else // DETOURS_OPTIONS_BITS
+ hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe");
+#endif // DETOURS_OPTIONS_BITS
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+ //for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP"));
+ //so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before
+
+ cchWrittenWideChar = MultiByteToWideChar(CP_ACP, 0, &helper->rDlls[0], -1, szDllName, ARRAYSIZE(szDllName));
+ if (cchWrittenWideChar >= ARRAYSIZE(szDllName) || cchWrittenWideChar <= 0) {
+ goto Cleanup;
+ }
+ hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand),
+ L"rundll32.exe \"%s\",#1", szDllName);
+ if (!SUCCEEDED(hr)) {
+ goto Cleanup;
+ }
+
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand));
+ if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi)) {
+
+ if (!DetourCopyPayloadToProcess(pi.hProcess,
+ DETOUR_EXE_HELPER_GUID,
+ helper, helper->cb)) {
+ DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
+ TerminateProcess(pi.hProcess, ~0u);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ goto Cleanup;
+ }
+
+ ResumeThread(pi.hThread);
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ DWORD dwResult = 500;
+ GetExitCodeProcess(pi.hProcess, &dwResult);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ if (dwResult != 0) {
+ DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult));
+ goto Cleanup;
+ }
+ Result = TRUE;
+ }
+ else {
+ DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError()));
+ goto Cleanup;
+ }
+
+ Cleanup:
+ FreeExeHelper(&helper);
+ return Result;
+}
+
+BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName,
+ _Inout_opt_ LPSTR lpCommandLine,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ _In_ BOOL bInheritHandles,
+ _In_ DWORD dwCreationFlags,
+ _In_opt_ LPVOID lpEnvironment,
+ _In_opt_ LPCSTR lpCurrentDirectory,
+ _In_ LPSTARTUPINFOA lpStartupInfo,
+ _Out_ LPPROCESS_INFORMATION lpProcessInformation,
+ _In_ LPCSTR lpDllName,
+ _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
+{
+ if (pfCreateProcessA == NULL) {
+ pfCreateProcessA = CreateProcessA;
+ }
+
+ PROCESS_INFORMATION backup;
+ if (lpProcessInformation == NULL) {
+ lpProcessInformation = &backup;
+ ZeroMemory(&backup, sizeof(backup));
+ }
+
+ if (!pfCreateProcessA(lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags | CREATE_SUSPENDED,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation)) {
+ return FALSE;
+ }
+
+ LPCSTR szDll = lpDllName;
+
+ if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) &&
+ !DetourProcessViaHelperA(lpProcessInformation->dwProcessId,
+ lpDllName,
+ pfCreateProcessA)) {
+
+ TerminateProcess(lpProcessInformation->hProcess, ~0u);
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ return FALSE;
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED)) {
+ ResumeThread(lpProcessInformation->hThread);
+ }
+
+ if (lpProcessInformation == &backup) {
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ }
+
+ return TRUE;
+}
+
+BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName,
+ _Inout_opt_ LPWSTR lpCommandLine,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ _In_ BOOL bInheritHandles,
+ _In_ DWORD dwCreationFlags,
+ _In_opt_ LPVOID lpEnvironment,
+ _In_opt_ LPCWSTR lpCurrentDirectory,
+ _In_ LPSTARTUPINFOW lpStartupInfo,
+ _Out_ LPPROCESS_INFORMATION lpProcessInformation,
+ _In_ LPCSTR lpDllName,
+ _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
+{
+ if (pfCreateProcessW == NULL) {
+ pfCreateProcessW = CreateProcessW;
+ }
+
+ PROCESS_INFORMATION backup;
+ if (lpProcessInformation == NULL) {
+ lpProcessInformation = &backup;
+ ZeroMemory(&backup, sizeof(backup));
+ }
+
+ if (!pfCreateProcessW(lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags | CREATE_SUSPENDED,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation)) {
+ return FALSE;
+ }
+
+
+ LPCSTR sz = lpDllName;
+
+ if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) &&
+ !DetourProcessViaHelperW(lpProcessInformation->dwProcessId,
+ lpDllName,
+ pfCreateProcessW)) {
+
+ TerminateProcess(lpProcessInformation->hProcess, ~0u);
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ return FALSE;
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED)) {
+ ResumeThread(lpProcessInformation->hThread);
+ }
+
+ if (lpProcessInformation == &backup) {
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ }
+ return TRUE;
+}
+
+BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,
+ _Inout_opt_ LPSTR lpCommandLine,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ _In_ BOOL bInheritHandles,
+ _In_ DWORD dwCreationFlags,
+ _In_opt_ LPVOID lpEnvironment,
+ _In_opt_ LPCSTR lpCurrentDirectory,
+ _In_ LPSTARTUPINFOA lpStartupInfo,
+ _Out_ LPPROCESS_INFORMATION lpProcessInformation,
+ _In_ DWORD nDlls,
+ _In_reads_(nDlls) LPCSTR *rlpDlls,
+ _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
+{
+ if (pfCreateProcessA == NULL) {
+ pfCreateProcessA = CreateProcessA;
+ }
+
+ PROCESS_INFORMATION backup;
+ if (lpProcessInformation == NULL) {
+ lpProcessInformation = &backup;
+ ZeroMemory(&backup, sizeof(backup));
+ }
+
+ if (!pfCreateProcessA(lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags | CREATE_SUSPENDED,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation)) {
+ return FALSE;
+ }
+
+ if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
+ !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId,
+ nDlls,
+ rlpDlls,
+ pfCreateProcessA)) {
+
+ TerminateProcess(lpProcessInformation->hProcess, ~0u);
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ return FALSE;
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED)) {
+ ResumeThread(lpProcessInformation->hThread);
+ }
+
+ if (lpProcessInformation == &backup) {
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ }
+
+ return TRUE;
+}
+
+BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName,
+ _Inout_opt_ LPWSTR lpCommandLine,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ _In_ BOOL bInheritHandles,
+ _In_ DWORD dwCreationFlags,
+ _In_opt_ LPVOID lpEnvironment,
+ _In_opt_ LPCWSTR lpCurrentDirectory,
+ _In_ LPSTARTUPINFOW lpStartupInfo,
+ _Out_ LPPROCESS_INFORMATION lpProcessInformation,
+ _In_ DWORD nDlls,
+ _In_reads_(nDlls) LPCSTR *rlpDlls,
+ _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
+{
+ if (pfCreateProcessW == NULL) {
+ pfCreateProcessW = CreateProcessW;
+ }
+
+ PROCESS_INFORMATION backup;
+ if (lpProcessInformation == NULL) {
+ lpProcessInformation = &backup;
+ ZeroMemory(&backup, sizeof(backup));
+ }
+
+ if (!pfCreateProcessW(lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags | CREATE_SUSPENDED,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation)) {
+ return FALSE;
+ }
+
+
+ if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
+ !DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId,
+ nDlls,
+ rlpDlls,
+ pfCreateProcessW)) {
+
+ TerminateProcess(lpProcessInformation->hProcess, ~0u);
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ return FALSE;
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED)) {
+ ResumeThread(lpProcessInformation->hThread);
+ }
+
+ if (lpProcessInformation == &backup) {
+ CloseHandle(lpProcessInformation->hProcess);
+ CloseHandle(lpProcessInformation->hThread);
+ }
+ return TRUE;
+}
+
+//
+///////////////////////////////////////////////////////////////// End of File.
diff --git a/r5dev/thirdparty/detours/src/uimports.cpp b/r5dev/thirdparty/detours/src/uimports.cpp
new file mode 100644
index 00000000..cd1fe0f8
--- /dev/null
+++ b/r5dev/thirdparty/detours/src/uimports.cpp
@@ -0,0 +1,335 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// Add DLLs to a module import table (uimports.cpp of detours.lib)
+//
+// Microsoft Research Detours Package, Version 4.0.1
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Note that this file is included into creatwth.cpp one or more times
+// (once for each supported module format).
+//
+
+#include "../include/detours.h"
+
+#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
+#error detours.h version mismatch
+#endif
+
+// UpdateImports32 aka UpdateImports64
+static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess,
+ HMODULE hModule,
+ __in_ecount(nDlls) LPCSTR *plpDlls,
+ DWORD nDlls)
+{
+ BOOL fSucceeded = FALSE;
+ DWORD cbNew = 0;
+
+ BYTE * pbNew = NULL;
+ DWORD i;
+ SIZE_T cbRead;
+ DWORD n;
+
+ PBYTE pbModule = (PBYTE)hModule;
+
+ IMAGE_DOS_HEADER idh;
+ ZeroMemory(&idh, sizeof(idh));
+ if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead)
+ || cbRead < sizeof(idh)) {
+
+ DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
+ pbModule, pbModule + sizeof(idh), GetLastError()));
+
+ finish:
+ if (pbNew != NULL) {
+ delete[] pbNew;
+ pbNew = NULL;
+ }
+ return fSucceeded;
+ }
+
+ IMAGE_NT_HEADERS_XX inh;
+ ZeroMemory(&inh, sizeof(inh));
+
+ if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead)
+ || cbRead < sizeof(inh)) {
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
+ pbModule + idh.e_lfanew,
+ pbModule + idh.e_lfanew + sizeof(inh),
+ GetLastError()));
+ goto finish;
+ }
+
+ if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) {
+ DETOUR_TRACE(("Wrong size image (%04x != %04x).\n",
+ inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX));
+ SetLastError(ERROR_INVALID_BLOCK);
+ goto finish;
+ }
+
+ // Zero out the bound table so loader doesn't use it instead of our new table.
+ inh.BOUND_DIRECTORY.VirtualAddress = 0;
+ inh.BOUND_DIRECTORY.Size = 0;
+
+ // Find the size of the mapped file.
+ DWORD dwSec = idh.e_lfanew +
+ FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) +
+ inh.FileHeader.SizeOfOptionalHeader;
+
+ for (i = 0; i < inh.FileHeader.NumberOfSections; i++) {
+ IMAGE_SECTION_HEADER ish;
+ ZeroMemory(&ish, sizeof(ish));
+
+ if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish,
+ sizeof(ish), &cbRead)
+ || cbRead < sizeof(ish)) {
+
+ DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
+ pbModule + dwSec + sizeof(ish) * i,
+ pbModule + dwSec + sizeof(ish) * (i + 1),
+ GetLastError()));
+ goto finish;
+ }
+
+ DETOUR_TRACE(("ish[%lu] : va=%08lx sr=%lu\n", i, ish.VirtualAddress, ish.SizeOfRawData));
+
+ // If the linker didn't suggest an IAT in the data directories, the
+ // loader will look for the section of the import directory to be used
+ // for this instead. Since we put out new IMPORT_DIRECTORY outside any
+ // section boundary, the loader will not find it. So we provide one
+ // explicitly to avoid the search.
+ //
+ if (inh.IAT_DIRECTORY.VirtualAddress == 0 &&
+ inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress &&
+ inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) {
+
+ inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress;
+ inh.IAT_DIRECTORY.Size = ish.SizeOfRawData;
+ }
+ }
+
+ if (inh.IMPORT_DIRECTORY.VirtualAddress != 0 && inh.IMPORT_DIRECTORY.Size == 0) {
+
+ // Don't worry about changing the PE file,
+ // because the load information of the original PE header has been saved and will be restored.
+ // The change here is just for the following code to work normally
+
+ PIMAGE_IMPORT_DESCRIPTOR pImageImport = (PIMAGE_IMPORT_DESCRIPTOR)(pbModule + inh.IMPORT_DIRECTORY.VirtualAddress);
+
+ do {
+ IMAGE_IMPORT_DESCRIPTOR ImageImport;
+ if (!ReadProcessMemory(hProcess, pImageImport, &ImageImport, sizeof(ImageImport), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+ inh.IMPORT_DIRECTORY.Size += sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ if (!ImageImport.Name) {
+ break;
+ }
+ ++pImageImport;
+ } while (TRUE);
+
+ DWORD dwLastError = GetLastError();
+ OutputDebugString(TEXT("[This PE file has an import table, but the import table size is marked as 0. This is an error.")
+ TEXT("If it is not repaired, the launched program will not work properly, Detours has automatically repaired its import table size for you! ! !]\r\n"));
+ if (GetLastError() != dwLastError) {
+ SetLastError(dwLastError);
+ }
+ }
+
+ DETOUR_TRACE((" Imports: %p..%p\n",
+ pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
+ pbModule + inh.IMPORT_DIRECTORY.VirtualAddress +
+ inh.IMPORT_DIRECTORY.Size));
+
+ // Calculate new import directory size. Note that since inh is from another
+ // process, inh could have been corrupted. We need to protect against
+ // integer overflow in allocation calculations.
+ DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ DWORD obRem;
+ if (DWordMult(sizeof(IMAGE_IMPORT_DESCRIPTOR), nDlls, &obRem) != S_OK) {
+ DETOUR_TRACE(("too many new DLLs.\n"));
+ goto finish;
+ }
+ DWORD obOld;
+ if (DWordAdd(obRem, sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls, &obOld) != S_OK) {
+ DETOUR_TRACE(("DLL entries overflow.\n"));
+ goto finish;
+ }
+ DWORD obTab = PadToDwordPtr(obOld);
+ // Check for integer overflow.
+ if (obTab < obOld) {
+ DETOUR_TRACE(("DLL entries padding overflow.\n"));
+ goto finish;
+ }
+ DWORD stSize;
+ if (DWordMult(sizeof(DWORD_XX) * 4, nDlls, &stSize) != S_OK) {
+ DETOUR_TRACE(("String table overflow.\n"));
+ goto finish;
+ }
+ DWORD obDll;
+ if (DWordAdd(obTab, stSize, &obDll) != S_OK) {
+ DETOUR_TRACE(("Import table size overflow\n"));
+ goto finish;
+ }
+ DWORD obStr = obDll;
+ cbNew = obStr;
+ for (n = 0; n < nDlls; n++) {
+ if (DWordAdd(cbNew, PadToDword((DWORD)strlen(plpDlls[n]) + 1), &cbNew) != S_OK) {
+ DETOUR_TRACE(("Overflow adding string table entry\n"));
+ goto finish;
+ }
+ }
+ pbNew = new BYTE [cbNew];
+ if (pbNew == NULL) {
+ DETOUR_TRACE(("new BYTE [cbNew] failed.\n"));
+ goto finish;
+ }
+ ZeroMemory(pbNew, cbNew);
+
+ PBYTE pbBase = pbModule;
+ PBYTE pbNext = pbBase
+ + inh.OptionalHeader.BaseOfCode
+ + inh.OptionalHeader.SizeOfCode
+ + inh.OptionalHeader.SizeOfInitializedData
+ + inh.OptionalHeader.SizeOfUninitializedData;
+ if (pbBase < pbNext) {
+ pbBase = pbNext;
+ }
+ DETOUR_TRACE(("pbBase = %p\n", pbBase));
+
+ PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbModule, pbBase, cbNew);
+ if (pbNewIid == NULL) {
+ DETOUR_TRACE(("FindAndAllocateNearBase failed.\n"));
+ goto finish;
+ }
+
+ PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew;
+ IMAGE_THUNK_DATAXX *pt = NULL;
+
+ DWORD obBase = (DWORD)(pbNewIid - pbModule);
+ DWORD dwProtect = 0;
+
+ if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) {
+ // Read the old import directory if it exists.
+ DETOUR_TRACE(("IMPORT_DIRECTORY perms=%lx\n", dwProtect));
+
+ if (!ReadProcessMemory(hProcess,
+ pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
+ &piid[nDlls],
+ nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead)
+ || cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
+
+ DETOUR_TRACE(("ReadProcessMemory(imports) failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+ }
+
+ for (n = 0; n < nDlls; n++) {
+ HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]);
+ if (FAILED(hrRet)) {
+ DETOUR_TRACE(("StringCchCopyA failed: %08lx\n", hrRet));
+ goto finish;
+ }
+
+ // After copying the string, we patch up the size "??" bits if any.
+ hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr,
+ cbNew - obStr,
+ DETOURS_STRINGIFY(DETOURS_BITS_XX));
+ if (FAILED(hrRet)) {
+ DETOUR_TRACE(("ReplaceOptionalSizeA failed: %08lx\n", hrRet));
+ goto finish;
+ }
+
+ DWORD nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * (4 * n));
+ piid[n].OriginalFirstThunk = obBase + nOffset;
+
+ // We need 2 thunks for the import table and 2 thunks for the IAT.
+ // One for an ordinal import and one to mark the end of the list.
+ pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset));
+ pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1;
+ pt[1].u1.Ordinal = 0;
+
+ nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * ((4 * n) + 2));
+ piid[n].FirstThunk = obBase + nOffset;
+ pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset));
+ pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1;
+ pt[1].u1.Ordinal = 0;
+ piid[n].TimeDateStamp = 0;
+ piid[n].ForwarderChain = 0;
+ piid[n].Name = obBase + obStr;
+
+ obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1);
+ }
+ _Analysis_assume_(obStr <= cbNew);
+
+#if 0
+ for (i = 0; i < nDlls + nOldDlls; i++) {
+ DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n",
+ i,
+ piid[i].OriginalFirstThunk,
+ piid[i].TimeDateStamp,
+ piid[i].ForwarderChain,
+ piid[i].Name,
+ piid[i].FirstThunk));
+ if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) {
+ break;
+ }
+ }
+#endif
+
+ if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(iid) failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+
+ DETOUR_TRACE(("obBaseBef = %08lx..%08lx\n",
+ inh.IMPORT_DIRECTORY.VirtualAddress,
+ inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size));
+ DETOUR_TRACE(("obBaseAft = %08lx..%08lx\n", obBase, obBase + obStr));
+
+ // In this case the file didn't have an import directory in first place,
+ // so we couldn't fix the missing IAT above. We still need to explicitly
+ // provide an IAT to prevent to loader from looking for one.
+ //
+ if (inh.IAT_DIRECTORY.VirtualAddress == 0) {
+ inh.IAT_DIRECTORY.VirtualAddress = obBase;
+ inh.IAT_DIRECTORY.Size = cbNew;
+ }
+
+ inh.IMPORT_DIRECTORY.VirtualAddress = obBase;
+ inh.IMPORT_DIRECTORY.Size = cbNew;
+
+ /////////////////////// Update the NT header for the new import directory.
+ //
+ if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,
+ PAGE_EXECUTE_READWRITE, &dwProtect)) {
+ DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+
+ inh.OptionalHeader.CheckSum = 0;
+
+ if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+ DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh)));
+
+ if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(inh) failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+ DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n",
+ pbModule + idh.e_lfanew,
+ pbModule + idh.e_lfanew + sizeof(inh)));
+
+ if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,
+ dwProtect, &dwProtect)) {
+ DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %lu\n", GetLastError()));
+ goto finish;
+ }
+
+ fSucceeded = TRUE;
+ goto finish;
+}
diff --git a/r5dev/thirdparty/lzham/include/lzham_assert.h b/r5dev/thirdparty/lzham/include/lzham_assert.h
new file mode 100644
index 00000000..d8a68515
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_assert.h
@@ -0,0 +1,40 @@
+// File: lzham_assert.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+const unsigned int LZHAM_FAIL_EXCEPTION_CODE = 256U;
+void lzham_enable_fail_exceptions(bool enabled);
+
+void lzham_assert(const char* pExp, const char* pFile, unsigned line);
+void lzham_fail(const char* pExp, const char* pFile, unsigned line);
+
+#ifdef NDEBUG
+ #define LZHAM_ASSERT(x) ((void)0)
+#else
+ #define LZHAM_ASSERT(_exp) (void)( (!!(_exp)) || (lzham_assert(#_exp, __FILE__, __LINE__), 0) )
+ #define LZHAM_ASSERTS_ENABLED 1
+#endif
+
+#define LZHAM_VERIFY(_exp) (void)( (!!(_exp)) || (lzham_assert(#_exp, __FILE__, __LINE__), 0) )
+
+#define LZHAM_FAIL(msg) do { lzham_fail(#msg, __FILE__, __LINE__); } while(0)
+
+#define LZHAM_ASSERT_OPEN_RANGE(x, l, h) LZHAM_ASSERT((x >= l) && (x < h))
+#define LZHAM_ASSERT_CLOSED_RANGE(x, l, h) LZHAM_ASSERT((x >= l) && (x <= h))
+
+void lzham_trace(const char* pFmt, va_list args);
+void lzham_trace(const char* pFmt, ...);
+
+// Borrowed from boost libraries.
+template struct assume_failure;
+template <> struct assume_failure { enum { blah = 1 }; };
+template struct assume_try { };
+
+#define LZHAM_JOINER_FINAL(a, b) a##b
+#define LZHAM_JOINER(a, b) LZHAM_JOINER_FINAL(a, b)
+#define LZHAM_JOIN(a, b) LZHAM_JOINER(a, b)
+#if defined(__GNUC__)
+ #define LZHAM_ASSUME(p) typedef assume_try < sizeof(assume_failure< (bool)(p) > ) > LZHAM_JOIN(assume_typedef, __COUNTER__) __attribute__((unused))
+#else
+ #define LZHAM_ASSUME(p) typedef assume_try < sizeof(assume_failure< (bool)(p) > ) > LZHAM_JOIN(assume_typedef, __COUNTER__)
+#endif
diff --git a/r5dev/thirdparty/lzham/include/lzham_checksum.h b/r5dev/thirdparty/lzham/include/lzham_checksum.h
new file mode 100644
index 00000000..515f3389
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_checksum.h
@@ -0,0 +1,13 @@
+// File: lzham_checksum.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ const uint cInitAdler32 = 1U;
+ uint adler32(const void* pBuf, size_t buflen, uint adler32 = cInitAdler32);
+
+ const uint cInitCRC32 = 0U;
+ uint crc32(uint crc, const lzham_uint8 *ptr, size_t buf_len);
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_config.h b/r5dev/thirdparty/lzham/include/lzham_config.h
new file mode 100644
index 00000000..e250c7ce
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_config.h
@@ -0,0 +1,23 @@
+// File: lzham_config.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#ifdef _DEBUG
+ #define LZHAM_BUILD_DEBUG
+
+ #ifndef DEBUG
+ #define DEBUG
+ #endif
+#else
+ #define LZHAM_BUILD_RELEASE
+
+ #ifndef NDEBUG
+ #define NDEBUG
+ #endif
+
+ #ifdef DEBUG
+ #error DEBUG cannot be defined in LZHAM_BUILD_RELEASE
+ #endif
+#endif
+#define LZHAM_BUFFERED_PRINTF 0
+#define LZHAM_PERF_SECTIONS 0
\ No newline at end of file
diff --git a/r5dev/thirdparty/lzham/include/lzham_core.h b/r5dev/thirdparty/lzham/include/lzham_core.h
new file mode 100644
index 00000000..3d4f3d3e
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_core.h
@@ -0,0 +1,170 @@
+// File: lzham_core.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+#include "core/stdafx.h"
+
+#if defined(_MSC_VER)
+ #pragma warning (disable: 4127) // conditional expression is constant
+#endif
+
+#if defined(_XBOX) && !defined(LZHAM_ANSI_CPLUSPLUS)
+ // X360
+ #include
+ #define _HAS_EXCEPTIONS 0
+ #define NOMINMAX
+
+ #define LZHAM_PLATFORM_X360 1
+ #define LZHAM_USE_WIN32_API 1
+ #define LZHAM_USE_WIN32_ATOMIC_FUNCTIONS 1
+ #define LZHAM_64BIT_POINTERS 0
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 1
+ #define LZHAM_BIG_ENDIAN_CPU 1
+ #define LZHAM_USE_UNALIGNED_INT_LOADS 1
+ #define LZHAM_RESTRICT __restrict
+ #define LZHAM_FORCE_INLINE __forceinline
+ #define LZHAM_NOTE_UNUSED(x) (void)x
+
+#elif defined(WIN32) && !defined(LZHAM_ANSI_CPLUSPLUS)
+ // MSVC or MinGW, x86 or x64, Win32 API's for threading and Win32 Interlocked API's or GCC built-ins for atomic ops.
+ #ifdef NDEBUG
+ // Ensure checked iterators are disabled.
+ #define _SECURE_SCL 0
+ #define _HAS_ITERATOR_DEBUGGING 0
+ #endif
+ #ifndef _DLL
+ // If we're using the DLL form of the run-time libs, we're also going to be enabling exceptions because we'll be building CLR apps.
+ // Otherwise, we disable exceptions for a small speed boost.
+ //#define _HAS_EXCEPTIONS 0
+ #endif
+ #define NOMINMAX
+
+ #ifndef _WIN32_WINNT
+ #define _WIN32_WINNT 0x500
+ #endif
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+
+ #include
+
+ #define LZHAM_USE_WIN32_API 1
+
+ #if defined(__MINGW32__) || defined(__MINGW64__)
+ #define LZHAM_USE_GCC_ATOMIC_BUILTINS 1
+ #else
+ #define LZHAM_USE_WIN32_ATOMIC_FUNCTIONS 1
+ #endif
+
+ #define LZHAM_PLATFORM_PC 1
+
+ #ifdef _WIN64
+ #define LZHAM_PLATFORM_PC_X64 1
+ #define LZHAM_64BIT_POINTERS 1
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 1
+ #define LZHAM_LITTLE_ENDIAN_CPU 1
+ #else
+ #define LZHAM_PLATFORM_PC_X86 1
+ #define LZHAM_64BIT_POINTERS 0
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 0
+ #define LZHAM_LITTLE_ENDIAN_CPU 1
+ #endif
+
+ #define LZHAM_USE_UNALIGNED_INT_LOADS 1
+ #define LZHAM_RESTRICT __restrict
+ #define LZHAM_FORCE_INLINE __forceinline
+
+ #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
+ #define LZHAM_USE_MSVC_INTRINSICS 1
+ #endif
+
+ #define LZHAM_NOTE_UNUSED(x) (void)x
+
+#elif defined(__GNUC__) && !defined(LZHAM_ANSI_CPLUSPLUS)
+ // GCC x86 or x64, pthreads for threading and GCC built-ins for atomic ops.
+ #define LZHAM_PLATFORM_PC 1
+
+ #if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__)
+ #define LZHAM_PLATFORM_PC_X64 1
+ #define LZHAM_64BIT_POINTERS 1
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 1
+ #else
+ #define LZHAM_PLATFORM_PC_X86 1
+ #define LZHAM_64BIT_POINTERS 0
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 0
+ #endif
+
+ #define LZHAM_USE_UNALIGNED_INT_LOADS 1
+
+ #define LZHAM_LITTLE_ENDIAN_CPU 1
+
+ #define LZHAM_USE_PTHREADS_API 1
+ #define LZHAM_USE_GCC_ATOMIC_BUILTINS 1
+
+ #define LZHAM_RESTRICT
+
+ #if defined(__clang__)
+ #define LZHAM_FORCE_INLINE inline
+ #else
+ #define LZHAM_FORCE_INLINE inline __attribute__((__always_inline__,__gnu_inline__))
+ #endif
+
+ #define LZHAM_NOTE_UNUSED(x) (void)x
+#else
+ // Vanilla ANSI-C/C++
+ // No threading support, unaligned loads are NOT okay.
+ #if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__)
+ #define LZHAM_64BIT_POINTERS 1
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 1
+ #else
+ #define LZHAM_64BIT_POINTERS 0
+ #define LZHAM_CPU_HAS_64BIT_REGISTERS 0
+ #endif
+
+ #define LZHAM_USE_UNALIGNED_INT_LOADS 0
+
+ #if __BIG_ENDIAN__
+ #define LZHAM_BIG_ENDIAN_CPU 1
+ #else
+ #define LZHAM_LITTLE_ENDIAN_CPU 1
+ #endif
+
+ #define LZHAM_USE_GCC_ATOMIC_BUILTINS 0
+ #define LZHAM_USE_WIN32_ATOMIC_FUNCTIONS 0
+
+ #define LZHAM_RESTRICT
+ #define LZHAM_FORCE_INLINE inline
+
+ #define LZHAM_NOTE_UNUSED(x) (void)x
+#endif
+
+#if LZHAM_LITTLE_ENDIAN_CPU
+ const bool c_lzham_little_endian_platform = true;
+#else
+ const bool c_lzham_little_endian_platform = false;
+#endif
+
+const bool c_lzham_big_endian_platform = !c_lzham_little_endian_platform;
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "lzham.h"
+#include "lzham_config.h"
+#include "lzham_types.h"
+#include "lzham_assert.h"
+#include "lzham_platform.h"
+
+#include "lzham_helpers.h"
+#include "lzham_traits.h"
+#include "lzham_mem.h"
+#include "lzham_math.h"
+#include "lzham_utils.h"
+#include "lzham_vector.h"
diff --git a/r5dev/thirdparty/lzham/include/lzham_helpers.h b/r5dev/thirdparty/lzham/include/lzham_helpers.h
new file mode 100644
index 00000000..11e0a119
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_helpers.h
@@ -0,0 +1,54 @@
+// File: lzham_helpers.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#define LZHAM_NO_COPY_OR_ASSIGNMENT_OP(c) c(const c&); c& operator= (const c&);
+
+namespace lzham
+{
+ namespace helpers
+ {
+ template struct rel_ops
+ {
+ friend inline bool operator!=(const T& x, const T& y) { return (!(x == y)); }
+ friend inline bool operator> (const T& x, const T& y) { return (y < x); }
+ friend inline bool operator<=(const T& x, const T& y) { return (!(y < x)); }
+ friend inline bool operator>=(const T& x, const T& y) { return (!(x < y)); }
+ };
+
+ template
+ inline T* construct(T* p)
+ {
+ return new (static_cast(p)) T;
+ }
+
+ template
+ inline T* construct(T* p, const U& init)
+ {
+ return new (static_cast(p)) T(init);
+ }
+
+ template
+ inline void construct_array(T* p, uint n);
+
+ template
+ inline void construct_array(T* p, uint n, const U& init)
+ {
+ T* q = p + n;
+ for ( ; p != q; ++p)
+ new (static_cast(p)) T(init);
+ }
+
+ template
+ inline void destruct(T* p)
+ {
+ LZHAM_NOTE_UNUSED(p);
+ p->~T();
+ }
+
+ template
+ inline void destruct_array(T* p, uint n);
+
+ } // namespace helpers
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_huffman_codes.h b/r5dev/thirdparty/lzham/include/lzham_huffman_codes.h
new file mode 100644
index 00000000..caab1a68
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_huffman_codes.h
@@ -0,0 +1,14 @@
+// File: lzham_huffman_codes.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ //const uint cHuffmanMaxSupportedSyms = 600;
+ const uint cHuffmanMaxSupportedSyms = 1024;
+
+ uint get_generate_huffman_codes_table_size();
+
+ bool generate_huffman_codes(void* pContext, uint num_syms, const uint16* pFreq, uint8* pCodesizes, uint& max_code_size, uint& total_freq_ret);
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_lzbase.h b/r5dev/thirdparty/lzham/include/lzham_lzbase.h
new file mode 100644
index 00000000..8904ddd4
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_lzbase.h
@@ -0,0 +1,45 @@
+// File: lzham_lzbase.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#include "../lzhamdecomp/lzham_lzdecompbase.h"
+
+//#define LZHAM_LZVERIFY
+//#define LZHAM_DISABLE_RAW_BLOCKS
+
+namespace lzham
+{
+ struct CLZBase : CLZDecompBase
+ {
+ uint8 m_slot_tab0[4096];
+ uint8 m_slot_tab1[512];
+ uint8 m_slot_tab2[256];
+
+ void init_slot_tabs();
+
+ inline void compute_lzx_position_slot(uint dist, uint& slot, uint& ofs)
+ {
+ uint s;
+ if (dist < 0x1000)
+ s = m_slot_tab0[dist];
+ else if (dist < 0x100000)
+ s = m_slot_tab1[dist >> 11];
+ else if (dist < 0x1000000)
+ s = m_slot_tab2[dist >> 16];
+ else if (dist < 0x2000000)
+ s = 48 + ((dist - 0x1000000) >> 23);
+ else if (dist < 0x4000000)
+ s = 50 + ((dist - 0x2000000) >> 24);
+ else
+ s = 52 + ((dist - 0x4000000) >> 25);
+
+ ofs = (dist - m_lzx_position_base[s]) & m_lzx_position_extra_mask[s];
+ slot = s;
+
+ LZHAM_ASSERT(s < m_num_lzx_slots);
+ LZHAM_ASSERT((m_lzx_position_base[slot] + ofs) == dist);
+ LZHAM_ASSERT(ofs < (1U << m_lzx_position_extra_bits[slot]));
+ }
+ };
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_match_accel.h b/r5dev/thirdparty/lzham/include/lzham_match_accel.h
new file mode 100644
index 00000000..384ea7dd
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_match_accel.h
@@ -0,0 +1,146 @@
+// File: lzham_match_accel.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+#include "lzham_lzbase.h"
+#include "lzham_threading.h"
+
+namespace lzham
+{
+ const uint cMatchAccelMaxSupportedProbes = 128;
+
+ struct node
+ {
+ uint m_left;
+ uint m_right;
+ };
+
+ LZHAM_DEFINE_BITWISE_MOVABLE(node);
+
+#pragma pack(push, 1)
+ struct dict_match
+ {
+ uint m_dist;
+ uint16 m_len;
+
+ inline uint get_dist() const { return m_dist & 0x7FFFFFFF; }
+ inline uint get_len() const { return m_len + 2; }
+ inline bool is_last() const { return (int)m_dist < 0; }
+ };
+#pragma pack(pop)
+
+ LZHAM_DEFINE_BITWISE_MOVABLE(dict_match);
+
+ class search_accelerator
+ {
+ public:
+ search_accelerator();
+
+ // If all_matches is true, the match finder returns all found matches with no filtering.
+ // Otherwise, the finder will tend to return lists of matches with mostly unique lengths.
+ // For each length, it will discard matches with worse distances (in the coding sense).
+ bool init(CLZBase* pLZBase, task_pool* pPool, uint max_helper_threads, uint max_dict_size, uint max_matches, bool all_matches, uint max_probes);
+
+ void reset();
+ void flush();
+
+ inline uint get_max_dict_size() const { return m_max_dict_size; }
+ inline uint get_max_dict_size_mask() const { return m_max_dict_size_mask; }
+ inline uint get_cur_dict_size() const { return m_cur_dict_size; }
+
+ inline uint get_lookahead_pos() const { return m_lookahead_pos; }
+ inline uint get_lookahead_size() const { return m_lookahead_size; }
+
+ inline uint get_char(int delta_pos) const { return m_dict[(m_lookahead_pos + delta_pos) & m_max_dict_size_mask]; }
+ inline uint get_char(uint cur_dict_pos, int delta_pos) const { return m_dict[(cur_dict_pos + delta_pos) & m_max_dict_size_mask]; }
+ inline const uint8* get_ptr(uint pos) const { return &m_dict[pos]; }
+
+ uint get_max_helper_threads() const { return m_max_helper_threads; }
+
+ inline uint operator[](uint pos) const { return m_dict[pos]; }
+
+ uint get_max_add_bytes() const;
+ bool add_bytes_begin(uint num_bytes, const uint8* pBytes);
+ inline atomic32_t get_num_completed_helper_threads() const { return m_num_completed_helper_threads; }
+ void add_bytes_end();
+
+ // Returns the lookahead's raw position/size/dict_size at the time add_bytes_begin() is called.
+ inline uint get_fill_lookahead_pos() const { return m_fill_lookahead_pos; }
+ inline uint get_fill_lookahead_size() const { return m_fill_lookahead_size; }
+ inline uint get_fill_dict_size() const { return m_fill_dict_size; }
+
+ uint get_len2_match(uint lookahead_ofs);
+ dict_match* find_matches(uint lookahead_ofs, bool spin = true);
+
+ void advance_bytes(uint num_bytes);
+
+ LZHAM_FORCE_INLINE uint get_match_len(uint lookahead_ofs, int dist, uint max_match_len, uint start_match_len = 0) const
+ {
+ LZHAM_ASSERT(lookahead_ofs < m_lookahead_size);
+ LZHAM_ASSERT(start_match_len <= max_match_len);
+ LZHAM_ASSERT(max_match_len <= (get_lookahead_size() - lookahead_ofs));
+
+ const int find_dict_size = m_cur_dict_size + lookahead_ofs;
+ if (dist > find_dict_size)
+ return 0;
+
+ const uint comp_pos = static_cast((m_lookahead_pos + lookahead_ofs - dist) & m_max_dict_size_mask);
+ const uint lookahead_pos = (m_lookahead_pos + lookahead_ofs) & m_max_dict_size_mask;
+
+ const uint8* pComp = &m_dict[comp_pos];
+ const uint8* pLookahead = &m_dict[lookahead_pos];
+
+ uint match_len;
+ for (match_len = start_match_len; match_len < max_match_len; match_len++)
+ if (pComp[match_len] != pLookahead[match_len])
+ break;
+
+ return match_len;
+ }
+
+ public:
+ CLZBase* m_pLZBase;
+ task_pool* m_pTask_pool;
+ uint m_max_helper_threads;
+
+ uint m_max_dict_size;
+ uint m_max_dict_size_mask;
+
+ uint m_lookahead_pos;
+ uint m_lookahead_size;
+
+ uint m_cur_dict_size;
+
+ lzham::vector m_dict;
+
+ enum { cHashSize = 65536 };
+ lzham::vector m_hash;
+ lzham::vector m_nodes;
+
+ lzham::vector m_matches;
+ lzham::vector m_match_refs;
+
+ lzham::vector m_hash_thread_index;
+
+ enum { cDigramHashSize = 4096 };
+ lzham::vector m_digram_hash;
+ lzham::vector m_digram_next;
+
+ uint m_fill_lookahead_pos;
+ uint m_fill_lookahead_size;
+ uint m_fill_dict_size;
+
+ uint m_max_probes;
+ uint m_max_matches;
+
+ bool m_all_matches;
+
+ volatile atomic32_t m_next_match_ref;
+
+ volatile atomic32_t m_num_completed_helper_threads;
+
+ void find_all_matches_callback(uint64 data, void* pData_ptr);
+ bool find_all_matches(uint num_bytes);
+ bool find_len2_matches();
+ };
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_math.h b/r5dev/thirdparty/lzham/include/lzham_math.h
new file mode 100644
index 00000000..299f299b
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_math.h
@@ -0,0 +1,113 @@
+// File: lzham_math.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#if defined(LZHAM_USE_MSVC_INTRINSICS) && !defined(__MINGW32__)
+ #include
+ #if defined(_MSC_VER)
+ #pragma intrinsic(_BitScanReverse)
+ #endif
+#endif
+
+namespace lzham
+{
+ namespace math
+ {
+ // Yes I know these should probably be pass by ref, not val:
+ // http://www.stepanovpapers.com/notes.pdf
+ // Just don't use them on non-simple (non built-in) types!
+ template inline T minimum(T a, T b) { return (a < b) ? a : b; }
+
+ template inline T minimum(T a, T b, T c) { return minimum(minimum(a, b), c); }
+
+ template inline T maximum(T a, T b) { return (a > b) ? a : b; }
+
+ template inline T maximum(T a, T b, T c) { return maximum(maximum(a, b), c); }
+
+ template inline T clamp(T value, T low, T high) { return (value < low) ? low : ((value > high) ? high : value); }
+
+ inline bool is_power_of_2(uint32 x) { return x && ((x & (x - 1U)) == 0U); }
+ inline bool is_power_of_2(uint64 x) { return x && ((x & (x - 1U)) == 0U); }
+
+ template inline T align_up_pointer(T p, uint alignment)
+ {
+ LZHAM_ASSERT(is_power_of_2(alignment));
+ ptr_bits_t q = reinterpret_cast(p);
+ q = (q + alignment - 1) & (~((uint_ptr)alignment - 1));
+ return reinterpret_cast(q);
+ }
+
+ // From "Hackers Delight"
+ // val remains unchanged if it is already a power of 2.
+ inline uint32 next_pow2(uint32 val)
+ {
+ val--;
+ val |= val >> 16;
+ val |= val >> 8;
+ val |= val >> 4;
+ val |= val >> 2;
+ val |= val >> 1;
+ return val + 1;
+ }
+
+ // val remains unchanged if it is already a power of 2.
+ inline uint64 next_pow2(uint64 val)
+ {
+ val--;
+ val |= val >> 32;
+ val |= val >> 16;
+ val |= val >> 8;
+ val |= val >> 4;
+ val |= val >> 2;
+ val |= val >> 1;
+ return val + 1;
+ }
+
+ inline uint floor_log2i(uint v)
+ {
+ uint l = 0;
+ while (v > 1U)
+ {
+ v >>= 1;
+ l++;
+ }
+ return l;
+ }
+
+ inline uint ceil_log2i(uint v)
+ {
+ uint l = floor_log2i(v);
+ if ((l != cIntBits) && (v > (1U << l)))
+ l++;
+ return l;
+ }
+
+ // Returns the total number of bits needed to encode v.
+ // This needs to be fast - it's used heavily when determining Polar codelengths.
+ inline uint total_bits(uint v)
+ {
+ unsigned long l = 0;
+#if defined(__MINGW32__)
+ if (v)
+ {
+ l = 32 -__builtin_clz(v);
+ }
+#elif defined(LZHAM_USE_MSVC_INTRINSICS)
+ if (_BitScanReverse(&l, v))
+ {
+ l++;
+ }
+#else
+ while (v > 0U)
+ {
+ v >>= 1;
+ l++;
+ }
+#endif
+ return l;
+ }
+
+ }
+
+} // namespace lzham
+
diff --git a/r5dev/thirdparty/lzham/include/lzham_mem.h b/r5dev/thirdparty/lzham/include/lzham_mem.h
new file mode 100644
index 00000000..d258efff
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_mem.h
@@ -0,0 +1,112 @@
+// File: lzham_mem.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ void lzham_mem_init();
+
+ void* lzham_malloc(size_t size, size_t* pActual_size = NULL);
+ void* lzham_realloc(void* p, size_t size, size_t* pActual_size = NULL, bool movable = true);
+ void lzham_free(void* p);
+ size_t lzham_msize(void* p);
+
+ template
+ inline T* lzham_new()
+ {
+ T* p = static_cast(lzham_malloc(sizeof(T)));
+ if (!p) return NULL;
+ if (LZHAM_IS_SCALAR_TYPE(T))
+ return p;
+ return helpers::construct(p);
+ }
+
+ template
+ inline T* lzham_new(const A& init0)
+ {
+ T* p = static_cast(lzham_malloc(sizeof(T)));
+ if (!p) return NULL;
+ return new (static_cast(p)) T(init0);
+ }
+
+ template
+ inline T* lzham_new(const A& init0, const B& init1)
+ {
+ T* p = static_cast(lzham_malloc(sizeof(T)));
+ if (!p) return NULL;
+ return new (static_cast(p)) T(init0, init1);
+ }
+
+ template
+ inline T* lzham_new(const A& init0, const B& init1, const C& init2)
+ {
+ T* p = static_cast(lzham_malloc(sizeof(T)));
+ if (!p) return NULL;
+ return new (static_cast(p)) T(init0, init1, init2);
+ }
+
+ template
+ inline T* lzham_new(const A& init0, const B& init1, const C& init2, const D& init3)
+ {
+ T* p = static_cast(lzham_malloc(sizeof(T)));
+ if (!p) return NULL;
+ return new (static_cast(p)) T(init0, init1, init2, init3);
+ }
+
+ template
+ inline T* lzham_new_array(uint32 num)
+ {
+ if (!num) num = 1;
+
+ uint8* q = static_cast(lzham_malloc(LZHAM_MIN_ALLOC_ALIGNMENT + sizeof(T) * num));
+ if (!q)
+ return NULL;
+
+ T* p = reinterpret_cast(q + LZHAM_MIN_ALLOC_ALIGNMENT);
+
+ reinterpret_cast(p)[-1] = num;
+ reinterpret_cast(p)[-2] = ~num;
+
+ if (!LZHAM_IS_SCALAR_TYPE(T))
+ {
+ helpers::construct_array(p, num);
+ }
+ return p;
+ }
+
+ template
+ inline void lzham_delete(T* p)
+ {
+ if (p)
+ {
+ if (!LZHAM_IS_SCALAR_TYPE(T))
+ {
+ helpers::destruct(p);
+ }
+ lzham_free(p);
+ }
+ }
+
+ template
+ inline void lzham_delete_array(T* p)
+ {
+ if (p)
+ {
+ const uint32 num = reinterpret_cast(p)[-1];
+ const uint32 num_check = reinterpret_cast(p)[-2];
+ LZHAM_ASSERT(num && (num == ~num_check));
+ if (num == ~num_check)
+ {
+ if (!LZHAM_IS_SCALAR_TYPE(T))
+ {
+ helpers::destruct_array(p, num);
+ }
+
+ lzham_free(reinterpret_cast(p) - LZHAM_MIN_ALLOC_ALIGNMENT);
+ }
+ }
+ }
+
+ void lzham_print_mem_stats();
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_null_threading.h b/r5dev/thirdparty/lzham/include/lzham_null_threading.h
new file mode 100644
index 00000000..00fb0337
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_null_threading.h
@@ -0,0 +1,97 @@
+// File: lzham_task_pool_null.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ class semaphore
+ {
+ LZHAM_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
+
+ public:
+ inline semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL)
+ {
+ initialCount, maximumCount, pName;
+ }
+
+ inline ~semaphore()
+ {
+ }
+
+ inline void release(long releaseCount = 1, long *pPreviousCount = NULL)
+ {
+ releaseCount, pPreviousCount;
+ }
+
+ inline bool wait(uint32 milliseconds = UINT32_MAX)
+ {
+ milliseconds;
+ return true;
+ }
+ };
+
+ class task_pool
+ {
+ public:
+ inline task_pool() { }
+ inline task_pool(uint num_threads) { num_threads; }
+ inline ~task_pool() { }
+
+ inline bool init(uint num_threads) { num_threads; return true; }
+ inline void deinit();
+
+ inline uint get_num_threads() const { return 0; }
+ inline uint get_num_outstanding_tasks() const { return 0; }
+
+ // C-style task callback
+ typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
+ inline bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL)
+ {
+ pFunc(data, pData_ptr);
+ return true;
+ }
+
+ class executable_task
+ {
+ public:
+ virtual void execute_task(uint64 data, void* pData_ptr) = 0;
+ };
+
+ // It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
+ inline bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL)
+ {
+ pObj->execute_task(data, pData_ptr);
+ return true;
+ }
+
+ template
+ inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL)
+ {
+ (pObject->*pObject_method)(data, pData_ptr);
+ return true;
+ }
+
+ template
+ inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL)
+ {
+ for (uint i = 0; i < num_tasks; i++)
+ {
+ (pObject->*pObject_method)(first_data + i, pData_ptr);
+ }
+ return true;
+ }
+
+ void join() { }
+ };
+
+ inline void lzham_sleep(unsigned int milliseconds)
+ {
+ milliseconds;
+ }
+
+ inline uint lzham_get_max_helper_threads()
+ {
+ return 0;
+ }
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_platform.h b/r5dev/thirdparty/lzham/include/lzham_platform.h
new file mode 100644
index 00000000..0cc58beb
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_platform.h
@@ -0,0 +1,284 @@
+// File: lzham_platform.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+bool lzham_is_debugger_present(void);
+void lzham_debug_break(void);
+void lzham_output_debug_string(const char* p);
+
+// actually in lzham_assert.cpp
+void lzham_assert(const char* pExp, const char* pFile, unsigned line);
+void lzham_fail(const char* pExp, const char* pFile, unsigned line);
+
+#ifdef WIN32
+ #define LZHAM_BREAKPOINT DebuggerBreak();
+ #define LZHAM_BUILTIN_EXPECT(c, v) c
+#elif defined(__GNUC__)
+ #define LZHAM_BREAKPOINT asm("int $3");
+ #define LZHAM_BUILTIN_EXPECT(c, v) __builtin_expect(c, v)
+#else
+ #define LZHAM_BREAKPOINT
+ #define LZHAM_BUILTIN_EXPECT(c, v) c
+#endif
+
+#if defined(__GNUC__) && LZHAM_PLATFORM_PC
+extern __inline__ __attribute__((__always_inline__,__gnu_inline__)) void lzham_yield_processor()
+{
+ __asm__ __volatile__("pause");
+}
+#elif LZHAM_PLATFORM_X360
+#define lzham_yield_processor() \
+ YieldProcessor(); \
+ __asm { or r0, r0, r0 } \
+ YieldProcessor(); \
+ __asm { or r1, r1, r1 } \
+ YieldProcessor(); \
+ __asm { or r0, r0, r0 } \
+ YieldProcessor(); \
+ __asm { or r1, r1, r1 } \
+ YieldProcessor(); \
+ __asm { or r0, r0, r0 } \
+ YieldProcessor(); \
+ __asm { or r1, r1, r1 } \
+ YieldProcessor(); \
+ __asm { or r0, r0, r0 } \
+ YieldProcessor(); \
+ __asm { or r1, r1, r1 }
+#else
+LZHAM_FORCE_INLINE void lzham_yield_processor()
+{
+#if LZHAM_USE_MSVC_INTRINSICS
+ #if LZHAM_PLATFORM_PC_X64
+ _mm_pause();
+ #else
+ YieldProcessor();
+ #endif
+#else
+ // No implementation
+#endif
+}
+#endif
+
+#ifndef _MSC_VER
+ int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...);
+ int vsprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, va_list args);
+#endif
+
+#if LZHAM_PLATFORM_X360
+ #define LZHAM_MEMORY_EXPORT_BARRIER MemoryBarrier();
+#else
+ // Barriers shouldn't be necessary on x86/x64.
+ // TODO: Should use __sync_synchronize() on other platforms that support GCC.
+ #define LZHAM_MEMORY_EXPORT_BARRIER
+#endif
+
+#if LZHAM_PLATFORM_X360
+ #define LZHAM_MEMORY_IMPORT_BARRIER MemoryBarrier();
+#else
+ // Barriers shouldn't be necessary on x86/x64.
+ // TODO: Should use __sync_synchronize() on other platforms that support GCC.
+ #define LZHAM_MEMORY_IMPORT_BARRIER
+#endif
+
+// Note: It's very important that LZHAM_READ_BIG_ENDIAN_UINT32() is fast on the target platform.
+// This is used to read every DWORD from the input stream.
+
+#if LZHAM_USE_UNALIGNED_INT_LOADS
+ #if LZHAM_BIG_ENDIAN_CPU
+ #define LZHAM_READ_BIG_ENDIAN_UINT32(p) *reinterpret_cast(p)
+ #else
+ #if defined(LZHAM_USE_MSVC_INTRINSICS)
+ #define LZHAM_READ_BIG_ENDIAN_UINT32(p) _byteswap_ulong(*reinterpret_cast(p))
+ #elif defined(__GNUC__)
+ #define LZHAM_READ_BIG_ENDIAN_UINT32(p) __builtin_bswap32(*reinterpret_cast(p))
+ #else
+ #define LZHAM_READ_BIG_ENDIAN_UINT32(p) utils::swap32(*reinterpret_cast(p))
+ #endif
+ #endif
+#else
+ #define LZHAM_READ_BIG_ENDIAN_UINT32(p) ((reinterpret_cast(p)[0] << 24) | (reinterpret_cast(p)[1] << 16) | (reinterpret_cast(p)[2] << 8) | (reinterpret_cast(p)[3]))
+#endif
+
+#if LZHAM_USE_WIN32_ATOMIC_FUNCTIONS
+ extern "C" __int64 _InterlockedCompareExchange64(__int64 volatile * Destination, __int64 Exchange, __int64 Comperand);
+ #if defined(_MSC_VER)
+ #pragma intrinsic(_InterlockedCompareExchange64)
+ #endif
+#endif // LZHAM_USE_WIN32_ATOMIC_FUNCTIONS
+
+namespace lzham
+{
+#if LZHAM_USE_WIN32_ATOMIC_FUNCTIONS
+ typedef LONG atomic32_t;
+ typedef LONGLONG atomic64_t;
+
+ // Returns the original value.
+ inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedCompareExchange(pDest, exchange, comparand);
+ }
+
+ // Returns the original value.
+ inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 7) == 0);
+ return _InterlockedCompareExchange64(pDest, exchange, comparand);
+ }
+
+ // Returns the resulting incremented value.
+ inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedIncrement(pDest);
+ }
+
+ // Returns the resulting decremented value.
+ inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedDecrement(pDest);
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedExchange(pDest, val);
+ }
+
+ // Returns the resulting value.
+ inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedExchangeAdd(pDest, val) + val;
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange_add(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedExchangeAdd(pDest, val);
+ }
+#elif LZHAM_USE_GCC_ATOMIC_BUILTINS
+ typedef long atomic32_t;
+ typedef long long atomic64_t;
+
+ // Returns the original value.
+ inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_val_compare_and_swap(pDest, comparand, exchange);
+ }
+
+ // Returns the original value.
+ inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 7) == 0);
+ return __sync_val_compare_and_swap(pDest, comparand, exchange);
+ }
+
+ // Returns the resulting incremented value.
+ inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_add_and_fetch(pDest, 1);
+ }
+
+ // Returns the resulting decremented value.
+ inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_sub_and_fetch(pDest, 1);
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_lock_test_and_set(pDest, val);
+ }
+
+ // Returns the resulting value.
+ inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_add_and_fetch(pDest, val);
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange_add(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_fetch_and_add(pDest, val);
+ }
+#else
+ #define LZHAM_NO_ATOMICS 1
+
+ // Atomic ops not supported - but try to do something reasonable. Assumes no threading at all.
+ typedef long atomic32_t;
+ typedef long long atomic64_t;
+
+ inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ atomic32_t cur = *pDest;
+ if (cur == comparand)
+ *pDest = exchange;
+ return cur;
+ }
+
+ inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 7) == 0);
+ atomic64_t cur = *pDest;
+ if (cur == comparand)
+ *pDest = exchange;
+ return cur;
+ }
+
+ inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return (*pDest += 1);
+ }
+
+ inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return (*pDest -= 1);
+ }
+
+ inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ atomic32_t cur = *pDest;
+ *pDest = val;
+ return cur;
+ }
+
+ inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return (*pDest += val);
+ }
+
+ inline atomic32_t atomic_exchange_add(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ LZHAM_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ atomic32_t cur = *pDest;
+ *pDest += val;
+ return cur;
+ }
+
+#endif
+
+#if LZHAM_BUFFERED_PRINTF
+ void lzham_buffered_printf(const char *format, ...);
+ void lzham_flush_buffered_printf();
+#else
+ inline void lzham_buffered_printf(const char *format, ...) { (void)format; }
+ inline void lzham_flush_buffered_printf() { }
+#endif
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_polar_codes.h b/r5dev/thirdparty/lzham/include/lzham_polar_codes.h
new file mode 100644
index 00000000..c478d9d6
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_polar_codes.h
@@ -0,0 +1,14 @@
+// File: lzham_polar_codes.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ //const uint cPolarMaxSupportedSyms = 600;
+ const uint cPolarMaxSupportedSyms = 1024;
+
+ uint get_generate_polar_codes_table_size();
+
+ bool generate_polar_codes(void* pContext, uint num_syms, const uint16* pFreq, uint8* pCodesizes, uint& max_code_size, uint& total_freq_ret);
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_prefix_coding.h b/r5dev/thirdparty/lzham/include/lzham_prefix_coding.h
new file mode 100644
index 00000000..a22903d6
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_prefix_coding.h
@@ -0,0 +1,144 @@
+// File: lzham_prefix_coding.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ namespace prefix_coding
+ {
+ const uint cMaxExpectedCodeSize = 16;
+ const uint cMaxSupportedSyms = 1024;
+ const uint cMaxTableBits = 11;
+
+ bool limit_max_code_size(uint num_syms, uint8* pCodesizes, uint max_code_size);
+
+ bool generate_codes(uint num_syms, const uint8* pCodesizes, uint16* pCodes);
+
+ class decoder_tables
+ {
+ public:
+ inline decoder_tables() :
+ m_table_shift(0), m_table_max_code(0), m_decode_start_code_size(0), m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL)
+ {
+ }
+
+ inline decoder_tables(const decoder_tables& other) :
+ m_table_shift(0), m_table_max_code(0), m_decode_start_code_size(0), m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL)
+ {
+ *this = other;
+ }
+
+ inline decoder_tables& operator= (const decoder_tables& rhs)
+ {
+ assign(rhs);
+ return *this;
+ }
+
+ inline bool assign(const decoder_tables& rhs)
+ {
+ if (this == &rhs)
+ return true;
+
+ uint32* pCur_lookup = m_lookup;
+ uint16* pCur_sorted_symbol_order = m_sorted_symbol_order;
+
+ memcpy(this, &rhs, sizeof(*this));
+
+ if ((pCur_lookup) && (pCur_sorted_symbol_order) && (rhs.m_cur_lookup_size == m_cur_lookup_size) && (rhs.m_cur_sorted_symbol_order_size == m_cur_sorted_symbol_order_size))
+ {
+ m_lookup = pCur_lookup;
+ m_sorted_symbol_order = pCur_sorted_symbol_order;
+
+ memcpy(m_lookup, rhs.m_lookup, sizeof(m_lookup[0]) * m_cur_lookup_size);
+ memcpy(m_sorted_symbol_order, rhs.m_sorted_symbol_order, sizeof(m_sorted_symbol_order[0]) * m_cur_sorted_symbol_order_size);
+ }
+ else
+ {
+ lzham_delete_array(pCur_lookup);
+ m_lookup = NULL;
+
+ if (rhs.m_lookup)
+ {
+ m_lookup = lzham_new_array(m_cur_lookup_size);
+ if (!m_lookup)
+ return false;
+ memcpy(m_lookup, rhs.m_lookup, sizeof(m_lookup[0]) * m_cur_lookup_size);
+ }
+
+ lzham_delete_array(pCur_sorted_symbol_order);
+ m_sorted_symbol_order = NULL;
+
+ if (rhs.m_sorted_symbol_order)
+ {
+ m_sorted_symbol_order = lzham_new_array(m_cur_sorted_symbol_order_size);
+ if (!m_sorted_symbol_order)
+ return false;
+ memcpy(m_sorted_symbol_order, rhs.m_sorted_symbol_order, sizeof(m_sorted_symbol_order[0]) * m_cur_sorted_symbol_order_size);
+ }
+ }
+
+ return true;
+ }
+
+ inline void clear()
+ {
+ if (m_lookup)
+ {
+ lzham_delete_array(m_lookup);
+ m_lookup = 0;
+ m_cur_lookup_size = 0;
+ }
+
+ if (m_sorted_symbol_order)
+ {
+ lzham_delete_array(m_sorted_symbol_order);
+ m_sorted_symbol_order = NULL;
+ m_cur_sorted_symbol_order_size = 0;
+ }
+ }
+
+ inline ~decoder_tables()
+ {
+ if (m_lookup)
+ lzham_delete_array(m_lookup);
+
+ if (m_sorted_symbol_order)
+ lzham_delete_array(m_sorted_symbol_order);
+ }
+
+ // DO NOT use any complex classes here - it is bitwise copied.
+
+ uint m_num_syms;
+ uint m_total_used_syms;
+ uint m_table_bits;
+ uint m_table_shift;
+ uint m_table_max_code;
+ uint m_decode_start_code_size;
+
+ uint8 m_min_code_size;
+ uint8 m_max_code_size;
+
+ uint m_max_codes[cMaxExpectedCodeSize + 1];
+ int m_val_ptrs[cMaxExpectedCodeSize + 1];
+
+ uint m_cur_lookup_size;
+ uint32* m_lookup;
+
+ uint m_cur_sorted_symbol_order_size;
+ uint16* m_sorted_symbol_order;
+
+ inline uint get_unshifted_max_code(uint len) const
+ {
+ LZHAM_ASSERT( (len >= 1) && (len <= cMaxExpectedCodeSize) );
+ uint k = m_max_codes[len - 1];
+ if (!k)
+ return UINT_MAX;
+ return (k - 1) >> (16 - len);
+ }
+ };
+
+ bool generate_decoder_tables(uint num_syms, const uint8* pCodesizes, decoder_tables* pTables, uint table_bits);
+
+ } // namespace prefix_coding
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_pthreads_threading.h b/r5dev/thirdparty/lzham/include/lzham_pthreads_threading.h
new file mode 100644
index 00000000..fe1de038
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_pthreads_threading.h
@@ -0,0 +1,383 @@
+// File: lzham_task_pool_pthreads.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#if LZHAM_USE_PTHREADS_API
+
+#if LZHAM_NO_ATOMICS
+#error No atomic operations defined in lzham_platform.h!
+#endif
+
+#include
+#include
+#include
+
+namespace lzham
+{
+ class semaphore
+ {
+ LZHAM_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
+
+ public:
+ inline semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL)
+ {
+ LZHAM_NOTE_UNUSED(maximumCount), LZHAM_NOTE_UNUSED(pName);
+ LZHAM_ASSERT(maximumCount >= initialCount);
+ if (sem_init(&m_sem, 0, initialCount))
+ {
+ LZHAM_FAIL("semaphore: sem_init() failed");
+ }
+ }
+
+ inline ~semaphore()
+ {
+ sem_destroy(&m_sem);
+ }
+
+ inline void release(long releaseCount = 1)
+ {
+ LZHAM_ASSERT(releaseCount >= 1);
+
+ int status = 0;
+#ifdef WIN32
+ if (1 == releaseCount)
+ status = sem_post(&m_sem);
+ else
+ status = sem_post_multiple(&m_sem, releaseCount);
+#else
+ while (releaseCount > 0)
+ {
+ status = sem_post(&m_sem);
+ if (status)
+ break;
+ releaseCount--;
+ }
+#endif
+
+ if (status)
+ {
+ LZHAM_FAIL("semaphore: sem_post() or sem_post_multiple() failed");
+ }
+ }
+
+ inline bool wait(uint32 milliseconds = UINT32_MAX)
+ {
+ int status;
+ if (milliseconds == UINT32_MAX)
+ {
+ status = sem_wait(&m_sem);
+ }
+ else
+ {
+ struct timespec interval;
+ interval.tv_sec = milliseconds / 1000;
+ interval.tv_nsec = (milliseconds % 1000) * 1000000L;
+ status = sem_timedwait(&m_sem, &interval);
+ }
+
+ if (status)
+ {
+ if (errno != ETIMEDOUT)
+ {
+ LZHAM_FAIL("semaphore: sem_wait() or sem_timedwait() failed");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ sem_t m_sem;
+ };
+
+ class spinlock
+ {
+ public:
+ inline spinlock()
+ {
+ if (pthread_spin_init(&m_spinlock, 0))
+ {
+ LZHAM_FAIL("spinlock: pthread_spin_init() failed");
+ }
+ }
+
+ inline ~spinlock()
+ {
+ pthread_spin_destroy(&m_spinlock);
+ }
+
+ inline void lock()
+ {
+ if (pthread_spin_lock(&m_spinlock))
+ {
+ LZHAM_FAIL("spinlock: pthread_spin_lock() failed");
+ }
+ }
+
+ inline void unlock()
+ {
+ if (pthread_spin_unlock(&m_spinlock))
+ {
+ LZHAM_FAIL("spinlock: pthread_spin_unlock() failed");
+ }
+ }
+
+ private:
+ pthread_spinlock_t m_spinlock;
+ };
+
+ template
+ class tsstack
+ {
+ public:
+ inline tsstack() : m_top(0)
+ {
+ }
+
+ inline ~tsstack()
+ {
+ }
+
+ inline void clear()
+ {
+ m_spinlock.lock();
+ m_top = 0;
+ m_spinlock.unlock();
+ }
+
+ inline bool try_push(const T& obj)
+ {
+ bool result = false;
+ m_spinlock.lock();
+ if (m_top < (int)cMaxSize)
+ {
+ m_stack[m_top++] = obj;
+ result = true;
+ }
+ m_spinlock.unlock();
+ return result;
+ }
+
+ inline bool pop(T& obj)
+ {
+ bool result = false;
+ m_spinlock.lock();
+ if (m_top > 0)
+ {
+ obj = m_stack[--m_top];
+ result = true;
+ }
+ m_spinlock.unlock();
+ return result;
+ }
+
+ private:
+ spinlock m_spinlock;
+ T m_stack[cMaxSize];
+ int m_top;
+ };
+
+ class task_pool
+ {
+ public:
+ task_pool();
+ task_pool(uint num_threads);
+ ~task_pool();
+
+ enum { cMaxThreads = LZHAM_MAX_HELPER_THREADS };
+ bool init(uint num_threads);
+ void deinit();
+
+ inline uint get_num_threads() const { return m_num_threads; }
+ inline uint get_num_outstanding_tasks() const { return m_num_outstanding_tasks; }
+
+ // C-style task callback
+ typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
+ bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL);
+
+ class executable_task
+ {
+ public:
+ virtual void execute_task(uint64 data, void* pData_ptr) = 0;
+ };
+
+ // It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
+ bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL);
+
+ template
+ inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL);
+
+ template
+ inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL);
+
+ void join();
+
+ private:
+ struct task
+ {
+ inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
+
+ uint64 m_data;
+ void* m_pData_ptr;
+
+ union
+ {
+ task_callback_func m_callback;
+ executable_task* m_pObj;
+ };
+
+ uint m_flags;
+ };
+
+ tsstack m_task_stack;
+
+ uint m_num_threads;
+ pthread_t m_threads[cMaxThreads];
+
+ semaphore m_tasks_available;
+
+ enum task_flags
+ {
+ cTaskFlagObject = 1
+ };
+
+ volatile atomic32_t m_num_outstanding_tasks;
+ volatile atomic32_t m_exit_flag;
+
+ void process_task(task& tsk);
+
+ static void* thread_func(void *pContext);
+ };
+
+ enum object_task_flags
+ {
+ cObjectTaskFlagDefault = 0,
+ cObjectTaskFlagDeleteAfterExecution = 1
+ };
+
+ template
+ class object_task : public task_pool::executable_task
+ {
+ public:
+ object_task(uint flags = cObjectTaskFlagDefault) :
+ m_pObject(NULL),
+ m_pMethod(NULL),
+ m_flags(flags)
+ {
+ }
+
+ typedef void (T::*object_method_ptr)(uint64 data, void* pData_ptr);
+
+ object_task(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault) :
+ m_pObject(pObject),
+ m_pMethod(pMethod),
+ m_flags(flags)
+ {
+ LZHAM_ASSERT(pObject && pMethod);
+ }
+
+ void init(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
+ {
+ LZHAM_ASSERT(pObject && pMethod);
+
+ m_pObject = pObject;
+ m_pMethod = pMethod;
+ m_flags = flags;
+ }
+
+ T* get_object() const { return m_pObject; }
+ object_method_ptr get_method() const { return m_pMethod; }
+
+ virtual void execute_task(uint64 data, void* pData_ptr)
+ {
+ (m_pObject->*m_pMethod)(data, pData_ptr);
+
+ if (m_flags & cObjectTaskFlagDeleteAfterExecution)
+ lzham_delete(this);
+ }
+
+ protected:
+ T* m_pObject;
+
+ object_method_ptr m_pMethod;
+
+ uint m_flags;
+ };
+
+ template
+ inline bool task_pool::queue_object_task(S* pObject, T pObject_method, uint64 data, void* pData_ptr)
+ {
+ object_task *pTask = lzham_new< object_task >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
+ if (!pTask)
+ return false;
+ return queue_task(pTask, data, pData_ptr);
+ }
+
+ template
+ inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
+ {
+ LZHAM_ASSERT(m_num_threads);
+ LZHAM_ASSERT(pObject);
+ LZHAM_ASSERT(num_tasks);
+ if (!num_tasks)
+ return true;
+
+ bool status = true;
+
+ uint i;
+ for (i = 0; i < num_tasks; i++)
+ {
+ task tsk;
+
+ tsk.m_pObj = lzham_new< object_task >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
+ if (!tsk.m_pObj)
+ {
+ status = false;
+ break;
+ }
+
+ tsk.m_data = first_data + i;
+ tsk.m_pData_ptr = pData_ptr;
+ tsk.m_flags = cTaskFlagObject;
+
+ if (!m_task_stack.try_push(tsk))
+ {
+ status = false;
+ break;
+ }
+ }
+
+ if (i)
+ {
+ atomic_add32(&m_num_outstanding_tasks, i);
+
+ m_tasks_available.release(i);
+ }
+
+ return status;
+ }
+
+ inline void lzham_sleep(unsigned int milliseconds)
+ {
+#ifdef WIN32
+ struct timespec interval;
+ interval.tv_sec = milliseconds / 1000;
+ interval.tv_nsec = (milliseconds % 1000) * 1000000L;
+ pthread_delay_np(&interval);
+#else
+ while (milliseconds)
+ {
+ int msecs_to_sleep = LZHAM_MIN(milliseconds, 1000);
+ usleep(msecs_to_sleep * 1000);
+ milliseconds -= msecs_to_sleep;
+ }
+#endif
+ }
+
+ // TODO: Implement
+ uint lzham_get_max_helper_threads();
+
+} // namespace lzham
+
+#endif // LZHAM_USE_PTHREADS_API
diff --git a/r5dev/thirdparty/lzham/include/lzham_symbol_codec.h b/r5dev/thirdparty/lzham/include/lzham_symbol_codec.h
new file mode 100644
index 00000000..824a4c74
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_symbol_codec.h
@@ -0,0 +1,556 @@
+// File: lzham_symbol_codec.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+#include "lzham_prefix_coding.h"
+
+namespace lzham
+{
+ class symbol_codec;
+ class adaptive_arith_data_model;
+
+ const uint cSymbolCodecArithMinLen = 0x01000000U;
+ const uint cSymbolCodecArithMaxLen = 0xFFFFFFFFU;
+
+ const uint cSymbolCodecArithProbBits = 11;
+ const uint cSymbolCodecArithProbScale = 1 << cSymbolCodecArithProbBits;
+ const uint cSymbolCodecArithProbHalfScale = 1 << (cSymbolCodecArithProbBits - 1);
+ const uint cSymbolCodecArithProbMoveBits = 5;
+
+ typedef uint64 bit_cost_t;
+ const uint32 cBitCostScaleShift = 24;
+ const uint32 cBitCostScale = (1U << cBitCostScaleShift);
+ const bit_cost_t cBitCostMax = UINT64_MAX;
+
+ inline bit_cost_t convert_to_scaled_bitcost(uint bits) { LZHAM_ASSERT(bits <= 255); uint32 scaled_bits = bits << cBitCostScaleShift; return static_cast(scaled_bits); }
+
+ extern uint32 g_prob_cost[cSymbolCodecArithProbScale];
+
+ class raw_quasi_adaptive_huffman_data_model
+ {
+ public:
+ raw_quasi_adaptive_huffman_data_model(bool encoding = true, uint total_syms = 0, bool fast_encoding = false, bool use_polar_codes = false);
+ raw_quasi_adaptive_huffman_data_model(const raw_quasi_adaptive_huffman_data_model& other);
+ ~raw_quasi_adaptive_huffman_data_model();
+
+ bool assign(const raw_quasi_adaptive_huffman_data_model& rhs);
+ raw_quasi_adaptive_huffman_data_model& operator= (const raw_quasi_adaptive_huffman_data_model& rhs);
+
+ void clear();
+
+ bool init(bool encoding, uint total_syms, bool fast_encoding, bool use_polar_codes, const uint16 *pInitial_sym_freq = NULL);
+ bool reset();
+
+ inline uint get_total_syms() const { return m_total_syms; }
+
+ void rescale();
+ void reset_update_rate();
+
+ bool update(uint sym);
+
+ inline bit_cost_t get_cost(uint sym) const { return convert_to_scaled_bitcost(m_code_sizes[sym]); }
+
+ public:
+ lzham::vector m_initial_sym_freq;
+
+ lzham::vector m_sym_freq;
+
+ lzham::vector m_codes;
+ lzham::vector m_code_sizes;
+
+ prefix_coding::decoder_tables* m_pDecode_tables;
+
+ uint m_total_syms;
+
+ uint m_max_cycle;
+ uint m_update_cycle;
+ uint m_symbols_until_update;
+
+ uint m_total_count;
+
+ uint8 m_decoder_table_bits;
+ bool m_encoding;
+ bool m_fast_updating;
+ bool m_use_polar_codes;
+
+ bool update();
+
+ friend class symbol_codec;
+ };
+
+ struct quasi_adaptive_huffman_data_model : public raw_quasi_adaptive_huffman_data_model
+ {
+#if LZHAM_64BIT_POINTERS
+ // Ensures sizeof(quasi_adaptive_huffman_data_model) is 128 bytes on x64 (it's 64 on x86).
+ char m_unused_alignment[128 - sizeof(raw_quasi_adaptive_huffman_data_model)];
+#endif
+ };
+
+ class adaptive_bit_model
+ {
+ public:
+ adaptive_bit_model();
+ adaptive_bit_model(float prob0);
+ adaptive_bit_model(const adaptive_bit_model& other);
+
+ inline adaptive_bit_model& operator= (const adaptive_bit_model& rhs) { m_bit_0_prob = rhs.m_bit_0_prob; return *this; }
+
+ inline void clear() { m_bit_0_prob = 1U << (cSymbolCodecArithProbBits - 1); }
+
+ void set_probability_0(float prob0);
+
+ inline void update(uint bit)
+ {
+ if (!bit)
+ m_bit_0_prob += ((cSymbolCodecArithProbScale - m_bit_0_prob) >> cSymbolCodecArithProbMoveBits);
+ else
+ m_bit_0_prob -= (m_bit_0_prob >> cSymbolCodecArithProbMoveBits);
+ LZHAM_ASSERT(m_bit_0_prob >= 1);
+ LZHAM_ASSERT(m_bit_0_prob < cSymbolCodecArithProbScale);
+ }
+
+ inline bit_cost_t get_cost(uint bit) const { return g_prob_cost[bit ? (cSymbolCodecArithProbScale - m_bit_0_prob) : m_bit_0_prob]; }
+
+ public:
+ uint16 m_bit_0_prob;
+
+ friend class symbol_codec;
+ friend class adaptive_arith_data_model;
+ };
+
+ // This class is not actually used by LZHAM - it's only here for comparison/experimental purposes.
+ class adaptive_arith_data_model
+ {
+ public:
+ adaptive_arith_data_model(bool encoding = true, uint total_syms = 0);
+ adaptive_arith_data_model(const adaptive_arith_data_model& other);
+ ~adaptive_arith_data_model();
+
+ adaptive_arith_data_model& operator= (const adaptive_arith_data_model& rhs);
+
+ void clear();
+
+ bool init(bool encoding, uint total_syms);
+ bool init(bool encoding, uint total_syms, bool fast_encoding, bool use_polar_codes = false) { LZHAM_NOTE_UNUSED(fast_encoding), LZHAM_NOTE_UNUSED(use_polar_codes); return init(encoding, total_syms); }
+ void reset();
+
+ void reset_update_rate();
+
+ bool update(uint sym);
+
+ uint get_total_syms() const { return m_total_syms; }
+ bit_cost_t get_cost(uint sym) const;
+
+ public:
+ uint m_total_syms;
+ typedef lzham::vector adaptive_bit_model_vector;
+ adaptive_bit_model_vector m_probs;
+
+ friend class symbol_codec;
+ };
+
+#if LZHAM_CPU_HAS_64BIT_REGISTERS
+ #define LZHAM_SYMBOL_CODEC_USE_64_BIT_BUFFER 1
+#else
+ #define LZHAM_SYMBOL_CODEC_USE_64_BIT_BUFFER 0
+#endif
+
+ class symbol_codec
+ {
+ public:
+ symbol_codec();
+
+ void reset();
+
+ // clear() is like reset(), except it also frees all memory.
+ void clear();
+
+ // Encoding
+ bool start_encoding(uint expected_file_size);
+ bool encode_bits(uint bits, uint num_bits);
+ bool encode_arith_init();
+ bool encode_align_to_byte();
+ bool encode(uint sym, quasi_adaptive_huffman_data_model& model);
+ bool encode(uint bit, adaptive_bit_model& model, bool update_model = true);
+ bool encode(uint sym, adaptive_arith_data_model& model);
+
+ inline uint encode_get_total_bits_written() const { return m_total_bits_written; }
+
+ bool stop_encoding(bool support_arith);
+
+ const lzham::vector& get_encoding_buf() const { return m_output_buf; }
+ lzham::vector& get_encoding_buf() { return m_output_buf; }
+
+ // Decoding
+
+ typedef void (*need_bytes_func_ptr)(size_t num_bytes_consumed, void *pPrivate_data, const uint8* &pBuf, size_t &buf_size, bool &eof_flag);
+
+ bool start_decoding(const uint8* pBuf, size_t buf_size, bool eof_flag = true, need_bytes_func_ptr pNeed_bytes_func = NULL, void *pPrivate_data = NULL);
+
+ inline void decode_set_input_buffer(const uint8* pBuf, size_t buf_size, const uint8* pBuf_next, bool eof_flag)
+ {
+ m_pDecode_buf = pBuf;
+ m_pDecode_buf_next = pBuf_next;
+ m_decode_buf_size = buf_size;
+ m_pDecode_buf_end = pBuf + buf_size;
+ m_decode_buf_eof = eof_flag;
+ }
+ inline uint64 decode_get_bytes_consumed() const { return m_pDecode_buf_next - m_pDecode_buf; }
+ inline uint64 decode_get_bits_remaining() const { return ((m_pDecode_buf_end - m_pDecode_buf_next) << 3) + m_bit_count; }
+
+ void start_arith_decoding();
+ uint decode_bits(uint num_bits);
+ uint decode_peek_bits(uint num_bits);
+ void decode_remove_bits(uint num_bits);
+ void decode_align_to_byte();
+ int decode_remove_byte_from_bit_buf();
+ uint decode(quasi_adaptive_huffman_data_model& model);
+ uint decode(adaptive_bit_model& model, bool update_model = true);
+ uint decode(adaptive_arith_data_model& model);
+ uint64 stop_decoding();
+
+ uint get_total_model_updates() const { return m_total_model_updates; }
+
+ public:
+ const uint8* m_pDecode_buf;
+ const uint8* m_pDecode_buf_next;
+ const uint8* m_pDecode_buf_end;
+ size_t m_decode_buf_size;
+ bool m_decode_buf_eof;
+
+ need_bytes_func_ptr m_pDecode_need_bytes_func;
+ void* m_pDecode_private_data;
+
+#if LZHAM_SYMBOL_CODEC_USE_64_BIT_BUFFER
+ typedef uint64 bit_buf_t;
+ enum { cBitBufSize = 64 };
+#else
+ typedef uint32 bit_buf_t;
+ enum { cBitBufSize = 32 };
+#endif
+
+ bit_buf_t m_bit_buf;
+ int m_bit_count;
+
+ uint m_total_model_updates;
+
+ lzham::vector m_output_buf;
+ lzham::vector m_arith_output_buf;
+
+ struct output_symbol
+ {
+ uint m_bits;
+
+ enum
+ {
+ cArithSym = -1,
+ cAlignToByteSym = -2,
+ cArithInit = -3
+ };
+ int16 m_num_bits;
+
+ uint16 m_arith_prob0;
+ };
+ lzham::vector m_output_syms;
+
+ uint m_total_bits_written;
+
+ uint m_arith_base;
+ uint m_arith_value;
+ uint m_arith_length;
+ uint m_arith_total_bits;
+
+ quasi_adaptive_huffman_data_model* m_pSaved_huff_model;
+ void* m_pSaved_model;
+ uint m_saved_node_index;
+
+ bool put_bits_init(uint expected_size);
+ bool record_put_bits(uint bits, uint num_bits);
+
+ void arith_propagate_carry();
+ bool arith_renorm_enc_interval();
+ void arith_start_encoding();
+ bool arith_stop_encoding();
+
+ bool put_bits(uint bits, uint num_bits);
+ bool put_bits_align_to_byte();
+ bool flush_bits();
+ bool assemble_output_buf();
+
+ uint get_bits(uint num_bits);
+ void remove_bits(uint num_bits);
+
+ void decode_need_bytes();
+
+ enum
+ {
+ cNull,
+ cEncoding,
+ cDecoding
+ } m_mode;
+ };
+
+// Optional macros for faster decompression. These macros implement the symbol_codec class's decode functionality.
+// This is hard to debug (and just plain ugly), but using these macros eliminate function calls, and they place the most important
+// member variables on the stack so they're hopefully put in registers (avoiding horrible load hit stores on some CPU's).
+// The user must define the LZHAM_DECODE_NEEDS_BYTES macro, which is invoked when the decode buffer is exhausted.
+
+#define LZHAM_SYMBOL_CODEC_DECODE_DECLARE(codec) \
+ uint arith_value = 0; \
+ uint arith_length = 0; \
+ symbol_codec::bit_buf_t bit_buf = 0; \
+ int bit_count = 0; \
+ const uint8* pDecode_buf_next = NULL;
+
+#define LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec) \
+ arith_value = codec.m_arith_value; \
+ arith_length = codec.m_arith_length; \
+ bit_buf = codec.m_bit_buf; \
+ bit_count = codec.m_bit_count; \
+ pDecode_buf_next = codec.m_pDecode_buf_next;
+
+#define LZHAM_SYMBOL_CODEC_DECODE_END(codec) \
+ codec.m_arith_value = arith_value; \
+ codec.m_arith_length = arith_length; \
+ codec.m_bit_buf = bit_buf; \
+ codec.m_bit_count = bit_count; \
+ codec.m_pDecode_buf_next = pDecode_buf_next;
+
+// The user must declare the LZHAM_DECODE_NEEDS_BYTES macro.
+
+#define LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, result, num_bits) \
+{ \
+ while (LZHAM_BUILTIN_EXPECT(bit_count < (int)(num_bits), 0)) \
+ { \
+ uint r; \
+ if (LZHAM_BUILTIN_EXPECT(pDecode_buf_next == codec.m_pDecode_buf_end, 0)) \
+ { \
+ if (LZHAM_BUILTIN_EXPECT(!codec.m_decode_buf_eof, 1)) \
+ { \
+ LZHAM_SYMBOL_CODEC_DECODE_END(codec) \
+ LZHAM_DECODE_NEEDS_BYTES \
+ LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec) \
+ } \
+ r = 0; \
+ if (LZHAM_BUILTIN_EXPECT(pDecode_buf_next < codec.m_pDecode_buf_end, 1)) r = *pDecode_buf_next++; \
+ } \
+ else \
+ r = *pDecode_buf_next++; \
+ bit_count += 8; \
+ bit_buf |= (static_cast(r) << (symbol_codec::cBitBufSize - bit_count)); \
+ } \
+ result = (num_bits) ? static_cast(bit_buf >> (symbol_codec::cBitBufSize - (num_bits))) : 0; \
+ bit_buf <<= (num_bits); \
+ bit_count -= (num_bits); \
+}
+
+#define LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, result, model) \
+{ \
+ adaptive_bit_model *pModel; \
+ pModel = &model; \
+ while (LZHAM_BUILTIN_EXPECT(arith_length < cSymbolCodecArithMinLen, 0)) \
+ { \
+ uint c; codec.m_pSaved_model = pModel; \
+ LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, c, 8); \
+ pModel = static_cast(codec.m_pSaved_model); \
+ arith_value = (arith_value << 8) | c; \
+ arith_length <<= 8; \
+ } \
+ uint x = pModel->m_bit_0_prob * (arith_length >> cSymbolCodecArithProbBits); \
+ result = (arith_value >= x); \
+ if (!result) \
+ { \
+ pModel->m_bit_0_prob += ((cSymbolCodecArithProbScale - pModel->m_bit_0_prob) >> cSymbolCodecArithProbMoveBits); \
+ arith_length = x; \
+ } \
+ else \
+ { \
+ pModel->m_bit_0_prob -= (pModel->m_bit_0_prob >> cSymbolCodecArithProbMoveBits); \
+ arith_value -= x; \
+ arith_length -= x; \
+ } \
+}
+
+#define LZHAM_SYMBOL_CODEC_DECODE_ADAPTIVE_ARITHMETIC(codec, result, model) \
+{ \
+ adaptive_arith_data_model *pArith_data_model; \
+ pArith_data_model = &model; \
+ uint node_index; \
+ node_index = 1; \
+ do \
+ { \
+ while (LZHAM_BUILTIN_EXPECT(arith_length < cSymbolCodecArithMinLen, 0)) \
+ { \
+ uint c; codec.m_saved_node_index = node_index; codec.m_pSaved_model = pArith_data_model; \
+ LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, c, 8); \
+ node_index = codec.m_saved_node_index; pArith_data_model = static_cast(codec.m_pSaved_model); \
+ arith_value = (arith_value << 8) | c; \
+ arith_length <<= 8; \
+ } \
+ adaptive_bit_model *pBit_model; pBit_model = &pArith_data_model->m_probs[node_index]; \
+ uint x = pBit_model->m_bit_0_prob * (arith_length >> cSymbolCodecArithProbBits); \
+ uint bit; bit = (arith_value >= x); \
+ if (!bit) \
+ { \
+ pBit_model->m_bit_0_prob += ((cSymbolCodecArithProbScale - pBit_model->m_bit_0_prob) >> cSymbolCodecArithProbMoveBits); \
+ arith_length = x; \
+ } \
+ else \
+ { \
+ pBit_model->m_bit_0_prob -= (pBit_model->m_bit_0_prob >> cSymbolCodecArithProbMoveBits); \
+ arith_value -= x; \
+ arith_length -= x; \
+ } \
+ node_index = (node_index << 1) + bit; \
+ } while (node_index < pArith_data_model->m_total_syms); \
+ result = node_index - pArith_data_model->m_total_syms; \
+}
+
+#if LZHAM_SYMBOL_CODEC_USE_64_BIT_BUFFER
+#define LZHAM_SYMBOL_CODEC_DECODE_ADAPTIVE_HUFFMAN(codec, result, model) \
+{ \
+ quasi_adaptive_huffman_data_model* pModel; const prefix_coding::decoder_tables* pTables; \
+ pModel = &model; pTables = model.m_pDecode_tables; \
+ if (LZHAM_BUILTIN_EXPECT(bit_count < 24, 0)) \
+ { \
+ uint c; \
+ pDecode_buf_next += sizeof(uint32); \
+ if (LZHAM_BUILTIN_EXPECT(pDecode_buf_next >= codec.m_pDecode_buf_end, 0)) \
+ { \
+ pDecode_buf_next -= sizeof(uint32); \
+ while (bit_count < 24) \
+ { \
+ if (!codec.m_decode_buf_eof) \
+ { \
+ codec.m_pSaved_huff_model = pModel; \
+ LZHAM_SYMBOL_CODEC_DECODE_END(codec) \
+ LZHAM_DECODE_NEEDS_BYTES \
+ LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec) \
+ pModel = codec.m_pSaved_huff_model; pTables = pModel->m_pDecode_tables; \
+ } \
+ c = 0; if (pDecode_buf_next < codec.m_pDecode_buf_end) c = *pDecode_buf_next++; \
+ bit_count += 8; \
+ bit_buf |= (static_cast(c) << (symbol_codec::cBitBufSize - bit_count)); \
+ } \
+ } \
+ else \
+ { \
+ c = LZHAM_READ_BIG_ENDIAN_UINT32(pDecode_buf_next - sizeof(uint32)); \
+ bit_count += 32; \
+ bit_buf |= (static_cast(c) << (symbol_codec::cBitBufSize - bit_count)); \
+ } \
+ } \
+ uint k = static_cast((bit_buf >> (symbol_codec::cBitBufSize - 16)) + 1); \
+ uint len; \
+ if (LZHAM_BUILTIN_EXPECT(k <= pTables->m_table_max_code, 1)) \
+ { \
+ uint32 t = pTables->m_lookup[bit_buf >> (symbol_codec::cBitBufSize - pTables->m_table_bits)]; \
+ result = t & UINT16_MAX; \
+ len = t >> 16; \
+ } \
+ else \
+ { \
+ len = pTables->m_decode_start_code_size; \
+ for ( ; ; ) \
+ { \
+ if (LZHAM_BUILTIN_EXPECT(k <= pTables->m_max_codes[len - 1], 0)) \
+ break; \
+ len++; \
+ } \
+ int val_ptr = pTables->m_val_ptrs[len - 1] + static_cast(bit_buf >> (symbol_codec::cBitBufSize - len)); \
+ if (((uint)val_ptr >= pModel->m_total_syms)) val_ptr = 0; \
+ result = pTables->m_sorted_symbol_order[val_ptr]; \
+ } \
+ bit_buf <<= len; \
+ bit_count -= len; \
+ uint freq = pModel->m_sym_freq[result]; \
+ freq++; \
+ pModel->m_sym_freq[result] = static_cast(freq); \
+ LZHAM_ASSERT(freq <= UINT16_MAX); \
+ if (LZHAM_BUILTIN_EXPECT(--pModel->m_symbols_until_update == 0, 0)) \
+ { \
+ pModel->update(); \
+ } \
+}
+#else
+#define LZHAM_SYMBOL_CODEC_DECODE_ADAPTIVE_HUFFMAN(codec, result, model) \
+{ \
+ quasi_adaptive_huffman_data_model* pModel; const prefix_coding::decoder_tables* pTables; \
+ pModel = &model; pTables = model.m_pDecode_tables; \
+ while (LZHAM_BUILTIN_EXPECT(bit_count < (symbol_codec::cBitBufSize - 8), 1)) \
+ { \
+ uint c; \
+ if (LZHAM_BUILTIN_EXPECT(pDecode_buf_next == codec.m_pDecode_buf_end, 0)) \
+ { \
+ if (LZHAM_BUILTIN_EXPECT(!codec.m_decode_buf_eof, 1)) \
+ { \
+ codec.m_pSaved_huff_model = pModel; \
+ LZHAM_SYMBOL_CODEC_DECODE_END(codec) \
+ LZHAM_DECODE_NEEDS_BYTES \
+ LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec) \
+ pModel = codec.m_pSaved_huff_model; pTables = pModel->m_pDecode_tables; \
+ } \
+ c = 0; if (LZHAM_BUILTIN_EXPECT(pDecode_buf_next < codec.m_pDecode_buf_end, 1)) c = *pDecode_buf_next++; \
+ } \
+ else \
+ c = *pDecode_buf_next++; \
+ bit_count += 8; \
+ bit_buf |= (static_cast(c) << (symbol_codec::cBitBufSize - bit_count)); \
+ } \
+ uint k = static_cast((bit_buf >> (symbol_codec::cBitBufSize - 16)) + 1); \
+ uint len; \
+ if (LZHAM_BUILTIN_EXPECT(k <= pTables->m_table_max_code, 1)) \
+ { \
+ uint32 t = pTables->m_lookup[bit_buf >> (symbol_codec::cBitBufSize - pTables->m_table_bits)]; \
+ result = t & UINT16_MAX; \
+ len = t >> 16; \
+ } \
+ else \
+ { \
+ len = pTables->m_decode_start_code_size; \
+ for ( ; ; ) \
+ { \
+ if (LZHAM_BUILTIN_EXPECT(k <= pTables->m_max_codes[len - 1], 0)) \
+ break; \
+ len++; \
+ } \
+ int val_ptr = pTables->m_val_ptrs[len - 1] + static_cast(bit_buf >> (symbol_codec::cBitBufSize - len)); \
+ if (LZHAM_BUILTIN_EXPECT(((uint)val_ptr >= pModel->m_total_syms), 0)) val_ptr = 0; \
+ result = pTables->m_sorted_symbol_order[val_ptr]; \
+ } \
+ bit_buf <<= len; \
+ bit_count -= len; \
+ uint freq = pModel->m_sym_freq[result]; \
+ freq++; \
+ pModel->m_sym_freq[result] = static_cast(freq); \
+ LZHAM_ASSERT(freq <= UINT16_MAX); \
+ if (LZHAM_BUILTIN_EXPECT(--pModel->m_symbols_until_update == 0, 0)) \
+ { \
+ pModel->update(); \
+ } \
+}
+#endif
+
+#define LZHAM_SYMBOL_CODEC_DECODE_ALIGN_TO_BYTE(codec) if (bit_count & 7) { int dummy_result; LZHAM_NOTE_UNUSED(dummy_result); LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, dummy_result, bit_count & 7); }
+
+#define LZHAM_SYMBOL_CODEC_DECODE_REMOVE_BYTE_FROM_BIT_BUF(codec, result) \
+{ \
+ result = -1; \
+ if (bit_count >= 8) \
+ { \
+ result = static_cast(bit_buf >> (symbol_codec::cBitBufSize - 8)); \
+ bit_buf <<= 8; \
+ bit_count -= 8; \
+ } \
+}
+
+#define LZHAM_SYMBOL_CODEC_DECODE_ARITH_START(codec) \
+{ \
+ for ( arith_value = 0, arith_length = 0; arith_length < 4; ++arith_length ) \
+ { \
+ uint val; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, val, 8); \
+ arith_value = (arith_value << 8) | val; \
+ } \
+ arith_length = cSymbolCodecArithMaxLen; \
+}
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_threading.h b/r5dev/thirdparty/lzham/include/lzham_threading.h
new file mode 100644
index 00000000..b8a1dbef
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_threading.h
@@ -0,0 +1,12 @@
+// File: lzham_threading.h
+// See Copyright Notice and license at the end of include/lzham.h
+
+#if LZHAM_USE_WIN32_API
+ #include "lzham_win32_threading.h"
+#elif LZHAM_USE_PTHREADS_API
+ #include "lzham_pthreads_threading.h"
+#else
+ #include "lzham_null_threading.h"
+#endif
+
+
diff --git a/r5dev/thirdparty/lzham/include/lzham_timer.h b/r5dev/thirdparty/lzham/include/lzham_timer.h
new file mode 100644
index 00000000..a522430a
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_timer.h
@@ -0,0 +1,99 @@
+// File: lzham_timer.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ typedef unsigned long long timer_ticks;
+
+ class lzham_timer
+ {
+ public:
+ lzham_timer();
+ lzham_timer(timer_ticks start_ticks);
+
+ void start();
+ void start(timer_ticks start_ticks);
+
+ void stop();
+
+ double get_elapsed_secs() const;
+ inline double get_elapsed_ms() const { return get_elapsed_secs() * 1000.0f; }
+ timer_ticks get_elapsed_us() const;
+
+ static void init();
+ static inline timer_ticks get_ticks_per_sec() { return g_freq; }
+ static timer_ticks get_init_ticks();
+ static timer_ticks get_ticks();
+ static double ticks_to_secs(timer_ticks ticks);
+ static inline double ticks_to_ms(timer_ticks ticks) { return ticks_to_secs(ticks) * 1000.0f; }
+ static inline double get_secs() { return ticks_to_secs(get_ticks()); }
+ static inline double get_ms() { return ticks_to_ms(get_ticks()); }
+
+ private:
+ static timer_ticks g_init_ticks;
+ static timer_ticks g_freq;
+ static double g_inv_freq;
+
+ timer_ticks m_start_time;
+ timer_ticks m_stop_time;
+
+ bool m_started : 1;
+ bool m_stopped : 1;
+ };
+
+ enum var_args_t { cVarArgs };
+
+#if LZHAM_PERF_SECTIONS
+ class scoped_perf_section
+ {
+ public:
+ inline scoped_perf_section() :
+ m_start_ticks(lzham_timer::get_ticks())
+ {
+ m_name[0] = '?';
+ m_name[1] = '\0';
+ }
+
+ inline scoped_perf_section(const char *pName) :
+ m_start_ticks(lzham_timer::get_ticks())
+ {
+ strcpy_s(m_name, pName);
+
+ lzham_buffered_printf("Thread: 0x%08X, BEGIN Time: %3.3fms, Section: %s\n", GetCurrentThreadId(), lzham_timer::ticks_to_ms(m_start_ticks), m_name);
+ }
+
+ inline scoped_perf_section(var_args_t, const char *pName, ...) :
+ m_start_ticks(lzham_timer::get_ticks())
+ {
+ va_list args;
+ va_start(args, pName);
+ vsprintf_s(m_name, sizeof(m_name), pName, args);
+ va_end(args);
+
+ lzham_buffered_printf("Thread: 0x%08X, BEGIN Time: %3.3fms, Section: %s\n", GetCurrentThreadId(), lzham_timer::ticks_to_ms(m_start_ticks), m_name);
+ }
+
+ inline ~scoped_perf_section()
+ {
+ double end_ms = lzham_timer::get_ms();
+ double start_ms = lzham_timer::ticks_to_ms(m_start_ticks);
+
+ lzham_buffered_printf("Thread: 0x%08X, END Time: %3.3fms, Total: %3.3fms, Section: %s\n", GetCurrentThreadId(), end_ms, end_ms - start_ms, m_name);
+ }
+
+ private:
+ char m_name[64];
+ timer_ticks m_start_ticks;
+ };
+#else
+ class scoped_perf_section
+ {
+ public:
+ inline scoped_perf_section() { }
+ inline scoped_perf_section(const char *pName) { (void)pName; }
+ inline scoped_perf_section(var_args_t, const char *pName, ...) { (void)pName; }
+ };
+#endif // LZHAM_PERF_SECTIONS
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_traits.h b/r5dev/thirdparty/lzham/include/lzham_traits.h
new file mode 100644
index 00000000..fbb68ce6
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_traits.h
@@ -0,0 +1,137 @@
+// File: lzham_traits.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ template
+ struct scalar_type
+ {
+ enum { cFlag = false };
+ static inline void construct(T* p) { helpers::construct(p); }
+ static inline void construct(T* p, const T& init) { helpers::construct(p, init); }
+ static inline void construct_array(T* p, uint n) { helpers::construct_array(p, n); }
+ static inline void destruct(T* p) { helpers::destruct(p); }
+ static inline void destruct_array(T* p, uint n) { helpers::destruct_array(p, n); }
+ };
+
+ template struct scalar_type
+ {
+ enum { cFlag = true };
+ static inline void construct(T** p) { memset(p, 0, sizeof(T*)); }
+ static inline void construct(T** p, T* init) { *p = init; }
+ static inline void construct_array(T** p, uint n) { memset(p, 0, sizeof(T*) * n); }
+ static inline void destruct(T** p) { LZHAM_NOTE_UNUSED(p); }
+ static inline void destruct_array(T** p, uint n) { LZHAM_NOTE_UNUSED(p); LZHAM_NOTE_UNUSED(n); }
+ };
+
+#define LZHAM_DEFINE_BUILT_IN_TYPE(X) \
+ template<> struct scalar_type { \
+ enum { cFlag = true }; \
+ static inline void construct(X* p) { memset(p, 0, sizeof(X)); } \
+ static inline void construct(X* p, const X& init) { memcpy(p, &init, sizeof(X)); } \
+ static inline void construct_array(X* p, uint n) { memset(p, 0, sizeof(X) * n); } \
+ static inline void destruct(X* p) { LZHAM_NOTE_UNUSED(p); } \
+ static inline void destruct_array(X* p, uint n) { LZHAM_NOTE_UNUSED(p); LZHAM_NOTE_UNUSED(n); } };
+
+ LZHAM_DEFINE_BUILT_IN_TYPE(bool)
+ LZHAM_DEFINE_BUILT_IN_TYPE(char)
+ LZHAM_DEFINE_BUILT_IN_TYPE(unsigned char)
+ LZHAM_DEFINE_BUILT_IN_TYPE(short)
+ LZHAM_DEFINE_BUILT_IN_TYPE(unsigned short)
+ LZHAM_DEFINE_BUILT_IN_TYPE(int)
+ LZHAM_DEFINE_BUILT_IN_TYPE(unsigned int)
+ LZHAM_DEFINE_BUILT_IN_TYPE(long)
+ LZHAM_DEFINE_BUILT_IN_TYPE(unsigned long)
+ LZHAM_DEFINE_BUILT_IN_TYPE(float)
+ LZHAM_DEFINE_BUILT_IN_TYPE(double)
+ LZHAM_DEFINE_BUILT_IN_TYPE(long double)
+ #if defined(WIN32)
+ LZHAM_DEFINE_BUILT_IN_TYPE(__int64)
+ LZHAM_DEFINE_BUILT_IN_TYPE(unsigned __int64)
+ #endif
+
+#undef LZHAM_DEFINE_BUILT_IN_TYPE
+
+// See: http://erdani.org/publications/cuj-2004-06.pdf
+
+ template
+ struct bitwise_movable { enum { cFlag = false }; };
+
+// Defines type Q as bitwise movable.
+#define LZHAM_DEFINE_BITWISE_MOVABLE(Q) template<> struct bitwise_movable { enum { cFlag = true }; };
+
+ template
+ struct bitwise_copyable { enum { cFlag = false }; };
+
+ // Defines type Q as bitwise copyable.
+#define LZHAM_DEFINE_BITWISE_COPYABLE(Q) template<> struct bitwise_copyable { enum { cFlag = true }; };
+
+#define LZHAM_IS_POD(T) __is_pod(T)
+
+#define LZHAM_IS_SCALAR_TYPE(T) (scalar_type::cFlag)
+
+#define LZHAM_IS_BITWISE_COPYABLE(T) ((scalar_type::cFlag) || (bitwise_copyable::cFlag) || LZHAM_IS_POD(T))
+
+#define LZHAM_IS_BITWISE_MOVABLE(T) (LZHAM_IS_BITWISE_COPYABLE(T) || (bitwise_movable::cFlag))
+
+#define LZHAM_HAS_DESTRUCTOR(T) ((!scalar_type::cFlag) && (!__is_pod(T)))
+
+ // From yasli_traits.h:
+ // Credit goes to Boost;
+ // also found in the C++ Templates book by Vandevoorde and Josuttis
+
+ typedef char (&yes_t)[1];
+ typedef char (&no_t)[2];
+
+ template yes_t class_test(int U::*);
+ template no_t class_test(...);
+
+ template struct is_class
+ {
+ enum { value = (sizeof(class_test(0)) == sizeof(yes_t)) };
+ };
+
+ template struct is_pointer
+ {
+ enum { value = false };
+ };
+
+ template struct is_pointer
+ {
+ enum { value = true };
+ };
+
+ LZHAM_DEFINE_BITWISE_COPYABLE(empty_type);
+ LZHAM_DEFINE_BITWISE_MOVABLE(empty_type);
+
+ namespace helpers
+ {
+ template
+ inline void construct_array(T* p, uint n)
+ {
+ if (LZHAM_IS_SCALAR_TYPE(T))
+ {
+ memset(p, 0, sizeof(T) * n);
+ }
+ else
+ {
+ T* q = p + n;
+ for ( ; p != q; ++p)
+ new (static_cast(p)) T;
+ }
+ }
+
+ template
+ inline void destruct_array(T* p, uint n)
+ {
+ if ( LZHAM_HAS_DESTRUCTOR(T) )
+ {
+ T* q = p + n;
+ for ( ; p != q; ++p)
+ p->~T();
+ }
+ }
+ }
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_types.h b/r5dev/thirdparty/lzham/include/lzham_types.h
new file mode 100644
index 00000000..a0227e8a
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_types.h
@@ -0,0 +1,74 @@
+// File: types.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ typedef unsigned char uint8;
+ typedef signed char int8;
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+ typedef signed short int16;
+ typedef unsigned int uint32;
+ typedef uint32 uint;
+ typedef signed int int32;
+
+ #ifdef __GNUC__
+ typedef unsigned long long uint64;
+ typedef long long int64;
+ #else
+ typedef unsigned __int64 uint64;
+ typedef signed __int64 int64;
+ #endif
+
+ const uint8 UINT8_MIN = 0;
+ //const uint8 UINT8_MAX = 0xFFU;
+ const uint16 UINT16_MIN = 0;
+ //const uint16 UINT16_MAX = 0xFFFFU;
+ const uint32 UINT32_MIN = 0;
+ //const uint32 UINT32_MAX = 0xFFFFFFFFU;
+ const uint64 UINT64_MIN = 0;
+ //const uint64 UINT64_MAX = 0xFFFFFFFFFFFFFFFFULL; //0xFFFFFFFFFFFFFFFFui64;
+
+ //const int8 INT8_MIN = -128;
+ //const int8 INT8_MAX = 127;
+ //const int16 INT16_MIN = -32768;
+ //const int16 INT16_MAX = 32767;
+ //const int32 INT32_MIN = (-2147483647 - 1);
+ //const int32 INT32_MAX = 2147483647;
+ //const int64 INT64_MIN = (int64)0x8000000000000000ULL; //(-9223372036854775807i64 - 1);
+ //const int64 INT64_MAX = (int64)0x7FFFFFFFFFFFFFFFULL; //9223372036854775807i64;
+
+#if LZHAM_64BIT_POINTERS
+ typedef uint64 uint_ptr;
+ typedef uint64 uint32_ptr;
+ typedef int64 signed_size_t;
+ typedef uint64 ptr_bits_t;
+ const ptr_bits_t PTR_BITS_XOR = 0xDB0DD4415C87DCF7ULL;
+#else
+ typedef unsigned int uint_ptr;
+ typedef unsigned int uint32_ptr;
+ typedef signed int signed_size_t;
+ typedef uint32 ptr_bits_t;
+ const ptr_bits_t PTR_BITS_XOR = 0x5C87DCF7UL;
+#endif
+
+ enum
+ {
+ cInvalidIndex = -1
+ };
+
+ const uint cIntBits = sizeof(uint) * CHAR_BIT;
+
+ template struct int_traits { enum { cMin = INT_MIN, cMax = INT_MAX, cSigned = true }; };
+ template<> struct int_traits { enum { cMin = INT8_MIN, cMax = INT8_MAX, cSigned = true }; };
+ template<> struct int_traits { enum { cMin = INT16_MIN, cMax = INT16_MAX, cSigned = true }; };
+ template<> struct int_traits { enum { cMin = INT32_MIN, cMax = INT32_MAX, cSigned = true }; };
+
+ template<> struct int_traits { enum { cMin = 0, cMax = UINT_MAX, cSigned = false }; };
+ template<> struct int_traits { enum { cMin = 0, cMax = UINT8_MAX, cSigned = false }; };
+ template<> struct int_traits { enum { cMin = 0, cMax = UINT16_MAX, cSigned = false }; };
+
+ struct empty_type { };
+
+} // namespace lzham
diff --git a/r5dev/thirdparty/lzham/include/lzham_utils.h b/r5dev/thirdparty/lzham/include/lzham_utils.h
new file mode 100644
index 00000000..0e8f5e8b
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_utils.h
@@ -0,0 +1,58 @@
+// File: lzham_utils.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#define LZHAM_GET_ALIGNMENT(v) ((!sizeof(v)) ? 1 : (__alignof(v) ? __alignof(v) : sizeof(uint32)))
+
+#define LZHAM_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define LZHAM_MAX(a, b) (((a) < (b)) ? (b) : (a))
+
+template T decay_array_to_subtype(T (&a)[N]);
+#define LZHAM_ARRAY_SIZE(X) (sizeof(X) / sizeof(decay_array_to_subtype(X)))
+
+namespace lzham
+{
+ namespace utils
+ {
+ template inline void swap(T& l, T& r)
+ {
+ T temp(l);
+ l = r;
+ r = temp;
+ }
+
+ template inline void zero_object(T& obj)
+ {
+ memset(&obj, 0, sizeof(obj));
+ }
+
+ static inline uint32 swap32(uint32 x) { return ((x << 24U) | ((x << 8U) & 0x00FF0000U) | ((x >> 8U) & 0x0000FF00U) | (x >> 24U)); }
+
+ inline uint count_leading_zeros16(uint v)
+ {
+ LZHAM_ASSERT(v < 0x10000);
+
+ uint temp;
+ uint n = 16;
+
+ temp = v >> 8;
+ if (temp) { n -= 8; v = temp; }
+
+ temp = v >> 4;
+ if (temp) { n -= 4; v = temp; }
+
+ temp = v >> 2;
+ if (temp) { n -= 2; v = temp; }
+
+ temp = v >> 1;
+ if (temp) { n -= 1; v = temp; }
+
+ if (v & 1) n--;
+
+ return n;
+ }
+
+ } // namespace utils
+
+} // namespace lzham
+
diff --git a/r5dev/thirdparty/lzham/include/lzham_vector.h b/r5dev/thirdparty/lzham/include/lzham_vector.h
new file mode 100644
index 00000000..90f3236d
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_vector.h
@@ -0,0 +1,588 @@
+// File: lzham_vector.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+namespace lzham
+{
+ struct elemental_vector
+ {
+ void* m_p;
+ uint m_size;
+ uint m_capacity;
+
+ typedef void (*object_mover)(void* pDst, void* pSrc, uint num);
+
+ bool increase_capacity(uint min_new_capacity, bool grow_hint, uint element_size, object_mover pRelocate, bool nofail);
+ };
+
+ template
+ class vector : public helpers::rel_ops< vector >
+ {
+ public:
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T value_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ inline vector() :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ }
+
+ inline vector(uint n, const T& init) :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ increase_capacity(n, false);
+ helpers::construct_array(m_p, n, init);
+ m_size = n;
+ }
+
+ inline vector(const vector& other) :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ increase_capacity(other.m_size, false);
+
+ m_size = other.m_size;
+
+ if (LZHAM_IS_BITWISE_COPYABLE(T))
+ memcpy(m_p, other.m_p, m_size * sizeof(T));
+ else
+ {
+ T* pDst = m_p;
+ const T* pSrc = other.m_p;
+ for (uint i = m_size; i > 0; i--)
+ helpers::construct(pDst++, *pSrc++);
+ }
+ }
+
+ inline explicit vector(uint size) :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ try_resize(size);
+ }
+
+ inline ~vector()
+ {
+ if (m_p)
+ {
+ scalar_type::destruct_array(m_p, m_size);
+ lzham_free(m_p);
+ }
+ }
+
+ inline vector& operator= (const vector& other)
+ {
+ if (this == &other)
+ return *this;
+
+ if (m_capacity >= other.m_size)
+ try_resize(0);
+ else
+ {
+ clear();
+ if (!increase_capacity(other.m_size, false))
+ {
+ LZHAM_FAIL("lzham::vector operator=: Out of memory!");
+ return *this;
+ }
+ }
+
+ if (LZHAM_IS_BITWISE_COPYABLE(T))
+ memcpy(m_p, other.m_p, other.m_size * sizeof(T));
+ else
+ {
+ T* pDst = m_p;
+ const T* pSrc = other.m_p;
+ for (uint i = other.m_size; i > 0; i--)
+ helpers::construct(pDst++, *pSrc++);
+ }
+
+ m_size = other.m_size;
+
+ return *this;
+ }
+
+ inline const T* begin() const { return m_p; }
+ T* begin() { return m_p; }
+
+ inline const T* end() const { return m_p + m_size; }
+ T* end() { return m_p + m_size; }
+
+ inline bool empty() const { return !m_size; }
+ inline uint size() const { return m_size; }
+ inline uint size_in_bytes() const { return m_size * sizeof(T); }
+ inline uint capacity() const { return m_capacity; }
+
+ // operator[] will assert on out of range indices, but in final builds there is (and will never be) any range checking on this method.
+ inline const T& operator[] (uint i) const { LZHAM_ASSERT(i < m_size); return m_p[i]; }
+ inline T& operator[] (uint i) { LZHAM_ASSERT(i < m_size); return m_p[i]; }
+
+ // at() always includes range checking, even in final builds, unlike operator [].
+ // The first element is returned if the index is out of range.
+ inline const T& at(uint i) const { LZHAM_ASSERT(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
+ inline T& at(uint i) { LZHAM_ASSERT(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
+
+ inline const T& front() const { LZHAM_ASSERT(m_size); return m_p[0]; }
+ inline T& front() { LZHAM_ASSERT(m_size); return m_p[0]; }
+
+ inline const T& back() const { LZHAM_ASSERT(m_size); return m_p[m_size - 1]; }
+ inline T& back() { LZHAM_ASSERT(m_size); return m_p[m_size - 1]; }
+
+ inline const T* get_ptr() const { return m_p; }
+ inline T* get_ptr() { return m_p; }
+
+ inline void clear()
+ {
+ if (m_p)
+ {
+ scalar_type::destruct_array(m_p, m_size);
+ lzham_free(m_p);
+ m_p = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ }
+ }
+
+ inline void clear_no_destruction()
+ {
+ if (m_p)
+ {
+ lzham_free(m_p);
+ m_p = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ }
+ }
+
+ inline bool try_reserve(uint new_capacity)
+ {
+ return increase_capacity(new_capacity, true, true);
+ }
+
+ inline bool try_resize(uint new_size, bool grow_hint = false)
+ {
+ if (m_size != new_size)
+ {
+ if (new_size < m_size)
+ scalar_type::destruct_array(m_p + new_size, m_size - new_size);
+ else
+ {
+ if (new_size > m_capacity)
+ {
+ if (!increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint, true))
+ return false;
+ }
+
+ scalar_type::construct_array(m_p + m_size, new_size - m_size);
+ }
+
+ m_size = new_size;
+ }
+
+ return true;
+ }
+
+ inline bool try_resize_no_construct(uint new_size, bool grow_hint = false)
+ {
+ if (new_size > m_capacity)
+ {
+ if (!increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint, true))
+ return false;
+ }
+
+ m_size = new_size;
+
+ return true;
+ }
+
+ inline T* try_enlarge(uint i)
+ {
+ uint cur_size = m_size;
+ if (!try_resize(cur_size + i, true))
+ return NULL;
+ return get_ptr() + cur_size;
+ }
+
+ inline bool try_push_back(const T& obj)
+ {
+ LZHAM_ASSERT(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size)));
+
+ if (m_size >= m_capacity)
+ {
+ if (!increase_capacity(m_size + 1, true, true))
+ return false;
+ }
+
+ scalar_type::construct(m_p + m_size, obj);
+ m_size++;
+
+ return true;
+ }
+
+ inline void pop_back()
+ {
+ LZHAM_ASSERT(m_size);
+
+ if (m_size)
+ {
+ m_size--;
+ scalar_type::destruct(&m_p[m_size]);
+ }
+ }
+
+ inline bool insert(uint index, const T* p, uint n)
+ {
+ LZHAM_ASSERT(index <= m_size);
+ if (!n)
+ return true;
+
+ const uint orig_size = m_size;
+ if (!try_resize(m_size + n, true))
+ return false;
+
+ const uint num_to_move = orig_size - index;
+ if (num_to_move)
+ {
+ if (LZHAM_IS_BITWISE_COPYABLE(T))
+ memmove(m_p + index + n, m_p + index, sizeof(T) * num_to_move);
+ else
+ {
+ const T* pSrc = m_p + orig_size - 1;
+ T* pDst = const_cast(pSrc) + n;
+
+ for (uint i = 0; i < num_to_move; i++)
+ {
+ LZHAM_ASSERT((pDst - m_p) < (int)m_size);
+ *pDst-- = *pSrc--;
+ }
+ }
+ }
+
+ T* pDst = m_p + index;
+
+ if (LZHAM_IS_BITWISE_COPYABLE(T))
+ memcpy(pDst, p, sizeof(T) * n);
+ else
+ {
+ for (uint i = 0; i < n; i++)
+ {
+ LZHAM_ASSERT((pDst - m_p) < (int)m_size);
+ *pDst++ = *p++;
+ }
+ }
+
+ return true;
+ }
+
+ // push_front() isn't going to be very fast - it's only here for usability.
+ inline bool try_push_front(const T& obj)
+ {
+ return insert(0, &obj, 1);
+ }
+
+ bool append(const vector& other)
+ {
+ if (other.m_size)
+ return insert(m_size, &other[0], other.m_size);
+ return true;
+ }
+
+ bool append(const T* p, uint n)
+ {
+ if (n)
+ return insert(m_size, p, n);
+ return true;
+ }
+
+ inline void erase(uint start, uint n)
+ {
+ LZHAM_ASSERT((start + n) <= m_size);
+ if ((start + n) > m_size)
+ return;
+
+ if (!n)
+ return;
+
+ const uint num_to_move = m_size - (start + n);
+
+ T* pDst = m_p + start;
+
+ const T* pSrc = m_p + start + n;
+
+ if (LZHAM_IS_BITWISE_COPYABLE(T))
+ memmove(pDst, pSrc, num_to_move * sizeof(T));
+ else
+ {
+ T* pDst_end = pDst + num_to_move;
+
+ while (pDst != pDst_end)
+ *pDst++ = *pSrc++;
+
+ scalar_type::destruct_array(pDst_end, n);
+ }
+
+ m_size -= n;
+ }
+
+ inline void erase(uint index)
+ {
+ erase(index, 1);
+ }
+
+ inline void erase(T* p)
+ {
+ LZHAM_ASSERT((p >= m_p) && (p < (m_p + m_size)));
+ erase(static_cast(p - m_p));
+ }
+
+ void erase_unordered(uint index)
+ {
+ LZHAM_ASSERT(index < m_size);
+
+ if ((index + 1) < m_size)
+ (*this)[index] = back();
+
+ pop_back();
+ }
+
+ inline bool operator== (const vector& rhs) const
+ {
+ if (m_size != rhs.m_size)
+ return false;
+ else if (m_size)
+ {
+ if (scalar_type::cFlag)
+ return memcmp(m_p, rhs.m_p, sizeof(T) * m_size) == 0;
+ else
+ {
+ const T* pSrc = m_p;
+ const T* pDst = rhs.m_p;
+ for (uint i = m_size; i; i--)
+ if (!(*pSrc++ == *pDst++))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline bool operator< (const vector& rhs) const
+ {
+ const uint min_size = math::minimum(m_size, rhs.m_size);
+
+ const T* pSrc = m_p;
+ const T* pSrc_end = m_p + min_size;
+ const T* pDst = rhs.m_p;
+
+ while ((pSrc < pSrc_end) && (*pSrc == *pDst))
+ {
+ pSrc++;
+ pDst++;
+ }
+
+ if (pSrc < pSrc_end)
+ return *pSrc < *pDst;
+
+ return m_size < rhs.m_size;
+ }
+
+ inline void swap(vector& other)
+ {
+ utils::swap(m_p, other.m_p);
+ utils::swap(m_size, other.m_size);
+ utils::swap(m_capacity, other.m_capacity);
+ }
+
+ inline void sort()
+ {
+ std::sort(begin(), end());
+ }
+
+ inline void unique()
+ {
+ if (!empty())
+ {
+ sort();
+
+ resize(std::unique(begin(), end()) - begin());
+ }
+ }
+
+ inline void reverse()
+ {
+ uint j = m_size >> 1;
+ for (uint i = 0; i < j; i++)
+ utils::swap(m_p[i], m_p[m_size - 1 - i]);
+ }
+
+ inline int find(const T& key) const
+ {
+ const T* p = m_p;
+ const T* p_end = m_p + m_size;
+
+ uint index = 0;
+
+ while (p != p_end)
+ {
+ if (key == *p)
+ return index;
+
+ p++;
+ index++;
+ }
+
+ return cInvalidIndex;
+ }
+
+ inline int find_sorted(const T& key) const
+ {
+ if (m_size)
+ {
+ // Uniform binary search - Knuth Algorithm 6.2.1 U, unrolled twice.
+ int i = ((m_size + 1) >> 1) - 1;
+ int m = m_size;
+
+ for ( ; ; )
+ {
+ LZHAM_ASSERT_OPEN_RANGE(i, 0, (int)m_size);
+ const T* pKey_i = m_p + i;
+ int cmp = key < *pKey_i;
+ if ((!cmp) && (key == *pKey_i)) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+
+ LZHAM_ASSERT_OPEN_RANGE(i, 0, (int)m_size);
+ pKey_i = m_p + i;
+ cmp = key < *pKey_i;
+ if ((!cmp) && (key == *pKey_i)) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+ }
+ }
+
+ return cInvalidIndex;
+ }
+
+ template
+ inline int find_sorted(const T& key, Q less_than) const
+ {
+ if (m_size)
+ {
+ // Uniform binary search - Knuth Algorithm 6.2.1 U, unrolled twice.
+ int i = ((m_size + 1) >> 1) - 1;
+ int m = m_size;
+
+ for ( ; ; )
+ {
+ LZHAM_ASSERT_OPEN_RANGE(i, 0, (int)m_size);
+ const T* pKey_i = m_p + i;
+ int cmp = less_than(key, *pKey_i);
+ if ((!cmp) && (!less_than(*pKey_i, key))) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+
+ LZHAM_ASSERT_OPEN_RANGE(i, 0, (int)m_size);
+ pKey_i = m_p + i;
+ cmp = less_than(key, *pKey_i);
+ if ((!cmp) && (!less_than(*pKey_i, key))) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+ }
+ }
+
+ return cInvalidIndex;
+ }
+
+ inline uint count_occurences(const T& key) const
+ {
+ uint c = 0;
+
+ const T* p = m_p;
+ const T* p_end = m_p + m_size;
+
+ while (p != p_end)
+ {
+ if (key == *p)
+ c++;
+
+ p++;
+ }
+
+ return c;
+ }
+
+ inline void set_all(const T& o)
+ {
+ if ((sizeof(T) == 1) && (scalar_type::cFlag))
+ memset(m_p, *reinterpret_cast(&o), m_size);
+ else
+ {
+ T* pDst = m_p;
+ T* pDst_end = pDst + m_size;
+ while (pDst != pDst_end)
+ *pDst++ = o;
+ }
+ }
+
+ private:
+ T* m_p;
+ uint m_size;
+ uint m_capacity;
+
+ template struct is_vector { enum { cFlag = false }; };
+ template struct is_vector< vector > { enum { cFlag = true }; };
+
+ static void object_mover(void* pDst_void, void* pSrc_void, uint num)
+ {
+ T* pSrc = static_cast(pSrc_void);
+ T* const pSrc_end = pSrc + num;
+ T* pDst = static_cast(pDst_void);
+
+ while (pSrc != pSrc_end)
+ {
+ new (static_cast(pDst)) T(*pSrc);
+ pSrc->~T();
+ pSrc++;
+ pDst++;
+ }
+ }
+
+ inline bool increase_capacity(uint min_new_capacity, bool grow_hint, bool nofail = false)
+ {
+ return reinterpret_cast(this)->increase_capacity(
+ min_new_capacity, grow_hint, sizeof(T),
+ (LZHAM_IS_BITWISE_MOVABLE(T) || (is_vector::cFlag)) ? NULL : object_mover, nofail);
+ }
+ };
+
+ template struct bitwise_movable< vector > { enum { cFlag = true }; };
+
+ extern void vector_test();
+
+ template
+ inline void swap(vector& a, vector& b)
+ {
+ a.swap(b);
+ }
+
+} // namespace lzham
+
diff --git a/r5dev/thirdparty/lzham/include/lzham_win32_threading.h b/r5dev/thirdparty/lzham/include/lzham_win32_threading.h
new file mode 100644
index 00000000..64125ac1
--- /dev/null
+++ b/r5dev/thirdparty/lzham/include/lzham_win32_threading.h
@@ -0,0 +1,368 @@
+// File: lzham_task_pool_win32.h
+// See Copyright Notice and license at the end of include/lzham.h
+#pragma once
+
+#if LZHAM_USE_WIN32_API
+
+#if LZHAM_NO_ATOMICS
+#error No atomic operations defined in lzham_platform.h!
+#endif
+
+namespace lzham
+{
+ class semaphore
+ {
+ LZHAM_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
+
+ public:
+ semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL)
+ {
+ m_handle = CreateSemaphoreA(NULL, initialCount, maximumCount, pName);
+ if (NULL == m_handle)
+ {
+ LZHAM_FAIL("semaphore: CreateSemaphore() failed");
+ }
+ }
+
+ ~semaphore()
+ {
+ if (m_handle)
+ {
+ CloseHandle(m_handle);
+ m_handle = NULL;
+ }
+ }
+
+ inline HANDLE get_handle(void) const { return m_handle; }
+
+ void release(long releaseCount = 1)
+ {
+ if (0 == ReleaseSemaphore(m_handle, releaseCount, NULL))
+ {
+ LZHAM_FAIL("semaphore: ReleaseSemaphore() failed");
+ }
+ }
+
+ bool wait(uint32 milliseconds = UINT32_MAX)
+ {
+ LZHAM_ASSUME(INFINITE == UINT32_MAX);
+
+ DWORD result = WaitForSingleObject(m_handle, milliseconds);
+
+ if (WAIT_FAILED == result)
+ {
+ LZHAM_FAIL("semaphore: WaitForSingleObject() failed");
+ }
+
+ return WAIT_OBJECT_0 == result;
+ }
+
+ private:
+ HANDLE m_handle;
+ };
+
+ template
+ class tsstack
+ {
+ public:
+ inline tsstack(bool use_freelist = true) :
+ m_use_freelist(use_freelist)
+ {
+ LZHAM_VERIFY(((ptr_bits_t)this & (LZHAM_GET_ALIGNMENT(tsstack) - 1)) == 0);
+ InitializeSListHead(&m_stack_head);
+ InitializeSListHead(&m_freelist_head);
+ }
+
+ inline ~tsstack()
+ {
+ clear();
+ }
+
+ inline void clear()
+ {
+ for ( ; ; )
+ {
+ node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
+ if (!pNode)
+ break;
+
+ LZHAM_MEMORY_IMPORT_BARRIER
+
+ helpers::destruct(&pNode->m_obj);
+
+ lzham_free(pNode);
+ }
+
+ flush_freelist();
+ }
+
+ inline void flush_freelist()
+ {
+ if (!m_use_freelist)
+ return;
+
+ for ( ; ; )
+ {
+ node* pNode = (node*)InterlockedPopEntrySList(&m_freelist_head);
+ if (!pNode)
+ break;
+
+ LZHAM_MEMORY_IMPORT_BARRIER
+
+ lzham_free(pNode);
+ }
+ }
+
+ inline bool try_push(const T& obj)
+ {
+ node* pNode = alloc_node();
+ if (!pNode)
+ return false;
+
+ helpers::construct(&pNode->m_obj, obj);
+
+ LZHAM_MEMORY_EXPORT_BARRIER
+
+ InterlockedPushEntrySList(&m_stack_head, &pNode->m_slist_entry);
+
+ return true;
+ }
+
+ inline bool pop(T& obj)
+ {
+ node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
+ if (!pNode)
+ return false;
+
+ LZHAM_MEMORY_IMPORT_BARRIER
+
+ obj = pNode->m_obj;
+
+ helpers::destruct(&pNode->m_obj);
+
+ free_node(pNode);
+
+ return true;
+ }
+
+ private:
+ SLIST_HEADER m_stack_head;
+ SLIST_HEADER m_freelist_head;
+
+ struct node
+ {
+ SLIST_ENTRY m_slist_entry;
+ T m_obj;
+ };
+
+ bool m_use_freelist;
+
+ inline node* alloc_node()
+ {
+ node* pNode = m_use_freelist ? (node*)InterlockedPopEntrySList(&m_freelist_head) : NULL;
+
+ if (!pNode)
+ pNode = (node*)lzham_malloc(sizeof(node));
+
+ return pNode;
+ }
+
+ inline void free_node(node* pNode)
+ {
+ if (m_use_freelist)
+ InterlockedPushEntrySList(&m_freelist_head, &pNode->m_slist_entry);
+ else
+ lzham_free(pNode);
+ }
+ };
+
+ class task_pool
+ {
+ public:
+ task_pool();
+ task_pool(uint num_threads);
+ ~task_pool();
+
+ enum { cMaxThreads = LZHAM_MAX_HELPER_THREADS };
+ bool init(uint num_threads);
+ void deinit();
+
+ inline uint get_num_threads() const { return m_num_threads; }
+ inline uint get_num_outstanding_tasks() const { return m_num_outstanding_tasks; }
+
+ // C-style task callback
+ typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
+ bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL);
+
+ class executable_task
+ {
+ public:
+ virtual void execute_task(uint64 data, void* pData_ptr) = 0;
+ };
+
+ // It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
+ bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL);
+
+ template
+ inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL);
+
+ template
+ inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL);
+
+ void join();
+
+ private:
+ struct task
+ {
+ //inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
+
+ uint64 m_data;
+ void* m_pData_ptr;
+
+ union
+ {
+ task_callback_func m_callback;
+ executable_task* m_pObj;
+ };
+
+ uint m_flags;
+ };
+
+ tsstack m_task_stack;
+
+ uint m_num_threads;
+ HANDLE m_threads[cMaxThreads];
+
+ semaphore m_tasks_available;
+
+ enum task_flags
+ {
+ cTaskFlagObject = 1
+ };
+
+ volatile atomic32_t m_num_outstanding_tasks;
+ volatile atomic32_t m_exit_flag;
+
+ void process_task(task& tsk);
+
+ static unsigned __stdcall thread_func(void* pContext);
+ };
+
+ enum object_task_flags
+ {
+ cObjectTaskFlagDefault = 0,
+ cObjectTaskFlagDeleteAfterExecution = 1
+ };
+
+ template
+ class object_task : public task_pool::executable_task
+ {
+ public:
+ object_task(uint flags = cObjectTaskFlagDefault) :
+ m_pObject(NULL),
+ m_pMethod(NULL),
+ m_flags(flags)
+ {
+ }
+
+ typedef void (T::*object_method_ptr)(uint64 data, void* pData_ptr);
+
+ object_task(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault) :
+ m_pObject(pObject),
+ m_pMethod(pMethod),
+ m_flags(flags)
+ {
+ LZHAM_ASSERT(pObject && pMethod);
+ }
+
+ void init(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
+ {
+ LZHAM_ASSERT(pObject && pMethod);
+
+ m_pObject = pObject;
+ m_pMethod = pMethod;
+ m_flags = flags;
+ }
+
+ T* get_object() const { return m_pObject; }
+ object_method_ptr get_method() const { return m_pMethod; }
+
+ virtual void execute_task(uint64 data, void* pData_ptr)
+ {
+ (m_pObject->*m_pMethod)(data, pData_ptr);
+
+ if (m_flags & cObjectTaskFlagDeleteAfterExecution)
+ lzham_delete(this);
+ }
+
+ protected:
+ T* m_pObject;
+
+ object_method_ptr m_pMethod;
+
+ uint m_flags;
+ };
+
+ template
+ inline bool task_pool::queue_object_task(S* pObject, T pObject_method, uint64 data, void* pData_ptr)
+ {
+ object_task *pTask = lzham_new< object_task >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
+ if (!pTask)
+ return false;
+ return queue_task(pTask, data, pData_ptr);
+ }
+
+ template
+ inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
+ {
+ LZHAM_ASSERT(m_num_threads);
+ LZHAM_ASSERT(pObject);
+ LZHAM_ASSERT(num_tasks);
+ if (!num_tasks)
+ return true;
+
+ bool status = true;
+
+ uint i;
+ for (i = 0; i < num_tasks; i++)
+ {
+ task tsk;
+
+ tsk.m_pObj = lzham_new< object_task >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
+ if (!tsk.m_pObj)
+ {
+ status = false;
+ break;
+ }
+
+ tsk.m_data = first_data + i;
+ tsk.m_pData_ptr = pData_ptr;
+ tsk.m_flags = cTaskFlagObject;
+
+ if (!m_task_stack.try_push(tsk))
+ {
+ status = false;
+ break;
+ }
+ }
+
+ if (i)
+ {
+ atomic_add32(&m_num_outstanding_tasks, i);
+
+ m_tasks_available.release(i);
+ }
+
+ return status;
+ }
+
+ inline void lzham_sleep(unsigned int milliseconds)
+ {
+ Sleep(milliseconds);
+ }
+
+ uint lzham_get_max_helper_threads();
+
+} // namespace lzham
+
+#endif // LZHAM_USE_WIN32_API
diff --git a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib
deleted file mode 100644
index 7f5d6e43..00000000
Binary files a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib and /dev/null differ
diff --git a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib
deleted file mode 100644
index f593696f..00000000
Binary files a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib and /dev/null differ
diff --git a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib
deleted file mode 100644
index 5772956d..00000000
Binary files a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib and /dev/null differ
diff --git a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib
deleted file mode 100644
index a6665324..00000000
Binary files a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib and /dev/null differ
diff --git a/r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib
deleted file mode 100644
index 18ac9a96..00000000
Binary files a/r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib and /dev/null differ
diff --git a/r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib
deleted file mode 100644
index 121dc906..00000000
Binary files a/r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib and /dev/null differ
diff --git a/r5dev/thirdparty/lzham/lzham_api.cpp b/r5dev/thirdparty/lzham/lzham_api.cpp
new file mode 100644
index 00000000..c622b6c5
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_api.cpp
@@ -0,0 +1,179 @@
+// File: lzham_api.cpp - Dynamic DLL entrypoints.
+// See Copyright Notice and license at the end of include/lzham.h
+#include "include/lzham_core.h"
+#include "lzhamdecomp/lzham_decomp.h"
+#include "lzhamcomp/lzham_comp.h"
+
+extern "C" LZHAM_DLL_EXPORT lzham_uint32 lzham_get_version(void)
+{
+ return LZHAM_DLL_VERSION;
+}
+
+extern "C" LZHAM_DLL_EXPORT void lzham_set_memory_callbacks(lzham_realloc_func pRealloc, lzham_msize_func pMSize, void* pUser_data)
+{
+ lzham::lzham_lib_set_memory_callbacks(pRealloc, pMSize, pUser_data);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_decompress_state_ptr lzham_decompress_init(const lzham_decompress_params *pParams)
+{
+ return lzham::lzham_lib_decompress_init(pParams);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_decompress_state_ptr lzham_decompress_reinit(lzham_decompress_state_ptr p, const lzham_decompress_params *pParams)
+{
+ return lzham::lzham_lib_decompress_reinit(p, pParams);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_decompress_checksums* lzham_decompress_deinit(lzham_decompress_state_ptr p)
+{
+ return lzham::lzham_lib_decompress_deinit(p);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_decompress_status_t lzham_decompress(
+ lzham_decompress_state_ptr p,
+ const lzham_uint8 *pIn_buf, size_t *pIn_buf_size,
+ lzham_uint8 *pOut_buf, size_t *pOut_buf_size,
+ lzham_bool no_more_input_bytes_flag)
+{
+ return lzham::lzham_lib_decompress(p, pIn_buf, pIn_buf_size, pOut_buf, pOut_buf_size, no_more_input_bytes_flag);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_decompress_status_t lzham_decompress_memory(const lzham_decompress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32, lzham_uint32 *pCrc32)
+{
+ return lzham::lzham_lib_decompress_memory(pParams, pDst_buf, pDst_len, pSrc_buf, src_len, pAdler32, pCrc32);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_compress_state_ptr lzham_compress_init(const lzham_compress_params *pParams)
+{
+ return lzham::lzham_lib_compress_init(pParams);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_compress_state_ptr lzham_compress_reinit(lzham_compress_state_ptr p)
+{
+ return lzham::lzham_lib_compress_reinit(p);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_compress_checksums* lzham_compress_deinit(lzham_compress_state_ptr p)
+{
+ return lzham::lzham_lib_compress_deinit(p);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_compress_status_t lzham_compress(
+ lzham_compress_state_ptr p,
+ const lzham_uint8 *pIn_buf, size_t *pIn_buf_size,
+ lzham_uint8 *pOut_buf, size_t *pOut_buf_size,
+ lzham_bool no_more_input_bytes_flag)
+{
+ return lzham::lzham_lib_compress(p, pIn_buf, pIn_buf_size, pOut_buf, pOut_buf_size, no_more_input_bytes_flag);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_compress_status_t lzham_compress2(
+ lzham_compress_state_ptr p,
+ const lzham_uint8 *pIn_buf, size_t *pIn_buf_size,
+ lzham_uint8 *pOut_buf, size_t *pOut_buf_size,
+ lzham_flush_t flush_type)
+{
+ return lzham::lzham_lib_compress2(p, pIn_buf, pIn_buf_size, pOut_buf, pOut_buf_size, flush_type);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_compress_status_t lzham_compress_memory(const lzham_compress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32, lzham_uint32 * pCrc32)
+{
+ return lzham::lzham_lib_compress_memory(pParams, pDst_buf, pDst_len, pSrc_buf, src_len, pAdler32, pCrc32);
+}
+
+// ----------------- zlib-style API's
+
+extern "C" LZHAM_DLL_EXPORT const char *lzham_z_version(void)
+{
+ return LZHAM_Z_VERSION;
+}
+
+extern "C" lzham_z_ulong LZHAM_DLL_EXPORT lzham_z_adler32(lzham_z_ulong adler, const unsigned char *ptr, size_t buf_len)
+{
+ return lzham::lzham_lib_z_adler32(adler, ptr, buf_len);
+}
+
+extern "C" lzham_z_ulong LZHAM_DLL_EXPORT lzham_z_crc32(lzham_z_ulong crc, const lzham_uint8 *ptr, size_t buf_len)
+{
+ return lzham::lzham_lib_z_crc32(crc, ptr, buf_len);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_deflateInit(lzham_z_streamp pStream, int level)
+{
+ return lzham::lzham_lib_z_deflateInit(pStream, level);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_deflateInit2(lzham_z_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
+{
+ return lzham::lzham_lib_z_deflateInit2(pStream, level, method, window_bits, mem_level, strategy);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_deflateReset(lzham_z_streamp pStream)
+{
+ return lzham::lzham_lib_z_deflateReset(pStream);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_deflate(lzham_z_streamp pStream, int flush)
+{
+ return lzham::lzham_lib_z_deflate(pStream, flush);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_deflateEnd(lzham_z_streamp pStream)
+{
+ return lzham::lzham_lib_z_deflateEnd(pStream);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_deflateBound(lzham_z_streamp pStream, lzham_z_ulong source_len)
+{
+ return lzham::lzham_lib_z_deflateBound(pStream, source_len);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_compress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len)
+{
+ return lzham::lzham_lib_z_compress(pDest, pDest_len, pSource, source_len);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_compress2(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len, int level)
+{
+ return lzham::lzham_lib_z_compress2(pDest, pDest_len, pSource, source_len, level);
+}
+
+extern "C" LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_compressBound(lzham_z_ulong source_len)
+{
+ return lzham::lzham_lib_z_compressBound(source_len);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_inflateInit(lzham_z_streamp pStream)
+{
+ return lzham::lzham_lib_z_inflateInit(pStream);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_inflateInit2(lzham_z_streamp pStream, int window_bits)
+{
+ return lzham::lzham_lib_z_inflateInit2(pStream, window_bits);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_inflateReset(lzham_z_streamp pStream)
+{
+ return lzham::lzham_lib_z_inflateReset(pStream);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_inflate(lzham_z_streamp pStream, int flush)
+{
+ return lzham::lzham_lib_z_inflate(pStream, flush);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_inflateEnd(lzham_z_streamp pStream)
+{
+ return lzham::lzham_lib_z_inflateEnd(pStream);
+}
+
+extern "C" LZHAM_DLL_EXPORT int lzham_z_uncompress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len)
+{
+ return lzham::lzham_lib_z_uncompress(pDest, pDest_len, pSource, source_len);
+}
+
+extern "C" LZHAM_DLL_EXPORT const char *lzham_z_error(int err)
+{
+ return lzham::lzham_lib_z_error(err);
+}
diff --git a/r5dev/thirdparty/lzham/lzham_assert.cpp b/r5dev/thirdparty/lzham/lzham_assert.cpp
new file mode 100644
index 00000000..efaf0a79
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_assert.cpp
@@ -0,0 +1,66 @@
+// File: lzham_assert.cpp
+// See Copyright Notice and license at the end of include/lzham.h
+#include "include/lzham_core.h"
+
+static bool g_fail_exceptions;
+static bool g_exit_on_failure = true;
+
+void lzham_enable_fail_exceptions(bool enabled)
+{
+ g_fail_exceptions = enabled;
+}
+
+void lzham_assert(const char* pExp, const char* pFile, unsigned line)
+{
+ char buf[512];
+
+ sprintf_s(buf, sizeof(buf), "%s(%u): Assertion failed: \"%s\"\n", pFile, line, pExp);
+
+ lzham_output_debug_string(buf);
+
+ printf("%s", buf);
+
+ if (lzham_is_debugger_present())
+ lzham_debug_break();
+}
+
+void lzham_fail(const char* pExp, const char* pFile, unsigned line)
+{
+ char buf[512];
+
+ sprintf_s(buf, sizeof(buf), "%s(%u): Failure: \"%s\"\n", pFile, line, pExp);
+
+ lzham_output_debug_string(buf);
+
+ printf("%s", buf);
+
+ if (lzham_is_debugger_present())
+ lzham_debug_break();
+
+#if LZHAM_USE_WIN32_API
+ if (g_fail_exceptions)
+ RaiseException(LZHAM_FAIL_EXCEPTION_CODE, 0, 0, NULL);
+ else
+#endif
+ if (g_exit_on_failure)
+ exit(EXIT_FAILURE);
+}
+
+void lzham_trace(const char* pFmt, va_list args)
+{
+ if (lzham_is_debugger_present())
+ {
+ char buf[512];
+ vsprintf_s(buf, sizeof(buf), pFmt, args);
+
+ lzham_output_debug_string(buf);
+ }
+};
+
+void lzham_trace(const char* pFmt, ...)
+{
+ va_list args;
+ va_start(args, pFmt);
+ lzham_trace(pFmt, args);
+ va_end(args);
+};
diff --git a/r5dev/thirdparty/lzham/lzham_checksum.cpp b/r5dev/thirdparty/lzham/lzham_checksum.cpp
new file mode 100644
index 00000000..3dc616d4
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_checksum.cpp
@@ -0,0 +1,73 @@
+// File: lzham_checksum.cpp
+#include "include/lzham_core.h"
+#include "include/lzham_checksum.h"
+
+namespace lzham
+{
+ // Originally from the public domain stb.h header.
+ uint adler32(const void* pBuf, size_t buflen, uint adler32)
+ {
+ if (!pBuf)
+ return cInitAdler32;
+
+ const uint8* buffer = static_cast(pBuf);
+
+ const unsigned long ADLER_MOD = 65521;
+ unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
+ size_t blocklen;
+ unsigned long i;
+
+ blocklen = buflen % 5552;
+ while (buflen)
+ {
+ for (i=0; i + 7 < blocklen; i += 8)
+ {
+ s1 += buffer[0], s2 += s1;
+ s1 += buffer[1], s2 += s1;
+ s1 += buffer[2], s2 += s1;
+ s1 += buffer[3], s2 += s1;
+ s1 += buffer[4], s2 += s1;
+ s1 += buffer[5], s2 += s1;
+ s1 += buffer[6], s2 += s1;
+ s1 += buffer[7], s2 += s1;
+
+ buffer += 8;
+ }
+
+ for (; i < blocklen; ++i)
+ s1 += *buffer++, s2 += s1;
+
+ s1 %= ADLER_MOD, s2 %= ADLER_MOD;
+ buflen -= blocklen;
+ blocklen = 5552;
+ }
+ return (s2 << 16) + s1;
+ }
+
+ // Karl Malbrain's compact CRC-32, with pre and post conditioning.
+ // See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed":
+ // http://www.geocities.com/malbrain/
+ static const lzham_uint32 s_crc32[16] =
+ {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+ };
+
+ uint crc32(uint crc, const lzham_uint8 *ptr, size_t buf_len)
+ {
+ if (!ptr)
+ return cInitCRC32;
+
+ crc = ~crc;
+ while (buf_len--)
+ {
+ lzham_uint8 b = *ptr++;
+ crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b & 0xF)];
+ crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b >> 4)];
+ }
+ return ~crc;
+ }
+
+
+} // namespace lzham
+
diff --git a/r5dev/thirdparty/lzham/lzham_huffman_codes.cpp b/r5dev/thirdparty/lzham/lzham_huffman_codes.cpp
new file mode 100644
index 00000000..2ca7e392
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_huffman_codes.cpp
@@ -0,0 +1,390 @@
+// File: huffman_codes.cpp
+// See Copyright Notice and license at the end of include/lzham.h
+#include "include/lzham_core.h"
+#include "include/lzham_huffman_codes.h"
+
+namespace lzham
+{
+ struct sym_freq
+ {
+ uint m_freq;
+ uint16 m_left;
+ uint16 m_right;
+
+ inline bool operator< (const sym_freq& other) const
+ {
+ return m_freq > other.m_freq;
+ }
+ };
+
+ static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* syms0, sym_freq* syms1)
+ {
+ const uint cMaxPasses = 2;
+ uint hist[256 * cMaxPasses];
+
+ memset(hist, 0, sizeof(hist[0]) * 256 * cMaxPasses);
+
+ {
+ sym_freq* p = syms0;
+ sym_freq* q = syms0 + (num_syms >> 1) * 2;
+
+ for ( ; p != q; p += 2)
+ {
+ const uint freq0 = p[0].m_freq;
+ const uint freq1 = p[1].m_freq;
+
+ hist[ freq0 & 0xFF]++;
+ hist[256 + ((freq0 >> 8) & 0xFF)]++;
+
+ hist[ freq1 & 0xFF]++;
+ hist[256 + ((freq1 >> 8) & 0xFF)]++;
+ }
+
+ if (num_syms & 1)
+ {
+ const uint freq = p->m_freq;
+
+ hist[ freq & 0xFF]++;
+ hist[256 + ((freq >> 8) & 0xFF)]++;
+ }
+ }
+
+ sym_freq* pCur_syms = syms0;
+ sym_freq* pNew_syms = syms1;
+
+ const uint total_passes = (hist[256] == num_syms) ? 1 : cMaxPasses;
+
+ for (uint pass = 0; pass < total_passes; pass++)
+ {
+ const uint* pHist = &hist[pass << 8];
+
+ uint offsets[256];
+
+ uint cur_ofs = 0;
+ for (uint i = 0; i < 256; i += 2)
+ {
+ offsets[i] = cur_ofs;
+ cur_ofs += pHist[i];
+
+ offsets[i+1] = cur_ofs;
+ cur_ofs += pHist[i+1];
+ }
+
+ const uint pass_shift = pass << 3;
+
+ sym_freq* p = pCur_syms;
+ sym_freq* q = pCur_syms + (num_syms >> 1) * 2;
+
+ for ( ; p != q; p += 2)
+ {
+ uint c0 = p[0].m_freq;
+ uint c1 = p[1].m_freq;
+
+ if (pass)
+ {
+ c0 >>= 8;
+ c1 >>= 8;
+ }
+
+ c0 &= 0xFF;
+ c1 &= 0xFF;
+
+ if (c0 == c1)
+ {
+ uint dst_offset0 = offsets[c0];
+
+ offsets[c0] = dst_offset0 + 2;
+
+ pNew_syms[dst_offset0] = p[0];
+ pNew_syms[dst_offset0 + 1] = p[1];
+ }
+ else
+ {
+ uint dst_offset0 = offsets[c0]++;
+ uint dst_offset1 = offsets[c1]++;
+
+ pNew_syms[dst_offset0] = p[0];
+ pNew_syms[dst_offset1] = p[1];
+ }
+ }
+
+ if (num_syms & 1)
+ {
+ uint c = ((p->m_freq) >> pass_shift) & 0xFF;
+
+ uint dst_offset = offsets[c];
+ offsets[c] = dst_offset + 1;
+
+ pNew_syms[dst_offset] = *p;
+ }
+
+ sym_freq* t = pCur_syms;
+ pCur_syms = pNew_syms;
+ pNew_syms = t;
+ }
+
+#if LZHAM_ASSERTS_ENABLED
+ uint prev_freq = 0;
+ for (uint i = 0; i < num_syms; i++)
+ {
+ LZHAM_ASSERT(!(pCur_syms[i].m_freq < prev_freq));
+ prev_freq = pCur_syms[i].m_freq;
+ }
+#endif
+
+ return pCur_syms;
+ }
+
+ struct huffman_work_tables
+ {
+ enum { cMaxInternalNodes = cHuffmanMaxSupportedSyms };
+
+ sym_freq syms0[cHuffmanMaxSupportedSyms + 1 + cMaxInternalNodes];
+ sym_freq syms1[cHuffmanMaxSupportedSyms + 1 + cMaxInternalNodes];
+
+#if !USE_CALCULATE_MINIMUM_REDUNDANCY
+ uint16 queue[cMaxInternalNodes];
+#endif
+ };
+
+ uint get_generate_huffman_codes_table_size()
+ {
+ return sizeof(huffman_work_tables);
+ }
+
+#define USE_CALCULATE_MINIMUM_REDUNDANCY 1
+#if USE_CALCULATE_MINIMUM_REDUNDANCY
+ /* calculate_minimum_redundancy() written by
+ Alistair Moffat, alistair@cs.mu.oz.au,
+ Jyrki Katajainen, jyrki@diku.dk
+ November 1996.
+ */
+ static void calculate_minimum_redundancy(int A[], int n) {
+ int root; /* next root node to be used */
+ int leaf; /* next leaf to be used */
+ int next; /* next value to be assigned */
+ int avbl; /* number of available nodes */
+ int used; /* number of internal nodes */
+ int dpth; /* current depth of leaves */
+
+ /* check for pathological cases */
+ if (n==0) { return; }
+ if (n==1) { A[0] = 0; return; }
+
+ /* first pass, left to right, setting parent pointers */
+ A[0] += A[1]; root = 0; leaf = 2;
+ for (next=1; next < n-1; next++) {
+ /* select first item for a pairing */
+ if (leaf>=n || A[root]=n || (root=0; next--)
+ A[next] = A[A[next]]+1;
+
+ /* third pass, right to left, setting leaf depths */
+ avbl = 1; used = dpth = 0; root = n-2; next = n-1;
+ while (avbl>0) {
+ while (root>=0 && A[root]==dpth) {
+ used++; root--;
+ }
+ while (avbl>used) {
+ A[next--] = dpth; avbl--;
+ }
+ avbl = 2*used; dpth++; used = 0;
+ }
+ }
+#endif
+
+ bool generate_huffman_codes(void* pContext, uint num_syms, const uint16* pFreq, uint8* pCodesizes, uint& max_code_size, uint& total_freq_ret)
+ {
+ if ((!num_syms) || (num_syms > cHuffmanMaxSupportedSyms))
+ return false;
+
+ huffman_work_tables& state = *static_cast(pContext);;
+
+ uint max_freq = 0;
+ uint total_freq = 0;
+
+ uint num_used_syms = 0;
+ for (uint i = 0; i < num_syms; i++)
+ {
+ uint freq = pFreq[i];
+
+ if (!freq)
+ pCodesizes[i] = 0;
+ else
+ {
+ total_freq += freq;
+ max_freq = math::maximum(max_freq, freq);
+
+ sym_freq& sf = state.syms0[num_used_syms];
+ sf.m_left = (uint16)i;
+ sf.m_right = UINT16_MAX;
+ sf.m_freq = freq;
+ num_used_syms++;
+ }
+ }
+
+ total_freq_ret = total_freq;
+
+ if (num_used_syms == 1)
+ {
+ pCodesizes[state.syms0[0].m_left] = 1;
+ return true;
+ }
+
+ sym_freq* syms = radix_sort_syms(num_used_syms, state.syms0, state.syms1);
+
+#if USE_CALCULATE_MINIMUM_REDUNDANCY
+ int x[cHuffmanMaxSupportedSyms];
+ for (uint i = 0; i < num_used_syms; i++)
+ x[i] = syms[i].m_freq;
+
+ calculate_minimum_redundancy(x, num_used_syms);
+
+ uint max_len = 0;
+ for (uint i = 0; i < num_used_syms; i++)
+ {
+ uint len = x[i];
+ max_len = math::maximum(len, max_len);
+ pCodesizes[syms[i].m_left] = static_cast(len);
+ }
+ max_code_size = max_len;
+#else
+ // Computes Huffman codelengths in linear time. More readable than calculate_minimum_redundancy(), and approximately the same speed, but not in-place.
+
+ // Dummy node
+ sym_freq& sf = state.syms0[num_used_syms];
+ sf.m_left = UINT16_MAX;
+ sf.m_right = UINT16_MAX;
+ sf.m_freq = UINT_MAX;
+
+ uint next_internal_node = num_used_syms + 1;
+
+ uint queue_front = 0;
+ uint queue_end = 0;
+
+ uint next_lowest_sym = 0;
+
+ uint num_nodes_remaining = num_used_syms;
+ do
+ {
+ uint left_freq = syms[next_lowest_sym].m_freq;
+ uint left_child = next_lowest_sym;
+
+ if ((queue_end > queue_front) && (syms[state.queue[queue_front]].m_freq < left_freq))
+ {
+ left_child = state.queue[queue_front];
+ left_freq = syms[left_child].m_freq;
+
+ queue_front++;
+ }
+ else
+ next_lowest_sym++;
+
+ uint right_freq = syms[next_lowest_sym].m_freq;
+ uint right_child = next_lowest_sym;
+
+ if ((queue_end > queue_front) && (syms[state.queue[queue_front]].m_freq < right_freq))
+ {
+ right_child = state.queue[queue_front];
+ right_freq = syms[right_child].m_freq;
+
+ queue_front++;
+ }
+ else
+ next_lowest_sym++;
+
+ LZHAM_ASSERT(next_internal_node < huffman_work_tables::cMaxInternalNodes);
+
+ const uint internal_node_index = next_internal_node;
+ next_internal_node++;
+
+ syms[internal_node_index].m_freq = left_freq + right_freq;
+ syms[internal_node_index].m_left = static_cast(left_child);
+ syms[internal_node_index].m_right = static_cast(right_child);
+
+ LZHAM_ASSERT(queue_end < huffman_work_tables::cMaxInternalNodes);
+ state.queue[queue_end] = static_cast(internal_node_index);
+ queue_end++;
+
+ num_nodes_remaining--;
+
+ } while (num_nodes_remaining > 1);
+
+ LZHAM_ASSERT(next_lowest_sym == num_used_syms);
+ LZHAM_ASSERT((queue_end - queue_front) == 1);
+
+ uint cur_node_index = state.queue[queue_front];
+
+ uint32* pStack = (syms == state.syms0) ? (uint32*)state.syms1 : (uint32*)state.syms0;
+ uint32* pStack_top = pStack;
+
+ uint max_level = 0;
+
+ for ( ; ; )
+ {
+ uint level = cur_node_index >> 16;
+ uint node_index = cur_node_index & 0xFFFF;
+
+ uint left_child = syms[node_index].m_left;
+ uint right_child = syms[node_index].m_right;
+
+ uint next_level = (cur_node_index + 0x10000) & 0xFFFF0000;
+
+ if (left_child < num_used_syms)
+ {
+ max_level = math::maximum(max_level, level);
+
+ pCodesizes[syms[left_child].m_left] = static_cast(level + 1);
+
+ if (right_child < num_used_syms)
+ {
+ pCodesizes[syms[right_child].m_left] = static_cast(level + 1);
+
+ if (pStack == pStack_top) break;
+ cur_node_index = *--pStack;
+ }
+ else
+ {
+ cur_node_index = next_level | right_child;
+ }
+ }
+ else
+ {
+ if (right_child < num_used_syms)
+ {
+ max_level = math::maximum(max_level, level);
+
+ pCodesizes[syms[right_child].m_left] = static_cast(level + 1);
+
+ cur_node_index = next_level | left_child;
+ }
+ else
+ {
+ *pStack++ = next_level | left_child;
+
+ cur_node_index = next_level | right_child;
+ }
+ }
+ }
+
+ max_code_size = max_level + 1;
+#endif
+
+ return true;
+ }
+
+} // namespace lzham
+
diff --git a/r5dev/thirdparty/lzham/lzham_lzbase.cpp b/r5dev/thirdparty/lzham/lzham_lzbase.cpp
new file mode 100644
index 00000000..e5cf7c06
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_lzbase.cpp
@@ -0,0 +1,71 @@
+// File: lzham_lzbase.cpp
+// See Copyright Notice and license at the end of include/lzham.h
+#include "include/lzham_core.h"
+#include "include/lzham_lzbase.h"
+
+namespace lzham
+{
+ void CLZBase::init_slot_tabs()
+ {
+ for (uint i = 0; i < m_num_lzx_slots; i++)
+ {
+ //printf("%u: 0x%08X - 0x%08X, %u\n", i, m_lzx_position_base[i], m_lzx_position_base[i] + (1 << m_lzx_position_extra_bits[i]) - 1, m_lzx_position_extra_bits[i]);
+
+ uint lo = m_lzx_position_base[i];
+ uint hi = lo + m_lzx_position_extra_mask[i];
+
+ uint8* pTab;
+ uint shift;
+ uint n; LZHAM_NOTE_UNUSED(n);
+
+ if (hi < 0x1000)
+ {
+ pTab = m_slot_tab0;
+ shift = 0;
+ n = sizeof(m_slot_tab0);
+ }
+ else if (hi < 0x100000)
+ {
+ pTab = m_slot_tab1;
+ shift = 11;
+ n = sizeof(m_slot_tab1);
+ }
+ else if (hi < 0x1000000)
+ {
+ pTab = m_slot_tab2;
+ shift = 16;
+ n = sizeof(m_slot_tab2);
+ }
+ else
+ break;
+
+ lo >>= shift;
+ hi >>= shift;
+
+ LZHAM_ASSERT(hi < n);
+ memset(pTab + lo, (uint8)i, hi - lo + 1);
+ }
+
+#ifdef LZHAM_BUILD_DEBUG
+ uint slot, ofs;
+ for (uint i = 1; i < m_num_lzx_slots; i++)
+ {
+ compute_lzx_position_slot(m_lzx_position_base[i], slot, ofs);
+ LZHAM_ASSERT(slot == i);
+
+ compute_lzx_position_slot(m_lzx_position_base[i] + m_lzx_position_extra_mask[i], slot, ofs);
+ LZHAM_ASSERT(slot == i);
+ }
+
+ for (uint i = 1; i <= (m_dict_size-1); i += 512U*1024U)
+ {
+ compute_lzx_position_slot(i, slot, ofs);
+ LZHAM_ASSERT(i == m_lzx_position_base[slot] + ofs);
+ }
+
+ compute_lzx_position_slot(m_dict_size - 1, slot, ofs);
+ LZHAM_ASSERT((m_dict_size - 1) == m_lzx_position_base[slot] + ofs);
+#endif
+ }
+} //namespace lzham
+
diff --git a/r5dev/thirdparty/lzham/lzham_match_accel.cpp b/r5dev/thirdparty/lzham/lzham_match_accel.cpp
new file mode 100644
index 00000000..629e950f
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_match_accel.cpp
@@ -0,0 +1,562 @@
+// File: lzham_match_accel.cpp
+// See Copyright Notice and license at the end of include/lzham.h
+#include "include/lzham_core.h"
+#include "include/lzham_match_accel.h"
+#include "include/lzham_timer.h"
+
+namespace lzham
+{
+ static inline uint32 hash2_to_12(uint c0, uint c1)
+ {
+ return c0 ^ (c1 << 4);
+ }
+
+ static inline uint32 hash3_to_16(uint c0, uint c1, uint c2)
+ {
+ return (c0 | (c1 << 8)) ^ (c2 << 4);
+ }
+
+ search_accelerator::search_accelerator() :
+ m_pLZBase(NULL),
+ m_pTask_pool(NULL),
+ m_max_helper_threads(0),
+ m_max_dict_size(0),
+ m_max_dict_size_mask(0),
+ m_lookahead_pos(0),
+ m_lookahead_size(0),
+ m_cur_dict_size(0),
+ m_fill_lookahead_pos(0),
+ m_fill_lookahead_size(0),
+ m_fill_dict_size(0),
+ m_max_probes(0),
+ m_max_matches(0),
+ m_all_matches(false),
+ m_next_match_ref(0),
+ m_num_completed_helper_threads(0)
+ {
+ }
+
+ bool search_accelerator::init(CLZBase* pLZBase, task_pool* pPool, uint max_helper_threads, uint max_dict_size, uint max_matches, bool all_matches, uint max_probes)
+ {
+ LZHAM_ASSERT(pLZBase);
+ LZHAM_ASSERT(max_dict_size && math::is_power_of_2(max_dict_size));
+ LZHAM_ASSERT(max_probes);
+
+ m_max_probes = LZHAM_MIN(cMatchAccelMaxSupportedProbes, max_probes);
+
+ m_pLZBase = pLZBase;
+ m_pTask_pool = max_helper_threads ? pPool : NULL;
+ m_max_helper_threads = m_pTask_pool ? max_helper_threads : 0;
+ m_max_matches = LZHAM_MIN(m_max_probes, max_matches);
+ m_all_matches = all_matches;
+
+ m_max_dict_size = max_dict_size;
+ m_max_dict_size_mask = m_max_dict_size - 1;
+ m_cur_dict_size = 0;
+ m_lookahead_size = 0;
+ m_lookahead_pos = 0;
+ m_fill_lookahead_pos = 0;
+ m_fill_lookahead_size = 0;
+ m_fill_dict_size = 0;
+ m_num_completed_helper_threads = 0;
+
+ if (!m_dict.try_resize_no_construct(max_dict_size + LZHAM_MIN(m_max_dict_size, static_cast(CLZBase::cMaxHugeMatchLen))))
+ return false;
+
+ if (!m_hash.try_resize_no_construct(cHashSize))
+ return false;
+
+ if (!m_nodes.try_resize_no_construct(max_dict_size))
+ return false;
+
+ memset(m_hash.get_ptr(), 0, m_hash.size_in_bytes());
+
+ return true;
+ }
+
+ void search_accelerator::reset()
+ {
+ m_cur_dict_size = 0;
+ m_lookahead_size = 0;
+ m_lookahead_pos = 0;
+ m_fill_lookahead_pos = 0;
+ m_fill_lookahead_size = 0;
+ m_fill_dict_size = 0;
+ m_num_completed_helper_threads = 0;
+
+ // Clearing the hash tables is only necessary for determinism (otherwise, it's possible the matches returned after a reset will depend on the data processes before the reset).
+ if (m_hash.size())
+ memset(m_hash.get_ptr(), 0, m_hash.size_in_bytes());
+ if (m_digram_hash.size())
+ memset(m_digram_hash.get_ptr(), 0, m_digram_hash.size_in_bytes());
+ }
+
+ void search_accelerator::flush()
+ {
+ m_cur_dict_size = 0;
+ }
+
+ uint search_accelerator::get_max_add_bytes() const
+ {
+ uint add_pos = static_cast(m_lookahead_pos & (m_max_dict_size - 1));
+ return m_max_dict_size - add_pos;
+ }
+
+ static uint8 g_hamming_dist[256] =
+ {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+ };
+
+ void search_accelerator::find_all_matches_callback(uint64 data, void* pData_ptr)
+ {
+ scoped_perf_section find_all_matches_timer("find_all_matches_callback");
+
+ LZHAM_NOTE_UNUSED(pData_ptr);
+ const uint thread_index = (uint)data;
+
+ dict_match temp_matches[cMatchAccelMaxSupportedProbes * 2];
+
+ uint fill_lookahead_pos = m_fill_lookahead_pos;
+ uint fill_dict_size = m_fill_dict_size;
+ uint fill_lookahead_size = m_fill_lookahead_size;
+
+ uint c0 = 0, c1 = 0;
+ if (fill_lookahead_size >= 2)
+ {
+ c0 = m_dict[fill_lookahead_pos & m_max_dict_size_mask];
+ c1 = m_dict[(fill_lookahead_pos & m_max_dict_size_mask) + 1];
+ }
+
+ const uint8* pDict = m_dict.get_ptr();
+
+ while (fill_lookahead_size >= 3)
+ {
+ uint insert_pos = fill_lookahead_pos & m_max_dict_size_mask;
+
+ uint c2 = pDict[insert_pos + 2];
+ uint h = hash3_to_16(c0, c1, c2);
+ c0 = c1;
+ c1 = c2;
+
+ LZHAM_ASSERT(!m_hash_thread_index.size() || (m_hash_thread_index[h] != UINT8_MAX));
+
+ // Only process those strings that this worker thread was assigned to - this allows us to manipulate multiple trees in parallel with no worries about synchronization.
+ if (m_hash_thread_index.size() && (m_hash_thread_index[h] != thread_index))
+ {
+ fill_lookahead_pos++;
+ fill_lookahead_size--;
+ fill_dict_size++;
+ continue;
+ }
+
+ dict_match* pDstMatch = temp_matches;
+
+ uint cur_pos = m_hash[h];
+ m_hash[h] = static_cast(fill_lookahead_pos);
+
+ uint *pLeft = &m_nodes[insert_pos].m_left;
+ uint *pRight = &m_nodes[insert_pos].m_right;
+
+ const uint max_match_len = LZHAM_MIN(static_cast(CLZBase::cMaxMatchLen), fill_lookahead_size);
+ uint best_match_len = 2;
+
+ const uint8* pIns = &pDict[insert_pos];
+
+ uint n = m_max_probes;
+ for ( ; ; )
+ {
+ uint delta_pos = fill_lookahead_pos - cur_pos;
+ if ((n-- == 0) || (!delta_pos) || (delta_pos >= fill_dict_size))
+ {
+ *pLeft = 0;
+ *pRight = 0;
+ break;
+ }
+
+ uint pos = cur_pos & m_max_dict_size_mask;
+ node *pNode = &m_nodes[pos];
+
+ // Unfortunately, the initial compare match_len must be 0 because of the way we hash and truncate matches at the end of each block.
+ uint match_len = 0;
+ const uint8* pComp = &pDict[pos];
+
+#if LZHAM_PLATFORM_X360
+ for ( ; match_len < max_match_len; match_len++)
+ if (pComp[match_len] != pIns[match_len])
+ break;
+#else
+ // Compare a qword at a time for a bit more efficiency.
+ const uint64* pComp_end = reinterpret_cast(pComp + max_match_len - 7);
+ const uint64* pComp_cur = reinterpret_cast(pComp);
+ const uint64* pIns_cur = reinterpret_cast(pIns);
+ while (pComp_cur < pComp_end)
+ {
+ if (*pComp_cur != *pIns_cur)
+ break;
+ pComp_cur++;
+ pIns_cur++;
+ }
+ uint alt_match_len = static_cast(reinterpret_cast(pComp_cur) - reinterpret_cast(pComp));
+ for ( ; alt_match_len < max_match_len; alt_match_len++)
+ if (pComp[alt_match_len] != pIns[alt_match_len])
+ break;
+#ifdef LZVERIFY
+ for ( ; match_len < max_match_len; match_len++)
+ if (pComp[match_len] != pIns[match_len])
+ break;
+ LZHAM_VERIFY(alt_match_len == match_len);
+#endif
+ match_len = alt_match_len;
+#endif
+
+ if (match_len > best_match_len)
+ {
+ pDstMatch->m_len = static_cast(match_len - CLZBase::cMinMatchLen);
+ pDstMatch->m_dist = delta_pos;
+ pDstMatch++;
+
+ best_match_len = match_len;
+
+ if (match_len == max_match_len)
+ {
+ *pLeft = pNode->m_left;
+ *pRight = pNode->m_right;
+ break;
+ }
+ }
+ else if (m_all_matches)
+ {
+ pDstMatch->m_len = static_cast(match_len - CLZBase::cMinMatchLen);
+ pDstMatch->m_dist = delta_pos;
+ pDstMatch++;
+ }
+ else if ((best_match_len > 2) && (best_match_len == match_len))
+ {
+ uint bestMatchDist = pDstMatch[-1].m_dist;
+ uint compMatchDist = delta_pos;
+
+ uint bestMatchSlot, bestMatchSlotOfs;
+ m_pLZBase->compute_lzx_position_slot(bestMatchDist, bestMatchSlot, bestMatchSlotOfs);
+
+ uint compMatchSlot, compMatchOfs;
+ m_pLZBase->compute_lzx_position_slot(compMatchDist, compMatchSlot, compMatchOfs);
+
+ // If both matches uses the same match slot, choose the one with the offset containing the lowest nibble as these bits separately entropy coded.
+ // This could choose a match which is further away in the absolute sense, but closer in a coding sense.
+ if ( (compMatchSlot < bestMatchSlot) ||
+ ((compMatchSlot >= 8) && (compMatchSlot == bestMatchSlot) && ((compMatchOfs & 15) < (bestMatchSlotOfs & 15))) )
+ {
+ LZHAM_ASSERT((pDstMatch[-1].m_len + (uint)CLZBase::cMinMatchLen) == best_match_len);
+ pDstMatch[-1].m_dist = delta_pos;
+ }
+ else if ((match_len < max_match_len) && (compMatchSlot <= bestMatchSlot))
+ {
+ // Choose the match which has lowest hamming distance in the mismatch byte for a tiny win on binary files.
+ // TODO: This competes against the prev. optimization.
+ uint desired_mismatch_byte = pIns[match_len];
+
+ uint cur_mismatch_byte = pDict[(insert_pos - bestMatchDist + match_len) & m_max_dict_size_mask];
+ uint cur_mismatch_dist = g_hamming_dist[cur_mismatch_byte ^ desired_mismatch_byte];
+
+ uint new_mismatch_byte = pComp[match_len];
+ uint new_mismatch_dist = g_hamming_dist[new_mismatch_byte ^ desired_mismatch_byte];
+ if (new_mismatch_dist < cur_mismatch_dist)
+ {
+ LZHAM_ASSERT((pDstMatch[-1].m_len + (uint)CLZBase::cMinMatchLen) == best_match_len);
+ pDstMatch[-1].m_dist = delta_pos;
+ }
+ }
+ }
+
+ uint new_pos;
+ if (pComp[match_len] < pIns[match_len])
+ {
+ *pLeft = cur_pos;
+ pLeft = &pNode->m_right;
+ new_pos = pNode->m_right;
+ }
+ else
+ {
+ *pRight = cur_pos;
+ pRight = &pNode->m_left;
+ new_pos = pNode->m_left;
+ }
+ if (new_pos == cur_pos)
+ break;
+ cur_pos = new_pos;
+ }
+
+ const uint num_matches = (uint)(pDstMatch - temp_matches);
+
+ if (num_matches)
+ {
+ pDstMatch[-1].m_dist |= 0x80000000;
+
+ const uint num_matches_to_write = LZHAM_MIN(num_matches, m_max_matches);
+
+ const uint match_ref_ofs = atomic_exchange_add(&m_next_match_ref, num_matches_to_write);
+
+ memcpy(&m_matches[match_ref_ofs],
+ temp_matches + (num_matches - num_matches_to_write),
+ sizeof(temp_matches[0]) * num_matches_to_write);
+
+ // FIXME: This is going to really hurt on platforms requiring export barriers.
+ LZHAM_MEMORY_EXPORT_BARRIER
+
+ atomic_exchange32((atomic32_t*)&m_match_refs[static_cast(fill_lookahead_pos - m_fill_lookahead_pos)], match_ref_ofs);
+ }
+ else
+ {
+ atomic_exchange32((atomic32_t*)&m_match_refs[static_cast(fill_lookahead_pos - m_fill_lookahead_pos)], -2);
+ }
+
+ fill_lookahead_pos++;
+ fill_lookahead_size--;
+ fill_dict_size++;
+ }
+
+ while (fill_lookahead_size)
+ {
+ uint insert_pos = fill_lookahead_pos & m_max_dict_size_mask;
+ m_nodes[insert_pos].m_left = 0;
+ m_nodes[insert_pos].m_right = 0;
+
+ atomic_exchange32((atomic32_t*)&m_match_refs[static_cast(fill_lookahead_pos - m_fill_lookahead_pos)], -2);
+
+ fill_lookahead_pos++;
+ fill_lookahead_size--;
+ fill_dict_size++;
+ }
+
+ atomic_increment32(&m_num_completed_helper_threads);
+ }
+
+ bool search_accelerator::find_len2_matches()
+ {
+ if (!m_digram_hash.size())
+ {
+ if (!m_digram_hash.try_resize(cDigramHashSize))
+ return false;
+ }
+
+ if (m_digram_next.size() < m_lookahead_size)
+ {
+ if (!m_digram_next.try_resize(m_lookahead_size))
+ return false;
+ }
+
+ uint lookahead_dict_pos = m_lookahead_pos & m_max_dict_size_mask;
+
+ for (int lookahead_ofs = 0; lookahead_ofs < ((int)m_lookahead_size - 1); ++lookahead_ofs, ++lookahead_dict_pos)
+ {
+ uint c0 = m_dict[lookahead_dict_pos];
+ uint c1 = m_dict[lookahead_dict_pos + 1];
+
+ uint h = hash2_to_12(c0, c1) & (cDigramHashSize - 1);
+
+ m_digram_next[lookahead_ofs] = m_digram_hash[h];
+ m_digram_hash[h] = m_lookahead_pos + lookahead_ofs;
+ }
+
+ m_digram_next[m_lookahead_size - 1] = 0;
+
+ return true;
+ }
+
+ uint search_accelerator::get_len2_match(uint lookahead_ofs)
+ {
+ if ((m_fill_lookahead_size - lookahead_ofs) < 2)
+ return 0;
+
+ uint cur_pos = m_lookahead_pos + lookahead_ofs;
+
+ uint next_match_pos = m_digram_next[cur_pos - m_fill_lookahead_pos];
+
+ uint match_dist = cur_pos - next_match_pos;
+
+ if ((!match_dist) || (match_dist > CLZBase::cMaxLen2MatchDist) || (match_dist > (m_cur_dict_size + lookahead_ofs)))
+ return 0;
+
+ const uint8* pCur = &m_dict[cur_pos & m_max_dict_size_mask];
+ const uint8* pMatch = &m_dict[next_match_pos & m_max_dict_size_mask];
+
+ if ((pCur[0] == pMatch[0]) && (pCur[1] == pMatch[1]))
+ return match_dist;
+
+ return 0;
+ }
+
+ bool search_accelerator::find_all_matches(uint num_bytes)
+ {
+ if (!m_matches.try_resize_no_construct(m_max_probes * num_bytes))
+ return false;
+
+ if (!m_match_refs.try_resize_no_construct(num_bytes))
+ return false;
+
+ memset(m_match_refs.get_ptr(), 0xFF, m_match_refs.size_in_bytes());
+
+ m_fill_lookahead_pos = m_lookahead_pos;
+ m_fill_lookahead_size = num_bytes;
+ m_fill_dict_size = m_cur_dict_size;
+
+ m_next_match_ref = 0;
+
+ if (!m_pTask_pool)
+ {
+ find_all_matches_callback(0, NULL);
+
+ m_num_completed_helper_threads = 0;
+ }
+ else
+ {
+ if (!m_hash_thread_index.try_resize_no_construct(0x10000))
+ return false;
+
+ memset(m_hash_thread_index.get_ptr(), 0xFF, m_hash_thread_index.size_in_bytes());
+
+ uint next_thread_index = 0;
+ const uint8* pDict = &m_dict[m_lookahead_pos & m_max_dict_size_mask];
+ uint num_unique_trigrams = 0;
+
+ if (num_bytes >= 3)
+ {
+ uint c0 = pDict[0];
+ uint c1 = pDict[1];
+
+ const int limit = ((int)num_bytes - 2);
+ for (int i = 0; i < limit; i++)
+ {
+ uint c2 = pDict[2];
+ uint t = hash3_to_16(c0, c1, c2);
+ c0 = c1;
+ c1 = c2;
+
+ pDict++;
+
+ if (m_hash_thread_index[t] == UINT8_MAX)
+ {
+ num_unique_trigrams++;
+
+ m_hash_thread_index[t] = static_cast(next_thread_index);
+ if (++next_thread_index == m_max_helper_threads)
+ next_thread_index = 0;
+ }
+ }
+ }
+
+ m_num_completed_helper_threads = 0;
+
+ if (!m_pTask_pool->queue_multiple_object_tasks(this, &search_accelerator::find_all_matches_callback, 0, m_max_helper_threads))
+ return false;
+ }
+
+ return find_len2_matches();
+ }
+
+ bool search_accelerator::add_bytes_begin(uint num_bytes, const uint8* pBytes)
+ {
+ LZHAM_ASSERT(num_bytes <= m_max_dict_size);
+ LZHAM_ASSERT(!m_lookahead_size);
+
+ uint add_pos = m_lookahead_pos & m_max_dict_size_mask;
+ LZHAM_ASSERT((add_pos + num_bytes) <= m_max_dict_size);
+
+ memcpy(&m_dict[add_pos], pBytes, num_bytes);
+
+ uint dict_bytes_to_mirror = LZHAM_MIN(static_cast(CLZBase::cMaxHugeMatchLen), m_max_dict_size);
+ if (add_pos < dict_bytes_to_mirror)
+ memcpy(&m_dict[m_max_dict_size], &m_dict[0], dict_bytes_to_mirror);
+
+ m_lookahead_size = num_bytes;
+
+ uint max_possible_dict_size = m_max_dict_size - num_bytes;
+ m_cur_dict_size = LZHAM_MIN(m_cur_dict_size, max_possible_dict_size);
+
+ m_next_match_ref = 0;
+
+ return find_all_matches(num_bytes);
+ }
+
+ void search_accelerator::add_bytes_end()
+ {
+ if (m_pTask_pool)
+ {
+ m_pTask_pool->join();
+ }
+
+ LZHAM_ASSERT((uint)m_next_match_ref <= m_matches.size());
+ }
+
+ dict_match* search_accelerator::find_matches(uint lookahead_ofs, bool spin)
+ {
+ LZHAM_ASSERT(lookahead_ofs < m_lookahead_size);
+
+ const uint match_ref_ofs = static_cast(m_lookahead_pos - m_fill_lookahead_pos + lookahead_ofs);
+
+ int match_ref;
+ uint spin_count = 0;
+
+ // This may spin until the match finder job(s) catch up to the caller's lookahead position.
+ for ( ; ; )
+ {
+ match_ref = m_match_refs[match_ref_ofs];
+ if (match_ref == -2)
+ return NULL;
+ else if (match_ref != -1)
+ break;
+
+ spin_count++;
+ const uint cMaxSpinCount = 1000;
+ if ((spin) && (spin_count < cMaxSpinCount))
+ {
+ lzham_yield_processor();
+ lzham_yield_processor();
+ lzham_yield_processor();
+ lzham_yield_processor();
+ lzham_yield_processor();
+ lzham_yield_processor();
+ lzham_yield_processor();
+ lzham_yield_processor();
+
+ LZHAM_MEMORY_IMPORT_BARRIER
+ }
+ else
+ {
+ spin_count = cMaxSpinCount;
+
+ lzham_sleep(1);
+ }
+ }
+
+ LZHAM_MEMORY_IMPORT_BARRIER
+
+ return &m_matches[match_ref];
+ }
+
+ void search_accelerator::advance_bytes(uint num_bytes)
+ {
+ LZHAM_ASSERT(num_bytes <= m_lookahead_size);
+
+ m_lookahead_pos += num_bytes;
+ m_lookahead_size -= num_bytes;
+
+ m_cur_dict_size += num_bytes;
+ LZHAM_ASSERT(m_cur_dict_size <= m_max_dict_size);
+ }
+}
diff --git a/r5dev/thirdparty/lzham/lzham_mem.cpp b/r5dev/thirdparty/lzham/lzham_mem.cpp
new file mode 100644
index 00000000..0c961ed6
--- /dev/null
+++ b/r5dev/thirdparty/lzham/lzham_mem.cpp
@@ -0,0 +1,272 @@
+// File: lzham_mem.cpp
+// See Copyright Notice and license at the end of include/lzham.h
+#include "include/lzham_core.h"
+#include
+#include
+#include
+
+using namespace lzham;
+
+#define LZHAM_MEM_STATS 0
+
+#ifndef LZHAM_USE_WIN32_API
+ #define _msize malloc_usable_size
+#endif
+
+namespace lzham
+{
+ #if LZHAM_64BIT_POINTERS
+ const uint64 MAX_POSSIBLE_BLOCK_SIZE = 0x400000000ULL;
+ #else
+ const uint32 MAX_POSSIBLE_BLOCK_SIZE = 0x7FFF0000U;
+ #endif
+
+ #if LZHAM_MEM_STATS
+ #if LZHAM_64BIT_POINTERS
+ typedef atomic64_t mem_stat_t;
+ #define LZHAM_MEM_COMPARE_EXCHANGE atomic_compare_exchange64
+ #else
+ typedef atomic32_t mem_stat_t;
+ #define LZHAM_MEM_COMPARE_EXCHANGE atomic_compare_exchange32
+ #endif
+
+ static volatile atomic32_t g_total_blocks;
+ static volatile mem_stat_t g_total_allocated;
+ static volatile mem_stat_t g_max_allocated;
+
+ static mem_stat_t update_total_allocated(int block_delta, mem_stat_t byte_delta)
+ {
+ atomic32_t cur_total_blocks;
+ for ( ; ; )
+ {
+ cur_total_blocks = g_total_blocks;
+ atomic32_t new_total_blocks = static_cast(cur_total_blocks + block_delta);
+ LZHAM_ASSERT(new_total_blocks >= 0);
+ if (atomic_compare_exchange32(&g_total_blocks, new_total_blocks, cur_total_blocks) == cur_total_blocks)
+ break;
+ }
+
+ mem_stat_t cur_total_allocated, new_total_allocated;
+ for ( ; ; )
+ {
+ cur_total_allocated = g_total_allocated;
+ new_total_allocated = static_cast(cur_total_allocated + byte_delta);
+ LZHAM_ASSERT(new_total_allocated >= 0);
+ if (LZHAM_MEM_COMPARE_EXCHANGE(&g_total_allocated, new_total_allocated, cur_total_allocated) == cur_total_allocated)
+ break;
+ }
+ for ( ; ; )
+ {
+ mem_stat_t cur_max_allocated = g_max_allocated;
+ mem_stat_t new_max_allocated = LZHAM_MAX(new_total_allocated, cur_max_allocated);
+ if (LZHAM_MEM_COMPARE_EXCHANGE(&g_max_allocated, new_max_allocated, cur_max_allocated) == cur_max_allocated)
+ break;
+ }
+ return new_total_allocated;
+ }
+ #endif // LZHAM_MEM_STATS
+
+ static void* lzham_default_realloc(void* p, size_t size, size_t* pActual_size, lzham_bool movable, void* pUser_data)
+ {
+ LZHAM_NOTE_UNUSED(pUser_data);
+
+ void* p_new;
+
+ if (!p)
+ {
+ p_new = malloc(size);
+ LZHAM_ASSERT( (reinterpret_cast(p_new) & (LZHAM_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
+
+ if (pActual_size)
+ *pActual_size = p_new ? _msize(p_new) : 0;
+ }
+ else if (!size)
+ {
+ free(p);
+ p_new = NULL;
+
+ if (pActual_size)
+ *pActual_size = 0;
+ }
+ else
+ {
+ void* p_final_block = p;
+#ifdef WIN32
+ p_new = _expand(p, size);
+#else
+
+ p_new = NULL;
+#endif
+
+ if (p_new)
+ {
+ LZHAM_ASSERT( (reinterpret_cast(p_new) & (LZHAM_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
+ p_final_block = p_new;
+ }
+ else if (movable)
+ {
+ p_new = realloc(p, size);
+
+ if (p_new)
+ {
+ LZHAM_ASSERT( (reinterpret_cast(p_new) & (LZHAM_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
+ p_final_block = p_new;
+ }
+ }
+
+ if (pActual_size)
+ *pActual_size = _msize(p_final_block);
+ }
+
+ return p_new;
+ }
+
+ static size_t lzham_default_msize(void* p, void* pUser_data)
+ {
+ LZHAM_NOTE_UNUSED(pUser_data);
+ return p ? _msize(p) : 0;
+ }
+
+ static lzham_realloc_func g_pRealloc = lzham_default_realloc;
+ static lzham_msize_func g_pMSize = lzham_default_msize;
+ static void* g_pUser_data;
+
+ static inline void lzham_mem_error(const char* p_msg)
+ {
+ lzham_assert(p_msg, __FILE__, __LINE__);
+ }
+
+ void* lzham_malloc(size_t size, size_t* pActual_size)
+ {
+ size = (size + sizeof(uint32) - 1U) & ~(sizeof(uint32) - 1U);
+ if (!size)
+ size = sizeof(uint32);
+
+ if (size > MAX_POSSIBLE_BLOCK_SIZE)
+ {
+ lzham_mem_error("lzham_malloc: size too big");
+ return NULL;
+ }
+
+ size_t actual_size = size;
+ uint8* p_new = static_cast((*g_pRealloc)(NULL, size, &actual_size, true, g_pUser_data));
+
+ if (pActual_size)
+ *pActual_size = actual_size;
+
+ if ((!p_new) || (actual_size < size))
+ {
+ lzham_mem_error("lzham_malloc: out of memory");
+ return NULL;
+ }
+
+ LZHAM_ASSERT((reinterpret_cast(p_new) & (LZHAM_MIN_ALLOC_ALIGNMENT - 1)) == 0);
+
+#if LZHAM_MEM_STATS
+ update_total_allocated(1, static_cast(actual_size));
+#endif
+
+ return p_new;
+ }
+
+ void* lzham_realloc(void* p, size_t size, size_t* pActual_size, bool movable)
+ {
+ if ((ptr_bits_t)p & (LZHAM_MIN_ALLOC_ALIGNMENT - 1))
+ {
+ lzham_mem_error("lzham_realloc: bad ptr");
+ return NULL;
+ }
+
+ if (size > MAX_POSSIBLE_BLOCK_SIZE)
+ {
+ lzham_mem_error("lzham_malloc: size too big");
+ return NULL;
+ }
+
+#if LZHAM_MEM_STATS
+ size_t cur_size = p ? (*g_pMSize)(p, g_pUser_data) : 0;
+#endif
+
+ size_t actual_size = size;
+ void* p_new = (*g_pRealloc)(p, size, &actual_size, movable, g_pUser_data);
+
+ if (pActual_size)
+ *pActual_size = actual_size;
+
+ LZHAM_ASSERT((reinterpret_cast