Compare commits

...

48 Commits

Author SHA1 Message Date
neru d47271cdc7 Merge branch 'main' of https://git.neru.rip/neru/UnlockedByDaylight
Build / build (push) Successful in 3m41s
2026-03-21 18:00:32 -03:00
neru cea8141c9c fix: handle bloodweb endpoints 2026-03-21 18:00:15 -03:00
neru 2a34166e38 Update README.md
Build / build (push) Has been cancelled
2026-03-20 23:24:49 -03:00
neru 6d0fcc4204 build: handle cases where there is no tag
Build / build (push) Successful in 3m42s
2026-03-20 23:20:29 -03:00
neru f01cf70179 build: link statically on non msvc builds
Build / build (push) Failing after 58s
2026-03-20 23:18:11 -03:00
neru 5421c42282 build: search for correct exe file
Build / build (push) Successful in 3m45s
2026-03-20 23:12:37 -03:00
neru a2e13f8935 build: add manual semver
Build / build (push) Successful in 3m35s
2026-03-20 23:06:46 -03:00
neru 4197b4f52e build: add semantic versioning
Build / build (push) Failing after 1m7s
2026-03-20 23:03:19 -03:00
neru 075590e9b9 build: downgrade upload-artifact to v3
Build / build (push) Successful in 3m53s
2026-03-20 22:54:35 -03:00
neru 6d4fab7d9f build: remove if check, upload artifact
Build / build (push) Failing after 3m33s
2026-03-20 22:49:54 -03:00
neru 91b4f33ca1 build: add cache and fix artifact
Build / build (push) Successful in 4m59s
2026-03-20 22:42:55 -03:00
neru 95e0447190 feat: add rel pkg
Build / build (push) Successful in 2m44s
2026-03-20 22:37:37 -03:00
neru f1e735038b build: remove dumper
Build / build (push) Successful in 2m46s
2026-03-20 22:34:09 -03:00
neru bedd89d0b8 build: link crypt32
Build / build (push) Has been cancelled
2026-03-20 22:33:38 -03:00
neru bdde4f02a0 fix: make windows include lowercase for linux
Build / build (push) Failing after 2m37s
2026-03-20 22:30:13 -03:00
neru 873405da66 build: try to restore consoleapi
Build / build (push) Failing after 2m7s
2026-03-20 22:27:09 -03:00
neru 2b4a8c833a build: update cmake and get openssl build
Build / build (push) Failing after 1m48s
2026-03-20 22:23:29 -03:00
neru 859203aa74 build: compile instead of download ssl
Build / build (push) Failing after 11m34s
2026-03-20 22:07:35 -03:00
neru ac72ea836b build: make msvc exclusive flags
Build / build (push) Failing after 1m36s
2026-03-20 21:58:11 -03:00
neru 32b59539fe build: change approach
Build / build (push) Failing after 1m34s
2026-03-20 21:55:09 -03:00
neru c8a11d975a build: make dumper VS only 2026-03-20 21:54:28 -03:00
neru aafb9910a0 build: try bumping down cmake version
Build / build (push) Failing after 1m20s
2026-03-20 21:49:40 -03:00
neru aadf174fdb build: restore old file
Build / build (push) Failing after 1m19s
2026-03-20 21:47:22 -03:00
neru 2ce8d0b425 build: try setting token on checkout
Build / build (push) Failing after 37s
2026-03-20 21:37:26 -03:00
neru 65a2e8a39f build: even more ai slop
Build / build (push) Failing after 25s
2026-03-20 21:30:40 -03:00
neru eddce75e91 build: ai slop test fml
Build / build (push) Failing after 25s
2026-03-20 21:28:41 -03:00
neru 2c60ab2360 build: try more auth stuff
Build / build (push) Failing after 24s
2026-03-20 21:25:24 -03:00
neru 823c89552b build: fix yml syntax error
Build / build (push) Failing after 24s
2026-03-20 21:23:54 -03:00
neru fa2d0d0efc build: trigger rerun 2026-03-20 21:23:10 -03:00
neru edaf6e8fd6 fix: try to get auth 2026-03-20 21:21:17 -03:00
neru e94b25f28c build: fetch submodules manually
Build / build (push) Failing after 25s
2026-03-20 21:18:36 -03:00
neru 5845e2e215 build: merge to ubuntu
Build / build (push) Failing after 1m26s
2026-03-20 21:12:45 -03:00
neru 63d41e8f81 build: add CI test
Build / build (push) Has been cancelled
2026-03-20 20:58:36 -03:00
neru e492b8ea3b fix: improve id handling, filter out bloodweb 2026-03-20 20:33:55 -03:00
neru 96746626bd fix: make filters more specific 2026-03-20 19:50:34 -03:00
neru 5d2c15d2b1 fix: remove spammy useless log 2026-03-20 19:25:06 -03:00
neru 4c55f7c123 build: remove dbg stuff from rel 2026-03-20 19:20:25 -03:00
neru 0eb7af76d4 fix: debloat includes 2026-03-20 19:19:51 -03:00
neru de76e89ed0 fix: stop console from breaking after installing cert 2026-03-20 19:19:04 -03:00
neru 1275adb5fd style: trailing newline 2026-03-20 19:06:08 -03:00
neru 9ac9be7302 feat: add format-code.bat 2026-03-20 19:05:51 -03:00
neru 2553f5ab4b style: clang-format pass 2026-03-20 19:05:41 -03:00
neru 3231638ce7 fix: wrong checks and wrong quantities 2026-03-20 18:46:53 -03:00
neru 93900a56cb fix: add more cleanup events 2026-03-20 18:40:11 -03:00
neru 93685b7ff0 fix: bad data handling 2026-03-20 18:40:01 -03:00
neru 25d9f9f631 build: randomize exe name 2026-03-20 18:36:13 -03:00
neru 3de7a7bb44 feat: add atexit handler for cleanup 2026-03-20 17:41:08 -03:00
neru b238f4f901 chore: change port to something more stealthy 2026-03-20 17:40:57 -03:00
10 changed files with 331 additions and 138 deletions
+134
View File
@@ -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
View File
@@ -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()
+1 -1
View File
@@ -8,7 +8,7 @@
###### chatgpt generated instructions below
###### chatgpt slop instructions below
## Requirements
+10
View File
@@ -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
+11 -3
View File
@@ -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
View File
@@ -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;
};
+118 -99
View File
@@ -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)
{
std::vector<std::string> localStackable;
std::vector<std::string> localUnique;
std::vector<std::string> localPerks;
@@ -362,42 +357,67 @@ 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\":[", "\"bloodwebRewards\":[",
"\"prestigeRewards\":["};
bool isBloodweb = url.find("/bloodweb") != std::string::npos;
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,46 +425,45 @@ 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);
}
}
};
injectIfMissing(localStackable, 100);
injectIfMissing(localUnique, 1);
injectIfMissing(localPerks, 3);
if (!isBloodweb && (std::string_view(arrayKey) == "\"characterItems\":[" ||
std::string_view(arrayKey) == "\"inventory\":["))
{
injectIfMissing(localStackable, 100);
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;
}
+1 -1
View File
@@ -549,4 +549,4 @@ void Proxy::handleClient(SOCKET hClientSocket)
if (connectionClosed) break;
}
}
}
}
+5 -1
View File
@@ -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;
+1 -1
View File
@@ -1,2 +1,2 @@
add_subdirectory(nerutils)
add_subdirectory(simdjson)
add_subdirectory(simdjson)