Compare commits
45 Commits
9e6a4318c8
...
v0.1.10
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d0fcc4204 | |||
| f01cf70179 | |||
| 5421c42282 | |||
| a2e13f8935 | |||
| 4197b4f52e | |||
| 075590e9b9 | |||
| 6d4fab7d9f | |||
| 91b4f33ca1 | |||
| 95e0447190 | |||
| f1e735038b | |||
| bedd89d0b8 | |||
| bdde4f02a0 | |||
| 873405da66 | |||
| 2b4a8c833a | |||
| 859203aa74 | |||
| ac72ea836b | |||
| 32b59539fe | |||
| c8a11d975a | |||
| aafb9910a0 | |||
| aadf174fdb | |||
| 2ce8d0b425 | |||
| 65a2e8a39f | |||
| eddce75e91 | |||
| 2c60ab2360 | |||
| 823c89552b | |||
| fa2d0d0efc | |||
| edaf6e8fd6 | |||
| e94b25f28c | |||
| 5845e2e215 | |||
| 63d41e8f81 | |||
| e492b8ea3b | |||
| 96746626bd | |||
| 5d2c15d2b1 | |||
| 4c55f7c123 | |||
| 0eb7af76d4 | |||
| de76e89ed0 | |||
| 1275adb5fd | |||
| 9ac9be7302 | |||
| 2553f5ab4b | |||
| 3231638ce7 | |||
| 93900a56cb | |||
| 93685b7ff0 | |||
| 25d9f9f631 | |||
| 3de7a7bb44 | |||
| b238f4f901 |
@@ -0,0 +1,134 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "master" ]
|
||||
pull_request:
|
||||
branches: [ "main", "master" ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Configure version information
|
||||
id: calculate-version
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
shell: bash
|
||||
run: |
|
||||
git fetch --tags || true
|
||||
|
||||
# Determine last tag and log range
|
||||
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
|
||||
LAST_TAG=$(git describe --tags --abbrev=0)
|
||||
RANGE="${LAST_TAG}..HEAD"
|
||||
else
|
||||
LAST_TAG="v0.1.9"
|
||||
RANGE="HEAD"
|
||||
fi
|
||||
|
||||
# Calculate next version
|
||||
IFS='.' read -r major minor patch <<< "${LAST_TAG#v}"
|
||||
NEW_VERSION="$major.$minor.$((patch + 1))"
|
||||
NEW_TAG="v$NEW_VERSION"
|
||||
|
||||
# Log commits
|
||||
CHANGELOG=$(git log $RANGE --oneline | sed 's/^/* /')
|
||||
[ -z "$CHANGELOG" ] && CHANGELOG="Maintenance build."
|
||||
|
||||
# Tag and push back to Gitea
|
||||
git tag $NEW_TAG
|
||||
git push origin $NEW_TAG
|
||||
|
||||
# Set outputs for next steps
|
||||
echo "version-string=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "## What's Changed" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGELOG" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Tools Cache
|
||||
id: tools-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
cmake-3.30.5-linux-x86_64
|
||||
ccache
|
||||
key: ${{ runner.os }}-tools-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y mingw-w64 zstd ccache zip
|
||||
mkdir -p ccache
|
||||
echo "CCACHE_DIR=$GITHUB_WORKSPACE/ccache" >> $GITHUB_ENV
|
||||
|
||||
- name: Install CMake 3.30
|
||||
if: steps.tools-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget -q https://github.com/Kitware/CMake/releases/download/v3.30.5/cmake-3.30.5-linux-x86_64.tar.gz
|
||||
tar xzf cmake-3.30.5-linux-x86_64.tar.gz
|
||||
|
||||
- name: Add CMake to path
|
||||
run: echo "$GITHUB_WORKSPACE/cmake-3.30.5-linux-x86_64/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Get OpenSSL for MinGW (pre-built from MSYS2)
|
||||
run: |
|
||||
if [ ! -d "/usr/x86_64-w64-mingw32/include/openssl" ]; then
|
||||
wget -q "https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-openssl-3.3.2-1-any.pkg.tar.zst"
|
||||
tar xf mingw-w64-x86_64-openssl-3.3.2-1-any.pkg.tar.zst
|
||||
sudo cp -r mingw64/include/openssl /usr/x86_64-w64-mingw32/include/
|
||||
sudo cp mingw64/lib/libssl.a mingw64/lib/libcrypto.a /usr/x86_64-w64-mingw32/lib/
|
||||
fi
|
||||
|
||||
- name: Patch missing MinGW headers
|
||||
run: |
|
||||
[ -f /usr/x86_64-w64-mingw32/include/consoleapi.h ] || \
|
||||
printf '#pragma once\n#include <windows.h>\n' | \
|
||||
sudo tee /usr/x86_64-w64-mingw32/include/consoleapi.h > /dev/null
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B build -S . \
|
||||
-DCMAKE_SYSTEM_NAME=Windows \
|
||||
-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc \
|
||||
-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ \
|
||||
-DCMAKE_RC_COMPILER=x86_64-w64-mingw32-windres \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DOPENSSL_ROOT_DIR=/usr/x86_64-w64-mingw32 \
|
||||
-DOPENSSL_INCLUDE_DIR=/usr/x86_64-w64-mingw32/include \
|
||||
-DOPENSSL_LIBRARIES="/usr/x86_64-w64-mingw32/lib/libssl.a;/usr/x86_64-w64-mingw32/lib/libcrypto.a"
|
||||
|
||||
- name: Build Unlocker
|
||||
run: cmake --build build --target dbd-unlocker
|
||||
|
||||
- name: Package Build
|
||||
run: |
|
||||
EXE=$(find build -name "*.exe" -not -path "*/CMakeFiles/*" | head -1)
|
||||
echo "Found EXE: $EXE"
|
||||
mkdir -p release
|
||||
cp "$EXE" release/
|
||||
cp res/*.json release/
|
||||
cd release && zip -j ../unlocker.zip *
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: unlocker-build
|
||||
path: unlocker.zip
|
||||
|
||||
- name: Create Gitea Release
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
files: unlocker.zip
|
||||
tag_name: v${{ steps.calculate-version.outputs.version-string }}
|
||||
name: Release v${{ steps.calculate-version.outputs.version-string }}
|
||||
body: ${{ steps.calculate-version.outputs.changelog }}
|
||||
+34
-17
@@ -1,18 +1,29 @@
|
||||
# ------------------------------
|
||||
# cmake config / project
|
||||
# ------------------------------
|
||||
cmake_minimum_required(VERSION 4.1.0)
|
||||
cmake_minimum_required(VERSION 3.25.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_FLAGS "/EHsc /D_AMD64_ /DWIN32_LEAN_AND_MEAN") # Set C++ exception handler, define platform and ignore some unused headers
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "/EHsc /D_AMD64_ /DWIN32_LEAN_AND_MEAN")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/O2 /DNDEBUG")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "-D_AMD64_ -DWIN32_LEAN_AND_MEAN")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
endif()
|
||||
|
||||
project(dbd-unlocker LANGUAGES CXX CSharp)
|
||||
project(dbd-unlocker LANGUAGES CXX)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
enable_language(CSharp)
|
||||
endif()
|
||||
|
||||
# ------------------------------
|
||||
# msvc stuff
|
||||
# ------------------------------
|
||||
set(CMAKE_CSharp_PROJECT_TYPE_GUID "9A19103F-16F7-4668-BE54-9A1E7A4F7556")
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
set(CMAKE_CSharp_PROJECT_TYPE_GUID "9A19103F-16F7-4668-BE54-9A1E7A4F7556")
|
||||
endif()
|
||||
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
string(APPEND CMAKE_CXX_FLAGS " /EHsc /DWIN32_LEAN_AND_MEAN /utf-8 /Zc:char8_t")
|
||||
@@ -73,7 +84,16 @@ endif()
|
||||
# ------------------------------
|
||||
file(GLOB_RECURSE UNLOCKER_SOURCES CONFIGURE_DEPENDS "src/unlocker/*.cpp" "src/unlocker/*.h")
|
||||
add_executable(dbd-unlocker ${UNLOCKER_SOURCES})
|
||||
target_link_libraries(dbd-unlocker PRIVATE dbd-unlocker-warnings OpenSSL::SSL OpenSSL::Crypto nerutils wsock32 ws2_32 wininet simdjson-wrapped)
|
||||
|
||||
# randomize exe name
|
||||
string(RANDOM LENGTH 12 ALPHABET "abcdefghijklmnopqrstuvwxyz0123456789" RANDOM_EXE_NAME)
|
||||
set_target_properties(dbd-unlocker PROPERTIES OUTPUT_NAME "${RANDOM_EXE_NAME}")
|
||||
|
||||
target_link_libraries(dbd-unlocker PRIVATE dbd-unlocker-warnings OpenSSL::SSL OpenSSL::Crypto nerutils wsock32 ws2_32 wininet crypt32 simdjson-wrapped)
|
||||
|
||||
if(NOT MSVC)
|
||||
target_link_options(dbd-unlocker PRIVATE -static -static-libgcc -static-libstdc++)
|
||||
endif()
|
||||
|
||||
set_property(TARGET dbd-unlocker PROPERTY USE_FOLDERS ON)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT dbd-unlocker)
|
||||
@@ -90,17 +110,14 @@ add_custom_command(TARGET dbd-unlocker POST_BUILD
|
||||
)
|
||||
|
||||
# ------------------------------
|
||||
# dumper
|
||||
# dumper (VS only)
|
||||
# ------------------------------
|
||||
include_external_msproject(dbd-dumper "${CMAKE_CURRENT_SOURCE_DIR}/src/dumper/dbd-dumper.csproj")
|
||||
|
||||
include_external_msproject(CUE4Parse "${CMAKE_CURRENT_SOURCE_DIR}/vendor/CUE4Parse/CUE4Parse/CUE4Parse.csproj")
|
||||
|
||||
set_target_properties(dbd-dumper PROPERTIES VS_PROJECT_TYPE "9A19103F-16F7-4668-BE54-9A1E7A4F7556")
|
||||
set_target_properties(CUE4Parse PROPERTIES VS_PROJECT_TYPE "9A19103F-16F7-4668-BE54-9A1E7A4F7556")
|
||||
|
||||
|
||||
add_dependencies(dbd-dumper CUE4Parse)
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT dbd-unlocker)
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
include_external_msproject(dbd-dumper "${CMAKE_CURRENT_SOURCE_DIR}/src/dumper/dbd-dumper.csproj")
|
||||
include_external_msproject(CUE4Parse "${CMAKE_CURRENT_SOURCE_DIR}/vendor/CUE4Parse/CUE4Parse/CUE4Parse.csproj")
|
||||
set_target_properties(dbd-dumper PROPERTIES VS_PROJECT_TYPE "9A19103F-16F7-4668-BE54-9A1E7A4F7556")
|
||||
set_target_properties(CUE4Parse PROPERTIES VS_PROJECT_TYPE "9A19103F-16F7-4668-BE54-9A1E7A4F7556")
|
||||
add_dependencies(dbd-dumper CUE4Parse)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT dbd-unlocker)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
echo formatting sources
|
||||
|
||||
for /r "src" %%f in (*.cpp *.h *.cs) do (
|
||||
echo Formatting: %%f
|
||||
clang-format -i "%%f"
|
||||
)
|
||||
|
||||
echo done
|
||||
pause
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#include <processthreadsapi.h>
|
||||
|
||||
std::string randomizeString(size_t length)
|
||||
{
|
||||
const char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
@@ -131,7 +133,15 @@ bool CertManager::GenerateCA()
|
||||
|
||||
Log::info("Generated new CA key and certificate files. Installing to Windows Root CA store automatically...");
|
||||
|
||||
system("certutil.exe -user -addstore root ca_cert.pem");
|
||||
STARTUPINFOA si = {sizeof(si)};
|
||||
PROCESS_INFORMATION pi;
|
||||
char cmd[] = "certutil.exe -user -addstore root ca_cert.pem";
|
||||
if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
|
||||
{
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -146,8 +156,6 @@ SSL_CTX* CertManager::CreateHostContext(const std::string& host)
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Log::verbose("Generating dynamic certificate for {}", host);
|
||||
|
||||
EVP_PKEY* pkey = _sessionPkey;
|
||||
if (!pkey) return nullptr;
|
||||
|
||||
|
||||
+16
-15
@@ -5,22 +5,23 @@
|
||||
#include <mutex>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
class CertManager {
|
||||
public:
|
||||
CertManager();
|
||||
~CertManager();
|
||||
class CertManager
|
||||
{
|
||||
public:
|
||||
CertManager();
|
||||
~CertManager();
|
||||
|
||||
bool Init();
|
||||
SSL_CTX* CreateHostContext(const std::string& host);
|
||||
bool Init();
|
||||
SSL_CTX* CreateHostContext(const std::string& host);
|
||||
|
||||
private:
|
||||
bool GenerateCA();
|
||||
bool LoadCA();
|
||||
private:
|
||||
bool GenerateCA();
|
||||
bool LoadCA();
|
||||
|
||||
EVP_PKEY* _caPkey = nullptr;
|
||||
X509* _caCert = nullptr;
|
||||
EVP_PKEY* _sessionPkey = nullptr;
|
||||
|
||||
std::mutex _mutex;
|
||||
std::unordered_map<std::string, SSL_CTX*> _hostContexts;
|
||||
EVP_PKEY* _caPkey = nullptr;
|
||||
X509* _caCert = nullptr;
|
||||
EVP_PKEY* _sessionPkey = nullptr;
|
||||
|
||||
std::mutex _mutex;
|
||||
std::unordered_map<std::string, SSL_CTX*> _hostContexts;
|
||||
};
|
||||
|
||||
+109
-96
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <nerutils/log.h>
|
||||
|
||||
#include <Windows.h>
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -62,12 +62,35 @@ bool setProxy(bool enable, const std::string& proxyAddr)
|
||||
return true;
|
||||
}
|
||||
|
||||
Proxy* g_proxy = nullptr;
|
||||
bool running = true;
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
static std::mutex cleanupMutex;
|
||||
std::lock_guard<std::mutex> lock(cleanupMutex);
|
||||
static bool cleaned = false;
|
||||
if (cleaned) return;
|
||||
cleaned = true;
|
||||
|
||||
if (g_proxy)
|
||||
{
|
||||
Log::info("Shutting down proxy");
|
||||
g_proxy->Shutdown();
|
||||
}
|
||||
|
||||
Log::info("Restoring system proxy settings");
|
||||
setProxy(false, "");
|
||||
}
|
||||
|
||||
BOOL WINAPI consoleHandler(DWORD dwType)
|
||||
{
|
||||
if (dwType == CTRL_C_EVENT || dwType == CTRL_CLOSE_EVENT)
|
||||
if (dwType == CTRL_C_EVENT || dwType == CTRL_CLOSE_EVENT || dwType == CTRL_LOGOFF_EVENT ||
|
||||
dwType == CTRL_SHUTDOWN_EVENT)
|
||||
{
|
||||
running = false;
|
||||
cleanup();
|
||||
exit(0);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -244,8 +267,9 @@ int main()
|
||||
{
|
||||
Log::createConsole();
|
||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
||||
atexit(cleanup);
|
||||
|
||||
Log::info("Unlocker init");
|
||||
Log::info("Init");
|
||||
|
||||
loadCatalogOnStartup();
|
||||
loadAllCustomItems();
|
||||
@@ -253,9 +277,9 @@ int main()
|
||||
/*
|
||||
proxy setup
|
||||
*/
|
||||
Log::verbose("Starting proxy");
|
||||
Proxy* proxy = new Proxy();
|
||||
if (!proxy->Init())
|
||||
Log::info("Starting proxy");
|
||||
g_proxy = new Proxy();
|
||||
if (!g_proxy->Init())
|
||||
{
|
||||
Log::error("Proxy failed to start");
|
||||
return 1;
|
||||
@@ -265,91 +289,62 @@ int main()
|
||||
/*
|
||||
listeners
|
||||
*/
|
||||
proxy->OnServerResponse.addListener([](const std::string& url, std::string& data) {
|
||||
g_proxy->OnServerResponse.addListener([](const std::string& url, std::string& data) {
|
||||
#ifdef _DEBUG
|
||||
if (url.find("bhvrdbd.com") != std::string::npos) Log::verbose("BHVR api res: {}", url);
|
||||
#endif
|
||||
|
||||
if (url.find("api/v1/extensions/store/getCatalogItems") != std::string::npos)
|
||||
updateCatalog(data);
|
||||
else if (url.find("api/v1/dbd-inventories/all") != std::string::npos)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_dataMutex);
|
||||
if (!g_allObjectIds.empty() || !g_stackableItems.empty() || !g_uniqueItems.empty())
|
||||
if (!g_allObjectIds.empty())
|
||||
{
|
||||
Log::info("Merging catalog and custom items into real inventory response");
|
||||
|
||||
std::regex qtyRegex(R"("quantity"\s*:\s*\d+)");
|
||||
for (const auto& id : g_stackableItems)
|
||||
{
|
||||
size_t pos = data.find("\"objectId\":\"" + id + "\"");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
size_t start = data.rfind("{", pos);
|
||||
size_t end = data.find("}", pos);
|
||||
if (start != std::string::npos && end != std::string::npos && start < end)
|
||||
{
|
||||
std::string objStr = data.substr(start, end - start + 1);
|
||||
objStr = std::regex_replace(objStr, qtyRegex, "\"quantity\":100");
|
||||
data.replace(start, end - start + 1, objStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::info("Merging catalog and dumped items into real inventory response");
|
||||
|
||||
size_t closePos = data.rfind("]}");
|
||||
if (closePos != std::string::npos)
|
||||
{
|
||||
uint64_t now = time(nullptr);
|
||||
std::string injected;
|
||||
injected.reserve((g_allObjectIds.size() + g_stackableItems.size() + g_uniqueItems.size()) * 60);
|
||||
injected.reserve((g_allObjectIds.size() + g_stackableItems.size()) * 80);
|
||||
|
||||
std::unordered_set<std::string> dbIds;
|
||||
for (auto& id : g_stackableItems)
|
||||
dbIds.insert(id);
|
||||
for (auto& id : g_uniqueItems)
|
||||
dbIds.insert(id);
|
||||
for (auto& id : g_perks)
|
||||
dbIds.insert(id);
|
||||
|
||||
std::unordered_set<std::string> handledIds;
|
||||
auto injectItem = [&](const std::string& id, int qty) {
|
||||
if (id.empty()) return;
|
||||
if (handledIds.count(id)) return;
|
||||
|
||||
std::string searchPat = "\"objectId\":\"" + id + "\"";
|
||||
if (data.find(searchPat) != std::string::npos)
|
||||
std::unordered_set<std::string> seenIds;
|
||||
size_t pos = 0;
|
||||
while ((pos = data.find("\"objectId\":\"", pos)) != std::string::npos)
|
||||
{
|
||||
pos += 12;
|
||||
size_t end = data.find("\"", pos);
|
||||
if (end != std::string::npos)
|
||||
{
|
||||
handledIds.insert(id);
|
||||
return;
|
||||
seenIds.insert(data.substr(pos, end - pos));
|
||||
pos = end;
|
||||
}
|
||||
}
|
||||
|
||||
injected += ",{\"lastUpdateAt\":" + std::to_string(now) +
|
||||
",\"quantity\":" + std::to_string(qty) + ",\"objectId\":\"" + id + "\"}";
|
||||
handledIds.insert(id);
|
||||
auto injectItem = [&](const std::string& id, int qty) {
|
||||
if (id.empty() || seenIds.count(id)) return;
|
||||
injected += std::format(",{{\"lastUpdateAt\":{},\"quantity\":{},\"objectId\":\"{}\"}}", now, qty, id);
|
||||
seenIds.insert(id);
|
||||
};
|
||||
|
||||
for (const auto& id : g_stackableItems)
|
||||
injectItem(id, 100);
|
||||
for (const auto& id : g_uniqueItems)
|
||||
injectItem(id, 1);
|
||||
for (const auto& id : g_perks)
|
||||
injectItem(id, 3);
|
||||
|
||||
for (const auto& id : g_allObjectIds)
|
||||
{
|
||||
if (dbIds.find(id) == dbIds.end()) injectItem(id, 1);
|
||||
}
|
||||
for (const auto& id : g_allObjectIds) injectItem(id, 1);
|
||||
for (const auto& id : g_stackableItems) injectItem(id, 100);
|
||||
for (const auto& id : g_uniqueItems) injectItem(id, 1);
|
||||
for (const auto& id : g_perks) injectItem(id, 3);
|
||||
|
||||
if (!injected.empty()) data.insert(closePos, injected);
|
||||
|
||||
Log::info("Injected {} new items into inventory",
|
||||
injected.empty() ? 0 : std::count(injected.begin(), injected.end(), '{'));
|
||||
Log::info("Injected {} items into global inventory",
|
||||
std::count(injected.begin(), injected.end(), '{'));
|
||||
}
|
||||
}
|
||||
else
|
||||
Log::warning("No catalog or custom data available to inject into inventory yet!");
|
||||
Log::warning("No catalog data available to inject into global inventory yet!");
|
||||
}
|
||||
else if (url.find("api/v1/dbd-character-data/get-all") != std::string::npos)
|
||||
else if (url.find("api/v1/dbd-character-data/") != std::string::npos && url.find("/bloodweb") == std::string::npos)
|
||||
{
|
||||
|
||||
std::vector<std::string> localStackable;
|
||||
std::vector<std::string> localUnique;
|
||||
std::vector<std::string> localPerks;
|
||||
@@ -362,42 +357,65 @@ int main()
|
||||
|
||||
if (!localStackable.empty() || !localUnique.empty() || !localPerks.empty())
|
||||
{
|
||||
size_t pos = 0;
|
||||
size_t count = 0;
|
||||
std::unordered_set<std::string> stackableSet(localStackable.begin(), localStackable.end());
|
||||
std::unordered_set<std::string> uniqueSet(localUnique.begin(), localUnique.end());
|
||||
std::unordered_set<std::string> perkSet(localPerks.begin(), localPerks.end());
|
||||
std::unordered_set<std::string> uniqueSet(localUnique.begin(), localUnique.end());
|
||||
std::regex qtyRegex(R"("quantity"\s*:\s*\d+)");
|
||||
|
||||
while ((pos = data.find("\"characterItems\":[", pos)) != std::string::npos)
|
||||
const char* targetArrays[] = {"\"characterItems\":[", "\"inventory\":["};
|
||||
size_t arraysModified = 0;
|
||||
|
||||
for (const char* arrayKey : targetArrays)
|
||||
{
|
||||
pos += 18;
|
||||
|
||||
size_t endPos = data.find("]", pos);
|
||||
if (endPos != std::string::npos)
|
||||
size_t pos = 0;
|
||||
while ((pos = data.find(arrayKey, pos)) != std::string::npos)
|
||||
{
|
||||
std::string currentItems = data.substr(pos, endPos - pos);
|
||||
std::string newItems;
|
||||
newItems.reserve(currentItems.size() * 2);
|
||||
pos += strlen(arrayKey);
|
||||
size_t endPos = data.find("]", pos);
|
||||
if (endPos == std::string::npos) break;
|
||||
|
||||
std::string currentItems = data.substr(pos, endPos - pos);
|
||||
std::unordered_set<std::string> seenIds;
|
||||
std::unordered_set<std::string> stackableSet(localStackable.begin(), localStackable.end());
|
||||
|
||||
size_t itemPos = 0;
|
||||
while ((itemPos = currentItems.find("\"itemId\":\"", itemPos)) != std::string::npos)
|
||||
{
|
||||
itemPos += 10;
|
||||
size_t quotePos = currentItems.find("\"", itemPos);
|
||||
if (quotePos == std::string::npos) break;
|
||||
size_t idStart = itemPos + 10;
|
||||
size_t idEnd = currentItems.find("\"", idStart);
|
||||
if (idEnd == std::string::npos) break;
|
||||
|
||||
std::string id = currentItems.substr(itemPos, quotePos - itemPos);
|
||||
std::string id = currentItems.substr(idStart, idEnd - idStart);
|
||||
seenIds.insert(id);
|
||||
|
||||
int qty = 100;
|
||||
int qty = -1;
|
||||
if (perkSet.count(id))
|
||||
qty = 3;
|
||||
else if (uniqueSet.count(id))
|
||||
qty = 1;
|
||||
else if (stackableSet.count(id))
|
||||
qty = 100;
|
||||
|
||||
newItems += "{\"itemId\":\"" + id + "\",\"quantity\":" + std::to_string(qty) + "},";
|
||||
if (qty != -1)
|
||||
{
|
||||
size_t objStart = currentItems.rfind("{", itemPos);
|
||||
size_t objEnd = currentItems.find("}", itemPos);
|
||||
if (objStart != std::string::npos && objEnd != std::string::npos && objStart < objEnd)
|
||||
{
|
||||
std::string objStr = currentItems.substr(objStart, objEnd - objStart + 1);
|
||||
if (std::regex_search(objStr, qtyRegex))
|
||||
objStr =
|
||||
std::regex_replace(objStr, qtyRegex, "\"quantity\":" + std::to_string(qty));
|
||||
else
|
||||
objStr.insert(objStr.length() - 1, ",\"quantity\":" + std::to_string(qty));
|
||||
|
||||
currentItems.replace(objStart, objEnd - objStart + 1, objStr);
|
||||
itemPos = objStart + objStr.length();
|
||||
}
|
||||
else
|
||||
itemPos = idEnd;
|
||||
}
|
||||
else
|
||||
itemPos = idEnd;
|
||||
}
|
||||
|
||||
auto injectIfMissing = [&](const std::vector<std::string>& list, int qty) {
|
||||
@@ -405,7 +423,9 @@ int main()
|
||||
{
|
||||
if (seenIds.find(id) == seenIds.end())
|
||||
{
|
||||
newItems += "{\"itemId\":\"" + id + "\",\"quantity\":" + std::to_string(qty) + "},";
|
||||
if (!currentItems.empty() && currentItems.back() != ',') currentItems += ",";
|
||||
currentItems +=
|
||||
"{\"itemId\":\"" + id + "\",\"quantity\":" + std::to_string(qty) + "}";
|
||||
seenIds.insert(id);
|
||||
}
|
||||
}
|
||||
@@ -415,36 +435,29 @@ int main()
|
||||
injectIfMissing(localUnique, 1);
|
||||
injectIfMissing(localPerks, 3);
|
||||
|
||||
if (!newItems.empty()) newItems.pop_back(); // trailing comma
|
||||
|
||||
data.replace(pos, endPos - pos, newItems);
|
||||
|
||||
endPos = pos + newItems.length();
|
||||
pos = endPos;
|
||||
count++;
|
||||
data.replace(pos, endPos - pos, currentItems);
|
||||
pos += currentItems.length();
|
||||
arraysModified++;
|
||||
}
|
||||
}
|
||||
Log::info("Injected missing items and targeted perk tiers in {} character inventories", count);
|
||||
Log::info("Spoofed items in {} character data arrays across response", arraysModified);
|
||||
}
|
||||
else
|
||||
Log::warning("No custom dumped items available to inject into character inventory!");
|
||||
Log::warning("No custom dumped items available to inject into character data!");
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
pause
|
||||
*/
|
||||
Log::verbose("Proxy running (CTRL+C to stop)");
|
||||
Log::info("Proxy running (CTRL+C to stop)");
|
||||
while (running)
|
||||
Sleep(100);
|
||||
|
||||
/*
|
||||
cleanup
|
||||
*/
|
||||
Log::verbose("Shutting down proxy");
|
||||
proxy->Shutdown();
|
||||
delete proxy;
|
||||
setProxy(false, "");
|
||||
cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -549,4 +549,4 @@ void Proxy::handleClient(SOCKET hClientSocket)
|
||||
if (connectionClosed) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
#include "cert_manager.h"
|
||||
#include <nerutils/callback.h>
|
||||
|
||||
#define PROXY_PORT 1337
|
||||
/*
|
||||
TO-DO:
|
||||
use random port, test availability
|
||||
*/
|
||||
#define PROXY_PORT 58421
|
||||
|
||||
typedef unsigned __int64 SOCKET;
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
add_subdirectory(nerutils)
|
||||
add_subdirectory(simdjson)
|
||||
add_subdirectory(simdjson)
|
||||
|
||||
Reference in New Issue
Block a user