Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ CheckOptions:
readability-identifier-naming.NamespaceCase: lower_case
readability-identifier-naming.ParameterCase: lower_case
readability-identifier-naming.VariableCase: lower_case

readability-identifier-length.IgnoredVariableNames: _
---
5 changes: 3 additions & 2 deletions .cruft.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"template": "[email protected]:bl-sdk/common_dotfiles.git",
"commit": "6b31480199099e9957b18918373a75d979951919",
"commit": "cee5c9dbf5b95f57bb636e5138171aa6a4964cf1",
"checkout": null,
"context": {
"cookiecutter": {
Expand All @@ -15,7 +15,8 @@
"__project_slug": "unrealsdk",
"include_cpp": true,
"include_py": false,
"_template": "[email protected]:bl-sdk/common_dotfiles.git"
"_template": "[email protected]:bl-sdk/common_dotfiles.git",
"_commit": "cee5c9dbf5b95f57bb636e5138171aa6a4964cf1"
}
},
"directory": null
Expand Down
6 changes: 6 additions & 0 deletions .devcontainer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Ignore any custom folders outside of our predefined ones, to let you create your own
# One use might be using your own container mapping the install path onto your actual game folder
*/
!clang-cross
!llvm-mingw
!mingw
65 changes: 65 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
FROM alpine:latest AS clang-cross
CMD ["/bin/bash"]
RUN <<EOF
apk add --no-cache \
bash \
clang \
clang-extra-tools \
cmake \
git \
lld \
llvm \
msitools \
ninja \
openssh \
perl \
python3 \
py3-requests
git clone https://github.com/mstorsjo/msvc-wine.git
msvc-wine/vsdownload.py \
--accept-license \
--dest /win-sdk \
Microsoft.VisualStudio.Workload.VCTools \
--arch x86 x64
msvc-wine/install.sh /win-sdk
rm -r msvc-wine
EOF

# llvm-mingw comes with pre-built ubuntu binaries, so using that
FROM ubuntu:latest AS llvm-mingw
CMD ["/bin/bash"]
RUN <<EOF
apt-get update
DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC \
apt-get install -y \
cmake \
git \
msitools \
ninja-build \
python-is-python3 \
python3 \
python3-requests \
wget \
xz-utils
wget -nv https://github.com/mstorsjo/llvm-mingw/releases/download/20250114/llvm-mingw-20250114-msvcrt-ubuntu-20.04-x86_64.tar.xz
tar -xf llvm-mingw-20250114-msvcrt-ubuntu-20.04-x86_64.tar.xz
rm llvm-mingw-20250114-msvcrt-ubuntu-20.04-x86_64.tar.xz
mv llvm-mingw-20250114-msvcrt-ubuntu-20.04-x86_64 /llvm-mingw
EOF
ENV PATH="/llvm-mingw/bin:$PATH"

FROM alpine:latest AS mingw
CMD ["/bin/bash"]
RUN <<EOF
apk add --no-cache \
bash \
cmake \
git \
i686-mingw-w64-gcc \
mingw-w64-gcc \
msitools \
ninja \
openssh \
py3-requests \
python3
EOF
7 changes: 7 additions & 0 deletions .devcontainer/clang-cross/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "clang-cross",
"build": {
"dockerfile": "../Dockerfile",
"target": "clang-cross"
}
}
7 changes: 7 additions & 0 deletions .devcontainer/llvm-mingw/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "llvm-mingw",
"build": {
"dockerfile": "../Dockerfile",
"target": "llvm-mingw"
}
}
7 changes: 7 additions & 0 deletions .devcontainer/mingw/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "mingw",
"build": {
"dockerfile": "../Dockerfile",
"target": "mingw"
}
}
96 changes: 29 additions & 67 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,76 +58,38 @@ jobs:
strategy:
fail-fast: false
matrix:
preset: [
"clang-cross-ue3-x86-release",
"clang-cross-ue4-x64-release",
"llvm-mingw-ue3-x86-release",
"llvm-mingw-ue4-x64-release",
"mingw-ue3-x86-release",
"mingw-ue4-x64-release",
]
toolchain:
- preset: clang-cross-ue3-x86-release
container: clang-cross
- preset: clang-cross-ue4-x64-release
container: clang-cross
- preset: llvm-mingw-ue3-x86-release
container: llvm-mingw
- preset: llvm-mingw-ue4-x64-release
container: llvm-mingw
- preset: mingw-ue3-x86-release
container: mingw
- preset: mingw-ue4-x64-release
container: mingw

steps:
- name: Setup CMake and Ninja
uses: lukka/get-cmake@latest

- name: Setup LLVM MinGW
if: startswith(matrix.preset, 'llvm-mingw')
run: |
wget -nv ${{ env.LLVM_MINGW_DOWNLOAD }}
tar -xf ${{ env.LLVM_MINGW_VERSION }}.tar.xz -C ~/
echo $(readlink -f ~/${{ env.LLVM_MINGW_VERSION }}/bin) >> $GITHUB_PATH
- name: Setup MinGW
if: startswith(matrix.preset, 'mingw')
uses: egor-tensin/setup-mingw@v2
with:
platform: ${{ fromJSON('["x86", "x64"]')[contains(matrix.preset, 'x64')] }}

- name: Setup Clang
if: startswith(matrix.preset, 'clang-cross')
uses: egor-tensin/setup-clang@v1

- name: Restore win sdk cache
if: startswith(matrix.preset, 'clang-cross')
uses: actions/cache@v4
id: cache-win-sdk
with:
path: ~/win-sdk
key: ${{ runner.os }}-win-sdk

- name: Setup msitools
if: startswith(matrix.preset, 'clang-cross') && steps.cache-win-sdk.outputs.cache-hit != 'true'
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: msitools
version: ${{ runner.os }}-msitools

- name: Setup win sdk
if: startswith(matrix.preset, 'clang-cross') && steps.cache-win-sdk.outputs.cache-hit != 'true'
run: |
git clone https://github.com/mstorsjo/msvc-wine.git
msvc-wine/vsdownload.py --accept-license --dest ~/win-sdk Microsoft.VisualStudio.Workload.VCTools
msvc-wine/install.sh ~/win-sdk
rm -r msvc-wine
- name: Checkout repository and submodules
uses: actions/checkout@v4
with:
submodules: recursive

- name: Configure CMake
working-directory: ${{ env.GITHUB_WORKSPACE }}
# The extra msvc wine arg won't do anything if we're not cross compiling
run: >
cmake .
--preset ${{ matrix.preset }}
-G Ninja
-DMSVC_WINE_ENV_SCRIPT=$(readlink -f ~)/win-sdk/bin/${{ fromJSON('["x86", "x64"]')[contains(matrix.preset, 'x64')] }}/msvcenv.sh
- name: Checkout repository and submodules
uses: actions/checkout@v4
with:
submodules: recursive

- name: Build
uses: devcontainers/[email protected]
with:
cacheFrom: ghcr.io/bl-sdk/${{ matrix.toolchain.container }}:latest
configFile: .devcontainer/${{ matrix.toolchain.container }}/devcontainer.json
push: never
# The git watcher cmake thinks something's unsafe? Doesn't happen to me locally.
runCmd: |
git config --global --add safe.directory `pwd`
cmake . --preset ${{ matrix.toolchain.preset }} -G Ninja
cmake --build out/build/${{ matrix.toolchain.preset }}
- name: Build
working-directory: ${{ env.GITHUB_WORKSPACE }}
run: cmake --build out/build/${{ matrix.preset }}
# ==============================================================================

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.vs
.vscode
.idea

# C/C++ excludes
.cache/clangd
Expand Down
6 changes: 6 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"environment": {
"MSVC_WINE_ENV_SCRIPT": "/win-sdk/bin/x86/msvcenv.sh"
},
"toolchainFile": "common_cmake/clang-cross-x86.cmake"
},
{
Expand All @@ -50,6 +53,9 @@
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"environment": {
"MSVC_WINE_ENV_SCRIPT": "/win-sdk/bin/x64/msvcenv.sh"
},
"toolchainFile": "common_cmake/clang-cross-x64.cmake"
},
{
Expand Down
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,24 @@ This turns out to be a bit of a problem - MSVC and GNU have different exception
both. Practically, this means when cross compiling, you should either compile everything from
scratch, or setup Clang to build with the MSVC ABI. [See this blog post for more info](https://apple1417.dev/posts/2023-05-18-debugging-proton).

# Running the SDK by itself
# Building the SDK by itself
The shared library is also useful when developing for the sdk itself, since it's the minimal
configuration to get it running. The CMake presets are set up to build this.

Note that you will need to use some game specific plugin loader to get the dll loaded. It is not set
up to alias any system dlls (since when actually using it as a library you don't want that), you
can't just call it `d3d9.dll` and assume your game will load fine.
configuration to get it running. The CMake presets are set up to build this. There are currently
five supported toolchains, each of which have a few different sdk configurations:

- MSVC
- Clang (Windows)
- Clang (Cross Compile) <sup>*</sup>
- MinGW <sup>*</sup>
- LLVM MinGW <sup>*</sup>

The toolchains with an asterix are all cross compiling toolchains. These all also have an associated
dev container, which is the recommended way of building them. The `clang-cross-*` presets in
particular hardcode a path assuming they're running in the container.

Note that you will need to use some game specific plugin loader to get the `unrealsdk.dll` loaded.
It is not set up to alias any system dlls (since when actually using it as a library you don't want
that), you can't just rename it to `d3d9.dll` and assume your game will load fine.

To build:

Expand Down
18 changes: 5 additions & 13 deletions src/unrealsdk/game/bl2/antidebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,9 @@ typedef NTSTATUS(WINAPI* NtQueryInformationProcess_func)(HANDLE ProcessHandle,
ULONG ProcessInformationLength,
PULONG ReturnLength);

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
#endif

constexpr auto ThreadHideFromDebugger = static_cast<THREAD_INFORMATION_CLASS>(17);
constexpr auto ProcessDebugObjectHandle = static_cast<PROCESSINFOCLASS>(30);

#if defined(__clang__)
#pragma clang diagnostic pop
#endif
// These are undocumented values, not in the header, treat as size_t to avoid enum conversion errors
constexpr size_t ThreadHideFromDebugger = 17;
constexpr size_t ProcessDebugObjectHandle = 30;

// NOLINTEND(readability-identifier-naming)

Expand All @@ -43,7 +35,7 @@ NTSTATUS NTAPI NtSetInformationThread_hook(HANDLE ThreadHandle,
PVOID ThreadInformation,
ULONG ThreadInformationLength) {
// NOLINTEND(readability-identifier-naming)
if (ThreadInformationClass == ThreadHideFromDebugger) {
if (static_cast<size_t>(ThreadInformationClass) == ThreadHideFromDebugger) {
return STATUS_SUCCESS;
}

Expand All @@ -61,7 +53,7 @@ NTSTATUS WINAPI NtQueryInformationProcess_hook(HANDLE ProcessHandle,
ULONG ProcessInformationLength,
PULONG ReturnLength) {
// NOLINTEND(readability-identifier-naming)
if (ProcessInformationClass == ProcessDebugObjectHandle) {
if (static_cast<size_t>(ProcessInformationClass) == ProcessDebugObjectHandle) {
return STATUS_PORT_NOT_SET;
}

Expand Down
2 changes: 1 addition & 1 deletion src/unrealsdk/game/selector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ using all_known_games = std::tuple<BL2Hook, TPSHook>;
* @tparam i Index of the game class being tested this iteration. Picked up automatically.
* @param executable The executable name to match against.
*/
template <int i = 0>
template <size_t i = 0>
std::unique_ptr<AbstractHook> find_correct_hook(std::string_view executable) {
if constexpr (i >= std::tuple_size_v<all_known_games>) {
throw std::runtime_error("Failed to find compatible game hook!");
Expand Down
19 changes: 11 additions & 8 deletions src/unrealsdk/hook_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ namespace {
thread_local bool should_inject_next_call = false;

bool should_log_all_calls = false;
std::unique_ptr<std::wostream> log_all_calls_stream;
std::wofstream log_all_calls_stream{};
std::mutex log_all_calls_stream_mutex{};

std::unordered_map<FName, utils::StringViewMap<std::wstring, List>> hooks{};
Expand All @@ -98,17 +98,17 @@ void log_all_calls(bool should_log) {
// Only keep this file stream open while we need it
if (should_log) {
const std::lock_guard<std::mutex> lock(log_all_calls_stream_mutex);
log_all_calls_stream = std::make_unique<std::wofstream>(
log_all_calls_stream.open(
utils::get_this_dll().parent_path()
/ config::get_str("unrealsdk.log_all_calls_file").value_or("unrealsdk.calls.tsv"),
std::ofstream::trunc);
std::wofstream::trunc);
}

should_log_all_calls = should_log;

if (!should_log) {
const std::lock_guard<std::mutex> lock(log_all_calls_stream_mutex);
log_all_calls_stream = nullptr;
log_all_calls_stream.close();
}
}

Expand Down Expand Up @@ -219,11 +219,14 @@ const List* preprocess_hook(std::wstring_view source, const UFunction* func, con
std::wstring func_name{};

if (should_log_all_calls) {
func_name = func->get_path_name();
auto obj_name = obj->get_path_name();
// Extra safety check
if (log_all_calls_stream.is_open()) {
func_name = func->get_path_name();
auto obj_name = obj->get_path_name();

const std::lock_guard<std::mutex> lock(log_all_calls_stream_mutex);
*log_all_calls_stream << source << L'\t' << func_name << L'\t' << obj_name << L'\n';
const std::lock_guard<std::mutex> lock(log_all_calls_stream_mutex);
log_all_calls_stream << source << L'\t' << func_name << L'\t' << obj_name << L'\n';
}
}

// Check if anything matches the function FName
Expand Down
2 changes: 1 addition & 1 deletion src/unrealsdk/unreal/structs/fname.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ FName::operator std::wstring() const {
return stream.str();
}

FName operator"" _fn(const wchar_t* str, size_t /*len*/) {
FName operator""_fn(const wchar_t* str, size_t /*len*/) {
return FName{str};
}

Expand Down
Loading
Loading