From 1de2f4ab460198e536e559086a5074b4830a2927 Mon Sep 17 00:00:00 2001 From: Jacob Babor Date: Fri, 19 Jun 2026 21:44:46 -0500 Subject: [PATCH] Add RED4ext logging shim --- .gitignore | 2 + docs/red4ext-logging-shim.md | 27 +++++ red4ext/EdgeWeightGPS/CMakeLists.txt | 24 ++++ .../cmake/mingw64-toolchain.cmake | 12 ++ .../include/RED4ext/rtti/ERTTIType.hpp | 2 + .../include/RED4ext/rtti/IType-inl.hpp | 2 + .../include/RED4ext/rtti/IType.hpp | 2 + red4ext/EdgeWeightGPS/include/Windows.h | 14 +++ red4ext/EdgeWeightGPS/src/Main.cpp | 103 ++++++++++++++++++ tools/build_red4ext_shim.sh | 19 ++++ tools/install_red4ext_shim.sh | 12 ++ 11 files changed, 219 insertions(+) create mode 100644 docs/red4ext-logging-shim.md create mode 100644 red4ext/EdgeWeightGPS/CMakeLists.txt create mode 100644 red4ext/EdgeWeightGPS/cmake/mingw64-toolchain.cmake create mode 100644 red4ext/EdgeWeightGPS/include/RED4ext/rtti/ERTTIType.hpp create mode 100644 red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType-inl.hpp create mode 100644 red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType.hpp create mode 100644 red4ext/EdgeWeightGPS/include/Windows.h create mode 100644 red4ext/EdgeWeightGPS/src/Main.cpp create mode 100755 tools/build_red4ext_shim.sh create mode 100755 tools/install_red4ext_shim.sh diff --git a/.gitignore b/.gitignore index c6918c4..293b902 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ __pycache__/ *.pyc /work/ /work-*.txt +/build/ +/vendor/ diff --git a/docs/red4ext-logging-shim.md b/docs/red4ext-logging-shim.md new file mode 100644 index 0000000..b2be1a7 --- /dev/null +++ b/docs/red4ext-logging-shim.md @@ -0,0 +1,27 @@ +# RED4ext logging shim + +`EdgeWeightGPS` is a minimal RED4ext plugin used to verify a native logging foothold without changing gameplay data. + +It writes `EdgeWeightGPS.log` beside the installed DLL: + +```text +Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.log +``` + +Current behavior: + +- Logs plugin load/unload. +- Logs the RED4ext plugin handle and SDK pointer value passed to `Main`. +- Does not call game RTTI or register script functions. + +Planned next step: + +- Build a richer RTTI/hooking variant with an MSVC-compatible Windows toolchain or a narrower hand-written SDK surface. The upstream RED4ext SDK headers build under MinGW for loader-only use, but the full SDK/static library currently pulls in generated native/GPU structures with MSVC layout assumptions. + +Build and install from the Fedora toolbox: + +```bash +toolbox run -c 2077 ./tools/install_red4ext_shim.sh +``` + +This shim is read-only. It does not enable any of the archived traffic data patches. diff --git a/red4ext/EdgeWeightGPS/CMakeLists.txt b/red4ext/EdgeWeightGPS/CMakeLists.txt new file mode 100644 index 0000000..0306971 --- /dev/null +++ b/red4ext/EdgeWeightGPS/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.21) + +project(EdgeWeightGPS VERSION 0.1.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_library(EdgeWeightGPS SHARED src/Main.cpp) +target_include_directories( + EdgeWeightGPS + PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/include" + "${CMAKE_CURRENT_LIST_DIR}/../../vendor/RED4ext.SDK/include" +) +target_compile_definitions(EdgeWeightGPS PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX) +target_compile_options(EdgeWeightGPS PRIVATE -fmax-errors=12) + +set_target_properties( + EdgeWeightGPS + PROPERTIES + PREFIX "" + OUTPUT_NAME "EdgeWeightGPS" +) diff --git a/red4ext/EdgeWeightGPS/cmake/mingw64-toolchain.cmake b/red4ext/EdgeWeightGPS/cmake/mingw64-toolchain.cmake new file mode 100644 index 0000000..1adb9b7 --- /dev/null +++ b/red4ext/EdgeWeightGPS/cmake/mingw64-toolchain.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/red4ext/EdgeWeightGPS/include/RED4ext/rtti/ERTTIType.hpp b/red4ext/EdgeWeightGPS/include/RED4ext/rtti/ERTTIType.hpp new file mode 100644 index 0000000..00a0539 --- /dev/null +++ b/red4ext/EdgeWeightGPS/include/RED4ext/rtti/ERTTIType.hpp @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType-inl.hpp b/red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType-inl.hpp new file mode 100644 index 0000000..f90bdf7 --- /dev/null +++ b/red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType-inl.hpp @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType.hpp b/red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType.hpp new file mode 100644 index 0000000..d2698a6 --- /dev/null +++ b/red4ext/EdgeWeightGPS/include/RED4ext/rtti/IType.hpp @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/red4ext/EdgeWeightGPS/include/Windows.h b/red4ext/EdgeWeightGPS/include/Windows.h new file mode 100644 index 0000000..8665810 --- /dev/null +++ b/red4ext/EdgeWeightGPS/include/Windows.h @@ -0,0 +1,14 @@ +#pragma once +#include_next + +#ifndef _MSC_VER +extern "C" inline char InterlockedExchange8(volatile char* aTarget, char aValue) +{ + return __atomic_exchange_n(aTarget, aValue, __ATOMIC_SEQ_CST); +} + +extern "C" inline char _InterlockedExchangeAdd8(volatile char* aTarget, char aValue) +{ + return __atomic_fetch_add(aTarget, aValue, __ATOMIC_SEQ_CST); +} +#endif diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp new file mode 100644 index 0000000..30ecaea --- /dev/null +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace RED4ext::v1 +{ +struct Sdk; +} + +namespace +{ +HMODULE gModule = nullptr; +std::mutex gLogMutex; + +std::filesystem::path GetLogPath() +{ + wchar_t modulePath[MAX_PATH]{}; + if (gModule && GetModuleFileNameW(gModule, modulePath, MAX_PATH) > 0) + { + auto path = std::filesystem::path(modulePath); + return path.parent_path() / L"EdgeWeightGPS.log"; + } + + return std::filesystem::path(L"red4ext") / L"plugins" / L"EdgeWeightGPS" / L"EdgeWeightGPS.log"; +} + +void Log(std::string_view aMessage) +{ + std::lock_guard lock(gLogMutex); + + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + std::tm localTime{}; + localtime_s(&localTime, &time); + + std::ofstream out(GetLogPath(), std::ios::app); + if (!out) + { + return; + } + + out << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << " " << aMessage << "\n"; +} + +const char* ReasonToString(RED4ext::v1::EMainReason aReason) +{ + switch (aReason) + { + case RED4ext::v1::EMainReason::Load: + return "Load"; + case RED4ext::v1::EMainReason::Unload: + return "Unload"; + } + + return "Unknown"; +} +} + +BOOL APIENTRY DllMain(HMODULE aModule, DWORD aReason, LPVOID) +{ + if (aReason == DLL_PROCESS_ATTACH) + { + gModule = aModule; + } + return TRUE; +} + +RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4ext::v1::EMainReason aReason, + const RED4ext::v1::Sdk* aSdk) +{ + std::ostringstream line; + line << "EdgeWeightGPS Main reason=" << ReasonToString(aReason) << " handle=" << aHandle << " sdk=" << aSdk; + Log(line.str()); + return true; +} + +RED4EXT_C_EXPORT void RED4EXT_CALL Query(RED4ext::v1::PluginInfo* aInfo) +{ + aInfo->name = L"EdgeWeightGPS"; + aInfo->author = L"salt+Codex"; + aInfo->version = RED4ext::v1::SemVer{0, 1, 0, {0, 0}}; + aInfo->runtime = RED4ext::v1::FileVer{3, 0, 80, 51928}; + aInfo->sdk = RED4ext::v1::SemVer{RED4EXT_VER_MAJOR, RED4EXT_VER_MINOR, RED4EXT_VER_PATCH, {0, 0}}; +} + +RED4EXT_C_EXPORT uint32_t RED4EXT_CALL Supports() +{ + return RED4EXT_API_VERSION_1; +} diff --git a/tools/build_red4ext_shim.sh b/tools/build_red4ext_shim.sh new file mode 100755 index 0000000..7a9ad82 --- /dev/null +++ b/tools/build_red4ext_shim.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +plugin_dir="$repo_root/red4ext/EdgeWeightGPS" +build_dir="$repo_root/build/red4ext/EdgeWeightGPS-mingw64" +sdk_dir="$repo_root/vendor/RED4ext.SDK" + +if [[ ! -f "$sdk_dir/include/RED4ext/Api/v1/PluginInfo.hpp" ]]; then + mkdir -p "$repo_root/vendor" + git clone --depth 1 https://github.com/WopsS/RED4ext.SDK.git "$sdk_dir" +fi + +cmake -S "$plugin_dir" -B "$build_dir" \ + -DCMAKE_TOOLCHAIN_FILE="$plugin_dir/cmake/mingw64-toolchain.cmake" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo +cmake --build "$build_dir" --parallel + +printf '%s\n' "$build_dir/EdgeWeightGPS.dll" diff --git a/tools/install_red4ext_shim.sh b/tools/install_red4ext_shim.sh new file mode 100755 index 0000000..d3952dc --- /dev/null +++ b/tools/install_red4ext_shim.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +game_dir="${CYBERPUNK2077_GAME_DIR:-/var/home/salt/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Cyberpunk 2077}" +plugin_out="$game_dir/red4ext/plugins/EdgeWeightGPS" + +dll_path="$("$repo_root/tools/build_red4ext_shim.sh" | tail -n 1)" +mkdir -p "$plugin_out" +install -m 0644 "$dll_path" "$plugin_out/EdgeWeightGPS.dll" + +printf 'Installed %s\n' "$plugin_out/EdgeWeightGPS.dll"