diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..dd4d938e1 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-unknown-linux-gnu] +linker = "./tools/zig/zig-cc-rust.bat" \ No newline at end of file diff --git a/.github/package.sh b/.github/package.sh new file mode 100755 index 000000000..45915b638 --- /dev/null +++ b/.github/package.sh @@ -0,0 +1,4 @@ +#!/bin/bash +mkdir package +cp build/Output/Game__Shipping__Linux/UE4SS/bin/libUE4SS.so package +cp assets/UE4SS-settings.ini package diff --git a/.github/workflows/UpdateUESubmodule.yml b/.github/workflows/UpdateUESubmodule.yml deleted file mode 100644 index f210c1e5e..000000000 --- a/.github/workflows/UpdateUESubmodule.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Update UE Submodule and PR - -on: - workflow_dispatch: - -env: - COMMITS_BODY: '' - -jobs: - update-submodule: - permissions: write-all - runs-on: ubuntu-latest - - steps: - - name: Setup github SSH for UEPseudo - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.UEPSEUDO_SSH_KEY }} - known_hosts: unnecessary - - - name: Checkout - uses: actions/checkout@v4 - - - name: Initialize submodule - run: | - git submodule update --init --recursive - - - name: Get commits between HEAD and main in the submodule - id: submodule-commits - run: | - cd ./deps/first/Unreal - git fetch origin main - git log HEAD..main --pretty="tformat:* %s (**%an**) _%h_" >> submodule-commits.txt - body=$(cat submodule-commits.txt) - body="${body//'%'/'%25'}" - body="${body//$'\n'/'%0A'}" - body="${body//$'\r'/'%0D'}" - echo "COMMITS_BODY=$body" >> $GITHUB_ENV - - - name: Update submodule to latest commit on main branch - run: | - cd ./deps/first/Unreal - git checkout main - git pull origin main - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 - with: - commit-message: Update UE Submodule - committer: GitHub - author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> - title: Update UE Submodule - body: ${{ env.COMMITS_BODY }} - branch: update-ue-submodule - base: main \ No newline at end of file diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml deleted file mode 100644 index e1f735122..000000000 --- a/.github/workflows/experimental.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Make Experimental Release - -on: - workflow_dispatch: - push: - branches: [ "main" ] - paths: - - "UE4SS/src/**" - - "UE4SS/include/**" - - "UE4SS/generated_src/**" - - "UE4SS/generated_include/**" - - "deps/**" - - "UE4SS/proxy_generator/**" - -permissions: - contents: read - -jobs: - make-release: - permissions: write-all - runs-on: windows-2022 - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 # needed to get commits since last tag - token: ${{ secrets.UEPSEUDO_PAT }} - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - - name: Setup xmake - uses: xmake-io/github-action-setup-xmake@v1 - with: - xmake-version: latest - - - name: Cache - uses: actions/cache@v4 - with: - path: | - .xmake - Binaries - Intermediates - C:/Users/runneradmin/AppData/Local/.xmake - key: ${{ runner.os }}-xmake-${{ hashFiles('**/xmake.lua') }} - - - name: Build - run: | - xmake f -m "Game__Shipping__Win64" -y - xmake build - - - name: Package - run: python tools/buildscripts/build.py package -e - - - name: Release - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: experimental - body_path: release/release_notes.md - files: | - release/UE4SS_v*.zip - release/zDEV-UE4SS_v*.zip - release/zCustomGameConfigs.zip - release/zMapGenBP.zip diff --git a/.github/workflows/linux-test.yml b/.github/workflows/linux-test.yml new file mode 100644 index 000000000..944d26519 --- /dev/null +++ b/.github/workflows/linux-test.yml @@ -0,0 +1,67 @@ +name: Linux Test Build + +on: + workflow_dispatch: {} + push: + branches: [ "linux-port" ] + paths: + - "UE4SS/src/**" + - "UE4SS/include/**" + - "UE4SS/generated_src/**" + - "UE4SS/generated_include/**" + - "deps/**" + - "UE4SS/proxy_generator/**" + - ".github/workflows/linux-test.yml" + - "CMakeLists.txt" + - "cmake/**" + - tools/** + - xmake.lua + +permissions: + contents: read + +jobs: + make-release: + runs-on: ubuntu-22.04 + steps: + - name: Setup github SSH + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSHKEY }} + known_hosts: unnecessary + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 # needed to get commits since last tag + ssh-key: ${{ secrets.SSHKEY }} + - uses: goto-bus-stop/setup-zig@v2 + - run: zig cc -v + - name: Setup Rust + uses: dtolnay/rust-toolchain@1.76.0 + - name: Setup xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: latest + actions-cache-folder: '.xmake-cache' + actions-cache-key: 'linux-test' + - name: Build + run: | + xmake f -y --ue4ssUI=TUI --ue4ssInput=y --ue4ssIsBeta=y --zig=y -m "Game__Shipping__Linux" + XMAKE_COLORTERM=nocolor xmake build -v -D > build.log + - name: Stripping + run: | + mkdir prepackage + objcopy --only-keep-debug Binaries/Game__Shipping__Linux/UE4SS/libUE4SS.so ./prepackage/libUE4SS.so.debug + cp Binaries/Game__Shipping__Linux/UE4SS/libUE4SS.so ./prepackage/libUE4SS.so + strip --strip-debug --strip-unneeded ./prepackage/libUE4SS.so + cd ./prepackage && objcopy --add-gnu-debuglink=libUE4SS.so.debug ./libUE4SS.so && cd .. + - name: Package + run: python ./tools/buildscripts/build.py package -d ./prepackage -s linux64 + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: Linux-Release + path: | + release/zDEV-UE4SS_0.0.0.zip + build.log diff --git a/.github/workflows/pushdocs.yml b/.github/workflows/pushdocs.yml deleted file mode 100644 index 93a2f66a1..000000000 --- a/.github/workflows/pushdocs.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Deploy docs - -on: - workflow_dispatch: - push: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Setup mdBook - run: | - mkdir mdbook - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook - echo `pwd`/mdbook >> $GITHUB_PATH - - - name: Build - run: | - python docs-export/export.py - python docs-repo/build.py - - - uses: actions/upload-pages-artifact@v1 - with: - path: docs-repo/docs - deploy: - needs: build - - permissions: - pages: write - id-token: write - - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - runs-on: ubuntu-latest - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 52b74a758..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Make Release -on: workflow_dispatch - -permissions: - contents: read - -jobs: - make-release: - permissions: write-all - runs-on: windows-2022 - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 # needed to get commits since last tag - token: ${{ secrets.UEPSEUDO_PAT }} - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - - name: Setup xmake - uses: xmake-io/github-action-setup-xmake@v1 - with: - xmake-version: latest - - - name: Release Commit - id: release_commit - run: python build.py release_commit ${{ github.actor }} - working-directory: ./tools/buildscripts - - - name: Cache - uses: actions/cache@v4 - with: - path: | - .xmake - Binaries - Intermediates - C:/Users/runneradmin/AppData/Local/.xmake - key: ${{ runner.os }}-xmake-${{ hashFiles('**/xmake.lua') }} - - - name: Build - run: | - xmake f -m "Game__Shipping__Win64" -y - xmake build - - - name: Package - run: python tools/buildscripts/build.py package - - - name: Push changes - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ github.ref }} - tags: true - - - name: Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ steps.release_commit.outputs.release_tag }} - body_path: release/release_notes.md - files: | - release/UE4SS_v*.zip - release/zDEV-UE4SS_v*.zip - release/zCustomGameConfigs.zip - release/zMapGenBP.zip diff --git a/.github/workflows/windows-test.yml b/.github/workflows/windows-test.yml new file mode 100644 index 000000000..394ebe62d --- /dev/null +++ b/.github/workflows/windows-test.yml @@ -0,0 +1,53 @@ +name: Windows Test Build + +on: + workflow_dispatch: + push: + branches: [ "linux-port" ] + paths: + - "UE4SS/src/**" + - "UE4SS/include/**" + - "UE4SS/generated_src/**" + - "UE4SS/generated_include/**" + - "deps/**" + - "UE4SS/proxy_generator/**" + - ".github/workflows/windows-test.yml" + - "CMakeLists.txt" + +permissions: + contents: read + +jobs: + make-release: + runs-on: windows-2022 + steps: + - name: Setup github SSH + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSHKEY }} + known_hosts: unnecessary + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 # needed to get commits since last tag + ssh-key: ${{ secrets.SSHKEY }} + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + - name: Setup xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: latest + - name: Setup Ninja + uses: seanmiddleditch/gha-setup-ninja@v3 + - name: Setup Rust + uses: dtolnay/rust-toolchain@1.76.0 + - name: Build + run: | + xmake f -m "Game__Shipping__Win64" -y + xmake build + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: Windows-Release + path: build/Output/Game__Shipping__Win64/UE4SS/bin diff --git a/.gitmodules b/.gitmodules index ce01d3b56..027e91abf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "deps/first/Unreal"] path = deps/first/Unreal - url = git@github.com:Re-UE4SS/UEPseudo.git + url = git@github.com:Yangff/UEPseudo.git [submodule "deps/first/patternsleuth"] path = deps/first/patternsleuth - url = git@github.com:trumank/patternsleuth.git + url = git@github.com:Yangff/patternsleuth.git diff --git a/UE4SS/generated_include/MacroSetter.hpp b/UE4SS/generated_include/MacroSetter.hpp index c776ad2ec..6a0eb0afa 100644 --- a/UE4SS/generated_include/MacroSetter.hpp +++ b/UE4SS/generated_include/MacroSetter.hpp @@ -1,892 +1,903 @@ -if (auto val = parser.get_int64(STR("UObjectBase"), STR("Class"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("Class"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("Class"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("ClassPrivate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("ClassPrivate"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("ClassPrivate"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("InternalIndex"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("InternalIndex"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("InternalIndex"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("Name"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("Name"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("Name"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("NamePrivate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("NamePrivate"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("NamePrivate"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("ObjectFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("ObjectFlags"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("ObjectFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("Outer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("Outer"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("Outer"), static_cast(val)); -if (auto val = parser.get_int64(STR("UObjectBase"), STR("OuterPrivate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UObjectBase"), SYSSTR("OuterPrivate"), -1); val != -1) Unreal::UObjectBase::MemberOffsets.emplace(STR("OuterPrivate"), static_cast(val)); -if (auto val = parser.get_int64(STR("UScriptStruct::ICppStructOps"), STR("Alignment"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UScriptStruct::ICppStructOps"), SYSSTR("Alignment"), -1); val != -1) Unreal::UScriptStruct::ICppStructOps::MemberOffsets.emplace(STR("Alignment"), static_cast(val)); -if (auto val = parser.get_int64(STR("UScriptStruct::ICppStructOps"), STR("Size"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UScriptStruct::ICppStructOps"), SYSSTR("Size"), -1); val != -1) Unreal::UScriptStruct::ICppStructOps::MemberOffsets.emplace(STR("Size"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("ArrayDim"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("ArrayDim"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("ArrayDim"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("DestructorLinkNext"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("DestructorLinkNext"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("DestructorLinkNext"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("ElementSize"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("ElementSize"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("ElementSize"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("NextRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("NextRef"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("NextRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("Offset_Internal"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("Offset_Internal"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("Offset_Internal"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("PostConstructLinkNext"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("PostConstructLinkNext"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("PostConstructLinkNext"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("PropertyFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("PropertyFlags"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("PropertyFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("PropertyLinkNext"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("PropertyLinkNext"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("PropertyLinkNext"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("RepIndex"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("RepIndex"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("RepIndex"), static_cast(val)); -if (auto val = parser.get_int64(STR("FProperty"), STR("RepNotifyFunc"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FProperty"), SYSSTR("RepNotifyFunc"), -1); val != -1) Unreal::FProperty::MemberOffsets.emplace(STR("RepNotifyFunc"), static_cast(val)); -if (auto val = parser.get_int64(STR("FSoftClassProperty"), STR("MetaClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FSoftClassProperty"), SYSSTR("MetaClass"), -1); val != -1) Unreal::FSoftClassProperty::MemberOffsets.emplace(STR("MetaClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("DefaultPlayerName"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("DefaultPlayerName"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("DefaultPlayerName"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("GameSession"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("GameSession"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("GameSession"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("GameSessionClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("GameSessionClass"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("GameSessionClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("HUDClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("HUDClass"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("HUDClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("OptionsString"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("OptionsString"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("OptionsString"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("PlayerStateClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("PlayerStateClass"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("PlayerStateClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("ServerStatReplicator"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("ServerStatReplicator"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("ServerStatReplicator"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("ServerStatReplicatorClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("ServerStatReplicatorClass"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("ServerStatReplicatorClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("SpectatorClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("SpectatorClass"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("SpectatorClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("bPauseable"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("bPauseable"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("bPauseable"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("bStartPlayersAsSpectators"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("bStartPlayersAsSpectators"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("bStartPlayersAsSpectators"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameModeBase"), STR("bUseSeamlessTravel"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameModeBase"), SYSSTR("bUseSeamlessTravel"), -1); val != -1) Unreal::AGameModeBase::MemberOffsets.emplace(STR("bUseSeamlessTravel"), static_cast(val)); -if (auto val = parser.get_int64(STR("FOutputDevice"), STR("bAutoEmitLineTerminator"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FOutputDevice"), SYSSTR("bAutoEmitLineTerminator"), -1); val != -1) Unreal::FOutputDevice::MemberOffsets.emplace(STR("bAutoEmitLineTerminator"), static_cast(val)); -if (auto val = parser.get_int64(STR("FOutputDevice"), STR("bSuppressEventTag"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FOutputDevice"), SYSSTR("bSuppressEventTag"), -1); val != -1) Unreal::FOutputDevice::MemberOffsets.emplace(STR("bSuppressEventTag"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("EventGraphCallOffset"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("EventGraphCallOffset"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("EventGraphCallOffset"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("EventGraphFunction"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("EventGraphFunction"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("EventGraphFunction"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("FirstPropertyToInit"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("FirstPropertyToInit"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("FirstPropertyToInit"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("Func"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("Func"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("FunctionFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("Func"), -1); val != -1) + Unreal::UFunction::MemberOffsets.emplace(STR("Func"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("FunctionFlags"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("FunctionFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("NumParms"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("NumParms"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("NumParms"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("ParmsSize"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("ParmsSize"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("ParmsSize"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("RPCId"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("RPCId"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("RPCId"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("RPCResponseId"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("RPCResponseId"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("RPCResponseId"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("RepOffset"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("RepOffset"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("RepOffset"), static_cast(val)); -if (auto val = parser.get_int64(STR("UFunction"), STR("ReturnValueOffset"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UFunction"), SYSSTR("ReturnValueOffset"), -1); val != -1) Unreal::UFunction::MemberOffsets.emplace(STR("ReturnValueOffset"), static_cast(val)); -if (auto val = parser.get_int64(STR("UField"), STR("Next"), -1); val != -1) Unreal::UField::MemberOffsets.emplace(STR("Next"), static_cast(val)); -if (auto val = parser.get_int64(STR("FEnumProperty"), STR("Enum"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UField"), SYSSTR("Next"), -1); val != -1) Unreal::UField::MemberOffsets.emplace(STR("Next"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("FEnumProperty"), SYSSTR("Enum"), -1); val != -1) Unreal::FEnumProperty::MemberOffsets.emplace(STR("Enum"), static_cast(val)); -if (auto val = parser.get_int64(STR("FEnumProperty"), STR("UnderlyingProp"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FEnumProperty"), SYSSTR("UnderlyingProp"), -1); val != -1) Unreal::FEnumProperty::MemberOffsets.emplace(STR("UnderlyingProp"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("ChildProperties"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("ChildProperties"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("ChildProperties"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("Children"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("Children"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("Children"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("DestructorLink"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("DestructorLink"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("DestructorLink"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("MinAlignment"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("MinAlignment"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("MinAlignment"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("PostConstructLink"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("PostConstructLink"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("PostConstructLink"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("PropertiesSize"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("PropertiesSize"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("PropertiesSize"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("PropertyLink"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("PropertyLink"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("PropertyLink"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("RefLink"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("RefLink"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("RefLink"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("Script"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("Script"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("ScriptAndPropertyObjectReferences"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("Script"), -1); val != -1) + Unreal::UStruct::MemberOffsets.emplace(STR("Script"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("ScriptAndPropertyObjectReferences"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("ScriptAndPropertyObjectReferences"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("ScriptObjectReferences"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("ScriptObjectReferences"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("ScriptObjectReferences"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("SuperStruct"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("SuperStruct"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("SuperStruct"), static_cast(val)); -if (auto val = parser.get_int64(STR("UStruct"), STR("UnresolvedScriptProperties"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UStruct"), SYSSTR("UnresolvedScriptProperties"), -1); val != -1) Unreal::UStruct::MemberOffsets.emplace(STR("UnresolvedScriptProperties"), static_cast(val)); -if (auto val = parser.get_int64(STR("FDelegateProperty"), STR("SignatureFunction"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FDelegateProperty"), SYSSTR("SignatureFunction"), -1); val != -1) Unreal::FDelegateProperty::MemberOffsets.emplace(STR("SignatureFunction"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("ActiveSplitscreenType"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("ActiveSplitscreenType"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("ActiveSplitscreenType"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("AudioDeviceHandle"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("AudioDeviceHandle"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("AudioDeviceHandle"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("CurrentBufferVisualizationMode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("CurrentBufferVisualizationMode"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("CurrentBufferVisualizationMode"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("CurrentLumenVisualizationMode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("CurrentLumenVisualizationMode"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("CurrentLumenVisualizationMode"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("CurrentNaniteVisualizationMode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("CurrentNaniteVisualizationMode"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("CurrentNaniteVisualizationMode"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("CurrentVirtualShadowMapVisualizationMode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("CurrentVirtualShadowMapVisualizationMode"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("CurrentVirtualShadowMapVisualizationMode"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("DebugProperties"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("DebugProperties"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("DebugProperties"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("EngineShowFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("EngineShowFlags"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("EngineShowFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("GameLayerManagerPtr"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("GameLayerManagerPtr"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("GameLayerManagerPtr"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("HighResScreenshotDialog"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("HighResScreenshotDialog"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("HighResScreenshotDialog"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("MaxSplitscreenPlayers"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("MaxSplitscreenPlayers"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("MaxSplitscreenPlayers"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("MouseCaptureMode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("MouseCaptureMode"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("MouseCaptureMode"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("MouseLockMode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("MouseLockMode"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("MouseLockMode"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("SplitscreenInfo"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("SplitscreenInfo"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("SplitscreenInfo"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("StatHitchesData"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("StatHitchesData"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("StatHitchesData"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("StatUnitData"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("StatUnitData"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("StatUnitData"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("TitleSafeZone"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("TitleSafeZone"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("TitleSafeZone"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("ViewModeIndex"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("ViewModeIndex"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("ViewModeIndex"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("Viewport"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("Viewport"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("Viewport"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("ViewportConsole"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("ViewportConsole"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("ViewportConsole"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("ViewportFrame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("ViewportFrame"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("ViewportFrame"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("ViewportOverlayWidget"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("ViewportOverlayWidget"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("ViewportOverlayWidget"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("Window"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("Window"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("Window"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("World"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("World"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("World"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bDisableSplitScreenOverride"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bDisableSplitScreenOverride"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bDisableSplitScreenOverride"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bDisableWorldRendering"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bDisableWorldRendering"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bDisableWorldRendering"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bHasAudioFocus"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bHasAudioFocus"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bHasAudioFocus"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bHideCursorDuringCapture"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bHideCursorDuringCapture"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bHideCursorDuringCapture"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bIgnoreInput"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bIgnoreInput"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bIgnoreInput"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bIsMouseOverClient"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bIsMouseOverClient"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bIsMouseOverClient"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bIsPlayInEditorViewport"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bIsPlayInEditorViewport"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bIsPlayInEditorViewport"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bLockDuringCapture"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bLockDuringCapture"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bLockDuringCapture"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bShowTitleSafeZone"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bShowTitleSafeZone"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bShowTitleSafeZone"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bSuppressTransitionMessage"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bSuppressTransitionMessage"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bSuppressTransitionMessage"), static_cast(val)); -if (auto val = parser.get_int64(STR("UGameViewportClient"), STR("bUseSoftwareCursorWidgets"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UGameViewportClient"), SYSSTR("bUseSoftwareCursorWidgets"), -1); val != -1) Unreal::UGameViewportClient::MemberOffsets.emplace(STR("bUseSoftwareCursorWidgets"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArAllowLazyLoading"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArAllowLazyLoading"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArAllowLazyLoading"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArContainsCode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArContainsCode"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArContainsCode"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArContainsMap"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArContainsMap"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArContainsMap"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArCustomPropertyList"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArCustomPropertyList"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArCustomPropertyList"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArEngineNetVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArEngineNetVer"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArEngineNetVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArEngineVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArEngineVer"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArEngineVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArForceByteSwapping"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArForceByteSwapping"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArForceByteSwapping"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArForceUnicode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArForceUnicode"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArForceUnicode"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArGameNetVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArGameNetVer"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArGameNetVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIgnoreArchetypeRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIgnoreArchetypeRef"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIgnoreArchetypeRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIgnoreClassGeneratedByRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIgnoreClassGeneratedByRef"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIgnoreClassGeneratedByRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIgnoreClassRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIgnoreClassRef"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIgnoreClassRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIgnoreOuterRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIgnoreOuterRef"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIgnoreOuterRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsCountingMemory"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsCountingMemory"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsCountingMemory"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsCriticalError"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsCriticalError"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsCriticalError"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsError"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsError"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsError"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsFilterEditorOnly"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsFilterEditorOnly"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsFilterEditorOnly"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsLoading"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsLoading"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsLoading"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsModifyingWeakAndStrongReferences"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsModifyingWeakAndStrongReferences"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsModifyingWeakAndStrongReferences"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsNetArchive"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsNetArchive"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsNetArchive"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsObjectReferenceCollector"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsObjectReferenceCollector"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsObjectReferenceCollector"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsPersistent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsPersistent"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsPersistent"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsSaveGame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsSaveGame"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsSaveGame"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsSaving"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsSaving"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsSaving"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsTextFormat"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsTextFormat"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsTextFormat"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArIsTransacting"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArIsTransacting"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArIsTransacting"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArLicenseeUE4Ver"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArLicenseeUE4Ver"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArLicenseeUE4Ver"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArMaxSerializeSize"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArMaxSerializeSize"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArMaxSerializeSize"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArNetVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArNetVer"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArNetVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArNoDelta"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArNoDelta"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArNoDelta"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArNoIntraPropertyDelta"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArNoIntraPropertyDelta"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArNoIntraPropertyDelta"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArPortFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArPortFlags"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArPortFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArRequiresLocalizationGather"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArRequiresLocalizationGather"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArRequiresLocalizationGather"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArSerializingDefaults"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArSerializingDefaults"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArSerializingDefaults"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArShouldSkipBulkData"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArShouldSkipBulkData"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArShouldSkipBulkData"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArUE4Ver"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArUE4Ver"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArUE4Ver"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArUseCustomPropertyList"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArUseCustomPropertyList"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArUseCustomPropertyList"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("ArWantBinaryPropertySerialization"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("ArWantBinaryPropertySerialization"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("ArWantBinaryPropertySerialization"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("CookingTargetPlatform"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("CookingTargetPlatform"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("CookingTargetPlatform"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("CustomVersionContainer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("CustomVersionContainer"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("CustomVersionContainer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("SerializedProperty"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("SerializedProperty"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("SerializedProperty"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchive"), STR("bCustomVersionsAreReset"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchive"), SYSSTR("bCustomVersionsAreReset"), -1); val != -1) Unreal::FArchive::MemberOffsets.emplace(STR("bCustomVersionsAreReset"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("CurrentID"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("CurrentID"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("CurrentID"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("DefaultPlayerName"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("DefaultPlayerName"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("DefaultPlayerName"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("EngineMessageClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("EngineMessageClass"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("EngineMessageClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("GameModeClassAliases"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("GameModeClassAliases"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("GameModeClassAliases"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("GameSession"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("GameSession"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("GameSession"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("HUDClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("HUDClass"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("HUDClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("InactivePlayerArray"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("InactivePlayerArray"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("InactivePlayerArray"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("InactivePlayerStateLifeSpan"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("InactivePlayerStateLifeSpan"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("InactivePlayerStateLifeSpan"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("MatchState"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("MatchState"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("MatchState"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("MaxInactivePlayers"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("MaxInactivePlayers"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("MaxInactivePlayers"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("MinRespawnDelay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("MinRespawnDelay"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("MinRespawnDelay"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("NumBots"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("NumBots"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("NumBots"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("NumPlayers"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("NumPlayers"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("NumPlayers"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("NumSpectators"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("NumSpectators"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("NumSpectators"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("NumTravellingPlayers"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("NumTravellingPlayers"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("NumTravellingPlayers"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("OptionsString"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("OptionsString"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("OptionsString"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("PlayerStateClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("PlayerStateClass"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("PlayerStateClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("SpectatorClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("SpectatorClass"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("SpectatorClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("bDelayedStart"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("bDelayedStart"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("bDelayedStart"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("bHandleDedicatedServerReplays"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("bHandleDedicatedServerReplays"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("bHandleDedicatedServerReplays"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("bPauseable"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("bPauseable"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("bPauseable"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("bStartPlayersAsSpectators"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("bStartPlayersAsSpectators"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("bStartPlayersAsSpectators"), static_cast(val)); -if (auto val = parser.get_int64(STR("AGameMode"), STR("bUseSeamlessTravel"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AGameMode"), SYSSTR("bUseSeamlessTravel"), -1); val != -1) Unreal::AGameMode::MemberOffsets.emplace(STR("bUseSeamlessTravel"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("AttachmentReplication"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("AttachmentReplication"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("AttachmentReplication"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("AutoReceiveInput"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("AutoReceiveInput"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("AutoReceiveInput"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("CachedLastRenderTime"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("CachedLastRenderTime"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("CachedLastRenderTime"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("ControllingMatineeActors"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("ControllingMatineeActors"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("ControllingMatineeActors"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("CreationTime"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("CreationTime"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("CreationTime"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("CustomTimeDilation"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("CustomTimeDilation"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("CustomTimeDilation"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("DefaultUpdateOverlapsMethodDuringLevelStreaming"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("DefaultUpdateOverlapsMethodDuringLevelStreaming"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("DefaultUpdateOverlapsMethodDuringLevelStreaming"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("DetachFence"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("DetachFence"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("DetachFence"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("HiddenEditorViews"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("HiddenEditorViews"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("HiddenEditorViews"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("InitialLifeSpan"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("InitialLifeSpan"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("InitialLifeSpan"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("InputComponent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("InputComponent"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("InputComponent"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("InputConsumeOption_DEPRECATED"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("InputConsumeOption_DEPRECATED"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("InputConsumeOption_DEPRECATED"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("InputPriority"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("InputPriority"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("InputPriority"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("LastNetUpdateTime"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("LastNetUpdateTime"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("LastNetUpdateTime"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("LastRenderTime"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("LastRenderTime"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("LastRenderTime"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("Layers"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("Layers"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("MinNetUpdateFrequency"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("Layers"), -1); val != -1) + Unreal::AActor::MemberOffsets.emplace(STR("Layers"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("MinNetUpdateFrequency"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("MinNetUpdateFrequency"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetCullDistanceSquared"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetCullDistanceSquared"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetCullDistanceSquared"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetDormancy"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetDormancy"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetDormancy"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetDriverName"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetDriverName"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetDriverName"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetPriority"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetPriority"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetPriority"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetTag"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetTag"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetUpdateFrequency"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetTag"), -1); val != -1) + Unreal::AActor::MemberOffsets.emplace(STR("NetTag"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetUpdateFrequency"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetUpdateFrequency"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("NetUpdateTime"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("NetUpdateTime"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("NetUpdateTime"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnActorBeginOverlap"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnActorBeginOverlap"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnActorBeginOverlap"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnActorEndOverlap"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnActorEndOverlap"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnActorEndOverlap"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnActorHit"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnActorHit"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnActorHit"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnBeginCursorOver"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnBeginCursorOver"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnBeginCursorOver"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnClicked"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnClicked"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnClicked"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnDestroyed"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnDestroyed"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnDestroyed"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnEndCursorOver"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnEndCursorOver"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnEndCursorOver"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnEndPlay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnEndPlay"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnEndPlay"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnInputTouchBegin"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnInputTouchBegin"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnInputTouchBegin"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnInputTouchEnd"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnInputTouchEnd"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnInputTouchEnd"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnInputTouchEnter"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnInputTouchEnter"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnInputTouchEnter"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnInputTouchLeave"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnInputTouchLeave"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnInputTouchLeave"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnReleased"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnReleased"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnReleased"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnTakeAnyDamage"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnTakeAnyDamage"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnTakeAnyDamage"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnTakePointDamage"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnTakePointDamage"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnTakePointDamage"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("OnTakeRadialDamage"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("OnTakeRadialDamage"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("OnTakeRadialDamage"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("ParentComponent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("ParentComponent"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("ParentComponent"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("PrimaryActorTick"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("PrimaryActorTick"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("PrimaryActorTick"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("RayTracingGroupId"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("RayTracingGroupId"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("RayTracingGroupId"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("RemoteRole"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("RemoteRole"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("RemoteRole"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("ReplicatedComponentsInfo"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("ReplicatedComponentsInfo"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("ReplicatedComponentsInfo"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("ReplicatedMovement"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("ReplicatedMovement"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("ReplicatedMovement"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("ReplicatedSubObjects"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("ReplicatedSubObjects"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("ReplicatedSubObjects"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("Role"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("Role"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("RootComponent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("Role"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("Role"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("RootComponent"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("RootComponent"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("SpawnCollisionHandlingMethod"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("SpawnCollisionHandlingMethod"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("SpawnCollisionHandlingMethod"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("Tags"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("Tags"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("TimerHandle_LifeSpanExpired"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("Tags"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("Tags"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("TimerHandle_LifeSpanExpired"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("TimerHandle_LifeSpanExpired"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("UpdateOverlapsMethodDuringLevelStreaming"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("UpdateOverlapsMethodDuringLevelStreaming"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("UpdateOverlapsMethodDuringLevelStreaming"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorBeginningPlayFromLevelStreaming"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorBeginningPlayFromLevelStreaming"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorBeginningPlayFromLevelStreaming"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorEnableCollision"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorEnableCollision"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorEnableCollision"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorInitialized"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorInitialized"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorInitialized"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorIsBeingConstructed"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorIsBeingConstructed"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorIsBeingConstructed"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorIsBeingDestroyed"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorIsBeingDestroyed"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorIsBeingDestroyed"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorSeamlessTraveled"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorSeamlessTraveled"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorSeamlessTraveled"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bActorWantsDestroyDuringBeginPlay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bActorWantsDestroyDuringBeginPlay"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bActorWantsDestroyDuringBeginPlay"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bAllowReceiveTickEventOnDedicatedServer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bAllowReceiveTickEventOnDedicatedServer"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bAllowReceiveTickEventOnDedicatedServer"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bAllowTickBeforeBeginPlay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bAllowTickBeforeBeginPlay"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bAllowTickBeforeBeginPlay"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bAlwaysRelevant"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bAlwaysRelevant"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bAlwaysRelevant"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bAsyncPhysicsTickEnabled"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bAsyncPhysicsTickEnabled"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bAsyncPhysicsTickEnabled"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bAutoDestroyWhenFinished"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bAutoDestroyWhenFinished"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bAutoDestroyWhenFinished"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bBlockInput"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bBlockInput"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bBlockInput"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bCallPreReplication"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bCallPreReplication"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bCallPreReplication"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bCallPreReplicationForReplay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bCallPreReplicationForReplay"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bCallPreReplicationForReplay"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bCanBeDamaged"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bCanBeDamaged"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bCanBeDamaged"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bCanBeInCluster"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bCanBeInCluster"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bCanBeInCluster"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bCollideWhenPlacing"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bCollideWhenPlacing"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bCollideWhenPlacing"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bEnableAutoLODGeneration"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bEnableAutoLODGeneration"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bEnableAutoLODGeneration"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bExchangedRoles"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bExchangedRoles"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bExchangedRoles"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bFindCameraComponentWhenViewTarget"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bFindCameraComponentWhenViewTarget"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bFindCameraComponentWhenViewTarget"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bForceNetAddressable"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bForceNetAddressable"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bForceNetAddressable"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bGenerateOverlapEventsDuringLevelStreaming"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bGenerateOverlapEventsDuringLevelStreaming"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bGenerateOverlapEventsDuringLevelStreaming"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bHasDeferredComponentRegistration"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bHasDeferredComponentRegistration"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bHasDeferredComponentRegistration"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bHasFinishedSpawning"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bHasFinishedSpawning"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bHasFinishedSpawning"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bHasRegisteredAllComponents"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bHasRegisteredAllComponents"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bHasRegisteredAllComponents"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bHidden"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bHidden"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bIgnoresOriginShifting"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bHidden"), -1); val != -1) + Unreal::AActor::MemberOffsets.emplace(STR("bHidden"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bIgnoresOriginShifting"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bIgnoresOriginShifting"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bIsEditorOnlyActor"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bIsEditorOnlyActor"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bIsEditorOnlyActor"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bNetCheckedInitialPhysicsState"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bNetCheckedInitialPhysicsState"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bNetCheckedInitialPhysicsState"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bNetLoadOnClient"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bNetLoadOnClient"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bNetLoadOnClient"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bNetStartup"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bNetStartup"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bNetStartup"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bNetTemporary"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bNetTemporary"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bNetTemporary"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bNetUseOwnerRelevancy"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bNetUseOwnerRelevancy"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bNetUseOwnerRelevancy"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bOnlyRelevantToOwner"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bOnlyRelevantToOwner"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bOnlyRelevantToOwner"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bPendingNetUpdate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bPendingNetUpdate"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bPendingNetUpdate"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bRelevantForLevelBounds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bRelevantForLevelBounds"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bRelevantForLevelBounds"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bRelevantForNetworkReplays"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bRelevantForNetworkReplays"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bRelevantForNetworkReplays"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bReplayRewindable"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bReplayRewindable"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bReplayRewindable"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bReplicateMovement"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bReplicateMovement"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bReplicateMovement"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bReplicateUsingRegisteredSubObjectList"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bReplicateUsingRegisteredSubObjectList"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bReplicateUsingRegisteredSubObjectList"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bReplicates"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bReplicates"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bReplicates"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bRunningUserConstructionScript"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bRunningUserConstructionScript"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bRunningUserConstructionScript"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bTearOff"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bTearOff"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bTearOff"), static_cast(val)); -if (auto val = parser.get_int64(STR("AActor"), STR("bTickFunctionsRegistered"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("AActor"), SYSSTR("bTickFunctionsRegistered"), -1); val != -1) Unreal::AActor::MemberOffsets.emplace(STR("bTickFunctionsRegistered"), static_cast(val)); -if (auto val = parser.get_int64(STR("UPlayer"), STR("ConfiguredInternetSpeed"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UPlayer"), SYSSTR("ConfiguredInternetSpeed"), -1); val != -1) Unreal::UPlayer::MemberOffsets.emplace(STR("ConfiguredInternetSpeed"), static_cast(val)); -if (auto val = parser.get_int64(STR("UPlayer"), STR("ConfiguredLanSpeed"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UPlayer"), SYSSTR("ConfiguredLanSpeed"), -1); val != -1) Unreal::UPlayer::MemberOffsets.emplace(STR("ConfiguredLanSpeed"), static_cast(val)); -if (auto val = parser.get_int64(STR("UPlayer"), STR("CurrentNetSpeed"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UPlayer"), SYSSTR("CurrentNetSpeed"), -1); val != -1) Unreal::UPlayer::MemberOffsets.emplace(STR("CurrentNetSpeed"), static_cast(val)); -if (auto val = parser.get_int64(STR("FByteProperty"), STR("Enum"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FByteProperty"), SYSSTR("Enum"), -1); val != -1) Unreal::FByteProperty::MemberOffsets.emplace(STR("Enum"), static_cast(val)); -if (auto val = parser.get_int64(STR("ULocalPlayer"), STR("AspectRatioAxisConstraint"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("ULocalPlayer"), SYSSTR("AspectRatioAxisConstraint"), -1); val != -1) Unreal::ULocalPlayer::MemberOffsets.emplace(STR("AspectRatioAxisConstraint"), static_cast(val)); -if (auto val = parser.get_int64(STR("ULocalPlayer"), STR("ControllerId"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("ULocalPlayer"), SYSSTR("ControllerId"), -1); val != -1) Unreal::ULocalPlayer::MemberOffsets.emplace(STR("ControllerId"), static_cast(val)); -if (auto val = parser.get_int64(STR("ULocalPlayer"), STR("LastViewLocation"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("ULocalPlayer"), SYSSTR("LastViewLocation"), -1); val != -1) Unreal::ULocalPlayer::MemberOffsets.emplace(STR("LastViewLocation"), static_cast(val)); -if (auto val = parser.get_int64(STR("ULocalPlayer"), STR("ViewportClient"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("ULocalPlayer"), SYSSTR("ViewportClient"), -1); val != -1) Unreal::ULocalPlayer::MemberOffsets.emplace(STR("ViewportClient"), static_cast(val)); -if (auto val = parser.get_int64(STR("ULocalPlayer"), STR("bSentSplitJoin"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("ULocalPlayer"), SYSSTR("bSentSplitJoin"), -1); val != -1) Unreal::ULocalPlayer::MemberOffsets.emplace(STR("bSentSplitJoin"), static_cast(val)); -if (auto val = parser.get_int64(STR("FMulticastDelegateProperty"), STR("SignatureFunction"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FMulticastDelegateProperty"), SYSSTR("SignatureFunction"), -1); val != -1) Unreal::FMulticastDelegateProperty::MemberOffsets.emplace(STR("SignatureFunction"), static_cast(val)); -if (auto val = parser.get_int64(STR("FObjectPropertyBase"), STR("PropertyClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FObjectPropertyBase"), SYSSTR("PropertyClass"), -1); val != -1) Unreal::FObjectPropertyBase::MemberOffsets.emplace(STR("PropertyClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArAllowLazyLoading"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArAllowLazyLoading"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArAllowLazyLoading"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArContainsCode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArContainsCode"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArContainsCode"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArContainsMap"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArContainsMap"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArContainsMap"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArCustomPropertyList"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArCustomPropertyList"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArCustomPropertyList"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArEngineNetVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArEngineNetVer"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArEngineNetVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArEngineVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArEngineVer"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArEngineVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArForceByteSwapping"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArForceByteSwapping"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArForceByteSwapping"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArForceUnicode"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArForceUnicode"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArForceUnicode"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArGameNetVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArGameNetVer"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArGameNetVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIgnoreArchetypeRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIgnoreArchetypeRef"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIgnoreArchetypeRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIgnoreClassGeneratedByRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIgnoreClassGeneratedByRef"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIgnoreClassGeneratedByRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIgnoreClassRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIgnoreClassRef"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIgnoreClassRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIgnoreOuterRef"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIgnoreOuterRef"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIgnoreOuterRef"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsCountingMemory"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsCountingMemory"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsCountingMemory"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsCriticalError"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsCriticalError"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsCriticalError"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsError"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsError"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsError"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsFilterEditorOnly"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsFilterEditorOnly"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsFilterEditorOnly"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsLoading"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsLoading"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsLoading"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsLoadingFromCookedPackage"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsLoadingFromCookedPackage"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsLoadingFromCookedPackage"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsModifyingWeakAndStrongReferences"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsModifyingWeakAndStrongReferences"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsModifyingWeakAndStrongReferences"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsNetArchive"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsNetArchive"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsNetArchive"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsObjectReferenceCollector"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsObjectReferenceCollector"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsObjectReferenceCollector"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsPersistent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsPersistent"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsPersistent"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsSaveGame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsSaveGame"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsSaveGame"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsSaving"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsSaving"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsSaving"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsTextFormat"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsTextFormat"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsTextFormat"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArIsTransacting"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArIsTransacting"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArIsTransacting"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArLicenseeUE4Ver"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArLicenseeUE4Ver"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArLicenseeUE4Ver"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArLicenseeUEVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArLicenseeUEVer"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArLicenseeUEVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArMaxSerializeSize"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArMaxSerializeSize"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArMaxSerializeSize"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArNoDelta"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArNoDelta"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArNoDelta"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArNoIntraPropertyDelta"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArNoIntraPropertyDelta"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArNoIntraPropertyDelta"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArPortFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArPortFlags"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArPortFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArRequiresLocalizationGather"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArRequiresLocalizationGather"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArRequiresLocalizationGather"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArSerializingDefaults"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArSerializingDefaults"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArSerializingDefaults"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArShouldSkipBulkData"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArShouldSkipBulkData"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArShouldSkipBulkData"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArShouldSkipCompilingAssets"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArShouldSkipCompilingAssets"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArShouldSkipCompilingAssets"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArUE4Ver"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArUE4Ver"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArUE4Ver"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArUEVer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArUEVer"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArUEVer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArUseCustomPropertyList"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArUseCustomPropertyList"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArUseCustomPropertyList"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArUseUnversionedPropertySerialization"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArUseUnversionedPropertySerialization"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArUseUnversionedPropertySerialization"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("ArWantBinaryPropertySerialization"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("ArWantBinaryPropertySerialization"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("ArWantBinaryPropertySerialization"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("CookingTargetPlatform"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("CookingTargetPlatform"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("CookingTargetPlatform"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("CustomVersionContainer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("CustomVersionContainer"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("CustomVersionContainer"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("SerializedProperty"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("SerializedProperty"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("SerializedProperty"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArchiveState"), STR("bCustomVersionsAreReset"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArchiveState"), SYSSTR("bCustomVersionsAreReset"), -1); val != -1) Unreal::FArchiveState::MemberOffsets.emplace(STR("bCustomVersionsAreReset"), static_cast(val)); -if (auto val = parser.get_int64(STR("FField"), STR("ClassPrivate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FField"), SYSSTR("ClassPrivate"), -1); val != -1) Unreal::FField::MemberOffsets.emplace(STR("ClassPrivate"), static_cast(val)); -if (auto val = parser.get_int64(STR("FField"), STR("FlagsPrivate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FField"), SYSSTR("FlagsPrivate"), -1); val != -1) Unreal::FField::MemberOffsets.emplace(STR("FlagsPrivate"), static_cast(val)); -if (auto val = parser.get_int64(STR("FField"), STR("NamePrivate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FField"), SYSSTR("NamePrivate"), -1); val != -1) Unreal::FField::MemberOffsets.emplace(STR("NamePrivate"), static_cast(val)); -if (auto val = parser.get_int64(STR("FField"), STR("Next"), -1); val != -1) Unreal::FField::MemberOffsets.emplace(STR("Next"), static_cast(val)); -if (auto val = parser.get_int64(STR("FField"), STR("Owner"), -1); val != -1) Unreal::FField::MemberOffsets.emplace(STR("Owner"), static_cast(val)); -if (auto val = parser.get_int64(STR("FClassProperty"), STR("MetaClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FField"), SYSSTR("Next"), -1); val != -1) Unreal::FField::MemberOffsets.emplace(STR("Next"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("FField"), SYSSTR("Owner"), -1); val != -1) + Unreal::FField::MemberOffsets.emplace(STR("Owner"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("FClassProperty"), SYSSTR("MetaClass"), -1); val != -1) Unreal::FClassProperty::MemberOffsets.emplace(STR("MetaClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("FBoolProperty"), STR("ByteMask"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FBoolProperty"), SYSSTR("ByteMask"), -1); val != -1) Unreal::FBoolProperty::MemberOffsets.emplace(STR("ByteMask"), static_cast(val)); -if (auto val = parser.get_int64(STR("FBoolProperty"), STR("ByteOffset"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FBoolProperty"), SYSSTR("ByteOffset"), -1); val != -1) Unreal::FBoolProperty::MemberOffsets.emplace(STR("ByteOffset"), static_cast(val)); -if (auto val = parser.get_int64(STR("FBoolProperty"), STR("FieldMask"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FBoolProperty"), SYSSTR("FieldMask"), -1); val != -1) Unreal::FBoolProperty::MemberOffsets.emplace(STR("FieldMask"), static_cast(val)); -if (auto val = parser.get_int64(STR("FBoolProperty"), STR("FieldSize"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FBoolProperty"), SYSSTR("FieldSize"), -1); val != -1) Unreal::FBoolProperty::MemberOffsets.emplace(STR("FieldSize"), static_cast(val)); -if (auto val = parser.get_int64(STR("UScriptStruct"), STR("CppStructOps"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UScriptStruct"), SYSSTR("CppStructOps"), -1); val != -1) Unreal::UScriptStruct::MemberOffsets.emplace(STR("CppStructOps"), static_cast(val)); -if (auto val = parser.get_int64(STR("UScriptStruct"), STR("StructFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UScriptStruct"), SYSSTR("StructFlags"), -1); val != -1) Unreal::UScriptStruct::MemberOffsets.emplace(STR("StructFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UScriptStruct"), STR("bCppStructOpsFromBaseClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UScriptStruct"), SYSSTR("bCppStructOpsFromBaseClass"), -1); val != -1) Unreal::UScriptStruct::MemberOffsets.emplace(STR("bCppStructOpsFromBaseClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("UScriptStruct"), STR("bPrepareCppStructOpsCompleted"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UScriptStruct"), SYSSTR("bPrepareCppStructOpsCompleted"), -1); val != -1) Unreal::UScriptStruct::MemberOffsets.emplace(STR("bPrepareCppStructOpsCompleted"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("ActiveLevelCollectionIndex"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("ActiveLevelCollectionIndex"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("ActiveLevelCollectionIndex"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("AudioDeviceHandle"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("AudioDeviceHandle"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("AudioDeviceHandle"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("AudioTimeSeconds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("AudioTimeSeconds"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("AudioTimeSeconds"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("BlockTillLevelStreamingCompletedEpoch"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("BlockTillLevelStreamingCompletedEpoch"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("BlockTillLevelStreamingCompletedEpoch"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("BuildStreamingDataTimer"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("BuildStreamingDataTimer"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("BuildStreamingDataTimer"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("CachedViewInfoRenderedLastFrame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("CachedViewInfoRenderedLastFrame"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("CachedViewInfoRenderedLastFrame"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("CleanupWorldTag"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("CleanupWorldTag"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("CleanupWorldTag"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("CommittedPersistentLevelName"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("CommittedPersistentLevelName"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("CommittedPersistentLevelName"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("ContentBundleManager"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("ContentBundleManager"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("ContentBundleManager"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("DebugDrawTraceTag"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("DebugDrawTraceTag"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("DebugDrawTraceTag"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("DeltaRealTimeSeconds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("DeltaRealTimeSeconds"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("DeltaRealTimeSeconds"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("DeltaTimeSeconds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("DeltaTimeSeconds"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("DeltaTimeSeconds"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("ExtraReferencedObjects"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("ExtraReferencedObjects"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("ExtraReferencedObjects"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("FullPurgeTriggered"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("FullPurgeTriggered"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("FullPurgeTriggered"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("IsInBlockTillLevelStreamingCompleted"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("IsInBlockTillLevelStreamingCompleted"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("IsInBlockTillLevelStreamingCompleted"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("LWILastAssignedUID"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("LWILastAssignedUID"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("LWILastAssignedUID"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("LastRenderTime"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("LastRenderTime"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("LastRenderTime"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("LastTimeUnbuiltLightingWasEncountered"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("LastTimeUnbuiltLightingWasEncountered"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("LastTimeUnbuiltLightingWasEncountered"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("LevelSequenceActors"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("LevelSequenceActors"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("LevelSequenceActors"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NextSwitchCountdown"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NextSwitchCountdown"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NextSwitchCountdown"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NextURL"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NextURL"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NumInvalidReflectionCaptureComponents"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NextURL"), -1); val != -1) + Unreal::UWorld::MemberOffsets.emplace(STR("NextURL"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NumInvalidReflectionCaptureComponents"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NumInvalidReflectionCaptureComponents"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NumLightingUnbuiltObjects"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NumLightingUnbuiltObjects"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NumLightingUnbuiltObjects"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NumStreamingLevelsBeingLoaded"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NumStreamingLevelsBeingLoaded"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NumStreamingLevelsBeingLoaded"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NumTextureStreamingDirtyResources"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NumTextureStreamingDirtyResources"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NumTextureStreamingDirtyResources"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NumTextureStreamingUnbuiltComponents"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NumTextureStreamingUnbuiltComponents"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NumTextureStreamingUnbuiltComponents"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("NumUnbuiltReflectionCaptures"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("NumUnbuiltReflectionCaptures"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("NumUnbuiltReflectionCaptures"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("OnWorldPartitionInitializedEvent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("OnWorldPartitionInitializedEvent"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("OnWorldPartitionInitializedEvent"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("OnWorldPartitionUninitializedEvent"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("OnWorldPartitionUninitializedEvent"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("OnWorldPartitionUninitializedEvent"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("OriginLocation"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("OriginLocation"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("OriginLocation"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("OriginOffsetThisFrame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("OriginOffsetThisFrame"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("OriginOffsetThisFrame"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("PauseDelay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("PauseDelay"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("PauseDelay"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("PerModuleDataObjects"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("PerModuleDataObjects"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("PerModuleDataObjects"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("PlayerNum"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("PlayerNum"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("PlayerNum"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("PreparingLevelNames"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("PreparingLevelNames"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("PreparingLevelNames"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("RealTimeSeconds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("RealTimeSeconds"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("RealTimeSeconds"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("RequestedOriginLocation"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("RequestedOriginLocation"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("RequestedOriginLocation"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("ServerStreamingLevelsVisibility"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("ServerStreamingLevelsVisibility"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("ServerStreamingLevelsVisibility"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("StreamingLevelsPrefix"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("StreamingLevelsPrefix"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("StreamingLevelsPrefix"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("StreamingVolumeUpdateDelay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("StreamingVolumeUpdateDelay"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("StreamingVolumeUpdateDelay"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("TimeSeconds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("TimeSeconds"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("TimeSeconds"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("TimeSinceLastPendingKillPurge"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("TimeSinceLastPendingKillPurge"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("TimeSinceLastPendingKillPurge"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("UnpausedTimeSeconds"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("UnpausedTimeSeconds"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("UnpausedTimeSeconds"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("ViewLocationsRenderedLastFrame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("ViewLocationsRenderedLastFrame"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("ViewLocationsRenderedLastFrame"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bActorsInitialized"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bActorsInitialized"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bActorsInitialized"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bAggressiveLOD"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bAggressiveLOD"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bAggressiveLOD"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bAllowAudioPlayback"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bAllowAudioPlayback"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bAllowAudioPlayback"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bAllowDeferredPhysicsStateCreation"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bAllowDeferredPhysicsStateCreation"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bAllowDeferredPhysicsStateCreation"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bAreConstraintsDirty"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bAreConstraintsDirty"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bAreConstraintsDirty"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bBegunPlay"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bBegunPlay"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bBegunPlay"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bCleanedUpWorld"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bCleanedUpWorld"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bCleanedUpWorld"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bCreateRenderStateForHiddenComponents"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bCreateRenderStateForHiddenComponents"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bCreateRenderStateForHiddenComponents"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bDebugDrawAllTraceTags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bDebugDrawAllTraceTags"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bDebugDrawAllTraceTags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bDebugFrameStepExecution"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bDebugFrameStepExecution"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bDebugFrameStepExecution"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bDebugPauseExecution"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bDebugPauseExecution"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bDebugPauseExecution"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bDoDelayedUpdateCullDistanceVolumes"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bDoDelayedUpdateCullDistanceVolumes"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bDoDelayedUpdateCullDistanceVolumes"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bDropDetail"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bDropDetail"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bDropDetail"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bHack_Force_UsesGameHiddenFlags_True"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bHack_Force_UsesGameHiddenFlags_True"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bHack_Force_UsesGameHiddenFlags_True"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bHasEverBeenInitialized"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bHasEverBeenInitialized"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bHasEverBeenInitialized"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bInTick"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bInTick"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bInitializedAndNeedsCleanup"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bInTick"), -1); val != -1) + Unreal::UWorld::MemberOffsets.emplace(STR("bInTick"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bInitializedAndNeedsCleanup"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bInitializedAndNeedsCleanup"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsBuilt"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsBuilt"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsBuilt"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsCameraMoveableWhenPaused"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsCameraMoveableWhenPaused"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsCameraMoveableWhenPaused"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsDefaultLevel"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsDefaultLevel"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsDefaultLevel"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsLevelStreamingFrozen"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsLevelStreamingFrozen"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsLevelStreamingFrozen"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsRunningConstructionScript"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsRunningConstructionScript"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsRunningConstructionScript"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsTearingDown"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsTearingDown"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsTearingDown"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bIsWorldInitialized"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bIsWorldInitialized"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bIsWorldInitialized"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bKismetScriptError"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bKismetScriptError"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bKismetScriptError"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bMarkedObjectsPendingKill"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bMarkedObjectsPendingKill"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bMarkedObjectsPendingKill"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bMatchStarted"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bMatchStarted"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bMatchStarted"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bMaterialParameterCollectionInstanceNeedsDeferredUpdate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bMaterialParameterCollectionInstanceNeedsDeferredUpdate"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bMaterialParameterCollectionInstanceNeedsDeferredUpdate"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bOriginOffsetThisFrame"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bOriginOffsetThisFrame"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bOriginOffsetThisFrame"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bPlayersOnly"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bPlayersOnly"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bPlayersOnly"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bPlayersOnlyPending"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bPlayersOnlyPending"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bPlayersOnlyPending"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bPostTickComponentUpdate"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bPostTickComponentUpdate"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bPostTickComponentUpdate"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bRequestedBlockOnAsyncLoading"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bRequestedBlockOnAsyncLoading"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bRequestedBlockOnAsyncLoading"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bRequiresHitProxies"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bRequiresHitProxies"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bRequiresHitProxies"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bShouldDelayGarbageCollect"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bShouldDelayGarbageCollect"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bShouldDelayGarbageCollect"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bShouldForceUnloadStreamingLevels"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bShouldForceUnloadStreamingLevels"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bShouldForceUnloadStreamingLevels"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bShouldForceVisibleStreamingLevels"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bShouldForceVisibleStreamingLevels"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bShouldForceVisibleStreamingLevels"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bShouldSimulatePhysics"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bShouldSimulatePhysics"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bShouldSimulatePhysics"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bShouldTick"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bShouldTick"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bShouldTick"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bStartup"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bStartup"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bStartup"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bStreamingDataDirty"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bStreamingDataDirty"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bStreamingDataDirty"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bSupportsMakingInvisibleTransactionRequests"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bSupportsMakingInvisibleTransactionRequests"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bSupportsMakingInvisibleTransactionRequests"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bSupportsMakingVisibleTransactionRequests"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bSupportsMakingVisibleTransactionRequests"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bSupportsMakingVisibleTransactionRequests"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bTickNewlySpawned"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bTickNewlySpawned"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bTickNewlySpawned"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bTriggerPostLoadMap"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bTriggerPostLoadMap"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bTriggerPostLoadMap"), static_cast(val)); -if (auto val = parser.get_int64(STR("UWorld"), STR("bWorldWasLoadedThisTick"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UWorld"), SYSSTR("bWorldWasLoadedThisTick"), -1); val != -1) Unreal::UWorld::MemberOffsets.emplace(STR("bWorldWasLoadedThisTick"), static_cast(val)); -if (auto val = parser.get_int64(STR("FSetProperty"), STR("ElementProp"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FSetProperty"), SYSSTR("ElementProp"), -1); val != -1) Unreal::FSetProperty::MemberOffsets.emplace(STR("ElementProp"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassAddReferencedObjects"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassAddReferencedObjects"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassAddReferencedObjects"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassCastFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassCastFlags"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassCastFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassConfigName"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassConfigName"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassConfigName"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassConstructor"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassConstructor"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassConstructor"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassDefaultObject"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassDefaultObject"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassDefaultObject"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassFlags"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassFlags"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassFlags"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassGeneratedBy"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassGeneratedBy"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassGeneratedBy"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassUnique"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassUnique"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassUnique"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassVTableHelperCtorCaller"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassVTableHelperCtorCaller"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassVTableHelperCtorCaller"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("ClassWithin"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("ClassWithin"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("ClassWithin"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("CppClassStaticFunctions"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("CppClassStaticFunctions"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("CppClassStaticFunctions"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("FirstOwnedClassRep"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("FirstOwnedClassRep"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("FirstOwnedClassRep"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("Interfaces"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("Interfaces"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("Interfaces"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("NetFields"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("NetFields"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("NetFields"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("SparseClassData"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("SparseClassData"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("SparseClassData"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("SparseClassDataStruct"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("SparseClassDataStruct"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("SparseClassDataStruct"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("UberGraphFramePointerProperty"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("UberGraphFramePointerProperty"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("UberGraphFramePointerProperty"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("bCooked"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("bCooked"), static_cast(val)); -if (auto val = parser.get_int64(STR("UClass"), STR("bLayoutChanging"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("bCooked"), -1); val != -1) + Unreal::UClass::MemberOffsets.emplace(STR("bCooked"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UClass"), SYSSTR("bLayoutChanging"), -1); val != -1) Unreal::UClass::MemberOffsets.emplace(STR("bLayoutChanging"), static_cast(val)); -if (auto val = parser.get_int64(STR("UEnum"), STR("CppForm"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("CppForm"), static_cast(val)); -if (auto val = parser.get_int64(STR("UEnum"), STR("CppType"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("CppType"), static_cast(val)); -if (auto val = parser.get_int64(STR("UEnum"), STR("EnumDisplayNameFn"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UEnum"), SYSSTR("CppForm"), -1); val != -1) + Unreal::UEnum::MemberOffsets.emplace(STR("CppForm"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UEnum"), SYSSTR("CppType"), -1); val != -1) + Unreal::UEnum::MemberOffsets.emplace(STR("CppType"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("UEnum"), SYSSTR("EnumDisplayNameFn"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("EnumDisplayNameFn"), static_cast(val)); -if (auto val = parser.get_int64(STR("UEnum"), STR("EnumFlags_Internal"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UEnum"), SYSSTR("EnumFlags_Internal"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("EnumFlags_Internal"), static_cast(val)); -if (auto val = parser.get_int64(STR("UEnum"), STR("EnumPackage"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UEnum"), SYSSTR("EnumPackage"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("EnumPackage"), static_cast(val)); -if (auto val = parser.get_int64(STR("UEnum"), STR("Names"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("Names"), static_cast(val)); -if (auto val = parser.get_int64(STR("FMapProperty"), STR("KeyProp"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("UEnum"), SYSSTR("Names"), -1); val != -1) Unreal::UEnum::MemberOffsets.emplace(STR("Names"), static_cast(val)); +if (auto val = parser.get_int64(SYSSTR("FMapProperty"), SYSSTR("KeyProp"), -1); val != -1) Unreal::FMapProperty::MemberOffsets.emplace(STR("KeyProp"), static_cast(val)); -if (auto val = parser.get_int64(STR("FMapProperty"), STR("ValueProp"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FMapProperty"), SYSSTR("ValueProp"), -1); val != -1) Unreal::FMapProperty::MemberOffsets.emplace(STR("ValueProp"), static_cast(val)); -if (auto val = parser.get_int64(STR("FStructProperty"), STR("Struct"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FStructProperty"), SYSSTR("Struct"), -1); val != -1) Unreal::FStructProperty::MemberOffsets.emplace(STR("Struct"), static_cast(val)); -if (auto val = parser.get_int64(STR("FArrayProperty"), STR("Inner"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FArrayProperty"), SYSSTR("Inner"), -1); val != -1) Unreal::FArrayProperty::MemberOffsets.emplace(STR("Inner"), static_cast(val)); -if (auto val = parser.get_int64(STR("FInterfaceProperty"), STR("InterfaceClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FInterfaceProperty"), SYSSTR("InterfaceClass"), -1); val != -1) Unreal::FInterfaceProperty::MemberOffsets.emplace(STR("InterfaceClass"), static_cast(val)); -if (auto val = parser.get_int64(STR("FFieldPathProperty"), STR("PropertyClass"), -1); val != -1) +if (auto val = parser.get_int64(SYSSTR("FFieldPathProperty"), SYSSTR("PropertyClass"), -1); val != -1) Unreal::FFieldPathProperty::MemberOffsets.emplace(STR("PropertyClass"), static_cast(val)); diff --git a/UE4SS/include/Common.hpp b/UE4SS/include/Common.hpp index 53368ad82..ac04e76f1 100644 --- a/UE4SS/include/Common.hpp +++ b/UE4SS/include/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_UE4SS_EXPORTS #ifndef RC_UE4SS_API #define RC_UE4SS_API __declspec(dllimport) @@ -9,3 +11,11 @@ #define RC_UE4SS_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_UE4SS_API +#define RC_UE4SS_API +#endif + +#endif diff --git a/UE4SS/include/ExceptionHandling.hpp b/UE4SS/include/ExceptionHandling.hpp index c6745b158..ee87974a2 100644 --- a/UE4SS/include/ExceptionHandling.hpp +++ b/UE4SS/include/ExceptionHandling.hpp @@ -8,11 +8,11 @@ #define UE4SS_ERROR_OUTPUTTER() \ if (!Output::has_internal_error()) \ { \ - Output::send(STR("Error: {}\n"), to_wstring(e.what())); \ + Output::send(SYSSTR("Error: {}\n"), to_system(e.what())); \ } \ else \ { \ - printf_s("Internal Error: %s\n", e.what()); \ + fprintf(stderr, "Internal Error: %s\n", e.what()); \ } namespace RC diff --git a/UE4SS/include/GUI/ConsoleOutputDevice.hpp b/UE4SS/include/GUI/ConsoleOutputDevice.hpp index d1fdf58d6..f05768c88 100644 --- a/UE4SS/include/GUI/ConsoleOutputDevice.hpp +++ b/UE4SS/include/GUI/ConsoleOutputDevice.hpp @@ -30,7 +30,7 @@ namespace RC::Output public: auto has_optional_arg() const -> bool override; - auto receive(File::StringViewType fmt) const -> void override; - auto receive_with_optional_arg(File::StringViewType fmt, int32_t optional_arg = 0) const -> void override; + auto receive(SystemStringViewType fmt) const -> void override; + auto receive_with_optional_arg(SystemStringViewType fmt, int32_t optional_arg = 0) const -> void override; }; } // namespace RC::Output diff --git a/UE4SS/include/GUI/GUI.hpp b/UE4SS/include/GUI/GUI.hpp index 76026b6ec..453fd0493 100644 --- a/UE4SS/include/GUI/GUI.hpp +++ b/UE4SS/include/GUI/GUI.hpp @@ -3,28 +3,65 @@ #include #include #include +#include #include #include #include +#include #include #include struct ImGuiSettingsHandler; +#ifndef HAS_TUI +#ifndef HAS_GUI +static_assert(false, "HAS_GUI or HAS_TUI must be defined."); +#endif + +#else + +#ifdef HAS_GUI +static_assert(false, "HAS_GUI and HAS_TUI cannot be defined at the same time at this moment."); +#endif + + +namespace ImGui +{ + static void BeginDisabled(bool disabled = true) + { + } + static void EndDisabled() + { + } +} // namespace ImGui +#endif + namespace RC::GUI { class GUITab; // dunno why forward declaration is necessary enum class GfxBackend { +#ifdef HAS_D3D11 DX11, +#endif +#ifdef HAS_GLFW GLFW3_OpenGL3, +#endif +#ifdef HAS_TUI + TUI +#endif }; enum class OSBackend { +#ifdef WIN32 Windows, +#endif +#ifdef LINUX + TUI, +#endif }; struct WindowSize @@ -39,6 +76,25 @@ namespace RC::GUI int32_t y; }; + struct BackendProperty { + float x_offset_0; + float x_offset_1; + float xdiv; + float ydiv; + float separator_height; + bool quirk_tui; + }; + + extern RC_UE4SS_API BackendProperty g_backend_properties; + + #define XOFFSET_0 (g_backend_properties.x_offset_0) + #define XOFFSET_1 (g_backend_properties.x_offset_1) + #define XDIV (g_backend_properties.xdiv) + #define YDIV (g_backend_properties.ydiv) + #define SEPARATOR_HEIGHT (g_backend_properties.separator_height) + #define NEGATIVE_MARGIN(x) (IS_TUI ? (-0.01f) : (x)) + #define IS_TUI (g_backend_properties.quirk_tui) + class GfxBackendBase { protected: @@ -76,6 +132,9 @@ namespace RC::GUI { return false; }; + virtual auto set_backend_properties(BackendProperty &properties) -> void + { + }; }; class OSBackendBase @@ -275,4 +334,16 @@ namespace RC::GUI code_to_try(); }); } + + #define ATTACH_ICON(icon, str) ((icon str) + (IS_TUI ? (UE4SSProgram::settings_manager.TUI.TUINerdFont ? (0) : (sizeof(icon) - 1)) : 0)) + #define ICON_ALT(icon, alt) (IS_TUI ? ((UE4SSProgram::settings_manager.TUI.TUINerdFont) ? (icon) : (alt)) : (icon)) +/* +#ifdef HAS_GUI +#define ATTACH_ICON(icon, str) icon str +#define ICON_ALT(icon, alt) icon +#else +#define ATTACH_ICON(icon, str) ((icon str) + (UE4SSProgram::settings_manager.TUI.TUINerdFont ? (0) : (sizeof(icon) - 1))) +#define ICON_ALT(icon, alt) (UE4SSProgram::settings_manager.TUI.TUINerdFont) ? (icon) : (alt) +#endif +*/ } // namespace RC::GUI diff --git a/UE4SS/include/GUI/GUITab.hpp b/UE4SS/include/GUI/GUITab.hpp index 455e22567..c6a87cbfd 100644 --- a/UE4SS/include/GUI/GUITab.hpp +++ b/UE4SS/include/GUI/GUITab.hpp @@ -22,12 +22,12 @@ namespace RC::GUI private: RenderFunctionType render_function{}; CppUserModBase* owner{}; - StringType tab_name{}; + UEStringType tab_name{}; public: GUITab() = delete; - GUITab(StringViewType name, RenderFunctionType render_function) : tab_name(name), render_function(render_function){}; - GUITab(StringViewType name, RenderFunctionType render_function, CppUserModBase* owner) + GUITab(UEStringViewType name, RenderFunctionType render_function) : tab_name(name), render_function(render_function){}; + GUITab(UEStringViewType name, RenderFunctionType render_function, CppUserModBase* owner) : tab_name(name), render_function(render_function), owner(owner){}; ~GUITab() = default; diff --git a/UE4SS/include/GUI/LiveView.hpp b/UE4SS/include/GUI/LiveView.hpp index 1929cf773..970206f38 100644 --- a/UE4SS/include/GUI/LiveView.hpp +++ b/UE4SS/include/GUI/LiveView.hpp @@ -61,9 +61,9 @@ namespace RC::GUI Output::Targets output{}; FProperty* property{}; UObject* container{}; - StringType object_name{}; - StringType property_name{}; - StringType property_value{}; + UEStringType object_name{}; + UEStringType property_name{}; + UEStringType property_value{}; size_t hash{}; std::string history{}; float history_previous_max_scroll_y{}; @@ -77,7 +77,7 @@ namespace RC::GUI std::pair function_hook_ids{}; Watch() = delete; - Watch(StringType&& object_name, StringType&& property_name); + Watch(UEStringType&& object_name, UEStringType&& property_name); }; private: @@ -89,7 +89,11 @@ namespace RC::GUI UObject* m_currently_opened_tree_node{}; std::string m_current_property_value_buffer{}; int64_t m_current_enum_value_buffer{}; +#ifdef HAS_GUI float m_top_size{300.0f}; +#else + float m_top_size{14.0f}; +#endif float m_bottom_size{0.0f}; UFunctionCallerWidget* m_function_caller_widget{}; bool m_is_searching_by_name{}; diff --git a/UE4SS/include/GUI/LiveView/Filter/ClassNamesFilter.hpp b/UE4SS/include/GUI/LiveView/Filter/ClassNamesFilter.hpp index e4206c495..e3df573bb 100644 --- a/UE4SS/include/GUI/LiveView/Filter/ClassNamesFilter.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/ClassNamesFilter.hpp @@ -8,9 +8,9 @@ namespace RC::GUI::Filter class ClassNamesFilter { public: - static inline StringType s_debug_name{STR("ClassNamesFilter")}; + static inline UEStringType s_debug_name{STR("ClassNamesFilter")}; static inline std::string s_internal_class_names{}; - static inline std::vector list_class_names; + static inline std::vector list_class_names; static inline bool b_is_exclude{true}; static auto post_eval(UObject* object) -> bool diff --git a/UE4SS/include/GUI/LiveView/Filter/DefaultObjectsOnly.hpp b/UE4SS/include/GUI/LiveView/Filter/DefaultObjectsOnly.hpp index 121d01312..b8bfd3123 100644 --- a/UE4SS/include/GUI/LiveView/Filter/DefaultObjectsOnly.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/DefaultObjectsOnly.hpp @@ -7,7 +7,7 @@ namespace RC::GUI::Filter class DefaultObjectsOnly { public: - static inline StringType s_debug_name{STR("DefaultObjectsOnly")}; + static inline UEStringType s_debug_name{STR("DefaultObjectsOnly")}; static inline bool s_enabled{}; static auto pre_eval(UObject* object) -> bool diff --git a/UE4SS/include/GUI/LiveView/Filter/ExcludeClassName.hpp b/UE4SS/include/GUI/LiveView/Filter/ExcludeClassName.hpp new file mode 100644 index 000000000..6bf97062e --- /dev/null +++ b/UE4SS/include/GUI/LiveView/Filter/ExcludeClassName.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace RC::GUI::Filter +{ + class ExcludeClassName + { + public: + static inline UEStringType s_debug_name{STR("ExcludeClassName")}; + static inline UEStringType s_value{}; + static inline std::string s_internal_value{}; + + static auto post_eval(UObject* object) -> bool + { + if (!s_value.empty()) + { + auto class_name = object->GetClassPrivate()->GetName(); + auto pos = class_name.find(s_value); + return pos != class_name.npos; + } + else + { + return false; + } + } + }; +} // namespace RC::GUI::Filter diff --git a/UE4SS/include/GUI/LiveView/Filter/FunctionParamFlags.hpp b/UE4SS/include/GUI/LiveView/Filter/FunctionParamFlags.hpp index 3880d367a..be64a26aa 100644 --- a/UE4SS/include/GUI/LiveView/Filter/FunctionParamFlags.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/FunctionParamFlags.hpp @@ -9,7 +9,7 @@ namespace RC::GUI::Filter class FunctionParamFlags { public: - static inline StringType s_debug_name{STR("FunctionParamFlags")}; + static inline UEStringType s_debug_name{STR("FunctionParamFlags")}; static inline bool s_enabled{}; static inline std::array s_checkboxes{}; static inline EPropertyFlags s_value{}; diff --git a/UE4SS/include/GUI/LiveView/Filter/HasProperty.hpp b/UE4SS/include/GUI/LiveView/Filter/HasProperty.hpp index 3f2f6ce4a..e5b19b921 100644 --- a/UE4SS/include/GUI/LiveView/Filter/HasProperty.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/HasProperty.hpp @@ -8,9 +8,9 @@ namespace RC::GUI::Filter class HasProperty { public: - static inline StringType s_debug_name{STR("HasProperty")}; + static inline UEStringType s_debug_name{STR("HasProperty")}; static inline std::string s_internal_properties{}; - static inline std::vector list_properties{}; + static inline std::vector list_properties{}; static auto post_eval(UObject* object) -> bool { diff --git a/UE4SS/include/GUI/LiveView/Filter/HasPropertyType.hpp b/UE4SS/include/GUI/LiveView/Filter/HasPropertyType.hpp index 97484ba7a..44af3a079 100644 --- a/UE4SS/include/GUI/LiveView/Filter/HasPropertyType.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/HasPropertyType.hpp @@ -10,7 +10,7 @@ namespace RC::GUI::Filter class HasPropertyType { public: - static inline StringType s_debug_name{STR("HasPropertyType")}; + static inline UEStringType s_debug_name{STR("HasPropertyType")}; static inline std::string s_internal_property_types{}; static inline std::vector list_property_types{}; diff --git a/UE4SS/include/GUI/LiveView/Filter/IncludeDefaultObjects.hpp b/UE4SS/include/GUI/LiveView/Filter/IncludeDefaultObjects.hpp index 2dc3f3b9a..655d870c5 100644 --- a/UE4SS/include/GUI/LiveView/Filter/IncludeDefaultObjects.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/IncludeDefaultObjects.hpp @@ -7,7 +7,7 @@ namespace RC::GUI::Filter class IncludeDefaultObjects { public: - static inline StringType s_debug_name{STR("IncludeDefaultObjects")}; + static inline UEStringType s_debug_name{STR("IncludeDefaultObjects")}; static inline bool s_enabled{true}; static auto pre_eval(UObject* object) -> bool diff --git a/UE4SS/include/GUI/LiveView/Filter/InstancesOnly.hpp b/UE4SS/include/GUI/LiveView/Filter/InstancesOnly.hpp index 298afae00..c36be7839 100644 --- a/UE4SS/include/GUI/LiveView/Filter/InstancesOnly.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/InstancesOnly.hpp @@ -7,7 +7,7 @@ namespace RC::GUI::Filter class InstancesOnly { public: - static inline StringType s_debug_name{STR("InstancesOnly")}; + static inline UEStringType s_debug_name{STR("InstancesOnly")}; static inline bool s_enabled{}; static auto pre_eval(UObject* object) -> bool { diff --git a/UE4SS/include/GUI/LiveView/Filter/NonInstancesOnly.hpp b/UE4SS/include/GUI/LiveView/Filter/NonInstancesOnly.hpp index 23bfb47ef..9716d8a2b 100644 --- a/UE4SS/include/GUI/LiveView/Filter/NonInstancesOnly.hpp +++ b/UE4SS/include/GUI/LiveView/Filter/NonInstancesOnly.hpp @@ -8,7 +8,7 @@ namespace RC::GUI::Filter class NonInstancesOnly { public: - static inline StringType s_debug_name{STR("NonInstancesOnly")}; + static inline UEStringType s_debug_name{STR("NonInstancesOnly")}; static inline bool s_enabled{}; static auto pre_eval(UObject* object) -> bool diff --git a/UE4SS/include/GUI/NerdFont.hpp b/UE4SS/include/GUI/NerdFont.hpp new file mode 100644 index 000000000..03017a056 --- /dev/null +++ b/UE4SS/include/GUI/NerdFont.hpp @@ -0,0 +1,17 @@ +#pragma once + +#define ICON_FA_TERMINAL "\uf120" +#define ICON_FA_ARCHIVE "\uf187" +#define ICON_FA_SYNC "\uf46a" +#define ICON_FA_FILE_ALT "\uf15b" +#define ICON_FA_EYE "\uf06e" +#define ICON_FA_PUZZLE_PIECE "\uf12e" +#define ICON_FA_SAVE "\ueb4b" + +#define ICON_FA_ANGLE_DOUBLE_LEFT "<<" +#define ICON_FA_ANGLE_DOUBLE_RIGHT ">>" +#define ICON_FA_BAN "\uf05e" +#define ICON_FA_COPY "\uf0c5" +#define ICON_FA_SEARCH "\uf002" +#define ICON_FA_MINUS "\uf068" +#define ICON_FA_PLUS "\uf067" \ No newline at end of file diff --git a/UE4SS/include/GUI/TUI.hpp b/UE4SS/include/GUI/TUI.hpp new file mode 100644 index 000000000..bd22a8127 --- /dev/null +++ b/UE4SS/include/GUI/TUI.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace RC::GUI +{ + class Backend_TUI : public OSBackendBase + { + public: + auto init() -> void override; + auto imgui_backend_newframe() -> void override; + auto create_window(int loc_x, int loc_y, int size_x, int size_y) -> void override; + auto exec_message_loop(bool* exit_requested) -> void override; + auto shutdown() -> void override; + auto cleanup() -> void override; + auto get_window_handle() -> void* override; + auto get_window_size() -> WindowSize override; + auto get_window_position() -> WindowPosition override; + auto on_gfx_backend_set() -> void override; + }; + + class Backend_GfxTUI : public GfxBackendBase + { + public: + ~Backend_GfxTUI() = default; + + public: + auto init() -> void override; + auto imgui_backend_newframe() -> void override; + auto render(const float clear_color_with_alpha[4]) -> void override; + auto shutdown() -> void override; + auto cleanup() -> void override; + auto create_device() -> bool override; + auto cleanup_device() -> void override; + auto handle_window_resize(int64_t param_1, int64_t param_2) -> void override; + auto on_os_backend_set() -> void override; + auto get_window_size() -> WindowSize override; + auto exit_requested() -> bool override; + auto set_backend_properties(BackendProperty &properties) -> void override; + }; +} // namespace RC::GUI diff --git a/UE4SS/include/LuaCustomMemberFunctions.hpp b/UE4SS/include/LuaCustomMemberFunctions.hpp index d3f8c5b3f..f770ad44f 100644 --- a/UE4SS/include/LuaCustomMemberFunctions.hpp +++ b/UE4SS/include/LuaCustomMemberFunctions.hpp @@ -49,9 +49,9 @@ namespace RC::LuaBackCompat // Backwards compatibility with UE4SS 1.3. - auto StaticFindObject(UClass* ObjectClass, UObject* InObjectPackage, const wchar_t* OrigInName, bool bExactClass = false) -> UObject*; - auto StaticFindObject(const wchar_t* OrigInName) -> UObject*; - auto NotifyOnNewObject(const wchar_t* class_name, std::function& callable) -> void; + auto StaticFindObject(UClass* ObjectClass, UObject* InObjectPackage, const UECharType* OrigInName, bool bExactClass = false) -> UObject*; + auto StaticFindObject(const UECharType* OrigInName) -> UObject*; + auto NotifyOnNewObject(const UECharType* class_name, std::function& callable) -> void; auto lua_RegisterHook_wrapper(lua_State*) -> int; auto lua_UObjectBase_IsA_wrapper(lua_State*) -> int; @@ -140,6 +140,7 @@ namespace RC::UnrealRuntimeTypes using TypeHandlerMap = std::unordered_map; TypeHandlerMap TypeHandlers{ {"Pointer", &Array_Type_Handler_Ptr}, + // TODO: should I change this to something? {"wchar_t", &Array_Type_Handler_WChar_T}, }; diff --git a/UE4SS/include/LuaType/LuaCustomProperty.hpp b/UE4SS/include/LuaType/LuaCustomProperty.hpp index b19edc7e2..c69903742 100644 --- a/UE4SS/include/LuaType/LuaCustomProperty.hpp +++ b/UE4SS/include/LuaType/LuaCustomProperty.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace RC::Unreal { class UObject; @@ -17,11 +19,11 @@ namespace RC::LuaType class LuaCustomProperty { public: - std::wstring m_name{}; + SystemStringType m_name{}; std::unique_ptr m_property; public: - LuaCustomProperty(std::wstring name, std::unique_ptr property); + LuaCustomProperty(SystemStringType name, std::unique_ptr property); private: class PropertyList @@ -30,9 +32,9 @@ namespace RC::LuaType std::vector properties; public: - auto add(std::wstring property_name, std::unique_ptr) -> void; + auto add(SystemStringType property_name, std::unique_ptr) -> void; auto clear() -> void; - auto find_or_nullptr(Unreal::UObject* base, std::wstring property_name) -> Unreal::FProperty*; + auto find_or_nullptr(Unreal::UObject* base, SystemStringType property_name) -> Unreal::FProperty*; }; public: diff --git a/UE4SS/include/LuaType/LuaUObject.hpp b/UE4SS/include/LuaType/LuaUObject.hpp index ea81196a3..4fcc724c9 100644 --- a/UE4SS/include/LuaType/LuaUObject.hpp +++ b/UE4SS/include/LuaType/LuaUObject.hpp @@ -462,7 +462,7 @@ namespace RC::LuaType { lua.throw_error("Function 'GetProperty' requires a string as the first parameter"); } - std::wstring property_name = to_wstring(lua.get_string(2)); + auto property_name = to_system(lua.get_string(2)); auto reflection_table = lua.get_table(); const auto& reflected_object = reflection_table.get_userdata_field("ReflectedObject").get_remote_cpp_object(); @@ -478,7 +478,7 @@ namespace RC::LuaType { obj_as_struct = reflected_object->GetClassPrivate(); } - auto* property = obj_as_struct->FindProperty(Unreal::FName(property_name)); + auto* property = obj_as_struct->FindProperty(Unreal::FName(to_ue(property_name))); construct_xproperty(lua, property); return 1; @@ -586,7 +586,7 @@ No overload found for function 'UObject.ProcessConsoleExec'. { lua.throw_error(error_overload_not_found); } - auto cmd = to_wstring(lua.get_string()); + auto cmd = to_system(lua.get_string()); if (lua.get_stack_size() < 2) { @@ -601,7 +601,7 @@ No overload found for function 'UObject.ProcessConsoleExec'. auto executor = lua.get_userdata(); auto ar = Unreal::FOutputDevice{}; - auto return_value = lua_object.get_remote_cpp_object()->ProcessConsoleExec(cmd.c_str(), ar, executor.get_remote_cpp_object()); + auto return_value = lua_object.get_remote_cpp_object()->ProcessConsoleExec(to_ue_string(cmd).c_str(), ar, executor.get_remote_cpp_object()); lua.set_bool(return_value); return 1; @@ -641,7 +641,7 @@ No overload found for function 'UObject.ProcessConsoleExec'. { auto& lua_object = lua.get_userdata(); - const std::wstring& member_name = to_const_wstring(lua.get_string()); + auto member_name = to_system_string(lua.get_string()); // If nullptr then we assume the UObject wasn't found so lets return an invalid UObject to Lua // This allows the safe chaining of "__index" as long as the Lua script checks ":IsValid()" before using the object @@ -656,17 +656,17 @@ No overload found for function 'UObject.ProcessConsoleExec'. SelfType::construct(lua, static_cast(nullptr)); break; case Operation::Set: - Output::send(STR("[Lua][Error] Tried setting member variable '{}' but UObject instance is nullptr\n"), member_name); + Output::send(SYSSTR("[Lua][Error] Tried setting member variable '{}' but UObject instance is nullptr\n"), member_name); break; default: - Output::send(STR("[Lua][Error] The UObject instance is nullptr & operation type was invalid\n")); + Output::send(SYSSTR("[Lua][Error] The UObject instance is nullptr & operation type was invalid\n")); break; } return; } - Unreal::FName property_name = Unreal::FName(member_name); + Unreal::FName property_name = Unreal::FName(to_ue(member_name)); Unreal::FField* field = LuaCustomProperty::StaticStorage::property_list.find_or_nullptr(lua_object.get_remote_cpp_object(), member_name); if (!field) @@ -777,10 +777,10 @@ No overload found for function 'UObject.ProcessConsoleExec'. { // We can either throw an error and kill the execution /**/ - std::wstring property_type_name = property_type.ToString(); + auto property_type_name = to_string(property_type.ToString()); lua.throw_error(std::format( "[LocalUnrealParam::prepare_to_handle] Tried accessing unreal property without a registered handler. Property type '{}' not supported.", - to_string(property_type_name))); + property_type_name)); //*/ // Or we can treat unhandled property types as some sort of generic type diff --git a/UE4SS/include/Mod/CppMod.hpp b/UE4SS/include/Mod/CppMod.hpp index 92d90e147..f7b0014ec 100644 --- a/UE4SS/include/Mod/CppMod.hpp +++ b/UE4SS/include/Mod/CppMod.hpp @@ -2,12 +2,18 @@ #include +#ifdef WIN32 #define NOMINMAX #include +#else +#include +#endif #include #include +#include + namespace RC { namespace LuaMadeSimple @@ -22,17 +28,20 @@ namespace RC typedef void (*uninstall_type)(CppUserModBase*); private: - std::wstring m_dlls_path; - + SystemStringType m_dlls_path; +#ifdef WIN32 HMODULE m_main_dll_module = NULL; DLL_DIRECTORY_COOKIE m_dlls_path_cookie = NULL; +#else + void* m_dl_handle = nullptr; +#endif start_type m_start_mod_func = nullptr; uninstall_type m_uninstall_mod_func = nullptr; CppUserModBase* m_mod = nullptr; public: - CppMod(UE4SSProgram&, std::wstring&& mod_name, std::wstring&& mod_path); + CppMod(UE4SSProgram&, SystemStringType&& mod_name, SystemStringType&& mod_path); CppMod(CppMod&) = delete; CppMod(CppMod&&) = delete; ~CppMod() override; @@ -41,7 +50,7 @@ namespace RC auto start_mod() -> void override; auto uninstall() -> void override; - auto fire_on_lua_start(StringViewType mod_name, + auto fire_on_lua_start(SystemStringViewType mod_name, LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, @@ -50,7 +59,7 @@ namespace RC auto fire_on_lua_start(LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, std::vector& hook_luas) -> void; - auto fire_on_lua_stop(StringViewType mod_name, + auto fire_on_lua_stop(SystemStringViewType mod_name, LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, @@ -63,6 +72,6 @@ namespace RC auto fire_ui_init() -> void override; auto fire_program_start() -> void override; auto fire_update() -> void override; - auto fire_dll_load(std::wstring_view dll_name) -> void; + auto fire_dll_load(SystemStringViewType dll_name) -> void; }; } // namespace RC diff --git a/UE4SS/include/Mod/CppUserModBase.hpp b/UE4SS/include/Mod/CppUserModBase.hpp index 710f17210..b9cbf0108 100644 --- a/UE4SS/include/Mod/CppUserModBase.hpp +++ b/UE4SS/include/Mod/CppUserModBase.hpp @@ -5,18 +5,20 @@ #include #include +#ifdef HAS_UI #include #include +#endif namespace RC { struct ModMetadata { - const StringType ModName{}; - const StringType ModVersion{}; - const StringType ModDescription{}; - const StringType ModAuthors{}; - const StringType ModIntendedSDKVersion{}; + const UEStringType ModName{}; + const UEStringType ModVersion{}; + const UEStringType ModDescription{}; + const UEStringType ModAuthors{}; + const UEStringType ModIntendedSDKVersion{}; }; namespace LuaMadeSimple @@ -29,14 +31,15 @@ namespace RC class CppUserModBase { protected: +#ifdef HAS_UI std::vector> GUITabs{}; - +#endif public: - StringType ModName{}; - StringType ModVersion{}; - StringType ModDescription{}; - StringType ModAuthors{}; - StringType ModIntendedSDKVersion{}; + UEStringType ModName{}; + UEStringType ModVersion{}; + UEStringType ModDescription{}; + UEStringType ModAuthors{}; + UEStringType ModIntendedSDKVersion{}; public: RC_UE4SS_API CppUserModBase(); @@ -72,7 +75,7 @@ namespace RC * @param async_lua This is the Lua instance for asynchronous things like ExecuteAsync and ExecuteWithDelay. * @param hook_luas This is a container of Lua instances that are used for game-thread hooks like ExecuteInGameThread. */ - RC_UE4SS_API virtual auto on_lua_start(StringViewType mod_name, + RC_UE4SS_API virtual auto on_lua_start(SystemStringViewType mod_name, LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, @@ -103,7 +106,7 @@ namespace RC * @param async_lua This is the Lua instance for asynchronous things like ExecuteAsync and ExecuteWithDelay. * @param hook_luas This is a container of Lua instances that are used for game-thread hooks like ExecuteInGameThread. */ - RC_UE4SS_API virtual auto on_lua_stop(StringViewType mod_name, + RC_UE4SS_API virtual auto on_lua_stop(SystemStringViewType mod_name, LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, @@ -125,16 +128,18 @@ namespace RC { } - RC_UE4SS_API virtual auto on_dll_load(std::wstring_view dll_name) -> void + RC_UE4SS_API virtual auto on_dll_load(SystemStringViewType dll_name) -> void { } +#ifdef HAS_UI RC_UE4SS_API virtual auto render_tab() -> void{}; protected: - RC_UE4SS_API auto register_tab(std::wstring_view tab_name, GUI::GUITab::RenderFunctionType) -> void; + RC_UE4SS_API auto register_tab(UEStringViewType tab_name, GUI::GUITab::RenderFunctionType) -> void; RC_UE4SS_API auto register_keydown_event(Input::Key, const Input::EventCallbackCallable&, uint8_t custom_data = 0) -> void; RC_UE4SS_API auto register_keydown_event(Input::Key, const Input::Handler::ModifierKeyArray&, const Input::EventCallbackCallable&, uint8_t custom_data = 0) -> void; +#endif }; } // namespace RC diff --git a/UE4SS/include/Mod/LuaMod.hpp b/UE4SS/include/Mod/LuaMod.hpp index 31ad8435c..029e94987 100644 --- a/UE4SS/include/Mod/LuaMod.hpp +++ b/UE4SS/include/Mod/LuaMod.hpp @@ -26,7 +26,7 @@ namespace RC class LuaMod : public Mod { private: - std::wstring m_scripts_path; + SystemStringType m_scripts_path; LuaMadeSimple::Lua& m_lua; public: @@ -97,19 +97,19 @@ namespace RC static inline std::vector m_call_function_by_name_with_arguments_post_callbacks; static inline std::vector m_local_player_exec_pre_callbacks; static inline std::vector m_local_player_exec_post_callbacks; - static inline std::unordered_map m_global_command_lua_callbacks; - static inline std::unordered_map m_custom_command_lua_pre_callbacks; + static inline std::unordered_map m_global_command_lua_callbacks; + static inline std::unordered_map m_custom_command_lua_pre_callbacks; static inline std::vector m_game_thread_actions{}; // This is storage that persists through hot-reloads. - static inline std::unordered_map m_shared_lua_variables{}; - static inline std::unordered_map m_custom_event_callbacks{}; + static inline std::unordered_map m_shared_lua_variables{}; + static inline std::unordered_map m_custom_event_callbacks{}; static inline std::vector m_load_map_pre_callbacks{}; static inline std::vector m_load_map_post_callbacks{}; static inline std::vector m_init_game_state_pre_callbacks{}; static inline std::vector m_init_game_state_post_callbacks{}; static inline std::vector m_begin_play_pre_callbacks{}; static inline std::vector m_begin_play_post_callbacks{}; - static inline std::unordered_map m_script_hook_callbacks{}; + static inline std::unordered_map m_script_hook_callbacks{}; static inline std::unordered_map m_generic_hook_id_to_native_hook_id{}; // Generic hook ids are generated incrementally so the first one is 0 and the next one is always +1 from the last id. static inline int32_t m_last_generic_hook_id{}; @@ -124,7 +124,7 @@ namespace RC std::mutex m_actions_lock{}; public: - LuaMod(UE4SSProgram&, std::wstring&& mod_name, std::wstring&& mod_path); + LuaMod(UE4SSProgram&, SystemStringType&& mod_name, SystemStringType&& mod_path); ~LuaMod() override = default; private: diff --git a/UE4SS/include/Mod/Mod.hpp b/UE4SS/include/Mod/Mod.hpp index 0f22559b4..19d8f145d 100644 --- a/UE4SS/include/Mod/Mod.hpp +++ b/UE4SS/include/Mod/Mod.hpp @@ -26,8 +26,9 @@ namespace RC protected: #pragma warning(disable : 4251) - std::wstring m_mod_name; - std::wstring m_mod_path; + // we almost never use this to interact with UE + SystemStringType m_mod_name; + SystemStringType m_mod_path; #pragma warning(default : 4251) protected: @@ -45,11 +46,11 @@ namespace RC }; public: - Mod(UE4SSProgram&, std::wstring&& mod_name, std::wstring&& mod_path); + Mod(UE4SSProgram&, SystemStringType&& mod_name, SystemStringType&& mod_path); virtual ~Mod() = default; public: - auto get_name() const -> std::wstring_view; + auto get_name() const -> SystemStringViewType; virtual auto start_mod() -> void = 0; virtual auto uninstall() -> void = 0; diff --git a/UE4SS/include/ObjectDumper/ObjectToString.hpp b/UE4SS/include/ObjectDumper/ObjectToString.hpp index 0b0525a2b..e4f72a78d 100644 --- a/UE4SS/include/ObjectDumper/ObjectToString.hpp +++ b/UE4SS/include/ObjectDumper/ObjectToString.hpp @@ -8,11 +8,11 @@ namespace RC::ObjectDumper { using ToStringHash = size_t; - using ObjectToStringDecl = std::function; + using ObjectToStringDecl = std::function; extern std::unordered_map object_to_string_functions; using ObjectToStringComplexDeclCallable = const std::function&; - using ObjectToStringComplexDecl = std::function; + using ObjectToStringComplexDecl = std::function; extern std::unordered_map object_to_string_complex_functions; auto get_to_string(size_t hash) -> ObjectToStringDecl; @@ -20,33 +20,33 @@ namespace RC::ObjectDumper auto to_string_exists(size_t hash) -> bool; auto to_string_complex_exists(size_t hash) -> bool; - auto object_trivial_dump_to_string(void* p_this, std::wstring& out_line, const wchar_t* post_delimiter = L".") -> void; - auto object_to_string(void* p_this, std::wstring& out_line) -> void; + auto object_trivial_dump_to_string(void* p_this, SystemStringType& out_line, const SystemCharType* post_delimiter = SYSSTR(".")) -> void; + auto object_to_string(void* p_this, SystemStringType& out_line) -> void; - auto property_trivial_dump_to_string(void* p_this, std::wstring& out_line) -> void; - auto property_to_string(void* p_this, std::wstring& out_line) -> void; + auto property_trivial_dump_to_string(void* p_this, SystemStringType& out_line) -> void; + auto property_to_string(void* p_this, SystemStringType& out_line) -> void; - auto arrayproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto arrayproperty_to_string_complex(void* p_this, std::wstring& out_line, ObjectToStringComplexDeclCallable callable) -> void; + auto arrayproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto arrayproperty_to_string_complex(void* p_this, SystemStringType& out_line, ObjectToStringComplexDeclCallable callable) -> void; - auto mapproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto mapproperty_to_string_complex(void* p_this, std::wstring& out_line, ObjectToStringComplexDeclCallable callable) -> void; + auto mapproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto mapproperty_to_string_complex(void* p_this, SystemStringType& out_line, ObjectToStringComplexDeclCallable callable) -> void; - auto classproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto delegateproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto fieldpathproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto interfaceproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto multicastdelegateproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto objectproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto structproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto enumproperty_to_string(void* p_this, std::wstring& out_line) -> void; - auto boolproperty_to_string(void* p_this, std::wstring& out_line) -> void; + auto classproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto delegateproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto fieldpathproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto interfaceproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto multicastdelegateproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto objectproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto structproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto enumproperty_to_string(void* p_this, SystemStringType& out_line) -> void; + auto boolproperty_to_string(void* p_this, SystemStringType& out_line) -> void; - auto enum_to_string(void* p_this, std::wstring& out_line) -> void; - auto struct_to_string(void* p_this, std::wstring& out_line) -> void; - auto function_to_string(void* p_this, std::wstring& out_line) -> void; + auto enum_to_string(void* p_this, SystemStringType& out_line) -> void; + auto struct_to_string(void* p_this, SystemStringType& out_line) -> void; + auto function_to_string(void* p_this, SystemStringType& out_line) -> void; - auto scriptstruct_to_string_complex(void* p_this, std::wstring& out_line, ObjectToStringComplexDeclCallable callable) -> void; + auto scriptstruct_to_string_complex(void* p_this, SystemStringType& out_line, ObjectToStringComplexDeclCallable callable) -> void; auto init() -> void; } // namespace RC::ObjectDumper diff --git a/UE4SS/include/Platform.hpp b/UE4SS/include/Platform.hpp new file mode 100644 index 000000000..8ec568f2a --- /dev/null +++ b/UE4SS/include/Platform.hpp @@ -0,0 +1,5 @@ +#pragma once +#include + +std::filesystem::path get_executable_path(); +void add_dlsearch_folder(std::filesystem::path& path); diff --git a/UE4SS/include/SDKGenerator/Common.hpp b/UE4SS/include/SDKGenerator/Common.hpp index ce5793824..5280e7d88 100644 --- a/UE4SS/include/SDKGenerator/Common.hpp +++ b/UE4SS/include/SDKGenerator/Common.hpp @@ -31,19 +31,19 @@ namespace RC }; auto is_integral_type(Unreal::FProperty* property) -> bool; - auto get_native_enum_name(Unreal::UEnum* uenum, bool include_type = true) -> File::StringType; + auto get_native_enum_name(Unreal::UEnum* uenum, bool include_type = true) -> SystemStringType; auto generate_property_cxx_name(Unreal::FProperty* property, bool is_top_level_declaration, Unreal::UObject* class_context, - EnableForwardDeclarations = EnableForwardDeclarations::No) -> File::StringType; - auto generate_property_lua_name(Unreal::FProperty* property, bool is_top_level_declaration, Unreal::UObject* class_context) -> File::StringType; - auto sanitize_property_name(const File::StringType& property_name) -> File::StringType; - auto generate_delegate_name(Unreal::FProperty* property, const File::StringType& context_name) -> File::StringType; - auto get_native_class_name(Unreal::UClass* uclass, bool interface_name = false) -> File::StringType; - auto get_native_struct_name(Unreal::UScriptStruct* script_struct) -> File::StringType; + EnableForwardDeclarations = EnableForwardDeclarations::No) -> SystemStringType; + auto generate_property_lua_name(Unreal::FProperty* property, bool is_top_level_declaration, Unreal::UObject* class_context) -> SystemStringType; + auto sanitize_property_name(const SystemStringType& property_name) -> SystemStringType; + auto generate_delegate_name(Unreal::FProperty* property, const SystemStringType& context_name) -> SystemStringType; + auto get_native_class_name(Unreal::UClass* uclass, bool interface_name = false) -> SystemStringType; + auto get_native_struct_name(Unreal::UScriptStruct* script_struct) -> SystemStringType; auto get_native_delegate_type_name(Unreal::UFunction* signature_function, Unreal::UClass* current_class = nullptr, bool strip_outer_name = false) - -> File::StringType; + -> SystemStringType; auto is_delegate_signature_function(Unreal::UFunction* signature_function) -> bool; - auto strip_delegate_signature_postfix(Unreal::UFunction* signature_function) -> File::StringType; + auto strip_delegate_signature_postfix(Unreal::UFunction* signature_function) -> SystemStringType; } // namespace UEGenerator } // namespace RC diff --git a/UE4SS/include/SDKGenerator/JSONDumper.hpp b/UE4SS/include/SDKGenerator/JSONDumper.hpp index fea36c867..1a117118c 100644 --- a/UE4SS/include/SDKGenerator/JSONDumper.hpp +++ b/UE4SS/include/SDKGenerator/JSONDumper.hpp @@ -9,5 +9,5 @@ namespace RC namespace RC::UEGenerator::JSONDumper { - auto dump_to_json(File::StringViewType file_name) -> void; + auto dump_to_json(SystemStringType file_name) -> void; } diff --git a/UE4SS/include/SDKGenerator/TMapOverrideGen.hpp b/UE4SS/include/SDKGenerator/TMapOverrideGen.hpp index 31e76d6c5..6a93735a1 100644 --- a/UE4SS/include/SDKGenerator/TMapOverrideGen.hpp +++ b/UE4SS/include/SDKGenerator/TMapOverrideGen.hpp @@ -7,12 +7,12 @@ namespace RC::UEGenerator { - using RC::Unreal::FName; + using ::RC::Unreal::FName; class TMapOverrideGenerator { public: - static std::unordered_set MapProperties; + static ::std::unordered_set MapProperties; static auto generate_tmapoverride() -> void; }; } // namespace RC::UEGenerator diff --git a/UE4SS/include/SDKGenerator/UEHeaderGenerator.hpp b/UE4SS/include/SDKGenerator/UEHeaderGenerator.hpp index 59cf49670..584b6afb0 100644 --- a/UE4SS/include/SDKGenerator/UEHeaderGenerator.hpp +++ b/UE4SS/include/SDKGenerator/UEHeaderGenerator.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -26,15 +28,15 @@ namespace RC::Unreal namespace RC::UEGenerator { - using FFilePath = std::filesystem::path; - using UObject = RC::Unreal::UObject; - using UStruct = RC::Unreal::UStruct; - using UClass = RC::Unreal::UClass; - using FProperty = RC::Unreal::FProperty; - using FField = RC::Unreal::FField; - using UEnum = RC::Unreal::UEnum; - using UScriptStruct = RC::Unreal::UScriptStruct; - using UFunction = RC::Unreal::UFunction; + using FFilePath = ::std::filesystem::path; + using UObject = ::RC::Unreal::UObject; + using UStruct = ::RC::Unreal::UStruct; + using UClass = ::RC::Unreal::UClass; + using FProperty = ::RC::Unreal::FProperty; + using FField = ::RC::Unreal::FField; + using UEnum = ::RC::Unreal::UEnum; + using UScriptStruct = ::RC::Unreal::UScriptStruct; + using UFunction = ::RC::Unreal::UFunction; enum class DependencyLevel { @@ -65,14 +67,14 @@ namespace RC::UEGenerator struct PropertyTypeDeclarationContext { - std::wstring context_name; + SystemStringType context_name; class GeneratedSourceFile* source_file; bool is_top_level_declaration; bool* out_is_bitmask_bool; - PropertyTypeDeclarationContext(const std::wstring& context_name, + PropertyTypeDeclarationContext(const SystemStringType& context_name, GeneratedSourceFile* source_file = NULL, bool is_top_level_declaration = false, bool* out_is_bitmask_bool = NULL) @@ -91,60 +93,83 @@ namespace RC::UEGenerator struct StringInsensitiveCompare { - auto operator()(const std::wstring& a, const std::wstring& b) const -> bool + auto operator()(const SystemStringType& a, const SystemStringType& b) const -> bool { +#ifdef LINUX + // Create a case insensitive string compare for Linux + return strcasecmp(a.c_str(), b.c_str()) < 0; +#else return _wcsicmp(a.c_str(), b.c_str()) < 0; +#endif } }; - using CaseInsensitiveSet = std::set; + using CaseInsensitiveSet = ::std::set; - class GeneratedFile + class UEGeneratedFile { protected: - std::wstring m_file_base_name; + SystemStringType m_file_base_name; FFilePath m_full_file_path; - std::wstring m_file_contents_buffer; + SystemStringType m_file_contents_buffer; int32_t m_current_indent_count; public: - GeneratedFile(const FFilePath& full_file_path); - virtual ~GeneratedFile() = default; + UEGeneratedFile(const FFilePath& full_file_path); + virtual ~UEGeneratedFile() = default; // Delete copy and move constructors and assignment operator - GeneratedFile(const GeneratedFile&) = delete; - GeneratedFile(GeneratedFile&&) = default; - auto operator=(const GeneratedFile&) -> void = delete; + UEGeneratedFile(const UEGeneratedFile&) = delete; + UEGeneratedFile(UEGeneratedFile&&) = default; + auto operator=(const UEGeneratedFile&) -> void = delete; - auto append_line(const std::wstring& line) -> void; - auto append_line_no_indent(const std::wstring& line) -> void; + auto append_line(const SystemStringType& line) -> void; + auto append_line_no_indent(const SystemStringType& line) -> void; auto begin_indent_level() -> void; auto end_indent_level() -> void; auto serialize_file_content_to_disk() -> bool; virtual auto has_content_to_save() const -> bool; - virtual auto generate_file_contents() -> std::wstring; + virtual auto generate_file_contents() -> SystemStringType; }; - class GeneratedSourceFile : public GeneratedFile + class GeneratedSourceFile : public UEGeneratedFile { private: - std::wstring m_file_module_name; - std::map m_dependencies; - std::set m_extra_includes; - mutable std::set m_dependency_module_names; + SystemStringType m_file_module_name; + ::std::map m_dependencies; + ::std::set m_extra_includes; + mutable ::std::set m_dependency_module_names; UObject* m_object; GeneratedSourceFile* m_header_file; bool m_is_implementation_file; bool m_needs_get_type_hash; public: - std::wstring m_implementation_constructor; - std::unordered_set parent_property_names{}; - std::map> attachments{}; - - GeneratedSourceFile(const FFilePath& file_path, const std::wstring& file_module_name, bool is_implementation_file, UObject* object); + // workaround for clang tuple bug + // https://github.com/llvm/llvm-project/issues/17042 + struct attachment_data + { + SystemStringType property_type; // <0> + SystemStringType attach_string; // <1> + bool access_type; // <2> + + attachment_data(const SystemStringType& property_type, const SystemStringType& attach_string, bool access_type) + : property_type(property_type), attach_string(attach_string), access_type(access_type) + { + } + + attachment_data(const attachment_data& other) = default; + attachment_data(attachment_data&& other) = default; + auto operator=(const attachment_data&) -> attachment_data& = default; + auto operator=(attachment_data&&) -> attachment_data& = default; + }; + SystemStringType m_implementation_constructor; + ::std::unordered_set parent_property_names{}; + ::std::map attachments{}; + + GeneratedSourceFile(const FFilePath& file_path, const SystemStringType& file_module_name, bool is_implementation_file, UObject* object); // Delete copy and move constructors and assignment operator GeneratedSourceFile(const GeneratedSourceFile&) = delete; @@ -153,9 +178,9 @@ namespace RC::UEGenerator auto set_header_file(GeneratedSourceFile* header_file) -> void; auto add_dependency_object(UObject* object, DependencyLevel dependency_level) -> void; - auto add_extra_include(const std::wstring& included_file_name) -> void; + auto add_extra_include(const SystemStringType& included_file_name) -> void; - auto get_header_module_name() const -> const std::wstring& + auto get_header_module_name() const -> const SystemStringType& { return m_file_module_name; } @@ -179,30 +204,30 @@ namespace RC::UEGenerator virtual auto has_content_to_save() const -> bool override; - auto copy_dependency_module_names(std::set& out_dependency_module_names) const -> void + auto copy_dependency_module_names(::std::set& out_dependency_module_names) const -> void { out_dependency_module_names.insert(m_dependency_module_names.begin(), m_dependency_module_names.end()); } auto static create_source_file(const FFilePath& root_dir, - const std::wstring& module_name, - const std::wstring& base_name, + const SystemStringType& module_name, + const SystemStringType& base_name, bool is_implementation_file, UObject* object) -> GeneratedSourceFile; - virtual auto generate_file_contents() -> std::wstring override; + virtual auto generate_file_contents() -> SystemStringType override; protected: auto has_dependency(UObject* object, DependencyLevel dependency_level) -> bool; - auto generate_pre_declarations_string() const -> std::wstring; - auto generate_includes_string() const -> std::wstring; + auto generate_pre_declarations_string() const -> SystemStringType; + auto generate_includes_string() const -> SystemStringType; }; struct UniqueName { static constexpr int32_t HAS_NO_DUPLICATES = 1; - File::StringType name{}; + SystemStringType name{}; int32_t usable_id{HAS_NO_DUPLICATES}; }; @@ -210,26 +235,26 @@ namespace RC::UEGenerator { private: FFilePath m_root_directory; - std::wstring m_primary_module_name; + SystemStringType m_primary_module_name; - std::set m_forced_module_dependencies; - std::set m_ignored_module_names; - std::set m_classes_with_object_initializer; + ::std::set m_forced_module_dependencies; + ::std::set m_ignored_module_names; + ::std::set m_classes_with_object_initializer; - std::unordered_map m_underlying_enum_types; - std::set m_blueprint_visible_enums; - std::set m_blueprint_visible_structs; - std::map>> m_module_dependencies; + ::std::unordered_map m_underlying_enum_types; + ::std::set m_blueprint_visible_enums; + ::std::set m_blueprint_visible_structs; + ::std::map>> m_module_dependencies; - std::vector m_header_files; - std::unordered_set m_structs_that_need_get_type_hash; + ::std::vector m_header_files; + ::std::unordered_set m_structs_that_need_get_type_hash; // Storage to ensure that we don't have duplicate file names - static std::map m_used_file_names; - static std::map m_dependency_object_to_unique_id; + static ::std::map m_used_file_names; + static ::std::map m_dependency_object_to_unique_id; // Storage for class defaultsubojects when populating property initializers - std::unordered_map m_class_subobjects; + ::std::unordered_map m_class_subobjects; public: UEHeaderGenerator(const FFilePath& root_directory); @@ -243,8 +268,8 @@ namespace RC::UEGenerator auto dump_native_packages() -> void; auto generate_object_description_file(UObject* object) -> bool; - auto generate_module_build_file(const std::wstring& module_name) -> void; - auto generate_module_implementation_file(const std::wstring& module_name) -> void; + auto generate_module_build_file(const SystemStringType& module_name) -> void; + auto generate_module_implementation_file(const SystemStringType& module_name) -> void; private: auto generate_interface_definition(UClass* function, GeneratedSourceFile& header_data) -> void; @@ -264,7 +289,7 @@ namespace RC::UEGenerator const CaseInsensitiveSet& blacklisted_property_names, bool generate_as_override = false) -> void; - auto generate_property_value(UStruct* ustruct, FProperty* property, void* object, GeneratedSourceFile& implementation_file, const std::wstring& property_scope) + auto generate_property_value(UStruct* ustruct, FProperty* property, void* object, GeneratedSourceFile& implementation_file, const SystemStringType& property_scope) -> void; auto generate_function_implementation(UClass* uclass, UFunction* function, @@ -272,49 +297,50 @@ namespace RC::UEGenerator bool is_generating_interface, const CaseInsensitiveSet& blacklisted_property_names) -> void; - auto generate_interface_flags(UClass* uinterface) const -> std::wstring; - auto generate_class_flags(UClass* uclass) const -> std::wstring; - auto generate_struct_flags(UScriptStruct* script_struct) const -> std::wstring; - auto generate_enum_flags(UEnum* uenum) const -> std::wstring; - auto generate_property_type_declaration(FProperty* property, const PropertyTypeDeclarationContext& context) -> std::wstring; - auto generate_property_flags(FProperty* property) const -> std::wstring; - auto generate_function_argument_flags(FProperty* property) const -> std::wstring; - auto generate_function_flags(UFunction* function, bool is_function_pure_virtual = false) const -> std::wstring; + auto generate_interface_flags(UClass* uinterface) const -> SystemStringType; + auto generate_class_flags(UClass* uclass) const -> SystemStringType; + auto generate_struct_flags(UScriptStruct* script_struct) const -> SystemStringType; + auto generate_enum_flags(UEnum* uenum) const -> SystemStringType; + auto generate_property_type_declaration(FProperty* property, const PropertyTypeDeclarationContext& context) -> SystemStringType; + auto generate_property_flags(FProperty* property) const -> SystemStringType; + auto generate_function_argument_flags(FProperty* property) const -> SystemStringType; + auto generate_function_flags(UFunction* function, bool is_function_pure_virtual = false) const -> SystemStringType; auto generate_function_parameter_list(UClass* property, UFunction* function, GeneratedSourceFile& header_data, bool generate_comma_before_name, - const std::wstring& context_name, + const SystemStringType& context_name, const CaseInsensitiveSet& blacklisted_property_names, - int32_t* out_num_params = NULL) -> std::wstring; - auto generate_default_property_value(FProperty* property, GeneratedSourceFile& header_data, const std::wstring& ContextName) -> std::wstring; + int32_t* out_num_params = NULL) -> SystemStringType; + auto generate_default_property_value(FProperty* property, GeneratedSourceFile& header_data, const SystemStringType& ContextName) -> SystemStringType; - auto generate_enum_value(UEnum* uenum, int64_t enum_value) -> std::wstring; + auto generate_enum_value(UEnum* uenum, int64_t enum_value) -> SystemStringType; auto generate_simple_assignment_expression(FProperty* property, - const std::wstring& value, + const SystemStringType& value, GeneratedSourceFile& implementation_file, - const std::wstring& property_scope, - const std::wstring& operator_type = STR(" = ")) -> void; + const SystemStringType& property_scope, + const SystemStringType& operator_type = SYSSTR(" = ")) -> void; auto generate_advanced_assignment_expression(FProperty* property, - const std::wstring& value, + const SystemStringType& value, GeneratedSourceFile& implementation_file, - const std::wstring& property_scope, - const std::wstring& property_type, - const std::wstring& operator_type = STR(" = ")) -> void; + const SystemStringType& property_scope, + const SystemStringType& property_type, + const SystemStringType& operator_type = SYSSTR(" = ")) -> void; - auto static generate_parameter_count_string(int32_t parameter_count) -> std::wstring; - auto static determine_primary_game_module_name() -> std::wstring; + auto static generate_parameter_count_string(int32_t parameter_count) -> SystemStringType; + auto static determine_primary_game_module_name() -> SystemStringType; public: - auto add_module_and_sub_module_dependencies(std::set& out_module_dependencies, const std::wstring& module_name, bool add_self_module = true) - -> void; + auto add_module_and_sub_module_dependencies(::std::set& out_module_dependencies, + const SystemStringType& module_name, + bool add_self_module = true) -> void; auto static collect_blacklisted_property_names(UObject* property) -> CaseInsensitiveSet; - auto static generate_object_pre_declaration(UObject* object) -> std::vector>; + auto static generate_object_pre_declaration(UObject* object) -> ::std::vector<::std::vector>; - auto static convert_module_name_to_api_name(const std::wstring& module_name) -> std::wstring; - auto static get_module_name_for_package(UObject* package) -> std::wstring; - auto static sanitize_enumeration_name(const std::wstring& enumeration_name) -> std::wstring; + auto static convert_module_name_to_api_name(const SystemStringType& module_name) -> SystemStringType; + auto static get_module_name_for_package(UObject* package) -> SystemStringType; + auto static sanitize_enumeration_name(const SystemStringType& enumeration_name) -> SystemStringType; auto static get_highest_enum(UEnum* uenum) -> int64_t; auto static get_lowest_enum(UEnum* uenum) -> int64_t; @@ -325,8 +351,8 @@ namespace RC::UEGenerator auto static append_access_modifier(GeneratedSourceFile& header_data, AccessModifier needed_access, AccessModifier& current_access) -> void; auto static get_property_access_modifier(FProperty* property) -> AccessModifier; auto static get_function_access_modifier(UFunction* function) -> AccessModifier; - auto static create_string_literal(const std::wstring& string) -> std::wstring; - auto static get_header_name_for_object(UObject* object, bool get_existing_header = false) -> std::wstring; - auto static generate_cross_module_include(UObject* object, const std::wstring& module_name, const std::wstring& fallback_name) -> std::wstring; + auto static create_string_literal(const SystemStringType& string) -> SystemStringType; + auto static get_header_name_for_object(UObject* object, bool get_existing_header = false) -> SystemStringType; + auto static generate_cross_module_include(UObject* object, const SystemStringType& module_name, const SystemStringType& fallback_name) -> SystemStringType; }; } // namespace RC::UEGenerator diff --git a/UE4SS/include/SettingsManager.hpp b/UE4SS/include/SettingsManager.hpp index 321497282..a90d13711 100644 --- a/UE4SS/include/SettingsManager.hpp +++ b/UE4SS/include/SettingsManager.hpp @@ -5,7 +5,10 @@ #include #include + +#ifdef HAS_UI #include +#endif namespace RC { @@ -14,7 +17,7 @@ namespace RC public: struct SectionOverrides { - File::StringType ModsFolderPath{}; + SystemStringType ModsFolderPath{}; } Overrides; struct SectionGeneral @@ -25,6 +28,7 @@ namespace RC bool EnableDebugKeyBindings{false}; int64_t SecondsToScanBeforeGivingUp{30}; bool UseUObjectArrayCache{true}; + SystemStringType InputSource{SYSSTR("Default")}; } General; struct SectionEngineVersionOverride @@ -61,7 +65,15 @@ namespace RC bool DebugConsoleEnabled{true}; bool DebugConsoleVisible{true}; float DebugGUIFontScaling{1.0}; +#ifdef HAS_UI +#ifdef HAS_GLFW GUI::GfxBackend GraphicsAPI{GUI::GfxBackend::GLFW3_OpenGL3}; +#endif +#ifdef HAS_TUI + GUI::GfxBackend GraphicsAPI{GUI::GfxBackend::TUI}; +#endif +#endif + int64_t LiveViewObjectsPerGroup{64 * 1024 / 2}; } Debug; struct SectionCrashDump @@ -99,6 +111,30 @@ namespace RC bool GUIUFunctionCaller{false}; } Experimental; + struct TUIFeatures + { + /*int ButtonLeft = 1; + int ButtonRight = 3; + int WheelUp = 4; + int WheelDown = 5;*/ + bool TUINerdFont = true; + /* + SystemStringType TerminalCode = "f120"; + SystemStringType ArchiveCode = "f187"; + SystemStringType SyncCode = "f46a"; + SystemStringType FileCode = "f15b"; + SystemStringType EyeCode = "f06e"; + SystemStringType PuzzlePieceCode = "f12e"; + SystemStringType AngleLeftCode = "f100"; + SystemStringType AngleRightCode = "f101"; + SystemStringType BanCode = "f05e"; + SystemStringType CopyCode = "f0c5"; + SystemStringType SearchCode = "f002";*/ + + SystemStringType TERMINFO = SYSSTR("/usr/share/terminfo"); + SystemStringType LCALL = SYSSTR("en_US.UTF-8"); + } TUI; + public: SettingsManager() = default; diff --git a/UE4SS/include/Signatures.hpp b/UE4SS/include/Signatures.hpp index f2951bc79..e5e7f2174 100644 --- a/UE4SS/include/Signatures.hpp +++ b/UE4SS/include/Signatures.hpp @@ -21,7 +21,7 @@ namespace RC using LuaScriptMatchFoundFunc = const std::function; using LuaScriptScanCompleteFunc = const std::function; - auto scan_from_lua_script(std::wstring& script_file_path_and_name, + auto scan_from_lua_script(SystemStringType& script_file_path_and_name, std::vector&, LuaScriptMatchFoundFunc& match_found_func, LuaScriptScanCompleteFunc& scan_complete_func = &scan_complete_default_func) -> void; diff --git a/UE4SS/include/UE4SSProgram.hpp b/UE4SS/include/UE4SSProgram.hpp index 672e92ec6..bacc6dc30 100644 --- a/UE4SS/include/UE4SSProgram.hpp +++ b/UE4SS/include/UE4SSProgram.hpp @@ -7,11 +7,21 @@ #include #include +#ifdef WIN32 #include +#endif #include + +#ifdef HAS_UI +#include #include #include +#endif + +#ifdef HAS_INPUT #include +#endif + #include #include #include @@ -80,13 +90,12 @@ namespace RC class UE4SSProgram : public MProgram { - public: friend class CppUserModBase; // m_input_handler public: - constexpr static wchar_t m_settings_file_name[] = L"UE4SS-settings.ini"; - constexpr static wchar_t m_log_file_name[] = L"UE4SS.log"; - constexpr static wchar_t m_object_dumper_file_name[] = L"UE4SS_ObjectDump.txt"; + constexpr static SystemCharType m_settings_file_name[] = SYSSTR("UE4SS-settings.ini"); + constexpr static SystemCharType m_log_file_name[] = SYSSTR("UE4SS.log"); + constexpr static SystemCharType m_object_dumper_file_name[] = SYSSTR("UE4SS_ObjectDump.txt"); public: RC_UE4SS_API static SettingsManager settings_manager; @@ -96,21 +105,31 @@ namespace RC bool m_is_program_started; protected: - Input::Handler m_input_handler{L"ConsoleWindowClass", L"UnrealWindow"}; +#ifdef HAS_INPUT + Input::Handler m_input_handler; +#endif std::jthread m_event_loop; public: +#ifdef HAS_UI std::jthread m_render_thread; - +#endif private: +#ifdef WIN32 CrashDumper m_crash_dumper{}; - +#endif private: std::filesystem::path m_game_path_and_exe_name; std::filesystem::path m_root_directory; std::filesystem::path m_module_file_path; std::filesystem::path m_working_directory; std::filesystem::path m_mods_directory; + + SystemStringType m_module_file_path_str; + SystemStringType m_working_directory_str; + SystemStringType m_game_executable_str; + SystemStringType m_mods_directory_str; + std::filesystem::path m_game_executable_directory; std::filesystem::path m_log_directory; std::filesystem::path m_object_dumper_output_directory; @@ -119,7 +138,10 @@ namespace RC std::filesystem::path m_legacy_root_directory; Output::DebugConsoleDevice* m_debug_console_device{}; Output::ConsoleDevice* m_console_device{}; + +#ifdef HAS_UI GUI::DebuggingGUI m_debugging_gui{}; +#endif using EventCallable = void (*)(void* data); struct Event @@ -131,6 +153,7 @@ namespace RC std::mutex m_event_queue_mutex{}; private: +#ifdef WIN32 std::unique_ptr m_load_library_a_hook; uint64_t m_hook_trampoline_load_library_a; @@ -142,11 +165,12 @@ namespace RC std::unique_ptr m_load_library_ex_w_hook; uint64_t m_hook_trampoline_load_library_ex_w; +#endif public: - static inline std::vector> m_mods; + std::vector> m_mods; - static inline RecognizableStruct m_shared_functions{}; + RecognizableStruct m_shared_functions{}; static inline UE4SSProgram* s_program{}; @@ -168,19 +192,19 @@ namespace RC }; public: - UE4SSProgram(const std::wstring& ModuleFilePath, std::initializer_list options); + UE4SSProgram(const SystemStringType& ModuleFilePath, std::initializer_list options); ~UE4SSProgram(); UE4SSProgram(const UE4SSProgram&) = delete; UE4SSProgram(UE4SSProgram&&) = delete; private: - auto setup_paths(const std::wstring& moduleFilePath) -> void; + auto setup_paths(const SystemStringType& moduleFilePath) -> void; enum class FunctionStatus { Success, Failure, }; - auto create_emergency_console_for_early_error(File::StringViewType error_message) -> void; + auto create_emergency_console_for_early_error(SystemStringViewType error_message) -> void; auto setup_mod_directory_path() -> void; auto create_simple_console() -> void; auto setup_unreal() -> void; @@ -204,21 +228,28 @@ namespace RC auto fire_unreal_init_for_cpp_mods() -> void; auto fire_ui_init_for_cpp_mods() -> void; auto fire_program_start_for_cpp_mods() -> void; - auto fire_dll_load_for_cpp_mods(std::wstring_view dll_name) -> void; + auto fire_dll_load_for_cpp_mods(SystemStringViewType dll_name) -> void; public: auto init() -> void; auto is_program_started() -> bool; auto reinstall_mods() -> void; - auto get_object_dumper_output_directory() -> const File::StringType; - RC_UE4SS_API auto get_module_directory() -> File::StringViewType; - RC_UE4SS_API auto get_game_executable_directory() -> File::StringViewType; - RC_UE4SS_API auto get_working_directory() -> File::StringViewType; - RC_UE4SS_API auto get_mods_directory() -> File::StringViewType; - RC_UE4SS_API auto get_legacy_root_directory() -> File::StringViewType; + auto get_object_dumper_output_directory() -> const SystemStringType; + RC_UE4SS_API auto get_module_directory() -> SystemStringViewType; + RC_UE4SS_API auto get_working_directory() -> SystemStringViewType; + RC_UE4SS_API auto get_mods_directory() -> SystemStringViewType; + RC_UE4SS_API auto get_legacy_root_directory() -> SystemStringViewType; + RC_UE4SS_API auto get_game_directory() -> SystemStringViewType; RC_UE4SS_API auto generate_uht_compatible_headers() -> void; RC_UE4SS_API auto generate_cxx_headers(const std::filesystem::path& output_dir) -> void; RC_UE4SS_API auto generate_lua_types(const std::filesystem::path& output_dir) -> void; +#ifdef HAS_INPUT + auto get_input_handler() -> Input::Handler& + { + return m_input_handler; + } +#endif +#ifdef HAS_UI auto get_debugging_ui() -> GUI::DebuggingGUI& { return m_debugging_gui; @@ -230,10 +261,13 @@ namespace RC { return ImGui::GetCurrentContext(); } +#ifdef WIN32 RC_UE4SS_API static auto get_current_imgui_allocator_functions(ImGuiMemAllocFunc* alloc_func, ImGuiMemFreeFunc* free_func, void** user_data) -> void { return ImGui::GetAllocatorFunctions(alloc_func, free_func, user_data); } +#endif +#endif RC_UE4SS_API auto queue_event(EventCallable callable, void* data) -> void; RC_UE4SS_API auto is_queue_empty() -> bool; RC_UE4SS_API auto can_process_events() -> bool @@ -242,6 +276,7 @@ namespace RC } public: +#ifdef HAS_INPUT // API pass-through for use outside the private scope of UE4SSProgram RC_UE4SS_API auto register_keydown_event(Input::Key, const Input::EventCallbackCallable&, uint8_t custom_data = 0, void* custom_data2 = nullptr) -> void; RC_UE4SS_API auto register_keydown_event(Input::Key, @@ -251,60 +286,44 @@ namespace RC void* custom_data2 = nullptr) -> void; RC_UE4SS_API auto is_keydown_event_registered(Input::Key) -> bool; RC_UE4SS_API auto is_keydown_event_registered(Input::Key, const Input::Handler::ModifierKeyArray&) -> bool; +#endif private: static auto install_cpp_mods() -> void; static auto install_lua_mods() -> void; using FMBNI_ExtraPredicate = std::function; - static auto find_mod_by_name_internal(std::wstring_view mod_name, + static auto find_mod_by_name_internal(SystemStringViewType mod_name, IsInstalled = IsInstalled::No, IsStarted = IsStarted::No, FMBNI_ExtraPredicate extra_predicate = {}) -> Mod*; public: - RC_UE4SS_API static auto dump_uobject(Unreal::UObject* object, std::unordered_set* dumped_fields, StringType& out_line, bool is_below_425) + RC_UE4SS_API static auto dump_uobject(Unreal::UObject* object, std::unordered_set* dumped_fields, SystemStringType& out_line, bool is_below_425) -> void; - RC_UE4SS_API static auto dump_xproperty(Unreal::FProperty* property, StringType& out_line) -> void; - RC_UE4SS_API static auto dump_all_objects_and_properties(const File::StringType& output_path_and_file_name) -> void; + RC_UE4SS_API static auto dump_xproperty(Unreal::FProperty* property, SystemStringType& out_line) -> void; + RC_UE4SS_API static auto dump_all_objects_and_properties(const SystemStringType& output_path_and_file_name) -> void; template - static auto find_mod_by_name(std::wstring_view mod_name, IsInstalled = IsInstalled::No, IsStarted = IsStarted::No) -> T* + static auto find_mod_by_name(SystemStringType mod_name, IsInstalled is_installed = IsInstalled::No, IsStarted is_started = IsStarted::No) -> T* { - std::abort(); + return static_cast(find_mod_by_name_internal(mod_name, is_installed, is_started, [](auto elem) -> bool { + return dynamic_cast(elem); + })); }; + template - static auto find_mod_by_name(std::string_view mod_name, IsInstalled = IsInstalled::No, IsStarted = IsStarted::No) -> T* + static auto find_mod_by_name(SystemStringViewType mod_name, IsInstalled is_installed = IsInstalled::No, IsStarted is_started = IsStarted::No) -> T* { - std::abort(); + return find_mod_by_name(to_system_string(mod_name), is_installed, is_started); }; - template <> - auto find_mod_by_name(std::wstring_view mod_name, IsInstalled is_installed, IsStarted is_started) -> LuaMod* - { - return static_cast(find_mod_by_name_internal(mod_name, is_installed, is_started, [](auto elem) -> bool { - return dynamic_cast(elem); - })); - } - template <> - auto find_mod_by_name(std::wstring_view mod_name, IsInstalled is_installed, IsStarted is_started) -> CppMod* - { - return static_cast(find_mod_by_name_internal(mod_name, is_installed, is_started, [](auto elem) -> bool { - return dynamic_cast(elem); - })); - } - template <> - auto find_mod_by_name(std::string_view mod_name, IsInstalled is_installed, IsStarted is_started) -> LuaMod* - { - return find_mod_by_name(to_wstring(mod_name), is_installed, is_started); - } - template <> - auto find_mod_by_name(std::string_view mod_name, IsInstalled is_installed, IsStarted is_started) -> CppMod* + + template + RC_UE4SS_API static auto find_lua_mod_by_name(S mod_name, IsInstalled is_installed = IsInstalled::No, IsStarted is_started = IsStarted::No) -> LuaMod* { - return find_mod_by_name(to_wstring(mod_name), is_installed, is_started); + auto mod_name_str = to_system_string(mod_name); + return static_cast(find_mod_by_name(mod_name_str, is_installed, is_started)); } - - RC_UE4SS_API static auto find_lua_mod_by_name(std::wstring_view mod_name, IsInstalled = IsInstalled::No, IsStarted = IsStarted::No) -> LuaMod*; - RC_UE4SS_API static auto find_lua_mod_by_name(std::string_view mod_name, IsInstalled = IsInstalled::No, IsStarted = IsStarted::No) -> LuaMod*; static auto static_cleanup() -> void; RC_UE4SS_API static auto get_program() -> UE4SSProgram& { @@ -312,9 +331,11 @@ namespace RC } private: +#ifdef WIN32 friend void* HookedLoadLibraryA(const char* dll_name); friend void* HookedLoadLibraryExA(const char* dll_name, void* file, int32_t flags); friend void* HookedLoadLibraryW(const wchar_t* dll_name); friend void* HookedLoadLibraryExW(const wchar_t* dll_name, void* file, int32_t flags); +#endif }; } // namespace RC diff --git a/UE4SS/include/USMapGenerator/writer.h b/UE4SS/include/USMapGenerator/writer.h index 3ded027cd..83c1515a2 100644 --- a/UE4SS/include/USMapGenerator/writer.h +++ b/UE4SS/include/USMapGenerator/writer.h @@ -5,6 +5,10 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-variable" +#ifdef LINUX +#define fopen_s(pFile, filename, mode) ((*(pFile)) = fopen((filename), (mode))) == NULL +#endif + class IBufferWriter { public: @@ -49,7 +53,7 @@ class StreamWriter : IBufferWriter FORCEINLINE void Seek(int Pos, int Origin = SEEK_CUR) override { - m_Stream.seekp(Pos, Origin); + m_Stream.seekp((std::streamoff) Pos, (std::ios_base::seekdir) Origin); } uint32_t Size() override diff --git a/UE4SS/proxy_generator/main.cpp b/UE4SS/proxy_generator/main.cpp index 8feee84e8..44701910b 100644 --- a/UE4SS/proxy_generator/main.cpp +++ b/UE4SS/proxy_generator/main.cpp @@ -170,10 +170,10 @@ int _tmain(int argc, TCHAR* argv[]) cpp_file << "void load_original_dll()" << endl; cpp_file << "{" << endl; - cpp_file << " File::CharType path[MAX_PATH];" << endl; + cpp_file << " SystemCharType path[MAX_PATH];" << endl; cpp_file << " GetSystemDirectory(path, MAX_PATH);" << endl; cpp_file << endl; - cpp_file << std::format(" File::StringType dll_path = File::StringType(path) + STR(\"\\\\{}\");", input_dll_name.string()) << endl; + cpp_file << std::format(" SystemStringType dll_path = SystemStringType(path) + SYSSTR(\"\\\\{}\");", input_dll_name.string()) << endl; cpp_file << endl; cpp_file << " SOriginalDll = LoadLibrary(dll_path.c_str());" << endl; cpp_file << " if (!SOriginalDll)" << endl; diff --git a/UE4SS/src/GUI/BPMods.cpp b/UE4SS/src/GUI/BPMods.cpp index b5507c6ba..25428ae20 100644 --- a/UE4SS/src/GUI/BPMods.cpp +++ b/UE4SS/src/GUI/BPMods.cpp @@ -16,6 +16,9 @@ #include +#undef min +#undef max + namespace RC::GUI::BPMods { using namespace RC::Unreal; @@ -113,7 +116,7 @@ namespace RC::GUI::BPMods const auto& mod_button = mod_info.ModButtons[i2]; if (ImGui::Button(std::format("{}", mod_button).c_str())) { - Output::send(STR("Mod button {} hit.\n"), to_wstring(mod_button)); + Output::send(SYSSTR("Mod button {} hit.\n"), mod_button); mod_info.ModActor->ModMenuButtonPressed(static_cast(i2)); } } diff --git a/UE4SS/src/GUI/Console.cpp b/UE4SS/src/GUI/Console.cpp index b0150c9a5..42c02a970 100644 --- a/UE4SS/src/GUI/Console.cpp +++ b/UE4SS/src/GUI/Console.cpp @@ -107,14 +107,14 @@ namespace RC::GUI bool reclaim_focus{}; ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; auto text_edit_callback_wrapper = [](ImGuiInputTextCallbackData* data) -> int { Console* console = - static_cast(data->UserData); Output::send(STR("text_edit_callback_wrapper\n")); + static_cast(data->UserData); Output::send(SYSSTR("text_edit_callback_wrapper\n")); //return console->text_edit_callback(data); return 0; }; ImGui::PushItemWidth(-12.0f); if (ImGui::InputText("##console_input_buffer", m_input_buffer, IM_ARRAYSIZE(m_input_buffer), input_text_flags, text_edit_callback_wrapper, this)) { - Output::send(STR("ConsoleInput\n")); + Output::send(SYSSTR("ConsoleInput\n")); reclaim_focus = true; } ImGui::PopItemWidth(); @@ -130,15 +130,15 @@ namespace RC::GUI /**/ std::lock_guard guard(m_lines_mutex); - m_text_editor.Render("TextEditor", {-16.0f, -31.0f + -8.0f}); - + m_text_editor.Render("TextEditor", {-16.0f / XDIV, NEGATIVE_MARGIN(-31.0f + -8.0f)}); + ImGui_AutoScroll("TextEditor", &m_previous_max_scroll_y); //*/ } auto Console::render_search_box() -> void { - m_filter.Draw("Search log", 200); + m_filter.Draw("Search log", 200 / XDIV); } static auto LogLevel_to_ImColor(Color::Color color) -> std::pair diff --git a/UE4SS/src/GUI/ConsoleOutputDevice.cpp b/UE4SS/src/GUI/ConsoleOutputDevice.cpp index 7e0ac8e67..dd6ee6145 100644 --- a/UE4SS/src/GUI/ConsoleOutputDevice.cpp +++ b/UE4SS/src/GUI/ConsoleOutputDevice.cpp @@ -11,17 +11,17 @@ namespace RC::Output return true; } - auto ConsoleDevice::receive(File::StringViewType fmt) const -> void + auto ConsoleDevice::receive(SystemStringViewType fmt) const -> void { receive_with_optional_arg(fmt, Color::NoColor); } - auto ConsoleDevice::receive_with_optional_arg(File::StringViewType fmt, [[maybe_unused]] int32_t optional_arg) const -> void + auto ConsoleDevice::receive_with_optional_arg(SystemStringViewType fmt, [[maybe_unused]] int32_t optional_arg) const -> void { #if ENABLE_OUTPUT_DEVICE_DEBUG_MODE - printf_s("ConsoleDevice received: %S", m_formatter(fmt).c_str()); + printf_s("ConsoleDevice received: " SystemStringPrint, m_formatter(fmt).c_str()); #else - auto fmt_copy = File::StringType{fmt}; + auto fmt_copy = SystemStringType{fmt}; if (fmt_copy.ends_with(STR('\n'))) { fmt_copy.pop_back(); diff --git a/UE4SS/src/GUI/Dumpers.cpp b/UE4SS/src/GUI/Dumpers.cpp index a2ddc0028..655cd4426 100644 --- a/UE4SS/src/GUI/Dumpers.cpp +++ b/UE4SS/src/GUI/Dumpers.cpp @@ -25,6 +25,9 @@ #include #include +#undef min +#undef max + namespace RC::GUI::Dumpers { using namespace ::RC::Unreal; @@ -79,9 +82,9 @@ namespace RC::GUI::Dumpers FMeshUVChannelInfo UVChannelData; }; - auto generate_root_component_csv(UObject* root_component) -> StringType + auto generate_root_component_csv(UObject* root_component) -> SystemStringType { - StringType root_actor_buffer{}; + SystemStringType root_actor_buffer{}; static auto location_property = root_component->GetPropertyByNameInChain(STR("RelativeLocation")); static auto rotation_property = root_component->GetPropertyByNameInChain(STR("RelativeRotation")); @@ -90,26 +93,26 @@ namespace RC::GUI::Dumpers auto location = root_component->GetValuePtrByPropertyNameInChain(STR("RelativeLocation")); FString location_string{}; location_property->ExportTextItem(location_string, location, nullptr, nullptr, 0); - root_actor_buffer.append(std::format(STR("\"{}\","), location_string.GetCharArray())); + root_actor_buffer.append(std::format(SYSSTR("\"{}\","), to_system(location_string.GetCharArray()))); auto rotation = root_component->GetValuePtrByPropertyNameInChain(STR("RelativeRotation")); FString rotation_string{}; rotation_property->ExportTextItem(rotation_string, rotation, nullptr, nullptr, 0); - root_actor_buffer.append(std::format(STR("\"{}\","), rotation_string.GetCharArray())); + root_actor_buffer.append(std::format(SYSSTR("\"{}\","), to_system(rotation_string.GetCharArray()))); auto scale = root_component->GetValuePtrByPropertyNameInChain(STR("RelativeScale3D")); FString scale_string{}; scale_property->ExportTextItem(scale_string, scale, nullptr, nullptr, 0); - root_actor_buffer.append(std::format(STR("\"{}\","), scale_string.GetCharArray())); + root_actor_buffer.append(std::format(SYSSTR("\"{}\","), to_system(scale_string.GetCharArray()))); return root_actor_buffer; } - static auto generate_actors_csv_file(UClass* dump_actor_class) -> StringType + static auto generate_actors_csv_file(UClass* dump_actor_class) -> SystemStringType { - StringType file_buffer{}; + SystemStringType file_buffer{}; - file_buffer.append(STR("---,Actor,Location,Rotation,Scale,Meshes\n")); + file_buffer.append(SYSSTR("---,Actor,Location,Rotation,Scale,Meshes\n")); size_t actor_count{}; FindObjectSearcher(dump_actor_class, AnySuperStruct::StaticClass()).ForEach([&](UObject* object) { @@ -126,30 +129,30 @@ namespace RC::GUI::Dumpers return LoopAction::Continue; } - StringType actor_buffer{}; + SystemStringType actor_buffer{}; - actor_buffer.append(std::format(STR("Row_{},"), actor_count)); + actor_buffer.append(std::format(SYSSTR("Row_{},"), actor_count)); static auto game_mode_base = UObjectGlobals::FindFirstOf(STR("GameModeBase")); static auto class_property = game_mode_base->GetPropertyByNameInChain(STR("GameStateClass")); FString actor_class_string{}; class_property->ExportTextItem(actor_class_string, &actor->GetClassPrivate(), nullptr, nullptr, 0); - actor_buffer.append(std::format(STR("{},"), actor_class_string.GetCharArray())); + actor_buffer.append(std::format(SYSSTR("{},"), to_system(actor_class_string.GetCharArray()))); // TODO: build system to handle other types of components - possibly including a way to specify which components to dump and which properties are important via a config file actor_buffer.append(generate_root_component_csv(*root_component)); - actor_buffer.append(STR("\"")); + actor_buffer.append(SYSSTR("\"")); static auto static_mesh_component_class = UObjectGlobals::StaticFindObject(nullptr, nullptr, STR("/Script/Engine.StaticMeshComponent")); const auto& static_mesh_components = actor->K2_GetComponentsByClass(static_mesh_component_class); if (static_mesh_components.Num() > 0) { - actor_buffer.append(STR("(")); + actor_buffer.append(SYSSTR("(")); for (auto [static_mesh_component_ptr, static_mesh_component_index] : static_mesh_components | views::enumerate) { const auto mesh = *static_mesh_component_ptr->GetValuePtrByPropertyNameInChain(STR("StaticMesh")); if (!mesh) { - Output::send(STR("SKIPPING COMPONENT! StaticMeshComponent '{}' has no mesh.\n"), + Output::send(SYSSTR("SKIPPING COMPONENT! StaticMeshComponent '{}' has no mesh.\n"), static_mesh_component_ptr->GetOuterPrivate()->GetName()); continue; } @@ -157,7 +160,7 @@ namespace RC::GUI::Dumpers static auto mesh_property = static_mesh_component_ptr->GetPropertyByNameInChain(STR("StaticMesh")); FString mesh_string{}; mesh_property->ExportTextItem(mesh_string, &mesh, nullptr, nullptr, 0); - actor_buffer.append(std::format(STR("(StaticMesh={}',"), mesh_string.GetCharArray())); + actor_buffer.append(std::format(SYSSTR("(StaticMesh={}',"), to_system(mesh_string.GetCharArray()))); auto materials_for_each_body = [&](const UObject* material_interface) { if (material_interface) @@ -166,8 +169,9 @@ namespace RC::GUI::Dumpers const auto material_type_space_location = material_full_name.find(STR(" ")); if (material_type_space_location == material_full_name.npos) { - Output::send(STR("SKIPPING MATERIAL! Was unable to find space in full material name in component: '{}'.\n"), - material_full_name); + Output::send( + SYSSTR("SKIPPING MATERIAL! Was unable to find space in full material name in component: '{}'.\n"), + material_full_name); return; } @@ -175,12 +179,12 @@ namespace RC::GUI::Dumpers { throw std::runtime_error{"integer overflow when converting material_type_space_location signed\n"}; } - auto material_typeless_name = StringViewType{material_full_name.begin() + static_cast(material_type_space_location) + 1, - material_full_name.end()}; + auto material_typeless_name = UEStringViewType{material_full_name.begin() + static_cast(material_type_space_location) + 1, + material_full_name.end()}; - actor_buffer.append(std::format(STR("{}'"), material_interface->GetClassPrivate()->GetName())); - actor_buffer.append(std::format(STR("\"\"{}"), material_typeless_name)); - actor_buffer.append(STR("\"\"'")); + actor_buffer.append(std::format(SYSSTR("{}'"), to_system(material_interface->GetClassPrivate()->GetName()))); + actor_buffer.append(std::format(SYSSTR("\"\"{}"), to_system(material_typeless_name))); + actor_buffer.append(SYSSTR("\"\"'")); } }; @@ -189,18 +193,18 @@ namespace RC::GUI::Dumpers const auto materials = *mesh->GetValuePtrByPropertyName>(STR("StaticMaterials")); if (materials.GetData()) { - actor_buffer.append(STR("Materials=(")); + actor_buffer.append(SYSSTR("Materials=(")); } for (auto [material, material_index] : materials | views::enumerate) { materials_for_each_body(material.MaterialInterface); if (material_index + 1 < materials.Num()) { - actor_buffer.append(STR(",")); + actor_buffer.append(SYSSTR(",")); } else { - actor_buffer.append(STR(")")); + actor_buffer.append(SYSSTR(")")); } } } @@ -209,31 +213,31 @@ namespace RC::GUI::Dumpers const auto& materials = *mesh->GetValuePtrByPropertyName>(STR("StaticMaterials")); if (materials.GetData()) { - actor_buffer.append(STR("Materials=(")); + actor_buffer.append(SYSSTR("Materials=(")); } for (auto [material, material_index] : materials | views::enumerate) { materials_for_each_body(material.MaterialInterface); if (material_index + 1 < materials.Num()) { - actor_buffer.append(STR(",")); + actor_buffer.append(SYSSTR(",")); } else { - actor_buffer.append(STR(")")); + actor_buffer.append(SYSSTR(")")); } } } - actor_buffer.append(STR(")")); + actor_buffer.append(SYSSTR(")")); if (static_mesh_component_index + 1 < static_mesh_components.Num()) { - actor_buffer.append(STR(",")); + actor_buffer.append(SYSSTR(",")); } } - actor_buffer.append(STR(")")); + actor_buffer.append(SYSSTR(")")); } - actor_buffer.append(STR("\"\n")); + actor_buffer.append(SYSSTR("\"\n")); file_buffer.append(actor_buffer); ++actor_count; @@ -243,7 +247,7 @@ namespace RC::GUI::Dumpers return file_buffer; } - static auto generate_actors_json_file(UClass* class_to_dump) -> StringType + static auto generate_actors_json_file(UClass* class_to_dump) -> SystemStringType { auto global_json_array = JSON::Array{}; @@ -264,37 +268,37 @@ namespace RC::GUI::Dumpers auto& actor_json_object = global_json_array.new_object(); - actor_json_object.new_string(STR("Name"), std::format(STR("Row_{}"), actor_count)); + actor_json_object.new_string(SYSSTR("Name"), std::format(SYSSTR("Row_{}"), actor_count)); static auto game_mode_base = UObjectGlobals::FindFirstOf(STR("GameModeBase")); static auto class_property = game_mode_base->GetPropertyByNameInChain(STR("GameStateClass")); FString actor_class_string{}; class_property->ExportTextItem(actor_class_string, &actor->GetClassPrivate(), nullptr, nullptr, 0); - actor_json_object.new_string(STR("Actor"), std::format(STR("{}"), StringViewType{actor_class_string.GetCharArray()})); + actor_json_object.new_string(SYSSTR("Actor"), std::format(SYSSTR("{}"), to_system(actor_class_string.GetCharArray()))); - auto& root_component_json_object = actor_json_object.new_object(STR("RootComponent")); + auto& root_component_json_object = actor_json_object.new_object(SYSSTR("RootComponent")); FString root_component_class_string{}; class_property->ExportTextItem(root_component_class_string, &(*root_component)->GetClassPrivate(), nullptr, nullptr, 0); - root_component_json_object.new_string(STR("SceneComponentClass"), std::format(STR("{}"), StringViewType{root_component_class_string.GetCharArray()})); + root_component_json_object.new_string(SYSSTR("SceneComponentClass"), std::format(SYSSTR("{}"), to_system(root_component_class_string.GetCharArray()))); - auto& location_json_object = root_component_json_object.new_object(STR("Location")); + auto& location_json_object = root_component_json_object.new_object(SYSSTR("Location")); auto location = (*root_component)->GetValuePtrByPropertyNameInChain(STR("RelativeLocation")); - location_json_object.new_number(STR("X"), location->X()); - location_json_object.new_number(STR("Y"), location->Y()); - location_json_object.new_number(STR("Z"), location->Z()); + location_json_object.new_number(SYSSTR("X"), location->X()); + location_json_object.new_number(SYSSTR("Y"), location->Y()); + location_json_object.new_number(SYSSTR("Z"), location->Z()); - auto& rotation_json_object = root_component_json_object.new_object(STR("Rotation")); + auto& rotation_json_object = root_component_json_object.new_object(SYSSTR("Rotation")); auto rotation = (*root_component)->GetValuePtrByPropertyNameInChain(STR("RelativeRotation")); - rotation_json_object.new_number(STR("Pitch"), rotation->GetPitch()); - rotation_json_object.new_number(STR("Yaw"), rotation->GetYaw()); - rotation_json_object.new_number(STR("Roll"), rotation->GetRoll()); + rotation_json_object.new_number(SYSSTR("Pitch"), rotation->GetPitch()); + rotation_json_object.new_number(SYSSTR("Yaw"), rotation->GetYaw()); + rotation_json_object.new_number(SYSSTR("Roll"), rotation->GetRoll()); - auto& scale_json_object = root_component_json_object.new_object(STR("Scale")); + auto& scale_json_object = root_component_json_object.new_object(SYSSTR("Scale")); auto scale = (*root_component)->GetValuePtrByPropertyNameInChain(STR("RelativeScale3D")); - scale_json_object.new_number(STR("X"), scale->X()); - scale_json_object.new_number(STR("Y"), scale->Y()); - scale_json_object.new_number(STR("Z"), scale->Z()); + scale_json_object.new_number(SYSSTR("X"), scale->X()); + scale_json_object.new_number(SYSSTR("Y"), scale->Y()); + scale_json_object.new_number(SYSSTR("Z"), scale->Z()); ++actor_count; return LoopAction::Continue; @@ -308,30 +312,27 @@ namespace RC::GUI::Dumpers void call_generate_static_mesh_file() { - Output::send(STR("Dumping CSV of all loaded static mesh actors, positions and mesh properties\n")); + Output::send(SYSSTR("Dumping CSV of all loaded static mesh actors, positions and mesh properties\n")); static auto dump_actor_class = UObjectGlobals::StaticFindObject(nullptr, nullptr, STR("/Script/Engine.StaticMeshActor")); - std::wstring file_buffer{}; + SystemStringType file_buffer{}; file_buffer.append(generate_actors_csv_file(dump_actor_class)); - auto file = - File::open(std::format(STR("{}\\{}-ue4ss_static_mesh_data.csv"), UE4SSProgram::get_program().get_working_directory(), long(std::time(nullptr))), - File::OpenFor::Writing, - File::OverwriteExistingFile::Yes, - File::CreateIfNonExistent::Yes); - file.write_string_to_file(file_buffer); - Output::send(STR("Finished dumping CSV of all loaded static mesh actors, positions and mesh properties\n")); + auto path = std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / + std::format(SYSSTR("{}-ue4ss_static_mesh_data.csv"), long(std::time(nullptr))); + auto file = File::open(path, File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); + file.write_file_string_to_file(to_file(file_buffer)); + Output::send(SYSSTR("Finished dumping CSV of all loaded static mesh actors, positions and mesh properties\n")); } void call_generate_all_actor_file() { - Output::send(STR("Dumping CSV of all loaded actor types, positions and mesh properties\n")); - std::wstring file_buffer{}; + Output::send(SYSSTR("Dumping CSV of all loaded actor types, positions and mesh properties\n")); + SystemStringType file_buffer{}; + auto path = std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / + std::format(SYSSTR("{}-ue4ss_actor_data.csv"), long(std::time(nullptr))); file_buffer.append(generate_actors_csv_file(AActor::StaticClass())); - auto file = File::open(std::format(STR("{}\\{}-ue4ss_actor_data.csv"), UE4SSProgram::get_program().get_working_directory(), long(std::time(nullptr))), - File::OpenFor::Writing, - File::OverwriteExistingFile::Yes, - File::CreateIfNonExistent::Yes); - file.write_string_to_file(file_buffer); - Output::send(STR("Finished dumping CSV of all loaded actor types, positions and mesh properties\n")); + auto file = File::open(path, File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); + file.write_file_string_to_file(to_file(file_buffer)); + Output::send(SYSSTR("Finished dumping CSV of all loaded actor types, positions and mesh properties\n")); } auto render() -> void @@ -341,6 +342,13 @@ namespace RC::GUI::Dumpers return; } +// this give the button a little bit of space between the top of the window +// and the buttons themselves +#ifdef LINUX + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {0.0f, 1.0f}); + ImGui::Spacing(); +#endif + if (ImGui::Button("Dump all static actor meshes to file")) { @@ -395,14 +403,18 @@ namespace RC::GUI::Dumpers if (ImGui::Button("Dump CXX Headers\n")) { - File::StringType working_dir{UE4SSProgram::get_program().get_working_directory()}; - UE4SSProgram::get_program().generate_cxx_headers(working_dir + STR("\\CXXHeaderDump")); + std::filesystem::path working_dir{UE4SSProgram::get_program().get_working_directory()}; + UE4SSProgram::get_program().generate_cxx_headers(working_dir / SYSSTR("CXXHeaderDump")); } if (ImGui::Button("Generate Lua Types\n")) { - File::StringType working_dir{UE4SSProgram::get_program().get_working_directory()}; - UE4SSProgram::get_program().generate_lua_types(working_dir + STR("\\Mods\\shared\\types")); + std::filesystem::path working_dir{UE4SSProgram::get_program().get_working_directory()}; + UE4SSProgram::get_program().generate_lua_types(working_dir / SYSSTR("Mods") / SYSSTR("shared") SYSSTR("types")); } + +#ifdef LINUX + ImGui::PopStyleVar(); +#endif } } // namespace RC::GUI::Dumpers diff --git a/UE4SS/src/GUI/GUI.cpp b/UE4SS/src/GUI/GUI.cpp index e770705c0..df201e8fb 100644 --- a/UE4SS/src/GUI/GUI.cpp +++ b/UE4SS/src/GUI/GUI.cpp @@ -6,10 +6,24 @@ #include #include #include -#include #include -#include + +#ifdef WIN32 #include +#ifdef HAS_D3D11 +#include +#endif +#else +#define sscanf_s sscanf +#endif + +#ifdef HAS_GLFW +#include +#endif + +#ifdef HAS_TUI +#include +#endif #include #include @@ -17,12 +31,17 @@ #undef TEXT #endif +#ifdef HAS_GUI #include "Roboto.hpp" #include "FaSolid900.hpp" -#include #include -#include +#elif defined(HAS_TUI) +#include +#endif + +#include +#include namespace RC::GUI { ImColor g_imgui_bg_color{0.22f, 0.22f, 0.22f, 1.00f}; @@ -43,6 +62,15 @@ namespace RC::GUI ImColor g_imgui_text_blue_color = ImColor{135, 195, 250, 255}; ImColor g_imgui_text_purple_color = ImColor{170, 145, 255, 255}; + BackendProperty g_backend_properties = { + .x_offset_0 = -14.0f, + .x_offset_1 = -16.0f, + .xdiv = 1.0f, + .ydiv = 1.0f, + .separator_height = 4.0f, + .quirk_tui = false, + }; + std::vector DebuggingGUI::s_end_of_frame_callbacks{}; auto DebuggingGUI::is_valid() -> bool @@ -76,7 +104,7 @@ namespace RC::GUI if (ImGui::BeginTabBar("##MainTabBar", ImGuiTabBarFlags_None)) { - if (ImGui::BeginTabItem(ICON_FA_TERMINAL " Console")) + if (ImGui::BeginTabItem(ATTACH_ICON(ICON_FA_TERMINAL, " Console"))) { get_console().render_search_box(); @@ -88,13 +116,14 @@ namespace RC::GUI { ImGui::BeginDisabled(true); } - if (ImGui::Button(ICON_FA_ARCHIVE " Dump Objects & Properties")) + if (ImGui::Button(ATTACH_ICON(ICON_FA_ARCHIVE, " Dump Objects & Properties"))) { m_event_thread_busy = true; UE4SSProgram::get_program().queue_event( [](void* data) { - UE4SSProgram::dump_all_objects_and_properties(UE4SSProgram::get_program().get_object_dumper_output_directory() + STR("\\") + - UE4SSProgram::m_object_dumper_file_name); + UE4SSProgram::dump_all_objects_and_properties( + to_system(std::filesystem::path{UE4SSProgram::get_program().get_object_dumper_output_directory()} / + UE4SSProgram::m_object_dumper_file_name)); static_cast(data)->m_event_thread_busy = false; }, this); @@ -109,7 +138,7 @@ namespace RC::GUI ImGui::BeginDisabled(true); } ImGui::SameLine(); - if (ImGui::Button(ICON_FA_SYNC " Restart All Mods")) + if (ImGui::Button(ATTACH_ICON(ICON_FA_SYNC, " Restart All Mods"))) { m_event_thread_busy = true; UE4SSProgram::get_program().queue_event( @@ -137,7 +166,7 @@ namespace RC::GUI { ImGui::BeginDisabled(true); } - if (ImGui::BeginTabItem(ICON_FA_FILE_ALT " Live View")) + if (ImGui::BeginTabItem(ATTACH_ICON(ICON_FA_FILE_ALT, " Live View"))) { listeners_are_required = true; m_live_view.set_listeners(); @@ -149,7 +178,7 @@ namespace RC::GUI should_unset_listeners = true; } - if (ImGui::BeginTabItem(ICON_FA_EYE " Watches")) + if (ImGui::BeginTabItem(ATTACH_ICON(ICON_FA_EYE, " Watches"))) { listeners_are_required = true; m_live_view.render_watches(); @@ -165,13 +194,13 @@ namespace RC::GUI m_live_view.unset_listeners(); } - if (ImGui::BeginTabItem(ICON_FA_ARCHIVE " Dumpers")) + if (ImGui::BeginTabItem(ATTACH_ICON(ICON_FA_ARCHIVE, " Dumpers"))) { Dumpers::render(); ImGui::EndTabItem(); } - if (ImGui::BeginTabItem(ICON_FA_PUZZLE_PIECE " BP Mods")) + if (ImGui::BeginTabItem(ATTACH_ICON(ICON_FA_PUZZLE_PIECE, " BP Mods"))) { BPMods::render(); ImGui::EndTabItem(); @@ -210,7 +239,11 @@ namespace RC::GUI ImGuiStyle& style = ImGui::GetStyle(); style.WindowPadding = ImVec2(8, 8); +#ifdef HAS_GUI style.FramePadding = ImVec2(12, 5); +#else + style.FramePadding = ImVec2(0.5f, 0.5f); +#endif style.CellPadding = ImVec2(3, 3); style.ItemSpacing = ImVec2(8, 4); style.ItemInnerSpacing = ImVec2(4, 4); @@ -264,8 +297,13 @@ namespace RC::GUI style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.55f, 0.22f, 0.45f, 1.00f); style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.63f, 0.24f, 0.50f, 1.00f); style.Colors[ImGuiCol_Button] = ImVec4(0.51f, 0.23f, 0.42f, 1.00f); +#ifdef HAS_GUI style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.59f, 0.22f, 0.48f, 1.00f); style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.63f, 0.24f, 0.50f, 1.00f); +#else + style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.4f, 0.5f, 0.8f, 1.00f); + style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.65f, 0.4f, 0.50f, 1.00f); +#endif style.Colors[ImGuiCol_Header] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.24f, 0.24f, 0.24f, 1.00f); @@ -276,8 +314,13 @@ namespace RC::GUI style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.92f, 0.24f, 0.84f, 0.67f); style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.92f, 0.24f, 0.84f, 0.95f); style.Colors[ImGuiCol_Tab] = ImVec4(0.51f, 0.23f, 0.42f, 1.00f); +#ifdef HAS_GUI style.Colors[ImGuiCol_TabHovered] = ImVec4(0.59f, 0.22f, 0.48f, 1.00f); style.Colors[ImGuiCol_TabActive] = ImVec4(0.63f, 0.24f, 0.50f, 1.00f); +#else + style.Colors[ImGuiCol_TabHovered] = ImVec4(0.4f, 0.5f, 0.8f, 1.00f); + style.Colors[ImGuiCol_TabActive] = ImVec4(0.65f, 0.4f, 0.50f, 1.00f); +#endif style.Colors[ImGuiCol_TabUnfocused] = ImVec4(0.07f, 0.10f, 0.15f, 0.97f); style.Colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.26f, 0.42f, 1.00f); style.Colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); @@ -333,16 +376,21 @@ namespace RC::GUI { if (!Output::has_internal_error()) { - Output::send(STR("Error: {}\n"), to_wstring(e.what())); + Output::send(SYSSTR("Error: {}\n"), to_system(e.what())); } else { - printf_s("Internal Error: %s\n", e.what()); + fprintf(stderr, "Internal Error: %s\n", e.what()); + fflush(stderr); } - // You're not allowed to throw exceptions directly inside a frame! - // Use GUI::TRY to move exceptions to the end of the frame. - abort(); +// You're not allowed to throw exceptions directly inside a frame! +// Use GUI::TRY to move exceptions to the end of the frame. +#ifdef WIN32 + __debugbreak(); +#else + asm("int3"); +#endif } ImGui::Render(); @@ -458,10 +506,11 @@ namespace RC::GUI IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - m_imgui_ini_file = to_string(StringType{UE4SSProgram::get_program().get_working_directory()} + STR("\\imgui.ini")); + m_imgui_ini_file = to_string_path(std::filesystem::path {UE4SSProgram::get_program().get_working_directory()} / SYSSTR("imgui.ini")); io.IniFilename = m_imgui_ini_file.c_str(); // Add .ini handle for UserData type + #ifdef WIN32 ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "UE4SSData"; ini_handler.TypeHash = ImHashStr("UE4SSData"); @@ -469,6 +518,7 @@ namespace RC::GUI ini_handler.ReadLineFn = imgui_ue4ss_data_read_line; ini_handler.WriteAllFn = imgui_ue4ss_data_write_all; ImGui::AddSettingsHandler(&ini_handler); + #endif ImGui::LoadIniSettingsFromDisk(m_imgui_ini_file.c_str()); @@ -481,7 +531,7 @@ namespace RC::GUI io.Fonts->Clear(); float base_font_size = 14 * UE4SSProgram::settings_manager.Debug.DebugGUIFontScaling; - +#ifdef HAS_GUI ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; // if true it will try to free memory and fail io.Fonts->AddFontFromMemoryTTF(Roboto, sizeof(Roboto), base_font_size, &font_cfg); @@ -494,7 +544,7 @@ namespace RC::GUI icons_cfg.PixelSnapH = true; icons_cfg.GlyphMinAdvanceX = icon_font_size; io.Fonts->AddFontFromMemoryTTF(FaSolid900, sizeof(FaSolid900), icon_font_size, &icons_cfg, icons_ranges); - +#endif m_os_backend->init(); m_gfx_backend->init(); @@ -510,8 +560,10 @@ namespace RC::GUI auto DebuggingGUI::set_gfx_backend(GfxBackend backend) -> void { + Output::send(SYSSTR("Setting gfx backend!\n")); switch (backend) { +#ifdef HAS_D3D11 case GfxBackend::DX11: m_gfx_backend = std::make_unique(); m_os_backend = std::make_unique(); @@ -520,12 +572,24 @@ namespace RC::GUI m_gfx_backend = std::make_unique(); m_os_backend = std::make_unique(); break; +#else +#ifdef HAS_GLFW + case GfxBackend::GLFW3_OpenGL3: + m_gfx_backend = std::make_unique(); + m_os_backend = std::make_unique(); + break; +#elif defined(HAS_TUI) + case GfxBackend::TUI: + m_gfx_backend = std::make_unique(); + m_os_backend = std::make_unique(); +#endif +#endif } - m_gfx_backend->set_os_backend(m_os_backend.get()); m_os_backend->set_gfx_backend(m_gfx_backend.get()); m_gfx_backend->on_os_backend_set(); m_os_backend->on_gfx_backend_set(); + m_gfx_backend->set_backend_properties(g_backend_properties); } auto DebuggingGUI::add_tab(std::shared_ptr tab) -> void @@ -550,7 +614,7 @@ namespace RC::GUI if (!debugging_ui) { - Output::send(STR("Could not start GUI render thread because 'debugging_ui' was nullptr.")); + Output::send(SYSSTR("Could not start GUI render thread because 'debugging_ui' was nullptr.")); return; } debugging_ui->setup(std::move(stop_token)); diff --git a/UE4SS/src/GUI/ImGuiUtility.cpp b/UE4SS/src/GUI/ImGuiUtility.cpp index 858516299..ec08cdac0 100644 --- a/UE4SS/src/GUI/ImGuiUtility.cpp +++ b/UE4SS/src/GUI/ImGuiUtility.cpp @@ -14,8 +14,13 @@ namespace RC::GUI // NOTE: This function makes use of 'imgui_internal.h'. // As a result, this function is prone to break if you update Imgui. ImGuiContext& g = *GImGui; +#ifdef WIN32 const char* child_window_name = NULL; ImFormatStringToTempBuffer(&child_window_name, NULL, "%s/%s_%08X", g.CurrentWindow->Name, label, ImGui::GetID(label)); +#else + static char child_window_name[1024]; + sprintf(child_window_name, "%s/%s_%08X", g.CurrentWindow->Name, label, ImGui::GetID(label)); +#endif ImGuiWindow* child_window = ImGui::FindWindowByName(child_window_name); if (child_window) { @@ -98,12 +103,12 @@ namespace RC::GUI static auto dump_json_integer_value(const JSON::Number& element) -> void; static int indent_level{}; - static auto indent() -> std::wstring + static auto indent() -> SystemStringType { - std::wstring indents{}; + SystemStringType indents{}; for (int i = 0; i < indent_level; ++i) { - indents.append(STR(" ")); + indents.append(SYSSTR(" ")); } return indents; } @@ -150,7 +155,7 @@ namespace RC::GUI static auto dump_json_object_pair(const JSON::TypedKeyValuePair element) -> void { - Output::send(STR("{}Object: {}\n"), indent(), element.key); + Output::send(SYSSTR("{}Object: {}\n"), indent(), element.key); const auto& object = element.value->get(); for (const auto& [_, inner_element] : object) @@ -163,7 +168,7 @@ namespace RC::GUI static auto dump_json_array_pair(const JSON::TypedKeyValuePair element) -> void { - Output::send(STR("{}Array: {}\n"), indent(), element.key); + Output::send(SYSSTR("{}Array: {}\n"), indent(), element.key); const auto& array = element.value->get(); for (const auto& inner_element : array) @@ -176,18 +181,18 @@ namespace RC::GUI static auto dump_json_string_pair(const JSON::TypedKeyValuePair element) -> void { - Output::send(STR("{}String: {} == {}\n"), indent(), element.key, element.value->get_view()); + Output::send(SYSSTR("{}String: {} == {}\n"), indent(), element.key, element.value->get_view()); } static auto dump_json_integer_pair(const JSON::TypedKeyValuePair element) -> void { - Output::send(STR("{}Integer: {} == {}\n"), indent(), element.key, element.value->get()); + Output::send(SYSSTR("{}Integer: {} == {}\n"), indent(), element.key, element.value->get()); } static auto dump_json_object_value(const JSON::Object& element) -> void { ++indent_level; - Output::send(STR("{}Object \n"), indent()); + Output::send(SYSSTR("{}Object \n"), indent()); const auto& object = element.get(); for (const auto& [_, inner_element] : object) @@ -203,7 +208,7 @@ namespace RC::GUI static auto dump_json_array_value(const JSON::Array& element) -> void { ++indent_level; - Output::send(STR("{}Array \n"), indent()); + Output::send(SYSSTR("{}Array \n"), indent()); const auto& array = element.get(); for (const auto& inner_element : array) @@ -218,11 +223,11 @@ namespace RC::GUI static auto dump_json_string_value(const JSON::String& element) -> void { - Output::send(STR("{}String: {}\n"), indent(), element.get_view()); + Output::send(SYSSTR("{}String: {}\n"), indent(), element.get_view()); } static auto dump_json_integer_value(const JSON::Number& element) -> void { - Output::send(STR("{}Integer: {}\n"), indent(), element.get()); + Output::send(SYSSTR("{}Integer: {}\n"), indent(), element.get()); } } // namespace RC::GUI diff --git a/UE4SS/src/GUI/LiveView.1.PVS-Studio.cfg b/UE4SS/src/GUI/LiveView.1.PVS-Studio.cfg deleted file mode 100644 index 5df1eca60..000000000 --- a/UE4SS/src/GUI/LiveView.1.PVS-Studio.cfg +++ /dev/null @@ -1,40 +0,0 @@ -analysis-mode=12 -disable-ms-extensions=yes -exclude-path=*\boost\* -exclude-path=*\zlib\* -exclude-path=*\png\* -exclude-path=*\libpng\* -exclude-path=*\pnglib\* -exclude-path=*\freetype\* -exclude-path=*\ImageMagick\* -exclude-path=*\jpeglib\* -exclude-path=*\libxml\* -exclude-path=*\libxslt\* -exclude-path=*\tifflib\* -exclude-path=*\wxWidgets\* -exclude-path=*\libtiff\* -exclude-path=*\mesa\* -exclude-path=*\cximage\* -exclude-path=*\bzip2-* -exclude-path=*\bzip2\* -exclude-path=*\UE_*\Engine\Source\* -exclude-path=*\wxWidgets\* -exclude-path=*\libtiff\* -exclude-path=*\glm\* -exclude-path=*\spdlog\* -exclude-path=*\cgltf\* -force-stdout-output=yes -fshort-double=no -fshort-float=no -funsigned-char=no -i-file=C:\Users\Kate\Desktop\ue4sswork\RE-UE4SS\UE4SS\src\GUI\LiveView.1.PVS-Studio.i -language=C++ -lic-file=C:\Users\Kate\AppData\Local\Temp\12156350750924277543.lic -new-output-format=yes -output-file=C:\Users\Kate\Desktop\ue4sswork\RE-UE4SS\.PVS-Studio\logs\LiveView.cpp.364.log -platform=x64 -preprocessor=visualcpp -source-file=C:\Users\Kate\Desktop\ue4sswork\RE-UE4SS\UE4SS\src\GUI\LiveView.cpp -sourcetree-root= -std=c++20 -timeout=600 diff --git a/UE4SS/src/GUI/LiveView.cpp b/UE4SS/src/GUI/LiveView.cpp index 70cbaec79..75534c7bc 100644 --- a/UE4SS/src/GUI/LiveView.cpp +++ b/UE4SS/src/GUI/LiveView.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -46,9 +47,17 @@ #include #include #include -#include #include +#ifdef HAS_GUI +#include +#elif defined(HAS_TUI) +#include +#endif + +#undef max +#undef min + namespace RC::GUI { using namespace Unreal; @@ -278,28 +287,28 @@ namespace RC::GUI }; FLiveViewDeleteListener FLiveViewDeleteListener::LiveViewDeleteListener{}; - static auto add_bool_filter_to_json(JSON::Array& json_filters, const StringType& filter_name, bool is_enabled) -> void + static auto add_bool_filter_to_json(JSON::Array& json_filters, const SystemStringType& filter_name, bool is_enabled) -> void { auto& json_object = json_filters.new_object(); - json_object.new_string(STR("FilterName"), filter_name); - auto& filter_data = json_object.new_object(STR("FilterData")); - filter_data.new_bool(STR("Enabled"), is_enabled); + json_object.new_string(SYSSTR("FilterName"), filter_name); + auto& filter_data = json_object.new_object(SYSSTR("FilterData")); + filter_data.new_bool(SYSSTR("Enabled"), is_enabled); } template - static auto add_array_filter_to_json(JSON::Array& json_filters, const StringType& filter_name, const ContainerType& container, const StringType& array_name) -> void + static auto add_array_filter_to_json(JSON::Array& json_filters, const SystemStringType& filter_name, const ContainerType& container, const SystemStringType& array_name) -> void { auto& json_object = json_filters.new_object(); - json_object.new_string(STR("FilterName"), filter_name); - auto& filter_data = json_object.new_object(STR("FilterData")); + json_object.new_string(SYSSTR("FilterName"), filter_name); + auto& filter_data = json_object.new_object(SYSSTR("FilterData")); - if (array_name == STR("ClassNames")) + if (array_name == SYSSTR("ClassNames")) { - filter_data.new_bool(STR("IsExclude"), Filter::ClassNamesFilter::b_is_exclude); + filter_data.new_bool(SYSSTR("IsExclude"), Filter::ClassNamesFilter::b_is_exclude); } - else if (array_name == STR("FunctionParamFlags")) + else if (array_name == SYSSTR("FunctionParamFlags")) { - filter_data.new_bool(STR("IncludeReturnProperty"), Filter::FunctionParamFlags::s_include_return_property); + filter_data.new_bool(SYSSTR("IncludeReturnProperty"), Filter::FunctionParamFlags::s_include_return_property); } auto& values_array = filter_data.new_array(array_name); @@ -307,7 +316,7 @@ namespace RC::GUI { if constexpr (std::is_same_v) { - values_array.new_string(value.ToString()); + values_array.new_string(to_system(value.ToString())); } else if constexpr (std::is_same_v) { @@ -315,7 +324,7 @@ namespace RC::GUI } else { - values_array.new_string(value); + values_array.new_string(to_system(value)); } } } @@ -323,30 +332,30 @@ namespace RC::GUI static auto internal_save_filters_to_disk() -> void { auto json = JSON::Object{}; - auto& json_filters = json.new_array(STR("Filters")); + auto& json_filters = json.new_array(SYSSTR("Filters")); { - add_bool_filter_to_json(json_filters, STR("IncludeInheritance"), LiveView::s_include_inheritance); - add_bool_filter_to_json(json_filters, STR("UseRegexForSearch"), LiveView::s_use_regex_for_search); - add_bool_filter_to_json(json_filters, STR("ApplySearchFiltersWhenNotSearching"), LiveView::s_apply_search_filters_when_not_searching); - add_bool_filter_to_json(json_filters, Filter::DefaultObjectsOnly::s_debug_name, Filter::DefaultObjectsOnly::s_enabled); - add_bool_filter_to_json(json_filters, Filter::IncludeDefaultObjects::s_debug_name, Filter::IncludeDefaultObjects::s_enabled); - add_bool_filter_to_json(json_filters, Filter::InstancesOnly::s_debug_name, Filter::InstancesOnly::s_enabled); - add_bool_filter_to_json(json_filters, Filter::NonInstancesOnly::s_debug_name, Filter::NonInstancesOnly::s_enabled); + add_bool_filter_to_json(json_filters, SYSSTR("IncludeInheritance"), LiveView::s_include_inheritance); + add_bool_filter_to_json(json_filters, SYSSTR("UseRegexForSearch"), LiveView::s_use_regex_for_search); + add_bool_filter_to_json(json_filters, SYSSTR("ApplySearchFiltersWhenNotSearching"), LiveView::s_apply_search_filters_when_not_searching); + add_bool_filter_to_json(json_filters, to_system(Filter::DefaultObjectsOnly::s_debug_name), Filter::DefaultObjectsOnly::s_enabled); + add_bool_filter_to_json(json_filters, to_system(Filter::IncludeDefaultObjects::s_debug_name), Filter::IncludeDefaultObjects::s_enabled); + add_bool_filter_to_json(json_filters, to_system(Filter::InstancesOnly::s_debug_name), Filter::InstancesOnly::s_enabled); + add_bool_filter_to_json(json_filters, to_system(Filter::NonInstancesOnly::s_debug_name), Filter::NonInstancesOnly::s_enabled); } { - add_array_filter_to_json(json_filters, Filter::ClassNamesFilter::s_debug_name, Filter::ClassNamesFilter::list_class_names, STR("ClassNames")); - add_array_filter_to_json(json_filters, Filter::HasProperty::s_debug_name, Filter::HasProperty::list_properties, STR("Properties")); - add_array_filter_to_json(json_filters, Filter::HasPropertyType::s_debug_name, Filter::HasPropertyType::list_property_types, STR("PropertyTypes")); - add_array_filter_to_json(json_filters, Filter::FunctionParamFlags::s_debug_name, Filter::FunctionParamFlags::s_checkboxes, STR("FunctionParamFlags")); + add_array_filter_to_json(json_filters, to_system(Filter::ClassNamesFilter::s_debug_name), Filter::ClassNamesFilter::list_class_names, SYSSTR("ClassNames")); + add_array_filter_to_json(json_filters, to_system(Filter::HasProperty::s_debug_name), Filter::HasProperty::list_properties, SYSSTR("Properties")); + add_array_filter_to_json(json_filters, to_system(Filter::HasPropertyType::s_debug_name), Filter::HasPropertyType::list_property_types, SYSSTR("PropertyTypes")); + add_array_filter_to_json(json_filters, to_system(Filter::FunctionParamFlags::s_debug_name), Filter::FunctionParamFlags::s_checkboxes, SYSSTR("FunctionParamFlags")); } auto json_file = - File::open(StringType{UE4SSProgram::get_program().get_working_directory()} + std::format(STR("\\liveview\\filters.meta.json")), + File::open(std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / SYSSTR("UE4SS-config") SYSSTR("liveview")SYSSTR("filters.meta.json"), File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); int32_t json_indent_level{}; - json_file.write_string_to_file(json.serialize(JSON::ShouldFormat::Yes, &json_indent_level)); + json_file.write_file_string_to_file(to_file(json.serialize(JSON::ShouldFormat::Yes, &json_indent_level))); } static auto save_filters_to_disk() -> void @@ -356,17 +365,27 @@ namespace RC::GUI }); } + template + static auto to_dest_string(T&& input) { + STRING_DISPATCH_NOERR(std::decay_t, to_string, to_wstring, to_u16string) + else if constexpr(std::is_same_v, FName>) { + return to_ue(input); + } + STRING_DISPATCH_ERROR(Dest) + } + template - static auto json_array_to_filters_list(JSON::Array& json_array, std::vector& list, StringType type, std::string& internal_value) -> void + static auto json_array_to_filters_list(JSON::Array& json_array, std::vector& list, SystemStringType type, std::string& internal_value) -> void { list.clear(); - internal_value.clear(); + internal_value.clear(); json_array.for_each([&](JSON::Value& item) { if (!item.is()) { throw std::runtime_error{std::format("Invalid {} in 'filters.meta.json'", to_string(type))}; } - list.emplace_back(item.as()->get_view()); + list.emplace_back(to_dest_string(item.as()->get_view())); + return LoopAction::Continue; }); for (const auto& class_name : list) @@ -390,7 +409,7 @@ namespace RC::GUI static auto internal_load_filters_from_disk() -> void { const auto json_file = - File::open(StringType{UE4SSProgram::get_program().get_working_directory()} + std::format(STR("\\liveview\\filters.meta.json")), + File::open(std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / SYSSTR("UE4SS-config") / SYSSTR("liveview") / SYSSTR("filters.meta.json"), File::OpenFor::Reading, File::OverwriteExistingFile::No, File::CreateIfNonExistent::Yes); @@ -401,68 +420,68 @@ namespace RC::GUI } const auto json_global_object = JSON::Parser::parse(json_file_contents); - const auto& json_filters = json_global_object->get(STR("Filters")); + const auto& json_filters = json_global_object->get(SYSSTR("Filters")); json_filters.for_each([&](const JSON::Value& filter) { if (!filter.is()) { throw std::runtime_error{"Invalid filter in 'filters.meta.json'"}; } auto& json_object = *filter.as(); - auto filter_name = json_object.get(STR("FilterName")).get_view(); - auto& filter_data = json_object.get(STR("FilterData")); + auto filter_name = json_object.get(SYSSTR("FilterName")).get_view(); + auto& filter_data = json_object.get(SYSSTR("FilterData")); - if (filter_name == STR("IncludeInheritance")) + if (filter_name == SYSSTR("IncludeInheritance")) { - LiveView::s_include_inheritance = filter_data.get(STR("Enabled")).get(); + LiveView::s_include_inheritance = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == STR("UseRegexForSearch")) + else if (filter_name == SYSSTR("UseRegexForSearch")) { - LiveView::s_use_regex_for_search = filter_data.get(STR("Enabled")).get(); + LiveView::s_use_regex_for_search = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == STR("ApplySearchFiltersWhenNotSearching")) + else if (filter_name == SYSSTR("ApplySearchFiltersWhenNotSearching")) { - LiveView::s_apply_search_filters_when_not_searching = filter_data.get(STR("Enabled")).get(); + LiveView::s_apply_search_filters_when_not_searching = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == Filter::DefaultObjectsOnly::s_debug_name) + else if (filter_name == to_system(Filter::DefaultObjectsOnly::s_debug_name)) { - Filter::DefaultObjectsOnly::s_enabled = filter_data.get(STR("Enabled")).get(); + Filter::DefaultObjectsOnly::s_enabled = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == Filter::IncludeDefaultObjects::s_debug_name) + else if (filter_name == to_system(Filter::IncludeDefaultObjects::s_debug_name)) { - Filter::IncludeDefaultObjects::s_enabled = filter_data.get(STR("Enabled")).get(); + Filter::IncludeDefaultObjects::s_enabled = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == Filter::InstancesOnly::s_debug_name) + else if (filter_name == to_system(Filter::InstancesOnly::s_debug_name)) { - Filter::InstancesOnly::s_enabled = filter_data.get(STR("Enabled")).get(); + Filter::InstancesOnly::s_enabled = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == Filter::NonInstancesOnly::s_debug_name) + else if (filter_name == to_system(Filter::NonInstancesOnly::s_debug_name)) { - Filter::NonInstancesOnly::s_enabled = filter_data.get(STR("Enabled")).get(); + Filter::NonInstancesOnly::s_enabled = filter_data.get(SYSSTR("Enabled")).get(); } - else if (filter_name == Filter::ClassNamesFilter::s_debug_name) + else if (filter_name == to_system(Filter::ClassNamesFilter::s_debug_name)) { - Filter::ClassNamesFilter::b_is_exclude = filter_data.get(STR("IsExclude")).get(); - auto& class_names = filter_data.get(STR("ClassNames")); - json_array_to_filters_list(class_names, Filter::ClassNamesFilter::list_class_names, STR("class name"), Filter::ClassNamesFilter::s_internal_class_names); + Filter::ClassNamesFilter::b_is_exclude = filter_data.get(SYSSTR("IsExclude")).get(); + auto& class_names = filter_data.get(SYSSTR("ClassNames")); + json_array_to_filters_list(class_names, Filter::ClassNamesFilter::list_class_names, SYSSTR("class name"), Filter::ClassNamesFilter::s_internal_class_names); } - else if (filter_name == Filter::HasProperty::s_debug_name) + else if (filter_name == to_system(Filter::HasProperty::s_debug_name)) { - auto& properties = filter_data.get(STR("Properties")); - json_array_to_filters_list(properties, Filter::HasProperty::list_properties, STR("property"), Filter::HasProperty::s_internal_properties); + auto& properties = filter_data.get(SYSSTR("Properties")); + json_array_to_filters_list(properties, Filter::HasProperty::list_properties, SYSSTR("property"), Filter::HasProperty::s_internal_properties); } - else if (filter_name == Filter::HasPropertyType::s_debug_name) + else if (filter_name == to_system(Filter::HasPropertyType::s_debug_name)) { - auto& property_types = filter_data.get(STR("PropertyTypes")); + auto& property_types = filter_data.get(SYSSTR("PropertyTypes")); json_array_to_filters_list(property_types, Filter::HasPropertyType::list_property_types, - STR("property type"), + SYSSTR("property type"), Filter::HasPropertyType::s_internal_property_types); } - else if (filter_name == Filter::FunctionParamFlags::s_debug_name) + else if (filter_name == to_system(Filter::FunctionParamFlags::s_debug_name)) { - Filter::FunctionParamFlags::s_include_return_property = filter_data.get(STR("IncludeReturnProperty")).get(); + Filter::FunctionParamFlags::s_include_return_property = filter_data.get(SYSSTR("IncludeReturnProperty")).get(); Filter::FunctionParamFlags::s_checkboxes.fill(false); - auto& function_param_flags = filter_data.get(STR("FunctionParamFlags")); + auto& function_param_flags = filter_data.get(SYSSTR("FunctionParamFlags")); if (function_param_flags.get().size() != Filter::FunctionParamFlags::s_checkboxes.size()) { throw std::runtime_error{"Invalid number of function param flag entires in 'filters.meta.json'"}; @@ -560,17 +579,17 @@ namespace RC::GUI case LiveView::Watch::AcquisitionMethod::StaticFindObject: { auto object_full_name = watch.container->GetFullName(); auto object_type_space_location = object_full_name.find(STR(" ")); - auto object_typeless_name = StringType{object_full_name.begin() + object_type_space_location + 1, object_full_name.end()}; - json_object->new_string(STR("AcquisitionID"), object_typeless_name); + auto object_typeless_name = UEStringType{object_full_name.begin() + object_type_space_location + 1, object_full_name.end()}; + json_object->new_string(SYSSTR("AcquisitionID"), to_system(object_typeless_name)); break; } case LiveView::Watch::AcquisitionMethod::FindFirstOf: - json_object->new_string(STR("AcquisitionID"), watch.container->GetClassPrivate()->GetName()); + json_object->new_string(SYSSTR("AcquisitionID"), to_system(watch.container->GetClassPrivate()->GetName())); break; } - json_object->new_string(STR("PropertyName"), watch.property_name); - json_object->new_number(STR("AcquisitionMethod"), static_cast(watch.acquisition_method)); - json_object->new_number(STR("WatchType"), + json_object->new_string(SYSSTR("PropertyName"), to_system(watch.property_name)); + json_object->new_number(SYSSTR("AcquisitionMethod"), static_cast(watch.acquisition_method)); + json_object->new_number(SYSSTR("WatchType"), watch.container->IsA() ? static_cast(LiveView::Watch::Type::Function) : static_cast(LiveView::Watch::Type::Property)); return json_object; @@ -578,31 +597,30 @@ namespace RC::GUI static auto internal_load_watches_from_disk() -> void { - auto working_directory_path = StringType{UE4SSProgram::get_program().get_working_directory()} + std::format(STR("\\watches\\watches.meta.json")); - auto legacy_root_directory_path = StringType{UE4SSProgram::get_program().get_legacy_root_directory()} + std::format(STR("\\watches\\watches.meta.json")); + auto working_directory_path = SystemStringType{UE4SSProgram::get_program().get_working_directory()} + std::format(SYSSTR("\\watches\\watches.meta.json")); + auto legacy_root_directory_path = SystemStringType{UE4SSProgram::get_program().get_legacy_root_directory()} + std::format(SYSSTR("\\watches\\watches.meta.json")); - StringType json_file_contents; bool is_legacy = !std::filesystem::exists(working_directory_path) && std::filesystem::exists(legacy_root_directory_path); auto json_file = File::open(is_legacy ? legacy_root_directory_path : working_directory_path, File::OpenFor::Reading, File::OverwriteExistingFile::No, File::CreateIfNonExistent::Yes); - + auto json_file_contents = json_file.read_file_all(); if (json_file_contents.empty()) { return; } auto json_global_object = JSON::Parser::parse(json_file_contents); - const auto& elements = json_global_object->get(STR("Watches")); + const auto& elements = json_global_object->get(SYSSTR("Watches")); elements.for_each([](JSON::Value& element) { if (!element.is()) { throw std::runtime_error{"Invalid watch in 'watches.meta.json'"}; } auto& json_watch_object = *element.as(); - auto acquisition_id = json_watch_object.get(STR("AcquisitionID")).get_view(); - auto property_name = json_watch_object.get(STR("PropertyName")).get_view(); + auto acquisition_id = to_ue(json_watch_object.get(SYSSTR("AcquisitionID")).get_view()); + auto property_name = to_ue(json_watch_object.get(SYSSTR("PropertyName")).get_view()); auto acquisition_method = - static_cast(json_watch_object.get(STR("AcquisitionMethod")).get()); - auto watch_type = static_cast(json_watch_object.get(STR("WatchType")).get()); + static_cast(json_watch_object.get(SYSSTR("AcquisitionMethod")).get()); + auto watch_type = static_cast(json_watch_object.get(SYSSTR("WatchType")).get()); UObject* object{}; switch (acquisition_method) @@ -659,7 +677,7 @@ namespace RC::GUI static auto internal_save_watches_to_disk() -> void { auto json = JSON::Object{}; - auto& json_uobjects = json.new_array(STR("Watches")); + auto& json_uobjects = json.new_array(SYSSTR("Watches")); { std::lock_guard lock{LiveView::Watch::s_watch_lock}; @@ -673,12 +691,12 @@ namespace RC::GUI } } - auto json_file = File::open(StringType{UE4SSProgram::get_program().get_working_directory()} + std::format(STR("\\watches\\watches.meta.json")), + auto json_file = File::open(std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / "watches" / "watches.meta.json", File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); int32_t json_indent_level{}; - json_file.write_string_to_file(json.serialize(JSON::ShouldFormat::Yes, &json_indent_level)); + json_file.write_file_string_to_file(to_file(json.serialize(JSON::ShouldFormat::Yes, &json_indent_level))); } static auto save_watches_to_disk() -> void @@ -710,14 +728,14 @@ namespace RC::GUI UObjectArray::RemoveUObjectDeleteListener(&FLiveViewDeleteListener::LiveViewDeleteListener); } - LiveView::Watch::Watch(StringType&& object_name, StringType&& property_name) : object_name(object_name), property_name(property_name) + LiveView::Watch::Watch(UEStringType&& object_name, UEStringType&& property_name) : object_name(object_name), property_name(property_name) { auto& file_device = output.get_device(); - file_device.set_file_name_and_path(StringType{UE4SSProgram::get_program().get_working_directory()} + - std::format(STR("\\watches\\ue4ss_watch_{}_{}.txt"), object_name, property_name)); - file_device.set_formatter([](File::StringViewType string) -> File::StringType { - const auto when_as_string = std::format(STR("{:%Y-%m-%d %H:%M:%S}"), std::chrono::system_clock::now()); - return std::format(STR("[{}] {}"), when_as_string, string); + file_device.set_file_name_and_path(to_system_string(std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / "watches" / + std::format("ue4ss_watch_{}_{}.txt", to_string(object_name), to_string(property_name)))); + file_device.set_formatter([](SystemStringViewType string) -> SystemStringType { + const auto when_as_string = std::format(SYSSTR("{:%Y-%m-%d %H:%M:%S}"), std::chrono::system_clock::now()); + return std::format(SYSSTR("[{}] {}"), when_as_string, string); }); } @@ -1531,7 +1549,7 @@ namespace RC::GUI auto LiveView::search_by_name() -> void { - Output::send(STR("Searching by name...\n")); + Output::send(SYSSTR("Searching by name...\n")); s_name_search_results.clear(); s_name_search_results_set.clear(); UObjectGlobals::ForEachUObject([&](UObject* object, ...) { @@ -1569,14 +1587,14 @@ namespace RC::GUI std::string search_buffer{m_search_by_name_buffer}; if (search_buffer.empty() || s_apply_search_filters_when_not_searching) { - Output::send(STR("Search all chunks\n")); + Output::send(SYSSTR("Search all chunks\n")); s_name_to_search_by.clear(); m_object_iterator = &LiveView::guobjectarray_iterator; m_is_searching_by_name = false; } else { - Output::send(STR("Search for: {}\n"), search_buffer.empty() ? STR("") : to_wstring(search_buffer)); + Output::send(SYSSTR("Search for: {}\n"), search_buffer.empty() ? SYSSTR("") : to_system(search_buffer)); s_name_to_search_by = search_buffer; m_object_iterator = &LiveView::guobjectarray_by_name_iterator; m_is_searching_by_name = true; @@ -1685,14 +1703,14 @@ namespace RC::GUI } } - Output::send(STR("{}\n"), uclass->GetFullName()); + Output::send(SYSSTR("{}\n"), uclass->GetFullName()); ImGui::Text("Properties"); for (FProperty* property : uclass->ForEachProperty()) { if (ImGui::TreeNode(to_string(property->GetFullName()).c_str())) { - Output::send(STR("Show property: {}\n"), property->GetFullName()); + Output::send(SYSSTR("Show property: {}\n"), property->GetFullName()); } } } @@ -1717,7 +1735,7 @@ namespace RC::GUI { if (ImGui::IsItemClicked()) { - printf_s("Clicked: %S\n", ustruct->GetFullName().c_str()); + printf_s("Clicked: " SystemStringPrint "\n", to_system(ustruct->GetFullName()).c_str()); select_object(0, ustruct->GetObjectItem(), ustruct, AffectsHistory::Yes); } } @@ -1956,13 +1974,13 @@ namespace RC::GUI } auto value_as_string = Unreal::UKismetNodeHelperLibrary::GetEnumeratorUserFriendlyName(uenum, enum_index); ImGui::SameLine(); - ImGui::Text("%S", value_as_string.c_str()); + ImGui::Text(SystemStringPrint, to_system(value_as_string).c_str()); render_property_value_context_menu(); } else { ImGui::SameLine(); - ImGui::Text("%S", property_text.GetCharArray()); + ImGui::Text(SystemStringPrint, to_system_string(property_text.GetCharArray()).c_str()); render_property_value_context_menu(); } @@ -1974,7 +1992,7 @@ namespace RC::GUI if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("%S", property->GetFullName().c_str()); + ImGui::Text(SystemStringPrint, to_system_string(property->GetFullName()).c_str()); ImGui::Separator(); ImGui::Text("Offset: 0x%X", property->GetOffset_Internal()); ImGui::Text("Size: 0x%X", property->GetSize()); @@ -1982,12 +2000,13 @@ namespace RC::GUI } auto obj = container_type == ContainerType::Array ? *static_cast(container) : static_cast(container); - StringType parent_name{}; + UEStringType parent_name{}; if (container_type == ContainerType::Object) { parent_name = obj ? obj->GetName() : STR("None"); } - auto edit_property_value_modal_name = to_string(std::format(STR("Edit value of property: {}->{}"), parent_name, property->GetName())); + auto edit_property_value_modal_name = + to_string(std::format(SYSSTR("Edit value of property: {}->{}"), to_system(parent_name), to_system(property->GetName()))); if (open_edit_value_popup) { @@ -2009,7 +2028,7 @@ namespace RC::GUI if (ImGui::Button("Apply")) { FOutputDevice placeholder_device{}; - if (!property->ImportText(to_wstring(m_current_property_value_buffer).c_str(), property->ContainerPtrToValuePtr(container), NULL, obj, &placeholder_device)) + if (!property->ImportText(to_ue(m_current_property_value_buffer).c_str(), property->ContainerPtrToValuePtr(container), NULL, obj, &placeholder_device)) { m_modal_edit_property_value_error_unable_to_edit = true; ImGui::OpenPopup("UnableToSetNewPropertyValueError"); @@ -2076,8 +2095,8 @@ namespace RC::GUI ++index; ImGui::TableNextColumn(); - ImGui::Text("%S", enum_name.c_str()); - if (ImGui::BeginPopupContextItem(to_string(std::format(STR("context-menu-{}"), enum_name)).c_str())) + ImGui::Text(SystemStringPrint, to_system(enum_name).c_str()); + if (ImGui::BeginPopupContextItem(to_string(std::format(SYSSTR("context-menu-{}"), to_system(enum_name))).c_str())) { if (ImGui::MenuItem("Copy name")) { @@ -2092,11 +2111,11 @@ namespace RC::GUI } ImGui::TableNextColumn(); - ImGui::Text("%S", enum_friendly_name.c_str()); + ImGui::Text(SystemStringPrint, to_system(enum_friendly_name).c_str()); ImGui::TableNextColumn(); ImGui::Text("%lld", name.Value); - if (ImGui::BeginPopupContextItem(to_string(std::format(STR("context-menu-{}-{}"), enum_name, name.Value)).c_str())) + if (ImGui::BeginPopupContextItem(to_string(std::format(SYSSTR("context-menu-{}-{}"), to_system(enum_name), to_system(name.Value))).c_str())) { if (ImGui::MenuItem("Copy value")) { @@ -2111,7 +2130,7 @@ namespace RC::GUI } ImGui::TableNextColumn(); - ImGui::PushID(to_string(std::format(STR("button_add_{}"), enum_name)).c_str()); + ImGui::PushID(to_string(std::format(SYSSTR("button_add_{}"), to_system(enum_name))).c_str()); if (ImGui::Button("+")) { open_add_name_popup = true; @@ -2119,18 +2138,18 @@ namespace RC::GUI } ImGui::PopID(); ImGui::SameLine(); - ImGui::PushID(to_string(std::format(STR("button_remove_{}"), enum_name)).c_str()); + ImGui::PushID(to_string(std::format(SYSSTR("button_remove_{}"), to_system(enum_name))).c_str()); if (ImGui::Button("-")) { uenum->RemoveFromNamesAt(index, 1); } ImGui::PopID(); - std::string edit_enum_name_modal_name = to_string(std::format(STR("Edit enum name for: {}"), name.Key.ToString())); + std::string edit_enum_name_modal_name = to_string(std::format(SYSSTR("Edit enum name for: {}"), to_system(name.Key.ToString()))); - std::string edit_enum_value_modal_name = to_string(std::format(STR("Edit enum value for: {}"), name.Key.ToString())); + std::string edit_enum_value_modal_name = to_string(std::format(SYSSTR("Edit enum value for: {}"), to_system(name.Key.ToString()))); - std::string add_enum_name_modal_name = to_string(std::format(STR("Enter new enum name after: {}"), name.Key.ToString())); + std::string add_enum_name_modal_name = to_string(std::format(SYSSTR("Enter new enum name after: {}"), to_system(name.Key.ToString()))); if (open_edit_name_popup) { @@ -2177,7 +2196,7 @@ namespace RC::GUI if (ImGui::Button("Apply")) { FOutputDevice placeholder_device{}; - StringType new_name = to_wstring(m_current_property_value_buffer); + UEStringType new_name = to_ue(m_current_property_value_buffer); FName new_key = FName(new_name, FNAME_Add); uenum->EditNameAt(index, new_key); if (uenum->GetEnumNames()[index].Key.ToString() != new_name) @@ -2261,7 +2280,7 @@ namespace RC::GUI if (ImGui::Button("Apply")) { FOutputDevice placeholder_device{}; - StringType new_name = to_wstring(m_current_property_value_buffer); + UEStringType new_name = to_ue(m_current_property_value_buffer); FName new_key = FName(new_name, FNAME_Add); int64 value = names[index].Value; @@ -2538,7 +2557,7 @@ namespace RC::GUI ImGui::EndPopup(); } ImGui::Text("ClassPrivate: %s", to_string(object->GetClassPrivate()->GetName()).c_str()); - ImGui::Text("Path: %S", object->GetPathName().c_str()); + ImGui::Text("Path: " SystemStringPrint, to_system(object->GetPathName()).c_str()); render_flags(object, "ObjectFlags"); if (auto as_class = Cast(object); as_class) { @@ -2582,7 +2601,7 @@ namespace RC::GUI auto supers_super = *supers_super_it; super_size -= supers_super->GetPropertiesSize(); } - ImGui::Text("%S: 0x%X (0x%X)", super->GetName().c_str(), super_size, super->GetPropertiesSize()); + ImGui::Text(SystemStringPrint ": 0x%X (0x%X)", to_system(super->GetName()).c_str(), super_size, super->GetPropertiesSize()); } ImGui::Unindent(); @@ -2612,7 +2631,7 @@ namespace RC::GUI bool tried_to_open_nullptr_property{}; auto property_full_name = property->GetFullName(); - ImGui::Text("Selected: %S", property->GetName().c_str()); + ImGui::Text("Selected: " SystemStringPrint, to_system(property->GetName()).c_str()); ImGui::Text("Address: %016llX", std::bit_cast(property)); if (ImGui::BeginPopupContextItem(to_string(property_full_name).c_str())) { @@ -2622,8 +2641,8 @@ namespace RC::GUI } ImGui::EndPopup(); } - ImGui::Text("Class: %S", property->GetClass().GetName().c_str()); - ImGui::Text("Path: %S", property->GetPathName().c_str()); + ImGui::Text("Class: " SystemStringPrint, to_system(property->GetClass().GetName()).c_str()); + ImGui::Text("Path: " SystemStringPrint, to_system(property->GetPathName()).c_str()); ImGui::Separator(); @@ -2687,7 +2706,7 @@ namespace RC::GUI ImGui::Unindent(); ImGui::Text("RepIndex: %i (0x%X)", property->GetRepIndex(), property->GetRepIndex()); ImGui::Text("OffsetInternal: %i (0x%X)", property->GetOffset_Internal(), property->GetOffset_Internal()); - ImGui::Text("RepNotifyFunc: %S", property->GetRepNotifyFunc().ToString().c_str()); + ImGui::Text("RepNotifyFunc: " SystemStringPrint, to_system(property->GetRepNotifyFunc().ToString()).c_str()); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -2696,7 +2715,7 @@ namespace RC::GUI } auto render_property_pointer = [](std::string_view pointer_name, FProperty* property) { - ImGui::Text("%s: %p %S", pointer_name.data(), property, property ? property->GetFullName().c_str() : STR("None")); + ImGui::Text("%s: %p " SystemStringPrint, pointer_name.data(), property, property ? to_system(property->GetFullName()).c_str() : SYSSTR("None")); return property; }; int go_to_property_menu_count{}; @@ -2805,7 +2824,7 @@ namespace RC::GUI auto LiveView::render_info_panel() -> void { - ImGui::BeginChild("LiveView_InfoPanel", {-16.0f, m_bottom_size}, true, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::BeginChild("LiveView_InfoPanel", {XOFFSET_0, m_bottom_size}, true, ImGuiWindowFlags_HorizontalScrollbar); size_t next_object_index_to_select{}; @@ -2846,7 +2865,7 @@ namespace RC::GUI { ImGui::BeginDisabled(); } - if (ImGui::Button(ICON_FA_SEARCH " Find functions")) + if (ImGui::Button(ATTACH_ICON(ICON_FA_SEARCH, " Find functions"))) { m_function_caller_widget->open_widget_deferred(); } @@ -2898,7 +2917,7 @@ namespace RC::GUI { FString live_value_fstring{}; watch.property->ExportTextItem(live_value_fstring, watch.property->ContainerPtrToValuePtr(watch.container), nullptr, nullptr, 0); - auto live_value_string = StringType{live_value_fstring.GetCharArray()}; + auto live_value_string = UEStringType{live_value_fstring.GetCharArray()}; if (watch.property_value == live_value_string) { @@ -2907,12 +2926,12 @@ namespace RC::GUI watch.property_value = std::move(live_value_string); - const auto when_as_string = std::format(STR("{:%H:%M:%S}"), std::chrono::system_clock::now()); - watch.history.append(to_string(when_as_string + STR(" ") + watch.property_value + STR("\n"))); + const auto when_as_string = std::format("{:%H:%M:%S}", std::chrono::system_clock::now()); + watch.history.append(when_as_string + " " + to_string(watch.property_value) + "\n"); if (watch.write_to_file) { - watch.output.send(STR("{}\n"), watch.property_value); + watch.output.send(SYSSTR("{}\n"), watch.property_value); } } @@ -2943,12 +2962,12 @@ namespace RC::GUI auto num_params = function->GetNumParms(); - const auto when_as_string = std::format(STR("{:%H:%M:%S}"), std::chrono::system_clock::now()); - StringType buffer{std::format(STR("Received call @ {}.\n"), when_as_string)}; + const auto when_as_string = std::format(SYSSTR("{:%H:%M:%S}"), std::chrono::system_clock::now()); + auto buffer{std::format(SYSSTR("Received call @ {}.\n"), when_as_string)}; - buffer.append(std::format(STR(" Context:\n {}\n"), context.Context->GetFullName())); + buffer.append(std::format(SYSSTR(" Context:\n {}\n"), to_system(context.Context->GetFullName()))); - buffer.append(STR(" Locals:\n")); + buffer.append(SYSSTR(" Locals:\n")); bool has_local_params{}; for (const auto& param : function->ForEachProperty()) { @@ -2960,15 +2979,15 @@ namespace RC::GUI FString param_text{}; auto container_ptr = param->ContainerPtrToValuePtr(context.TheStack.Locals()); param->ExportTextItem(param_text, container_ptr, container_ptr, std::bit_cast(function), NULL); - buffer.append(std::format(STR(" {} = {}\n"), param->GetName(), param_text.GetCharArray())); + buffer.append(std::format(SYSSTR(" {} = {}\n"), to_system(param->GetName()), to_system(param_text.GetCharArray()))); } if (!has_local_params) { - buffer.append(STR(" \n")); + buffer.append(SYSSTR(" \n")); } bool has_out_params{}; - buffer.append(STR(" Out:\n")); + buffer.append(SYSSTR(" Out:\n")); for (const auto& param : function->ForEachProperty()) { if (param->HasAnyPropertyFlags(CPF_ReturnParm)) @@ -2983,33 +3002,33 @@ namespace RC::GUI FString param_text{}; auto container_ptr = FindOutParamValueAddress(context.TheStack, param); param->ExportTextItem(param_text, container_ptr, container_ptr, std::bit_cast(function), NULL); - buffer.append(std::format(STR(" {} = {}\n"), param->GetName(), param_text.GetCharArray())); + buffer.append(std::format(SYSSTR(" {} = {}\n"), to_system(param->GetName()), to_system(param_text.GetCharArray()))); } if (!has_out_params) { - buffer.append(STR(" \n")); + buffer.append(SYSSTR(" \n")); } - buffer.append(STR(" ReturnValue\n")); + buffer.append(SYSSTR(" ReturnValue\n")); auto return_property = function->GetReturnProperty(); if (return_property) { FString return_property_text{}; auto container_ptr = context.RESULT_DECL; return_property->ExportTextItem(return_property_text, container_ptr, container_ptr, std::bit_cast(function), NULL); - buffer.append(std::format(STR(" {}"), return_property_text.GetCharArray())); + buffer.append(std::format(SYSSTR(" {}"), to_system(return_property_text.GetCharArray()))); } else { - buffer.append(STR(" ")); + buffer.append(SYSSTR(" ")); } - buffer.append(STR("\n\n")); + buffer.append(SYSSTR("\n\n")); watch.history.append(to_string(buffer)); if (watch.write_to_file) { - watch.output.send(STR("{}"), buffer); + watch.output.send(SYSSTR("{}"), buffer); } } @@ -3200,7 +3219,7 @@ namespace RC::GUI while (std::getline(ss, class_name, ',')) { std::erase_if(class_name, isspace); - Filter::ClassNamesFilter::list_class_names.emplace_back(to_wstring(class_name)); + Filter::ClassNamesFilter::list_class_names.emplace_back(to_ue(class_name)); } } } @@ -3217,7 +3236,7 @@ namespace RC::GUI while (std::getline(ss, class_name, ',')) { std::erase_if(class_name, isspace); - Filter::ClassNamesFilter::list_class_names.emplace_back(to_wstring(class_name)); + Filter::ClassNamesFilter::list_class_names.emplace_back(to_ue(class_name)); } } } @@ -3238,7 +3257,7 @@ namespace RC::GUI while (std::getline(ss, property_name, ',')) { std::erase_if(property_name, isspace); - Filter::HasProperty::list_properties.emplace_back(to_wstring(property_name)); + Filter::HasProperty::list_properties.emplace_back(to_ue(property_name)); } } } @@ -3261,7 +3280,7 @@ namespace RC::GUI std::erase_if(property_type_name, isspace); if (!property_type_name.empty()) { - Filter::HasPropertyType::list_property_types.emplace_back(FName(to_wstring(property_type_name), FNAME_Add)); + Filter::HasPropertyType::list_property_types.emplace_back(FName(to_ue(property_type_name), FNAME_Add)); } } } @@ -3387,7 +3406,7 @@ namespace RC::GUI if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::BeginTooltip(); - ImGui::Text(ICON_FA_BAN " Feature disabled due to 'General.bUseUObjectArrayCache' being set to 0 in UE4SS-settings.ini."); + ImGui::Text(ATTACH_ICON(ICON_FA_BAN, " Feature disabled due to 'General.bUseUObjectArrayCache' being set to 0 in UE4SS-settings.ini.")); ImGui::EndTooltip(); } } @@ -3411,11 +3430,9 @@ namespace RC::GUI } ImGui::SameLine(); - - // Remember to update text width calculations for the last ImGui::PushItemWidth call if this text gets updated. - if (ImGui::Button(ICON_FA_COPY " Copy search result")) + if (ImGui::Button(ATTACH_ICON(ICON_FA_COPY, " Copy search result"))) { - StringType result{}; + SystemStringType result{}; auto is_below_425 = Version::IsBelow(4, 25); for (const auto& search_result : s_name_search_results) { @@ -3423,19 +3440,22 @@ namespace RC::GUI } ImGui::SetClipboardText(to_string(result).c_str()); } - // Y - Windows title bar offset - Bottom window margin - Splitter height - auto split_pane_height = ImGui::GetContentRegionAvail().y - 31.0f - 8.0f - 4.0f; + float window_margin = 0.0f; + if (!IS_TUI) { + window_margin = 31.0f + 8.0f + SEPARATOR_HEIGHT; + } + auto split_pane_height = ImGui::GetContentRegionAvail().y - window_margin; if (m_bottom_size > 0 && m_bottom_size + m_top_size != split_pane_height) { // Window height changed, scale panes by ratio m_top_size = std::max(ImGui::GetFrameHeight(), std::round(split_pane_height * (m_top_size / (m_top_size + m_bottom_size)))); } m_bottom_size = std::max(ImGui::GetFrameHeight(), split_pane_height - m_top_size); - ImGui_Splitter(false, 4.0f, &m_top_size, &m_bottom_size, ImGui::GetFrameHeight(), ImGui::GetFrameHeight(), -16.0f); + ImGui_Splitter(false, SEPARATOR_HEIGHT, &m_top_size, &m_bottom_size, ImGui::GetFrameHeight(), ImGui::GetFrameHeight(), -16); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4{0.156f, 0.156f, 0.156f, 1.0f}); - ImGui::BeginChild("LiveView_TreeView", {-16.0f, m_top_size}, true); + ImGui::BeginChild("LiveView_TreeView", {XOFFSET_1, m_top_size}, true); auto do_iteration = [&](int32_t int_data_1 = 0, int32_t int_data_2 = 0) { ((*this).*((*this).m_object_iterator))(int_data_1, int_data_2, [&](UObject* object) { @@ -3444,9 +3464,9 @@ namespace RC::GUI auto render_context_menu = [&] { if (ImGui::BeginPopupContextItem(tree_node_name.c_str())) { - if (ImGui::MenuItem(ICON_FA_COPY " Copy Full Name")) + if (ImGui::MenuItem(ATTACH_ICON(ICON_FA_COPY, " Copy Full Name"))) { - Output::send(STR("Copy Full Name: {}\n"), object->GetFullName()); + Output::send(SYSSTR("Copy Full Name: {}\n"), object->GetFullName()); ImGui::SetClipboardText(tree_node_name.c_str()); } if (object->IsA()) @@ -3456,14 +3476,14 @@ namespace RC::GUI if (function_watcher_it == s_watch_map.end()) { ImGui::Separator(); - if (ImGui::MenuItem(ICON_FA_EYE " Watch value")) + if (ImGui::MenuItem(ATTACH_ICON(ICON_FA_EYE, " Watch value"))) { add_watch(watch_id, static_cast(object)); } } else { - ImGui::Checkbox(ICON_FA_EYE " Watch value", &function_watcher_it->second->enabled); + ImGui::Checkbox(ATTACH_ICON(ICON_FA_EYE, " Watch value"), &function_watcher_it->second->enabled); } } ImGui::EndPopup(); @@ -3555,8 +3575,7 @@ namespace RC::GUI load_watches_from_disk(); s_watches_loaded_from_disk = true; } - - ImGui::BeginChild("watch_render_frame", {-16.0f, -31.0f + -8.0f}); + ImGui::BeginChild("watch_render_frame", {XOFFSET_1, (-31.0f + -8.0f) / XDIV}); if (ImGui::Button("All Off")) { @@ -3569,12 +3588,16 @@ namespace RC::GUI } static int num_columns = 3; +#ifdef HAS_GUI ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, {2.0f, 2.0f}); - if (ImGui::BeginTable("watch_table", num_columns, ImGuiTableFlags_Borders)) +#endif + if (ImGui::BeginTable("watch_table", num_columns, ImGuiTableFlags_Borders | ImGuiTableFlags_NoPadOuterX)) { - ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthFixed, ImGui::GetFrameHeight() * 3.0f + 4.0f); + float width_controls = std::max(ImGui::CalcTextSize(ICON_FA_COPY "Controls").x, ImGui::GetFrameHeight() * 3.0f + 4.0f); + ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthFixed, width_controls); ImGui::TableSetupColumn("Watch Identifier", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("Save", ImGuiTableColumnFlags_WidthFixed, ImGui::GetFrameHeight()); + float width_save = std::max(ImGui::CalcTextSize(ICON_FA_COPY "Save").x, ImGui::GetFrameHeight()); + ImGui::TableSetupColumn("Save", ImGuiTableColumnFlags_WidthFixed, width_save); ImGui::TableHeadersRow(); { @@ -3584,7 +3607,7 @@ namespace RC::GUI auto& watch = *watch_ptr; ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Checkbox(to_string(std::format(STR("##watch-on-off-{}"), watch.hash)).c_str(), &watch.enabled)) + if (ImGui::Checkbox(to_string(std::format(SYSSTR("##watch-on-off-{}"), watch.hash)).c_str(), &watch.enabled)) { if (watch.container->IsA()) { @@ -3598,7 +3621,7 @@ namespace RC::GUI ImGui::EndTooltip(); } ImGui::SameLine(0.0f, 2.0f); - ImGui::Checkbox(to_string(std::format(STR("##watch-write-to-file-{}"), watch.hash)).c_str(), &watch.write_to_file); + ImGui::Checkbox(to_string(std::format(SYSSTR("##watch-write-to-file-{}"), watch.hash)).c_str(), &watch.write_to_file); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -3608,12 +3631,12 @@ namespace RC::GUI ImGui::SameLine(0.0f, 2.0f); ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.5f)); ImGui::PushID(std::format("collapse_history_{}", watch.hash).c_str()); - ImGui::Selectable(watch.show_history ? ICON_FA_MINUS : ICON_FA_PLUS, &watch.show_history, ImGuiSelectableFlags_NoPadWithHalfSpacing); + ImGui::Selectable(watch.show_history ? ICON_ALT(ICON_FA_MINUS, "-") : ICON_ALT(ICON_FA_PLUS, "+"), &watch.show_history, ImGuiSelectableFlags_NoPadWithHalfSpacing); ImGui::PopID(); ImGui::PopStyleVar(); ImGui::TableNextColumn(); - ImGui::Text("%S.%S", watch.object_name.c_str(), watch.property_name.c_str()); + ImGui::Text(SystemStringPrint "." SystemStringPrint, to_system(watch.object_name).c_str(), to_system(watch.property_name).c_str()); if (watch.show_history) { ImGui::PushID(std::format("history_{}", watch.hash).c_str()); @@ -3625,12 +3648,12 @@ namespace RC::GUI ImGui::PopID(); } ImGui::TableNextColumn(); - if (ImGui::Checkbox(to_string(std::format(STR("##watch-from-disk-{}"), watch.hash)).c_str(), &watch.load_on_startup)) + if (ImGui::Checkbox(to_string(std::format(SYSSTR("##watch-from-disk-{}"), watch.hash)).c_str(), &watch.load_on_startup)) { save_watches_to_disk(); } - ImGui::SetNextWindowSize({690.0f, 0.0f}); - if (ImGui::BeginPopupContextItem(to_string(std::format(STR("##watch-from-disk-settings-popup-{}"), watch.hash)).c_str())) + ImGui::SetNextWindowSize({690.0f / XDIV, 0.0f}); + if (ImGui::BeginPopupContextItem(to_string(std::format(SYSSTR("##watch-from-disk-settings-popup-{}"), watch.hash)).c_str())) { ImGui::Text("Acquisition Method"); ImGui::Text("This determines how the watch will be reacquired."); @@ -3651,7 +3674,9 @@ namespace RC::GUI } ImGui::EndTable(); +#ifdef HAS_GUI ImGui::PopStyleVar(); +#endif } ImGui::EndChild(); } diff --git a/UE4SS/src/GUI/DX11.cpp b/UE4SS/src/GUI/Platform/D3D11/DX11.cpp similarity index 100% rename from UE4SS/src/GUI/DX11.cpp rename to UE4SS/src/GUI/Platform/D3D11/DX11.cpp diff --git a/UE4SS/src/GUI/GLFW3_OpenGL3.cpp b/UE4SS/src/GUI/Platform/GLFW/GLFW3_OpenGL3.cpp similarity index 77% rename from UE4SS/src/GUI/GLFW3_OpenGL3.cpp rename to UE4SS/src/GUI/Platform/GLFW/GLFW3_OpenGL3.cpp index 24727ba9a..663becc37 100644 --- a/UE4SS/src/GUI/GLFW3_OpenGL3.cpp +++ b/UE4SS/src/GUI/Platform/GLFW/GLFW3_OpenGL3.cpp @@ -10,13 +10,23 @@ #include #include +#include + +#ifdef LINUX +#include +#endif + namespace RC::GUI { static void glfw_error_callback(int error, const char* description) { - Output::send(STR("Glfw Error {}: {}\n"), error, to_wstring(description)); + Output::send(SYSSTR("Glfw Error {}: {}\n"), error, description); } +#ifdef LINUX + static std::shared_ptr g_input_source; +#endif + auto Backend_GLFW3_OpenGL3::init() -> void { // Setup window @@ -46,6 +56,25 @@ namespace RC::GUI throw std::runtime_error{"Was unable to initialize glad"}; } + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + throw std::runtime_error{"Was unable to initialize glad"}; + } + +#ifdef LINUX + if (auto source = Input::Handler::get_input_source("GLFW3")) + { + g_input_source = std::dynamic_pointer_cast(source); + glfwSetKeyCallback(m_window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { + // map keys to windows'definition + if (g_input_source) + { + g_input_source->receive_input(key, action, mods); + } + }); + } +#endif + int left, top, right, bottom; glfwGetWindowFrameSize(m_window, &left, &top, &right, &bottom); glfwSetWindowSize(m_window, 1280 - left - right, 800 - top - bottom); @@ -80,6 +109,12 @@ namespace RC::GUI { ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); + glfwDestroyWindow(m_window); + m_window = nullptr; + +#ifdef LINUX + g_input_source = nullptr; +#endif } auto Backend_GLFW3_OpenGL3::cleanup() -> void diff --git a/UE4SS/src/GUI/Platform/TUI/TUI.cpp b/UE4SS/src/GUI/Platform/TUI/TUI.cpp new file mode 100644 index 000000000..bb8c1d037 --- /dev/null +++ b/UE4SS/src/GUI/Platform/TUI/TUI.cpp @@ -0,0 +1,186 @@ +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace RC::GUI +{ + static ImTui::TScreen* g_screen = nullptr; + static bool tui_shutdown = false; + static std::shared_ptr ncurses_source{}; + int saved_stderr = -1; + void Backend_TUI::init() + { + // fprintf(stderr, "Backend_TUI::init\n"); + setenv("TERMINFO", UE4SSProgram::settings_manager.TUI.TERMINFO.c_str(), 0); + if (!UE4SSProgram::settings_manager.TUI.LCALL.empty()) + { + setenv("LC_ALL", UE4SSProgram::settings_manager.TUI.LCALL.c_str(), 0); + } + g_screen = ImTui_ImplNcurses_Init(true); + auto source = Input::Handler::get_input_source("Ncurses"); + ncurses_source = std::dynamic_pointer_cast(source); + + // disable stderr + saved_stderr = dup(STDERR_FILENO); + freopen("./imtui.log", "w", stderr); + setbuf(stderr, NULL); + fprintf(stderr, "Backend_TUI::init\n"); + fflush(stderr); + } + + void Backend_TUI::imgui_backend_newframe() + { + // fprintf(stderr, "Backend_TUI::imgui_backend_newframe\n"); + if (ncurses_source) + { + ncurses_source->begin_frame(); + } + ImTui_ImplNcurses_NewFrame([](int x) { + if (ncurses_source) + { + ncurses_source->receive_input(x); + } + }); + if (ncurses_source) + { + ncurses_source->end_frame(); + } + } + + void Backend_TUI::create_window(int loc_x, int loc_y, int size_x, int size_y) + { + // Do nothing + } + + WindowPosition Backend_TUI::get_window_position() + { + return {0, 0}; + } + + void Backend_TUI::exec_message_loop(bool* exit_requested) + { + ImTui_ImplNcurses_ProcessEvent(); + *exit_requested = false; + } + + void Backend_TUI::shutdown() + { + ImTui_ImplNcurses_Shutdown(); + g_screen = nullptr; + tui_shutdown = true; + dup2(saved_stderr, STDERR_FILENO); + close(saved_stderr); + saved_stderr = -1; + fprintf(stderr, "Backend_TUI::shutdown, stderr restored\n"); + } + + void Backend_TUI::cleanup() + { + // Do nothing + } + + void* Backend_TUI::get_window_handle() + { + return g_screen; + } + + WindowSize Backend_TUI::get_window_size() + { + if (g_screen) + { + return {g_screen->nx, g_screen->ny}; + } + else + { + return {0, 0}; + } + } + + void Backend_TUI::on_gfx_backend_set() + { + // Do nothing + } + + void Backend_GfxTUI::init() + { + ImTui_ImplText_Init(); + } + + void Backend_GfxTUI::imgui_backend_newframe() + { + ImTui_ImplText_NewFrame(); + } + + void Backend_GfxTUI::render(const float clear_color_with_alpha[4]) + { + if (tui_shutdown) + { + return; + } + ImTui_ImplText_RenderDrawData(ImGui::GetDrawData(), g_screen); + ImTui_ImplNcurses_DrawScreen(); + } + + void Backend_GfxTUI::shutdown() + { + ImTui_ImplText_Shutdown(); + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + } + + void Backend_GfxTUI::cleanup() + { + // Do nothing + } + + bool Backend_GfxTUI::create_device() + { + return true; + } + + void Backend_GfxTUI::cleanup_device() + { + // Do nothing + } + + void Backend_GfxTUI::handle_window_resize(int64_t param_1, int64_t param_2) + { + // Do nothing + } + + void Backend_GfxTUI::on_os_backend_set() + { + // Do nothing + } + + WindowSize Backend_GfxTUI::get_window_size() + { + return {g_screen->nx, g_screen->ny}; + } + + bool Backend_GfxTUI::exit_requested() + { + return tui_shutdown; + } + + void Backend_GfxTUI::set_backend_properties(BackendProperty& properties) + { + properties.quirk_tui = true; + properties.xdiv = 6.66f; + properties.ydiv = 21.1f; + properties.separator_height = 0.5f; + properties.x_offset_0 = 0; + properties.x_offset_1 = 0; + } +}; // namespace RC::GUI diff --git a/UE4SS/src/GUI/Windows.cpp b/UE4SS/src/GUI/Platform/Windows/Windows.cpp similarity index 97% rename from UE4SS/src/GUI/Windows.cpp rename to UE4SS/src/GUI/Platform/Windows/Windows.cpp index 486654ef4..26cee4cdd 100644 --- a/UE4SS/src/GUI/Windows.cpp +++ b/UE4SS/src/GUI/Platform/Windows/Windows.cpp @@ -36,10 +36,10 @@ namespace RC::GUI auto Backend_Windows::create_window(int loc_x, int loc_y, int size_x, int size_y) -> void { - StringType title_bar_text{STR("UE4SS Debugging Tools")}; + SystemStringType title_bar_text{SYSSTR("UE4SS Debugging Tools")}; if (dynamic_cast(m_gfx_backend)) { - title_bar_text.append(STR(" (DX11)")); + title_bar_text.append(SYSSTR(" (DX11)")); } // ImGui_ImplWin32_EnableDpiAwareness() diff --git a/UE4SS/src/GUI/SearcherWidget.cpp b/UE4SS/src/GUI/SearcherWidget.cpp index 2cb338cc8..2249d68f0 100644 --- a/UE4SS/src/GUI/SearcherWidget.cpp +++ b/UE4SS/src/GUI/SearcherWidget.cpp @@ -1,7 +1,7 @@ #include #include - +#define strncpy_s(dest, destsz, src, count) strncpy((dest), (src), (((count) < (destsz)) ? (count) : (destsz))) namespace RC::GUI { SearcherWidget::SearcherWidget(SearchModeChangedCallback ufunction_caller_search_mode_changed_callback, @@ -55,7 +55,7 @@ namespace RC::GUI std::string search_buffer{m_search_by_name_buffer}; if (search_buffer.empty()) { - // Output::send(STR("Search all functions\n")); + // Output::send(SYSSTR("Search all functions\n")); m_name_to_search_by.clear(); m_current_iterator = m_all_iterator; m_is_searching_by_name = false; @@ -63,7 +63,7 @@ namespace RC::GUI } else { - // Output::send(STR("Search for: {}\n"), search_buffer.empty() ? STR("") : to_wstring(search_buffer)); + // Output::send(SYSSTR("Search for: {}\n"), search_buffer.empty() ? STR("") : to_wstring(search_buffer)); m_name_to_search_by = search_buffer; m_current_iterator = m_search_iterator; m_is_searching_by_name = true; diff --git a/UE4SS/src/GUI/UFunctionCallerWidget.cpp b/UE4SS/src/GUI/UFunctionCallerWidget.cpp index e072e6332..5e8351b6f 100644 --- a/UE4SS/src/GUI/UFunctionCallerWidget.cpp +++ b/UE4SS/src/GUI/UFunctionCallerWidget.cpp @@ -14,10 +14,14 @@ #include #include #include +#include #include #include +#undef min +#undef max + namespace RC::GUI { UFunctionCallerWidget::UFunctionCallerWidget() = default; @@ -139,7 +143,7 @@ namespace RC::GUI static bool s_do_call{}; static UObject* s_instance{}; - static StringType s_cmd{}; + static SystemStringType s_cmd{}; static FOutputDevice s_ar{}; static UFunction* s_function{}; static UObject* s_executor{}; @@ -150,9 +154,9 @@ namespace RC::GUI s_do_call = false; auto& function_flags = s_function->GetFunctionFlags(); function_flags |= FUNC_Exec; - Output::send(STR("Processing command: {}\n"), s_cmd); - bool call_succeeded = s_instance->ProcessConsoleExec(s_cmd.c_str(), s_ar, s_executor); - Output::send(STR("call_succeeded: {}\n"), call_succeeded); + Output::send(SYSSTR("Processing command: {}\n"), s_cmd); + bool call_succeeded = s_instance->ProcessConsoleExec(to_ue(s_cmd).c_str(), s_ar, s_executor); + Output::send(SYSSTR("call_succeeded: {}\n"), call_succeeded); function_flags &= ~FUNC_Exec; } } @@ -164,13 +168,13 @@ namespace RC::GUI } auto function = m_currently_selected_function->function; - auto cmd = std::format(STR("{}"), function->GetName()); + auto cmd = std::format(SYSSTR("{}"), to_system(function->GetName())); for (const auto& param : m_params_for_selected_function) { - cmd.append(std::format(STR(" {}"), to_wstring(param.value_from_ui))); + cmd.append(std::format(SYSSTR(" {}"), to_system(param.value_from_ui))); } - Output::send(STR("Queueing command: {}\n"), cmd); + Output::send(SYSSTR("Queueing command: {}\n"), cmd); s_cmd = cmd; s_instance = instance; @@ -223,41 +227,49 @@ namespace RC::GUI { if (auto as_struct_property = CastField(param.unreal_param); as_struct_property) { - ImGui::Text("%S (%S)", param.unreal_param->GetClass().GetName().c_str(), as_struct_property->GetStruct()->GetName().c_str()); + ImGui::Text(SystemStringPrint " (" SystemStringPrint ")", + to_system(param.unreal_param->GetClass().GetName()).c_str(), + to_system(as_struct_property->GetStruct()->GetName()).c_str()); } else if (auto as_array_property = CastField(param.unreal_param); as_array_property) { - ImGui::Text("%S (%S)", param.unreal_param->GetClass().GetName().c_str(), as_array_property->GetInner()->GetName().c_str()); + ImGui::Text(SystemStringPrint " (" SystemStringPrint ")", + to_system(param.unreal_param->GetClass().GetName()).c_str(), + to_system(as_array_property->GetInner()->GetName()).c_str()); } else if (auto as_object_property = CastField(param.unreal_param); as_object_property) { - ImGui::Text("%S (%S)", param.unreal_param->GetClass().GetName().c_str(), as_object_property->GetPropertyClass()->GetName().c_str()); + ImGui::Text(SystemStringPrint " (" SystemStringPrint ")", + to_system(param.unreal_param->GetClass().GetName()).c_str(), + to_system(as_object_property->GetPropertyClass()->GetName()).c_str()); } else if (auto as_class_property = CastField(param.unreal_param); as_class_property) { - ImGui::Text("%S (%S)", param.unreal_param->GetClass().GetName().c_str(), as_class_property->GetMetaClass()->GetName().c_str()); + ImGui::Text(SystemStringPrint " (" SystemStringPrint ")", + to_system(param.unreal_param->GetClass().GetName()).c_str(), + to_system(as_class_property->GetMetaClass()->GetName()).c_str()); } else { - ImGui::Text("%S", param.unreal_param->GetClass().GetName().c_str()); + ImGui::Text(SystemStringPrint, to_system(param.unreal_param->GetClass().GetName()).c_str()); } } static auto get_typeless_object_name(UObject* object) -> std::string { - auto object_name = to_string(object->GetFullName()); - auto object_name_type_space_location = object_name.find(" "); + auto object_name = to_system(object->GetFullName()); + auto object_name_type_space_location = object_name.find(SYSSTR(" ")); if (object_name_type_space_location == object_name.npos) { - Output::send(STR("Could not copy name of PlayerController, was unable to find space in full PlayerController name: '{}'."), - to_wstring(object_name)); + Output::send(SYSSTR("Could not copy name of PlayerController, was unable to find space in full PlayerController name: '{}'."), + object_name); return {}; } else { if (object_name_type_space_location > static_cast(std::numeric_limits::max())) { - Output::send(STR("integer overflow when converting pc_name_type_space_location to signed")); + Output::send(SYSSTR("integer overflow when converting pc_name_type_space_location to signed")); return {}; } else @@ -283,7 +295,7 @@ namespace RC::GUI return params.ReturnValue; } - static auto render_other_list(std::string_view menu_name, StringViewType class_name, UFunctionParam& param) -> void + static auto render_other_list(std::string_view menu_name, UEStringViewType class_name, UFunctionParam& param) -> void { if (ImGui::BeginMenu(menu_name.data())) { @@ -313,14 +325,14 @@ namespace RC::GUI { m_prev_instance = instance; - auto popup_modal_id = to_string(std::format(STR("##functions-for-{}"), instance->HashObject())); + auto popup_modal_id = to_string(std::format(SYSSTR("##functions-for-{}"), instance->HashObject())); auto& is_open = is_widget_open(); if (is_open) { ImGui::OpenPopup(popup_modal_id.c_str()); } - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, {925.0f, 300.0f}); + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, {925.0f / XDIV, 300.0f / YDIV}); if (ImGui::BeginPopupModal(popup_modal_id.c_str(), &is_open)) { ImGui::PushItemWidth(-1.0f); diff --git a/UE4SS/src/LuaLibrary.cpp b/UE4SS/src/LuaLibrary.cpp index 2d5e608e2..99c1b1344 100644 --- a/UE4SS/src/LuaLibrary.cpp +++ b/UE4SS/src/LuaLibrary.cpp @@ -9,6 +9,12 @@ #include #include +#ifdef WIN32 +#include +#else +#include +#endif + namespace RC::LuaLibrary { auto get_outputdevice_ref(const LuaMadeSimple::Lua& lua) -> const Unreal::FOutputDevice* @@ -33,13 +39,13 @@ namespace RC::LuaLibrary { auto* output_device = get_outputdevice_ref(lua); - StringType formatted_string = STR("[Lua] "); - StringType outdevice_string; + SystemStringType formatted_string = SYSSTR("[Lua] "); + SystemStringType outdevice_string; if (output_device) { // Remove stack item from get_outputdevice_ref's lua_getglobal lua.discard_value(-1); - outdevice_string = STR("[Lua] "); + outdevice_string = SYSSTR("[Lua] "); } int32_t stack_size = lua.get_stack_size(); @@ -47,13 +53,13 @@ namespace RC::LuaLibrary { // lua_tostring (macro of lua_tolstring) is NOT the same as luaL_tolstring // luaL_tolstring provides tostring()-ish conversion for any value - auto lua_str = to_generic_string(luaL_tolstring(lua.get_lua_state(), i, nullptr)); + auto lua_str = to_system(luaL_tolstring(lua.get_lua_state(), i, nullptr)); if (i > 1) { // Use double tab, as single tab might make the spacing too thin in the console - formatted_string.append(STR("\t\t")); - if (output_device) outdevice_string.append(STR(" ")); + formatted_string.append(SYSSTR("\t\t")); + if (output_device) outdevice_string.append(SYSSTR(" ")); } formatted_string.append(lua_str); if (output_device) outdevice_string.append(lua_str); @@ -64,7 +70,7 @@ namespace RC::LuaLibrary Output::send(formatted_string); - if (output_device) output_device->Log(outdevice_string.c_str()); + if (output_device) output_device->Log(to_ue_string(outdevice_string).c_str()); return 0; } @@ -73,17 +79,22 @@ namespace RC::LuaLibrary { if (lua.get_stack_size() != 1 || !lua.is_integer()) { - Output::send(STR("[Fatal] Lua function 'DerefToInt32' must have only 1 parameter and it must be of type 'int'.\n")); + Output::send(SYSSTR("[Fatal] Lua function 'DerefToInt32' must have only 1 parameter and it must be of type 'int'.\n")); lua.set_nil(); return 1; } int32_t* int32_ptr = reinterpret_cast(lua.get_integer()); - int32_t int32_val = Helper::Casting::offset_deref_safe(int32_ptr, 0, GetCurrentProcess()); + #ifdef WIN32 + auto self = GetCurrentProcess(); + #else + auto self = getpid(); + #endif + int32_t int32_val = Helper::Casting::offset_deref_safe(int32_ptr, 0, self); if (int32_val == 0) { - Output::send(STR("[Fatal] Address passed to Lua function 'DerefToInt32' was not a valid pointer.\n")); + Output::send(SYSSTR("[Fatal] Address passed to Lua function 'DerefToInt32' was not a valid pointer.\n")); lua.set_nil(); return 1; } @@ -98,7 +109,7 @@ namespace RC::LuaLibrary // Logging will only happen to the debug console but it's something at least if (!Output::has_internal_error()) { - Output::send(STR("Error: {}\n"), to_wstring(e)); + Output::send(SYSSTR("Error: {}\n"), e); } else { @@ -106,31 +117,31 @@ namespace RC::LuaLibrary } } - static auto exported_function_status_to_string(ExportedFunctionStatus status) -> std::wstring_view + static auto exported_function_status_to_string(ExportedFunctionStatus status) -> SystemStringViewType { switch (status) { case ExportedFunctionStatus::NO_ERROR_TO_EXPORT: - return L"NO_ERROR_TO_EXPORT | 0"; + return SYSSTR("NO_ERROR_TO_EXPORT | 0"); case ExportedFunctionStatus::UNKNOWN_ERROR: - return L"UNKNOWN_ERROR | 7"; + return SYSSTR("UNKNOWN_ERROR | 7"); case ExportedFunctionStatus::SUCCESS: - return L"SUCCESS | 1"; + return SYSSTR("SUCCESS | 1"); case ExportedFunctionStatus::VARIABLE_NOT_FOUND: - return L"VARIABLE_NOT_FOUND | 2"; + return SYSSTR("VARIABLE_NOT_FOUND | 2"); case ExportedFunctionStatus::MOD_IS_NULLPTR: - return L"MOD_IS_NULLPTR | 3"; + return SYSSTR("MOD_IS_NULLPTR | 3"); case ExportedFunctionStatus::SCRIPT_FUNCTION_RETURNED_FALSE: - return L"SCRIPT_FUNCTION_RETURNED_FALSE | 4"; + return SYSSTR("SCRIPT_FUNCTION_RETURNED_FALSE | 4"); case ExportedFunctionStatus::UNABLE_TO_CALL_SCRIPT_FUNCTION: - return L"UNABLE_TO_CALL_SCRIPT_FUNCTION | 5"; + return SYSSTR("UNABLE_TO_CALL_SCRIPT_FUNCTION | 5"); case ExportedFunctionStatus::SCRIPT_FUNCTION_NOT_FOUND: - return L"SCRIPT_FUNCTION_NOT_FOUND | 6"; + return SYSSTR("SCRIPT_FUNCTION_NOT_FOUND | 6"); case ExportedFunctionStatus::UE4SS_NOT_INITIALIZED: - return L"UE4SS_NOT_INITIALIZED | 8"; + return SYSSTR("UE4SS_NOT_INITIALIZED | 8"); } - return L"Missed switch case"; + return SYSSTR("Missed switch case"); } auto get_lua_state_by_mod_name(const char* mod_name) -> lua_State* @@ -184,7 +195,7 @@ namespace RC::LuaLibrary if (!Unreal::UnrealInitializer::StaticStorage::bIsInitialized) { return_struct.status = ExportedFunctionStatus::UE4SS_NOT_INITIALIZED; - Output::send(STR("set_script_variable_int32 | UE4SS is not initialized\n")); + Output::send(SYSSTR("set_script_variable_int32 | UE4SS is not initialized\n")); return; } @@ -194,14 +205,14 @@ namespace RC::LuaLibrary const std::wstring variable_name_wide = std::wstring(tmp_var_name.begin(), tmp_var_name.end()); const std::string tmp_mod_name = mod_name; const std::wstring mod_name_wide = std::wstring(tmp_mod_name.begin(), tmp_mod_name.end()); - Output::send(STR("Setting variable '{}' in mod '{}' to {}\n"), variable_name_wide, mod_name_wide, new_value); + Output::send(SYSSTR("Setting variable '{}' in mod '{}' to {}\n"), variable_name_wide, mod_name_wide, new_value); //*/ auto mod = UE4SSProgram::find_lua_mod_by_name(mod_name, UE4SSProgram::IsInstalled::Yes, UE4SSProgram::IsStarted::Yes); if (!mod) { return_struct.status = ExportedFunctionStatus::MOD_IS_NULLPTR; - Output::send(STR("set_script_variable_int32 | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("set_script_variable_int32 | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); return; } @@ -213,7 +224,7 @@ namespace RC::LuaLibrary if (type == LUA_TNIL) { return_struct.status = ExportedFunctionStatus::VARIABLE_NOT_FOUND; - Output::send(STR("set_script_variable_int32 | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("set_script_variable_int32 | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); return; } @@ -221,7 +232,7 @@ namespace RC::LuaLibrary lua_setglobal(lua.get_lua_state(), variable_name); return_struct.status = ExportedFunctionStatus::SUCCESS; - Output::send(STR("set_script_variable_int32 | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("set_script_variable_int32 | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); } catch (std::runtime_error& e) { @@ -240,7 +251,7 @@ namespace RC::LuaLibrary if (!Unreal::UnrealInitializer::StaticStorage::bIsInitialized) { return_struct.status = ExportedFunctionStatus::UE4SS_NOT_INITIALIZED; - Output::send(STR("set_script_variable_default_data | UE4SS is not initialized\n")); + Output::send(SYSSTR("set_script_variable_default_data | UE4SS is not initialized\n")); return; } @@ -257,11 +268,11 @@ namespace RC::LuaLibrary { const std::string tmp_data1_value_ansi = external_data.data1.as_string; const std::wstring data1_value_wide = std::wstring(tmp_data1_value_ansi.begin(), tmp_data1_value_ansi.end()); - Output::send(STR("Setting '{}.data1' as string to '{}' in mod '{}'"), variable_name_wide, data1_value_wide, mod_name_wide); + Output::send(SYSSTR("Setting '{}.data1' as string to '{}' in mod '{}'"), variable_name_wide, data1_value_wide, mod_name_wide); break; } case DefaultDataType::Float: - Output::send(STR("Setting '{}.data1' as float to '{}' in mod '{}'"), variable_name_wide, external_data.data1.as_float, mod_name_wide); + Output::send(SYSSTR("Setting '{}.data1' as float to '{}' in mod '{}'"), variable_name_wide, external_data.data1.as_float, mod_name_wide); break; } //*/ @@ -270,7 +281,7 @@ namespace RC::LuaLibrary if (!mod) { return_struct.status = ExportedFunctionStatus::MOD_IS_NULLPTR; - Output::send(STR("set_script_variable_default_data | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("set_script_variable_default_data | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); return; } @@ -347,7 +358,7 @@ namespace RC::LuaLibrary if (!Unreal::UnrealInitializer::StaticStorage::bIsInitialized) { return_struct.status = ExportedFunctionStatus::UE4SS_NOT_INITIALIZED; - Output::send(STR("call_script_function | UE4SS is not initialized\n")); + Output::send(SYSSTR("call_script_function | UE4SS is not initialized\n")); return; } @@ -357,14 +368,14 @@ namespace RC::LuaLibrary const std::wstring func_name_wide = std::wstring(tmp_func_name.begin(), tmp_func_name.end()); const std::string tmp_mod_name = mod_name; const std::wstring mod_name_wide = std::wstring(tmp_mod_name.begin(), tmp_mod_name.end()); - Output::send(STR("Calling script function '{}' in mod '{}'\n"), func_name_wide, mod_name_wide); + Output::send(SYSSTR("Calling script function '{}' in mod '{}'\n"), func_name_wide, mod_name_wide); //*/ auto mod = UE4SSProgram::find_lua_mod_by_name(mod_name, UE4SSProgram::IsInstalled::Yes, UE4SSProgram::IsStarted::Yes); if (!mod) { return_struct.status = ExportedFunctionStatus::MOD_IS_NULLPTR; - Output::send(STR("call_script_function | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("call_script_function | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); return; } @@ -377,7 +388,7 @@ namespace RC::LuaLibrary catch (std::runtime_error&) { return_struct.status = ExportedFunctionStatus::SCRIPT_FUNCTION_NOT_FOUND; - Output::send(STR("call_script_function | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("call_script_function | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); return; } @@ -414,7 +425,7 @@ namespace RC::LuaLibrary return_struct.status = ExportedFunctionStatus::SUCCESS; } - Output::send(STR("call_script_function | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); + Output::send(SYSSTR("call_script_function | return_struct.status: {}\n"), exported_function_status_to_string(return_struct.status)); } catch (std::runtime_error& e) { diff --git a/UE4SS/src/LuaType/LuaCustomProperty.cpp b/UE4SS/src/LuaType/LuaCustomProperty.cpp index 90b4be840..e029e458b 100644 --- a/UE4SS/src/LuaType/LuaCustomProperty.cpp +++ b/UE4SS/src/LuaType/LuaCustomProperty.cpp @@ -12,11 +12,12 @@ namespace RC::LuaType { LuaCustomProperty::PropertyList LuaCustomProperty::StaticStorage::property_list; - LuaCustomProperty::LuaCustomProperty(std::wstring name, std::unique_ptr property) : m_name(name), m_property(std::move(property)) + LuaCustomProperty::LuaCustomProperty(SystemStringType name, std::unique_ptr property) + : m_name(name), m_property(std::move(property)) { } - auto LuaCustomProperty::PropertyList::add(std::wstring property_name, std::unique_ptr property) -> void + auto LuaCustomProperty::PropertyList::add(SystemStringType property_name, std::unique_ptr property) -> void { (void)properties.emplace_back(LuaCustomProperty{property_name, std::move(property)}).m_property.get(); } @@ -26,7 +27,7 @@ namespace RC::LuaType properties.clear(); } - auto LuaCustomProperty::PropertyList::find_or_nullptr(Unreal::UObject* base, std::wstring property_name) -> Unreal::FProperty* + auto LuaCustomProperty::PropertyList::find_or_nullptr(Unreal::UObject* base, SystemStringType property_name) -> Unreal::FProperty* { Unreal::FProperty* custom_property_found{}; diff --git a/UE4SS/src/LuaType/LuaFName.cpp b/UE4SS/src/LuaType/LuaFName.cpp index 207ecc3e8..9353a175c 100644 --- a/UE4SS/src/LuaType/LuaFName.cpp +++ b/UE4SS/src/LuaType/LuaFName.cpp @@ -60,11 +60,11 @@ No overload found for function 'FName'. } int64_t name_comparison_index{}; - StringType name_string{}; + SystemStringType name_string{}; Unreal::EFindName find_type{Unreal::EFindName::FNAME_Add}; if (lua.is_string()) { - name_string = to_wstring(lua.get_string()); + name_string = to_system(lua.get_string()); } else if (lua.is_integer()) { @@ -94,7 +94,7 @@ No overload found for function 'FName'. } else { - LuaType::FName::construct(lua, Unreal::FName(name_string, find_type)); + LuaType::FName::construct(lua, Unreal::FName(to_ue(name_string), find_type)); } return 1; diff --git a/UE4SS/src/LuaType/LuaFOutputDevice.cpp b/UE4SS/src/LuaType/LuaFOutputDevice.cpp index 105c59ce2..e6f68c15e 100644 --- a/UE4SS/src/LuaType/LuaFOutputDevice.cpp +++ b/UE4SS/src/LuaType/LuaFOutputDevice.cpp @@ -65,7 +65,7 @@ No overload found for function 'Log'. } auto message = lua.get_string(); - lua_object.get_remote_cpp_object()->Log(to_wstring(message).c_str()); + lua_object.get_remote_cpp_object()->Log(to_ue_string(message).c_str()); return 0; }); diff --git a/UE4SS/src/LuaType/LuaFString.cpp b/UE4SS/src/LuaType/LuaFString.cpp index ad412aa01..0b4bdcbe2 100644 --- a/UE4SS/src/LuaType/LuaFString.cpp +++ b/UE4SS/src/LuaType/LuaFString.cpp @@ -54,10 +54,10 @@ namespace RC::LuaType table.add_pair("ToString", [](const LuaMadeSimple::Lua& lua) -> int { auto& lua_object = lua.get_userdata(); - const wchar_t* string_data = lua_object.get_local_cpp_object().GetCharArray(); + const UECharType* string_data = lua_object.get_local_cpp_object().GetCharArray(); if (string_data) { - lua.set_string(to_string(string_data)); + lua.set_string(to_string(UEStringType(string_data))); } else { diff --git a/UE4SS/src/LuaType/LuaFText.cpp b/UE4SS/src/LuaType/LuaFText.cpp index d28a91ef6..48a94bb07 100644 --- a/UE4SS/src/LuaType/LuaFText.cpp +++ b/UE4SS/src/LuaType/LuaFText.cpp @@ -58,17 +58,17 @@ No overload found for function 'FText'. lua.discard_value(); } - StringType text_string{}; + SystemStringType text_string{}; if (lua.is_string()) { - text_string = to_wstring(lua.get_string()); + text_string = to_system(lua.get_string()); } else { lua.throw_error(error_overload_not_found); } - LuaType::FText::construct(lua, Unreal::FText(text_string)); + LuaType::FText::construct(lua, Unreal::FText(to_ue(text_string))); return 1; }); diff --git a/UE4SS/src/LuaType/LuaModRef.cpp b/UE4SS/src/LuaType/LuaModRef.cpp index ee9fa4b15..75fddb7f7 100644 --- a/UE4SS/src/LuaType/LuaModRef.cpp +++ b/UE4SS/src/LuaType/LuaModRef.cpp @@ -50,7 +50,7 @@ No overload found for function 'SetSharedVariable'. { throw std::runtime_error{error_overload_not_found}; } - auto variable_name = std::string{lua.get_string()}; + auto variable_name = to_system_string(lua.get_string()); RC::LuaMod::SharedLuaVariable* currently_stored_value{}; if (auto it = RC::LuaMod::m_shared_lua_variables.find(variable_name); it != RC::LuaMod::m_shared_lua_variables.end()) @@ -159,7 +159,7 @@ No overload found for function 'GetSharedVariable'. { throw std::runtime_error{error_overload_not_found}; } - auto variable_name = std::string{lua.get_string()}; + auto variable_name = to_system_string(lua.get_string()); if (auto it = RC::LuaMod::m_shared_lua_variables.find(variable_name); it != RC::LuaMod::m_shared_lua_variables.end()) { diff --git a/UE4SS/src/LuaType/LuaTArray.cpp b/UE4SS/src/LuaType/LuaTArray.cpp index af33b8553..2003507e9 100644 --- a/UE4SS/src/LuaType/LuaTArray.cpp +++ b/UE4SS/src/LuaType/LuaTArray.cpp @@ -23,7 +23,7 @@ namespace RC::LuaType if (!lua_object.m_inner_property) { - Output::send(STR("TArray::construct: m_inner_property is nullptr for {}"), lua_object.m_property->GetFullName()); + Output::send(SYSSTR("TArray::construct: m_inner_property is nullptr for {}"), to_system(lua_object.m_property->GetFullName())); } auto metatable_name = ClassName::ToString(); diff --git a/UE4SS/src/LuaType/LuaUClass.cpp b/UE4SS/src/LuaType/LuaUClass.cpp index 21ece9996..f4046201b 100644 --- a/UE4SS/src/LuaType/LuaUClass.cpp +++ b/UE4SS/src/LuaType/LuaUClass.cpp @@ -63,7 +63,7 @@ namespace RC::LuaType if (!lua_object.get_remote_cpp_object()) { LuaType::UObject::construct(lua, nullptr); - Output::send(STR("[Lua][Error] Tried getting the CDO but the UClass instance is nullptr\n")); + Output::send(SYSSTR("[Lua][Error] Tried getting the CDO but the UClass instance is nullptr\n")); } else { diff --git a/UE4SS/src/LuaType/LuaUEnum.cpp b/UE4SS/src/LuaType/LuaUEnum.cpp index f25a8a15d..ea4bcaa95 100644 --- a/UE4SS/src/LuaType/LuaUEnum.cpp +++ b/UE4SS/src/LuaType/LuaUEnum.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace RC::LuaType { @@ -153,7 +154,7 @@ No overload found for function 'UEnum.InsertIntoNames'. lua.throw_error("Function 'UEnum.InsertIntoNames' cannot be called with 0 parameters."); } - StringType param_name{}; + SystemStringType param_name{}; int64_t param_value = 0; int32_t param_index = 0; bool param_shift = false; @@ -161,7 +162,7 @@ No overload found for function 'UEnum.InsertIntoNames'. // P1 (Name), string if (lua.is_string()) { - param_name = to_wstring(lua.get_string()); + param_name = to_system(lua.get_string()); } else { @@ -194,8 +195,8 @@ No overload found for function 'UEnum.InsertIntoNames'. param_shift = lua.get_bool(); } - const Unreal::FName key{param_name, Unreal::FNAME_Add}; - const auto pair = Unreal::TPair{key, param_value}; + const Unreal::FName key{to_ue(param_name), Unreal::FNAME_Add}; + const auto pair = Unreal::TPair{key, param_value}; lua_object.get_remote_cpp_object()->InsertIntoNames(pair, param_index, param_shift); return 1; @@ -242,7 +243,7 @@ No overload found for function 'UEnum.EditNameAt'. lua.throw_error("'UEnum.EditNameAt' could not load parameter for \"NewName\""); } - Unreal::FName new_key = Unreal::FName(to_wstring(param_new_name), Unreal::FNAME_Add); + Unreal::FName new_key = Unreal::FName(to_ue_string(param_new_name), Unreal::FNAME_Add); lua_object.get_remote_cpp_object()->EditNameAt(param_index, new_key); return 0; diff --git a/UE4SS/src/LuaType/LuaUObject.cpp b/UE4SS/src/LuaType/LuaUObject.cpp index 03d588ebe..48510e7f8 100644 --- a/UE4SS/src/LuaType/LuaUObject.cpp +++ b/UE4SS/src/LuaType/LuaUObject.cpp @@ -216,8 +216,8 @@ namespace RC::LuaType } else { - std::string parameter_type_name = to_string(property_type_fname.ToString()); - std::string parameter_name = to_string(param_next->GetName()); + auto parameter_type_name = to_string(property_type_fname.ToString()); + auto parameter_name = to_string(param_next->GetName()); lua.throw_error( std::format("Tried calling UFunction without a registered handler for parameter. Parameter '{}' of type '{}' not supported.", parameter_name, @@ -249,7 +249,7 @@ namespace RC::LuaType auto reuse_same_table = param->IsA() || param->IsA(); if (!reuse_same_table) { - lua_table.add_key(to_string(param->GetName()).c_str()); + lua_table.add_key(to_lua(param->GetName()).c_str()); } Unreal::FName param_type_fname = param->GetClass().GetFName(); @@ -271,7 +271,7 @@ namespace RC::LuaType } else { - std::string param_type_name = to_string(param_type_fname.ToString()); + auto param_type_name = to_string(param_type_fname.ToString()); lua.throw_error(std::format("Tried calling UFunction without a registered handler 'Out' param. Type '{}' not supported.", param_type_name)); } @@ -537,7 +537,7 @@ namespace RC::LuaType // return LoopAction::Continue; //} - std::string field_name = to_string(field->GetName()); + auto field_name = to_lua(field->GetName()); Unreal::FName field_type = field->GetClass().GetFName(); int32_t name_comparison_index = field_type.GetComparisonIndex(); @@ -562,10 +562,10 @@ namespace RC::LuaType } else { - std::string field_type_name = to_string(field_type.ToString()); + auto field_type_name = to_string(field_type.ToString()); lua.throw_error(std::format("Tried getting without a registered handler. 'StructProperty'.'{}' not supported. Field: '{}'", field_type_name, - field_name)); + to_string(field_name))); } }; @@ -581,7 +581,7 @@ namespace RC::LuaType for (Unreal::FProperty* field : script_struct->ForEachPropertyInChain()) { Unreal::FName field_type_fname = field->GetClass().GetFName(); - const std::string field_name = to_string(field->GetName()); + const auto field_name = to_lua(field->GetName()); // At the top of the stack now: table that has struct data @@ -622,10 +622,10 @@ namespace RC::LuaType } else { - std::string field_type_name = to_string(field_type_fname.ToString()); + auto field_type_name = to_string(field_type_fname.ToString()); params.lua.throw_error(std::format("Tried pushing (Operation::Set) StructProperty without a registered handler for field '{} {}'.", field_type_name, - field_name)); + to_string(field_name))); } }; @@ -724,7 +724,7 @@ namespace RC::LuaType } else { - std::string property_type_name = to_string(property_type_fname.ToString()); + auto property_type_name = to_string(property_type_fname.ToString()); lua.throw_error( std::format("Tried interacting with an array but the inner property has no registered handler. Property type '{}' not supported.", property_type_name)); @@ -739,7 +739,7 @@ namespace RC::LuaType int32_t name_comparison_index = inner_type_fname.GetComparisonIndex(); if (!StaticState::m_property_value_pushers.contains(name_comparison_index)) { - std::string inner_type_name = to_string(inner_type_fname.ToString()); + auto inner_type_name = to_string(inner_type_fname.ToString()); params.lua.throw_error(std::format("Tried pushing (Operation::Set) ArrayProperty with unsupported inner type of '{}'", inner_type_name)); } @@ -755,7 +755,7 @@ namespace RC::LuaType { array = new unsigned char[array_property->GetElementSize() * table_length]; - params.lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { + params.lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { // Skip this table entry if the key wasn't numerical, who knows what the user put in their script if (!table.key.is_integer()) { @@ -958,11 +958,11 @@ namespace RC::LuaType // Make global table to store enum name/value pairs -> START auto table = params.lua.prepare_new_table(); - std::string prop_name = to_string(params.property->GetName()); + auto prop_name = to_lua(params.property->GetName()); for (const auto& elem : enum_ptr->ForEachName()) { - std::string elem_name = to_string(elem.Key.ToString()); + auto elem_name = to_lua(elem.Key.ToString()); table.add_pair(elem_name.c_str(), elem.Value); } @@ -1016,7 +1016,7 @@ namespace RC::LuaType } case Operation::Set: // For now, doing nothing just to get past the error - Output::send(STR("[push_weakobjectproperty] Operation::Set is not supported\n")); + Output::send(SYSSTR("[push_weakobjectproperty] Operation::Set is not supported\n")); return; case Operation::GetParam: params.lua.throw_error("[push_weakobjectproperty] Operation::GetParam is not supported"); @@ -1107,7 +1107,7 @@ namespace RC::LuaType if (params.lua.is_string()) { auto lua_string = params.lua.get_string(); - auto fstring = Unreal::FString{to_wstring(lua_string).c_str()}; + auto fstring = Unreal::FString{to_ue(lua_string).c_str()}; *string = fstring; } else if (params.lua.is_userdata()) @@ -1233,7 +1233,7 @@ No overload found for function 'IsA'. } else if (lua.is_string()) { - auto* object_class = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_wstring(lua.get_string())); + auto* object_class = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_ue_string(lua.get_string())); lua.set_bool(is_a_internal(lua, object, object_class)); } else @@ -1297,7 +1297,7 @@ No overload found for function 'IsA'. { // We can either throw an error and kill the execution /**/ - std::string property_type_name = to_string(property_type.ToString()); + auto property_type_name = to_string(property_type.ToString()); lua.throw_error(std::format( "[handle_unreal_property_value] Tried accessing unreal property without a registered handler. Property type '{}' not supported.", property_type_name)); @@ -1422,7 +1422,7 @@ No overload found for function 'IsA'. { // We can either throw an error and kill the execution /**/ - std::string property_type_name = to_string(lua_object.m_type.ToString()); + auto property_type_name = to_string(lua_object.m_type.ToString()); lua.throw_error(std::format( "[RemoteUnrealParam::prepare_to_handle] Tried accessing unreal property without a registered handler. Property type '{}' not supported.", property_type_name)); diff --git a/UE4SS/src/LuaType/LuaUScriptStruct.cpp b/UE4SS/src/LuaType/LuaUScriptStruct.cpp index b0474fe7d..27043f813 100644 --- a/UE4SS/src/LuaType/LuaUScriptStruct.cpp +++ b/UE4SS/src/LuaType/LuaUScriptStruct.cpp @@ -196,7 +196,7 @@ namespace RC::LuaType { auto& lua_object = lua.get_userdata(); - Unreal::FName property_name = Unreal::FName(to_wstring(lua.get_string())); + Unreal::FName property_name = Unreal::FName(to_ue_string(lua.get_string())); // Check if property_name is 'NONE' if (property_name.GetComparisonIndex() == 0) diff --git a/UE4SS/src/LuaType/LuaUWorld.cpp b/UE4SS/src/LuaType/LuaUWorld.cpp index 887db969b..a2c515a7f 100644 --- a/UE4SS/src/LuaType/LuaUWorld.cpp +++ b/UE4SS/src/LuaType/LuaUWorld.cpp @@ -82,7 +82,7 @@ No overload found for function 'SpawnActor'. } else if (lua.is_table()) { - lua.for_each_in_table([&](const LuaMadeSimple::LuaTableReference& table) { + lua.for_each_in_table([&](const LuaMadeSimple::LuaTableReference& table) { if (table.key.is_string() && table.key.get_string() == "X" && table.value.is_number()) { location.SetX(table.value.get_number()); @@ -113,7 +113,7 @@ No overload found for function 'SpawnActor'. } else if (lua.is_table()) { - lua.for_each_in_table([&](const LuaMadeSimple::LuaTableReference& table) { + lua.for_each_in_table([&](const LuaMadeSimple::LuaTableReference& table) { if (table.key.is_string() && table.key.get_string() == "Yaw" && table.value.is_number()) { rotation.SetYaw(table.value.get_number()); diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index dc9944626..70332a8d1 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -114,7 +114,7 @@ namespace RC::LuaType if (lua_object.get_remote_cpp_object()) { // Set the return value to the ansi version of the full name - lua.set_string(to_string(lua_object.get_remote_cpp_object()->GetFullName()).c_str()); + lua.set_string(to_lua(lua_object.get_remote_cpp_object()->GetFullName()).c_str()); } else { @@ -147,7 +147,7 @@ No overload found for function 'IsA'. if (lua.is_table()) { int64_t ffield_class_pointer{}; - lua.for_each_in_table([&](const LuaMadeSimple::LuaTableReference& table) { + lua.for_each_in_table([&](const LuaMadeSimple::LuaTableReference& table) { if (table.key.is_string() && table.key.get_string() == "FFieldClassPointer") { if (!table.value.is_integer()) @@ -224,10 +224,10 @@ No overload found for function 'ImportText'. const auto& lua_object = lua.get_userdata(); - File::StringType buffer; + SystemStringType buffer; if (lua.is_string()) { - buffer = to_wstring(lua.get_string()); + buffer = to_system(lua.get_string()); } else { @@ -261,7 +261,7 @@ No overload found for function 'ImportText'. } auto* owner_object = lua.get_userdata().get_remote_cpp_object(); - lua_object.get_remote_cpp_object()->ImportText(buffer.c_str(), data, port_flags, owner_object, nullptr); + lua_object.get_remote_cpp_object()->ImportText(to_ue(buffer).c_str(), data, port_flags, owner_object, nullptr); return 0; }); diff --git a/UE4SS/src/Mod/CppMod.cpp b/UE4SS/src/Mod/CppMod.cpp index b8a7b4904..eb0989d21 100644 --- a/UE4SS/src/Mod/CppMod.cpp +++ b/UE4SS/src/Mod/CppMod.cpp @@ -6,27 +6,48 @@ #include #include +#ifdef LINUX +#define printf_s printf +#define _GNU_SOURCE +#include +#endif + namespace RC { - CppMod::CppMod(UE4SSProgram& program, std::wstring&& mod_name, std::wstring&& mod_path) : Mod(program, std::move(mod_name), std::move(mod_path)) + CppMod::CppMod(UE4SSProgram& program, SystemStringType&& mod_name, SystemStringType&& mod_path) : Mod(program, std::move(mod_name), std::move(mod_path)) { - m_dlls_path = m_mod_path + L"\\dlls"; - - if (!std::filesystem::exists(m_dlls_path)) + auto resolved_dlls_path = File::get_path_if_exists(m_mod_path, SYSSTR("dlls")); + if (resolved_dlls_path) + { + m_dlls_path = to_system_string(*resolved_dlls_path); + } + else { - Output::send(STR("Could not find the dlls folder for mod {}\n"), m_mod_name); + Output::send(SYSSTR("Could not find the dlls folder for mod {}\n"), m_mod_name); set_installable(false); return; } - - auto dll_path = m_dlls_path + L"\\main.dll"; +#define STRINGIFY(x) #x +#define CONCATENATE_WIDE_STRING(name, ext) SYSSTR(name) STRINGIFY(ext) + auto resolved_dll_path = File::get_path_if_exists(m_dlls_path, CONCATENATE_WIDE_STRING("main", DLLEXT)); + if (!resolved_dll_path) + { + Output::send(SYSSTR("Could not find the main dll for mod {}\n"), m_mod_name); + set_installable(false); + return; + } + auto dll_path = to_system_string(*resolved_dll_path); +#ifdef WIN32 // Add mods dlls directory to search path for dynamic/shared linked libraries in mods m_dlls_path_cookie = AddDllDirectory(m_dlls_path.c_str()); m_main_dll_module = LoadLibraryExW(dll_path.c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); if (!m_main_dll_module) { - Output::send(STR("Failed to load dll <{}> for mod {}, error code: 0x{:x}\n"), dll_path, m_mod_name, GetLastError()); + Output::send(SYSSTR("Failed to load dll <{}> for mod {}, error code: 0x{:x}\n"), + to_system_string(dll_path), + m_mod_name, + GetLastError()); set_installable(false); return; } @@ -36,7 +57,7 @@ namespace RC if (!m_start_mod_func || !m_uninstall_mod_func) { - Output::send(STR("Failed to find exported mod lifecycle functions for mod {}\n"), m_mod_name); + Output::send(SYSSTR("Failed to find exported mod lifecycle functions for mod {}\n"), m_mod_name); FreeLibrary(m_main_dll_module); m_main_dll_module = NULL; @@ -44,6 +65,29 @@ namespace RC set_installable(false); return; } + +#else + // use RTLD_LOCAL to avoid symbol conflicts + m_dl_handle = dlopen(dll_path.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (!m_dl_handle) + { + Output::send(SYSSTR("Failed to load dll <{}> for mod {}, because: {}\n"), to_system_string(dll_path), m_mod_name, dlerror()); + set_installable(false); + return; + } + + m_start_mod_func = reinterpret_cast(dlsym(m_dl_handle, "start_mod")); + m_uninstall_mod_func = reinterpret_cast(dlsym(m_dl_handle, "uninstall_mod")); + + if (!m_start_mod_func || !m_uninstall_mod_func) + { + Output::send(SYSSTR("Failed to find exported mod lifecycle functions for mod {}\n"), m_mod_name); + dlclose(m_dl_handle); + m_dl_handle = NULL; + set_installable(false); + return; + } +#endif } auto CppMod::start_mod() -> void @@ -57,10 +101,10 @@ namespace RC { if (!Output::has_internal_error()) { - Output::send(STR("Failed to load dll <{}> for mod {}, because: {}\n"), - m_dlls_path + L"\\main.dll\n", + Output::send(SYSSTR("Failed to load dll <{}> for mod {}, because: {}\n"), + to_system_string(std::filesystem::path{m_dlls_path} / CONCATENATE_WIDE_STRING("main", DLLEXT)), m_mod_name, - to_wstring(e.what())); + e.what()); } else { @@ -71,14 +115,14 @@ namespace RC auto CppMod::uninstall() -> void { - Output::send(STR("Stopping C++ mod '{}' for uninstall\n"), m_mod_name); + Output::send(SYSSTR("Stopping C++ mod '{}' for uninstall\n"), m_mod_name); if (m_mod && m_uninstall_mod_func) { m_uninstall_mod_func(m_mod); } } - auto CppMod::fire_on_lua_start(StringViewType mod_name, + auto CppMod::fire_on_lua_start(SystemStringViewType mod_name, LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, @@ -99,7 +143,7 @@ namespace RC } } - auto CppMod::fire_on_lua_stop(StringViewType mod_name, + auto CppMod::fire_on_lua_stop(SystemStringViewType mod_name, LuaMadeSimple::Lua& lua, LuaMadeSimple::Lua& main_lua, LuaMadeSimple::Lua& async_lua, @@ -152,7 +196,7 @@ namespace RC } } - auto CppMod::fire_dll_load(std::wstring_view dll_name) -> void + auto CppMod::fire_dll_load(SystemStringViewType dll_name) -> void { if (m_mod) { @@ -162,10 +206,18 @@ namespace RC CppMod::~CppMod() { +#ifdef WIN32 if (m_main_dll_module) { FreeLibrary(m_main_dll_module); RemoveDllDirectory(m_dlls_path_cookie); } +#else + if (m_dl_handle) + { + dlclose(m_dl_handle); + m_dl_handle = NULL; + } +#endif } } // namespace RC \ No newline at end of file diff --git a/UE4SS/src/Mod/CppUserModBase.cpp b/UE4SS/src/Mod/CppUserModBase.cpp index 8d93279d4..215f58a23 100644 --- a/UE4SS/src/Mod/CppUserModBase.cpp +++ b/UE4SS/src/Mod/CppUserModBase.cpp @@ -2,18 +2,21 @@ #include #include +#include + namespace RC { CppUserModBase::CppUserModBase() { if (ModIntendedSDKVersion.empty()) { - ModIntendedSDKVersion = std::format(STR("{}.{}.{}"), UE4SS_LIB_VERSION_MAJOR, UE4SS_LIB_VERSION_MINOR, UE4SS_LIB_VERSION_HOTFIX); + ModIntendedSDKVersion = to_ue(std::format(SYSSTR("{}.{}.{}"), UE4SS_LIB_VERSION_MAJOR, UE4SS_LIB_VERSION_MINOR, UE4SS_LIB_VERSION_HOTFIX)); } } CppUserModBase::~CppUserModBase() { +#ifdef HAS_UI for (const auto& tab : GUITabs) { if (tab) @@ -23,12 +26,11 @@ namespace RC } GUITabs.clear(); - auto& key_events = UE4SSProgram::get_program().m_input_handler.get_events(); - std::erase_if(key_events, [&](Input::KeySet& input_event) -> bool { - bool were_all_events_registered_from_this_mod = true; - for (auto& [key, vector_of_key_data] : input_event.key_data) - { - std::erase_if(vector_of_key_data, [&](Input::KeyData& key_data) -> bool { + UE4SSProgram::get_program().m_input_handler.get_events_safe([&](auto& key_set) { + std::erase_if(key_set.key_data, [&](auto& item) -> bool { + auto& [_, key_data] = item; + bool were_all_events_registered_from_this_mod = true; + std::erase_if(key_data, [&](Input::KeyData& key_data) -> bool { // custom_data == 1: Bind came from Lua, and custom_data2 is nullptr. // custom_data == 2: Bind came from C++, and custom_data2 is a pointer to KeyDownEventData. Must free it. auto event_data = static_cast(key_data.custom_data2); @@ -43,13 +45,15 @@ namespace RC return false; } }); - } - return were_all_events_registered_from_this_mod; + return were_all_events_registered_from_this_mod; + }); }); + #endif } - auto CppUserModBase::register_tab(std::wstring_view tab_name, GUI::GUITab::RenderFunctionType render_function) -> void +#ifdef HAS_UI + auto CppUserModBase::register_tab(UEStringViewType tab_name, GUI::GUITab::RenderFunctionType render_function) -> void { auto& tab = GUITabs.emplace_back(std::make_shared(tab_name, render_function, this)); UE4SSProgram::get_program().add_gui_tab(tab); @@ -67,4 +71,5 @@ namespace RC { UE4SSProgram::get_program().register_keydown_event(key, callback, modifier_keys, 2, new KeyDownEventData{custom_data, this}); } +#endif } // namespace RC diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index b3bb5ed3b..0589d099d 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -9,9 +9,12 @@ #include #include -#include #include + +#ifdef HAS_INPUT #include +#endif + #include #include #include @@ -23,6 +26,7 @@ #include #include #include + #include #include #pragma warning(disable : 4005) @@ -62,6 +66,8 @@ #include #pragma warning(default : 4005) +#include + namespace RC { LuaMadeSimple::Lua* LuaStatics::console_executor{}; @@ -254,14 +260,14 @@ namespace RC // If the type wasn't supported then we simply clean the Lua stack, output a warning and then do nothing lua_data.lua.discard_value(); - std::wstring parameter_type_name = property_type_name.ToString(); - std::wstring parameter_name = lua_data.return_property->GetName(); + auto parameter_type_name = to_system(property_type_name.ToString()); + auto parameter_name = to_system(lua_data.return_property->GetName()); - Output::send( - STR("Tried altering return value of a hooked UFunction without a registered handler for return type Return property '{}' of type " - "'{}' not supported."), - parameter_name, - parameter_type_name); + Output::send(SYSSTR("Tried altering return value of a hooked UFunction without a registered handler for return type Return property '{}' " + "of type " + "'{}' not supported."), + parameter_name, + parameter_type_name); } } }; @@ -629,16 +635,21 @@ namespace RC efindname_table.make_global("EFindName"); } - LuaMod::LuaMod(UE4SSProgram& program, std::wstring&& mod_name, std::wstring&& mod_path) + LuaMod::LuaMod(UE4SSProgram& program, SystemStringType&& mod_name, SystemStringType&& mod_path) : Mod(program, std::move(mod_name), std::move(mod_path)), m_lua(LuaMadeSimple::new_state()) { // Verify that there's a 'Scripts' directory // Give the full path to the 'Scripts' directory to the mod container - m_scripts_path = m_mod_path + L"\\scripts"; - - // If the 'Scripts' directory doesn't exist then mark the mod as non-installable and move on to the next mod - if (!std::filesystem::exists(m_scripts_path)) + std::filesystem::path mod_path_fs = m_mod_path; + auto scripts_directory = File::get_path_if_exists(mod_path_fs, "Scripts"); + if (scripts_directory) + { + // "scripts" get priority over "Scripts" + m_scripts_path = to_system(*scripts_directory); + } + else { + // If the 'Scripts' directory doesn't exist then mark the mod as non-installable and move on to the next mod set_installable(false); return; } @@ -812,22 +823,34 @@ namespace RC auto LuaMod::setup_lua_require_paths(const LuaMadeSimple::Lua& lua) const -> void { + // TODO: use correct \/ for linux auto* lua_state = m_lua.get_lua_state(); lua_getglobal(lua_state, "package"); lua_getfield(lua_state, -1, "path"); std::string current_paths = lua_tostring(lua_state, -1); - current_paths.append(std::format(";{}\\{}\\Scripts\\?.lua", to_string(m_program.get_mods_directory()).c_str(), to_string(get_name()))); - current_paths.append(std::format(";{}\\shared\\?.lua", to_string(m_program.get_mods_directory()).c_str())); - current_paths.append(std::format(";{}\\shared\\?\\?.lua", to_string(m_program.get_mods_directory()).c_str())); + auto scripts_directory = File::get_path_if_exists(m_mod_path, "Scripts"); + if (scripts_directory) + { + auto scripts_path = ((*scripts_directory) / "?.lua").string(); + current_paths.append(std::format(";{}", scripts_path)); + } + current_paths.append(std::format(";{}" LUA_DIRSEP "shared" LUA_DIRSEP "?.lua", to_string(m_program.get_mods_directory()).c_str())); + current_paths.append(std::format(";{}" LUA_DIRSEP "shared" LUA_DIRSEP "?" LUA_DIRSEP "?.lua", to_string(m_program.get_mods_directory()).c_str())); lua_pop(lua_state, 1); lua_pushstring(lua_state, current_paths.c_str()); lua_setfield(lua_state, -2, "path"); lua_getfield(lua_state, -1, "cpath"); std::string current_cpaths = lua_tostring(lua_state, -1); - current_cpaths.append(std::format(";{}\\{}\\Scripts\\?.dll", to_string(m_program.get_mods_directory()).c_str(), to_string(get_name()))); - current_cpaths.append(std::format(";{}\\{}\\?.dll", to_string(m_program.get_mods_directory()).c_str(), to_string(get_name()))); + if (scripts_directory) + { +#define STRINGIFY(x) #x + auto dlls_path = ((*scripts_directory) / ("?" STRINGIFY(DLLEXT))).string(); + current_cpaths.append(std::format(";{}", dlls_path)); + } + current_cpaths.append( + std::format(";{}" LUA_DIRSEP "{}" LUA_DIRSEP "?" STRINGIFY(DLLEXT), to_string(m_program.get_mods_directory()).c_str(), to_string(get_name()))); lua_pop(lua_state, 1); lua_pushstring(lua_state, current_cpaths.c_str()); lua_setfield(lua_state, -2, "cpath"); @@ -859,7 +882,7 @@ No overload found for function 'StaticFindObject'. // Ignores any params after P1 if (lua.is_string()) { - Unreal::UObject* object = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_wstring(lua.get_string())); + Unreal::UObject* object = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_ue_string(lua.get_string())); // Construct a Lua object of type 'UObject' // Auto constructing is nullptr safe @@ -883,7 +906,7 @@ No overload found for function 'StaticFindObject'. Unreal::UClass* param_class{}; Unreal::UObject* param_in_outer{}; - std::wstring param_name{}; + SystemStringType param_name{}; bool param_exact_class{}; // P1 (Class), userdata @@ -919,7 +942,7 @@ No overload found for function 'StaticFindObject'. // P3 (Name), string if (lua.is_string()) { - param_name = to_wstring(lua.get_string()); + param_name = to_system(lua.get_string()); } else { @@ -933,7 +956,7 @@ No overload found for function 'StaticFindObject'. } // There's no error if P4 isn't a bool, simply ignore all parameters after P3 - Unreal::UObject* object = Unreal::UObjectGlobals::StaticFindObject(param_class, param_in_outer, param_name, param_exact_class); + Unreal::UObject* object = Unreal::UObjectGlobals::StaticFindObject(param_class, param_in_outer, to_ue(param_name), param_exact_class); // Construct a Lua object of type 'UObject' // Auto constructing is nullptr safe @@ -961,7 +984,7 @@ No overload found for function 'FindFirstOf'. // Ignores any params after P1 if (lua.is_string()) { - Unreal::UObject* object = Unreal::UObjectGlobals::FindFirstOf(to_wstring(lua.get_string())); + Unreal::UObject* object = Unreal::UObjectGlobals::FindFirstOf(to_ue_string(lua.get_string())); // Construct a Lua object of type 'UObject' // Auto constructing is nullptr safe @@ -1045,6 +1068,7 @@ No overload found for function 'FindAllOf'. if (is_true_mod == Mod::IsTrueMod::Yes) { +#ifdef HAS_INPUT lua.register_function("IsKeyBindRegistered", [](const LuaMadeSimple::Lua& lua) -> int { std::string error_overload_not_found{R"( No overload found for function 'IsKeyBindRegistered'. @@ -1071,7 +1095,7 @@ No overload found for function 'IsKeyBindRegistered'. Input::Handler::ModifierKeyArray modifier_keys{}; uint8_t table_counter{}; - lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { + lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { if (!table.value.is_integer()) { lua.throw_error( @@ -1089,7 +1113,6 @@ No overload found for function 'IsKeyBindRegistered'. return false; }); - if (table_counter > 0) { lua.set_bool(mod->m_program.is_keydown_event_registered(key_to_check, modifier_keys)); @@ -1137,7 +1160,7 @@ No overload found for function 'RegisterKeyBindAsync'. } catch (std::runtime_error& e) { - Output::send(STR("{}\n"), to_wstring(lua.handle_error(e.what()))); + Output::send(SYSSTR("{}\n"), lua.handle_error(e.what())); } }; @@ -1172,7 +1195,7 @@ No overload found for function 'RegisterKeyBindAsync'. Input::Handler::ModifierKeyArray modifier_keys{}; uint8_t table_counter{}; - lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { + lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { if (!table.value.is_integer()) { lua.throw_error( @@ -1256,7 +1279,7 @@ No overload found for function 'RegisterKeyBind'. } catch (std::runtime_error& e) { - Output::send(STR("{}\n"), to_wstring(lua.handle_error(e.what()))); + Output::send(SYSSTR("{}\n"), lua.handle_error(e.what())); } }; @@ -1291,7 +1314,7 @@ No overload found for function 'RegisterKeyBind'. Input::Handler::ModifierKeyArray modifier_keys{}; uint8_t table_counter{}; - lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { + lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool { if (!table.value.is_integer()) { lua.throw_error("Lua function 'RegisterKeyBind', overload #2, requires a table of 1-byte large integers as the second parameter"); @@ -1342,6 +1365,7 @@ No overload found for function 'RegisterKeyBind'. return 0; }); +#endif lua.register_function("UnregisterHook", [](const LuaMadeSimple::Lua& lua) -> int { std::lock_guard guard{LuaMod::m_thread_actions_mutex}; @@ -1355,8 +1379,8 @@ No overload found for function 'UnregisterHook'. lua.throw_error(error_overload_not_found); } - std::wstring function_name = to_wstring(lua.get_string()); - std::wstring function_name_no_prefix = function_name.substr(function_name.find_first_of(L" ") + 1, function_name.size()); + auto function_name = to_ue(lua.get_string()); + auto function_name_no_prefix = function_name.substr(function_name.find_first_of(STR(" ")) + 1, function_name.size()); Unreal::UFunction* unreal_function = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, function_name_no_prefix); if (!unreal_function) @@ -1392,9 +1416,9 @@ No overload found for function 'UnregisterHook'. if (native_hook_pre_id_it != LuaMod::m_generic_hook_id_to_native_hook_id.end() && native_hook_post_id_it != LuaMod::m_generic_hook_id_to_native_hook_id.end()) { - Output::send(STR("Unregistering native hook with pre-id: {}\n"), native_hook_pre_id_it->first); + Output::send(SYSSTR("Unregistering native hook with pre-id: {}\n"), native_hook_pre_id_it->first); unreal_function->UnregisterHook(static_cast(native_hook_pre_id_it->second)); - Output::send(STR("Unregistering native hook with post-id: {}\n"), native_hook_post_id_it->first); + Output::send(SYSSTR("Unregistering native hook with post-id: {}\n"), native_hook_post_id_it->first); unreal_function->UnregisterHook(static_cast(native_hook_post_id_it->second)); // LuaUnrealScriptFunctionData contains the hook's lua registry references, captured in RegisterHook in two different lua states. @@ -1417,10 +1441,10 @@ No overload found for function 'UnregisterHook'. } else { - if (auto callback_data_it = LuaMod::m_script_hook_callbacks.find(unreal_function->GetFullName()); + if (auto callback_data_it = LuaMod::m_script_hook_callbacks.find(to_system(unreal_function->GetFullName())); callback_data_it != LuaMod::m_script_hook_callbacks.end()) { - Output::send(STR("Unregistering script hook with id: {}\n"), post_id); + Output::send(SYSSTR("Unregistering script hook with id: {}\n"), post_id); auto& registry_indexes = callback_data_it->second.registry_indexes; std::erase_if(registry_indexes, [&](const auto& pair) -> bool { return post_id == pair.second.identifier; @@ -1437,15 +1461,15 @@ No overload found for function 'UnregisterHook'. { lua.throw_error("Could not dump objects and properties because the pointer to 'Mod' was nullptr"); } - UE4SSProgram::dump_all_objects_and_properties(mod->m_program.get_object_dumper_output_directory() + STR("\\") + - UE4SSProgram::m_object_dumper_file_name); + UE4SSProgram::dump_all_objects_and_properties( + to_system_string(std::filesystem::path{mod->m_program.get_object_dumper_output_directory()} / UE4SSProgram::m_object_dumper_file_name)); return 0; }); lua.register_function("GenerateSDK", []([[maybe_unused]] const LuaMadeSimple::Lua& lua) -> int { const Mod* mod = get_mod_ref(lua); - File::StringType working_dir{mod->m_program.get_working_directory()}; - mod->m_program.generate_cxx_headers(working_dir + STR("\\CXXHeaderDump")); + std::filesystem::path working_dir{mod->m_program.get_working_directory()}; + mod->m_program.generate_cxx_headers(working_dir / SYSSTR("CXXHeaderDump")); return 0; }); @@ -1617,9 +1641,9 @@ No overload found for function 'RegisterCustomProperty'. struct PropertyInfo { - std::wstring name{}; + SystemStringType name{}; PropertyTypeInfo type{}; // Figure out what to do here, it shouldn't be just a string - std::wstring belongs_to_class{}; + SystemStringType belongs_to_class{}; int32_t offset_internal{-1}; int32_t element_size{-1}; // Is this required for trivial types like integers and floats ? @@ -1718,12 +1742,12 @@ No overload found for function 'RegisterCustomProperty'. }; // Always required, for all property types - property_info.name = to_wstring(lua_table.get_string_field("Name")); + property_info.name = to_system(lua_table.get_string_field("Name")); property_info.type.name = lua_table.get_table_field("Type").get_string_field("Name"); property_info.type.size = verify_and_convert_int64_to_int32("Type", "Size"); property_info.type.ffieldclass_pointer = reinterpret_cast(lua_table.get_table_field("Type").get_int_field("FFieldClassPointer")); property_info.type.static_pointer = reinterpret_cast(lua_table.get_table_field("Type").get_int_field("StaticPointer")); - property_info.belongs_to_class = to_wstring(lua_table.get_string_field("BelongsToClass")); + property_info.belongs_to_class = to_system(lua_table.get_string_field("BelongsToClass")); std::string oi_property_name; int32_t oi_relative_offset{}; @@ -1766,7 +1790,7 @@ No overload found for function 'RegisterCustomProperty'. lua.throw_error("Parameter #1 for function 'RegisterCustomProperty'. The table is missing required fields."); } - Unreal::UClass* belongs_to_class = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, property_info.belongs_to_class); + Unreal::UClass* belongs_to_class = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_ue(property_info.belongs_to_class)); if (!belongs_to_class) { lua.throw_error("Tried to 'RegisterCustomProperty' but 'BelongsToClass' could not be found"); @@ -1774,7 +1798,7 @@ No overload found for function 'RegisterCustomProperty'. if (property_info.offset_internal_is_table) { - auto name = Unreal::FName(to_wstring(oi_property_name)); + auto name = Unreal::FName(to_ue(oi_property_name)); Unreal::FProperty* oi_property = belongs_to_class->FindProperty(name); if (!oi_property) { @@ -1811,14 +1835,14 @@ No overload found for function 'RegisterCustomProperty'. printf_s("Registered Custom Property\n"); printf_s("PropertyInfo {\n"); - printf_s("\tName: %S\n", property_info.name.c_str()); + printf_s("\tName: " SystemStringPrint "\n", property_info.name.c_str()); printf_s("\tType {\n"); printf_s("\t\tName: %s\n", property_info.type.name.data()); printf_s("\t\tSize: 0x%X\n", property_info.type.size); printf_s("\t\tFFieldClassPointer: 0x%p\n", property_info.type.ffieldclass_pointer); printf_s("\t\tStaticPointer: 0x%p\n", property_info.type.static_pointer); printf_s("\t}\n"); - printf_s("\tBelongsToClass: %S\n", property_info.belongs_to_class.c_str()); + printf_s("\tBelongsToClass: " SystemStringPrint "\n", property_info.belongs_to_class.c_str()); printf_s("\tOffsetInternal: 0x%X\n", property_info.offset_internal); if (property_info.is_array_property) @@ -1870,7 +1894,7 @@ No overload found for function 'NotifyOnNewObject'. lua.throw_error(error_overload_not_found); } - std::wstring class_name = to_wstring(lua.get_string()); + auto class_name = to_system(lua.get_string()); if (!lua.is_function()) { @@ -1888,7 +1912,7 @@ No overload found for function 'NotifyOnNewObject'. const auto func_ref = hook_lua->registry().make_ref(); const auto thread_ref = mod->lua().registry().make_ref(); - Unreal::UClass* instance_of_class = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, class_name); + Unreal::UClass* instance_of_class = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_ue(class_name)); LuaMod::m_static_construct_object_lua_callbacks.emplace_back(LuaMod::LuaCancellableCallbackData{hook_lua, instance_of_class, func_ref, thread_ref}); @@ -1906,7 +1930,7 @@ No overload found for function 'RegisterCustomEvent'. lua.throw_error(error_overload_not_found); } - std::wstring event_name = to_wstring(lua.get_string()); + auto event_name = to_system(lua.get_string()); if (!lua.is_function()) { @@ -1942,7 +1966,7 @@ No overload found for function 'UnregisterCustomEvent'. { lua.throw_error(error_overload_not_found); } - auto custom_event_name = to_wstring(lua.get_string()); + auto custom_event_name = to_system_string(lua.get_string()); LuaMod::m_custom_event_callbacks.erase(custom_event_name); @@ -2121,18 +2145,18 @@ No overload found for function 'IterateGameDirectories'. Overloads: #1: IterateGameDirectories()"}; - std::filesystem::path game_executable_directory = UE4SSProgram::get_program().get_game_executable_directory(); - auto game_content_dir = game_executable_directory.parent_path().parent_path() / "Content"; + std::filesystem::path game_exe_directory = UE4SSProgram::get_program().get_game_directory(); + auto game_content_dir = game_exe_directory.parent_path().parent_path() / "Content"; if (!std::filesystem::exists(game_content_dir)) { - Output::send(STR("IterateGameDirectories: Could not locate the root directory because the directory structure is unknown " - "(not /Game/Binaries/Win64)\n")); + Output::send(SYSSTR("IterateGameDirectories: Could not locate the root directory because the directory structure is unknown " + "(not /Game/Binaries/Win64)\n")); lua.set_nil(); return 1; } - auto game_name = game_executable_directory.parent_path().parent_path().stem(); - auto game_root_directory = game_executable_directory.parent_path().parent_path().parent_path(); + auto game_name = game_exe_directory.parent_path().parent_path().stem(); + auto game_root_directory = game_exe_directory.parent_path().parent_path().parent_path(); auto directories_table = lua.prepare_new_table(); std::function iterate_directory = @@ -2237,30 +2261,30 @@ No overload found for function 'CreateLogicModsDirectory'. Overloads: #1: CreateLogicModsDirectory()"}; - std::filesystem::path game_executable_directory = UE4SSProgram::get_program().get_game_executable_directory(); - auto game_content_dir = game_executable_directory.parent_path().parent_path() / "Content"; + std::filesystem::path game_exe_directory = UE4SSProgram::get_program().get_game_directory(); + auto game_content_dir = game_exe_directory.parent_path().parent_path() / "Content"; if (!std::filesystem::exists(game_content_dir)) { lua.throw_error("CreateLogicModsDirectory: Could not locate the \"Content\" directory because the directory structure is unknown (not " "/Game/Content)\n"); } - auto logic_mods_dir = game_content_dir / "Paks/LogicMods"; + auto logic_mods_dir = game_content_dir / "Paks" / "LogicMods"; if (std::filesystem::exists(logic_mods_dir)) { Output::send( - STR("CreateLogicModsDirectory: \"LogicMods\" directory already exists. Cancelling creation of new directory.\n")); + SYSSTR("CreateLogicModsDirectory: \"LogicMods\" directory already exists. Cancelling creation of new directory.\n")); lua.set_bool(false); return 1; } - Output::send(STR("CreateLogicModsDirectory: LogicMods directory not found. Creating LogicMods directory.\n")); + Output::send(SYSSTR("CreateLogicModsDirectory: LogicMods directory not found. Creating LogicMods directory.\n")); auto new_logic_mods_dir = std::filesystem::create_directory(logic_mods_dir); if (!new_logic_mods_dir) { lua.throw_error("CreateLogicModsDirectory: Unable to create \"LogicMods\" directory. Try creating manually.\n"); } - Output::send(STR("CreateLogicModsDirectory: LogicMods directory created.\n")); + Output::send(SYSSTR("CreateLogicModsDirectory: LogicMods directory created.\n")); lua.set_bool(true); return 1; @@ -2486,7 +2510,7 @@ No overload found for function 'RegisterConsoleCommandGlobalHandler'. { throw std::runtime_error{error_overload_not_found}; } - auto command_name = to_wstring(lua.get_string()); + auto command_name = to_system_string(lua.get_string()); if (!lua.is_function()) { @@ -2521,7 +2545,7 @@ No overload found for function 'RegisterConsoleCommandHandler'. { throw std::runtime_error{error_overload_not_found}; } - auto command_name = to_wstring(lua.get_string()); + auto command_name = to_system_string(lua.get_string()); if (!lua.is_function()) { @@ -2561,7 +2585,7 @@ No overload found for function 'LoadAsset'. { throw std::runtime_error{error_overload_not_found}; } - auto asset_path_and_name = Unreal::FName(to_wstring(lua.get_string()), Unreal::FNAME_Add); + auto asset_path_and_name = Unreal::FName(to_ue(lua.get_string()), Unreal::FNAME_Add); auto* asset_registry = static_cast(Unreal::UAssetRegistryHelpers::GetAssetRegistry().ObjectPointer); if (!asset_registry) @@ -2580,11 +2604,11 @@ No overload found for function 'LoadAsset'. if (loaded_asset) { did_asset_load = true; - Output::send(STR("Asset loaded\n")); + Output::send(SYSSTR("Asset loaded\n")); } else { - Output::send(STR("Asset was found but not loaded, could be a package\n")); + Output::send(SYSSTR("Asset was found but not loaded, could be a package\n")); } } @@ -2612,7 +2636,7 @@ No overload found for function 'FindObject'. bool could_be_in_class{}; if (lua.is_string()) { - object_class_name = Unreal::FName(to_wstring(lua.get_string()), Unreal::FNAME_Add); + object_class_name = Unreal::FName(to_ue(lua.get_string()), Unreal::FNAME_Add); } else if (lua.is_userdata()) { @@ -2655,7 +2679,7 @@ No overload found for function 'FindObject'. bool could_be_object_short_name{}; if (lua.is_string()) { - object_short_name = Unreal::FName(to_wstring(lua.get_string()), Unreal::FNAME_Add); + object_short_name = Unreal::FName(to_ue(lua.get_string()), Unreal::FNAME_Add); could_be_object_short_name = true; } else if (lua.is_userdata()) @@ -2739,7 +2763,7 @@ No overload found for function 'FindObject'. if (could_be_in_class && could_be_in_outer && could_be_in_name) { - LuaType::auto_construct_object(lua, Unreal::UObjectGlobals::FindObject(in_class, in_outer, to_wstring(in_name), exact_class)); + LuaType::auto_construct_object(lua, Unreal::UObjectGlobals::FindObject(in_class, in_outer, to_ue(in_name), exact_class)); } else { @@ -2786,7 +2810,7 @@ No overload found for function 'FindObjects'. bool object_class_name_supplied{true}; if (lua.is_string()) { - object_class_name = Unreal::FName(to_wstring(lua.get_string()), Unreal::FNAME_Add); + object_class_name = Unreal::FName(to_ue(lua.get_string()), Unreal::FNAME_Add); } else if (lua.is_userdata()) { @@ -2824,7 +2848,7 @@ No overload found for function 'FindObjects'. Unreal::FName object_short_name{}; if (lua.is_string()) { - object_short_name = Unreal::FName(to_wstring(lua.get_string()), Unreal::FNAME_Add); + object_short_name = Unreal::FName(to_ue(lua.get_string()), Unreal::FNAME_Add); } else if (lua.is_userdata()) { @@ -2966,8 +2990,8 @@ No overload found for function 'RegisterHook'. lua.throw_error(error_overload_not_found); } - std::wstring function_name = to_wstring(lua.get_string()); - std::wstring function_name_no_prefix = function_name.substr(function_name.find_first_of(L"/"), function_name.size()); + auto function_name = to_system(lua.get_string()); + auto function_name_no_prefix = function_name.substr(function_name.find_first_of(SYSSTR("/")), function_name.size()); if (!lua.is_function()) { @@ -2997,7 +3021,7 @@ No overload found for function 'RegisterHook'. has_post_callback = true; } - Unreal::UFunction* unreal_function = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, function_name_no_prefix); + Unreal::UFunction* unreal_function = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, to_ue(function_name_no_prefix)); if (!unreal_function) { lua.throw_error("Tried to register a hook with Lua function 'RegisterHook' but no UFunction with the specified name was found."); @@ -3020,24 +3044,24 @@ No overload found for function 'RegisterHook'. generic_pre_id = m_last_generic_hook_id; m_generic_hook_id_to_native_hook_id.emplace(++m_last_generic_hook_id, post_id); generic_post_id = m_last_generic_hook_id; - Output::send(STR("[RegisterHook] Registered native hook ({}, {}) for {}\n"), + Output::send(SYSSTR("[RegisterHook] Registered native hook ({}, {}) for {}\n"), generic_pre_id, generic_post_id, - unreal_function->GetFullName()); + to_system(unreal_function->GetFullName())); } else if (func_ptr && func_ptr == Unreal::UObject::ProcessInternalInternal.get_function_address() && !unreal_function->HasAnyFunctionFlags(Unreal::EFunctionFlags::FUNC_Native)) { ++m_last_generic_hook_id; - auto [callback_data, _] = LuaMod::m_script_hook_callbacks.emplace(unreal_function->GetFullName(), LuaCallbackData{*hook_lua, nullptr, {}}); + auto [callback_data, _] = LuaMod::m_script_hook_callbacks.emplace(to_system(unreal_function->GetFullName()), LuaCallbackData{lua, nullptr, {}}); callback_data->second.registry_indexes.emplace_back(hook_lua, LuaMod::LuaCallbackData::RegistryIndex{lua_callback_registry_index, m_last_generic_hook_id}); generic_pre_id = m_last_generic_hook_id; generic_post_id = m_last_generic_hook_id; - Output::send(STR("[RegisterHook] Registered script hook ({}, {}) for {}\n"), + Output::send(SYSSTR("[RegisterHook] Registered script hook ({}, {}) for {}\n"), generic_pre_id, generic_post_id, - unreal_function->GetFullName()); + to_system(unreal_function->GetFullName())); } else { @@ -3249,8 +3273,8 @@ No overload found for function 'FPackageName:IsShortPackageName'. { lua.throw_error(error_overload_not_found); } - - File::StringType PossiblyLongName = to_wstring(lua.get_string()); + auto local_str = to_ue(lua.get_string()); + UEStringViewType PossiblyLongName = local_str; lua.set_bool(Unreal::FPackageName::IsShortPackageName(PossiblyLongName)); return 1; @@ -3266,8 +3290,8 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. { lua.throw_error(error_overload_not_found); } - - File::StringType InLongPackageName = to_wstring(lua.get_string()); + auto local_str = to_ue(lua.get_string()); + UEStringViewType InLongPackageName = local_str; lua.set_bool(Unreal::FPackageName::IsValidLongPackageName(InLongPackageName)); return 1; @@ -3314,7 +3338,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. return; } - for (const auto& mod : UE4SSProgram::m_mods) + for (const auto& mod : UE4SSProgram::get_program().m_mods) { if (auto cpp_mod = dynamic_cast(mod.get()); cpp_mod && mod->is_started()) { @@ -3334,7 +3358,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. return; } - for (const auto& mod : UE4SSProgram::m_mods) + for (const auto& mod : UE4SSProgram::get_program().m_mods) { if (auto cpp_mod = dynamic_cast(mod.get()); cpp_mod && mod->is_started()) { @@ -3364,11 +3388,11 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. // Don't crash on syntax errors. try { - main_lua()->execute_file(m_scripts_path + L"\\main.lua"); + main_lua()->execute_file(to_system_string(std::filesystem::path{m_scripts_path} / SYSSTR("main.lua"))); } catch (std::runtime_error& e) { - Output::send(STR("{}\n"), to_wstring(e.what())); + Output::send(SYSSTR("{}\n"), to_system(e.what())); } } @@ -3377,7 +3401,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. // ProcessEvent hook may try to run, and the lua state will not be valid std::lock_guard guard{LuaMod::m_thread_actions_mutex}; - Output::send(STR("Stopping mod '{}' for uninstall\n"), m_mod_name); + Output::send(SYSSTR("Stopping mod '{}' for uninstall\n"), m_mod_name); fire_on_lua_stop_for_cpp_mods(); @@ -3408,8 +3432,14 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. std::erase_if(g_hooked_script_function_data, [&](std::unique_ptr& item) -> bool { if (item->mod == this) { - Output::send(STR("\tUnregistering hook by id '{}#{}' for mod {}\n"), item->unreal_function->GetName(), item->pre_callback_id, item->mod->get_name()); - Output::send(STR("\tUnregistering hook by id '{}#{}' for mod {}\n"), item->unreal_function->GetName(), item->post_callback_id, item->mod->get_name()); + Output::send(SYSSTR("\tUnregistering hook by id '{}#{}' for mod {}\n"), + to_system(item->unreal_function->GetName()), + item->pre_callback_id, + item->mod->get_name()); + Output::send(SYSSTR("\tUnregistering hook by id '{}#{}' for mod {}\n"), + to_system(item->unreal_function->GetName()), + item->post_callback_id, + item->mod->get_name()); item->unreal_function->UnregisterHook(item->pre_callback_id); item->unreal_function->UnregisterHook(item->post_callback_id); return true; @@ -3465,12 +3495,13 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. { std::lock_guard guard{LuaMod::m_thread_actions_mutex}; - auto execute_hook = [&](std::unordered_map& callback_container, bool precise_name_match) { + auto execute_hook = [&](std::unordered_map& callback_container, bool precise_name_match) { if (callback_container.empty()) { return; } - if (auto it = callback_container.find(precise_name_match ? Stack.Node()->GetFullName() : Stack.Node()->GetName()); it != callback_container.end()) + if (auto it = callback_container.find(precise_name_match ? to_system(Stack.Node()->GetFullName()) : to_system(Stack.Node()->GetName())); + it != callback_container.end()) { const auto& callback_data = it->second; for (const auto& [lua_ptr, registry_index] : callback_data.registry_indexes) @@ -3564,13 +3595,10 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. } else { - std::wstring return_property_type_name = return_property_type.ToString(); - std::wstring return_property_name = return_property->GetName(); - - Output::send(STR("Tried altering return value of a custom BP function without a registered handler for return type Return " - "property '{}' of type '{}' not supported."), - return_property_name, - return_property_type_name); + Output::send(SYSSTR("Tried altering return value of a custom BP function without a registered handler for return type Return " + "property '{}' of type '{}' not supported."), + return_property->GetName(), + return_property_type.ToString()); } } } @@ -3779,7 +3807,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. } catch (std::runtime_error& e) { - Output::send(STR("{}\n"), to_wstring(e.what())); + Output::send(SYSSTR("{}\n"), e.what()); } if (cancel) @@ -3798,8 +3826,10 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. return constructed_object; }); - Unreal::Hook::RegisterULocalPlayerExecPreCallback([](Unreal::ULocalPlayer* context, Unreal::UWorld* in_world, const TCHAR* cmd, Unreal::FOutputDevice& ar) - -> Unreal::Hook::ULocalPlayerExecCallbackReturnValue { + Unreal::Hook::RegisterULocalPlayerExecPreCallback([](Unreal::ULocalPlayer* context, + Unreal::UWorld* in_world, + const RC::Unreal::TCHAR* cmd, + Unreal::FOutputDevice& ar) -> Unreal::Hook::ULocalPlayerExecCallbackReturnValue { return TRY([&] { for (const auto& callback_data : m_local_player_exec_pre_callbacks) { @@ -3814,7 +3844,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. static auto s_object_property_name = Unreal::FName(STR("ObjectProperty")); LuaType::RemoteUnrealParam::construct(callback_data.lua, &context, s_object_property_name); LuaType::RemoteUnrealParam::construct(callback_data.lua, &in_world, s_object_property_name); - callback_data.lua.set_string(to_string(cmd)); + callback_data.lua.set_string(to_string((UECharType*)cmd)); LuaType::FOutputDevice::construct(callback_data.lua, &ar); callback_data.lua.call_function(4, 2); @@ -3858,8 +3888,10 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. }); }); - Unreal::Hook::RegisterULocalPlayerExecPostCallback([](Unreal::ULocalPlayer* context, Unreal::UWorld* in_world, const TCHAR* cmd, Unreal::FOutputDevice& ar) - -> Unreal::Hook::ULocalPlayerExecCallbackReturnValue { + Unreal::Hook::RegisterULocalPlayerExecPostCallback([](Unreal::ULocalPlayer* context, + Unreal::UWorld* in_world, + const RC::Unreal::TCHAR* cmd, + Unreal::FOutputDevice& ar) -> Unreal::Hook::ULocalPlayerExecCallbackReturnValue { return TRY([&] { for (const auto& callback_data : m_local_player_exec_post_callbacks) { @@ -3874,7 +3906,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. static auto s_object_property_name = Unreal::FName(STR("ObjectProperty")); LuaType::RemoteUnrealParam::construct(callback_data.lua, &context, s_object_property_name); LuaType::RemoteUnrealParam::construct(callback_data.lua, &in_world, s_object_property_name); - callback_data.lua.set_string(to_string(cmd)); + callback_data.lua.set_string(to_string((UECharType*)cmd)); LuaType::FOutputDevice::construct(callback_data.lua, &ar); callback_data.lua.call_function(4, 2); @@ -3919,7 +3951,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. }); Unreal::Hook::RegisterCallFunctionByNameWithArgumentsPreCallback( - [](Unreal::UObject* context, const TCHAR* str, Unreal::FOutputDevice& ar, Unreal::UObject* executor, bool b_force_call_with_non_exec) + [](Unreal::UObject* context, const RC::Unreal::TCHAR* str, Unreal::FOutputDevice& ar, Unreal::UObject* executor, bool b_force_call_with_non_exec) -> std::pair { return TRY([&] { for (const auto& callback_data : m_call_function_by_name_with_arguments_pre_callbacks) @@ -3934,7 +3966,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. static auto s_object_property_name = Unreal::FName(STR("ObjectProperty")); LuaType::RemoteUnrealParam::construct(callback_data.lua, &context, s_object_property_name); - callback_data.lua.set_string(to_string(str)); + callback_data.lua.set_string(to_string((UECharType*)str)); LuaType::FOutputDevice::construct(callback_data.lua, &ar); LuaType::RemoteUnrealParam::construct(callback_data.lua, &executor, s_object_property_name); callback_data.lua.set_bool(b_force_call_with_non_exec); @@ -3967,7 +3999,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. }); Unreal::Hook::RegisterCallFunctionByNameWithArgumentsPostCallback( - [](Unreal::UObject* context, const TCHAR* str, Unreal::FOutputDevice& ar, Unreal::UObject* executor, bool b_force_call_with_non_exec) + [](Unreal::UObject* context, const RC::Unreal::TCHAR* str, Unreal::FOutputDevice& ar, Unreal::UObject* executor, bool b_force_call_with_non_exec) -> std::pair { return TRY([&] { for (const auto& callback_data : m_call_function_by_name_with_arguments_post_callbacks) @@ -3982,7 +4014,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. static auto s_object_property_name = Unreal::FName(STR("ObjectProperty")); LuaType::RemoteUnrealParam::construct(callback_data.lua, &context, s_object_property_name); - callback_data.lua.set_string(to_string(str)); + callback_data.lua.set_string(to_string((UECharType*)str)); LuaType::FOutputDevice::construct(callback_data.lua, &ar); LuaType::RemoteUnrealParam::construct(callback_data.lua, &executor, s_object_property_name); callback_data.lua.set_bool(b_force_call_with_non_exec); @@ -4015,91 +4047,92 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. }); // Lua from the in-game console. - Unreal::Hook::RegisterProcessConsoleExecCallback([](Unreal::UObject* context, const TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> bool { - auto logln = [&ar](const File::StringType& log_message) { - Output::send(std::format(STR("{}\n"), log_message)); - ar.Log(log_message.c_str()); - }; + Unreal::Hook::RegisterProcessConsoleExecCallback( + [](Unreal::UObject* context, const RC::Unreal::TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> bool { + auto logln = [&ar](const SystemStringType& log_message) { + Output::send(std::format(SYSSTR("{}\n"), log_message)); + ar.Log((const RC::Unreal::TCHAR*)log_message.c_str()); + }; - if (!LuaStatics::console_executor_enabled && String::iequal(File::StringViewType{cmd}, STR("luastart"))) - { - start_console_lua_executor(); - logln(STR("Console Lua executor started")); - return true; - } - else if (LuaStatics::console_executor_enabled && String::iequal(File::StringViewType{cmd}, STR("luastop"))) - { - stop_console_lua_executor(); - logln(STR("Console Lua executor stopped")); - return true; - } - else if (LuaStatics::console_executor_enabled && String::iequal(File::StringViewType{cmd}, STR("luarestart"))) - { - stop_console_lua_executor(); - start_console_lua_executor(); - logln(STR("Console Lua executor restarted")); - return true; - } - else if (String::iequal(File::StringViewType{cmd}, STR("clear"))) - { - // TODO: Replace with proper implementation when we have UGameViewportClient and UConsole. - // This should be fairly cross-game & cross-engine-version compatible even without the proper implementation. - // This is because I don't think they've changed the layout here and we have a reflected property right before the unreflected one that we're looking for. - Unreal::UObject** console = static_cast(context->GetValuePtrByPropertyName(STR("ViewportConsole"))); - auto* default_texture_white = std::bit_cast*>( - static_cast((*console)->GetValuePtrByPropertyNameInChain(STR("DefaultTexture_White"))) + 0x8); - auto* scrollback = std::bit_cast(std::bit_cast(default_texture_white) + 0x10); - default_texture_white->SetNum(0); - default_texture_white->SetMax(0); - *scrollback = 0; - return true; - } - else if (LuaStatics::console_executor_enabled) - { - if (!LuaStatics::console_executor) - { - logln(STR("Console Lua executor is enabled but the Lua instance is nullptr. Please try run RC_LUA_START again.")); - return true; - } + if (!LuaStatics::console_executor_enabled && String::iequal(UEStringViewType{(UECharType*)cmd}, STR("luastart"))) + { + start_console_lua_executor(); + logln(SYSSTR("Console Lua executor started")); + return true; + } + else if (LuaStatics::console_executor_enabled && String::iequal(UEStringViewType{(UECharType*)cmd}, STR("luastop"))) + { + stop_console_lua_executor(); + logln(SYSSTR("Console Lua executor stopped")); + return true; + } + else if (LuaStatics::console_executor_enabled && String::iequal(UEStringViewType{(UECharType*)cmd}, STR("luarestart"))) + { + stop_console_lua_executor(); + start_console_lua_executor(); + logln(SYSSTR("Console Lua executor restarted")); + return true; + } + else if (String::iequal(UEStringViewType{(UECharType*)cmd}, STR("clear"))) + { + // TODO: Replace with proper implementation when we have UGameViewportClient and UConsole. + // This should be fairly cross-game & cross-engine-version compatible even without the proper implementation. + // This is because I don't think they've changed the layout here and we have a reflected property right before the unreflected one that we're looking for. + Unreal::UObject** console = static_cast(context->GetValuePtrByPropertyName(STR("ViewportConsole"))); + auto* default_texture_white = std::bit_cast*>( + static_cast((*console)->GetValuePtrByPropertyNameInChain(STR("DefaultTexture_White"))) + 0x8); + auto* scrollback = std::bit_cast(std::bit_cast(default_texture_white) + 0x10); + default_texture_white->SetNum(0); + default_texture_white->SetMax(0); + *scrollback = 0; + return true; + } + else if (LuaStatics::console_executor_enabled) + { + if (!LuaStatics::console_executor) + { + logln(SYSSTR("Console Lua executor is enabled but the Lua instance is nullptr. Please try run RC_LUA_START again.")); + return true; + } - LuaLibrary::set_outputdevice_ref(*LuaStatics::console_executor, &ar); + LuaLibrary::set_outputdevice_ref(*LuaStatics::console_executor, &ar); - // logln(std::format(STR("Executing '{}' as Lua"), cmd)); + // logln(std::format(SYSSTR("Executing '{}' as Lua"), cmd)); - try - { - if (int status = luaL_loadstring(LuaStatics::console_executor->get_lua_state(), to_string(cmd).c_str()); status != LUA_OK) - { - LuaStatics::console_executor->throw_error( - std::format("luaL_loadstring returned {}", LuaStatics::console_executor->resolve_status_message(status, true))); - } + try + { + if (int status = luaL_loadstring(LuaStatics::console_executor->get_lua_state(), to_string((UECharType*)cmd).c_str()); status != LUA_OK) + { + LuaStatics::console_executor->throw_error( + std::format("luaL_loadstring returned {}", LuaStatics::console_executor->resolve_status_message(status, true))); + } - if (int status = lua_pcall(LuaStatics::console_executor->get_lua_state(), 0, LUA_MULTRET, 0); status != LUA_OK) + if (int status = lua_pcall(LuaStatics::console_executor->get_lua_state(), 0, LUA_MULTRET, 0); status != LUA_OK) + { + LuaStatics::console_executor->throw_error( + std::format("lua_pcall returned {}", LuaStatics::console_executor->resolve_status_message(status, true))); + } + } + catch (std::runtime_error& e) + { + logln(to_system_string(e.what())); + } + + // We always return true when the console Lua executor is enabled in order to suppress other handlers + return true; + } + else { - LuaStatics::console_executor->throw_error( - std::format("lua_pcall returned {}", LuaStatics::console_executor->resolve_status_message(status, true))); + return false; } - } - catch (std::runtime_error& e) - { - logln(to_wstring(e.what())); - } - - // We always return true when the console Lua executor is enabled in order to suppress other handlers - return true; - } - else - { - return false; - } - }); + }); // RegisterProcessConsoleExecPreHook Unreal::Hook::RegisterProcessConsoleExecGlobalPreCallback( - [](Unreal::UObject* context, const TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> std::pair { + [](Unreal::UObject* context, const RC::Unreal::TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> std::pair { return TRY([&] { - auto command = File::StringViewType{cmd}; - auto command_parts = explode_by_occurrence(cmd, ' '); + auto command = UEStringViewType{(const UECharType*)cmd}; + auto command_parts = explode_by_occurrence((const UECharType*)cmd, ' '); for (const auto& callback_data : m_process_console_exec_pre_callbacks) { @@ -4152,10 +4185,10 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. // RegisterProcessConsoleExecPostHook Unreal::Hook::RegisterProcessConsoleExecGlobalPostCallback( - [](Unreal::UObject* context, const TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> std::pair { + [](Unreal::UObject* context, const RC::Unreal::TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> std::pair { return TRY([&] { - auto command = File::StringViewType{cmd}; - auto command_parts = explode_by_occurrence(cmd, ' '); + auto command = UEStringViewType{(const UECharType*)cmd}; + auto command_parts = explode_by_occurrence((const UECharType*)cmd, ' '); for (const auto& callback_data : m_process_console_exec_post_callbacks) { @@ -4207,138 +4240,140 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. }); // RegisterConsoleCommandHandler - Unreal::Hook::RegisterProcessConsoleExecCallback([](Unreal::UObject* context, const TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> bool { - (void)executor; + Unreal::Hook::RegisterProcessConsoleExecCallback( + [](Unreal::UObject* context, const RC::Unreal::TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> bool { + (void)executor; - if (!Unreal::Cast(context)) - { - return false; - } + if (!Unreal::Cast(context)) + { + return false; + } - return TRY([&] { - auto command = File::StringViewType{cmd}; - auto command_parts = explode_by_occurrence(cmd, ' '); - File::StringType command_name; - if (command_parts.size() > 1) - { - command_name = command_parts[0]; - } - else - { - command_name = command; - } + return TRY([&] { + auto command = to_system_string(cmd); + auto command_parts = explode_by_occurrence(command, ' '); + SystemStringType command_name; + if (command_parts.size() > 1) + { + command_name = command_parts[0]; + } + else + { + command_name = command; + } - if (auto it = m_custom_command_lua_pre_callbacks.find(command_name); it != m_custom_command_lua_pre_callbacks.end()) - { - const auto& callback_data = it->second; + if (auto it = m_custom_command_lua_pre_callbacks.find(command_name); it != m_custom_command_lua_pre_callbacks.end()) + { + const auto& callback_data = it->second; - // This is a promise that we're in the game thread, used by other functions to ensure that we don't execute when unsafe - set_is_in_game_thread(callback_data.lua, true); + // This is a promise that we're in the game thread, used by other functions to ensure that we don't execute when unsafe + set_is_in_game_thread(callback_data.lua, true); - bool return_value{}; + bool return_value{}; - for (const auto& [lua, registry_index] : callback_data.registry_indexes) - { - callback_data.lua.registry().get_function_ref(registry_index.lua_index); - callback_data.lua.set_string(to_string(command)); + for (const auto& [lua, registry_index] : callback_data.registry_indexes) + { + callback_data.lua.registry().get_function_ref(registry_index.lua_index); + callback_data.lua.set_string(to_string(command)); - auto params_table = callback_data.lua.prepare_new_table(); - for (size_t i = 1; i < command_parts.size(); ++i) - { - const auto& command_part = command_parts[i]; - params_table.add_pair(i, to_string(command_part).c_str()); - } + auto params_table = callback_data.lua.prepare_new_table(); + for (size_t i = 1; i < command_parts.size(); ++i) + { + const auto& command_part = command_parts[i]; + params_table.add_pair(i, to_string(command_part).c_str()); + } - LuaType::FOutputDevice::construct(callback_data.lua, &ar); + LuaType::FOutputDevice::construct(callback_data.lua, &ar); - callback_data.lua.call_function(3, 1); + callback_data.lua.call_function(3, 1); - if (!callback_data.lua.is_bool()) - { - throw std::runtime_error{"A custom console command handle must return true or false"}; - } + if (!callback_data.lua.is_bool()) + { + throw std::runtime_error{"A custom console command handle must return true or false"}; + } - return_value = callback_data.lua.get_bool(); - } - // No longer promising to be in the game thread - set_is_in_game_thread(callback_data.lua, false); + return_value = callback_data.lua.get_bool(); + } + // No longer promising to be in the game thread + set_is_in_game_thread(callback_data.lua, false); - return return_value; - } + return return_value; + } - return false; - }); - }); + return false; + }); + }); // RegisterConsoleCommandGlobalHandler - Unreal::Hook::RegisterProcessConsoleExecCallback([](Unreal::UObject* context, const TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> bool { - (void)context; - (void)executor; + Unreal::Hook::RegisterProcessConsoleExecCallback( + [](Unreal::UObject* context, const RC::Unreal::TCHAR* cmd, Unreal::FOutputDevice& ar, Unreal::UObject* executor) -> bool { + (void)context; + (void)executor; - return TRY([&] { - auto command = File::StringViewType{cmd}; - auto command_parts = explode_by_occurrence(cmd, ' '); - File::StringType command_name; - if (command_parts.size() > 1) - { - command_name = command_parts[0]; - } - else - { - command_name = command; - } + return TRY([&] { + auto command = to_system_string(cmd); + auto command_parts = explode_by_occurrence(command, SYSSTR(' ')); + SystemStringType command_name; + if (command_parts.size() > 1) + { + command_name = command_parts[0]; + } + else + { + command_name = command; + } - if (auto it = m_global_command_lua_callbacks.find(command_name); it != m_global_command_lua_callbacks.end()) - { - const auto& callback_data = it->second; + if (auto it = m_global_command_lua_callbacks.find(command_name); it != m_global_command_lua_callbacks.end()) + { + const auto& callback_data = it->second; - // This is a promise that we're in the game thread, used by other functions to ensure that we don't execute when unsafe - set_is_in_game_thread(callback_data.lua, true); + // This is a promise that we're in the game thread, used by other functions to ensure that we don't execute when unsafe + set_is_in_game_thread(callback_data.lua, true); - bool return_value{}; + bool return_value{}; - for (const auto& [lua, registry_index] : callback_data.registry_indexes) - { - callback_data.lua.registry().get_function_ref(registry_index.lua_index); - callback_data.lua.set_string(to_string(command)); + for (const auto& [lua, registry_index] : callback_data.registry_indexes) + { + callback_data.lua.registry().get_function_ref(registry_index.lua_index); + callback_data.lua.set_string(to_string(command)); - auto params_table = callback_data.lua.prepare_new_table(); - for (size_t i = 1; i < command_parts.size(); ++i) - { - const auto& command_part = command_parts[i]; - params_table.add_pair(i, to_string(command_part).c_str()); - } + auto params_table = callback_data.lua.prepare_new_table(); + for (size_t i = 1; i < command_parts.size(); ++i) + { + const auto& command_part = command_parts[i]; + params_table.add_pair(i, to_string(command_part).c_str()); + } - LuaType::FOutputDevice::construct(callback_data.lua, &ar); + LuaType::FOutputDevice::construct(callback_data.lua, &ar); - callback_data.lua.call_function(3, 1); + callback_data.lua.call_function(3, 1); - if (!callback_data.lua.is_bool()) - { - throw std::runtime_error{"A custom console command handle must return true or false"}; - } + if (!callback_data.lua.is_bool()) + { + throw std::runtime_error{"A custom console command handle must return true or false"}; + } - return_value = callback_data.lua.get_bool(); - } + return_value = callback_data.lua.get_bool(); + } - // No longer promising to be in the game thread - set_is_in_game_thread(callback_data.lua, false); + // No longer promising to be in the game thread + set_is_in_game_thread(callback_data.lua, false); - return return_value; - } + return return_value; + } - return false; - }); - }); + return false; + }); + }); if (Unreal::UObject::ProcessLocalScriptFunctionInternal.is_ready() && Unreal::Version::IsAtLeast(4, 22)) { - Output::send(STR("Enabling custom events\n")); + Output::send(SYSSTR("Enabling custom events\n")); Unreal::Hook::RegisterProcessLocalScriptFunctionPostCallback(script_hook); } else if (Unreal::UObject::ProcessInternalInternal.is_ready() && Unreal::Version::IsBelow(4, 22)) { - Output::send(STR("Enabling custom events\n")); + Output::send(SYSSTR("Enabling custom events\n")); Unreal::Hook::RegisterProcessInternalPostCallback(script_hook); } } @@ -4395,9 +4430,10 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. } catch (std::runtime_error& e) { - Output::send(STR("[{}] {}\n"), - to_wstring(action.type == LuaMod::ActionType::Loop ? "LoopAsync" : "DelayedAction"), - to_wstring(e.what())); + Output::send(SYSSTR("[{}] {}\n"), + to_system(action.type == LuaMod::ActionType::Loop ? SYSSTR("LoopAsync") + : SYSSTR("DelayedAction")), + to_system(e.what())); } return result; diff --git a/UE4SS/src/Mod/Mod.cpp b/UE4SS/src/Mod/Mod.cpp index 65550fa71..944cb43ec 100644 --- a/UE4SS/src/Mod/Mod.cpp +++ b/UE4SS/src/Mod/Mod.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -48,15 +47,14 @@ #include #pragma warning(default : 4005) - namespace RC { - Mod::Mod(UE4SSProgram& program, std::wstring&& mod_name, std::wstring&& mod_path) : m_program(program), m_mod_name(mod_name), m_mod_path(mod_path) + Mod::Mod(UE4SSProgram& program, SystemStringType&& mod_name, SystemStringType&& mod_path) : m_program(program), m_mod_name(mod_name), m_mod_path(mod_path) { } - auto Mod::get_name() const -> std::wstring_view + auto Mod::get_name() const -> SystemStringViewType { return m_mod_name; } diff --git a/UE4SS/src/ObjectDumper/ObjectToString.cpp b/UE4SS/src/ObjectDumper/ObjectToString.cpp index 22784d9e3..332874e4a 100644 --- a/UE4SS/src/ObjectDumper/ObjectToString.cpp +++ b/UE4SS/src/ObjectDumper/ObjectToString.cpp @@ -56,54 +56,54 @@ namespace RC::ObjectDumper return object_to_string_complex_functions.contains(hash); } - auto object_trivial_dump_to_string(void* p_this, std::wstring& out_line, const wchar_t* post_delimiter) -> void + auto object_trivial_dump_to_string(void* p_this, SystemStringType& out_line, const SystemCharType* post_delimiter) -> void { UObject* p_typed_this = static_cast(p_this); - out_line.append(std::format(L"[{:016X}] ", reinterpret_cast(p_this))); - out_line.append(p_typed_this->GetFullName()); - out_line.append(std::format(L" [n: {:X}] [c: {:016X}] [or: {:016X}]", + out_line.append(std::format(SYSSTR("[{:016X}] "), reinterpret_cast(p_this))); + out_line.append(to_system(p_typed_this->GetFullName())); + out_line.append(std::format(SYSSTR(" [n: {:X}] [c: {:016X}] [or: {:016X}]"), p_typed_this->GetNamePrivate().GetComparisonIndex(), reinterpret_cast(p_typed_this->GetClassPrivate()), reinterpret_cast(p_typed_this->GetOuterPrivate()))); } - auto object_to_string(void* p_this, std::wstring& out_line) -> void + auto object_to_string(void* p_this, SystemStringType& out_line) -> void { object_trivial_dump_to_string(p_this, out_line); } - auto property_trivial_dump_to_string(void* p_this, std::wstring& out_line) -> void + auto property_trivial_dump_to_string(void* p_this, SystemStringType& out_line) -> void { FProperty* p_typed_this = static_cast(p_this); - out_line.append(std::format(L"[{:016X}] ", reinterpret_cast(p_this))); - out_line.append(p_typed_this->GetFullName()); - out_line.append(std::format(L" [o: {:X}] ", p_typed_this->GetOffset_Internal())); + out_line.append(std::format(SYSSTR("[{:016X}] "), reinterpret_cast(p_this))); + out_line.append(to_system(p_typed_this->GetFullName())); + out_line.append(std::format(SYSSTR(" [o: {:X}] "), p_typed_this->GetOffset_Internal())); auto property_class = p_typed_this->GetClass(); - out_line.append(std::format(L"[n: {:X}] [c: {:016X}]", p_typed_this->GetFName().GetComparisonIndex(), property_class.HashObject())); + out_line.append(std::format(SYSSTR("[n: {:X}] [c: {:016X}]"), p_typed_this->GetFName().GetComparisonIndex(), property_class.HashObject())); if (Version::IsAtLeast(4, 25)) { - out_line.append(std::format(L" [owr: {:016X}]", p_typed_this->GetOwnerVariant().HashObject())); + out_line.append(std::format(SYSSTR(" [owr: {:016X}]"), p_typed_this->GetOwnerVariant().HashObject())); } } - auto property_to_string(void* p_this, std::wstring& out_line) -> void + auto property_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); } - auto arrayproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto arrayproperty_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); FArrayProperty* p_typed_this = static_cast(p_this); - out_line.append(std::format(L" [ai: {:016X}]", reinterpret_cast(p_typed_this->GetInner()))); + out_line.append(std::format(SYSSTR(" [ai: {:016X}]"), reinterpret_cast(p_typed_this->GetInner()))); } - auto arrayproperty_to_string_complex(void* p_this, std::wstring& out_line, ObjectToStringComplexDeclCallable callable) -> void + auto arrayproperty_to_string_complex(void* p_this, SystemStringType& out_line, ObjectToStringComplexDeclCallable callable) -> void { FProperty* array_inner = static_cast(p_this)->GetInner(); auto array_inner_class = array_inner->GetClass().HashObject(); @@ -116,7 +116,7 @@ namespace RC::ObjectDumper { // If this code is executed then we'll be having another line before we return to the dumper, so we need to explicitly add a new line // If this code is not executed then we'll not be having another line and the dumper will add the new line - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); get_to_string_complex(array_inner_class)(array_inner, out_line, [&]([[maybe_unused]] void* prop) { // It's possible that a new line is supposed to be appended here @@ -127,22 +127,23 @@ namespace RC::ObjectDumper } else { - out_line.append(array_inner->GetFullName()); + out_line.append(to_system(array_inner->GetFullName())); callable(array_inner); } } - auto mapproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto mapproperty_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); FMapProperty* typed_this = static_cast(p_this); FProperty* key_property = typed_this->GetKeyProp(); FProperty* value_property = typed_this->GetValueProp(); - out_line.append(std::format(L" [kp: {:016X}] [vp: {:016X}]", reinterpret_cast(key_property), reinterpret_cast(value_property))); + out_line.append( + std::format(SYSSTR(" [kp: {:016X}] [vp: {:016X}]"), reinterpret_cast(key_property), reinterpret_cast(value_property))); } - auto mapproperty_to_string_complex(void* p_this, std::wstring& out_line, ObjectToStringComplexDeclCallable callable) -> void + auto mapproperty_to_string_complex(void* p_this, SystemStringType& out_line, ObjectToStringComplexDeclCallable callable) -> void { FMapProperty* typed_this = static_cast(p_this); FProperty* key_property = typed_this->GetKeyProp(); @@ -159,7 +160,7 @@ namespace RC::ObjectDumper { // If this code is executed then we'll be having another line before we return to the dumper, so we need to explicitly add a new line // If this code is not executed then we'll not be having another line and the dumper will add the new line - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); get_to_string_complex(property_class)(property, out_line, [&]([[maybe_unused]] void* prop) {}); } @@ -168,7 +169,7 @@ namespace RC::ObjectDumper } else { - out_line.append(property->GetFullName()); + out_line.append(to_system(property->GetFullName())); callable(property); } }; @@ -177,83 +178,83 @@ namespace RC::ObjectDumper dump_property(value_property, value_property_class); } - auto classproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto classproperty_to_string(void* p_this, SystemStringType& out_line) -> void { FClassProperty* typed_this = static_cast(p_this); property_trivial_dump_to_string(p_this, out_line); // mc = MetaClass - out_line.append(std::format(L" [mc: {:016X}]", reinterpret_cast(typed_this->GetMetaClass()))); + out_line.append(std::format(SYSSTR(" [mc: {:016X}]"), reinterpret_cast(typed_this->GetMetaClass()))); } - auto delegateproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto delegateproperty_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); FDelegateProperty* p_typed_this = static_cast(p_this); - out_line.append(std::format(L" [df: {:016X}]", reinterpret_cast(p_typed_this->GetSignatureFunction()))); + out_line.append(std::format(SYSSTR(" [df: {:016X}]"), reinterpret_cast(p_typed_this->GetSignatureFunction()))); } - auto fieldpathproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto fieldpathproperty_to_string(void* p_this, SystemStringType& out_line) -> void { FFieldPathProperty* typed_this = static_cast(p_this); property_trivial_dump_to_string(p_this, out_line); - out_line.append(std::format(L" [pc: {:016X}]", reinterpret_cast(typed_this->GetPropertyClass()))); + out_line.append(std::format(SYSSTR(" [pc: {:016X}]"), reinterpret_cast(typed_this->GetPropertyClass()))); } - auto interfaceproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto interfaceproperty_to_string(void* p_this, SystemStringType& out_line) -> void { FInterfaceProperty* typed_this = static_cast(p_this); property_trivial_dump_to_string(p_this, out_line); - out_line.append(std::format(L" [ic: {:016X}]", reinterpret_cast(typed_this->GetInterfaceClass()))); + out_line.append(std::format(SYSSTR(" [ic: {:016X}]"), reinterpret_cast(typed_this->GetInterfaceClass()))); } - auto multicastdelegateproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto multicastdelegateproperty_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); FMulticastDelegateProperty* p_typed_this = static_cast(p_this); - out_line.append(std::format(L" [df: {:016X}]", reinterpret_cast(p_typed_this->GetSignatureFunction()))); + out_line.append(std::format(SYSSTR(" [df: {:016X}]"), reinterpret_cast(p_typed_this->GetSignatureFunction()))); } - auto objectproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto objectproperty_to_string(void* p_this, SystemStringType& out_line) -> void { FObjectProperty* typed_this = static_cast(p_this); property_trivial_dump_to_string(p_this, out_line); - out_line.append(std::format(L" [pc: {:016X}]", reinterpret_cast(typed_this->GetPropertyClass()))); + out_line.append(std::format(SYSSTR(" [pc: {:016X}]"), reinterpret_cast(typed_this->GetPropertyClass()))); } - auto structproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto structproperty_to_string(void* p_this, SystemStringType& out_line) -> void { FStructProperty* typed_this = static_cast(p_this); property_trivial_dump_to_string(p_this, out_line); - out_line.append(std::format(L" [ss: {:016X}]", reinterpret_cast(typed_this->GetStruct()))); + out_line.append(std::format(SYSSTR(" [ss: {:016X}]"), reinterpret_cast(typed_this->GetStruct()))); } - auto enumproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto enumproperty_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); auto* typed_this = static_cast(p_this); - out_line.append(std::format(L" [em: {:016X}]", reinterpret_cast(typed_this->GetEnum()))); + out_line.append(std::format(SYSSTR(" [em: {:016X}]"), reinterpret_cast(typed_this->GetEnum()))); } - auto boolproperty_to_string(void* p_this, std::wstring& out_line) -> void + auto boolproperty_to_string(void* p_this, SystemStringType& out_line) -> void { property_trivial_dump_to_string(p_this, out_line); auto* typed_this = static_cast(p_this); if (typed_this->GetFieldMask() != 255) { - out_line.append(std::format(L" [fm: {:X}] [bm: {:X}]", typed_this->GetFieldMask(), typed_this->GetByteMask())); + out_line.append(std::format(SYSSTR(" [fm: {:X}] [bm: {:X}]"), typed_this->GetFieldMask(), typed_this->GetByteMask())); } } - auto enum_to_string(void* p_this, std::wstring& out_line) -> void + auto enum_to_string(void* p_this, SystemStringType& out_line) -> void { object_trivial_dump_to_string(p_this, out_line); @@ -261,24 +262,24 @@ namespace RC::ObjectDumper for (auto& Elem : typed_this->ForEachName()) { - out_line.append(std::format(L"\n[{:016X}] {} [n: {:X}] [v: {}]", 0, Elem.Key.ToString(), Elem.Key.GetComparisonIndex(), Elem.Value)); + out_line.append(std::format(SYSSTR("\n[{:016X}] {} [n: {:X}] [v: {}]"), 0, to_system(Elem.Key.ToString()), Elem.Key.GetComparisonIndex(), Elem.Value)); } } - auto struct_to_string(void* p_this, std::wstring& out_line) -> void + auto struct_to_string(void* p_this, SystemStringType& out_line) -> void { UStruct* typed_this = static_cast(p_this); object_trivial_dump_to_string(p_this, out_line); - out_line.append(std::format(L" [sps: {:016X}]", reinterpret_cast(typed_this->GetSuperStruct()))); + out_line.append(std::format(SYSSTR(" [sps: {:016X}]"), reinterpret_cast(typed_this->GetSuperStruct()))); } - auto function_to_string(void* p_this, std::wstring& out_line) -> void + auto function_to_string(void* p_this, SystemStringType& out_line) -> void { - object_trivial_dump_to_string(p_this, out_line, L":"); + object_trivial_dump_to_string(p_this, out_line, SYSSTR(":")); } - auto scriptstruct_to_string_complex(void* p_this, std::wstring& out_line, ObjectToStringComplexDeclCallable callable) -> void + auto scriptstruct_to_string_complex(void* p_this, SystemStringType& out_line, ObjectToStringComplexDeclCallable callable) -> void { UScriptStruct* script_struct = static_cast(p_this); diff --git a/UE4SS/src/Platform/Linux/EntryLinux.cpp b/UE4SS/src/Platform/Linux/EntryLinux.cpp new file mode 100644 index 000000000..d9c582bb9 --- /dev/null +++ b/UE4SS/src/Platform/Linux/EntryLinux.cpp @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include + +#include "UE4SSProgram.hpp" +#include "Platform.hpp" + +// #define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +// typeof is a gnu extension +#ifndef typeof +#ifdef __clang__ +#define typeof(x) __typeof__(x) +#endif +#endif + +using namespace RC; + +pthread_t ue4ss_mainthread; +static bool UE4SSInited = false; + +void UE4SS_Start() +{ + // find libUE4SS.so path using dlfcn + Dl_info dl_info; + if (dladdr((void*)UE4SS_Start, &dl_info) == 0) + { + std::cerr << "Failed to find libUE4SS.so path" << std::endl; + return; + } +// #define EARLY_THROW_TEST +#ifdef EARLY_THROW_TEST + try + { + throw std::runtime_error{"Hello from Here!"}; + } + catch (std::exception& e) + { + fprintf(stderr, "%s\n", e.what()); + } +#endif + + auto ue4sspath = new SystemStringType(to_system_string(dl_info.dli_fname)); + // use pthread here + pthread_create( + &ue4ss_mainthread, + nullptr, + [](void* arg) -> void* { + auto ue4sspathPtr = reinterpret_cast(arg); + auto program = new UE4SSProgram(*ue4sspathPtr, {}); + delete ue4sspathPtr; + UE4SSInited = true; + + if (auto e = program->get_error_object(); !e->has_error()) + { + program->init(); + } + + if (auto e = program->get_error_object(); e->has_error()) + { + // If the output system errored out then use printf_s as a fallback + // Logging will only happen to the debug console but it's something at least + if (!Output::has_internal_error()) + { + Output::send(SYSSTR("Fatal Error: {}\n"), e->get_message()); + } + else + { + printf("Error: %s\n", e->get_message()); + } + } + return nullptr; + }, + ue4sspath); +} + +/* +void __attribute__((constructor)) UE4SS_Init() { + // It may not init in correct order.. + if (!UE4SSInited) { + printf("UE4SS_Init Called\n"); + // UE4SS_Start(); + printf("UE4SS_Start Returned\n"); + UE4SSInited = true; + } +} +*/ + +static int (*next_main)(int, char**, char**); +static bool inited = false; +int hooked_main(int argc, char** argv, char** envp) +{ + // delay the hook to make sure all objects are constructed + fprintf(stderr, "Running hooked main before actual main my pid = %d\n", getpid()); + if (!inited) + { + fprintf(stderr, "Do another hook?\n"); + UE4SS_Start(); + inited = true; + } + else + { + fprintf(stderr, "UE4SS already inited? \n"); + } + return next_main(argc, argv, envp); +} + +#define ALIGN_UP_PAGE(x) ((((unsigned long)(x)) + 0xFFF) & ~0xFFF) +#define ALIGN_DOWN_PAGE(x) (((unsigned long)(x)) & ~0xFFF) + +extern char** environ; + +extern "C" +{ + + struct link_map_private + { + Elf64_Addr l_addr; + char* l_name; + Elf64_Dyn* l_ld; + struct link_map_private *l_next, *l_prev; + struct link_map_private* l_real; + Lmid_t l_ns; + struct libname_list* l_libname; + Elf64_Dyn* l_info[DT_NUM + 0 + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; + }; + + int __libc_start_main( + int (*main)(int, char**, char**), int argc, char** argv, int (*init)(int, char**, char**), void (*fini)(void), void (*rtld_fini)(void), void* stack_end) + { + next_main = main; + typeof(&__libc_start_main) orig = (typeof(&__libc_start_main))dlsym(RTLD_NEXT, "__libc_start_main"); + // gxx fix + Dl_info dl_info; + Elf64_Sym* sym; + // libsteam_api is okay, the UE engine is causing problem + void* current___cxa_throw = dlsym(RTLD_DEFAULT, "__cxa_throw"); + dladdr1(current___cxa_throw, &dl_info, (void**)&sym, RTLD_DL_SYMENT); + fprintf(stderr, "__cxa_throw found in %s @ %p\n", dl_info.dli_fname, current___cxa_throw); + if ((unsigned long)current___cxa_throw < 0x7fffffff) + { + fprintf(stderr, "maybe UE, patched\n"); + mprotect((void*)ALIGN_DOWN_PAGE(sym), ALIGN_UP_PAGE(sym + 1) - ALIGN_DOWN_PAGE(sym), PROT_READ | PROT_WRITE); + sym->st_info = ELF64_ST_INFO(STB_LOCAL, ELF64_ST_TYPE(sym->st_info)); + sym->st_other = STV_INTERNAL; + // assume this is UE5's address range and remove this symbol + void* working___cxa_throw = dlsym(RTLD_DEFAULT, "__cxa_throw"); + struct link_map_private* map1; + dladdr1(working___cxa_throw, &dl_info, (void**)&map1, RTLD_DL_SYMENT); + fprintf(stderr, "now we're using __cxa_throw from %s @ %p\n", dl_info.dli_fname, working___cxa_throw); + + struct data_encap + { + void* working___cxa_throw; + void* current___cxa_throw; + } data = {working___cxa_throw, current___cxa_throw}; + dl_iterate_phdr( + [](struct dl_phdr_info* info, size_t size, void* data) -> int { + auto working___cxa_throw = ((struct data_encap*)data)->working___cxa_throw; + auto current___cxa_throw = ((struct data_encap*)data)->current___cxa_throw; + fprintf(stderr, "Examing %s @ %p... \n", info->dlpi_name, info->dlpi_addr); + // skip vdso + if (strstr(info->dlpi_name, "vdso") != nullptr) + { + fprintf(stderr, " Skipping %s\n", info->dlpi_name); + return 0; + } + // get dynamic + Elf64_Dyn* dyn = nullptr; + for (int i = 0; i < info->dlpi_phnum; i++) + { + if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) + { + dyn = (Elf64_Dyn*)(info->dlpi_phdr[i].p_vaddr + info->dlpi_addr); + } + } + + // iterate dynamic, find relocation to __cxa_throw + struct RELA_INFOS + { + Elf64_Rela* rela; + ssize_t sz; + } rela_infos[2] = {}; + // symbol table + Elf64_Sym* sym = nullptr; + // string table + char* strtab = nullptr; + for (int i = 0; dyn[i].d_tag != DT_NULL; i++) + { + // DT_JMPREL + if (dyn[i].d_tag == DT_JMPREL) + { + rela_infos[0].rela = (Elf64_Rela*)(dyn[i].d_un.d_ptr); + } + if (dyn[i].d_tag == DT_RELA) + { + rela_infos[1].rela = (Elf64_Rela*)(dyn[i].d_un.d_ptr); + } + // DT_SYMTAB + if (dyn[i].d_tag == DT_SYMTAB) + { + sym = (Elf64_Sym*)(dyn[i].d_un.d_ptr); + } + // DT_STRTAB + if (dyn[i].d_tag == DT_STRTAB) + { + strtab = (char*)(dyn[i].d_un.d_ptr); + } + // DT_PLTRELSZ + if (dyn[i].d_tag == DT_PLTRELSZ) + { + rela_infos[0].sz = dyn[i].d_un.d_val; + } + // DT_RELASZ + if (dyn[i].d_tag == DT_RELASZ) + { + rela_infos[1].sz = dyn[i].d_un.d_val; + } + } + if (sym == nullptr || strtab == nullptr) + { + fprintf(stderr, " No sym or strtab found %p %p\n", sym, strtab); + return 0; + } + if ((rela_infos[0].rela == nullptr) && (rela_infos[1].rela == nullptr)) + { + fprintf(stderr, " No rela found %p %p\n", rela_infos[0].rela, rela_infos[1].rela); + return 0; + } + auto process_rela = [&](Elf64_Rela* rela, ssize_t sz, Elf64_Sym* sym, char* strtab) { + fprintf(stderr, " Found rela @ %p, sym @ %p, strtab @ %p\n", rela, sym, strtab); + for (int i = 0; i < sz / sizeof(Elf64_Rela); i++) + { + if (sym[ELF64_R_SYM(rela[i].r_info)].st_name != 0) + { + char* name = strtab + sym[ELF64_R_SYM(rela[i].r_info)].st_name; + if (strncmp(name, "__cxa_throw", 11) == 0) + { + fprintf(stderr, " Found __cxa_throw symbol @ %p\n", rela[i].r_offset); + } + else + { + continue; + } + // got table address + unsigned long* address = (unsigned long*)(rela[i].r_offset + info->dlpi_addr); + if (*address == (unsigned long)current___cxa_throw) + { + fprintf(stderr, " Found __cxa_throw in .plt.got @ %p, offset = %d\n", address, rela[i].r_offset); + mprotect((void*)ALIGN_DOWN_PAGE(address), ALIGN_UP_PAGE(address + 1) - ALIGN_DOWN_PAGE(address), PROT_READ | PROT_WRITE); + *address = (unsigned long)working___cxa_throw; + fprintf(stderr, " Patched __cxa_throw to @ %p\n", working___cxa_throw); + } + } + } + }; + for (int i = 0; i < 2; i++) + { + if (rela_infos[i].rela != nullptr) + { + process_rela(rela_infos[i].rela, rela_infos[i].sz, sym, strtab); + } + } + return 0; + }, + &data); + } + + // remove LD_PRELOAD from environ + int nenv = 0; + for (int i = 0; environ[i]; i++) + nenv++; + for (int i = 0; i < nenv; i++) + { + if (strncmp(environ[i], "LD_PRELOAD=", 11) == 0) + { + fprintf(stderr, "Found LD_PRELOAD @%d %s\n", i, environ[i]); + environ[i] = environ[nenv - 1]; + nenv--; + } + } + environ[nenv] = NULL; + + return orig(hooked_main, argc, argv, init, fini, rtld_fini, stack_end); + } +} + +// destructor +void __attribute__((destructor)) UE4SS_End() +{ + if (UE4SSInited) + { + // invoke cleanup when the program ends + UE4SSProgram::static_cleanup(); + UE4SSInited = false; + } +} + +std::filesystem::path get_executable_path() +{ + // get executable path on Linux + char result[1024]; + ssize_t count = readlink("/proc/self/exe", result, 1023); + return std::filesystem::path(std::string(result, (count > 0) ? count : 0)); +} + +void add_dlsearch_folder(std::filesystem::path& path) +{ + // not implemented +} diff --git a/UE4SS/src/CrashDumper.cpp b/UE4SS/src/Platform/Win32/CrashDumper.cpp similarity index 97% rename from UE4SS/src/CrashDumper.cpp rename to UE4SS/src/Platform/Win32/CrashDumper.cpp index 5e35de5f9..95054f573 100644 --- a/UE4SS/src/CrashDumper.cpp +++ b/UE4SS/src/Platform/Win32/CrashDumper.cpp @@ -27,7 +27,7 @@ namespace RC LONG WINAPI ExceptionHandler(_EXCEPTION_POINTERS* exception_pointers) { const auto now = time_point_cast(system_clock::now()); - const std::wstring dump_path = std::format(L"{}\\crash_{:%Y_%m_%d_%H_%M_%S}.dmp", StringType{UE4SSProgram::get_program().get_working_directory()}, now); + const std::wstring dump_path = std::format(L"{}\\crash_{:%Y_%m_%d_%H_%M_%S}.dmp", SystemStringType{UE4SSProgram::get_program().get_working_directory()}, now); const HANDLE file = CreateFileW(dump_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); diff --git a/UE4SS/src/main_ue4ss_rewritten.cpp b/UE4SS/src/Platform/Win32/EntryWin32.cpp similarity index 87% rename from UE4SS/src/main_ue4ss_rewritten.cpp rename to UE4SS/src/Platform/Win32/EntryWin32.cpp index a6b836933..1c17c80d2 100644 --- a/UE4SS/src/main_ue4ss_rewritten.cpp +++ b/UE4SS/src/Platform/Win32/EntryWin32.cpp @@ -7,6 +7,8 @@ #include #include "UE4SSProgram.hpp" +#include "Platform.hpp" + #include #include @@ -29,7 +31,7 @@ auto thread_dll_start(UE4SSProgram* program) -> unsigned long // Logging will only happen to the debug console but it's something at least if (!Output::has_internal_error()) { - Output::send(STR("Fatal Error: {}\n"), to_wstring(e->get_message())); + Output::send(SYSSTR("Fatal Error: {}\n"), to_wstring(e->get_message())); } else { @@ -131,4 +133,18 @@ auto WIN_API_FUNCTION_NAME(HMODULE hModule, DWORD ul_reason_for_call, [[maybe_un break; } return TRUE; -} \ No newline at end of file +} + +std::filesystem::path get_executable_path() +{ + wchar_t exe_path_buffer[1024]; + GetModuleFileNameW(GetModuleHandle(nullptr), exe_path_buffer, 1023); + return std::filesystem::path(exe_path_buffer); +} + +void add_dlsearch_folder(std::filesystem::path& path) +{ + SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + // Make sure game directory DLLs are also included + AddDllDirectory(path.c_str()); +} diff --git a/UE4SS/src/SDKGenerator/Common.cpp b/UE4SS/src/SDKGenerator/Common.cpp index 3948dbf5d..2bc9d5db2 100644 --- a/UE4SS/src/SDKGenerator/Common.cpp +++ b/UE4SS/src/SDKGenerator/Common.cpp @@ -27,36 +27,38 @@ #include #include #include +#include #pragma warning(default : 4005) #define DELEGATE_SIGNATURE_POSTFIX STR("__DelegateSignature") +#define DELEGATE_SIGNATURE_POSTFIX_SYS SYSSTR("__DelegateSignature") namespace RC::UEGenerator { using namespace Unreal; - auto get_native_class_name(UClass* uclass, bool interface_name) -> File::StringType + auto get_native_class_name(UClass* uclass, bool interface_name) -> SystemStringType { - File::StringType result_string; + SystemStringType result_string; if (interface_name) { - result_string.append(STR("I")); + result_string.append(SYSSTR("I")); } else if (uclass->IsChildOf()) { - result_string.append(STR("A")); + result_string.append(SYSSTR("A")); } else { - result_string.append(STR("U")); + result_string.append(SYSSTR("U")); } if ((uclass->GetClassFlags() & Unreal::CLASS_Deprecated) != 0) { - result_string.append(STR("DEPRECATED_")); + result_string.append(SYSSTR("DEPRECATED_")); } - result_string.append(uclass->GetName()); + result_string.append(to_system(uclass->GetName())); return result_string; } @@ -72,43 +74,43 @@ namespace RC::UEGenerator } } - auto get_native_enum_name(UEnum* uenum, bool include_type) -> File::StringType + auto get_native_enum_name(UEnum* uenum, bool include_type) -> SystemStringType { - std::wstring result_string; + SystemStringType result_string; // Seems to be not needed, because enum objects, unlike classes or structs, retain their normal E prefix - // ResultString.append(STR("E")); - result_string.append(uenum->GetName()); + // ResultString.append(SYSSTR("E")); + result_string.append(to_system(uenum->GetName())); // Namespaced enums need to have ::Type appended for the type if (uenum->GetCppForm() == UEnum::ECppForm::Namespaced && include_type) { - result_string.append(STR("::Type")); + result_string.append(SYSSTR("::Type")); } return result_string; } - auto get_native_struct_name(UScriptStruct* script_struct) -> File::StringType + auto get_native_struct_name(UScriptStruct* script_struct) -> SystemStringType { - std::wstring result_string; + SystemStringType result_string; - result_string.append(STR("F")); - result_string.append(script_struct->GetName()); + result_string.append(SYSSTR("F")); + result_string.append(to_system(script_struct->GetName())); return result_string; } - auto sanitize_property_name(const File::StringType& property_name) -> File::StringType + auto sanitize_property_name(const SystemStringType& property_name) -> SystemStringType { - std::wstring resulting_name = property_name; + SystemStringType resulting_name = property_name; // Remove heading underscore, used by private variables in some games - if (resulting_name.length() >= 2 && resulting_name[0] == '_') + if (resulting_name.length() >= 2 && resulting_name[0] == STR('_')) { resulting_name.erase(0, 1); } // Remove heading m if it is followed by uppercase letter, used by variables in some games - if (resulting_name.length() >= 2 && resulting_name[0] == 'm' && towupper(resulting_name[1]) == resulting_name[1]) + if (resulting_name.length() >= 2 && resulting_name[0] == STR('m') && towupper(resulting_name[1]) == resulting_name[1]) { resulting_name.erase(0, 1); } @@ -121,16 +123,16 @@ namespace RC::UEGenerator return resulting_name; } - auto generate_delegate_name(FProperty* property, const File::StringType& context_name) -> File::StringType + auto generate_delegate_name(FProperty* property, const SystemStringType& context_name) -> SystemStringType { - const std::wstring property_name = sanitize_property_name(property->GetName()); - return std::format(STR("F{}{}"), context_name, property_name); + const auto property_name = sanitize_property_name(to_system_string(property->GetName())); + return std::format(SYSSTR("F{}{}"), to_system(context_name), to_system(property_name)); } auto generate_property_cxx_name(FProperty* property, bool is_top_level_declaration, UObject* class_context, EnableForwardDeclarations enable_forward_declarations) - -> File::StringType + -> SystemStringType { - const std::wstring field_class_name = property->GetClass().GetName(); + const auto field_class_name = to_system(property->GetClass().GetName()); // Byte Property if (property->IsA()) @@ -141,10 +143,10 @@ namespace RC::UEGenerator if (enum_value != NULL) { // Non-EnumClass enumerations should be wrapped into TEnumAsByte according to UHT - const std::wstring enum_type_name = get_native_enum_name(enum_value); - return std::format(STR("TEnumAsByte<{}>"), enum_type_name); + const SystemStringType enum_type_name = to_system(get_native_enum_name(enum_value)); + return std::format(SYSSTR("TEnumAsByte<{}>"), enum_type_name); } - return STR("uint8"); + return SYSSTR("uint8"); } // Enum Property @@ -155,10 +157,10 @@ namespace RC::UEGenerator if (uenum == NULL) { - throw std::runtime_error(RC::fmt("EnumProperty %S does not have a valid Enum value", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("EnumProperty {} does not have a valid Enum value", property->GetName())); } - const std::wstring enum_type_name = get_native_enum_name(uenum); + const SystemStringType enum_type_name = get_native_enum_name(uenum); return enum_type_name; } @@ -168,47 +170,47 @@ namespace RC::UEGenerator FBoolProperty* bool_property = static_cast(property); if (is_top_level_declaration && bool_property->GetFieldMask() != 255) { - return STR("uint8"); + return SYSSTR("uint8"); } - return STR("bool"); + return SYSSTR("bool"); } // Standard Numeric Properties if (property->IsA()) { - return STR("int8"); + return SYSSTR("int8"); } else if (property->IsA()) { - return STR("int16"); + return SYSSTR("int16"); } else if (property->IsA()) { - return STR("int32"); + return SYSSTR("int32"); } else if (property->IsA()) { - return STR("int64"); + return SYSSTR("int64"); } else if (property->IsA()) { - return STR("uint16"); + return SYSSTR("uint16"); } else if (property->IsA()) { - return STR("uint32"); + return SYSSTR("uint32"); } else if (property->IsA()) { - return STR("uint64"); + return SYSSTR("uint64"); } else if (property->IsA()) { - return STR("float"); + return SYSSTR("float"); } else if (property->IsA()) { - return STR("double"); + return SYSSTR("double"); } // Class Properties @@ -219,22 +221,22 @@ namespace RC::UEGenerator if (meta_class == NULL || meta_class == UObject::StaticClass()) { - return STR("UClass*"); + return SYSSTR("UClass*"); } - File::StringType meta_class_name{}; + SystemStringType meta_class_name{}; if (enable_forward_declarations == EnableForwardDeclarations::Yes) { - meta_class_name = STR("class "); + meta_class_name = SYSSTR("class "); } meta_class_name.append(get_native_class_name(meta_class, false)); - return std::format(STR("TSubclassOf<{}>"), meta_class_name); + return std::format(SYSSTR("TSubclassOf<{}>"), meta_class_name); } if (auto* class_property = CastField(property); class_property) { // TODO: Confirm that this is accurate - return STR("TObjectPtr"); + return SYSSTR("TObjectPtr"); } if (property->IsA()) @@ -244,15 +246,15 @@ namespace RC::UEGenerator if (meta_class == NULL) { - return STR("TSoftClassPtr"); + return SYSSTR("TSoftClassPtr"); } else if (meta_class == UObject::StaticClass()) { - return STR("TSoftClassPtr"); + return SYSSTR("TSoftClassPtr"); } - - const std::wstring meta_class_name = get_native_class_name(meta_class, false); - return std::format(STR("TSoftClassPtr<{}>"), meta_class_name); + + const SystemStringType meta_class_name = get_native_class_name(meta_class, false); + return std::format(SYSSTR("TSoftClassPtr<{}>"), meta_class_name); } // Object Properties @@ -265,11 +267,11 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("UObject*"); + return SYSSTR("UObject*"); } - const std::wstring property_class_name = get_native_class_name(property_class, false); - return std::format(STR("{}*"), property_class_name); + const SystemStringType property_class_name = get_native_class_name(property_class, false); + return std::format(SYSSTR("{}*"), property_class_name); } if (auto* object_property = CastField(property); object_property) @@ -278,12 +280,12 @@ namespace RC::UEGenerator if (!property_class) { - return STR("TObjectPtr"); + return SYSSTR("TObjectPtr"); } else { const auto property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TObjectPtr<{}>"), property_class_name); } } @@ -294,16 +296,16 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("TWeakObjectPtr"); + return SYSSTR("TWeakObjectPtr"); } - File::StringType property_class_name{}; + SystemStringType property_class_name{}; if (enable_forward_declarations == EnableForwardDeclarations::Yes) { - property_class_name = std::format(STR("class ")); + property_class_name = std::format(SYSSTR("class ")); } property_class_name.append(get_native_class_name(property_class, false)); - return std::format(STR("TWeakObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TWeakObjectPtr<{}>"), property_class_name); } if (property->IsA()) @@ -313,16 +315,16 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("TLazyObjectPtr"); + return SYSSTR("TLazyObjectPtr"); } - File::StringType property_class_name{}; + SystemStringType property_class_name{}; if (enable_forward_declarations == EnableForwardDeclarations::Yes) { - property_class_name = STR("class "); + property_class_name = SYSSTR("class "); } property_class_name.append(get_native_class_name(property_class, false)); - return std::format(STR("TLazyObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TLazyObjectPtr<{}>"), property_class_name); } if (property->IsA()) @@ -332,11 +334,11 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("TSoftObjectPtr"); + return SYSSTR("TSoftObjectPtr"); } - const std::wstring property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TSoftObjectPtr<{}>"), property_class_name); + const SystemStringType property_class_name = get_native_class_name(property_class, false); + return std::format(SYSSTR("TSoftObjectPtr<{}>"), property_class_name); } // Interface Property @@ -347,16 +349,16 @@ namespace RC::UEGenerator if (interface_class == NULL || interface_class == UInterface::StaticClass()) { - return STR("FScriptInterface"); + return SYSSTR("FScriptInterface"); } - File::StringType interface_class_name{}; + SystemStringType interface_class_name{}; if (enable_forward_declarations == EnableForwardDeclarations::Yes) { - interface_class_name = STR("class "); + interface_class_name = SYSSTR("class "); } interface_class_name.append(get_native_class_name(interface_class, true)); - return std::format(STR("TScriptInterface<{}>"), interface_class_name); + return std::format(SYSSTR("TScriptInterface<{}>"), interface_class_name); } // Struct Property @@ -367,10 +369,10 @@ namespace RC::UEGenerator if (script_struct == NULL) { - throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty %S", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty {}", property->GetName())); } - const std::wstring native_struct_name = get_native_struct_name(script_struct); + const SystemStringType native_struct_name = get_native_struct_name(script_struct); return native_struct_name; } @@ -379,7 +381,7 @@ namespace RC::UEGenerator { FDelegateProperty* delegate_property = static_cast(property); - const std::wstring delegate_type_name = generate_delegate_name(delegate_property, class_context->GetName()); + const auto delegate_type_name = generate_delegate_name(delegate_property, to_system(class_context->GetName())); return delegate_type_name; } @@ -389,7 +391,7 @@ namespace RC::UEGenerator { FMulticastInlineDelegateProperty* delegate_property = static_cast(property); - const std::wstring delegate_type_name = generate_delegate_name(delegate_property, class_context->GetName()); + const auto delegate_type_name = generate_delegate_name(delegate_property, to_system(class_context->GetName())); return delegate_type_name; } @@ -397,7 +399,7 @@ namespace RC::UEGenerator { FMulticastSparseDelegateProperty* delegate_property = static_cast(property); - const std::wstring delegate_type_name = generate_delegate_name(delegate_property, class_context->GetName()); + const auto delegate_type_name = generate_delegate_name(delegate_property, to_system(class_context->GetName())); return delegate_type_name; } @@ -405,8 +407,8 @@ namespace RC::UEGenerator if (property->IsA()) { FFieldPathProperty* field_path_property = static_cast(property); - const std::wstring property_class_name = field_path_property->GetPropertyClass()->GetName(); - return std::format(STR("TFieldPath"), property_class_name); + const auto property_class_name = to_system(field_path_property->GetPropertyClass()->GetName()); + return std::format(SYSSTR("TFieldPath"), property_class_name); } // Collection and Map Properties @@ -416,16 +418,16 @@ namespace RC::UEGenerator FArrayProperty* array_property = static_cast(property); FProperty* inner_property = array_property->GetInner(); - File::StringType inner_property_type{}; + SystemStringType inner_property_type{}; if (enable_forward_declarations == EnableForwardDeclarations::Yes && !is_integral_type(inner_property)) { if (inner_property->IsA()) { - inner_property_type = STR("class "); + inner_property_type = SYSSTR("class "); } } inner_property_type.append(generate_property_cxx_name(inner_property, is_top_level_declaration, class_context)); - return std::format(STR("TArray<{}>"), inner_property_type); + return std::format(SYSSTR("TArray<{}>"), inner_property_type); } if (property->IsA()) @@ -433,8 +435,8 @@ namespace RC::UEGenerator FSetProperty* set_property = static_cast(property); FProperty* element_prop = set_property->GetElementProp(); - const std::wstring element_property_type = generate_property_cxx_name(element_prop, is_top_level_declaration, class_context); - return std::format(STR("TSet<{}>"), element_property_type); + const SystemStringType element_property_type = generate_property_cxx_name(element_prop, is_top_level_declaration, class_context); + return std::format(SYSSTR("TSet<{}>"), element_property_type); } // TODO: This is missing support for freeze image map properties because XMapProperty is incomplete. (low priority) @@ -444,48 +446,48 @@ namespace RC::UEGenerator FProperty* key_property = map_property->GetKeyProp(); FProperty* value_property = map_property->GetValueProp(); - File::StringType key_type{}; - File::StringType value_type{}; + SystemStringType key_type{}; + SystemStringType value_type{}; if (enable_forward_declarations == EnableForwardDeclarations::Yes && !is_integral_type(key_property) && !is_integral_type(value_property)) { if (!key_property->IsA()) { - key_type = STR("class "); + key_type = SYSSTR("class "); } if (!value_property->IsA()) { - value_type = STR("class "); + value_type = SYSSTR("class "); } } key_type.append(generate_property_cxx_name(key_property, is_top_level_declaration, class_context)); value_type.append(generate_property_cxx_name(value_property, is_top_level_declaration, class_context)); - return std::format(STR("TMap<{}, {}>"), key_type, value_type); + return std::format(SYSSTR("TMap<{}, {}>"), key_type, value_type); } // Standard properties that do not have any special attributes if (property->IsA()) { - return STR("FName"); + return SYSSTR("FName"); } else if (property->IsA()) { - return STR("FString"); + return SYSSTR("FString"); } else if (property->IsA()) { - return STR("FText"); + return SYSSTR("FText"); } - throw std::runtime_error(RC::fmt("Unsupported property class %S", field_class_name.c_str())); + throw std::runtime_error(RC::fmt("Unsupported property class " SystemStringPrint, field_class_name)); } - auto generate_property_lua_name(FProperty* property, bool is_top_level_declaration, UObject* class_context) -> File::StringType + auto generate_property_lua_name(FProperty* property, bool is_top_level_declaration, UObject* class_context) -> SystemStringType { - const std::wstring field_class_name = property->GetClass().GetName(); + const auto field_class_name = to_system(property->GetClass().GetName()); // Byte Property - if (field_class_name == STR("ByteProperty")) + if (field_class_name == SYSSTR("ByteProperty")) { FByteProperty* byte_property = static_cast(property); UEnum* enum_value = byte_property->GetEnum(); @@ -493,86 +495,86 @@ namespace RC::UEGenerator if (enum_value != NULL) { // Non-EnumClass enumerations should be wrapped into TEnumAsByte according to UHT - const std::wstring enum_type_name = get_native_enum_name(enum_value); - return std::format(STR("{}"), enum_type_name); + const SystemStringType enum_type_name = get_native_enum_name(enum_value); + return std::format(SYSSTR("{}"), enum_type_name); } - return STR("uint8"); + return SYSSTR("uint8"); } // Enum Property - if (field_class_name == STR("EnumProperty")) + if (field_class_name == SYSSTR("EnumProperty")) { FEnumProperty* enum_property = static_cast(property); UEnum* uenum = enum_property->GetEnum(); if (uenum == NULL) { - throw std::runtime_error(RC::fmt("EnumProperty %S does not have a valid Enum value", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("EnumProperty {} does not have a valid Enum value", property->GetName())); } - const std::wstring enum_type_name = get_native_enum_name(uenum); + const SystemStringType enum_type_name = get_native_enum_name(uenum); return enum_type_name; } // Bool Property - if (field_class_name == STR("BoolProperty")) + if (field_class_name == SYSSTR("BoolProperty")) { - return STR("boolean"); + return SYSSTR("boolean"); } // Standard Numeric Properties - if (field_class_name == STR("Int8Property")) + if (field_class_name == SYSSTR("Int8Property")) { - return STR("int8"); + return SYSSTR("int8"); } - else if (field_class_name == STR("Int16Property")) + else if (field_class_name == SYSSTR("Int16Property")) { - return STR("int16"); + return SYSSTR("int16"); } - else if (field_class_name == STR("IntProperty")) + else if (field_class_name == SYSSTR("IntProperty")) { - return STR("int32"); + return SYSSTR("int32"); } - else if (field_class_name == STR("Int64Property")) + else if (field_class_name == SYSSTR("Int64Property")) { - return STR("int64"); + return SYSSTR("int64"); } - else if (field_class_name == STR("UInt16Property")) + else if (field_class_name == SYSSTR("UInt16Property")) { - return STR("uint16"); + return SYSSTR("uint16"); } - else if (field_class_name == STR("UInt32Property")) + else if (field_class_name == SYSSTR("UInt32Property")) { - return STR("uint32"); + return SYSSTR("uint32"); } - else if (field_class_name == STR("UInt64Property")) + else if (field_class_name == SYSSTR("UInt64Property")) { - return STR("uint64"); + return SYSSTR("uint64"); } - else if (field_class_name == STR("FloatProperty")) + else if (field_class_name == SYSSTR("FloatProperty")) { - return STR("float"); + return SYSSTR("float"); } - else if (field_class_name == STR("DoubleProperty")) + else if (field_class_name == SYSSTR("DoubleProperty")) { - return STR("double"); + return SYSSTR("double"); } // Object Properties // TODO: Verify that the syntax for 'AssetObjectProperty' is the same as for 'ObjectProperty'. // If it's not, then add another branch here after you figure out what the syntax should be. - if (field_class_name == STR("ObjectProperty") || field_class_name == STR("AssetObjectProperty")) + if (field_class_name == SYSSTR("ObjectProperty") || field_class_name == SYSSTR("AssetObjectProperty")) { FObjectProperty* object_property = static_cast(property); UClass* property_class = object_property->GetPropertyClass(); if (property_class == NULL) { - return STR("UObject"); + return SYSSTR("UObject"); } - const std::wstring property_class_name = get_native_class_name(property_class, false); - return std::format(STR("{}"), property_class_name); + const SystemStringType property_class_name = get_native_class_name(property_class, false); + return std::format(SYSSTR("{}"), property_class_name); } if (auto* object_property = CastField(property); object_property) @@ -581,223 +583,223 @@ namespace RC::UEGenerator if (!property_class) { - return STR("TObjectPtr"); + return SYSSTR("TObjectPtr"); } else { const auto property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TObjectPtr<{}>"), property_class_name); } } - if (field_class_name == STR("WeakObjectProperty")) + if (field_class_name == SYSSTR("WeakObjectProperty")) { FWeakObjectProperty* weak_object_property = static_cast(property); UClass* property_class = weak_object_property->GetPropertyClass(); if (property_class == NULL) { - return STR("TWeakObjectPtr"); + return SYSSTR("TWeakObjectPtr"); } - File::StringType property_class_name{}; + SystemStringType property_class_name{}; property_class_name.append(get_native_class_name(property_class, false)); - return std::format(STR("TWeakObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TWeakObjectPtr<{}>"), property_class_name); } - if (field_class_name == STR("LazyObjectProperty")) + if (field_class_name == SYSSTR("LazyObjectProperty")) { FLazyObjectProperty* lazy_object_property = static_cast(property); UClass* property_class = lazy_object_property->GetPropertyClass(); if (property_class == NULL) { - return STR("TLazyObjectPtr"); + return SYSSTR("TLazyObjectPtr"); } - File::StringType property_class_name{}; + SystemStringType property_class_name{}; property_class_name.append(get_native_class_name(property_class, false)); - return std::format(STR("TLazyObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TLazyObjectPtr<{}>"), property_class_name); } - if (field_class_name == STR("SoftObjectProperty")) + if (field_class_name == SYSSTR("SoftObjectProperty")) { FSoftObjectProperty* soft_object_property = static_cast(property); UClass* property_class = soft_object_property->GetPropertyClass(); if (property_class == NULL) { - return STR("TSoftObjectPtr"); + return SYSSTR("TSoftObjectPtr"); } - const std::wstring property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TSoftObjectPtr<{}>"), property_class_name); + const SystemStringType property_class_name = get_native_class_name(property_class, false); + return std::format(SYSSTR("TSoftObjectPtr<{}>"), property_class_name); } // Class Properties - if (field_class_name == STR("ClassProperty") || field_class_name == STR("AssetClassProperty")) + if (field_class_name == SYSSTR("ClassProperty") || field_class_name == SYSSTR("AssetClassProperty")) { FClassProperty* class_property = static_cast(property); UClass* meta_class = class_property->GetMetaClass(); if (meta_class == NULL || meta_class == UObject::StaticClass()) { - return STR("UClass"); + return SYSSTR("UClass"); } - File::StringType meta_class_name{}; + SystemStringType meta_class_name{}; meta_class_name.append(get_native_class_name(meta_class, false)); - return std::format(STR("TSubclassOf<{}>"), meta_class_name); + return std::format(SYSSTR("TSubclassOf<{}>"), meta_class_name); } if (auto* class_property = CastField(property); class_property) { // TODO: Confirm that this is accurate - return STR("TObjectPtr"); + return SYSSTR("TObjectPtr"); } - if (field_class_name == STR("SoftClassProperty")) + if (field_class_name == SYSSTR("SoftClassProperty")) { FSoftClassProperty* soft_class_property = static_cast(property); UClass* meta_class = soft_class_property->GetMetaClass(); if (meta_class == NULL || meta_class == UObject::StaticClass()) { - return STR("TSoftClassPtr"); + return SYSSTR("TSoftClassPtr"); } - const std::wstring meta_class_name = get_native_class_name(meta_class, false); - return std::format(STR("TSoftClassPtr<{}>"), meta_class_name); + const SystemStringType meta_class_name = get_native_class_name(meta_class, false); + return std::format(SYSSTR("TSoftClassPtr<{}>"), meta_class_name); } // Interface Property - if (field_class_name == STR("InterfaceProperty")) + if (field_class_name == SYSSTR("InterfaceProperty")) { FInterfaceProperty* interface_property = static_cast(property); UClass* interface_class = interface_property->GetInterfaceClass(); if (interface_class == NULL || interface_class == UInterface::StaticClass()) { - return STR("FScriptInterface"); + return SYSSTR("FScriptInterface"); } - File::StringType interface_class_name{}; + SystemStringType interface_class_name{}; interface_class_name.append(get_native_class_name(interface_class, true)); - return std::format(STR("TScriptInterface<{}>"), interface_class_name); + return std::format(SYSSTR("TScriptInterface<{}>"), interface_class_name); } // Struct Property - if (field_class_name == STR("StructProperty")) + if (field_class_name == SYSSTR("StructProperty")) { FStructProperty* struct_property = static_cast(property); UScriptStruct* script_struct = struct_property->GetStruct(); if (script_struct == NULL) { - throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty %S", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty {}", property->GetName())); } - const std::wstring native_struct_name = get_native_struct_name(script_struct); + const SystemStringType native_struct_name = get_native_struct_name(script_struct); return native_struct_name; } // Delegate Properties - if (field_class_name == STR("DelegateProperty")) + if (field_class_name == SYSSTR("DelegateProperty")) { FDelegateProperty* delegate_property = static_cast(property); - const std::wstring delegate_type_name = generate_delegate_name(delegate_property, class_context->GetName()); + const auto delegate_type_name = generate_delegate_name(delegate_property, to_system(class_context->GetName())); return delegate_type_name; } // In 4.23, they replaced 'MulticastDelegateProperty' with 'Inline' & 'Sparse' variants // It looks like the delegate macro might be the same as the 'Inline' variant in later versions, so we'll use the same branch here - if (field_class_name == STR("MulticastInlineDelegateProperty") || field_class_name == STR("MulticastDelegateProperty")) + if (field_class_name == SYSSTR("MulticastInlineDelegateProperty") || field_class_name == SYSSTR("MulticastDelegateProperty")) { FMulticastInlineDelegateProperty* delegate_property = static_cast(property); - const std::wstring delegate_type_name = generate_delegate_name(delegate_property, class_context->GetName()); + const auto delegate_type_name = generate_delegate_name(delegate_property, to_system(class_context->GetName())); return delegate_type_name; } - if (field_class_name == STR("MulticastSparseDelegateProperty")) + if (field_class_name == SYSSTR("MulticastSparseDelegateProperty")) { FMulticastSparseDelegateProperty* delegate_property = static_cast(property); - const std::wstring delegate_type_name = generate_delegate_name(delegate_property, class_context->GetName()); + const auto delegate_type_name = generate_delegate_name(delegate_property, to_system(class_context->GetName())); return delegate_type_name; } // Field path property - if (field_class_name == STR("FieldPathProperty")) + if (field_class_name == SYSSTR("FieldPathProperty")) { FFieldPathProperty* field_path_property = static_cast(property); - const std::wstring property_class_name = field_path_property->GetPropertyClass()->GetName(); - return std::format(STR("TFieldPath"), property_class_name); + const auto property_class_name = to_system(field_path_property->GetPropertyClass()->GetName()); + return std::format(SYSSTR("TFieldPath"), property_class_name); } // Collection and Map Properties // TODO: This is missing support for freeze image array properties because XArrayProperty is incomplete. (low priority) - if (field_class_name == STR("ArrayProperty")) + if (field_class_name == SYSSTR("ArrayProperty")) { FArrayProperty* array_property = static_cast(property); FProperty* inner_property = array_property->GetInner(); - File::StringType inner_property_type{}; + SystemStringType inner_property_type{}; inner_property_type.append(generate_property_lua_name(inner_property, is_top_level_declaration, class_context)); - return std::format(STR("TArray<{}>"), inner_property_type); + return std::format(SYSSTR("TArray<{}>"), inner_property_type); } - if (field_class_name == STR("SetProperty")) + if (field_class_name == SYSSTR("SetProperty")) { FSetProperty* set_property = static_cast(property); FProperty* element_prop = set_property->GetElementProp(); - const std::wstring element_property_type = generate_property_lua_name(element_prop, is_top_level_declaration, class_context); - return std::format(STR("TSet<{}>"), element_property_type); + const SystemStringType element_property_type = generate_property_lua_name(element_prop, is_top_level_declaration, class_context); + return std::format(SYSSTR("TSet<{}>"), element_property_type); } // TODO: This is missing support for freeze image map properties because XMapProperty is incomplete. (low priority) - if (field_class_name == STR("MapProperty")) + if (field_class_name == SYSSTR("MapProperty")) { FMapProperty* map_property = static_cast(property); FProperty* key_property = map_property->GetKeyProp(); FProperty* value_property = map_property->GetValueProp(); - File::StringType key_type{}; - File::StringType value_type{}; + SystemStringType key_type{}; + SystemStringType value_type{}; key_type.append(generate_property_lua_name(key_property, is_top_level_declaration, class_context)); value_type.append(generate_property_lua_name(value_property, is_top_level_declaration, class_context)); - return std::format(STR("TMap<{}, {}>"), key_type, value_type); + return std::format(SYSSTR("TMap<{}, {}>"), key_type, value_type); } // Standard properties that do not have any special attributes - if (field_class_name == STR("NameProperty")) + if (field_class_name == SYSSTR("NameProperty")) { - return STR("FName"); + return SYSSTR("FName"); } - else if (field_class_name == STR("StrProperty")) + else if (field_class_name == SYSSTR("StrProperty")) { - return STR("FString"); + return SYSSTR("FString"); } - else if (field_class_name == STR("TextProperty")) + else if (field_class_name == SYSSTR("TextProperty")) { - return STR("FText"); + return SYSSTR("FText"); } - throw std::runtime_error(RC::fmt("Unsupported property class %S", field_class_name.c_str())); + throw std::runtime_error(RC::fmt("Unsupported property class {}", field_class_name)); } - auto get_native_delegate_type_name(Unreal::UFunction* signature_function, Unreal::UClass* current_class, bool strip_outer_name) -> File::StringType + auto get_native_delegate_type_name(Unreal::UFunction* signature_function, Unreal::UClass* current_class, bool strip_outer_name) -> SystemStringType { if (!is_delegate_signature_function(signature_function)) { - throw std::runtime_error(RC::fmt("Function %S is not a delegate signature function", signature_function->GetName().c_str())); + throw std::runtime_error(RC::fmt("Function {} is not a delegate signature function", signature_function->GetName())); } // Delegate names always start with F and have __DelegateSignature postfix - File::StringType delegate_type_name = strip_delegate_signature_postfix(signature_function); - delegate_type_name.insert(0, STR("F")); + SystemStringType delegate_type_name = strip_delegate_signature_postfix(signature_function); + delegate_type_name.insert(0, SYSSTR("F")); // Return the delegate name without the outer name if we have been requested to strip it if (strip_outer_name) @@ -816,16 +818,16 @@ namespace RC::UEGenerator { // For interface, delegates are declared inside the interface definition bool is_class_interface = delegate_outer_class->IsChildOf(); - const File::StringType outer_class_name = get_native_class_name(delegate_outer_class, is_class_interface); + const SystemStringType outer_class_name = get_native_class_name(delegate_outer_class, is_class_interface); - delegate_type_name.insert(0, STR("::")); + delegate_type_name.insert(0, SYSSTR("::")); delegate_type_name.insert(0, outer_class_name); } } else if (!delegate_outer->IsA()) { // Delegate signature functions should never exist outside the UPackage or UClass - throw std::runtime_error(RC::fmt("Delegate signature function %S does not have class or package as outer", delegate_outer->GetName().c_str())); + throw std::runtime_error(RC::fmt("Delegate signature function {} does not have class or package as outer", delegate_outer->GetName())); } return delegate_type_name; } @@ -835,17 +837,17 @@ namespace RC::UEGenerator return (function->GetFunctionFlags() & Unreal::FUNC_Delegate) != 0 && function->GetName().ends_with(DELEGATE_SIGNATURE_POSTFIX); } - auto strip_delegate_signature_postfix(Unreal::UFunction* signature_function) -> File::StringType + auto strip_delegate_signature_postfix(Unreal::UFunction* signature_function) -> SystemStringType { if (!is_delegate_signature_function(signature_function)) { - throw std::runtime_error(RC::fmt("Function %S is not a delegate signature function", signature_function->GetName().c_str())); + throw std::runtime_error(RC::fmt("Function {} is not a delegate signature function", signature_function->GetName())); } // Delegate names always start with F and have __DelegateSignature postfix - const File::StringType delegate_signature_postfix = DELEGATE_SIGNATURE_POSTFIX; + const SystemStringType delegate_signature_postfix = DELEGATE_SIGNATURE_POSTFIX_SYS; - File::StringType delegate_name = signature_function->GetName(); + auto delegate_name = to_system(signature_function->GetName()); delegate_name.erase(delegate_name.length() - delegate_signature_postfix.length()); return delegate_name; } diff --git a/UE4SS/src/SDKGenerator/Generator.cpp b/UE4SS/src/SDKGenerator/Generator.cpp index 6980cd161..81be14545 100644 --- a/UE4SS/src/SDKGenerator/Generator.cpp +++ b/UE4SS/src/SDKGenerator/Generator.cpp @@ -71,38 +71,38 @@ namespace RC::UEGenerator { std::filesystem::path primary_file_name; std::filesystem::path secondary_file_name; - std::vector ordered_primary_file_contents; - std::vector ordered_secondary_file_contents; - File::StringType package_name; + std::vector ordered_primary_file_contents; + std::vector ordered_secondary_file_contents; + SystemStringType package_name; File::Handle primary_file; File::Handle secondary_file; bool primary_file_has_no_contents; bool secondary_file_has_no_contents; }; - auto generate_tab(size_t num_tabs = 1) -> File::StringType + auto generate_tab(size_t num_tabs = 1) -> SystemStringType { - File::StringType tab_storage{}; + SystemStringType tab_storage{}; for (size_t i = 0; i < num_tabs; ++i) { - tab_storage += STR(" "); + tab_storage += SYSSTR(" "); } return tab_storage; } - auto generate_prefix(UStruct* obj) -> File::StringType + auto generate_prefix(UStruct* obj) -> SystemStringType { UClass* obj_class = obj->GetClassPrivate(); if (obj_class->IsChildOf()) { - return STR("struct"); + return SYSSTR("struct"); } else { - return STR("class"); + return SYSSTR("class"); } } - auto generate_class_name(UStruct* class_to_generate) -> File::StringType + auto generate_class_name(UStruct* class_to_generate) -> SystemStringType { if (class_to_generate->GetClassPrivate()->IsChildOf()) { @@ -134,7 +134,7 @@ namespace RC::UEGenerator // Map of FName.ComparisonIndex -> File::Handle std::unordered_map m_files{}; - std::unordered_map m_file_names{}; + std::unordered_map m_file_names{}; std::unordered_map m_classes_dumped{}; public: @@ -146,7 +146,7 @@ namespace RC::UEGenerator auto create_all_files() -> void { - Output::send(STR("Creating all files...\n")); + Output::send(SYSSTR("Creating all files...\n")); for (auto& [comparison_index, generated_file] : m_files) { if (!generated_file.ordered_primary_file_contents.empty()) @@ -158,7 +158,7 @@ namespace RC::UEGenerator sort_files(generated_file.ordered_primary_file_contents); - File::StringType combined_file_contents; + SystemStringType combined_file_contents; for (auto& line : generated_file.ordered_primary_file_contents) { combined_file_contents.append(line); @@ -166,11 +166,11 @@ namespace RC::UEGenerator if (combined_file_contents.empty()) { - Output::send(STR("Empty primary file contents in '{}'\n"), generated_file.package_name); + Output::send(SYSSTR("Empty primary file contents in '{}'\n"), generated_file.package_name); } else { - generated_file.primary_file.write_string_to_file(combined_file_contents); + generated_file.primary_file.write_file_string_to_file(to_file(combined_file_contents)); } specification.generate_file_footer(generated_file); @@ -185,7 +185,7 @@ namespace RC::UEGenerator sort_files(generated_file.ordered_secondary_file_contents); - File::StringType combined_file_contents; + SystemStringType combined_file_contents; for (auto& line : generated_file.ordered_secondary_file_contents) { combined_file_contents.append(line); @@ -193,11 +193,11 @@ namespace RC::UEGenerator if (combined_file_contents.empty()) { - Output::send(STR("Empty secondary file contents in '{}'\n"), generated_file.package_name); + Output::send(SYSSTR("Empty secondary file contents in '{}'\n"), generated_file.package_name); } else { - generated_file.secondary_file.write_string_to_file(combined_file_contents); + generated_file.secondary_file.write_file_string_to_file(to_file(combined_file_contents)); } generated_file.secondary_file.close(); @@ -205,18 +205,18 @@ namespace RC::UEGenerator } } - auto sort_files(std::vector& content) -> void + auto sort_files(std::vector& content) -> void { - std::vector struct_content; - std::vector class_content; - std::vector other_content; + std::vector struct_content; + std::vector class_content; + std::vector other_content; for (auto& line : content) { - if (line.starts_with(STR("struct"))) + if (line.starts_with(SYSSTR("struct"))) { struct_content.push_back(line); } - else if (line.starts_with(STR("class"))) + else if (line.starts_with(SYSSTR("class"))) { class_content.push_back(line); } @@ -237,7 +237,7 @@ namespace RC::UEGenerator content.insert(content.end(), other_content.begin(), other_content.end()); } - auto sort_types(std::vector& content) -> void + auto sort_types(std::vector& content) -> void { std::sort(content.begin(), content.end(), [&](const auto& a, const auto& b) { auto a_class_name = get_class_name(a); @@ -246,16 +246,16 @@ namespace RC::UEGenerator }); } - auto get_class_name(const auto& x) -> std::wstring + auto get_class_name(const auto& x) -> SystemStringType { // Using this method instead of regex because it is extremely slow - auto class_name = x.substr(x.find(STR(' ')) + 1); - class_name = class_name.substr(0, class_name.find(STR(' '))); - if (class_name == STR("class")) + auto class_name = x.substr(x.find(SYSSTR(' ')) + 1); + class_name = class_name.substr(0, class_name.find(SYSSTR(' '))); + if (class_name == SYSSTR("class")) { // Case for enum class - class_name = x.substr(x.find(STR(' ')) + 7); - class_name = class_name.substr(0, class_name.find(STR(' '))); + class_name = x.substr(x.find(SYSSTR(' ')) + 7); + class_name = class_name.substr(0, class_name.find(SYSSTR(' '))); } return class_name; } @@ -265,11 +265,11 @@ namespace RC::UEGenerator return object->GetClassPrivate()->GetNamePrivate().Equals(Unreal::GPackageName); } - auto generate_offset_comment(XProperty* property, File::StringType& line) -> File::StringType + auto generate_offset_comment(XProperty* property, SystemStringType& line) -> SystemStringType { if (UE4SSProgram::settings_manager.CXXHeaderGenerator.DumpOffsetsAndSizes) { - return std::format(STR("{:85} // 0x{:04X} (size: 0x{:X})"), line, property->GetOffset_Internal(), property->GetSize()); + return std::format(SYSSTR("{:85} // 0x{:04X} (size: 0x{:X})"), line, property->GetOffset_Internal(), property->GetSize()); } else { @@ -323,7 +323,7 @@ namespace RC::UEGenerator auto generate_function_declaration(ObjectInfo& owner, const FunctionInfo& function_info, GeneratedFile& generated_file, - File::StringType& out_current_class_content, + SystemStringType& out_current_class_content, IsDelegateFunction is_delegate_function = IsDelegateFunction::No) -> void { std::optional return_property_info = [&]() -> std::optional { @@ -342,14 +342,14 @@ namespace RC::UEGenerator return return_property_info.has_value() ? return_property_info.value().property : nullptr; }(); - File::StringType function_name{function_info.function->GetName()}; + SystemStringType function_name{to_system(function_info.function->GetName())}; if (is_delegate_function == IsDelegateFunction::Yes) { // Remove the last 19 characters, which is always '__DelegateSignature' for delegates function_name.erase(function_name.size() - 19, 19); } - StringType current_class_content{}; + SystemStringType current_class_content{}; specification.generate_function_declaration(this, current_class_content, owner, function_info, function_name, return_property, return_property_info); // Commenting out this code because all network replicated functions are events @@ -357,14 +357,14 @@ namespace RC::UEGenerator /* if ((function->get_function_flags() & Unreal::EFunctionFlags::FUNC_Event) != 0) { - current_class_content.append(STR(" // EVENT")); + current_class_content.append(SYSSTR(" // EVENT")); } //*/ - current_class_content.append(STR("\n")); + current_class_content.append(SYSSTR("\n")); out_current_class_content.append(current_class_content); } - auto generate_class_dependency(ObjectInfo& owner, UStruct* inherited_class, File::StringType& current_class_content) -> void + auto generate_class_dependency(ObjectInfo& owner, UStruct* inherited_class, SystemStringType& current_class_content) -> void { if (!inherited_class) { @@ -377,7 +377,7 @@ namespace RC::UEGenerator if (package_file_for_inherited_class) { auto& inherited_object_info = m_classes_dumped.emplace(inherited_class, ObjectInfo{inherited_class, &owner}).first->second; - File::StringType new_class_content{}; + SystemStringType new_class_content{}; generate_class(inherited_object_info, *package_file_for_inherited_class, new_class_content); if (!package_file_for_inherited_class->primary_file_has_no_contents) { @@ -389,7 +389,7 @@ namespace RC::UEGenerator } } - auto generate_class_dependency_from_property(ObjectInfo& owner, XProperty* property, File::StringType& current_class_content) -> bool + auto generate_class_dependency_from_property(ObjectInfo& owner, XProperty* property, SystemStringType& current_class_content) -> bool { if (property->IsA()) { @@ -441,7 +441,7 @@ namespace RC::UEGenerator return false; } - auto make_function_info(ObjectInfo& owner, UFunction* function, File::StringType& current_class_content) -> FunctionInfo + auto make_function_info(ObjectInfo& owner, UFunction* function, SystemStringType& current_class_content) -> FunctionInfo { FunctionInfo function_info{ .function = function, @@ -461,7 +461,7 @@ namespace RC::UEGenerator return function_info; } - auto generate_class(ObjectInfo object_info, GeneratedFile& generated_file, File::StringType& current_class_content) -> XProperty* + auto generate_class(ObjectInfo object_info, GeneratedFile& generated_file, SystemStringType& current_class_content) -> XProperty* { UStruct* native_class = static_cast(object_info.object); if (specification.should_generate_class(native_class)) @@ -477,7 +477,7 @@ namespace RC::UEGenerator { generated_file.secondary_file_has_no_contents = false; - File::StringType content_buffer; + SystemStringType content_buffer; UEnum* uenum = static_cast(native_object); specification.generate_enum_declaration(content_buffer, uenum); @@ -488,17 +488,17 @@ namespace RC::UEGenerator size_t colon_pos = enum_value_full_name.rfind(STR(":")); auto enum_value_name = colon_pos == enum_value_full_name.npos ? enum_value_full_name : enum_value_full_name.substr(colon_pos + 1); - specification.generate_enum_member(content_buffer, uenum, enum_value_name, elem); + specification.generate_enum_member(content_buffer, uenum, to_system(enum_value_name), elem); } specification.generate_enum_end(content_buffer, uenum); - content_buffer.append(STR("\n\n")); + content_buffer.append(SYSSTR("\n\n")); generated_file.ordered_secondary_file_contents.push_back(content_buffer); } - auto generate_package(UObject* package, File::StringType& out) -> void + auto generate_package(UObject* package, SystemStringType& out) -> void { UObjectGlobals::ForEachUObject([&](void* object, [[maybe_unused]] int32_t chunk_index, [[maybe_unused]] int32_t object_index) { return LoopAction::Continue; @@ -538,10 +538,10 @@ namespace RC::UEGenerator else { // Get rid of everything before the last slash + the last slash, leaving only the actual name - File::StringType package_name = package->GetNamePrivate().ToString(); - package_name = package_name.substr(package_name.rfind(STR("/")) + 1); - File::StringType package_name_all_lower = package_name; - std::transform(package_name_all_lower.begin(), package_name_all_lower.end(), package_name_all_lower.begin(), [](File::CharType c) { + SystemStringType package_name = to_system(package->GetNamePrivate().ToString()); + package_name = package_name.substr(package_name.rfind(SYSSTR("/")) + 1); + SystemStringType package_name_all_lower = package_name; + std::transform(package_name_all_lower.begin(), package_name_all_lower.end(), package_name_all_lower.begin(), [](SystemCharType c) { return std::towlower(c); }); @@ -549,25 +549,29 @@ namespace RC::UEGenerator { // File name collision auto& file_name = m_file_names[package_name_all_lower]; - package_name.append(std::format(STR("_DUPL_{}"), ++file_name.num_collisions)); - Output::send(STR("File name collision, renamed to '{}'\n"), package_name); + package_name.append(std::format(SYSSTR("_DUPL_{}"), ++file_name.num_collisions)); + Output::send(SYSSTR("File name collision, renamed to '{}'\n"), package_name); } else { m_file_names.emplace(package_name_all_lower, FileName{}); } - // The '\\?\' at the beginning of the string unlocks path size restriction from MAX_PATH to 32k +// The '\\?\' at the beginning of the string unlocks path size restriction from MAX_PATH to 32k +#ifdef WIN32 std::filesystem::path directory_to_generate_in = std::filesystem::path("\\\\?\\"); directory_to_generate_in += (m_directory_to_generate_in); +#else + std::filesystem::path directory_to_generate_in = (m_directory_to_generate_in); +#endif - File::StringType ext = specification.get_file_extension(); + SystemStringType ext = specification.get_file_extension(); std::filesystem::path primary_file_path_and_name = directory_to_generate_in; primary_file_path_and_name.append(package_name); primary_file_path_and_name.replace_extension(ext); std::filesystem::path secondary_file_path_and_name = directory_to_generate_in; - secondary_file_path_and_name.append(package_name + STR("_enums")); + secondary_file_path_and_name.append(package_name + SYSSTR("_enums")); secondary_file_path_and_name.replace_extension(ext); GeneratedFile generated_file{ @@ -612,9 +616,9 @@ namespace RC::UEGenerator public: auto generate() -> void { - Output::send(STR("Cleaning up old SDK files...\n")); + Output::send(SYSSTR("Cleaning up old SDK files...\n")); cleanup_old_sdk(); - Output::send(STR("Generating SDK...\n")); + Output::send(SYSSTR("Generating SDK...\n")); // 400k should be enough for most games, and it's highly unlikely to cause more than one reallocation even if the game is huge m_classes_dumped.reserve(400000); @@ -643,7 +647,7 @@ namespace RC::UEGenerator { // Generate a class for this object auto& object_info = m_classes_dumped.emplace(object, ObjectInfo{object}).first->second; - File::StringType class_content{}; + SystemStringType class_content{}; generate_class(object_info, *package_file, class_content); if (!package_file->primary_file_has_no_contents) { @@ -667,66 +671,67 @@ namespace RC::UEGenerator class CXXHeaderGenerator { public: - auto get_file_extension() -> File::StringType + auto get_file_extension() -> SystemStringType { - return STR(".hpp"); + return SYSSTR(".hpp"); } auto generate_file_header(GeneratedFile& generated_file) -> void { - generated_file.primary_file.write_string_to_file( - std::format(STR("#ifndef UE4SS_SDK_{}_HPP\n#define UE4SS_SDK_{}_HPP\n\n"), generated_file.package_name, generated_file.package_name)); + generated_file.primary_file.write_file_string_to_file(to_file( + std::format(SYSSTR("#ifndef UE4SS_SDK_{}_HPP\n#define UE4SS_SDK_{}_HPP\n\n"), generated_file.package_name, generated_file.package_name))); if (!generated_file.secondary_file_has_no_contents) { - generated_file.primary_file.write_string_to_file(std::format(STR("#include \"{}\"\n\n"), generated_file.secondary_file_name.filename().c_str())); + generated_file.primary_file.write_file_string_to_file( + to_file(std::format(SYSSTR("#include \"{}\"\n\n"), generated_file.secondary_file_name.filename().c_str()))); } } auto generate_file_footer(GeneratedFile& generated_file) -> void { - generated_file.primary_file.write_string_to_file(std::format(STR("#endif\n"))); + generated_file.primary_file.write_file_string_to_file(IOSTR("#endif\n")); } - auto generate_enum_declaration(File::StringType& content_buffer, UEnum* uenum) -> void + auto generate_enum_declaration(SystemStringType& content_buffer, UEnum* uenum) -> void { const auto cpp_form = uenum->GetCppForm(); if (cpp_form == UEnum::ECppForm::Regular) { - content_buffer.append(std::format(STR("enum {} {{\n"), get_native_enum_name(uenum, false))); + content_buffer.append(std::format(SYSSTR("enum {} {{\n"), get_native_enum_name(uenum, false))); } else if (cpp_form == UEnum::ECppForm::Namespaced) { - content_buffer.append(std::format(STR("namespace {} {{\n{}enum Type {{\n"), get_native_enum_name(uenum, false), generate_tab())); + content_buffer.append(std::format(SYSSTR("namespace {} {{\n{}enum Type {{\n"), get_native_enum_name(uenum, false), generate_tab())); } else if (cpp_form == UEnum::ECppForm::EnumClass) { - content_buffer.append(std::format(STR("enum class {} {{\n"), get_native_enum_name(uenum, false))); + content_buffer.append(std::format(SYSSTR("enum class {} {{\n"), get_native_enum_name(uenum, false))); } } - auto generate_enum_member(File::StringType& content_buffer, UEnum* uenum, const File::StringType& enum_value_name, const Unreal::FEnumNamePair& elem) -> void + auto generate_enum_member(SystemStringType& content_buffer, UEnum* uenum, const SystemStringType& enum_value_name, const Unreal::FEnumNamePair& elem) -> void { - content_buffer.append(std::format(STR("{}{}{} = {},\n"), + content_buffer.append(std::format(SYSSTR("{}{}{} = {},\n"), generate_tab(), - uenum->GetCppForm() == UEnum::ECppForm::Namespaced ? generate_tab() : STR(""), + uenum->GetCppForm() == UEnum::ECppForm::Namespaced ? generate_tab() : SYSSTR(""), enum_value_name, elem.Value)); } - auto generate_enum_end(File::StringType& content_buffer, UEnum* uenum) -> void + auto generate_enum_end(SystemStringType& content_buffer, UEnum* uenum) -> void { const auto cpp_form = uenum->GetCppForm(); - content_buffer.append(std::format(STR("{}}};"), cpp_form == UEnum::ECppForm::Namespaced ? generate_tab() : STR(""))); + content_buffer.append(std::format(SYSSTR("{}}};"), cpp_form == UEnum::ECppForm::Namespaced ? generate_tab() : SYSSTR(""))); if (cpp_form == UEnum::ECppForm::Namespaced) { - content_buffer.append(STR("\n}")); + content_buffer.append(SYSSTR("\n}")); } } auto should_generate_class(UStruct* native_class) { return true; } - auto generate_class(TypeGenerator* generator, ObjectInfo& object_info, GeneratedFile& generated_file, File::StringType& current_class_content) + auto generate_class(TypeGenerator* generator, ObjectInfo& object_info, GeneratedFile& generated_file, SystemStringType& current_class_content) { UStruct* native_class = static_cast(object_info.object); - File::StringType content_buffer{}; + SystemStringType content_buffer{}; UStruct* inherits_from_class = native_class->GetSuperStruct(); @@ -772,22 +777,24 @@ namespace RC::UEGenerator int32_t current_property_offset = property->GetOffset_Internal(); int32_t current_property_size = property->GetSize(); - StringType part_one{}; + SystemStringType part_one{}; try { - part_one = std::format(STR("{}{}{} {};"), + part_one = std::format(SYSSTR("{}{}{} {};"), generate_tab(), - property_info.should_forward_declare ? STR("class ") : STR(""), + property_info.should_forward_declare ? SYSSTR("class ") : SYSSTR(""), generate_property_cxx_name(property, true, native_class, EnableForwardDeclarations::Yes), - property->GetName()); + to_system(property->GetName())); } catch (std::exception& e) { - Output::send(STR("Could not generate property '{}' because: {}\n"), property->GetFullName(), to_wstring(e.what())); + Output::send(SYSSTR("Could not generate property '{}' because: {}\n"), + to_system(property->GetFullName()), + to_system(e.what())); continue; } - content_buffer.append(std::format(STR("{}\n"), generator->generate_offset_comment(property, part_one))); + content_buffer.append(std::format(SYSSTR("{}\n"), generator->generate_offset_comment(property, part_one))); if (property->IsA()) { @@ -839,12 +846,12 @@ namespace RC::UEGenerator int32_t padding_property_offset = current_property_end_location; int32_t padding_property_size = next_property_offset - padding_property_offset; - auto padding_part_one = std::format(STR("{}char {}[0x{:X}];"), + auto padding_part_one = std::format(SYSSTR("{}char {}[0x{:X}];"), generate_tab(), - std::format(STR("padding_{}"), num_padding_elements++), + std::format(SYSSTR("padding_{}"), num_padding_elements++), padding_property_size); content_buffer.append( - std::format(STR("{:85} // 0x{:04X} (size: 0x{:X})\n"), padding_part_one, padding_property_offset, padding_property_size)); + std::format(SYSSTR("{:85} // 0x{:04X} (size: 0x{:X})\n"), padding_part_one, padding_property_offset, padding_property_size)); } } } @@ -858,7 +865,7 @@ namespace RC::UEGenerator // Functions if (native_class->HasChildren()) { - content_buffer.append(STR("\n")); + content_buffer.append(SYSSTR("\n")); for (const auto& function_info : functions_to_generate) { generator->generate_function_declaration(object_info, function_info, generated_file, content_buffer); @@ -867,26 +874,26 @@ namespace RC::UEGenerator generate_class_end(content_buffer, class_size); - content_buffer.append(STR("\n\n")); + content_buffer.append(SYSSTR("\n\n")); current_class_content.append(content_buffer); } - auto generate_class_declaration(File::StringType& content_buffer, UStruct* native_class, UStruct* inherits_from_class) -> void + auto generate_class_declaration(SystemStringType& content_buffer, UStruct* native_class, UStruct* inherits_from_class) -> void { auto class_name = generate_class_name(native_class); if (inherits_from_class) { content_buffer.append( - std::format(STR("{} {} : public {}\n{{\n"), generate_prefix(native_class), class_name, generate_class_name(inherits_from_class))); + std::format(SYSSTR("{} {} : public {}\n{{\n"), generate_prefix(native_class), class_name, generate_class_name(inherits_from_class))); } else { - content_buffer.append(std::format(STR("{} {}\n{{\n"), generate_prefix(native_class), class_name)); + content_buffer.append(std::format(SYSSTR("{} {}\n{{\n"), generate_prefix(native_class), class_name)); } } - auto generate_class_struct_end(File::StringType& content_buffer, - const File::StringType& class_name, + auto generate_class_struct_end(SystemStringType& content_buffer, + const SystemStringType& class_name, size_t class_size, int32_t num_padding_elements, XProperty* last_property_in_this_class) -> void @@ -912,8 +919,9 @@ namespace RC::UEGenerator printf_s("last_property_offset: %X\n", last_property_offset); printf_s("first_property_offset: %X\n", first_property_offset); - auto padding_part_one = std::format(STR("{}char {}[0x{:X}];"), generate_tab(), std::format(STR("padding_{}"), num_padding_elements), padding_size); - out.append(std::format(STR("{:85} // 0x{:04X} (size: 0x{:X})\n"), padding_part_one, last_property_offset + last_property_size, padding_size)); + auto padding_part_one = std::format(SYSSTR("{}char {}[0x{:X}];"), generate_tab(), std::format(SYSSTR("padding_{}"), + num_padding_elements), padding_size); out.append(std::format(SYSSTR("{:85} // 0x{:04X} (size: 0x{:X})\n"), padding_part_one, + last_property_offset + last_property_size, padding_size)); } } //*/ @@ -923,32 +931,32 @@ namespace RC::UEGenerator // No reflected member variables exist but there are non-reflected member variables // Add padding for non-reflected member variables, for alignment purposes auto padding_part_one = - std::format(STR("{}char {}[0x{:X}];"), generate_tab(), std::format(STR("padding_{}"), num_padding_elements), class_size); - content_buffer.append(std::format(STR("{:85} // 0x0000 (size: 0x{:X})\n"), padding_part_one, 0x0)); + std::format(SYSSTR("{}char {}[0x{:X}];"), generate_tab(), std::format(SYSSTR("padding_{}"), num_padding_elements), class_size); + content_buffer.append(std::format(SYSSTR("{:85} // 0x0000 (size: 0x{:X})\n"), padding_part_one, 0x0)); } } } - auto generate_class_end(File::StringType& content_buffer, size_t class_size) -> void + auto generate_class_end(SystemStringType& content_buffer, size_t class_size) -> void { if (UE4SSProgram::settings_manager.CXXHeaderGenerator.DumpOffsetsAndSizes) { - content_buffer.append(std::format(STR("}}; // Size: 0x{:X}"), class_size)); + content_buffer.append(std::format(SYSSTR("}}; // Size: 0x{:X}"), class_size)); } else { - content_buffer.append(STR("};")); + content_buffer.append(SYSSTR("};")); } } auto generate_function_declaration(TypeGenerator* generator, - File::StringType& current_class_content, + SystemStringType& current_class_content, ObjectInfo& owner, const FunctionInfo& function_info, - File::StringType function_name, + SystemStringType function_name, XProperty* return_property, std::optional return_property_info) -> void { - File::StringType function_type_name{}; + SystemStringType function_type_name{}; if (return_property) { try @@ -957,23 +965,23 @@ namespace RC::UEGenerator } catch (std::exception& e) { - Output::send(STR("Could not generate function '{}' because: {}\n"), - function_info.function->GetFullName(), - to_wstring(e.what())); + Output::send(SYSSTR("Could not generate function '{}' because: {}\n"), + to_system(function_info.function->GetFullName()), + to_system(e.what())); return; } if (return_property_info.value().should_forward_declare && !generator->check_ignore_forward_declaration(owner, return_property)) { - function_type_name.insert(0, STR("class ")); + function_type_name.insert(0, SYSSTR("class ")); } } else { - function_type_name = STR("void"); + function_type_name = SYSSTR("void"); } - current_class_content.append(std::format(STR("{}{} {}("), generate_tab(), function_type_name, function_name)); + current_class_content.append(std::format(SYSSTR("{}{} {}("), generate_tab(), function_type_name, function_name)); for (size_t i = 0; i < function_info.params.size(); ++i) { @@ -983,18 +991,18 @@ namespace RC::UEGenerator try { current_class_content.append( - std::format(STR("{}{}{}{} {}"), - param_info.property->HasAnyPropertyFlags(Unreal::CPF_ConstParm) ? STR("const ") : STR(""), - param_info.should_forward_declare ? STR("class ") : STR(""), + std::format(SYSSTR("{}{}{}{} {}"), + param_info.property->HasAnyPropertyFlags(Unreal::CPF_ConstParm) ? SYSSTR("const ") : SYSSTR(""), + param_info.should_forward_declare ? SYSSTR("class ") : SYSSTR(""), generate_property_cxx_name(param_info.property, true, function_info.function, EnableForwardDeclarations::Yes), - param_info.property->HasAnyPropertyFlags(Unreal::CPF_ReferenceParm | Unreal::CPF_OutParm) ? STR("&") : STR(""), - param_info.property->GetName())); + param_info.property->HasAnyPropertyFlags(Unreal::CPF_ReferenceParm | Unreal::CPF_OutParm) ? SYSSTR("&") : SYSSTR(""), + to_system(param_info.property->GetName()))); } catch (std::exception& e) { - Output::send(STR("Could not generate function '{}' because: {}\n"), - function_info.function->GetFullName(), - to_wstring(e.what())); + Output::send(SYSSTR("Could not generate function '{}' because: {}\n"), + to_system(function_info.function->GetFullName()), + to_system(e.what())); return; } @@ -1003,24 +1011,25 @@ namespace RC::UEGenerator auto* next_param = function_info.params[i + 1].property; if (next_param && (!next_param->HasAnyPropertyFlags(Unreal::CPF_ReturnParm) || i + 2 < function_info.params.size())) { - current_class_content.append(STR(", ")); + current_class_content.append(SYSSTR(", ")); } } } } - current_class_content.append(STR(");")); + current_class_content.append(SYSSTR(");")); } }; class LuaTypesGenerator { private: - auto is_valid_lua_symbol(const File::StringType& str) -> bool + auto is_valid_lua_symbol(const SystemStringType& str) -> bool { - static const std::set keywords = {STR("and"), STR("break"), STR("do"), STR("else"), STR("elseif"), STR("end"), - STR("false"), STR("for"), STR("function"), STR("if"), STR("in"), STR("local"), - STR("nil"), STR("not"), STR("or"), STR("repeat"), STR("return"), STR("then"), - STR("true"), STR("until"), STR("while")}; + static const std::set keywords = {SYSSTR("and"), SYSSTR("break"), SYSSTR("do"), SYSSTR("else"), SYSSTR("elseif"), + SYSSTR("end"), SYSSTR("false"), SYSSTR("for"), SYSSTR("function"), SYSSTR("if"), + SYSSTR("in"), SYSSTR("local"), SYSSTR("nil"), SYSSTR("not"), SYSSTR("or"), + SYSSTR("repeat"), SYSSTR("return"), SYSSTR("then"), SYSSTR("true"), SYSSTR("until"), + SYSSTR("while")}; if (keywords.contains(str)) { return false; @@ -1042,9 +1051,9 @@ namespace RC::UEGenerator } return true; } - auto quote_lua_symbol(const File::StringType& symbol) -> File::StringType + auto quote_lua_symbol(const SystemStringType& symbol) -> SystemStringType { - File::StringType quoted; + SystemStringType quoted; quoted.reserve(symbol.size() + 2); quoted.push_back('\''); for (auto it = symbol.begin(); it != symbol.end(); ++it) @@ -1059,9 +1068,9 @@ namespace RC::UEGenerator quoted.push_back('\''); return quoted; } - auto make_valid_symbol(const File::StringType& symbol) -> File::StringType + auto make_valid_symbol(const SystemStringType& symbol) -> SystemStringType { - File::StringType valid; + SystemStringType valid; valid.reserve(symbol.size()); auto it = symbol.begin(); if (it == symbol.end() || std::isdigit(*it)) @@ -1077,29 +1086,29 @@ namespace RC::UEGenerator } public: - auto get_file_extension() -> File::StringType + auto get_file_extension() -> SystemStringType { - return STR(".lua"); + return SYSSTR(".lua"); } auto generate_file_header(GeneratedFile& generated_file) -> void { - generated_file.primary_file.write_string_to_file(STR("---@meta\n\n")); + generated_file.primary_file.write_file_string_to_file(IOSTR("---@meta\n\n")); } auto generate_file_footer(GeneratedFile& generated_file) -> void { } - auto generate_enum_declaration(File::StringType& content_buffer, UEnum* uenum) -> void + auto generate_enum_declaration(SystemStringType& content_buffer, UEnum* uenum) -> void { - auto enum_name = uenum->GetName(); - content_buffer.append(std::format(STR("---@enum {}\n{} = {{\n"), enum_name, enum_name)); + auto enum_name = to_system(uenum->GetName()); + content_buffer.append(std::format(SYSSTR("---@enum {}\n{} = {{\n"), enum_name, enum_name)); } - auto generate_enum_member(File::StringType& content_buffer, UEnum* uenum, const File::StringType& enum_value_name, const Unreal::FEnumNamePair& elem) -> void + auto generate_enum_member(SystemStringType& content_buffer, UEnum* uenum, const SystemStringType& enum_value_name, const Unreal::FEnumNamePair& elem) -> void { - content_buffer.append(std::format(STR("{}{} = {},\n"), generate_tab(), enum_value_name, elem.Value)); + content_buffer.append(std::format(SYSSTR("{}{} = {},\n"), generate_tab(), enum_value_name, elem.Value)); } - auto generate_enum_end(File::StringType& content_buffer, UEnum* uenum) -> void + auto generate_enum_end(SystemStringType& content_buffer, UEnum* uenum) -> void { - content_buffer.append(STR("}")); + content_buffer.append(SYSSTR("}")); } auto should_generate_class(UStruct* native_class) @@ -1107,10 +1116,10 @@ namespace RC::UEGenerator // skip UObject to define externally return native_class != UObject::StaticClass(); } - auto generate_class(TypeGenerator* generator, ObjectInfo& object_info, GeneratedFile& generated_file, File::StringType& current_class_content) + auto generate_class(TypeGenerator* generator, ObjectInfo& object_info, GeneratedFile& generated_file, SystemStringType& current_class_content) { UStruct* native_class = static_cast(object_info.object); - File::StringType content_buffer{}; + SystemStringType content_buffer{}; UStruct* inherits_from_class = native_class->GetSuperStruct(); @@ -1158,20 +1167,23 @@ namespace RC::UEGenerator try { - const auto& property_name = property->GetName(); + const auto& property_name = to_system(property->GetName()); if (is_valid_lua_symbol(property_name)) { - content_buffer.append(std::format(STR("---@field {} {}\n"), property_name, generate_property_lua_name(property, true, native_class))); + content_buffer.append(std::format(SYSSTR("---@field {} {}\n"), property_name, generate_property_lua_name(property, true, native_class))); } else { - content_buffer.append( - std::format(STR("---@field [{}] {}\n"), quote_lua_symbol(property_name), generate_property_lua_name(property, true, native_class))); + content_buffer.append(std::format(SYSSTR("---@field [{}] {}\n"), + quote_lua_symbol(property_name), + generate_property_lua_name(property, true, native_class))); } } catch (std::exception& e) { - Output::send(STR("Could not generate property '{}' because: {}\n"), property->GetFullName(), to_wstring(e.what())); + Output::send(SYSSTR("Could not generate property '{}' because: {}\n"), + to_system(property->GetFullName()), + to_system(e.what())); continue; } @@ -1186,7 +1198,7 @@ namespace RC::UEGenerator // Functions if (native_class->HasChildren()) { - content_buffer.append(STR("\n")); + content_buffer.append(SYSSTR("\n")); for (const auto& function_info : functions_to_generate) { generator->generate_function_declaration(object_info, function_info, generated_file, content_buffer); @@ -1195,39 +1207,39 @@ namespace RC::UEGenerator generate_class_end(content_buffer, class_size); - content_buffer.append(STR("\n\n")); + content_buffer.append(SYSSTR("\n\n")); current_class_content.append(content_buffer); } - auto generate_class_declaration(File::StringType& content_buffer, UStruct* native_class, UStruct* inherits_from_class) -> void + auto generate_class_declaration(SystemStringType& content_buffer, UStruct* native_class, UStruct* inherits_from_class) -> void { auto class_name = generate_class_name(native_class); if (inherits_from_class) { - content_buffer.append(std::format(STR("---@class {} : {}\n"), class_name, generate_class_name(inherits_from_class))); + content_buffer.append(std::format(SYSSTR("---@class {} : {}\n"), class_name, generate_class_name(inherits_from_class))); } else { - content_buffer.append(std::format(STR("---@class {}\n"), class_name)); + content_buffer.append(std::format(SYSSTR("---@class {}\n"), class_name)); } } - auto generate_class_struct_end(File::StringType& content_buffer, - const File::StringType& class_name, + auto generate_class_struct_end(SystemStringType& content_buffer, + const SystemStringType& class_name, size_t class_size, int32_t num_padding_elements, XProperty* last_property_in_this_class) -> void { - content_buffer.append(std::format(STR("{} = {{}}\n"), class_name)); + content_buffer.append(std::format(SYSSTR("{} = {{}}\n"), class_name)); } - auto generate_class_end(File::StringType& content_buffer, size_t class_size) -> void + auto generate_class_end(SystemStringType& content_buffer, size_t class_size) -> void { } auto generate_function_declaration(TypeGenerator* generator, - File::StringType& current_class_content, + SystemStringType& current_class_content, ObjectInfo& owner, const FunctionInfo& function_info, - File::StringType function_name, + SystemStringType function_name, XProperty* return_property, std::optional return_property_info) -> void { @@ -1238,17 +1250,17 @@ namespace RC::UEGenerator { try { - auto param_name = param_info.property->GetName(); + auto param_name = to_system(param_info.property->GetName()); // TODO disambiguate param renames - current_class_content.append(std::format(STR("---@param {} {}\n"), + current_class_content.append(std::format(SYSSTR("---@param {} {}\n"), make_valid_symbol(param_name), generate_property_lua_name(param_info.property, true, function_info.function))); } catch (std::exception& e) { - Output::send(STR("Could not generate function '{}' because: {}\n"), - function_info.function->GetFullName(), - to_wstring(e.what())); + Output::send(SYSSTR("Could not generate function '{}' because: {}\n"), + to_system(function_info.function->GetFullName()), + to_system(e.what())); return; } } @@ -1258,13 +1270,13 @@ namespace RC::UEGenerator { try { - current_class_content.append(std::format(STR("---@return {}\n"), generate_property_lua_name(return_property, true, function_info.function))); + current_class_content.append(std::format(SYSSTR("---@return {}\n"), generate_property_lua_name(return_property, true, function_info.function))); } catch (std::exception& e) { - Output::send(STR("Could not generate function '{}' because: {}\n"), - function_info.function->GetFullName(), - to_wstring(e.what())); + Output::send(SYSSTR("Could not generate function '{}' because: {}\n"), + to_system(function_info.function->GetFullName()), + to_system(e.what())); return; } } @@ -1273,11 +1285,11 @@ namespace RC::UEGenerator if (is_valid_lua_symbol(function_name)) { - current_class_content.append(std::format(STR("function {}:{}("), class_name, function_name)); + current_class_content.append(std::format(SYSSTR("function {}:{}("), class_name, function_name)); } else { - current_class_content.append(std::format(STR("{}[{}] = function("), class_name, quote_lua_symbol(function_name))); + current_class_content.append(std::format(SYSSTR("{}[{}] = function("), class_name, quote_lua_symbol(function_name))); } for (size_t i = 0; i < function_info.params.size(); ++i) @@ -1285,21 +1297,21 @@ namespace RC::UEGenerator const auto& param_info = function_info.params[i]; if (!param_info.property->HasAnyPropertyFlags(Unreal::CPF_ReturnParm)) { - auto param_name = param_info.property->GetName(); + auto param_name = to_system(param_info.property->GetName()); // TODO disambiguate param renames - current_class_content.append(std::format(STR("{}"), make_valid_symbol(param_name))); + current_class_content.append(std::format(SYSSTR("{}"), make_valid_symbol(param_name))); if (i + 1 < function_info.params.size()) { auto* next_param = function_info.params[i + 1].property; if (next_param && (!next_param->HasAnyPropertyFlags(Unreal::CPF_ReturnParm) || i + 2 < function_info.params.size())) { - current_class_content.append(STR(", ")); + current_class_content.append(SYSSTR(", ")); } } } } - current_class_content.append(STR(") end")); + current_class_content.append(SYSSTR(") end")); } }; diff --git a/UE4SS/src/SDKGenerator/JSONDumper.cpp b/UE4SS/src/SDKGenerator/JSONDumper.cpp index a8163d063..0b98a75e0 100644 --- a/UE4SS/src/SDKGenerator/JSONDumper.cpp +++ b/UE4SS/src/SDKGenerator/JSONDumper.cpp @@ -15,7 +15,7 @@ namespace RC::UEGenerator::JSONDumper { using namespace ::RC::Unreal; - auto static is_valid_class_to_dump(File::StringViewType class_name, UObject* object) -> bool + auto static is_valid_class_to_dump(SystemStringViewType class_name, UObject* object) -> bool { static bool is_below_425 = Unreal::Version::IsBelow(4, 25); if (is_below_425 && Unreal::TypeChecker::is_property(object)) @@ -41,31 +41,31 @@ namespace RC::UEGenerator::JSONDumper return false; } - if (class_name.find(STR("BP_")) != class_name.npos) + if (class_name.find(SYSSTR("BP_")) != class_name.npos) { return true; } - if (class_name.find(STR("ENE_")) != class_name.npos) + if (class_name.find(SYSSTR("ENE_")) != class_name.npos) { return true; } - if (class_name.find(STR("BPL_")) != class_name.npos) + if (class_name.find(SYSSTR("BPL_")) != class_name.npos) { return true; } - if (class_name.find(STR("OBJ_")) != class_name.npos) + if (class_name.find(SYSSTR("OBJ_")) != class_name.npos) { return true; } - if (class_name.find(STR("LIB_")) != class_name.npos) + if (class_name.find(SYSSTR("LIB_")) != class_name.npos) { return true; } - if (class_name.find(STR("PRJ_")) != class_name.npos) + if (class_name.find(SYSSTR("PRJ_")) != class_name.npos) { return true; } - if (class_name.find(STR("WPN_")) != class_name.npos) + if (class_name.find(SYSSTR("WPN_")) != class_name.npos) { return true; } @@ -177,21 +177,21 @@ namespace RC::UEGenerator::JSONDumper return should_skip_general_function(function); } - auto static should_skip_event(File::StringViewType event_name) -> bool + auto static should_skip_event(SystemStringViewType event_name) -> bool { - if (event_name.find(STR("BndEvt")) != event_name.npos) + if (event_name.find(SYSSTR("BndEvt")) != event_name.npos) { return true; } return false; } - auto dump_to_json(File::StringViewType file_name) -> void + auto dump_to_json(SystemStringViewType file_name) -> void { - Output::send(STR("Loading all assets...\n")); + Output::send(SYSSTR("Loading all assets...\n")); UAssetRegistry::LoadAllAssets(); - Output::send(STR("Dumping to JSON file\n")); + Output::send(SYSSTR("Dumping to JSON file\n")); auto json = JSON::Array{}; UObjectGlobals::ForEachUObject([&](void* raw_object, int32_t chunk_index, int32_t object_index) { @@ -201,7 +201,7 @@ namespace RC::UEGenerator::JSONDumper } UObject* object = static_cast(raw_object); - auto object_name = object->GetName(); + auto object_name = to_system(object->GetName()); if (!is_valid_class_to_dump(object_name, object)) { return LoopAction::Continue; @@ -211,17 +211,17 @@ namespace RC::UEGenerator::JSONDumper object_name.erase(object_name.size() - 2, 2); auto& bp_class = json.new_object(); - bp_class.new_string(STR("bp_class"), object_name); + bp_class.new_string(SYSSTR("bp_class"), object_name); if (auto* super_struct = object_as_class->GetSuperStruct(); super_struct) { - bp_class.new_string(STR("inherits"), super_struct->GetName()); + bp_class.new_string(SYSSTR("inherits"), to_system(super_struct->GetName())); } else { - bp_class.new_null(STR("inherits")); + bp_class.new_null(SYSSTR("inherits")); } - auto& events = bp_class.new_array(STR("events")); + auto& events = bp_class.new_array(SYSSTR("events")); for (UFunction* event_function : object_as_class->ForEachFunction()) { if (should_skip_general_function(event_function)) @@ -233,16 +233,16 @@ namespace RC::UEGenerator::JSONDumper continue; } - auto event_name = event_function->GetName(); + auto event_name = to_system(event_function->GetName()); if (should_skip_event(event_name)) { continue; } auto& bp_events = events.new_object(); - bp_events.new_string(STR("name"), event_name); + bp_events.new_string(SYSSTR("name"), event_name); - auto& bp_event_args = bp_events.new_array(STR("args")); + auto& bp_event_args = bp_events.new_array(SYSSTR("args")); for (FProperty* param : event_function->ForEachProperty()) { if (should_skip_property(param)) @@ -251,15 +251,15 @@ namespace RC::UEGenerator::JSONDumper } auto& bp_event_arg = bp_event_args.new_object(); - bp_event_arg.new_string(STR("name"), param->GetName()); - bp_event_arg.new_string(STR("type"), generate_property_cxx_name(param, true, event_function)); + bp_event_arg.new_string(SYSSTR("name"), to_system(param->GetName())); + bp_event_arg.new_string(SYSSTR("type"), generate_property_cxx_name(param, true, event_function)); bool is_out = param->HasAnyPropertyFlags(EPropertyFlags::CPF_OutParm) && !param->HasAnyPropertyFlags(EPropertyFlags::CPF_ConstParm); - bp_event_arg.new_bool(STR("is_out"), is_out); - bp_event_arg.new_bool(STR("is_return"), param->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ReturnParm)); + bp_event_arg.new_bool(SYSSTR("is_out"), is_out); + bp_event_arg.new_bool(SYSSTR("is_return"), param->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ReturnParm)); } } - auto& functions = bp_class.new_array(STR("functions")); + auto& functions = bp_class.new_array(SYSSTR("functions")); for (UFunction* function : object_as_class->ForEachFunction()) { if (should_skip_function(function)) @@ -268,9 +268,9 @@ namespace RC::UEGenerator::JSONDumper } auto& bp_function = functions.new_object(); - bp_function.new_string(STR("name"), function->GetName()); + bp_function.new_string(SYSSTR("name"), to_system(function->GetName())); - auto& bp_function_args = bp_function.new_array(STR("args")); + auto& bp_function_args = bp_function.new_array(SYSSTR("args")); for (FProperty* param : function->ForEachProperty()) { if (should_skip_property(param)) @@ -279,15 +279,15 @@ namespace RC::UEGenerator::JSONDumper } auto& bp_function_arg = bp_function_args.new_object(); - bp_function_arg.new_string(STR("name"), param->GetName()); - bp_function_arg.new_string(STR("type"), generate_property_cxx_name(param, true, function)); + bp_function_arg.new_string(SYSSTR("name"), to_system(param->GetName())); + bp_function_arg.new_string(SYSSTR("type"), generate_property_cxx_name(param, true, function)); bool is_out = param->HasAnyPropertyFlags(EPropertyFlags::CPF_OutParm) && !param->HasAnyPropertyFlags(EPropertyFlags::CPF_ConstParm); - bp_function_arg.new_bool(STR("is_out"), is_out); - bp_function_arg.new_bool(STR("is_return"), param->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ReturnParm)); + bp_function_arg.new_bool(SYSSTR("is_out"), is_out); + bp_function_arg.new_bool(SYSSTR("is_return"), param->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ReturnParm)); } } - auto& properties = bp_class.new_array(STR("properties")); + auto& properties = bp_class.new_array(SYSSTR("properties")); for (FProperty* property : object_as_class->ForEachProperty()) { if (should_skip_property(property)) @@ -296,11 +296,11 @@ namespace RC::UEGenerator::JSONDumper } auto& bp_property = properties.new_object(); - bp_property.new_string(STR("name"), property->GetName()); - bp_property.new_string(STR("type"), generate_property_cxx_name(property, true, object_as_class)); + bp_property.new_string(SYSSTR("name"), to_system(property->GetName())); + bp_property.new_string(SYSSTR("type"), generate_property_cxx_name(property, true, object_as_class)); } - auto& delegates = bp_class.new_array(STR("delegates")); + auto& delegates = bp_class.new_array(SYSSTR("delegates")); for (UFunction* delegate_function : object_as_class->ForEachFunction()) { if (should_skip_general_function(delegate_function)) @@ -313,9 +313,9 @@ namespace RC::UEGenerator::JSONDumper } auto& bp_delegate = delegates.new_object(); - bp_delegate.new_string(STR("name"), delegate_function->GetName()); + bp_delegate.new_string(SYSSTR("name"), to_system(delegate_function->GetName())); - auto& bp_delegate_args = bp_delegate.new_array(STR("args")); + auto& bp_delegate_args = bp_delegate.new_array(SYSSTR("args")); for (FProperty* param : delegate_function->ForEachProperty()) { if (should_skip_property(param)) @@ -324,11 +324,11 @@ namespace RC::UEGenerator::JSONDumper } auto& bp_delegate_arg = bp_delegate_args.new_object(); - bp_delegate_arg.new_string(STR("name"), param->GetName()); - bp_delegate_arg.new_string(STR("type"), generate_property_cxx_name(param, true, delegate_function)); + bp_delegate_arg.new_string(SYSSTR("name"), to_system(param->GetName())); + bp_delegate_arg.new_string(SYSSTR("type"), generate_property_cxx_name(param, true, delegate_function)); bool is_out = param->HasAnyPropertyFlags(EPropertyFlags::CPF_OutParm) && !param->HasAnyPropertyFlags(EPropertyFlags::CPF_ConstParm); - bp_delegate_arg.new_bool(STR("is_out"), is_out); - bp_delegate_arg.new_bool(STR("is_return"), param->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ReturnParm)); + bp_delegate_arg.new_bool(SYSSTR("is_out"), is_out); + bp_delegate_arg.new_bool(SYSSTR("is_return"), param->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ReturnParm)); } } @@ -337,10 +337,10 @@ namespace RC::UEGenerator::JSONDumper auto json_file = File::open(file_name, File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); int32_t indent_level{}; - json_file.write_string_to_file(json.serialize(JSON::ShouldFormat::Yes, &indent_level)); + json_file.write_file_string_to_file(to_file(json.serialize(JSON::ShouldFormat::Yes, &indent_level))); json_file.close(); - Output::send(STR("Unloading all forcefully loaded assets\n")); + Output::send(SYSSTR("Unloading all forcefully loaded assets\n")); UAssetRegistry::FreeAllForcefullyLoadedAssets(); } } // namespace RC::UEGenerator::JSONDumper diff --git a/UE4SS/src/SDKGenerator/TMapOverrideGen.cpp b/UE4SS/src/SDKGenerator/TMapOverrideGen.cpp index 5b9440038..9516d89a0 100644 --- a/UE4SS/src/SDKGenerator/TMapOverrideGen.cpp +++ b/UE4SS/src/SDKGenerator/TMapOverrideGen.cpp @@ -25,7 +25,7 @@ namespace RC::UEGenerator auto TMapOverrideGenerator::generate_tmapoverride() -> void { - Output::send(STR("Dumping TMap Property Overrides\n")); + Output::send(SYSSTR("Dumping TMap Property Overrides\n")); auto fm_object = JSON::Object{}; auto uaapi_object = JSON::Object{}; @@ -53,7 +53,7 @@ namespace RC::UEGenerator MapProperties.insert(property->GetFName()); - auto property_name = property->GetFName().ToString(); + auto property_name = to_system(property->GetFName().ToString()); auto key_as_struct_property = CastField(static_cast(property)->GetKeyProp()); auto key_struct_type = key_as_struct_property ? key_as_struct_property->GetStruct() : nullptr; @@ -67,32 +67,32 @@ namespace RC::UEGenerator { continue; } - Output::send(STR("Found Relevant TMap Property: {} in Class: {}\n"), property_name, object->GetName()); + Output::send(SYSSTR("Found Relevant TMap Property: {} in Class: {}\n"), property_name, object->GetName()); auto& fm_json_object = fm_object.new_object(property_name); auto& uaapi_array = uaapi_object.new_array(property_name); if (is_key_valid) { - auto key_name = key_as_struct_property->GetStruct()->GetName(); - fm_json_object.new_string(STR("Key"), key_name); + auto key_name = to_system(key_as_struct_property->GetStruct()->GetName()); + fm_json_object.new_string(SYSSTR("Key"), key_name); uaapi_array.new_string(key_name); } else { - fm_json_object.new_string(STR("Key"), STR("")); + fm_json_object.new_string(SYSSTR("Key"), SYSSTR("")); uaapi_array.new_null(); } if (is_value_valid) { - auto value_name = value_as_struct_property->GetStruct()->GetName(); - fm_json_object.new_string(STR("Value"), value_name); + auto value_name = to_system(value_as_struct_property->GetStruct()->GetName()); + fm_json_object.new_string(SYSSTR("Value"), value_name); uaapi_array.new_string(value_name); } else { - fm_json_object.new_string(STR("Value"), STR("")); + fm_json_object.new_string(SYSSTR("Value"), SYSSTR("")); uaapi_array.new_null(); } @@ -110,17 +110,17 @@ namespace RC::UEGenerator // Retrieve JSON as a string. int32_t indent_level{}; - auto uaapifile = open(StringType{UE4SSProgram::get_program().get_working_directory()} + STR("\\UAssetAPITMapOverrides.json"), + auto uaapifile = open(std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / SYSSTR("UAssetAPITMapOverrides.json"), File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); - auto fmodelfile = open(StringType{UE4SSProgram::get_program().get_working_directory()} + STR("\\FModelTMapOverrides.json"), + auto fmodelfile = open(std::filesystem::path{UE4SSProgram::get_program().get_working_directory()} / SYSSTR("FModelTMapOverrides.json"), File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); - uaapifile.write_string_to_file(uaapi_object.serialize(JSON::ShouldFormat::Yes, &indent_level)); - fmodelfile.write_string_to_file(fm_object.serialize(JSON::ShouldFormat::Yes, &indent_level)); - Output::send(STR("Finished Dumping {} TMap Properties\n"), num_objects_generated); + uaapifile.write_file_string_to_file(to_file(uaapi_object.serialize(JSON::ShouldFormat::Yes, &indent_level))); + fmodelfile.write_file_string_to_file(to_file(fm_object.serialize(JSON::ShouldFormat::Yes, &indent_level))); + Output::send(SYSSTR("Finished Dumping {} TMap Properties\n"), num_objects_generated); MapProperties.clear(); } } // namespace RC::UEGenerator \ No newline at end of file diff --git a/UE4SS/src/SDKGenerator/UEHeaderGenerator.cpp b/UE4SS/src/SDKGenerator/UEHeaderGenerator.cpp index 4c1cb29b1..6af35660c 100644 --- a/UE4SS/src/SDKGenerator/UEHeaderGenerator.cpp +++ b/UE4SS/src/SDKGenerator/UEHeaderGenerator.cpp @@ -1,8 +1,11 @@ +#ifdef WIN32 + #define NOMINMAX #include #ifdef TEXT #undef TEXT #endif +#endif #include #include @@ -48,13 +51,14 @@ #include #include #include +#include #pragma warning(default : 4005) namespace RC::UEGenerator { using namespace RC::Unreal; - std::map UEHeaderGenerator::m_used_file_names{}; + std::map UEHeaderGenerator::m_used_file_names{}; std::map UEHeaderGenerator::m_dependency_object_to_unique_id{}; auto static is_subtype_struct_valid(UScriptStruct* subtype) -> bool @@ -132,7 +136,7 @@ namespace RC::UEGenerator } } - auto string_to_uppercase(std::wstring s) -> std::wstring + auto string_to_uppercase(SystemStringType s) -> SystemStringType { std::transform(s.begin(), s.end(), s.begin(), [](wchar_t c) { return towupper(c); @@ -142,8 +146,8 @@ namespace RC::UEGenerator class FlagFormatHelper { - std::set m_switches; - std::map> m_parameters; + std::set m_switches; + std::map> m_parameters; std::shared_ptr m_meta_helper; FlagFormatHelper(bool is_root_helper) @@ -159,14 +163,14 @@ namespace RC::UEGenerator { } - auto add_switch(const std::wstring& switch_name) -> void + auto add_switch(const SystemStringType& switch_name) -> void { m_switches.insert(switch_name); } - auto add_parameter(const std::wstring& parameter_name, const std::wstring& parameter_value) -> void + auto add_parameter(const SystemStringType& parameter_name, const SystemStringType& parameter_value) -> void { - if (parameter_name == STR("meta")) + if (parameter_name == SYSSTR("meta")) { throw std::invalid_argument("Use get_meta() to add metadata to the flag declaration"); } @@ -187,54 +191,54 @@ namespace RC::UEGenerator return m_meta_helper.get(); } - auto build_flag_string() const -> std::wstring + auto build_flag_string() const -> SystemStringType { - std::wstring resulting_string; + SystemStringType resulting_string; - for (const std::wstring& switch_name : m_switches) + for (const SystemStringType& switch_name : m_switches) { resulting_string.append(switch_name); - resulting_string.append(STR(", ")); + resulting_string.append(SYSSTR(", ")); } for (const auto& parameter_pair : m_parameters) { resulting_string.append(parameter_pair.first); - resulting_string.append(STR("=")); - const std::set& parameter_values = parameter_pair.second; + resulting_string.append(SYSSTR("=")); + const std::set& parameter_values = parameter_pair.second; if (parameter_values.size() != 1) { - resulting_string.append(STR("(")); + resulting_string.append(SYSSTR("(")); - for (const std::wstring& parameter_value : parameter_values) + for (const SystemStringType& parameter_value : parameter_values) { resulting_string.append(parameter_value); - resulting_string.append(STR(", ")); + resulting_string.append(SYSSTR(", ")); } if (parameter_values.size() != 0) { resulting_string.erase(resulting_string.size() - 1, 1); } - resulting_string.append(STR(")")); + resulting_string.append(SYSSTR(")")); } else { resulting_string.append(*parameter_values.begin()); } - resulting_string.append(STR(", ")); + resulting_string.append(SYSSTR(", ")); } if (m_meta_helper) { - const std::wstring meta_flag_string = m_meta_helper->build_flag_string(); + const SystemStringType meta_flag_string = m_meta_helper->build_flag_string(); if (!meta_flag_string.empty()) { - resulting_string.append(STR("meta=(")); + resulting_string.append(SYSSTR("meta=(")); resulting_string.append(meta_flag_string); - resulting_string.append(STR(")")); - resulting_string.append(STR(", ")); + resulting_string.append(SYSSTR(")")); + resulting_string.append(SYSSTR(", ")); } } @@ -246,69 +250,71 @@ namespace RC::UEGenerator } }; - auto UEHeaderGenerator::generate_module_build_file(const std::wstring& module_name) -> void + auto UEHeaderGenerator::generate_module_build_file(const SystemStringType& module_name) -> void { - const FFilePath module_file_path = m_root_directory / module_name / std::format(STR("{}.Build.cs"), module_name); - GeneratedFile module_build_file = GeneratedFile(module_file_path); + auto module_name_sys = to_system(module_name); + const FFilePath module_file_path = m_root_directory / module_name_sys / std::format(SYSSTR("{}.Build.cs"), module_name_sys); + UEGeneratedFile module_build_file = UEGeneratedFile(module_file_path); - module_build_file.append_line(STR("using UnrealBuildTool;")); - module_build_file.append_line(STR("")); + module_build_file.append_line(SYSSTR("using UnrealBuildTool;")); + module_build_file.append_line(SYSSTR("")); - module_build_file.append_line(std::format(STR("public class {} : ModuleRules {{"), module_name)); + module_build_file.append_line(std::format(SYSSTR("public class {} : ModuleRules {{"), module_name_sys)); module_build_file.begin_indent_level(); - module_build_file.append_line(std::format(STR("public {}(ReadOnlyTargetRules Target) : base(Target) {{"), module_name)); + module_build_file.append_line(std::format(SYSSTR("public {}(ReadOnlyTargetRules Target) : base(Target) {{"), module_name_sys)); module_build_file.begin_indent_level(); - module_build_file.append_line(STR("PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;")); + module_build_file.append_line(SYSSTR("PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;")); if (Version::IsAtLeast(4, 24)) { - module_build_file.append_line(STR("bLegacyPublicIncludePaths = false;")); - module_build_file.append_line(STR("ShadowVariableWarningLevel = WarningLevel.Warning;")); + module_build_file.append_line(SYSSTR("bLegacyPublicIncludePaths = false;")); + module_build_file.append_line(SYSSTR("ShadowVariableWarningLevel = WarningLevel.Warning;")); } - module_build_file.append_line(STR("")); - module_build_file.append_line(STR("PublicDependencyModuleNames.AddRange(new string[] {")); + module_build_file.append_line(SYSSTR("")); + module_build_file.append_line(SYSSTR("PublicDependencyModuleNames.AddRange(new string[] {")); module_build_file.begin_indent_level(); - std::set all_module_dependencies = this->m_forced_module_dependencies; - std::set clean_module_dependencies{}; + std::set all_module_dependencies = this->m_forced_module_dependencies; + std::set clean_module_dependencies{}; add_module_and_sub_module_dependencies(clean_module_dependencies, module_name, false); all_module_dependencies.insert(clean_module_dependencies.begin(), clean_module_dependencies.end()); - for (const std::wstring& other_module_name : all_module_dependencies) + for (const SystemStringType& other_module_name : all_module_dependencies) { - module_build_file.append_line(std::format(STR("\"{}\","), other_module_name)); + module_build_file.append_line(std::format(SYSSTR("\"{}\","), to_system(other_module_name))); } module_build_file.end_indent_level(); - module_build_file.append_line(STR("});")); + module_build_file.append_line(SYSSTR("});")); module_build_file.end_indent_level(); - module_build_file.append_line(STR("}")); + module_build_file.append_line(SYSSTR("}")); module_build_file.end_indent_level(); - module_build_file.append_line(STR("}")); + module_build_file.append_line(SYSSTR("}")); module_build_file.serialize_file_content_to_disk(); } - auto UEHeaderGenerator::generate_module_implementation_file(const std::wstring& module_name) -> void + auto UEHeaderGenerator::generate_module_implementation_file(const SystemStringType& module_name) -> void { - const FFilePath module_file_path = m_root_directory / module_name / STR("Private") / std::format(STR("{}Module.cpp"), module_name); - GeneratedFile module_impl_file = GeneratedFile(module_file_path); + auto module_name_sys = to_system(module_name); + const FFilePath module_file_path = m_root_directory / module_name_sys / SYSSTR("Private") / std::format(SYSSTR("{}Module.cpp"), module_name_sys); + UEGeneratedFile module_impl_file = UEGeneratedFile(module_file_path); - module_impl_file.append_line(STR("#include \"Modules/ModuleManager.h\"")); - module_impl_file.append_line(STR("")); + module_impl_file.append_line(SYSSTR("#include \"Modules/ModuleManager.h\"")); + module_impl_file.append_line(SYSSTR("")); if (module_name != m_primary_module_name) { - module_impl_file.append_line(std::format(STR("IMPLEMENT_MODULE(FDefaultGameModuleImpl, {});"), module_name)); + module_impl_file.append_line(std::format(SYSSTR("IMPLEMENT_MODULE(FDefaultGameModuleImpl, {});"), module_name_sys)); } else { - module_impl_file.append_line(std::format(STR("IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, {}, {});"), module_name, module_name)); + module_impl_file.append_line(std::format(SYSSTR("IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, {}, {});"), module_name_sys, module_name_sys)); } module_impl_file.serialize_file_content_to_disk(); @@ -316,40 +322,40 @@ namespace RC::UEGenerator auto UEHeaderGenerator::generate_interface_definition(UClass* uclass, GeneratedSourceFile& header_data) -> void { - const std::wstring interface_class_native_name = get_native_class_name(uclass); - const std::wstring interface_flags_string = generate_interface_flags(uclass); + const SystemStringType interface_class_native_name = get_native_class_name(uclass); + const SystemStringType interface_flags_string = generate_interface_flags(uclass); - std::wstring maybe_api_name; + SystemStringType maybe_api_name; if ((uclass->GetClassFlags() & CLASS_RequiredAPI) != 0) { maybe_api_name.append(convert_module_name_to_api_name(header_data.get_header_module_name())); - maybe_api_name.append(STR(" ")); + maybe_api_name.append(SYSSTR(" ")); } UClass* super_class = uclass->GetSuperClass(); header_data.add_dependency_object(super_class, DependencyLevel::Include); - std::wstring parent_interface_class_name = get_native_class_name(super_class); + SystemStringType parent_interface_class_name = get_native_class_name(super_class); // Generate interface UCLASS declaration - header_data.append_line(std::format(STR("UINTERFACE({})"), interface_flags_string)); - header_data.append_line(std::format(STR("class {}{} : public {} {{"), maybe_api_name, interface_class_native_name, parent_interface_class_name)); + header_data.append_line(std::format(SYSSTR("UINTERFACE({})"), interface_flags_string)); + header_data.append_line(std::format(SYSSTR("class {}{} : public {} {{"), maybe_api_name, interface_class_native_name, parent_interface_class_name)); header_data.begin_indent_level(); - header_data.append_line(STR("GENERATED_BODY()")); + header_data.append_line(SYSSTR("GENERATED_BODY()")); header_data.end_indent_level(); - header_data.append_line(STR("};")); - header_data.append_line(STR("")); + header_data.append_line(SYSSTR("};")); + header_data.append_line(SYSSTR("")); // Generate interface real class declaration - const std::wstring interface_native_name = get_native_class_name(uclass, true); - const std::wstring parent_interface_name = get_native_class_name(super_class, true); + const SystemStringType interface_native_name = get_native_class_name(uclass, true); + const SystemStringType parent_interface_name = get_native_class_name(super_class, true); - header_data.append_line(std::format(STR("class {}{} : public {} {{"), maybe_api_name, interface_native_name, parent_interface_name)); + header_data.append_line(std::format(SYSSTR("class {}{} : public {} {{"), maybe_api_name, interface_native_name, parent_interface_name)); header_data.begin_indent_level(); - header_data.append_line(STR("GENERATED_BODY()")); + header_data.append_line(SYSSTR("GENERATED_BODY()")); AccessModifier current_access_modifier = AccessModifier::None; append_access_modifier(header_data, AccessModifier::Public, current_access_modifier); @@ -366,7 +372,7 @@ namespace RC::UEGenerator } if (NumDelegatesGenerated) { - header_data.append_line(STR("")); + header_data.append_line(SYSSTR("")); } // Generate interface functions @@ -380,30 +386,30 @@ namespace RC::UEGenerator } header_data.end_indent_level(); - header_data.append_line(STR("};")); + header_data.append_line(SYSSTR("};")); } auto UEHeaderGenerator::generate_object_definition(UClass* uclass, GeneratedSourceFile& header_data) -> void { - const std::wstring class_native_name = get_native_class_name(uclass); - const std::wstring class_flags_string = generate_class_flags(uclass); + const SystemStringType class_native_name = get_native_class_name(uclass); + const SystemStringType class_flags_string = generate_class_flags(uclass); - std::wstring maybe_api_name; + SystemStringType maybe_api_name; if ((uclass->GetClassFlags() & CLASS_RequiredAPI) != 0) { maybe_api_name.append(convert_module_name_to_api_name(header_data.get_header_module_name())); - maybe_api_name.append(STR(" ")); + maybe_api_name.append(SYSSTR(" ")); } UClass* super_class = uclass->GetSuperClass(); - std::wstring parent_class_name; + SystemStringType parent_class_name; if (super_class) { parent_class_name = get_native_class_name(super_class); } else { - parent_class_name = STR("UObjectBaseUtility"); + parent_class_name = SYSSTR("UObjectBaseUtility"); } if (super_class) @@ -411,23 +417,23 @@ namespace RC::UEGenerator header_data.add_dependency_object(super_class, DependencyLevel::Include); } - std::wstring interface_list_string; + SystemStringType interface_list_string; auto implemented_interfaces = uclass->GetInterfaces(); for (const RC::Unreal::FImplementedInterface& uinterface : implemented_interfaces) { header_data.add_dependency_object(uinterface.Class, DependencyLevel::Include); - const std::wstring interface_name = get_native_class_name(uinterface.Class, true); + const SystemStringType interface_name = get_native_class_name(uinterface.Class, true); - interface_list_string.append(STR(", public ")); + interface_list_string.append(SYSSTR(", public ")); interface_list_string.append(interface_name); } - header_data.append_line(std::format(STR("UCLASS({})"), class_flags_string)); - header_data.append_line(std::format(STR("class {}{} : public {}{} {{"), maybe_api_name, class_native_name, parent_class_name, interface_list_string)); + header_data.append_line(std::format(SYSSTR("UCLASS({})"), class_flags_string)); + header_data.append_line(std::format(SYSSTR("class {}{} : public {}{} {{"), maybe_api_name, class_native_name, parent_class_name, interface_list_string)); header_data.begin_indent_level(); - header_data.append_line(STR("GENERATED_BODY()")); + header_data.append_line(SYSSTR("GENERATED_BODY()")); AccessModifier current_access_modifier = AccessModifier::None; append_access_modifier(header_data, AccessModifier::Public, current_access_modifier); @@ -447,7 +453,7 @@ namespace RC::UEGenerator } if (NumDelegatesGenerated) { - header_data.append_line(STR("")); + header_data.append_line(SYSSTR("")); } // Generate properties @@ -471,19 +477,19 @@ namespace RC::UEGenerator append_access_modifier(header_data, AccessModifier::Public, current_access_modifier); // Generate constructor - std::wstring constructor_string; + SystemStringType constructor_string; if (uclass->IsChildOf() || uclass->IsChildOf()) { - constructor_string.append(STR("const FObjectInitializer& ObjectInitializer")); + constructor_string.append(SYSSTR("const FObjectInitializer& ObjectInitializer")); } - header_data.append_line(std::format(STR("{}({});"), class_native_name, constructor_string)); - header_data.append_line_no_indent(STR("")); + header_data.append_line(std::format(SYSSTR("{}({});"), class_native_name, constructor_string)); + header_data.append_line_no_indent(SYSSTR("")); // Generate GetLifetimeReplicatedProps override if we have encountered replicated properties if (encountered_replicated_properties) { - header_data.append_line(STR("virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override;")); - header_data.append_line_no_indent(STR("")); + header_data.append_line(SYSSTR("virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override;")); + header_data.append_line_no_indent(SYSSTR("")); } // Generate functions @@ -501,8 +507,8 @@ namespace RC::UEGenerator // Generate overrides for all inherited virtual functions if (implemented_interfaces.Num() > 0) { - header_data.append_line_no_indent(STR("")); - header_data.append_line(STR("// Fix for true pure virtual functions not being implemented")); + header_data.append_line_no_indent(SYSSTR("")); + header_data.append_line(SYSSTR("// Fix for true pure virtual functions not being implemented")); } for (const RC::Unreal::FImplementedInterface& uinterface : implemented_interfaces) { @@ -519,33 +525,34 @@ namespace RC::UEGenerator } header_data.end_indent_level(); - header_data.append_line(STR("};")); + header_data.append_line(SYSSTR("};")); } auto UEHeaderGenerator::generate_struct_definition(UScriptStruct* script_struct, GeneratedSourceFile& header_data) -> void { - const std::wstring struct_native_name = get_native_struct_name(script_struct); - const std::wstring struct_flags_string = generate_struct_flags(script_struct); + const SystemStringType struct_native_name = get_native_struct_name(script_struct); + const SystemStringType struct_flags_string = generate_struct_flags(script_struct); - std::wstring api_macro_name = convert_module_name_to_api_name(header_data.get_header_module_name()); - api_macro_name.append(STR(" ")); + SystemStringType api_macro_name = convert_module_name_to_api_name(header_data.get_header_module_name()); + api_macro_name.append(SYSSTR(" ")); bool is_struct_exported = (script_struct->GetStructFlags() & STRUCT_RequiredAPI) != 0; UScriptStruct* super_struct = script_struct->GetSuperScriptStruct(); - std::wstring parent_struct_declaration; + SystemStringType parent_struct_declaration; if (super_struct) { header_data.add_dependency_object(super_struct, DependencyLevel::Include); - const std::wstring super_struct_native_name = get_native_struct_name(super_struct); - parent_struct_declaration.append(std::format(STR(" : public {}"), super_struct_native_name)); + const SystemStringType super_struct_native_name = get_native_struct_name(super_struct); + parent_struct_declaration.append(std::format(SYSSTR(" : public {}"), super_struct_native_name)); } - header_data.append_line(std::format(STR("USTRUCT({})"), struct_flags_string)); - header_data.append_line(std::format(STR("struct {}{}{} {{"), is_struct_exported ? api_macro_name : STR(""), struct_native_name, parent_struct_declaration)); + header_data.append_line(std::format(SYSSTR("USTRUCT({})"), struct_flags_string)); + header_data.append_line( + std::format(SYSSTR("struct {}{}{} {{"), is_struct_exported ? api_macro_name : SYSSTR(""), struct_native_name, parent_struct_declaration)); header_data.begin_indent_level(); - header_data.append_line(STR("GENERATED_BODY()")); + header_data.append_line(SYSSTR("GENERATED_BODY()")); AccessModifier current_access_modifier = AccessModifier::None; append_access_modifier(header_data, AccessModifier::Public, current_access_modifier); @@ -568,34 +575,34 @@ namespace RC::UEGenerator // Generate constructor and make sure it's public append_access_modifier(header_data, AccessModifier::Public, current_access_modifier); - header_data.append_line(std::format(STR("{}{}();"), !is_struct_exported ? api_macro_name : STR(""), struct_native_name)); + header_data.append_line(std::format(SYSSTR("{}{}();"), !is_struct_exported ? api_macro_name : SYSSTR(""), struct_native_name)); header_data.end_indent_level(); - header_data.append_line(STR("};")); + header_data.append_line(SYSSTR("};")); } auto UEHeaderGenerator::generate_enum_definition(UEnum* uenum, GeneratedSourceFile& header_data) -> void { - const StringType native_enum_name = get_native_enum_name(uenum, false); + const SystemStringType native_enum_name = get_native_enum_name(uenum, false); const int64 highest_enum_value = get_highest_enum(uenum); const bool can_use_uint8_override = (highest_enum_value <= 255 && get_lowest_enum(uenum) >= 0); - const StringType enum_flags_string = generate_enum_flags(uenum); + const SystemStringType enum_flags_string = generate_enum_flags(uenum); const auto underlying_type = m_underlying_enum_types.find(native_enum_name); const bool has_known_underlying_type = underlying_type != m_underlying_enum_types.end(); UEnum::ECppForm cpp_form = uenum->GetCppForm(); bool enum_is_uint8{false}; - header_data.append_line(std::format(STR("UENUM({})"), enum_flags_string)); + header_data.append_line(std::format(SYSSTR("UENUM({})"), enum_flags_string)); if (cpp_form == UEnum::ECppForm::Namespaced) { - header_data.append_line(std::format(STR("namespace {} {{"), native_enum_name)); + header_data.append_line(std::format(SYSSTR("namespace {} {{"), native_enum_name)); header_data.begin_indent_level(); - header_data.append_line(STR("enum Type {")); + header_data.append_line(SYSSTR("enum Type {")); } else if (cpp_form == UEnum::ECppForm::Regular) { - header_data.append_line(std::format(STR("enum {} {{"), native_enum_name)); + header_data.append_line(std::format(SYSSTR("enum {} {{"), native_enum_name)); } else if (cpp_form == UEnum::ECppForm::EnumClass) { @@ -603,34 +610,34 @@ namespace RC::UEGenerator { if (UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeEnumClassesBlueprintType && can_use_uint8_override) { - header_data.append_line(std::format(STR("enum class {} : uint8 {{"), native_enum_name)); + header_data.append_line(std::format(SYSSTR("enum class {} : uint8 {{"), native_enum_name)); enum_is_uint8 = true; } else { // Enum has never been used in any native classes or structures, go with implicit type - header_data.append_line(std::format(STR("enum class {} {{"), native_enum_name)); + header_data.append_line(std::format(SYSSTR("enum class {} {{"), native_enum_name)); } } else { - std::wstring underlying_type_string = underlying_type->second; + SystemStringType underlying_type_string = underlying_type->second; - header_data.append_line(std::format(STR("enum class {} : {} {{"), native_enum_name, underlying_type_string)); + header_data.append_line(std::format(SYSSTR("enum class {} : {} {{"), native_enum_name, underlying_type_string)); } } header_data.begin_indent_level(); - StringType enum_prefix = uenum->GenerateEnumPrefix(); + auto enum_prefix = uenum->GenerateEnumPrefix(); int64 expected_next_enum_value = 0; bool last_value_was_negative_one{false}; - std::set enum_name_set{}; + std::set enum_name_set{}; for (auto [Name, Value] : uenum->ForEachName()) { - StringType enum_name = Name.ToString(); - StringType result_enumeration_line = sanitize_enumeration_name(enum_name); - StringType pre_append_result_line = result_enumeration_line; + auto enum_name = Name.ToString(); + auto result_enumeration_line = sanitize_enumeration_name(to_system_string(enum_name)); + auto pre_append_result_line = result_enumeration_line; // If an enum name is listed in the array twice, that likely means it is used as the value for another enum. Long story short, don't print it. if (enum_name_set.contains(enum_name)) @@ -641,73 +648,74 @@ namespace RC::UEGenerator { enum_name_set.emplace(enum_name); } - + // Taking advantage of GetNameByValue returning the first result for the value to determine if there are any enumerator names that // reference an already declared value/name. - StringType first_name_with_value = uenum->GetNameByValue(Value).ToString(); + auto first_name_with_value = uenum->GetNameByValue(Value).ToString(); if (first_name_with_value != Name.ToString()) { - result_enumeration_line.append(std::format(STR(" = {}"), sanitize_enumeration_name(first_name_with_value))); + result_enumeration_line.append(std::format(SYSSTR(" = {}"), sanitize_enumeration_name(to_system_string(first_name_with_value)))); } else if (Value != expected_next_enum_value || last_value_was_negative_one) { - const StringType CastString = (enum_is_uint8 && Value < 0) ? STR("(uint8)") : STR(""); - const StringType MinusSign = Value < 0 ? STR("-") : STR(""); - result_enumeration_line.append(std::format(STR(" = {}{}{}"), CastString, MinusSign, std::abs(Value))); + const auto CastString = (enum_is_uint8 && Value < 0) ? SYSSTR("(uint8)") : SYSSTR(""); + const auto MinusSign = Value < 0 ? SYSSTR("-") : SYSSTR(""); + result_enumeration_line.append(std::format(SYSSTR(" = {}{}{}"), CastString, MinusSign, Value < 0 ? -Value : Value)); } expected_next_enum_value = Value + 1; last_value_was_negative_one = (Value == -1); - StringType pre_append_result_line_lower = pre_append_result_line; + SystemStringType pre_append_result_line_lower = pre_append_result_line; std::transform(pre_append_result_line_lower.begin(), pre_append_result_line_lower.end(), pre_append_result_line_lower.begin(), ::towlower); - if (pre_append_result_line_lower.ends_with(STR("_max"))) + if (pre_append_result_line_lower.ends_with(SYSSTR("_max"))) { - const StringType expected_full_constant_name = std::format(STR("{}_MAX"), enum_prefix); - StringType expected_full_constant_name_lower = expected_full_constant_name; + const auto expected_full_constant_name = std::format(SYSSTR("{}_MAX"), to_system(enum_prefix)); + SystemStringType expected_full_constant_name_lower = expected_full_constant_name; std::transform(expected_full_constant_name_lower.begin(), expected_full_constant_name_lower.end(), expected_full_constant_name_lower.begin(), ::towlower); - + int64_t expected_max_value = highest_enum_value + 1; // Skip enum _MAX constant if it has a matching name and is 1 greater than the highest value used, which means it has been autogenerated - if ((pre_append_result_line_lower == expected_full_constant_name_lower || pre_append_result_line_lower == sanitize_enumeration_name(expected_full_constant_name_lower)) && + if ((pre_append_result_line_lower == expected_full_constant_name_lower || + pre_append_result_line_lower == sanitize_enumeration_name(expected_full_constant_name_lower)) && Value == expected_max_value) { continue; } // Otherwise, just make sure it's hidden and not visible to the end user - result_enumeration_line.append(STR(" UMETA(Hidden)")); + result_enumeration_line.append(SYSSTR(" UMETA(Hidden)")); } - result_enumeration_line.append(STR(",")); + result_enumeration_line.append(SYSSTR(",")); header_data.append_line(result_enumeration_line); } header_data.end_indent_level(); - header_data.append_line(STR("};")); + header_data.append_line(SYSSTR("};")); if (cpp_form == UEnum::ECppForm::Namespaced) { header_data.end_indent_level(); - header_data.append_line(STR("}")); + header_data.append_line(SYSSTR("}")); } } auto UEHeaderGenerator::generate_delegate_type_declaration(UFunction* signature_function, UClass* delegate_class, GeneratedSourceFile& header_data) -> void { - std::wstring owning_class; + SystemStringType owning_class; if (delegate_class == nullptr) { - owning_class = STR("UObject*"); + owning_class = SYSSTR("UObject*"); } else { - owning_class = delegate_class->GetNamePrivate().ToString(); + owning_class = to_system(delegate_class->GetNamePrivate().ToString()); } auto function_flags = signature_function->GetFunctionFlags(); if ((function_flags & Unreal::FUNC_Delegate) == 0) { - throw std::runtime_error(RC::fmt("Delegate Signature function %S is missing FUNC_Delegate flag", signature_function->GetName().c_str())); + throw std::runtime_error(RC::fmt("Delegate Signature function {} is missing FUNC_Delegate flag", signature_function->GetName())); } // TODO not particularly nice or reliable, but will do for now @@ -716,85 +724,86 @@ namespace RC::UEGenerator const bool is_multicast = (function_flags & Unreal::FUNC_MulticastDelegate) != 0; const bool declared_const = (function_flags & FUNC_Const) != 0; - const std::wstring delegate_type_name = get_native_delegate_type_name(signature_function, nullptr, true); + const SystemStringType delegate_type_name = get_native_delegate_type_name(signature_function, nullptr, true); FProperty* return_value_property = signature_function->GetReturnProperty(); - std::wstring delegate_macro_string; + SystemStringType delegate_macro_string; // Delegate macro declaration is only allowed on the top level delegates, class-based types are limited to being implicit if (signature_function->GetOuterPrivate()->IsA()) { - delegate_macro_string.append(STR("UDELEGATE(")); + delegate_macro_string.append(SYSSTR("UDELEGATE(")); delegate_macro_string.append(generate_function_flags(signature_function)); - delegate_macro_string.append(STR(") ")); + delegate_macro_string.append(SYSSTR(") ")); } PropertyTypeDeclarationContext context(delegate_type_name, &header_data); int32_t num_delegate_parameters = 0; - std::wstring delegate_parameter_list = + SystemStringType delegate_parameter_list = generate_function_parameter_list(nullptr, signature_function, header_data, true, context.context_name, {}, &num_delegate_parameters); if (num_delegate_parameters > 0) { - delegate_parameter_list.insert(0, STR(", ")); + delegate_parameter_list.insert(0, SYSSTR(", ")); } if (num_delegate_parameters > 9) { - Output::send(STR("Invalid delegate parameter count in Delegate: {}. Using _TooMany\n"), delegate_type_name); + Output::send(SYSSTR("Invalid delegate parameter count in Delegate: {}. Using _TooMany\n"), delegate_type_name); } - std::wstring return_value_declaration; + SystemStringType return_value_declaration; if (return_value_property != NULL) { return_value_declaration = generate_property_type_declaration(return_value_property, context); - return_value_declaration.append(STR(", ")); - } - - std::wstring delegate_declaration_string = std::format(STR("{}DECLARE_DYNAMIC{}{}_DELEGATE{}{}{}({}{}{}{});"), - delegate_macro_string, - is_multicast ? STR("_MULTICAST") : STR(""), - is_sparse ? STR("_SPARSE") : STR(""), - return_value_property ? STR("_RetVal") : STR(""), - generate_parameter_count_string(num_delegate_parameters), - declared_const ? STR("_Const") : STR(""), - return_value_declaration, - delegate_type_name, - // TODO: Actually get delegate property name. - is_sparse ? std::format(STR("{}, {}"), owning_class, STR("EnterPropertyName")) : STR(""), - delegate_parameter_list); + return_value_declaration.append(SYSSTR(", ")); + } + + SystemStringType delegate_declaration_string = + std::format(SYSSTR("{}DECLARE_DYNAMIC{}{}_DELEGATE{}{}{}({}{}{}{});"), + delegate_macro_string, + is_multicast ? SYSSTR("_MULTICAST") : SYSSTR(""), + is_sparse ? SYSSTR("_SPARSE") : SYSSTR(""), + return_value_property ? SYSSTR("_RetVal") : SYSSTR(""), + generate_parameter_count_string(num_delegate_parameters), + declared_const ? SYSSTR("_Const") : SYSSTR(""), + return_value_declaration, + delegate_type_name, + // TODO: Actually get delegate property name. + is_sparse ? std::format(SYSSTR("{}, {}"), owning_class, SYSSTR("EnterPropertyName")) : SYSSTR(""), + delegate_parameter_list); header_data.append_line(delegate_declaration_string); } auto UEHeaderGenerator::generate_object_implementation(UClass* uclass, GeneratedSourceFile& implementation_file) -> void { - const std::wstring class_native_name = get_native_class_name(uclass); + auto class_native_name = get_native_class_name(uclass); - std::wstring constructor_content_string; - std::wstring constructor_postfix_string; + SystemStringType constructor_content_string; + SystemStringType constructor_postfix_string; UClass* super_class = uclass->GetSuperClass(); - const std::wstring native_parent_class_name = super_class ? get_native_class_name(super_class) : STR("UObjectUtility"); + const SystemStringType native_parent_class_name = super_class ? get_native_class_name(super_class) : SYSSTR("UObjectUtility"); // Generate constructor implementation except for overrides. // If class is a child of AActor we add the UObjectInitializer constructor. // This may not be required in all cases, but is necessary to override subcomponents and does not hurt anything. - std::wstring object_initializer_overrides; + SystemStringType object_initializer_overrides; if (uclass->IsChildOf() || uclass->IsChildOf()) { - constructor_content_string.append(STR("const FObjectInitializer& ObjectInitializer")); - constructor_postfix_string.append(std::format(STR(") : Super(ObjectInitializer{}"), object_initializer_overrides)); + constructor_content_string.append(SYSSTR("const FObjectInitializer& ObjectInitializer")); + constructor_postfix_string.append(std::format(SYSSTR(") : Super(ObjectInitializer{}"), object_initializer_overrides)); } // If parent class contains the UObjectInitializer constructor without default value, // we need to create the explicit call to such constructor and pass UObjectInitializer::Get() as the argument. else if (m_classes_with_object_initializer.contains(native_parent_class_name)) { - constructor_postfix_string.append(std::format(STR(") : {}(FObjectInitializer::Get()"), native_parent_class_name)); + constructor_postfix_string.append(std::format(SYSSTR(") : {}(FObjectInitializer::Get()"), native_parent_class_name)); } implementation_file.m_implementation_constructor.append( - std::format(STR("{}::{}({}{}"), class_native_name, class_native_name, constructor_content_string, constructor_postfix_string)); + std::format(SYSSTR("{}::{}({}{}"), class_native_name, class_native_name, constructor_content_string, constructor_postfix_string)); implementation_file.begin_indent_level(); @@ -805,34 +814,39 @@ namespace RC::UEGenerator { for (FProperty* property : uclass->OrderedForEachPropertyInChain()) { - generate_property_value(uclass, property, class_default_object, implementation_file, STR("this->")); + generate_property_value(uclass, property, class_default_object, implementation_file, SYSSTR("this->")); } } else { - implementation_file.append_line(STR("// Null default object.")); + implementation_file.append_line(SYSSTR("// Null default object.")); } m_class_subobjects.clear(); // Generate component attachments for (auto attachment : implementation_file.attachments) { - if (get<2>(attachment.second) == false) + if ((attachment.second).access_type == false) { - generate_simple_assignment_expression(attachment.first, get<1>(attachment.second), implementation_file, STR("this->"), STR("->")); + generate_simple_assignment_expression(attachment.first, (attachment.second).attach_string, implementation_file, SYSSTR("this->"), SYSSTR("->")); } else { - generate_advanced_assignment_expression(attachment.first, get<1>(attachment.second), implementation_file, STR("this->"), get<0>(attachment.second), STR("->")); + generate_advanced_assignment_expression(attachment.first, + (attachment.second).attach_string, + implementation_file, + SYSSTR("this->"), + (attachment.second).property_type, + SYSSTR("->")); } } - + implementation_file.end_indent_level(); - implementation_file.append_line(STR("}\n")); + implementation_file.append_line(SYSSTR("}\n")); // Finalize constructor. We do this after the property generation because we need information from the properties // to determine the required overrides within the constructor. - implementation_file.m_implementation_constructor.append(STR(") {")); + implementation_file.m_implementation_constructor.append(SYSSTR(") {")); CaseInsensitiveSet blacklisted_property_names = collect_blacklisted_property_names(uclass); @@ -842,7 +856,7 @@ namespace RC::UEGenerator if (!is_delegate_signature_function(function)) { generate_function_implementation(uclass, function, implementation_file, false, blacklisted_property_names); - implementation_file.append_line(STR("")); + implementation_file.append_line(SYSSTR("")); } } @@ -856,35 +870,35 @@ namespace RC::UEGenerator // Generate replicated properties implementation if we really need it if (encountered_replicated_properties) { - implementation_file.add_extra_include(STR("Net/UnrealNetwork.h")); + implementation_file.add_extra_include(SYSSTR("Net/UnrealNetwork.h")); implementation_file.append_line( - std::format(STR("void {}::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const {{"), class_native_name)); + std::format(SYSSTR("void {}::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const {{"), class_native_name)); implementation_file.begin_indent_level(); - implementation_file.append_line(STR("Super::GetLifetimeReplicatedProps(OutLifetimeProps);")); - implementation_file.append_line(STR("")); + implementation_file.append_line(SYSSTR("Super::GetLifetimeReplicatedProps(OutLifetimeProps);")); + implementation_file.append_line(SYSSTR("")); for (FProperty* property : uclass->ForEachProperty()) { if ((property->GetPropertyFlags() & CPF_Net) != 0) { - implementation_file.append_line(std::format(STR("DOREPLIFETIME({}, {});"), class_native_name, property->GetName())); + implementation_file.append_line(std::format(SYSSTR("DOREPLIFETIME({}, {});"), class_native_name, to_system(property->GetName()))); } } implementation_file.end_indent_level(); - implementation_file.append_line(STR("}")); - implementation_file.append_line(STR("")); + implementation_file.append_line(SYSSTR("}")); + implementation_file.append_line(SYSSTR("")); } } auto UEHeaderGenerator::generate_struct_implementation(UScriptStruct* script_struct, GeneratedSourceFile& implementation_file) -> void { - const std::wstring struct_native_name = get_native_struct_name(script_struct); + const SystemStringType struct_native_name = get_native_struct_name(script_struct); // Generate constructor implementation and initialize properties inside - implementation_file.m_implementation_constructor.append(std::format(STR("{}::{}() {{"), struct_native_name, struct_native_name)); + implementation_file.m_implementation_constructor.append(std::format(SYSSTR("{}::{}() {{"), struct_native_name, struct_native_name)); implementation_file.begin_indent_level(); // Generate properties @@ -895,26 +909,26 @@ namespace RC::UEGenerator for (FProperty* property : script_struct->OrderedForEachPropertyInChain()) { - generate_property_value(script_struct, property, struct_default_object, implementation_file, STR("this->")); + generate_property_value(script_struct, property, struct_default_object, implementation_file, SYSSTR("this->")); } // TODO: ScriptStruct->DestroyStruct(StructDefaultObject); free(struct_default_object); implementation_file.end_indent_level(); - implementation_file.append_line(STR("}")); + implementation_file.append_line(SYSSTR("}")); } auto UEHeaderGenerator::generate_property(UObject* uclass, FProperty* property, GeneratedSourceFile& header_data) -> void { - const std::wstring property_flags_string = generate_property_flags(property); + const SystemStringType property_flags_string = generate_property_flags(property); bool is_bitmask_bool = false; - PropertyTypeDeclarationContext Context(uclass->GetName(), &header_data, true, &is_bitmask_bool); + PropertyTypeDeclarationContext Context(to_system(uclass->GetName()), &header_data, true, &is_bitmask_bool); - std::wstring property_type_string{}; + SystemStringType property_type_string{}; bool type_is_valid = true; - std::wstring error_string{}; + SystemStringType error_string{}; try { property_type_string = generate_property_type_declaration(property, Context); @@ -922,34 +936,34 @@ namespace RC::UEGenerator catch (std::exception& e) { type_is_valid = false; - error_string = to_wstring(e.what()); + error_string = to_system(e.what()); } if (!type_is_valid) { - Output::send(STR("Warning: {}\n"), error_string); - header_data.append_line(std::format(STR("// UPROPERTY({})"), property_flags_string)); - header_data.append_line(std::format(STR("// Missed Property: {}"), property->GetName())); - header_data.append_line(std::format(STR("// {}"), error_string)); - header_data.append_line(STR("")); + Output::send(SYSSTR("Warning: {}\n"), error_string); + header_data.append_line(std::format(SYSSTR("// UPROPERTY({})"), property_flags_string)); + header_data.append_line(std::format(SYSSTR("// Missed Property: {}"), to_system(property->GetName()))); + header_data.append_line(std::format(SYSSTR("// {}"), error_string)); + header_data.append_line(SYSSTR("")); return; } - std::wstring property_extra_declaration; + SystemStringType property_extra_declaration; if (property->GetArrayDim() != 1) { - property_extra_declaration.append(STR("[")); - property_extra_declaration.append(std::to_wstring(property->GetArrayDim())); - property_extra_declaration.append(STR("]")); + property_extra_declaration.append(SYSSTR("[")); + property_extra_declaration.append(to_system(std::to_string(property->GetArrayDim()))); + property_extra_declaration.append(SYSSTR("]")); } else if (is_bitmask_bool) { - property_extra_declaration.append(STR(": 1")); + property_extra_declaration.append(SYSSTR(": 1")); } - header_data.append_line(std::format(STR("UPROPERTY({})"), property_flags_string)); - header_data.append_line(std::format(STR("{} {}{};"), property_type_string, property->GetName(), property_extra_declaration)); - header_data.append_line(STR("")); + header_data.append_line(std::format(SYSSTR("UPROPERTY({})"), property_flags_string)); + header_data.append_line(std::format(SYSSTR("{} {}{};"), property_type_string, to_system(property->GetName()), property_extra_declaration)); + header_data.append_line(SYSSTR("")); } // TODO FUNC_Final is not properly handled (should be always set except some weird cases) @@ -961,90 +975,91 @@ namespace RC::UEGenerator bool generate_as_override) -> void { auto function_flags = function->GetFunctionFlags(); - const std::wstring context_name = uclass->GetName(); + const auto context_name = uclass->GetName(); bool is_function_pure_virtual = generate_as_override; - std::wstring function_modifier_string; + SystemStringType function_modifier_string; if ((function_flags & FUNC_Static) != 0) { - function_modifier_string.append(STR("static ")); + function_modifier_string.append(SYSSTR("static ")); } else if ((function_flags & FUNC_BlueprintEvent) == 0 && is_generating_interface) { // When we have a blueprint function that is not blueprint event inside the interface, // it means we are dealing with the native interface that cannot be implemented via blueprints // and uses pure virtual functions implemented through native code - function_modifier_string.append(STR("virtual ")); + function_modifier_string.append(SYSSTR("virtual ")); is_function_pure_virtual = true; } FProperty* return_property = function->GetReturnProperty(); - std::wstring return_property_string; + SystemStringType return_property_string; if (return_property != NULL) { - PropertyTypeDeclarationContext context(uclass->GetName(), &header_data); + PropertyTypeDeclarationContext context(to_system(uclass->GetName()), &header_data); return_property_string = generate_property_type_declaration(return_property, context); } else { - return_property_string = STR("void"); + return_property_string = SYSSTR("void"); } - std::wstring function_extra_postfix_string; + SystemStringType function_extra_postfix_string; if ((function_flags & FUNC_Const) != 0) { - function_extra_postfix_string.append(STR(" const")); + function_extra_postfix_string.append(SYSSTR(" const")); } if (is_function_pure_virtual) { - std::wstring return_statement_string; + SystemStringType return_statement_string; if (return_property != NULL) { - const std::wstring default_property_value = generate_default_property_value(return_property, header_data, context_name); - return_statement_string = std::format(STR(" return {};"), default_property_value); + const auto default_property_value = generate_default_property_value(return_property, header_data, to_system_string(context_name)); + return_statement_string = std::format(SYSSTR(" return {};"), default_property_value); } if (generate_as_override) { - function_extra_postfix_string.append(STR(" override")); + function_extra_postfix_string.append(SYSSTR(" override")); } - function_extra_postfix_string.append(std::format(STR(" PURE_VIRTUAL({},{})"), function->GetName(), return_statement_string)); + function_extra_postfix_string.append(std::format(SYSSTR(" PURE_VIRTUAL({},{})"), to_system(function->GetName()), return_statement_string)); } - std::wstring function_argument_list = generate_function_parameter_list(uclass, function, header_data, false, context_name, blacklisted_property_names); + auto function_argument_list = + generate_function_parameter_list(uclass, function, header_data, false, to_system_string(context_name), blacklisted_property_names); - const std::wstring function_flags_string = generate_function_flags(function, is_function_pure_virtual); - header_data.append_line(std::format(STR("UFUNCTION({})"), function_flags_string)); + const auto function_flags_string = generate_function_flags(function, is_function_pure_virtual); + header_data.append_line(std::format(SYSSTR("UFUNCTION({})"), function_flags_string)); // Format for virtual functions // virtual () PURE_VIRTUAL(, ) - header_data.append_line(std::format(STR("{}{} {}({}){};"), + header_data.append_line(std::format(SYSSTR("{}{} {}({}){};"), function_modifier_string, return_property_string, - function->GetName(), + to_system(function->GetName()), function_argument_list, function_extra_postfix_string)); - header_data.append_line(STR("")); + header_data.append_line(SYSSTR("")); } - auto UEHeaderGenerator::generate_enum_value(UEnum* uenum, int64_t enum_value) -> std::wstring + auto UEHeaderGenerator::generate_enum_value(UEnum* uenum, int64_t enum_value) -> SystemStringType { UEnum::ECppForm cpp_form = uenum->GetCppForm(); - const std::wstring enum_native_name = get_native_enum_name(uenum, false); + const SystemStringType enum_native_name = get_native_enum_name(uenum, false); - std::wstring enum_constant_name; + SystemStringType enum_constant_name; for (auto [Name, Value] : uenum->ForEachName()) { if (Value == enum_value) { - enum_constant_name = sanitize_enumeration_name(Name.ToString()); + enum_constant_name = sanitize_enumeration_name(to_system_string(Name.ToString())); } } if (enum_constant_name.empty()) { - Output::send(STR("Warning: Invalid value for enum '{}', casting instead of using enum name. Value '{}' will be cast to the enum.\n"), + Output::send(SYSSTR("Warning: Invalid value for enum '{}', casting instead of using enum name. Value '{}' will be cast to the enum.\n"), enum_native_name, enum_value); - return std::format(STR("({}){}"), enum_native_name, enum_value); + return std::format(SYSSTR("({}){}"), enum_native_name, enum_value); } else { @@ -1053,65 +1068,74 @@ namespace RC::UEGenerator { return enum_constant_name; } - return std::format(STR("{}::{}"), enum_native_name, enum_constant_name); + return std::format(SYSSTR("{}::{}"), enum_native_name, enum_constant_name); } } auto UEHeaderGenerator::generate_simple_assignment_expression(FProperty* property, - const std::wstring& value, + const SystemStringType& value, GeneratedSourceFile& implementation_file, - const std::wstring& property_scope, - const std::wstring& operator_type) -> void + const SystemStringType& property_scope, + const SystemStringType& operator_type) -> void { - const std::wstring field_class_name = property->GetName(); + const auto field_class_name = to_system(property->GetName()); if (property->GetArrayDim() == 1) { - implementation_file.append_line(std::format(STR("{}{}{}{};"), property_scope, field_class_name, operator_type, value)); + implementation_file.append_line(std::format(SYSSTR("{}{}{}{};"), property_scope, field_class_name, operator_type, value)); } else { for (int32_t i = 0; i < property->GetArrayDim(); i++) { - implementation_file.append_line(std::format(STR("{}{}[{}]{}{};"), property_scope, field_class_name, i, operator_type, value)); + implementation_file.append_line(std::format(SYSSTR("{}{}[{}]{}{};"), property_scope, field_class_name, i, operator_type, value)); } } } auto UEHeaderGenerator::generate_advanced_assignment_expression(FProperty* property, - const std::wstring& value, + const SystemStringType& value, GeneratedSourceFile& implementation_file, - const std::wstring& property_scope, - const std::wstring& property_type, - const std::wstring& operator_type) -> void + const SystemStringType& property_scope, + const SystemStringType& property_type, + const SystemStringType& operator_type) -> void { - const std::wstring field_class_name = property->GetName(); - implementation_file.append_line(std::format(STR("const FProperty* p_{} = GetClass()->FindPropertyByName(\"{}\");"), field_class_name, field_class_name)); + const auto field_class_name = to_system(property->GetName()); + implementation_file.append_line(std::format(SYSSTR("const FProperty* p_{} = GetClass()->FindPropertyByName(\"{}\");"), field_class_name, field_class_name)); if (property->GetArrayDim() == 1) { - implementation_file.append_line(std::format(STR("(*p_{}->ContainerPtrToValuePtr<{}>(this)){}{};"), field_class_name, property_type, operator_type, value)); + implementation_file.append_line( + std::format(SYSSTR("(*p_{}->ContainerPtrToValuePtr<{}>(this)){}{};"), field_class_name, property_type, operator_type, value)); } else { for (int32_t i = 0; i < property->GetArrayDim(); i++) { - implementation_file.append_line( - std::format(STR("*p_{}->ContainerPtrToValuePtr<{}>(this){}[{}]{}{};"), field_class_name, property_scope, field_class_name, i, operator_type, value)); + implementation_file.append_line(std::format(SYSSTR("*p_{}->ContainerPtrToValuePtr<{}>(this){}[{}]{}{};"), + field_class_name, + property_scope, + field_class_name, + i, + operator_type, + value)); } } } auto UEHeaderGenerator::generate_property_value( - UStruct* ustruct, FProperty* property, void* object, GeneratedSourceFile& implementation_file, const std::wstring& property_scope) -> void + UStruct* ustruct, FProperty* property, void* object, GeneratedSourceFile& implementation_file, const SystemStringType& property_scope) -> void { - const std::wstring property_name = property->GetName(); - if (property_name == STR("NativeClass") || property_name == STR("hudClass")) { return; } + const auto property_name = property->GetName(); + if (property_name == STR("NativeClass") || property_name == STR("hudClass")) + { + return; + } const bool private_access_modifier = get_property_access_modifier(property) == AccessModifier::Private; bool super_and_no_access = false; // Populate super for checks to ensure values are only initialized if they are overriden in the child class UStruct* super; void* super_object = nullptr; FProperty* super_property = nullptr; - const std::wstring property_type = generate_property_cxx_name(property, true, ustruct); + const auto property_type = generate_property_cxx_name(property, true, ustruct); auto as_class = Cast(ustruct); if (as_class) { @@ -1152,15 +1176,19 @@ namespace RC::UEGenerator } UEnum* uenum = byte_property->GetEnum(); - std::wstring result_property_value; + SystemStringType result_property_value; if (uenum != NULL) { - const std::wstring enum_type_name = get_native_enum_name(uenum); + const SystemStringType enum_type_name = get_native_enum_name(uenum); result_property_value = generate_enum_value(uenum, *byte_property_value); } else { +#ifdef WIN32 result_property_value = std::to_wstring(*byte_property_value); +#else + result_property_value = std::to_string(*byte_property_value); +#endif } if (!super_and_no_access) @@ -1181,7 +1209,7 @@ namespace RC::UEGenerator UEnum* uenum = p_enum_property->GetEnum(); if (uenum == NULL) { - throw std::runtime_error(RC::fmt("EnumProperty %S does not have a valid Enum value", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("EnumProperty {} does not have a valid Enum value", property->GetName())); } FNumericProperty* underlying_property = p_enum_property->GetUnderlyingProperty(); @@ -1200,7 +1228,7 @@ namespace RC::UEGenerator } implementation_file.add_dependency_object(uenum, DependencyLevel::Include); - std::wstring result_property_value = generate_enum_value(uenum, value); + SystemStringType result_property_value = generate_enum_value(uenum, value); if (!super_and_no_access) { generate_simple_assignment_expression(property, result_property_value, implementation_file, property_scope); @@ -1242,7 +1270,7 @@ namespace RC::UEGenerator super_and_no_access = private_access_modifier; } - const std::wstring result_property_value = result_bool_value ? STR("true") : STR("false"); + const SystemStringType result_property_value = result_bool_value ? SYSSTR("true") : SYSSTR("false"); if (!super_and_no_access) { generate_simple_assignment_expression(property, result_property_value, implementation_file, property_scope); @@ -1250,7 +1278,10 @@ namespace RC::UEGenerator else { // Skip private bitmask bools TODO: Fix setting these - if (bool_property->GetFieldMask() != 255) { return; } + if (bool_property->GetFieldMask() != 255) + { + return; + } generate_advanced_assignment_expression(property, result_property_value, implementation_file, property_scope, property_type); } return; @@ -1260,13 +1291,13 @@ namespace RC::UEGenerator if (property->IsA()) { FName* name_value = property->ContainerPtrToValuePtr(object); - const std::wstring name_value_string = name_value->ToString(); + const auto name_value_string = (name_value->ToString()); // Ensure property either does not exist in parent class or is overriden in the CDO for the child class if (super_property != nullptr) { FName* super_name_value = super_property->ContainerPtrToValuePtr(super_object); - const std::wstring super_name_value_string = super_name_value->ToString(); + const auto super_name_value_string = (super_name_value->ToString()); if (name_value_string == super_name_value_string) { return; @@ -1276,7 +1307,7 @@ namespace RC::UEGenerator if (name_value_string != STR("None")) { - const std::wstring result_property_value = std::format(STR("TEXT(\"{}\")"), name_value_string); + const auto result_property_value = std::format(SYSSTR("TEXT(\"{}\")"), to_system(name_value_string)); if (!super_and_no_access) { generate_simple_assignment_expression(property, result_property_value, implementation_file, property_scope); @@ -1293,13 +1324,13 @@ namespace RC::UEGenerator if (property->IsA()) { FString* string_value = property->ContainerPtrToValuePtr(object); - const std::wstring string_value_string = string_value->GetCharArray(); + const auto string_value_string = (to_system(string_value->GetCharArray())); // Ensure property either does not exist in parent class or is overriden in the CDO for the child class if (super_property != nullptr) { FString* super_string_value = super_property->ContainerPtrToValuePtr(super_object); - const std::wstring super_string_value_string = super_string_value->GetCharArray(); + const auto super_string_value_string = (to_system(super_string_value->GetCharArray())); if (string_value_string == super_string_value_string) { return; @@ -1307,9 +1338,9 @@ namespace RC::UEGenerator super_and_no_access = private_access_modifier; } - if (string_value_string != STR("")) + if (string_value_string != SYSSTR("")) { - const std::wstring result_value = create_string_literal(string_value_string); + const auto result_value = create_string_literal(to_system_string(string_value_string)); if (!super_and_no_access) { generate_simple_assignment_expression(property, result_value, implementation_file, property_scope); @@ -1342,7 +1373,7 @@ namespace RC::UEGenerator if (text_value_string != STR("")) { - const std::wstring result_property_value = std::format(STR("FText::FromString({})"), create_string_literal(text_value_string)); + const auto result_property_value = std::format(SYSSTR("FText::FromString({})"), create_string_literal(to_system(text_value_string))); if (!super_and_no_access) { generate_simple_assignment_expression(property, result_property_value, implementation_file, property_scope); @@ -1378,11 +1409,11 @@ namespace RC::UEGenerator // If class value is NULL, generate a simple NULL assignment if (!super_and_no_access) { - generate_simple_assignment_expression(property, STR("NULL"), implementation_file, property_scope); + generate_simple_assignment_expression(property, SYSSTR("NULL"), implementation_file, property_scope); } else { - generate_advanced_assignment_expression(property, STR("NULL"), implementation_file, property_scope, property_type); + generate_advanced_assignment_expression(property, SYSSTR("NULL"), implementation_file, property_scope, property_type); } } else if ((class_value->GetClassFlags() & CLASS_Native) != 0) @@ -1390,8 +1421,8 @@ namespace RC::UEGenerator implementation_file.add_dependency_object(class_value, DependencyLevel::Include); // Otherwise, generate StaticClass call, assuming the class is native - const std::wstring object_class_name = get_native_class_name(class_value); - const std::wstring initializer = std::format(STR("{}::StaticClass()"), object_class_name); + const SystemStringType object_class_name = get_native_class_name(class_value); + const SystemStringType initializer = std::format(SYSSTR("{}::StaticClass()"), object_class_name); if (!super_and_no_access) { @@ -1405,7 +1436,7 @@ namespace RC::UEGenerator else { // Unhandled case, reference to the non-native blueprint class potentially? - Output::send(STR("Unhandled default value of the FClassProperty {}: {}\n"), property->GetFullName(), class_value->GetFullName()); + Output::send(SYSSTR("Unhandled default value of the FClassProperty {}: {}\n"), (property->GetFullName()), (class_value->GetFullName())); } return; } @@ -1436,11 +1467,11 @@ namespace RC::UEGenerator // TODO: Needs additional checks to see if the class is abstract to potentially change this to a default object init if (!super_and_no_access) { - generate_simple_assignment_expression(property, STR("NULL"), implementation_file, property_scope); + generate_simple_assignment_expression(property, SYSSTR("NULL"), implementation_file, property_scope); } else { - generate_advanced_assignment_expression(property, STR("NULL"), implementation_file, property_scope, property_type); + generate_advanced_assignment_expression(property, SYSSTR("NULL"), implementation_file, property_scope, property_type); } return; } @@ -1448,14 +1479,14 @@ namespace RC::UEGenerator if (sub_object_value->HasAnyFlags(EObjectFlags::RF_DefaultSubObject)) { UClass* object_class_type = sub_object_value->GetClassPrivate(); - const std::wstring object_name = sub_object_value->GetName(); + const auto object_name = (sub_object_value->GetName()); UClass* super_object_class_type{}; // Additional checks to ensure this property needs to be initialized in the current class if (super_sub_object_value) { super_object_class_type = super_sub_object_value->GetClassPrivate(); - const std::wstring super_object_name = super_sub_object_value->GetName(); + const auto super_object_name = (super_sub_object_value->GetName()); if ((object_class_type == super_object_class_type) && (object_name == super_object_name)) { return; @@ -1464,7 +1495,7 @@ namespace RC::UEGenerator } bool parent_component_found = false; - std::wstring prior_property_variable{}; + SystemStringType prior_property_variable{}; // Check to see if any other property in the super initialized a component with the same name to ensure // we are not creating the subobject in a child class unnecessarily. @@ -1478,7 +1509,7 @@ namespace RC::UEGenerator UObject* check_super_sub_object_value = *check_super_object_property->ContainerPtrToValuePtr(super_object); if (check_super_sub_object_value) { - std::wstring check_super_object_name = check_super_sub_object_value->GetName(); + auto check_super_object_name = (check_super_sub_object_value->GetName()); if (check_super_object_name == object_name) { parent_component_found = true; @@ -1491,33 +1522,33 @@ namespace RC::UEGenerator // Generate an initializer by either setting this property to a pre-existing property // overriding the object class of an existing component, or creating a new default subobject - std::wstring initializer{}; + SystemStringType initializer{}; if (auto it = m_class_subobjects.find(object_name); it != m_class_subobjects.end()) { // Set property to equal previous property referencing the same object - initializer = it->second; - FProperty* prior_property = ustruct->GetPropertyByNameInChain(initializer.c_str()); + initializer = to_system(it->second); + FProperty* prior_property = ustruct->GetPropertyByNameInChain(it->second.c_str()); bool prior_private = get_property_access_modifier(prior_property) == AccessModifier::Private; if (prior_private) { UObject* check_sub_object_value = *prior_property->ContainerPtrToValuePtr(object); - std::wstring prior_prop_class_name = STR("NULL"); + SystemStringType prior_prop_class_name = SYSSTR("NULL"); if (check_sub_object_value->GetClassPrivate() != nullptr) { prior_prop_class_name = get_native_class_name(check_sub_object_value->GetClassPrivate()); } implementation_file.append_line( - std::format(STR("FProperty* p_{}_Prior = GetClass()->FindPropertyByName(\"{}\");"), initializer, initializer)); - initializer = std::format(STR("*p_{}_Prior->ContainerPtrToValuePtr<{}*>(this)"), initializer, prior_prop_class_name); + std::format(SYSSTR("FProperty* p_{}_Prior = GetClass()->FindPropertyByName(\"{}\");"), initializer, initializer)); + initializer = std::format(SYSSTR("*p_{}_Prior->ContainerPtrToValuePtr<{}*>(this)"), initializer, prior_prop_class_name); } if (!super_and_no_access) { - initializer = std::format(STR("({}*){}"), get_native_class_name(object_property->GetPropertyClass()), initializer); + initializer = std::format(SYSSTR("({}*){}"), get_native_class_name(object_property->GetPropertyClass()), initializer); generate_simple_assignment_expression(property, initializer, implementation_file, property_scope); } else { - initializer = std::format(STR("({}*){}"), get_native_class_name(object_property->GetPropertyClass()), initializer); + initializer = std::format(SYSSTR("({}*){}"), get_native_class_name(object_property->GetPropertyClass()), initializer); generate_advanced_assignment_expression(property, initializer, implementation_file, property_scope, property_type); } } @@ -1526,16 +1557,16 @@ namespace RC::UEGenerator // Add an objectinitializer default subobject class override to the constructor implementation_file.add_dependency_object(object_class_type, DependencyLevel::Include); implementation_file.m_implementation_constructor.append( - std::format(STR(".SetDefaultSubobjectClass<{}>(TEXT(\"{}\"))"), get_native_class_name(object_class_type), object_name)); - m_class_subobjects.try_emplace(object_name, property->GetName()); + std::format(SYSSTR(".SetDefaultSubobjectClass<{}>(TEXT(\"{}\"))"), get_native_class_name(object_class_type), to_system(object_name))); + m_class_subobjects.try_emplace(object_name, (property->GetName())); } else { // Generate a CreateDefaultSubobject function call implementation_file.add_dependency_object(object_class_type, DependencyLevel::Include); - const std::wstring object_class_name = get_native_class_name(object_class_type); - initializer = std::format(STR("CreateDefaultSubobject<{}>(TEXT(\"{}\"))"), object_class_name, object_name); - m_class_subobjects.try_emplace(object_name, property->GetName()); + const SystemStringType object_class_name = get_native_class_name(object_class_type); + initializer = std::format(SYSSTR("CreateDefaultSubobject<{}>(TEXT(\"{}\"))"), object_class_name, to_system(object_name)); + m_class_subobjects.try_emplace(object_name, (property->GetName())); if (!super_and_no_access) { generate_simple_assignment_expression(property, initializer, implementation_file, property_scope); @@ -1554,14 +1585,14 @@ namespace RC::UEGenerator } if (attach_parent_object_value != NULL) { - const std::wstring attach_parent_object_name = attach_parent_object_value->GetName(); - const std::wstring operator_type = STR("->"); + const auto attach_parent_object_name = (attach_parent_object_value->GetName()); + const SystemStringType operator_type = SYSSTR("->"); bool parent_found = false; - std::wstring attach_string; + SystemStringType attach_string; if (auto it = m_class_subobjects.find(attach_parent_object_name); it != m_class_subobjects.end()) { // Set property to equal previous property referencing the same object - attach_string = std::format(STR("SetupAttachment({})"), it->second); + attach_string = std::format(SYSSTR("SetupAttachment({})"), to_system(it->second)); parent_found = true; } else if (as_class) @@ -1574,25 +1605,26 @@ namespace RC::UEGenerator UObject* check_sub_object_value = *check_object_property->ContainerPtrToValuePtr(object); if (check_sub_object_value) { - std::wstring check_object_name = check_sub_object_value->GetName(); + auto check_object_name = (check_sub_object_value->GetName()); if (check_object_name == attach_parent_object_name) { if (get_property_access_modifier(check_object_property) != AccessModifier::Private) { - attach_string = std::format(STR("SetupAttachment({})"), check_property->GetName()); + attach_string = std::format(SYSSTR("SetupAttachment({})"), to_system(check_property->GetName())); } else { - StringType parent_property_name = std::format(STR("const FProperty* p_{}_Parent = GetClass()->FindPropertyByName(\"{}\");"), - check_property->GetName(), - check_property->GetName()); + auto parent_property_name = + std::format(SYSSTR("const FProperty* p_{}_Parent = GetClass()->FindPropertyByName(\"{}\");"), + to_system(check_property->GetName()), + to_system(check_property->GetName())); if (!implementation_file.parent_property_names.contains(parent_property_name)) { implementation_file.parent_property_names.emplace(parent_property_name); implementation_file.append_line(parent_property_name); } - attach_string = std::format(STR("SetupAttachment(p_{}_Parent->ContainerPtrToValuePtr<{}>(this))"), - check_property->GetName(), + attach_string = std::format(SYSSTR("SetupAttachment(p_{}_Parent->ContainerPtrToValuePtr<{}>(this))"), + to_system(check_property->GetName()), get_native_class_name(check_sub_object_value->GetClassPrivate())); implementation_file.add_dependency_object(check_sub_object_value->GetClassPrivate(), DependencyLevel::Include); } @@ -1607,11 +1639,11 @@ namespace RC::UEGenerator { if (!super_and_no_access) { - implementation_file.attachments.try_emplace(property, std::tuple{property_type, attach_string, false}); + implementation_file.attachments.try_emplace(property, GeneratedSourceFile::attachment_data{property_type, attach_string, false}); } else { - implementation_file.attachments.try_emplace(property, std::tuple{property_type, attach_string, true}); + implementation_file.attachments.try_emplace(property, GeneratedSourceFile::attachment_data{property_type, attach_string, true}); } } } @@ -1634,8 +1666,8 @@ namespace RC::UEGenerator // Generate a ::StaticClass call if this object represents a class implementation_file.add_dependency_object(sub_object_as_class, DependencyLevel::Include); - const std::wstring object_class_name = get_native_class_name(sub_object_as_class); - const std::wstring initializer = std::format(STR("{}::StaticClass()"), object_class_name); + const SystemStringType object_class_name = get_native_class_name(sub_object_as_class); + const SystemStringType initializer = std::format(SYSSTR("{}::StaticClass()"), object_class_name); if (!super_and_no_access) { generate_simple_assignment_expression(property, initializer, implementation_file, property_scope); @@ -1648,14 +1680,14 @@ namespace RC::UEGenerator } // Unhandled case, might be some external object reference - Output::send(STR("Unhandled default value of the FObjectProperty {}: {}\n"), property->GetFullName(), sub_object_value->GetFullName()); + Output::send(SYSSTR("Unhandled default value of the FObjectProperty {}: {}\n"), (property->GetFullName()), (sub_object_value->GetFullName())); return; } // Struct properties are serialization as normal struct constructors with custom scope // TODO there are a lot of issues with that regarding member access, unnecessary assignments and so on - /*if (FieldClassName == STR("StructProperty")) { + /*if (FieldClassName == SYSSTR("StructProperty")) { XStructProperty* StructProperty = static_cast(Property); UScriptStruct* ScriptStruct = StructProperty->get_script_struct(); @@ -1664,7 +1696,7 @@ namespace RC::UEGenerator } void* StructDataPointer = StructProperty->container_ptr_to_value_ptr(Object); - const std::wstring NewPropertyScope = std::format(STR("{}{}."), PropertyScope, StructProperty->GetName()); + const SystemStringType NewPropertyScope = std::format(SYSSTR("{}{}."), PropertyScope, StructProperty->GetName()); //Generate values for each struct property //TODO we do not really need to generate assignments for each struct member, we only really need members that are different from the constructor set @@ -1694,7 +1726,8 @@ namespace RC::UEGenerator { if (!super_and_no_access) { - implementation_file.append_line(std::format(STR("{}{}.AddDefaulted({});"), property_scope, property->GetName(), property_value->Num())); + implementation_file.append_line( + std::format(SYSSTR("{}{}.AddDefaulted({});"), property_scope, to_system(property->GetName()), (int32_t)property_value->Num())); } else { @@ -1729,16 +1762,16 @@ namespace RC::UEGenerator super_and_no_access = private_access_modifier; } - std::wstring number_constant_string; + SystemStringType number_constant_string; if (!numeric_property->IsFloatingPoint()) { int64 value = numeric_property->GetSignedIntPropertyValue(numeric_property->ContainerPtrToValuePtr(object)); - number_constant_string = std::to_wstring(value); + number_constant_string = to_system(ToString(value)); } else { double value = numeric_property->GetFloatingPointPropertyValue(numeric_property->ContainerPtrToValuePtr(object)); - number_constant_string = std::format(STR("{:.2f}f"), value); + number_constant_string = std::format(SYSSTR("{:.2f}f"), value); } if (!super_and_no_access) { @@ -1758,24 +1791,24 @@ namespace RC::UEGenerator bool is_generating_interface, const CaseInsensitiveSet& blacklisted_property_names) -> void { - const std::wstring class_native_name = get_native_class_name(uclass, is_generating_interface); - const std::wstring raw_function_name = function->GetName(); + const auto class_native_name = get_native_class_name(uclass, is_generating_interface); + const auto raw_function_name = to_system(function->GetName()); auto function_flags = function->GetFunctionFlags(); - PropertyTypeDeclarationContext context(uclass->GetName(), &implementation_file); + PropertyTypeDeclarationContext context(to_system(uclass->GetName()), &implementation_file); - std::wstring function_implementation_name; - std::wstring net_validate_function_name; + SystemStringType function_implementation_name; + SystemStringType net_validate_function_name; bool is_input_function_const = ((function_flags)&FUNC_Const) != 0; if ((function_flags & FUNC_Net) != 0) { // Network functions always have the implementation inside the _Implementation function - function_implementation_name = std::format(STR("{}::{}_Implementation"), class_native_name, raw_function_name); + function_implementation_name = std::format(SYSSTR("{}::{}_Implementation"), class_native_name, raw_function_name); // Validated network functions by default have their validation function name set to _Validate if ((function_flags & FUNC_NetValidate) != 0) { - net_validate_function_name = std::format(STR("{}::{}_Validate"), class_native_name, raw_function_name); + net_validate_function_name = std::format(SYSSTR("{}::{}_Validate"), class_native_name, raw_function_name); } } else if ((function_flags & FUNC_BlueprintEvent) != 0) @@ -1784,16 +1817,16 @@ namespace RC::UEGenerator // BlueprintImplementableEvents do not have any native functions at all, they're just thunks if ((function_flags & FUNC_Native) != 0) { - function_implementation_name = std::format(STR("{}::{}_Implementation"), class_native_name, raw_function_name); + function_implementation_name = std::format(SYSSTR("{}::{}_Implementation"), class_native_name, raw_function_name); } } else { // Otherwise, normal UFunctions get a standard name matching the function in question - function_implementation_name = std::format(STR("{}::{}"), class_native_name, raw_function_name); + function_implementation_name = std::format(SYSSTR("{}::{}"), class_native_name, raw_function_name); } - std::wstring function_parameter_list; + SystemStringType function_parameter_list; if (!function_implementation_name.empty() || !net_validate_function_name.empty()) { function_parameter_list = @@ -1804,63 +1837,63 @@ namespace RC::UEGenerator { FProperty* return_value_property = function->GetReturnProperty(); - const std::wstring return_value_type = return_value_property ? generate_property_type_declaration(return_value_property, context) : STR("void"); + const SystemStringType return_value_type = return_value_property ? generate_property_type_declaration(return_value_property, context) : SYSSTR("void"); - implementation_file.append_line(std::format(STR("{} {}({}){} {{"), + implementation_file.append_line(std::format(SYSSTR("{} {}({}){} {{"), return_value_type, function_implementation_name, function_parameter_list, - is_input_function_const ? STR(" const") : STR(""))); + is_input_function_const ? SYSSTR(" const") : SYSSTR(""))); implementation_file.begin_indent_level(); if (return_value_property != NULL) { - const std::wstring default_value = generate_default_property_value(return_value_property, implementation_file, context.context_name); - implementation_file.append_line(std::format(STR("return {};"), default_value)); + const SystemStringType default_value = generate_default_property_value(return_value_property, implementation_file, context.context_name); + implementation_file.append_line(std::format(SYSSTR("return {};"), default_value)); } implementation_file.end_indent_level(); - implementation_file.append_line(STR("}")); + implementation_file.append_line(SYSSTR("}")); } if (!net_validate_function_name.empty()) { - implementation_file.append_line(std::format(STR("bool {}({}) {{"), net_validate_function_name, function_parameter_list)); + implementation_file.append_line(std::format(SYSSTR("bool {}({}) {{"), net_validate_function_name, function_parameter_list)); implementation_file.begin_indent_level(); - implementation_file.append_line(STR("return true;")); + implementation_file.append_line(SYSSTR("return true;")); implementation_file.end_indent_level(); - implementation_file.append_line(STR("}")); + implementation_file.append_line(SYSSTR("}")); } } - auto UEHeaderGenerator::generate_parameter_count_string(int32_t parameter_count) -> std::wstring + auto UEHeaderGenerator::generate_parameter_count_string(int32_t parameter_count) -> SystemStringType { switch (parameter_count) { case 0: - return STR(""); + return SYSSTR(""); case 1: - return STR("_OneParam"); + return SYSSTR("_OneParam"); case 2: - return STR("_TwoParams"); + return SYSSTR("_TwoParams"); case 3: - return STR("_ThreeParams"); + return SYSSTR("_ThreeParams"); case 4: - return STR("_FourParams"); + return SYSSTR("_FourParams"); case 5: - return STR("_FiveParams"); + return SYSSTR("_FiveParams"); case 6: - return STR("_SixParams"); + return SYSSTR("_SixParams"); case 7: - return STR("_SevenParams"); + return SYSSTR("_SevenParams"); case 8: - return STR("_EightParams"); + return SYSSTR("_EightParams"); case 9: - return STR("_NineParams"); + return SYSSTR("_NineParams"); default: - return STR("_TooMany"); + return SYSSTR("_TooMany"); } } @@ -1872,15 +1905,15 @@ namespace RC::UEGenerator if (needed_access == AccessModifier::Public) { - header_data.append_line_no_indent(STR("public:")); + header_data.append_line_no_indent(SYSSTR("public:")); } else if (needed_access == AccessModifier::Protected) { - header_data.append_line_no_indent(STR("protected:")); + header_data.append_line_no_indent(SYSSTR("protected:")); } else if (needed_access == AccessModifier::Private) { - header_data.append_line_no_indent(STR("private:")); + header_data.append_line_no_indent(SYSSTR("private:")); } } } @@ -1923,40 +1956,40 @@ namespace RC::UEGenerator return AccessModifier::Public; } - auto UEHeaderGenerator::create_string_literal(const std::wstring& string) -> std::wstring + auto UEHeaderGenerator::create_string_literal(const SystemStringType& string) -> SystemStringType { - std::wstring result; - result.append(STR("TEXT(\"")); + SystemStringType result; + result.append(SYSSTR("TEXT(\"")); bool previous_character_was_hex = false; - const wchar_t* ptr = string.c_str(); - while (wchar_t ch = *ptr++) + const SystemCharType* ptr = string.c_str(); + while (SystemCharType ch = *ptr++) { switch (ch) { - case STR('\r'): { + case SYSSTR('\r'): { continue; } - case STR('\n'): { - result.append(STR("\\n")); + case SYSSTR('\n'): { + result.append(SYSSTR("\\n")); previous_character_was_hex = false; break; } - case STR('\\'): { - result.append(STR("\\\\")); + case SYSSTR('\\'): { + result.append(SYSSTR("\\\\")); previous_character_was_hex = false; break; } - case STR('\"'): { - result.append(STR("\\\"")); + case SYSSTR('\"'): { + result.append(SYSSTR("\\\"")); previous_character_was_hex = false; break; } default: { - if (ch < 31 || ch >= 128) + if ((unsigned char)ch < 31 || (unsigned char)ch >= 128) { - result.append(std::format(STR("\\x{:04X}"), ch)); + result.append(std::format(SYSSTR("\\x{:04X}"), ch)); previous_character_was_hex = true; } else @@ -1965,7 +1998,7 @@ namespace RC::UEGenerator // appended to the hex sequence, causing a different number if (previous_character_was_hex && iswxdigit(ch) != 0) { - result.append(STR("\")TEXT(\"")); + result.append(SYSSTR("\")TEXT(\"")); } previous_character_was_hex = false; result.push_back(ch); @@ -1974,19 +2007,19 @@ namespace RC::UEGenerator } } } - result.append(STR("\")")); + result.append(SYSSTR("\")")); return result; } - auto UEHeaderGenerator::convert_module_name_to_api_name(const std::wstring& module_name) -> std::wstring + auto UEHeaderGenerator::convert_module_name_to_api_name(const SystemStringType& module_name) -> SystemStringType { - std::wstring uppercase_string = string_to_uppercase(module_name); - uppercase_string.append(STR("_API")); + SystemStringType uppercase_string = string_to_uppercase(module_name); + uppercase_string.append(SYSSTR("_API")); return uppercase_string; } - auto UEHeaderGenerator::add_module_and_sub_module_dependencies(std::set& out_module_dependencies, - const std::wstring& module_name, + auto UEHeaderGenerator::add_module_and_sub_module_dependencies(std::set& out_module_dependencies, + const SystemStringType& module_name, bool add_self_module) -> void { // Prevent infinite recursion @@ -2002,7 +2035,7 @@ namespace RC::UEGenerator const auto iterator = m_module_dependencies.find(module_name); if (iterator != m_module_dependencies.end()) { - for (const std::wstring& DependencyModuleName : *iterator->second) + for (const SystemStringType& DependencyModuleName : *iterator->second) { out_module_dependencies.insert(DependencyModuleName); } @@ -2019,12 +2052,12 @@ namespace RC::UEGenerator for (FProperty* property : class_object->ForEachProperty()) { - result_set.insert(property->GetName()); + result_set.insert(to_system(property->GetName())); } for (UFunction* function : class_object->ForEachFunction()) { - result_set.insert(function->GetName()); + result_set.insert(to_system(function->GetName())); } } else if (uclass->GetClassPrivate()->IsChildOf(UScriptStruct::StaticClass())) @@ -2033,7 +2066,7 @@ namespace RC::UEGenerator for (FProperty* property : script_struct->ForEachProperty()) { - result_set.insert(property->GetName()); + result_set.insert(to_system(property->GetName())); } } return result_set; @@ -2041,7 +2074,7 @@ namespace RC::UEGenerator // TODO CannotImplementInterfaceInBlueprint is not exactly right, // TODO you can have interface with no implementable blueprint methods but that you can still implement in blueprint - auto UEHeaderGenerator::generate_interface_flags(UClass* uinterface) const -> std::wstring + auto UEHeaderGenerator::generate_interface_flags(UClass* uinterface) const -> SystemStringType { FlagFormatHelper flag_format_helper{}; @@ -2053,7 +2086,7 @@ namespace RC::UEGenerator if ((class_own_flags & CLASS_MinimalAPI) != 0) { - flag_format_helper.add_switch(STR("MinimalAPI")); + flag_format_helper.add_switch(SYSSTR("MinimalAPI")); } ClassBlueprintInfo blueprint_info = get_class_blueprint_info(uinterface); @@ -2063,22 +2096,22 @@ namespace RC::UEGenerator { if (!parent_blueprint_info.is_blueprintable) { - flag_format_helper.add_switch(STR("Blueprintable")); + flag_format_helper.add_switch(SYSSTR("Blueprintable")); } } else if (blueprint_info.is_blueprint_type) { if (!parent_blueprint_info.is_blueprint_type) { - flag_format_helper.add_switch(STR("BlueprintType")); + flag_format_helper.add_switch(SYSSTR("BlueprintType")); } - flag_format_helper.get_meta()->add_switch(STR("CannotImplementInterfaceInBlueprint")); + flag_format_helper.get_meta()->add_switch(SYSSTR("CannotImplementInterfaceInBlueprint")); } return flag_format_helper.build_flag_string(); } - auto UEHeaderGenerator::generate_class_flags(UClass* uclass) const -> std::wstring + auto UEHeaderGenerator::generate_class_flags(UClass* uclass) const -> SystemStringType { FlagFormatHelper flag_format_helper{}; @@ -2090,7 +2123,7 @@ namespace RC::UEGenerator if ((class_own_flags & CLASS_MinimalAPI) != 0) { - flag_format_helper.add_switch(STR("MinimalAPI")); + flag_format_helper.add_switch(SYSSTR("MinimalAPI")); } ClassBlueprintInfo blueprint_info = get_class_blueprint_info(uclass); @@ -2102,7 +2135,7 @@ namespace RC::UEGenerator if (UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeAllPropertyBlueprintsReadWrite) { - flag_format_helper.add_switch(STR("Blueprintable")); + flag_format_helper.add_switch(SYSSTR("Blueprintable")); } else { @@ -2110,97 +2143,97 @@ namespace RC::UEGenerator { if (!parent_blueprint_info.is_blueprintable) { - flag_format_helper.add_switch(STR("Blueprintable")); + flag_format_helper.add_switch(SYSSTR("Blueprintable")); } } else if (blueprint_info.is_blueprint_type) { if (!parent_blueprint_info.is_blueprint_type) { - flag_format_helper.add_switch(STR("BlueprintType")); + flag_format_helper.add_switch(SYSSTR("BlueprintType")); } } } if ((class_own_flags & CLASS_Deprecated) != 0) { - flag_format_helper.add_switch(STR("Deprecated")); + flag_format_helper.add_switch(SYSSTR("Deprecated")); } if ((class_own_flags & CLASS_Abstract) != 0) { - flag_format_helper.add_switch(STR("Abstract")); + flag_format_helper.add_switch(SYSSTR("Abstract")); } if ((class_own_flags & CLASS_MinimalAPI) != 0) { - flag_format_helper.add_switch(STR("MinimalAPI")); + flag_format_helper.add_switch(SYSSTR("MinimalAPI")); } if ((class_own_flags & CLASS_NoExport) != 0) { - flag_format_helper.add_switch(STR("NoExport")); + flag_format_helper.add_switch(SYSSTR("NoExport")); } // TODO not quite the case, because UHT boilerplate implicitly marks every native class as CLASS_Intrinsic // if ((ClassOwnFlags & CLASS_Intrinsic) != 0) { - // FlagFormatHelper.AddSwitch(STR("Intrinsic")); + // FlagFormatHelper.AddSwitch(SYSSTR("Intrinsic")); // } if ((class_own_flags & CLASS_Const) != 0) { - flag_format_helper.add_switch(STR("Const")); + flag_format_helper.add_switch(SYSSTR("Const")); } if ((class_own_flags & CLASS_DefaultToInstanced) != 0) { - flag_format_helper.add_switch(STR("DefaultToInstanced")); + flag_format_helper.add_switch(SYSSTR("DefaultToInstanced")); } UClass* class_within = uclass->GetClassWithin(); if (class_within != NULL && class_within != UObject::StaticClass() && (super_class == NULL || class_within != super_class->GetClassWithin())) { - flag_format_helper.add_parameter(STR("Within"), class_within->GetName()); + flag_format_helper.add_parameter(SYSSTR("Within"), to_system(class_within->GetName())); } if ((class_own_flags & CLASS_Transient) != 0) { - flag_format_helper.add_switch(STR("Transient")); + flag_format_helper.add_switch(SYSSTR("Transient")); } else if ((parent_class_flags & CLASS_Transient) != 0) { - flag_format_helper.add_switch(STR("NonTransient")); + flag_format_helper.add_switch(SYSSTR("NonTransient")); } if ((class_own_flags & CLASS_EditInlineNew) != 0) { - flag_format_helper.add_switch(STR("EditInlineNew")); + flag_format_helper.add_switch(SYSSTR("EditInlineNew")); } else if ((class_flags & CLASS_EditInlineNew) == 0 && (parent_class_flags & CLASS_EditInlineNew) != 0) { - flag_format_helper.add_switch(STR("NotEditInlineNew")); + flag_format_helper.add_switch(SYSSTR("NotEditInlineNew")); } if ((class_own_flags & CLASS_NotPlaceable) != 0) { - flag_format_helper.add_switch(STR("NotPlaceable")); + flag_format_helper.add_switch(SYSSTR("NotPlaceable")); } else if ((class_flags & CLASS_NotPlaceable) == 0 && (parent_class_flags & CLASS_NotPlaceable) != 0) { - flag_format_helper.add_switch(STR("Placeable")); + flag_format_helper.add_switch(SYSSTR("Placeable")); } bool add_config_name{false}; if ((class_own_flags & CLASS_DefaultConfig) != 0) { - flag_format_helper.add_switch(STR("DefaultConfig")); + flag_format_helper.add_switch(SYSSTR("DefaultConfig")); add_config_name = true; } if ((class_own_flags & CLASS_GlobalUserConfig) != 0) { - flag_format_helper.add_switch(STR("GlobalUserConfig")); + flag_format_helper.add_switch(SYSSTR("GlobalUserConfig")); add_config_name = true; } if ((class_own_flags & CLASS_ProjectUserConfig) != 0) { - flag_format_helper.add_switch(STR("ProjectUserConfig")); + flag_format_helper.add_switch(SYSSTR("ProjectUserConfig")); add_config_name = true; } @@ -2212,65 +2245,65 @@ namespace RC::UEGenerator } } - const std::wstring class_config_name = uclass->GetClassConfigName().ToString(); + const auto class_config_name = uclass->GetClassConfigName().ToString(); if (super_class == NULL || class_config_name != super_class->GetClassConfigName().ToString()) { - flag_format_helper.add_parameter(STR("Config"), class_config_name); + flag_format_helper.add_parameter(SYSSTR("Config"), to_system(class_config_name)); // Don't add our override config if we add the real one here add_config_name = false; } if (UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeAllConfigsEngineConfig && add_config_name) { - flag_format_helper.add_parameter(STR("Config"), STR("Engine")); + flag_format_helper.add_parameter(SYSSTR("Config"), SYSSTR("Engine")); } if ((class_own_flags & CLASS_PerObjectConfig) != 0) { - flag_format_helper.add_switch(STR("PerObjectConfig")); + flag_format_helper.add_switch(SYSSTR("PerObjectConfig")); } if ((class_own_flags & CLASS_ConfigDoNotCheckDefaults) != 0) { - flag_format_helper.add_switch(STR("ConfigDoNotCheckDefaults")); + flag_format_helper.add_switch(SYSSTR("ConfigDoNotCheckDefaults")); } if ((class_own_flags & CLASS_HideDropDown) != 0) { - flag_format_helper.add_switch(STR("HideDropdown")); + flag_format_helper.add_switch(SYSSTR("HideDropdown")); } if ((class_own_flags & CLASS_CollapseCategories) != 0) { - flag_format_helper.add_switch(STR("CollapseCategories")); + flag_format_helper.add_switch(SYSSTR("CollapseCategories")); } else if ((parent_class_flags & CLASS_CollapseCategories) != 0) { - flag_format_helper.add_switch(STR("DontCollapseCategories")); + flag_format_helper.add_switch(SYSSTR("DontCollapseCategories")); } // Mark all UActorComponent derived classes as BlueprintSpawnableComponent by default // This will allow using them inside the Simple Construction Script of the blueprint assets if (uclass->IsChildOf()) { - flag_format_helper.get_meta()->add_switch(STR("BlueprintSpawnableComponent")); - flag_format_helper.add_parameter(STR("ClassGroup"), STR("Custom")); + flag_format_helper.get_meta()->add_switch(SYSSTR("BlueprintSpawnableComponent")); + flag_format_helper.add_parameter(SYSSTR("ClassGroup"), SYSSTR("Custom")); } return flag_format_helper.build_flag_string(); } /**/ - auto UEHeaderGenerator::generate_property_type_declaration(FProperty* property, const PropertyTypeDeclarationContext& context) -> std::wstring + auto UEHeaderGenerator::generate_property_type_declaration(FProperty* property, const PropertyTypeDeclarationContext& context) -> SystemStringType { UClass* current_class = Unreal::Cast(property->GetOutermostOwner()); - const std::wstring field_class_name = property->GetClass().GetName(); + const auto field_class_name = property->GetClass().GetName(); // Byte Property if (property->IsA()) { FByteProperty* byte_property = static_cast(property); UEnum* enum_value = byte_property->GetEnum(); - + if (enum_value != NULL) { if (context.source_file != NULL) @@ -2278,7 +2311,7 @@ namespace RC::UEGenerator context.source_file->add_dependency_object(enum_value, DependencyLevel::Include); } - const std::wstring enum_type_name = get_native_enum_name(enum_value); + const SystemStringType enum_type_name = get_native_enum_name(enum_value); if ((property->GetPropertyFlags() & CPF_BlueprintVisible) != 0) { @@ -2286,9 +2319,9 @@ namespace RC::UEGenerator } // Non-EnumClass enumerations should be wrapped into TEnumAsByte according to UHT, but implicit uint8s should not use TEnumAsByte - return std::format(STR("TEnumAsByte<{}>"), enum_type_name); + return std::format(SYSSTR("TEnumAsByte<{}>"), enum_type_name); } - return STR("uint8"); + return SYSSTR("uint8"); } // Enum Property @@ -2299,20 +2332,20 @@ namespace RC::UEGenerator UEnum* uenum = enum_property->GetEnum(); if (uenum == NULL) { - throw std::runtime_error(RC::fmt("EnumProperty %S does not have a valid Enum value", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("EnumProperty {} does not have a valid Enum value", property->GetName())); } if (context.source_file != NULL) { context.source_file->add_dependency_object(uenum, DependencyLevel::Include); } - const std::wstring enum_type_name = get_native_enum_name(uenum); + const SystemStringType enum_type_name = get_native_enum_name(uenum); if ((property->GetPropertyFlags() & CPF_BlueprintVisible) != 0) { this->m_blueprint_visible_enums.insert(enum_type_name); } - const std::wstring underlying_enum_type = generate_property_type_declaration(underlying_property, context); + const SystemStringType underlying_enum_type = generate_property_type_declaration(underlying_property, context); this->m_underlying_enum_types.insert({enum_type_name, underlying_enum_type}); return enum_type_name; } @@ -2326,48 +2359,48 @@ namespace RC::UEGenerator if (bool_property->GetFieldMask() != 255) { *context.out_is_bitmask_bool = true; - return STR("uint8"); + return SYSSTR("uint8"); } } - return STR("bool"); + return SYSSTR("bool"); } // Standard Numeric Properties if (property->IsA()) { - return STR("int8"); + return SYSSTR("int8"); } else if (property->IsA()) { - return STR("int16"); + return SYSSTR("int16"); } else if (property->IsA()) { - return STR("int32"); + return SYSSTR("int32"); } else if (property->IsA()) { - return STR("int64"); + return SYSSTR("int64"); } else if (property->IsA()) { - return STR("uint16"); + return SYSSTR("uint16"); } else if (property->IsA()) { - return STR("uint32"); + return SYSSTR("uint32"); } else if (property->IsA()) { - return STR("uint64"); + return SYSSTR("uint64"); } else if (property->IsA()) { - return STR("float"); + return SYSSTR("float"); } else if (property->IsA()) { - return STR("double"); + return SYSSTR("double"); } // Class Properties @@ -2378,23 +2411,23 @@ namespace RC::UEGenerator if (meta_class == NULL || meta_class == UObject::StaticClass()) { - return STR("UClass*"); + return SYSSTR("UClass*"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(meta_class, DependencyLevel::PreDeclaration); - context.source_file->add_extra_include(STR("Templates/SubclassOf.h")); + context.source_file->add_extra_include(SYSSTR("Templates/SubclassOf.h")); } - const std::wstring meta_class_name = get_native_class_name(meta_class, false); + const SystemStringType meta_class_name = get_native_class_name(meta_class, false); - return std::format(STR("TSubclassOf<{}>"), meta_class_name); + return std::format(SYSSTR("TSubclassOf<{}>"), meta_class_name); } if (auto* class_property = CastField(property); class_property) { // TODO: Confirm that this is accurate - return STR("TClassPtr"); + return SYSSTR("TClassPtr"); } if (property->IsA()) @@ -2404,16 +2437,16 @@ namespace RC::UEGenerator if (meta_class == NULL || meta_class == UObject::StaticClass()) { - return STR("TSoftClassPtr"); + return SYSSTR("TSoftClassPtr"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(meta_class, DependencyLevel::PreDeclaration); } - const std::wstring meta_class_name = get_native_class_name(meta_class, false); + const SystemStringType meta_class_name = get_native_class_name(meta_class, false); - return std::format(STR("TSoftClassPtr<{}>"), meta_class_name); + return std::format(SYSSTR("TSoftClassPtr<{}>"), meta_class_name); } // Object Properties @@ -2426,15 +2459,15 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("UObject*"); + return SYSSTR("UObject*"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(property_class, DependencyLevel::PreDeclaration); } - const std::wstring property_class_name = get_native_class_name(property_class, false); + const SystemStringType property_class_name = get_native_class_name(property_class, false); - return std::format(STR("{}*"), property_class_name); + return std::format(SYSSTR("{}*"), property_class_name); } if (auto* object_property = CastField(property); object_property) @@ -2443,7 +2476,7 @@ namespace RC::UEGenerator if (!property_class) { - return STR("TObjectPtr"); + return SYSSTR("TObjectPtr"); } else { @@ -2453,7 +2486,7 @@ namespace RC::UEGenerator } const auto property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TObjectPtr<{}>"), property_class_name); } } @@ -2464,16 +2497,16 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("TWeakObjectPtr"); + return SYSSTR("TWeakObjectPtr"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(property_class, DependencyLevel::PreDeclaration); } - const std::wstring property_class_name = get_native_class_name(property_class, false); + const SystemStringType property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TWeakObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TWeakObjectPtr<{}>"), property_class_name); } if (property->IsA()) @@ -2483,16 +2516,16 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("TLazyObjectPtr"); + return SYSSTR("TLazyObjectPtr"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(property_class, DependencyLevel::PreDeclaration); } - const std::wstring property_class_name = get_native_class_name(property_class, false); + const SystemStringType property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TLazyObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TLazyObjectPtr<{}>"), property_class_name); } if (property->IsA()) @@ -2502,16 +2535,16 @@ namespace RC::UEGenerator if (property_class == NULL) { - return STR("TSoftObjectPtr"); + return SYSSTR("TSoftObjectPtr"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(property_class, DependencyLevel::PreDeclaration); } - const std::wstring property_class_name = get_native_class_name(property_class, false); + const SystemStringType property_class_name = get_native_class_name(property_class, false); - return std::format(STR("TSoftObjectPtr<{}>"), property_class_name); + return std::format(SYSSTR("TSoftObjectPtr<{}>"), property_class_name); } // Interface Property @@ -2522,16 +2555,16 @@ namespace RC::UEGenerator if (interface_class == NULL || interface_class == UInterface::StaticClass()) { - return STR("FScriptInterface"); + return SYSSTR("FScriptInterface"); } if (context.source_file != NULL) { context.source_file->add_dependency_object(interface_class, DependencyLevel::PreDeclaration); } - const std::wstring interface_class_name = get_native_class_name(interface_class, true); + const SystemStringType interface_class_name = get_native_class_name(interface_class, true); - return std::format(STR("TScriptInterface<{}>"), interface_class_name); + return std::format(SYSSTR("TScriptInterface<{}>"), interface_class_name); } // Struct Property @@ -2542,9 +2575,9 @@ namespace RC::UEGenerator if (script_struct == NULL) { - throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty %S", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty {}", property->GetName())); } - const std::wstring native_struct_name = get_native_struct_name(script_struct); + const SystemStringType native_struct_name = get_native_struct_name(script_struct); if (context.source_file != NULL) { @@ -2613,8 +2646,8 @@ namespace RC::UEGenerator if (property->IsA()) { FFieldPathProperty* field_path_property = static_cast(property); - const std::wstring property_class_name = field_path_property->GetPropertyClass()->GetName(); - return std::format(STR("TFieldPath"), property_class_name); + const auto property_class_name = to_system(field_path_property->GetPropertyClass()->GetName()); + return std::format(SYSSTR("TFieldPath"), property_class_name); } // Collection and Map Properties @@ -2624,8 +2657,8 @@ namespace RC::UEGenerator FArrayProperty* array_property = static_cast(property); FProperty* inner_property = array_property->GetInner(); - const std::wstring inner_property_type = generate_property_type_declaration(inner_property, context.inner_context()); - return std::format(STR("TArray<{}>"), inner_property_type); + const SystemStringType inner_property_type = generate_property_type_declaration(inner_property, context.inner_context()); + return std::format(SYSSTR("TArray<{}>"), inner_property_type); } if (property->IsA()) @@ -2633,8 +2666,8 @@ namespace RC::UEGenerator FSetProperty* set_property = static_cast(property); FProperty* element_prop = set_property->GetElementProp(); - const std::wstring element_property_type = generate_property_type_declaration(element_prop, context.inner_context()); - return std::format(STR("TSet<{}>"), element_property_type); + const SystemStringType element_property_type = generate_property_type_declaration(element_prop, context.inner_context()); + return std::format(SYSSTR("TSet<{}>"), element_property_type); } // TODO: This is missing support for freeze image map properties because XMapProperty is incomplete. (low priority) @@ -2644,63 +2677,62 @@ namespace RC::UEGenerator FProperty* key_property = map_property->GetKeyProp(); FProperty* value_property = map_property->GetValueProp(); - const std::wstring key_type = generate_property_type_declaration(key_property, context.inner_context()); - const std::wstring value_type = generate_property_type_declaration(value_property, context.inner_context()); + const SystemStringType key_type = generate_property_type_declaration(key_property, context.inner_context()); + const SystemStringType value_type = generate_property_type_declaration(value_property, context.inner_context()); - return std::format(STR("TMap<{}, {}>"), key_type, value_type); + return std::format(SYSSTR("TMap<{}, {}>"), key_type, value_type); } // Standard properties that do not have any special attributes if (property->IsA()) { - return STR("FName"); + return SYSSTR("FName"); } else if (property->IsA()) { - return STR("FString"); + return SYSSTR("FString"); } else if (property->IsA()) { - return STR("FText"); + return SYSSTR("FText"); } - throw std::runtime_error(RC::fmt("[generate_property_type_declaration] Unsupported property class '%S', full name: '%S'", - field_class_name.c_str(), - property->GetFullName().c_str())); + throw std::runtime_error( + RC::fmt("[generate_property_type_declaration] Unsupported property class '{}', full name: '{}'", field_class_name, property->GetFullName())); } //*/ - auto UEHeaderGenerator::generate_function_argument_flags(FProperty* property) const -> std::wstring + auto UEHeaderGenerator::generate_function_argument_flags(FProperty* property) const -> SystemStringType { FlagFormatHelper flag_format_helper{}; auto property_flags = property->GetPropertyFlags(); // CPF_ConstParm is handled explicitly in the parameter list generator, it will generate const before parameter // if ((PropertyFlags & CPF_ConstParm) != 0) { - // FlagFormatHelper.AddSwitch(STR("Const")); + // FlagFormatHelper.AddSwitch(SYSSTR("Const")); // } // We only want to add UPARAM(Ref) when parameter is marked as reference AND output, // while not being marked as constant, because if it's marked as constant, it might be a parameter passed by const reference if ((property_flags & CPF_ReferenceParm) != 0 && (property_flags & CPF_OutParm) != 0 && (property_flags & CPF_ConstParm) == 0) { - flag_format_helper.add_switch(STR("Ref")); + flag_format_helper.add_switch(SYSSTR("Ref")); } if ((property_flags & CPF_RepSkip) != 0) { - flag_format_helper.add_switch(STR("NotReplicated")); + flag_format_helper.add_switch(SYSSTR("NotReplicated")); } return flag_format_helper.build_flag_string(); } - auto UEHeaderGenerator::generate_property_flags(FProperty* property) const -> std::wstring + auto UEHeaderGenerator::generate_property_flags(FProperty* property) const -> SystemStringType { FlagFormatHelper flag_format_helper{}; auto property_flags = property->GetPropertyFlags(); if (UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeAllPropertyBlueprintsReadWrite) { - flag_format_helper.add_switch(STR("EditAnywhere")); + flag_format_helper.add_switch(SYSSTR("EditAnywhere")); } else if ((property_flags & CPF_Edit) != 0) { @@ -2708,97 +2740,97 @@ namespace RC::UEGenerator { if ((property_flags & CPF_DisableEditOnTemplate) != 0) { - flag_format_helper.add_switch(STR("VisibleInstanceOnly")); + flag_format_helper.add_switch(SYSSTR("VisibleInstanceOnly")); } else if ((property_flags & CPF_DisableEditOnInstance) != 0) { - flag_format_helper.add_switch(STR("VisibleDefaultsOnly")); + flag_format_helper.add_switch(SYSSTR("VisibleDefaultsOnly")); } else { - flag_format_helper.add_switch(STR("VisibleAnywhere")); + flag_format_helper.add_switch(SYSSTR("VisibleAnywhere")); } } else { if ((property_flags & CPF_DisableEditOnTemplate) != 0) { - flag_format_helper.add_switch(STR("EditInstanceOnly")); + flag_format_helper.add_switch(SYSSTR("EditInstanceOnly")); } else if ((property_flags & CPF_DisableEditOnInstance) != 0) { - flag_format_helper.add_switch(STR("EditDefaultsOnly")); + flag_format_helper.add_switch(SYSSTR("EditDefaultsOnly")); } else { - flag_format_helper.add_switch(STR("EditAnywhere")); + flag_format_helper.add_switch(SYSSTR("EditAnywhere")); } } } if ((property_flags & CPF_NoClear) != 0) { - flag_format_helper.add_switch(STR("NoClear")); + flag_format_helper.add_switch(SYSSTR("NoClear")); } if ((property_flags & CPF_EditFixedSize) != 0) { - flag_format_helper.add_switch(STR("EditFixedSize")); + flag_format_helper.add_switch(SYSSTR("EditFixedSize")); } if ((property_flags & CPF_SimpleDisplay) != 0) { - flag_format_helper.add_switch(STR("SimpleDisplay")); + flag_format_helper.add_switch(SYSSTR("SimpleDisplay")); } if ((property_flags & CPF_AdvancedDisplay) != 0) { - flag_format_helper.add_switch(STR("AdvancedDisplay")); + flag_format_helper.add_switch(SYSSTR("AdvancedDisplay")); } if (UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeAllPropertyBlueprintsReadWrite) { if (property->GetArrayDim() == 1 && is_subtype_valid(property)) { - flag_format_helper.add_switch(STR("BlueprintReadWrite")); + flag_format_helper.add_switch(SYSSTR("BlueprintReadWrite")); } - flag_format_helper.get_meta()->add_parameter(STR("AllowPrivateAccess"), STR("true")); + flag_format_helper.get_meta()->add_parameter(SYSSTR("AllowPrivateAccess"), SYSSTR("true")); } else if ((property_flags & CPF_BlueprintVisible) != 0) { if ((property_flags & CPF_BlueprintReadOnly) != 0) { - flag_format_helper.add_switch(STR("BlueprintReadOnly")); + flag_format_helper.add_switch(SYSSTR("BlueprintReadOnly")); } else { - flag_format_helper.add_switch(STR("BlueprintReadWrite")); + flag_format_helper.add_switch(SYSSTR("BlueprintReadWrite")); } if ((property_flags & CPF_NativeAccessSpecifierPrivate) != 0) { - flag_format_helper.get_meta()->add_parameter(STR("AllowPrivateAccess"), STR("true")); + flag_format_helper.get_meta()->add_parameter(SYSSTR("AllowPrivateAccess"), SYSSTR("true")); } } if ((property_flags & CPF_BlueprintAssignable) != 0) { - flag_format_helper.add_switch(STR("BlueprintAssignable")); + flag_format_helper.add_switch(SYSSTR("BlueprintAssignable")); } if ((property_flags & CPF_BlueprintCallable) != 0) { - flag_format_helper.add_switch(STR("BlueprintCallable")); + flag_format_helper.add_switch(SYSSTR("BlueprintCallable")); } if ((property_flags & CPF_BlueprintAuthorityOnly) != 0) { - flag_format_helper.add_switch(STR("BlueprintAuthorityOnly")); + flag_format_helper.add_switch(SYSSTR("BlueprintAuthorityOnly")); } if ((property_flags & CPF_Config) != 0) { if ((property_flags & CPF_GlobalConfig) != 0) { - flag_format_helper.add_switch(STR("GlobalConfig")); + flag_format_helper.add_switch(SYSSTR("GlobalConfig")); } else { - flag_format_helper.add_switch(STR("Config")); + flag_format_helper.add_switch(SYSSTR("Config")); } } @@ -2806,55 +2838,55 @@ namespace RC::UEGenerator { if ((property_flags & CPF_RepNotify) != 0) { - const std::wstring rep_notify_func_name = property->GetRepNotifyFunc().ToString(); - flag_format_helper.add_parameter(STR("ReplicatedUsing"), rep_notify_func_name); + const auto rep_notify_func_name = to_system(property->GetRepNotifyFunc().ToString()); + flag_format_helper.add_parameter(SYSSTR("ReplicatedUsing"), rep_notify_func_name); } else { - flag_format_helper.add_switch(STR("Replicated")); + flag_format_helper.add_switch(SYSSTR("Replicated")); } } if ((property_flags & CPF_RepSkip) != 0) { - flag_format_helper.add_switch(STR("NotReplicated")); + flag_format_helper.add_switch(SYSSTR("NotReplicated")); } if ((property_flags & CPF_AssetRegistrySearchable) != 0) { - flag_format_helper.add_switch(STR("AssetRegistrySearchable")); + flag_format_helper.add_switch(SYSSTR("AssetRegistrySearchable")); } if ((property_flags & CPF_Interp) != 0) { - flag_format_helper.add_switch(STR("Interp")); + flag_format_helper.add_switch(SYSSTR("Interp")); } if ((property_flags & CPF_SaveGame) != 0) { - flag_format_helper.add_switch(STR("SaveGame")); + flag_format_helper.add_switch(SYSSTR("SaveGame")); } if ((property_flags & CPF_NonTransactional) != 0) { - flag_format_helper.add_switch(STR("NonTransactional")); + flag_format_helper.add_switch(SYSSTR("NonTransactional")); } if ((property_flags & CPF_Transient) != 0) { - flag_format_helper.add_switch(STR("Transient")); + flag_format_helper.add_switch(SYSSTR("Transient")); } if ((property_flags & CPF_DuplicateTransient) != 0) { - flag_format_helper.add_switch(STR("DuplicateTransient")); + flag_format_helper.add_switch(SYSSTR("DuplicateTransient")); } if ((property_flags & CPF_TextExportTransient) != 0) { - flag_format_helper.add_switch(STR("TextExportTransient")); + flag_format_helper.add_switch(SYSSTR("TextExportTransient")); } if ((property_flags & CPF_NonPIEDuplicateTransient) != 0) { - flag_format_helper.add_switch(STR("NonPIEDuplicateTransient")); + flag_format_helper.add_switch(SYSSTR("NonPIEDuplicateTransient")); } if ((property_flags & CPF_SkipSerialization) != 0) { - flag_format_helper.add_switch(STR("SkipSerialization")); + flag_format_helper.add_switch(SYSSTR("SkipSerialization")); } // Need to have all of these flags, otherwise we might accidentally get Instanced of delegate properties; CPF_ExportObject is not set for delegate properties @@ -2867,16 +2899,16 @@ namespace RC::UEGenerator (property->IsA() || (property->IsA() && static_cast(property)->GetInner()->IsA()) || (property->IsA() && static_cast(property)->GetValueProp()->IsA()))) { - flag_format_helper.add_switch(STR("Instanced")); + flag_format_helper.add_switch(SYSSTR("Instanced")); } else if ((property_flags & CPF_ExportObject) != 0) { - flag_format_helper.add_switch(STR("Export")); + flag_format_helper.add_switch(SYSSTR("Export")); } return flag_format_helper.build_flag_string(); } - auto UEHeaderGenerator::generate_struct_flags(UScriptStruct* script_struct) const -> std::wstring + auto UEHeaderGenerator::generate_struct_flags(UScriptStruct* script_struct) const -> SystemStringType { FlagFormatHelper flag_format_helper{}; @@ -2886,40 +2918,40 @@ namespace RC::UEGenerator EStructFlags struct_own_flags = (EStructFlags)(struct_flags & (~(parent_struct_flags & STRUCT_Inherit))); - const std::wstring native_struct_name = get_native_struct_name(script_struct); + const SystemStringType native_struct_name = get_native_struct_name(script_struct); if (is_struct_blueprint_type(script_struct) || m_blueprint_visible_structs.contains(native_struct_name) || UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeAllPropertyBlueprintsReadWrite) { - flag_format_helper.add_switch(STR("BlueprintType")); + flag_format_helper.add_switch(SYSSTR("BlueprintType")); } if ((struct_own_flags & STRUCT_NoExport) != 0) { - flag_format_helper.add_switch(STR("NoExport")); + flag_format_helper.add_switch(SYSSTR("NoExport")); } if ((struct_own_flags & STRUCT_Atomic) != 0) { - flag_format_helper.add_switch(STR("Atomic")); + flag_format_helper.add_switch(SYSSTR("Atomic")); } if ((struct_own_flags & STRUCT_Immutable) != 0) { - flag_format_helper.add_switch(STR("Immutable")); + flag_format_helper.add_switch(SYSSTR("Immutable")); } return flag_format_helper.build_flag_string(); } - auto UEHeaderGenerator::generate_enum_flags(UEnum* uenum) const -> std::wstring + auto UEHeaderGenerator::generate_enum_flags(UEnum* uenum) const -> SystemStringType { - + FlagFormatHelper flag_format_helper{}; auto enum_flags = uenum->GetEnumFlags(); if ((((int32_t)enum_flags) & ((int32_t)EEnumFlags::Flags)) != 0) { - flag_format_helper.add_switch(STR("Flags")); + flag_format_helper.add_switch(SYSSTR("Flags")); } - const std::wstring enum_native_name = get_native_enum_name(uenum); + const SystemStringType enum_native_name = get_native_enum_name(uenum); if (UE4SSProgram::settings_manager.UHTHeaderGenerator.MakeEnumClassesBlueprintType) { @@ -2927,30 +2959,28 @@ namespace RC::UEGenerator if (cpp_form == UEnum::ECppForm::EnumClass) { const auto underlying_type = m_underlying_enum_types.find(enum_native_name); - if (underlying_type->second == STR("uint8") || - (underlying_type == m_underlying_enum_types.end() && - (get_highest_enum(uenum) <= 255 && - get_lowest_enum(uenum) >= 0))) + if ((underlying_type == m_underlying_enum_types.end() && (get_highest_enum(uenum) <= 255 && get_lowest_enum(uenum) >= 0)) || + ((underlying_type != m_underlying_enum_types.end()) && (underlying_type->second == SYSSTR("uint8")))) { // Underlying type is implicit or explicitly uint8. - flag_format_helper.add_switch(STR("BlueprintType")); + flag_format_helper.add_switch(SYSSTR("BlueprintType")); } } else { - flag_format_helper.add_switch(STR("BlueprintType")); + flag_format_helper.add_switch(SYSSTR("BlueprintType")); } } return flag_format_helper.build_flag_string(); } - auto UEHeaderGenerator::sanitize_enumeration_name(const std::wstring& enumeration_name) -> std::wstring + auto UEHeaderGenerator::sanitize_enumeration_name(const SystemStringType& enumeration_name) -> SystemStringType { - std::wstring result_enum_name = enumeration_name; + SystemStringType result_enum_name = enumeration_name; // Remove enumeration name from the string - size_t enum_name_string_split = enumeration_name.find(STR("::")); - if (enum_name_string_split != std::wstring::npos) + size_t enum_name_string_split = enumeration_name.find(SYSSTR("::")); + if (enum_name_string_split != SystemStringType::npos) { result_enum_name.erase(0, enum_name_string_split + 2); } @@ -2965,15 +2995,15 @@ namespace RC::UEGenerator } int64 highest_enum_value = 0; - const StringType enum_prefix = uenum->GenerateEnumPrefix(); - const StringType expected_max_name = std::format(STR("{}_MAX"), enum_prefix); - StringType expected_max_name_lower = expected_max_name; + const auto enum_prefix = to_system(uenum->GenerateEnumPrefix()); + const auto expected_max_name = std::format(SYSSTR("{}_MAX"), enum_prefix); + auto expected_max_name_lower = expected_max_name; std::transform(expected_max_name_lower.begin(), expected_max_name_lower.end(), expected_max_name_lower.begin(), ::towlower); - + for (auto [Name, Value] : uenum->ForEachName()) { - StringType enum_name = sanitize_enumeration_name(Name.ToString()); - StringType enum_name_lower = enum_name; + auto enum_name = sanitize_enumeration_name(to_system_string(Name.ToString())); + auto enum_name_lower = enum_name; std::transform(enum_name_lower.begin(), enum_name_lower.end(), enum_name_lower.begin(), ::towlower); if ((enum_name_lower != expected_max_name_lower && enum_name_lower != sanitize_enumeration_name(expected_max_name_lower)) && Value > highest_enum_value) { @@ -3001,7 +3031,7 @@ namespace RC::UEGenerator return lowest_enum_value; } - auto UEHeaderGenerator::generate_function_flags(UFunction* function, bool is_function_pure_virtual) const -> std::wstring + auto UEHeaderGenerator::generate_function_flags(UFunction* function, bool is_function_pure_virtual) const -> SystemStringType { FlagFormatHelper flag_format_helper{}; @@ -3022,7 +3052,7 @@ namespace RC::UEGenerator // Interface functions cannot be BlueprintPure if ((function_flags & FUNC_BlueprintPure) != 0 && !is_interface_function) { - flag_format_helper.add_switch(STR("BlueprintPure")); + flag_format_helper.add_switch(SYSSTR("BlueprintPure")); } else { @@ -3030,9 +3060,9 @@ namespace RC::UEGenerator // it has been explicitly marked as blueprint impure, and we need to preserve this behavior if ((function_flags & FUNC_Const) != 0 && !is_interface_function) { - flag_format_helper.add_parameter(STR("BlueprintPure"), STR("false")); + flag_format_helper.add_parameter(SYSSTR("BlueprintPure"), SYSSTR("false")); } - flag_format_helper.add_switch(STR("BlueprintCallable")); + flag_format_helper.add_switch(SYSSTR("BlueprintCallable")); blueprint_callable_added = true; } } @@ -3050,7 +3080,7 @@ namespace RC::UEGenerator if (!has_invalid_param) { - flag_format_helper.add_switch(STR("BlueprintCallable")); + flag_format_helper.add_switch(SYSSTR("BlueprintCallable")); } } @@ -3058,11 +3088,11 @@ namespace RC::UEGenerator { if ((function_flags & FUNC_Native) != 0) { - flag_format_helper.add_switch(STR("BlueprintNativeEvent")); + flag_format_helper.add_switch(SYSSTR("BlueprintNativeEvent")); } else { - flag_format_helper.add_switch(STR("BlueprintImplementableEvent")); + flag_format_helper.add_switch(SYSSTR("BlueprintImplementableEvent")); } } @@ -3070,50 +3100,50 @@ namespace RC::UEGenerator { if ((function_flags & FUNC_NetServer) != 0) { - flag_format_helper.add_switch(STR("Server")); + flag_format_helper.add_switch(SYSSTR("Server")); } else if ((function_flags & FUNC_NetClient) != 0) { - flag_format_helper.add_switch(STR("Client")); + flag_format_helper.add_switch(SYSSTR("Client")); } else if ((function_flags & FUNC_NetMulticast) != 0) { - flag_format_helper.add_switch(STR("NetMulticast")); + flag_format_helper.add_switch(SYSSTR("NetMulticast")); } else if ((function_flags & FUNC_NetRequest) != 0) { - flag_format_helper.add_switch(STR("ServiceRequest")); + flag_format_helper.add_switch(SYSSTR("ServiceRequest")); } else if ((function_flags & FUNC_NetResponse) != 0) { - flag_format_helper.add_switch(STR("ServiceResponse")); + flag_format_helper.add_switch(SYSSTR("ServiceResponse")); } if ((function_flags & FUNC_NetReliable) != 0) { - flag_format_helper.add_switch(STR("Reliable")); + flag_format_helper.add_switch(SYSSTR("Reliable")); } else { - flag_format_helper.add_switch(STR("Unreliable")); + flag_format_helper.add_switch(SYSSTR("Unreliable")); } if ((function_flags & FUNC_NetValidate) != 0) { - flag_format_helper.add_switch(STR("WithValidation")); + flag_format_helper.add_switch(SYSSTR("WithValidation")); } } if ((function_flags & FUNC_Exec) != 0) { - flag_format_helper.add_switch(STR("Exec")); + flag_format_helper.add_switch(SYSSTR("Exec")); } if ((function_flags & FUNC_BlueprintAuthorityOnly) != 0) { - flag_format_helper.add_switch(STR("BlueprintAuthorityOnly")); + flag_format_helper.add_switch(SYSSTR("BlueprintAuthorityOnly")); } if ((function_flags & FUNC_BlueprintCosmetic) != 0) { - flag_format_helper.add_switch(STR("BlueprintCosmetic")); + flag_format_helper.add_switch(SYSSTR("BlueprintCosmetic")); } static auto latent_action_info = UObjectGlobals::StaticFindObject(nullptr, nullptr, STR("/Script/Engine.LatentActionInfo")); @@ -3121,11 +3151,11 @@ namespace RC::UEGenerator bool bLAFound = false; for (FProperty* param : function->ForEachProperty()) { - auto param_name = param->GetName(); + auto param_name = to_system(param->GetName()); auto param_uc_name = string_to_uppercase(param_name); - if (param_uc_name.find(STR("WORLDCONTEXT")) != param_uc_name.npos) + if (param_uc_name.find(SYSSTR("WORLDCONTEXT")) != param_uc_name.npos) { - flag_format_helper.get_meta()->add_parameter(STR("WorldContext"), std::format(STR("\"{}\""), param_name)); + flag_format_helper.get_meta()->add_parameter(SYSSTR("WorldContext"), std::format(SYSSTR("\"{}\""), param_name)); bWCFound = true; } if (auto as_struct_property = CastField(param); as_struct_property) @@ -3133,8 +3163,8 @@ namespace RC::UEGenerator // We now know this is a StructProperty. if (as_struct_property->GetStruct()->IsChildOf(latent_action_info)) { - flag_format_helper.get_meta()->add_parameter(STR("LatentInfo"), std::format(STR("\"{}\""), param_name)); - flag_format_helper.get_meta()->add_switch(STR("Latent")); + flag_format_helper.get_meta()->add_parameter(SYSSTR("LatentInfo"), std::format(SYSSTR("\"{}\""), param_name)); + flag_format_helper.get_meta()->add_switch(SYSSTR("Latent")); bLAFound = true; } } @@ -3151,28 +3181,28 @@ namespace RC::UEGenerator UFunction* function, GeneratedSourceFile& header_data, bool generate_comma_before_name, - const std::wstring& context_name, + const SystemStringType& context_name, const CaseInsensitiveSet& blacklisted_property_names, - int32_t* out_num_params) -> std::wstring + int32_t* out_num_params) -> SystemStringType { - std::wstring function_arguments_string; + SystemStringType function_arguments_string; for (FProperty* property : function->ForEachProperty()) { auto property_flags = property->GetPropertyFlags(); if ((property_flags & CPF_Parm) != 0 && (property_flags & CPF_ReturnParm) == 0) { - std::wstring param_declaration; + SystemStringType param_declaration; // We only generate UPARAM declarations if we are not generating the implementation file if (!header_data.is_implementation_file()) { - const std::wstring parameter_flags_string = generate_function_argument_flags(property); + const SystemStringType parameter_flags_string = generate_function_argument_flags(property); if (!parameter_flags_string.empty()) { - param_declaration.append(STR("UPARAM(")); + param_declaration.append(SYSSTR("UPARAM(")); param_declaration.append(parameter_flags_string); - param_declaration.append(STR(") ")); + param_declaration.append(SYSSTR(") ")); } } @@ -3184,7 +3214,7 @@ namespace RC::UEGenerator // Append const keyword to the parameter declaration if it is marked as const parameter if ((property_flags & CPF_ConstParm) != 0 || should_force_const_ref) { - param_declaration.append(STR("const ")); + param_declaration.append(SYSSTR("const ")); } PropertyTypeDeclarationContext context(context_name, &header_data); @@ -3194,27 +3224,27 @@ namespace RC::UEGenerator // which would also be always set for output parameters if ((property_flags & (CPF_ReferenceParm | CPF_OutParm)) != 0 || should_force_const_ref) { - param_declaration.append(STR("&")); + param_declaration.append(SYSSTR("&")); } if (generate_comma_before_name) { - param_declaration.append(STR(",")); + param_declaration.append(SYSSTR(",")); } - param_declaration.append(STR(" ")); + param_declaration.append(SYSSTR(" ")); - std::wstring property_name = property->GetName(); + auto property_name = to_system_string(property->GetName()); // If property name is blacklisted, capitalize first letter and prepend New if ((uclass && is_function_parameter_shadowing(uclass, property)) || blacklisted_property_names.contains(property_name)) { property_name[0] = towupper(property_name[0]); - property_name.insert(0, STR("New")); + property_name.insert(0, SYSSTR("New")); } param_declaration.append(property_name); function_arguments_string.append(param_declaration); - function_arguments_string.append(STR(", ")); + function_arguments_string.append(SYSSTR(", ")); if (out_num_params) { (*out_num_params)++; @@ -3230,9 +3260,10 @@ namespace RC::UEGenerator return function_arguments_string; } - auto UEHeaderGenerator::generate_default_property_value(FProperty* property, GeneratedSourceFile& header_data, const std::wstring& ContextName) -> std::wstring + auto UEHeaderGenerator::generate_default_property_value(FProperty* property, GeneratedSourceFile& header_data, const SystemStringType& ContextName) + -> SystemStringType { - const std::wstring field_class_name = property->GetClass().GetName(); + const auto field_class_name = (property->GetClass().GetName()); PropertyTypeDeclarationContext context(ContextName, &header_data); // Byte Property @@ -3246,7 +3277,7 @@ namespace RC::UEGenerator const int64_t first_enum_constant_value = Enum->GetEnumNameByIndex(0).Value; return generate_enum_value(Enum, first_enum_constant_value); } - return STR("0"); + return SYSSTR("0"); } // Enum Property @@ -3257,7 +3288,7 @@ namespace RC::UEGenerator if (uenum == NULL) { - throw std::runtime_error(RC::fmt("EnumProperty %S does not have a valid Enum value", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("EnumProperty {} does not have a valid Enum value", property->GetName())); } const int64_t first_enum_constant_value = uenum->GetEnumNameByIndex(0).Value; return generate_enum_value(uenum, first_enum_constant_value); @@ -3266,36 +3297,36 @@ namespace RC::UEGenerator // Bool Property if (field_class_name == STR("BoolProperty")) { - return STR("false"); + return SYSSTR("false"); } // Standard Numeric Properties if (field_class_name == STR("Int8Property") || field_class_name == STR("Int16Property") || field_class_name == STR("IntProperty") || field_class_name == STR("Int64Property") || field_class_name == STR("UInt16Property") || field_class_name == STR("UInt32Property") || field_class_name == STR("UInt64Property")) { - return STR("0"); + return SYSSTR("0"); } if (field_class_name == STR("FloatProperty")) { - return STR("0.0f"); + return SYSSTR("0.0f"); } if (field_class_name == STR("DoubleProperty")) { - return STR("0.0"); + return SYSSTR("0.0"); } // Object Properties if (field_class_name == STR("ObjectProperty") || field_class_name == STR("WeakObjectProperty") || field_class_name == STR("LazyObjectProperty") || field_class_name == STR("SoftObjectProperty") || field_class_name == STR("AssetObjectProperty") || property->IsA()) { - return STR("NULL"); + return SYSSTR("NULL"); } // Class Properties if (field_class_name == STR("ClassProperty") || field_class_name == STR("SoftClassProperty") || field_class_name == STR("InterfaceProperty") || field_class_name == STR("AssetClassProperty") || property->IsA()) { - return STR("NULL"); + return SYSSTR("NULL"); } // Struct Property @@ -3306,24 +3337,24 @@ namespace RC::UEGenerator if (script_struct == NULL) { - throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty %S", property->GetName().c_str())); + throw std::runtime_error(RC::fmt("Struct is NULL for StructProperty {}", property->GetName())); } - const std::wstring native_struct_name = get_native_struct_name(script_struct); - return std::format(STR("{}{{}}"), native_struct_name); + const SystemStringType native_struct_name = get_native_struct_name(script_struct); + return std::format(SYSSTR("{}{{}}"), native_struct_name); } // Delegate Properties if (field_class_name == STR("DelegateProperty") || field_class_name == STR("MulticastInlineDelegateProperty") || field_class_name == STR("MulticastSparseDelegateProperty")) { - const std::wstring delegate_type_name = generate_delegate_name(property, context.context_name); - return std::format(STR("{}()"), delegate_type_name); + const SystemStringType delegate_type_name = generate_delegate_name(property, context.context_name); + return std::format(SYSSTR("{}()"), delegate_type_name); } // Field path property if (field_class_name == STR("FieldPathProperty")) { - return STR("FFieldPath()"); + return SYSSTR("FFieldPath()"); } // Collection and Map Properties @@ -3332,8 +3363,8 @@ namespace RC::UEGenerator FArrayProperty* array_property = static_cast(property); FProperty* inner_property = array_property->GetInner(); - const std::wstring inner_property_type = generate_property_type_declaration(inner_property, context); - return std::format(STR("TArray<{}>()"), inner_property_type); + const SystemStringType inner_property_type = generate_property_type_declaration(inner_property, context); + return std::format(SYSSTR("TArray<{}>()"), inner_property_type); } if (field_class_name == STR("SetProperty")) @@ -3341,8 +3372,8 @@ namespace RC::UEGenerator FSetProperty* set_property = static_cast(property); FProperty* element_prop = set_property->GetElementProp(); - const std::wstring element_property_type = generate_property_type_declaration(element_prop, context); - return std::format(STR("TSet<{}>()"), element_property_type); + const SystemStringType element_property_type = generate_property_type_declaration(element_prop, context); + return std::format(SYSSTR("TSet<{}>()"), element_property_type); } if (field_class_name == STR("MapProperty")) @@ -3351,28 +3382,27 @@ namespace RC::UEGenerator FProperty* key_property = map_property->GetKeyProp(); FProperty* value_property = map_property->GetValueProp(); - const std::wstring key_type = generate_property_type_declaration(key_property, context); - const std::wstring value_type = generate_property_type_declaration(value_property, context); + const auto key_type = generate_property_type_declaration(key_property, context); + const auto value_type = generate_property_type_declaration(value_property, context); - return std::format(STR("TMap<{}, {}>()"), key_type, value_type); + return std::format(SYSSTR("TMap<{}, {}>()"), key_type, value_type); } // Various string, name and text properties if (field_class_name == STR("NameProperty")) { - return STR("NAME_None"); + return SYSSTR("NAME_None"); } if (field_class_name == STR("StrProperty")) { - return STR("TEXT(\"\")"); + return SYSSTR("TEXT(\"\")"); } if (field_class_name == STR("TextProperty")) { - return STR("FText::GetEmpty()"); + return SYSSTR("FText::GetEmpty()"); } - throw std::runtime_error(RC::fmt("[generate_default_property_value] Unsupported property class '%S', full name: '%S'", - field_class_name.c_str(), - property->GetFullName().c_str())); + throw std::runtime_error( + RC::fmt("[generate_default_property_value] Unsupported property class '{}', full name: '{}'", field_class_name, property->GetFullName())); } auto UEHeaderGenerator::get_class_blueprint_info(UClass* uclass) -> ClassBlueprintInfo @@ -3462,18 +3492,18 @@ namespace RC::UEGenerator return is_shadowing; } - auto UEHeaderGenerator::get_module_name_for_package(UObject* package) -> std::wstring + auto UEHeaderGenerator::get_module_name_for_package(UObject* package) -> SystemStringType { if (package->GetOuterPrivate() != NULL) { throw std::invalid_argument("Encountered a package with an outer object set"); } - std::wstring package_name = package->GetName(); - if (!package_name.starts_with(STR("/Script/"))) + auto package_name = to_system_string(package->GetName()); + if (!package_name.starts_with(SYSSTR("/Script/"))) { - return STR(""); + return SYSSTR(""); } - package_name.erase(0, wcslen(STR("/Script/"))); + package_name.erase(0, SystemStringType(SYSSTR("/Script/")).length()); return package_name; } @@ -3483,22 +3513,22 @@ namespace RC::UEGenerator this->m_primary_module_name = determine_primary_game_module_name(); // Force inclusion of Core and CoreUObject into all the generated module build files - this->m_forced_module_dependencies.insert(STR("Core")); - this->m_forced_module_dependencies.insert(STR("CoreUObject")); + this->m_forced_module_dependencies.insert(SYSSTR("Core")); + this->m_forced_module_dependencies.insert(SYSSTR("CoreUObject")); // TODO not optimal, but still needed for the majority of the cases - this->m_forced_module_dependencies.insert(STR("Engine")); + this->m_forced_module_dependencies.insert(SYSSTR("Engine")); // Add few classes that require explicit UObjectInitializer constructor call, excluding classes inheriting from AActor. - this->m_classes_with_object_initializer.insert(STR("UUserWidget")); - this->m_classes_with_object_initializer.insert(STR("UListView")); - this->m_classes_with_object_initializer.insert(STR("UMovieSceneTrack")); - this->m_classes_with_object_initializer.insert(STR("USoundWaveProcedural")); - this->m_classes_with_object_initializer.insert(STR("URichTextBlock")); - this->m_classes_with_object_initializer.insert(STR("URichTextBlockImageDecorator")); - this->m_classes_with_object_initializer.insert(STR("URichTextBlockDecorator")); - this->m_classes_with_object_initializer.insert(STR("USkeletalMeshComponentBudgeted")); - this->m_classes_with_object_initializer.insert(STR("UIpNetDriver")); - this->m_classes_with_object_initializer.insert(STR("UAITask")); + this->m_classes_with_object_initializer.insert(SYSSTR("UUserWidget")); + this->m_classes_with_object_initializer.insert(SYSSTR("UListView")); + this->m_classes_with_object_initializer.insert(SYSSTR("UMovieSceneTrack")); + this->m_classes_with_object_initializer.insert(SYSSTR("USoundWaveProcedural")); + this->m_classes_with_object_initializer.insert(SYSSTR("URichTextBlock")); + this->m_classes_with_object_initializer.insert(SYSSTR("URichTextBlockImageDecorator")); + this->m_classes_with_object_initializer.insert(SYSSTR("URichTextBlockDecorator")); + this->m_classes_with_object_initializer.insert(SYSSTR("USkeletalMeshComponentBudgeted")); + this->m_classes_with_object_initializer.insert(SYSSTR("UIpNetDriver")); + this->m_classes_with_object_initializer.insert(SYSSTR("UAITask")); } auto UEHeaderGenerator::ignore_selected_modules() -> void @@ -3510,144 +3540,144 @@ namespace RC::UEGenerator if (UE4SSProgram::settings_manager.UHTHeaderGenerator.IgnoreEngineAndCoreUObject || UE4SSProgram::settings_manager.UHTHeaderGenerator.IgnoreAllCoreEngineModules) { - this->m_ignored_module_names.insert(STR("Engine")); - this->m_ignored_module_names.insert(STR("CoreUObject")); + this->m_ignored_module_names.insert(SYSSTR("Engine")); + this->m_ignored_module_names.insert(SYSSTR("CoreUObject")); } // Skip all core engine packages if requested if (UE4SSProgram::settings_manager.UHTHeaderGenerator.IgnoreAllCoreEngineModules) { - this->m_ignored_module_names.insert(STR("ActorLayerUtilities")); - this->m_ignored_module_names.insert(STR("ActorSequence")); - this->m_ignored_module_names.insert(STR("AIModule")); - this->m_ignored_module_names.insert(STR("AndroidPermission")); - this->m_ignored_module_names.insert(STR("AnimationCore")); - this->m_ignored_module_names.insert(STR("AnimationSharing")); - this->m_ignored_module_names.insert(STR("AnimGraphRuntime")); - this->m_ignored_module_names.insert(STR("AppleImageUtils")); - this->m_ignored_module_names.insert(STR("ArchVisCharacter")); - this->m_ignored_module_names.insert(STR("AssetRegistry")); - this->m_ignored_module_names.insert(STR("AssetTags")); - this->m_ignored_module_names.insert(STR("AudioAnalyzer")); - this->m_ignored_module_names.insert(STR("AudioCapture")); - this->m_ignored_module_names.insert(STR("AudioExtensions")); - this->m_ignored_module_names.insert(STR("AudioMixer")); - this->m_ignored_module_names.insert(STR("AudioPlatformConfiguration")); - this->m_ignored_module_names.insert(STR("AudioSynesthesia")); - this->m_ignored_module_names.insert(STR("AugmentedReality")); - this->m_ignored_module_names.insert(STR("AutomationUtils")); - this->m_ignored_module_names.insert(STR("AvfMediaFactory")); - this->m_ignored_module_names.insert(STR("BuildPatchServices")); - this->m_ignored_module_names.insert(STR("CableComponent")); - this->m_ignored_module_names.insert(STR("Chaos")); - this->m_ignored_module_names.insert(STR("ChaosCloth")); - this->m_ignored_module_names.insert(STR("ChaosNiagara")); - this->m_ignored_module_names.insert(STR("ChaosSolvers")); - this->m_ignored_module_names.insert(STR("ChaosSolverEngine")); - this->m_ignored_module_names.insert(STR("CinematicCamera")); - this->m_ignored_module_names.insert(STR("ClothingSystemRuntimeCommon")); - this->m_ignored_module_names.insert(STR("ClothingSystemRuntimeInterface")); - this->m_ignored_module_names.insert(STR("ClothingSystemRuntimeNv")); - this->m_ignored_module_names.insert(STR("CustomMeshComponent")); - this->m_ignored_module_names.insert(STR("DatasmithContent")); - this->m_ignored_module_names.insert(STR("DeveloperSettings")); - this->m_ignored_module_names.insert(STR("EditableMesh")); - this->m_ignored_module_names.insert(STR("EngineMessages")); - this->m_ignored_module_names.insert(STR("EngineSettings")); - this->m_ignored_module_names.insert(STR("EyeTracker")); - this->m_ignored_module_names.insert(STR("FacialAnimation")); - this->m_ignored_module_names.insert(STR("FieldSystemCore")); - this->m_ignored_module_names.insert(STR("FieldSystemEngine")); - this->m_ignored_module_names.insert(STR("Foliage")); - this->m_ignored_module_names.insert(STR("GameplayTags")); - this->m_ignored_module_names.insert(STR("GameplayTasks")); - this->m_ignored_module_names.insert(STR("GeometryCache")); - this->m_ignored_module_names.insert(STR("GeometryCacheTracks")); - this->m_ignored_module_names.insert(STR("GeometryCollectionCore")); - this->m_ignored_module_names.insert(STR("GeometryCollectionSimulationCore")); - this->m_ignored_module_names.insert(STR("GeometryCollectionEngine")); - this->m_ignored_module_names.insert(STR("GeometryCollectionTracks")); - this->m_ignored_module_names.insert(STR("GooglePAD")); - this->m_ignored_module_names.insert(STR("HeadMountedDisplay")); - this->m_ignored_module_names.insert(STR("ImageWrapper")); - this->m_ignored_module_names.insert(STR("ImageWriteQueue")); - this->m_ignored_module_names.insert(STR("ImgMedia")); - this->m_ignored_module_names.insert(STR("ImgMediaFactory")); - this->m_ignored_module_names.insert(STR("InputCore")); - this->m_ignored_module_names.insert(STR("InteractiveToolsFramework")); - this->m_ignored_module_names.insert(STR("JsonUtilities")); - this->m_ignored_module_names.insert(STR("Landscape")); - this->m_ignored_module_names.insert(STR("LevelSequence")); - this->m_ignored_module_names.insert(STR("LightPropagationVolumeRuntime")); - this->m_ignored_module_names.insert(STR("LiveLinkInterface")); - this->m_ignored_module_names.insert(STR("LocationServicesBPLibrary")); - this->m_ignored_module_names.insert(STR("LuminRuntimeSettings")); - this->m_ignored_module_names.insert(STR("MagicLeap")); - this->m_ignored_module_names.insert(STR("MagicLeapAR")); - this->m_ignored_module_names.insert(STR("MagicLeapARPin")); - this->m_ignored_module_names.insert(STR("MagicLeapAudio")); - this->m_ignored_module_names.insert(STR("MagicLeapController")); - this->m_ignored_module_names.insert(STR("MagicLeapEyeTracker")); - this->m_ignored_module_names.insert(STR("MagicLeapHandMeshing")); - this->m_ignored_module_names.insert(STR("MagicLeapHandTracking")); - this->m_ignored_module_names.insert(STR("MagicLeapIdentity")); - this->m_ignored_module_names.insert(STR("MagicLeapImageTracker")); - this->m_ignored_module_names.insert(STR("MagicLeapLightEstimation")); - this->m_ignored_module_names.insert(STR("MagicLeapPlanes")); - this->m_ignored_module_names.insert(STR("MagicLeapPrivileges")); - this->m_ignored_module_names.insert(STR("MagicLeapSecureStorage")); - this->m_ignored_module_names.insert(STR("MagicLeapSharedWorld")); - this->m_ignored_module_names.insert(STR("MaterialShaderQualitySettings")); - this->m_ignored_module_names.insert(STR("MediaAssets")); - this->m_ignored_module_names.insert(STR("MediaCompositing")); - this->m_ignored_module_names.insert(STR("MediaUtils")); - this->m_ignored_module_names.insert(STR("MeshDescription")); - this->m_ignored_module_names.insert(STR("MobilePatchingUtils")); - this->m_ignored_module_names.insert(STR("MotoSynth")); - this->m_ignored_module_names.insert(STR("MoviePlayer")); - this->m_ignored_module_names.insert(STR("MovieScene")); - this->m_ignored_module_names.insert(STR("MovieSceneCapture")); - this->m_ignored_module_names.insert(STR("MovieSceneTracks")); - this->m_ignored_module_names.insert(STR("MRMesh")); - this->m_ignored_module_names.insert(STR("NavigationSystem")); - this->m_ignored_module_names.insert(STR("NetCore")); - this->m_ignored_module_names.insert(STR("Niagara")); - this->m_ignored_module_names.insert(STR("NiagaraAnimNotifies")); - this->m_ignored_module_names.insert(STR("NiagaraCore")); - this->m_ignored_module_names.insert(STR("NiagaraShader")); - this->m_ignored_module_names.insert(STR("OculusHMD")); - this->m_ignored_module_names.insert(STR("OculusInput")); - this->m_ignored_module_names.insert(STR("OculusMR")); - this->m_ignored_module_names.insert(STR("OnlineSubsystem")); - this->m_ignored_module_names.insert(STR("OnlineSubsystemUtils")); - this->m_ignored_module_names.insert(STR("Overlay")); - this->m_ignored_module_names.insert(STR("PacketHandler")); - this->m_ignored_module_names.insert(STR("Paper2D")); - this->m_ignored_module_names.insert(STR("PhysicsCore")); - this->m_ignored_module_names.insert(STR("PhysXVehicles")); - this->m_ignored_module_names.insert(STR("ProceduralMeshComponent")); - this->m_ignored_module_names.insert(STR("PropertyAccess")); - this->m_ignored_module_names.insert(STR("PropertyPath")); - this->m_ignored_module_names.insert(STR("Renderer")); - this->m_ignored_module_names.insert(STR("Serialization")); - this->m_ignored_module_names.insert(STR("SessionMessages")); - this->m_ignored_module_names.insert(STR("SignificanceManager")); - this->m_ignored_module_names.insert(STR("Slate")); - this->m_ignored_module_names.insert(STR("SlateCore")); - this->m_ignored_module_names.insert(STR("SoundFields")); - this->m_ignored_module_names.insert(STR("StaticMeshDescription")); - this->m_ignored_module_names.insert(STR("SteamVR")); - this->m_ignored_module_names.insert(STR("SteamVRInputDevice")); - this->m_ignored_module_names.insert(STR("Synthesis")); - this->m_ignored_module_names.insert(STR("TcpMessaging")); - this->m_ignored_module_names.insert(STR("TemplateSequence")); - this->m_ignored_module_names.insert(STR("TimeManagement")); - this->m_ignored_module_names.insert(STR("UdpMessaging")); - this->m_ignored_module_names.insert(STR("UMG")); - this->m_ignored_module_names.insert(STR("UObjectPlugin")); - this->m_ignored_module_names.insert(STR("VariantManagerContent")); - this->m_ignored_module_names.insert(STR("VectorVM")); - this->m_ignored_module_names.insert(STR("WmfMediaFactory")); + this->m_ignored_module_names.insert(SYSSTR("ActorLayerUtilities")); + this->m_ignored_module_names.insert(SYSSTR("ActorSequence")); + this->m_ignored_module_names.insert(SYSSTR("AIModule")); + this->m_ignored_module_names.insert(SYSSTR("AndroidPermission")); + this->m_ignored_module_names.insert(SYSSTR("AnimationCore")); + this->m_ignored_module_names.insert(SYSSTR("AnimationSharing")); + this->m_ignored_module_names.insert(SYSSTR("AnimGraphRuntime")); + this->m_ignored_module_names.insert(SYSSTR("AppleImageUtils")); + this->m_ignored_module_names.insert(SYSSTR("ArchVisCharacter")); + this->m_ignored_module_names.insert(SYSSTR("AssetRegistry")); + this->m_ignored_module_names.insert(SYSSTR("AssetTags")); + this->m_ignored_module_names.insert(SYSSTR("AudioAnalyzer")); + this->m_ignored_module_names.insert(SYSSTR("AudioCapture")); + this->m_ignored_module_names.insert(SYSSTR("AudioExtensions")); + this->m_ignored_module_names.insert(SYSSTR("AudioMixer")); + this->m_ignored_module_names.insert(SYSSTR("AudioPlatformConfiguration")); + this->m_ignored_module_names.insert(SYSSTR("AudioSynesthesia")); + this->m_ignored_module_names.insert(SYSSTR("AugmentedReality")); + this->m_ignored_module_names.insert(SYSSTR("AutomationUtils")); + this->m_ignored_module_names.insert(SYSSTR("AvfMediaFactory")); + this->m_ignored_module_names.insert(SYSSTR("BuildPatchServices")); + this->m_ignored_module_names.insert(SYSSTR("CableComponent")); + this->m_ignored_module_names.insert(SYSSTR("Chaos")); + this->m_ignored_module_names.insert(SYSSTR("ChaosCloth")); + this->m_ignored_module_names.insert(SYSSTR("ChaosNiagara")); + this->m_ignored_module_names.insert(SYSSTR("ChaosSolvers")); + this->m_ignored_module_names.insert(SYSSTR("ChaosSolverEngine")); + this->m_ignored_module_names.insert(SYSSTR("CinematicCamera")); + this->m_ignored_module_names.insert(SYSSTR("ClothingSystemRuntimeCommon")); + this->m_ignored_module_names.insert(SYSSTR("ClothingSystemRuntimeInterface")); + this->m_ignored_module_names.insert(SYSSTR("ClothingSystemRuntimeNv")); + this->m_ignored_module_names.insert(SYSSTR("CustomMeshComponent")); + this->m_ignored_module_names.insert(SYSSTR("DatasmithContent")); + this->m_ignored_module_names.insert(SYSSTR("DeveloperSettings")); + this->m_ignored_module_names.insert(SYSSTR("EditableMesh")); + this->m_ignored_module_names.insert(SYSSTR("EngineMessages")); + this->m_ignored_module_names.insert(SYSSTR("EngineSettings")); + this->m_ignored_module_names.insert(SYSSTR("EyeTracker")); + this->m_ignored_module_names.insert(SYSSTR("FacialAnimation")); + this->m_ignored_module_names.insert(SYSSTR("FieldSystemCore")); + this->m_ignored_module_names.insert(SYSSTR("FieldSystemEngine")); + this->m_ignored_module_names.insert(SYSSTR("Foliage")); + this->m_ignored_module_names.insert(SYSSTR("GameplayTags")); + this->m_ignored_module_names.insert(SYSSTR("GameplayTasks")); + this->m_ignored_module_names.insert(SYSSTR("GeometryCache")); + this->m_ignored_module_names.insert(SYSSTR("GeometryCacheTracks")); + this->m_ignored_module_names.insert(SYSSTR("GeometryCollectionCore")); + this->m_ignored_module_names.insert(SYSSTR("GeometryCollectionSimulationCore")); + this->m_ignored_module_names.insert(SYSSTR("GeometryCollectionEngine")); + this->m_ignored_module_names.insert(SYSSTR("GeometryCollectionTracks")); + this->m_ignored_module_names.insert(SYSSTR("GooglePAD")); + this->m_ignored_module_names.insert(SYSSTR("HeadMountedDisplay")); + this->m_ignored_module_names.insert(SYSSTR("ImageWrapper")); + this->m_ignored_module_names.insert(SYSSTR("ImageWriteQueue")); + this->m_ignored_module_names.insert(SYSSTR("ImgMedia")); + this->m_ignored_module_names.insert(SYSSTR("ImgMediaFactory")); + this->m_ignored_module_names.insert(SYSSTR("InputCore")); + this->m_ignored_module_names.insert(SYSSTR("InteractiveToolsFramework")); + this->m_ignored_module_names.insert(SYSSTR("JsonUtilities")); + this->m_ignored_module_names.insert(SYSSTR("Landscape")); + this->m_ignored_module_names.insert(SYSSTR("LevelSequence")); + this->m_ignored_module_names.insert(SYSSTR("LightPropagationVolumeRuntime")); + this->m_ignored_module_names.insert(SYSSTR("LiveLinkInterface")); + this->m_ignored_module_names.insert(SYSSTR("LocationServicesBPLibrary")); + this->m_ignored_module_names.insert(SYSSTR("LuminRuntimeSettings")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeap")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapAR")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapARPin")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapAudio")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapController")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapEyeTracker")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapHandMeshing")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapHandTracking")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapIdentity")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapImageTracker")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapLightEstimation")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapPlanes")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapPrivileges")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapSecureStorage")); + this->m_ignored_module_names.insert(SYSSTR("MagicLeapSharedWorld")); + this->m_ignored_module_names.insert(SYSSTR("MaterialShaderQualitySettings")); + this->m_ignored_module_names.insert(SYSSTR("MediaAssets")); + this->m_ignored_module_names.insert(SYSSTR("MediaCompositing")); + this->m_ignored_module_names.insert(SYSSTR("MediaUtils")); + this->m_ignored_module_names.insert(SYSSTR("MeshDescription")); + this->m_ignored_module_names.insert(SYSSTR("MobilePatchingUtils")); + this->m_ignored_module_names.insert(SYSSTR("MotoSynth")); + this->m_ignored_module_names.insert(SYSSTR("MoviePlayer")); + this->m_ignored_module_names.insert(SYSSTR("MovieScene")); + this->m_ignored_module_names.insert(SYSSTR("MovieSceneCapture")); + this->m_ignored_module_names.insert(SYSSTR("MovieSceneTracks")); + this->m_ignored_module_names.insert(SYSSTR("MRMesh")); + this->m_ignored_module_names.insert(SYSSTR("NavigationSystem")); + this->m_ignored_module_names.insert(SYSSTR("NetCore")); + this->m_ignored_module_names.insert(SYSSTR("Niagara")); + this->m_ignored_module_names.insert(SYSSTR("NiagaraAnimNotifies")); + this->m_ignored_module_names.insert(SYSSTR("NiagaraCore")); + this->m_ignored_module_names.insert(SYSSTR("NiagaraShader")); + this->m_ignored_module_names.insert(SYSSTR("OculusHMD")); + this->m_ignored_module_names.insert(SYSSTR("OculusInput")); + this->m_ignored_module_names.insert(SYSSTR("OculusMR")); + this->m_ignored_module_names.insert(SYSSTR("OnlineSubsystem")); + this->m_ignored_module_names.insert(SYSSTR("OnlineSubsystemUtils")); + this->m_ignored_module_names.insert(SYSSTR("Overlay")); + this->m_ignored_module_names.insert(SYSSTR("PacketHandler")); + this->m_ignored_module_names.insert(SYSSTR("Paper2D")); + this->m_ignored_module_names.insert(SYSSTR("PhysicsCore")); + this->m_ignored_module_names.insert(SYSSTR("PhysXVehicles")); + this->m_ignored_module_names.insert(SYSSTR("ProceduralMeshComponent")); + this->m_ignored_module_names.insert(SYSSTR("PropertyAccess")); + this->m_ignored_module_names.insert(SYSSTR("PropertyPath")); + this->m_ignored_module_names.insert(SYSSTR("Renderer")); + this->m_ignored_module_names.insert(SYSSTR("Serialization")); + this->m_ignored_module_names.insert(SYSSTR("SessionMessages")); + this->m_ignored_module_names.insert(SYSSTR("SignificanceManager")); + this->m_ignored_module_names.insert(SYSSTR("Slate")); + this->m_ignored_module_names.insert(SYSSTR("SlateCore")); + this->m_ignored_module_names.insert(SYSSTR("SoundFields")); + this->m_ignored_module_names.insert(SYSSTR("StaticMeshDescription")); + this->m_ignored_module_names.insert(SYSSTR("SteamVR")); + this->m_ignored_module_names.insert(SYSSTR("SteamVRInputDevice")); + this->m_ignored_module_names.insert(SYSSTR("Synthesis")); + this->m_ignored_module_names.insert(SYSSTR("TcpMessaging")); + this->m_ignored_module_names.insert(SYSSTR("TemplateSequence")); + this->m_ignored_module_names.insert(SYSSTR("TimeManagement")); + this->m_ignored_module_names.insert(SYSSTR("UdpMessaging")); + this->m_ignored_module_names.insert(SYSSTR("UMG")); + this->m_ignored_module_names.insert(SYSSTR("UObjectPlugin")); + this->m_ignored_module_names.insert(SYSSTR("VariantManagerContent")); + this->m_ignored_module_names.insert(SYSSTR("VectorVM")); + this->m_ignored_module_names.insert(SYSSTR("WmfMediaFactory")); } } @@ -3655,20 +3685,20 @@ namespace RC::UEGenerator { ignore_selected_modules(); - Output::send(STR("Cleaning up previously generated SDK (if one exists)\n")); + Output::send(SYSSTR("Cleaning up previously generated SDK (if one exists)\n")); if (std::filesystem::exists(m_root_directory)) { std::filesystem::remove_all(m_root_directory); } - Output::send(STR("Initializing native packages dump\n")); + Output::send(SYSSTR("Initializing native packages dump\n")); std::vector native_classes_to_dump; std::vector native_structs_to_dump; std::vector native_enums_to_dump; std::vector native_delegates_to_dump; - Output::send(STR("Gathering native objects for dumping\n")); + Output::send(SYSSTR("Gathering native objects for dumping\n")); UObjectGlobals::ForEachUObject([&](void* raw_object, int32_t chunk_index, int32_t object_index) { UObject* typed_object = static_cast(raw_object); @@ -3713,37 +3743,37 @@ namespace RC::UEGenerator return RC::LoopAction::Continue; }); - Output::send(STR("Attempting to dump {} native classes\n"), native_classes_to_dump.size()); + Output::send(SYSSTR("Attempting to dump {} native classes\n"), native_classes_to_dump.size()); for (UFunction* delegate_signature_function : native_delegates_to_dump) { - // Output::send(STR("Dumping native delegate type {}\n"), global_delegate_signature->GetName()); + // Output::send(SYSSTR("Dumping native delegate type {}\n"), global_delegate_signature->GetName()); generate_object_description_file(delegate_signature_function); } for (UClass* class_to_dump : native_classes_to_dump) { - // Output::send(STR("Dumping native class {}\n"), class_to_dump->GetName()); + // Output::send(SYSSTR("Dumping native class {}\n"), class_to_dump->GetName()); generate_object_description_file(class_to_dump); } - Output::send(STR("Attempting to dump {} native structs\n"), native_structs_to_dump.size()); + Output::send(SYSSTR("Attempting to dump {} native structs\n"), native_structs_to_dump.size()); for (UScriptStruct* struct_to_dump : native_structs_to_dump) { - // Output::send(STR("Dumping native struct {}\n"), struct_to_dump->GetName()); + // Output::send(SYSSTR("Dumping native struct {}\n"), struct_to_dump->GetName()); generate_object_description_file(struct_to_dump); } - Output::send(STR("Attempting to dump {} native enums\n"), native_enums_to_dump.size()); + Output::send(SYSSTR("Attempting to dump {} native enums\n"), native_enums_to_dump.size()); for (UEnum* enum_to_dump : native_enums_to_dump) { - // Output::send(STR("Dumping native enum {}\n"), enum_to_dump->GetName()); + // Output::send(SYSSTR("Dumping native enum {}\n"), enum_to_dump->GetName()); generate_object_description_file(enum_to_dump); } - Output::send(STR("Writing stub module build files for {} modules\n"), m_module_dependencies.size()); + Output::send(SYSSTR("Writing stub module build files for {} modules\n"), m_module_dependencies.size()); for (const auto& module_pair : m_module_dependencies) { generate_module_implementation_file(module_pair.first); @@ -3758,7 +3788,7 @@ namespace RC::UEGenerator bool is_class = object->IsA(); if ((is_struct || is_class) && m_structs_that_need_get_type_hash.find(std::bit_cast(object)) != m_structs_that_need_get_type_hash.end()) { - File::StringType name{}; + SystemStringType name{}; if (is_class) { name = get_native_class_name(std::bit_cast(object)); @@ -3767,7 +3797,7 @@ namespace RC::UEGenerator { name = get_native_struct_name(std::bit_cast(object)); } - header_file.append_line(std::format(STR("FORCEINLINE uint32 GetTypeHash(const {}) {{ return 0; }}"), name)); + header_file.append_line(std::format(SYSSTR("FORCEINLINE uint32 GetTypeHash(const {}) {{ return 0; }}"), name)); } // Case for FTickFunction struct @@ -3777,32 +3807,32 @@ namespace RC::UEGenerator auto super_struct = struct_object->GetSuperScriptStruct(); if (super_struct) { - if (get_native_struct_name(super_struct) == STR("FTickFunction")) + if (get_native_struct_name(super_struct) == SYSSTR("FTickFunction")) { - File::StringType name{}; + SystemStringType name{}; name = get_native_struct_name(struct_object); - header_file.append_line(STR("")); - header_file.append_line(STR("template<>")); - header_file.append_line(std::format(STR("struct TStructOpsTypeTraits<{}> : public TStructOpsTypeTraitsBase2<{}>"), name, name)); - header_file.append_line(STR("{")); - header_file.append_line(STR(" enum")); - header_file.append_line(STR(" {")); - header_file.append_line(STR(" WithCopy = false")); - header_file.append_line(STR(" };")); - header_file.append_line(STR("};")); + header_file.append_line(SYSSTR("")); + header_file.append_line(SYSSTR("template<>")); + header_file.append_line(std::format(SYSSTR("struct TStructOpsTypeTraits<{}> : public TStructOpsTypeTraitsBase2<{}>"), name, name)); + header_file.append_line(SYSSTR("{")); + header_file.append_line(SYSSTR(" enum")); + header_file.append_line(SYSSTR(" {")); + header_file.append_line(SYSSTR(" WithCopy = false")); + header_file.append_line(SYSSTR(" };")); + header_file.append_line(SYSSTR("};")); } } } header_file.serialize_file_content_to_disk(); } - Output::send(STR("Done!\n")); + Output::send(SYSSTR("Done!\n")); } auto UEHeaderGenerator::generate_object_description_file(UObject* object) -> bool { - const std::wstring module_name = get_module_name_for_package(object->GetOutermost()); - const std::wstring file_base_name = get_header_name_for_object(object); + const SystemStringType module_name = get_module_name_for_package(object->GetOutermost()); + const SystemStringType file_base_name = get_header_name_for_object(object); if (module_name.empty()) { @@ -3843,24 +3873,23 @@ namespace RC::UEGenerator { if (!is_delegate_signature_function(function)) { - throw std::runtime_error(RC::fmt("Function %S is not a delegate signature function", function->GetName().c_str())); + throw std::runtime_error(RC::fmt("Function {} is not a delegate signature function", function->GetName())); } if (!function->GetOuterPrivate()->IsA()) { - throw std::runtime_error(RC::fmt("Delegate Signature Function %S does not have a UPackage as it's owner", function->GetName().c_str())); + throw std::runtime_error(RC::fmt("Delegate Signature Function {} does not have a UPackage as it's owner", function->GetName())); } generate_global_delegate_declaration(function, NULL, header_file); } else { - throw std::runtime_error( - RC::fmt("Provided object %S is not of a supported type: %S", object->GetName().c_str(), object->GetClassPrivate()->GetName().c_str())); + throw std::runtime_error(RC::fmt("Provided object {} is not of a supported type: {}", object->GetName(), object->GetClassPrivate()->GetName())); } auto iterator = this->m_module_dependencies.find(module_name); if (iterator == this->m_module_dependencies.end()) { - iterator = this->m_module_dependencies.insert({module_name, std::make_shared>()}).first; + iterator = this->m_module_dependencies.insert({module_name, std::make_shared>()}).first; } if (!header_file.has_content_to_save()) @@ -3875,16 +3904,16 @@ namespace RC::UEGenerator header_file.generate_file_contents(); // Record module names used in the headers - std::shared_ptr> out_dependency_module_names = iterator->second; + std::shared_ptr> out_dependency_module_names = iterator->second; header_file.copy_dependency_module_names(*out_dependency_module_names); implementation_file.copy_dependency_module_names(*out_dependency_module_names); return true; } - auto UEHeaderGenerator::generate_object_pre_declaration(UObject* object) -> std::vector> + auto UEHeaderGenerator::generate_object_pre_declaration(UObject* object) -> std::vector> { - std::vector> pre_declarations; + std::vector> pre_declarations; UClass* object_class = object->GetClassPrivate(); @@ -3894,15 +3923,15 @@ namespace RC::UEGenerator if (uclass->IsChildOf(UInterface::StaticClass())) { - pre_declarations.push_back({STR("class "), get_native_class_name(uclass, true), STR(";\n")}); + pre_declarations.push_back({SYSSTR("class "), get_native_class_name(uclass, true), SYSSTR(";\n")}); } - pre_declarations.push_back({STR("class "), get_native_class_name(uclass, false), STR(";\n")}); + pre_declarations.push_back({SYSSTR("class "), get_native_class_name(uclass, false), SYSSTR(";\n")}); } else if (object_class->IsChildOf(UScriptStruct::StaticClass())) { UScriptStruct* script_struct = static_cast(object); - pre_declarations.push_back({STR("struct "), get_native_struct_name(script_struct), STR(";\n")}); + pre_declarations.push_back({SYSSTR("struct "), get_native_struct_name(script_struct), SYSSTR(";\n")}); } else if (object_class->IsChildOf(UEnum::StaticClass())) { @@ -3917,22 +3946,22 @@ namespace RC::UEGenerator return pre_declarations; } - auto UEHeaderGenerator::get_header_name_for_object(UObject* object, bool get_existing_header) -> std::wstring + auto UEHeaderGenerator::get_header_name_for_object(UObject* object, bool get_existing_header) -> SystemStringType { - File::StringType header_name{}; + SystemStringType header_name{}; UObject* final_object{}; if (object->IsA() || object->IsA()) { // Class and struct headers follow the relevant object name - header_name = object->GetName(); + header_name = to_system(object->GetName()); final_object = object; } else if (object->IsA()) { // Enumeration usually have the E prefix which will be present in the header names // We do not strip it because there are some broken headers that do not follow that convention (e.g. funny Wwise) - header_name = object->GetName(); + header_name = to_system(object->GetName()); final_object = object; } else @@ -3952,8 +3981,8 @@ namespace RC::UEGenerator { // Otherwise, remove the postfix and use the function name as the header name // Also append the delegate postfix because apparently there can be conflicts - std::wstring DelegateName = strip_delegate_signature_postfix(signature_function); - DelegateName.append(STR("Delegate")); + SystemStringType DelegateName = strip_delegate_signature_postfix(signature_function); + DelegateName.append(SYSSTR("Delegate")); header_name = DelegateName; final_object = object; } @@ -3963,21 +3992,21 @@ namespace RC::UEGenerator if (header_name.empty()) { // Unsupported dependency object type - throw std::runtime_error(RC::fmt("Unsupported dependency object type %S: %S", object->GetClassPrivate()->GetName().c_str(), object->GetName().c_str())); + throw std::runtime_error(RC::fmt("Unsupported dependency object type {}: {}", object->GetClassPrivate()->GetName(), object->GetName())); } if (get_existing_header) { if (auto it2 = m_dependency_object_to_unique_id.find(final_object); it2 != m_dependency_object_to_unique_id.end()) { - header_name.append(std::format(STR("{}"), it2->second)); + header_name.append(std::format(SYSSTR("{}"), it2->second)); } } else { if (auto it = m_used_file_names.find(header_name); it != m_used_file_names.end()) { - header_name.append(std::format(STR("{}"), ++it->second.usable_id)); + header_name.append(std::format(SYSSTR("{}"), ++it->second.usable_id)); m_dependency_object_to_unique_id.emplace(final_object, it->second.usable_id); } else @@ -3994,26 +4023,30 @@ namespace RC::UEGenerator generate_delegate_type_declaration(signature_function, delegate_class, header_data); } - auto UEHeaderGenerator::determine_primary_game_module_name() -> std::wstring + auto UEHeaderGenerator::determine_primary_game_module_name() -> SystemStringType { + /* HMODULE primary_executable_module = GetModuleHandleW(NULL); wchar_t module_name_buffer[1024]{'\0'}; GetModuleFileNameW(primary_executable_module, module_name_buffer, ARRAYSIZE(module_name_buffer)); // Retrieve the filename from the full path, strip down the extension - FFilePath root_executable_path((std::wstring(module_name_buffer))); - std::wstring filename = root_executable_path.filename().replace_extension().wstring(); + FFilePath root_executable_path((SystemStringType(module_name_buffer))); + SystemStringType filename = root_executable_path.filename().replace_extension().wstring(); // Remove the shipping file postfix - std::wstring shipping_postfix = STR("-Win64-Shipping"); + SystemStringType shipping_postfix = SYSSTR("-Win64-Shipping"); if (filename.ends_with(shipping_postfix)) { filename.erase(filename.length() - shipping_postfix.length()); } return filename; + */ + return SYSSTR("Main"); } - auto UEHeaderGenerator::generate_cross_module_include(UObject* object, const std::wstring& module_name, const std::wstring& fallback_name) -> std::wstring + auto UEHeaderGenerator::generate_cross_module_include(UObject* object, const SystemStringType& module_name, const SystemStringType& fallback_name) + -> SystemStringType { // Retrieve the most top level object located inside the native package UObject* top_level_object = object; @@ -4023,39 +4056,43 @@ namespace RC::UEGenerator top_level_object = top_level_object->GetOuterPrivate(); } - const std::wstring object_name = top_level_object->GetName(); - return std::format(STR("//CROSS-MODULE INCLUDE V2: -ModuleName={} -ObjectName={} -FallbackName={}\n"), module_name, object_name, fallback_name); + const auto object_name = to_system(top_level_object->GetName()); + return std::format(SYSSTR("//CROSS-MODULE INCLUDE V2: -ModuleName={} -ObjectName={} -FallbackName={}\n"), + to_system(module_name), + object_name, + to_system(fallback_name)); } - GeneratedFile::GeneratedFile(const FFilePath& full_file_path) + UEGeneratedFile::UEGeneratedFile(const FFilePath& full_file_path) { this->m_full_file_path = full_file_path; - this->m_file_base_name = full_file_path.filename().replace_extension().wstring(); + // [[keep]] + this->m_file_base_name = to_system_string(full_file_path.filename().replace_extension()); this->m_current_indent_count = 0; } - auto GeneratedFile::append_line(const std::wstring& line) -> void + auto UEGeneratedFile::append_line(const SystemStringType& line) -> void { for (int32_t i = 0; i < m_current_indent_count; i++) { - m_file_contents_buffer.append(STR(" ")); + m_file_contents_buffer.append(SYSSTR(" ")); } m_file_contents_buffer.append(line); - m_file_contents_buffer.append(STR("\n")); + m_file_contents_buffer.append(SYSSTR("\n")); } - auto GeneratedFile::append_line_no_indent(const std::wstring& line) -> void + auto UEGeneratedFile::append_line_no_indent(const SystemStringType& line) -> void { m_file_contents_buffer.append(line); - m_file_contents_buffer.append(STR("\n")); + m_file_contents_buffer.append(SYSSTR("\n")); } - auto GeneratedFile::begin_indent_level() -> void + auto UEGeneratedFile::begin_indent_level() -> void { m_current_indent_count++; } - auto GeneratedFile::end_indent_level() -> void + auto UEGeneratedFile::end_indent_level() -> void { m_current_indent_count--; if (m_current_indent_count < 0) @@ -4064,7 +4101,7 @@ namespace RC::UEGenerator } } - auto GeneratedFile::serialize_file_content_to_disk() -> bool + auto UEGeneratedFile::serialize_file_content_to_disk() -> bool { if (!has_content_to_save()) { @@ -4072,48 +4109,51 @@ namespace RC::UEGenerator } // TODO might be slow, maybe move it out into the header generator? std::filesystem::create_directories(this->m_full_file_path.parent_path()); - - std::wofstream file_output_stream; + auto file = File::open(m_full_file_path, File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); + file.write_file_string_to_file(to_file(generate_file_contents())); + /* + UEOStreamType file_output_stream; file_output_stream.open(m_full_file_path); if (!file_output_stream.is_open()) { throw std::invalid_argument("Failed to open the header file"); } - file_output_stream << generate_file_contents(); - file_output_stream.close(); + // TODO: FIX STREAM + file_output_stream << generate_file_contents().c_str(); + file_output_stream.close();*/ return true; } - auto GeneratedFile::generate_file_contents() -> std::wstring + auto UEGeneratedFile::generate_file_contents() -> SystemStringType { return m_file_contents_buffer; } - auto GeneratedFile::has_content_to_save() const -> bool + auto UEGeneratedFile::has_content_to_save() const -> bool { return !m_file_contents_buffer.empty(); } auto GeneratedSourceFile::create_source_file(const FFilePath& root_dir, - const std::wstring& module_name, - const std::wstring& base_name, + const SystemStringType& module_name, + const SystemStringType& base_name, bool is_implementation_file, UObject* object) -> GeneratedSourceFile { FFilePath full_file_path; if (is_implementation_file) { - full_file_path = root_dir / module_name / STR("Private") / (base_name + STR(".cpp")); + full_file_path = root_dir / module_name / SYSSTR("Private") / (base_name + SYSSTR(".cpp")); } else { - full_file_path = root_dir / module_name / STR("Public") / (base_name + STR(".h")); + full_file_path = root_dir / module_name / SYSSTR("Public") / (base_name + SYSSTR(".h")); } return GeneratedSourceFile(full_file_path, module_name, is_implementation_file, object); } - GeneratedSourceFile::GeneratedSourceFile(const FFilePath& file_path, const std::wstring& file_module_name, bool is_implementation_file, UObject* object) - : GeneratedFile(file_path) + GeneratedSourceFile::GeneratedSourceFile(const FFilePath& file_path, const SystemStringType& file_module_name, bool is_implementation_file, UObject* object) + : UEGeneratedFile(file_path) { this->m_file_module_name = file_module_name; this->m_is_implementation_file = is_implementation_file; @@ -4125,7 +4165,7 @@ namespace RC::UEGenerator this->m_header_file = header_file; } - auto GeneratedSourceFile::add_extra_include(const std::wstring& included_file_name) -> void + auto GeneratedSourceFile::add_extra_include(const SystemStringType& included_file_name) -> void { this->m_extra_includes.insert(included_file_name); } @@ -4141,44 +4181,44 @@ namespace RC::UEGenerator } } - auto GeneratedSourceFile::generate_file_contents() -> std::wstring + auto GeneratedSourceFile::generate_file_contents() -> SystemStringType { - std::wstring result_header_contents; + SystemStringType result_header_contents; result_header_contents.append(generate_includes_string()); - result_header_contents.append(STR("\n")); + result_header_contents.append(SYSSTR("\n")); - std::wstring pre_declarations_string = generate_pre_declarations_string(); + SystemStringType pre_declarations_string = generate_pre_declarations_string(); if (!pre_declarations_string.empty()) { result_header_contents.append(pre_declarations_string); - result_header_contents.append(STR("\n")); + result_header_contents.append(SYSSTR("\n")); } if (!m_implementation_constructor.empty()) { result_header_contents.append(m_implementation_constructor); - result_header_contents.append(STR("\n")); + result_header_contents.append(SYSSTR("\n")); } if (!m_file_contents_buffer.empty()) { result_header_contents.append(m_file_contents_buffer); - result_header_contents.append(STR("\n")); + result_header_contents.append(SYSSTR("\n")); } return result_header_contents; } - auto GeneratedSourceFile::generate_includes_string() const -> std::wstring + auto GeneratedSourceFile::generate_includes_string() const -> SystemStringType { - std::wstring result_include_string; - std::vector> include_lines; - std::vector cross_module_includes; + SystemStringType result_include_string; + std::vector> include_lines; + std::vector cross_module_includes; // For the header file, we generate the pragma and minimal core includes if (!m_is_implementation_file) { - result_include_string.append(STR("#pragma once\n")); - result_include_string.append(STR("#include \"CoreMinimal.h\"\n")); + result_include_string.append(SYSSTR("#pragma once\n")); + result_include_string.append(SYSSTR("#include \"CoreMinimal.h\"\n")); } // For CPP implementation file, we need to generate the header include if (m_is_implementation_file) @@ -4186,19 +4226,19 @@ namespace RC::UEGenerator if (m_header_file != NULL) { // Generate it if we have the correct header file set - result_include_string.append(std::format(STR("#include \"{}.h\"\n"), m_header_file->m_file_base_name)); + result_include_string.append(std::format(SYSSTR("#include \"{}.h\"\n"), m_header_file->m_file_base_name)); } else { // Otherwise, we generate a simple minimal core include - result_include_string.append(STR("#include \"CoreMinimal.h\"\n")); + result_include_string.append(SYSSTR("#include \"CoreMinimal.h\"\n")); } } // Generate extra includes we might need that do not represent objects - for (const std::wstring& extra_included_file : m_extra_includes) + for (const SystemStringType& extra_included_file : m_extra_includes) { - include_lines.push_back({STR("#include \""), extra_included_file, STR("\"\n")}); + include_lines.push_back({SYSSTR("#include \""), extra_included_file, SYSSTR("\"\n")}); } // Generate includes for the relevant object files @@ -4212,7 +4252,7 @@ namespace RC::UEGenerator continue; } - const std::wstring object_header_name = UEHeaderGenerator::get_header_name_for_object(dependency_object, true); + const SystemStringType object_header_name = UEHeaderGenerator::get_header_name_for_object(dependency_object, true); // Definitely skip include if object in question is placed into this header if (object_header_name == m_file_base_name) @@ -4229,7 +4269,7 @@ namespace RC::UEGenerator } } UObject* package = dependency_object->GetOutermost(); - std::wstring native_module_name = UEHeaderGenerator::get_module_name_for_package(package); + SystemStringType native_module_name = UEHeaderGenerator::get_module_name_for_package(package); if (!native_module_name.empty()) { @@ -4237,7 +4277,7 @@ namespace RC::UEGenerator // since generated headers are always located in the module root and follow one file per object convention if (m_file_module_name == native_module_name) { - include_lines.push_back({STR("#include \""), object_header_name, STR(".h\"\n")}); + include_lines.push_back({SYSSTR("#include \""), object_header_name, SYSSTR(".h\"\n")}); } else { @@ -4254,13 +4294,13 @@ namespace RC::UEGenerator // Remove duplicates - there are sometimes multiple instances of the same cross module include cross_module_includes.erase(std::unique(cross_module_includes.begin(), cross_module_includes.end()), cross_module_includes.end()); - for (const std::wstring& cross_module_include : cross_module_includes) + for (const SystemStringType& cross_module_include : cross_module_includes) { result_include_string.append(cross_module_include); } // Sort the includes by module name, since we want to make sure that they are always in the same order - std::sort(include_lines.begin(), include_lines.end(), [](const std::vector& a, const std::vector& b) { + std::sort(include_lines.begin(), include_lines.end(), [](const std::vector& a, const std::vector& b) { return a[1] < b[1]; }); @@ -4275,15 +4315,15 @@ namespace RC::UEGenerator // Last include of the header file should always be a generated one if (!m_is_implementation_file) { - result_include_string.append(std::format(STR("#include \"{}.generated.h\"\n"), m_file_base_name)); + result_include_string.append(std::format(SYSSTR("#include \"{}.generated.h\"\n"), m_file_base_name)); } return result_include_string; } - auto GeneratedSourceFile::generate_pre_declarations_string() const -> std::wstring + auto GeneratedSourceFile::generate_pre_declarations_string() const -> SystemStringType { - std::wstring result_declarations; - std::vector>> pre_declarations; + SystemStringType result_declarations; + std::vector>> pre_declarations; // Generate pre-declarations for the relevant object files for (const auto& dependency_pair : m_dependencies) @@ -4298,7 +4338,7 @@ namespace RC::UEGenerator // We still need to reference the object's owner module UObject* package = dependency_object->GetOutermost(); - std::wstring native_module_name = UEHeaderGenerator::get_module_name_for_package(package); + SystemStringType native_module_name = UEHeaderGenerator::get_module_name_for_package(package); if (!native_module_name.empty() && m_file_module_name != native_module_name) { @@ -4311,7 +4351,7 @@ namespace RC::UEGenerator // Sort the entries alphabetically by the class name std::sort(pre_declarations.begin(), pre_declarations.end(), - [](const std::vector>& a, const std::vector>& b) { + [](const std::vector>& a, const std::vector>& b) { return a[0][1] < b[0][1]; }); diff --git a/UE4SS/src/SettingsManager.cpp b/UE4SS/src/SettingsManager.cpp index 56557f7d4..eac755d8b 100644 --- a/UE4SS/src/SettingsManager.cpp +++ b/UE4SS/src/SettingsManager.cpp @@ -5,7 +5,7 @@ #define REGISTER_STRING_SETTING(member_var, section_name, key) \ try \ { \ - (member_var) = parser.get_string(section_name, STR(#key)); \ + (member_var) = parser.get_string(section_name, SYSSTR(#key)); \ } \ catch (std::exception&) \ { \ @@ -14,7 +14,7 @@ #define REGISTER_INT64_SETTING(member_var, section_name, key) \ try \ { \ - (member_var) = parser.get_int64(section_name, STR(#key)); \ + (member_var) = parser.get_int64(section_name, SYSSTR(#key)); \ } \ catch (std::exception&) \ { \ @@ -23,7 +23,7 @@ #define REGISTER_BOOL_SETTING(member_var, section_name, key) \ try \ { \ - (member_var) = parser.get_bool(section_name, STR(#key)); \ + (member_var) = parser.get_bool(section_name, SYSSTR(#key)); \ } \ catch (std::exception&) \ { \ @@ -32,7 +32,7 @@ #define REGISTER_FLOAT_SETTING(member_var, section_name, key) \ try \ { \ - (member_var) = parser.get_float(section_name, STR(#key)); \ + (member_var) = parser.get_float(section_name, SYSSTR(#key)); \ } \ catch (std::exception&) \ { \ @@ -46,31 +46,31 @@ namespace RC Ini::Parser parser; parser.parse(file); file.close(); - - constexpr static File::CharType section_overrides[] = STR("Overrides"); + constexpr static SystemCharType section_overrides[] = SYSSTR("Overrides"); REGISTER_STRING_SETTING(Overrides.ModsFolderPath, section_overrides, ModsFolderPath) - constexpr static File::CharType section_general[] = STR("General"); + constexpr static SystemCharType section_general[] = SYSSTR("General"); REGISTER_BOOL_SETTING(General.EnableHotReloadSystem, section_general, EnableHotReloadSystem) REGISTER_BOOL_SETTING(General.UseCache, section_general, UseCache) REGISTER_BOOL_SETTING(General.InvalidateCacheIfDLLDiffers, section_general, InvalidateCacheIfDLLDiffers) REGISTER_BOOL_SETTING(General.EnableDebugKeyBindings, section_general, EnableDebugKeyBindings) REGISTER_INT64_SETTING(General.SecondsToScanBeforeGivingUp, section_general, SecondsToScanBeforeGivingUp) REGISTER_BOOL_SETTING(General.UseUObjectArrayCache, section_general, bUseUObjectArrayCache) + REGISTER_STRING_SETTING(General.InputSource, section_general, InputSource) - constexpr static File::CharType section_engine_version_override[] = STR("EngineVersionOverride"); + constexpr static SystemCharType section_engine_version_override[] = SYSSTR("EngineVersionOverride"); REGISTER_INT64_SETTING(EngineVersionOverride.MajorVersion, section_engine_version_override, MajorVersion) REGISTER_INT64_SETTING(EngineVersionOverride.MinorVersion, section_engine_version_override, MinorVersion) - constexpr static File::CharType section_object_dumper[] = STR("ObjectDumper"); + constexpr static SystemCharType section_object_dumper[] = SYSSTR("ObjectDumper"); REGISTER_BOOL_SETTING(ObjectDumper.LoadAllAssetsBeforeDumpingObjects, section_object_dumper, LoadAllAssetsBeforeDumpingObjects) - constexpr static File::CharType section_cxx_header_generator[] = STR("CXXHeaderGenerator"); + constexpr static SystemCharType section_cxx_header_generator[] = SYSSTR("CXXHeaderGenerator"); REGISTER_BOOL_SETTING(CXXHeaderGenerator.DumpOffsetsAndSizes, section_cxx_header_generator, DumpOffsetsAndSizes) REGISTER_BOOL_SETTING(CXXHeaderGenerator.KeepMemoryLayout, section_cxx_header_generator, KeepMemoryLayout) REGISTER_BOOL_SETTING(CXXHeaderGenerator.LoadAllAssetsBeforeGeneratingCXXHeaders, section_cxx_header_generator, LoadAllAssetsBeforeGeneratingCXXHeaders) - constexpr static File::CharType section_uht_header_generator[] = STR("UHTHeaderGenerator"); + constexpr static SystemCharType section_uht_header_generator[] = SYSSTR("UHTHeaderGenerator"); REGISTER_BOOL_SETTING(UHTHeaderGenerator.IgnoreAllCoreEngineModules, section_uht_header_generator, IgnoreAllCoreEngineModules) REGISTER_BOOL_SETTING(UHTHeaderGenerator.IgnoreEngineAndCoreUObject, section_uht_header_generator, IgnoreEngineAndCoreUObject) REGISTER_BOOL_SETTING(UHTHeaderGenerator.MakeAllFunctionsBlueprintCallable, section_uht_header_generator, MakeAllFunctionsBlueprintCallable) @@ -78,34 +78,54 @@ namespace RC REGISTER_BOOL_SETTING(UHTHeaderGenerator.MakeEnumClassesBlueprintType, section_uht_header_generator, MakeEnumClassesBlueprintType) REGISTER_BOOL_SETTING(UHTHeaderGenerator.MakeAllConfigsEngineConfig, section_uht_header_generator, MakeAllConfigsEngineConfig) - constexpr static File::CharType section_debug[] = STR("Debug"); + constexpr static SystemCharType section_debug[] = SYSSTR("Debug"); REGISTER_BOOL_SETTING(Debug.SimpleConsoleEnabled, section_debug, ConsoleEnabled) REGISTER_BOOL_SETTING(Debug.DebugConsoleEnabled, section_debug, GuiConsoleEnabled) REGISTER_BOOL_SETTING(Debug.DebugConsoleVisible, section_debug, GuiConsoleVisible) REGISTER_FLOAT_SETTING(Debug.DebugGUIFontScaling, section_debug, GuiConsoleFontScaling) - StringType graphics_api_string{}; + +#ifdef HAS_UI + SystemStringType graphics_api_string{}; REGISTER_STRING_SETTING(graphics_api_string, section_debug, GraphicsAPI) - if (String::iequal(graphics_api_string, STR("DX11")) || String::iequal(graphics_api_string, STR("D3D11"))) + if (false) + { + } +#ifdef HAS_D3D11 + else if (String::iequal(graphics_api_string, SYSSTR("DX11")) || String::iequal(graphics_api_string, SYSSTR("D3D11"))) { Debug.GraphicsAPI = GUI::GfxBackend::DX11; } - else if (String::iequal(graphics_api_string, STR("OpenGL"))) +#endif +#ifdef HAS_GLFW + else if (String::iequal(graphics_api_string, SYSSTR("OpenGL"))) { Debug.GraphicsAPI = GUI::GfxBackend::GLFW3_OpenGL3; } +#endif +#ifdef HAS_TUI + else if (String::iequal(graphics_api_string, SYSSTR("TUI"))) + { + Debug.GraphicsAPI = GUI::GfxBackend::TUI; + } +#endif + else + { + } +#endif + REGISTER_INT64_SETTING(Debug.LiveViewObjectsPerGroup, section_debug, LiveViewObjectsPerGroup); - constexpr static File::CharType section_crash_dump[] = STR("CrashDump"); - REGISTER_BOOL_SETTING(CrashDump.EnableDumping, section_crash_dump, EnableDumping); - REGISTER_BOOL_SETTING(CrashDump.FullMemoryDump, section_crash_dump, FullMemoryDump); + /// constexpr static SystemCharType section_crash_dump[] = SSTR("CrashDump"); + /// REGISTER_BOOL_SETTING(CrashDump.EnableDumping, section_crash_dump, EnableDumping); + /// REGISTER_BOOL_SETTING(CrashDump.FullMemoryDump, section_crash_dump, FullMemoryDump); - constexpr static File::CharType section_threads[] = STR("Threads"); + constexpr static SystemCharType section_threads[] = SYSSTR("Threads"); REGISTER_INT64_SETTING(Threads.SigScannerNumThreads, section_threads, SigScannerNumThreads) REGISTER_INT64_SETTING(Threads.SigScannerMultithreadingModuleSizeThreshold, section_threads, SigScannerMultithreadingModuleSizeThreshold) - constexpr static File::CharType section_memory[] = STR("Memory"); + constexpr static SystemCharType section_memory[] = SYSSTR("Memory"); REGISTER_INT64_SETTING(Memory.MaxMemoryUsageDuringAssetLoading, section_memory, MaxMemoryUsageDuringAssetLoading) - constexpr static File::CharType section_hooks[] = STR("Hooks"); + constexpr static SystemCharType section_hooks[] = SYSSTR("Hooks"); REGISTER_BOOL_SETTING(Hooks.HookProcessInternal, section_hooks, HookProcessInternal) REGISTER_BOOL_SETTING(Hooks.HookProcessLocalScriptFunction, section_hooks, HookProcessLocalScriptFunction) REGISTER_BOOL_SETTING(Hooks.HookLoadMap, section_hooks, HookLoadMap) @@ -116,7 +136,29 @@ namespace RC REGISTER_BOOL_SETTING(Hooks.HookAActorTick, section_hooks, HookAActorTick) REGISTER_INT64_SETTING(Hooks.FExecVTableOffsetInLocalPlayer, section_hooks, FExecVTableOffsetInLocalPlayer) - constexpr static File::CharType section_experimental_features[] = STR("ExperimentalFeatures"); + constexpr static SystemCharType section_experimental_features[] = SYSSTR("ExperimentalFeatures"); REGISTER_BOOL_SETTING(Experimental.GUIUFunctionCaller, section_experimental_features, GUIUFunctionCaller) + + constexpr static SystemCharType section_tui_features[] = SYSSTR("TUI"); + /*REGISTER_INT64_SETTING(TUI.ButtonLeft, section_tui_features, ButtonLeft) + REGISTER_INT64_SETTING(TUI.ButtonRight, section_tui_features, ButtonRight) + REGISTER_INT64_SETTING(TUI.WheelUp, section_tui_features, WheelUp) + REGISTER_INT64_SETTING(TUI.WheelDown, section_tui_features, WheelDown)*/ + REGISTER_BOOL_SETTING(TUI.TUINerdFont, section_tui_features, TUINerdFont) + REGISTER_STRING_SETTING(TUI.TERMINFO, section_tui_features, TERMINFO) + REGISTER_STRING_SETTING(TUI.LCALL, section_tui_features, LCALL) + /* + REGISTER_STRING_SETTING(TUI.TerminalCode, section_tui_features, TerminalCode) + REGISTER_STRING_SETTING(TUI.ArchiveCode, section_tui_features, ArchiveCode) + REGISTER_STRING_SETTING(TUI.SyncCode, section_tui_features, SyncCode) + REGISTER_STRING_SETTING(TUI.FileCode, section_tui_features, FileCode) + REGISTER_STRING_SETTING(TUI.EyeCode, section_tui_features, EyeCode) + REGISTER_STRING_SETTING(TUI.PuzzlePieceCode, section_tui_features, PuzzlePieceCode) + REGISTER_STRING_SETTING(TUI.AngleLeftCode, section_tui_features, AngleLeftCode) + REGISTER_STRING_SETTING(TUI.AngleRightCode, section_tui_features, AngleRightCode) + REGISTER_STRING_SETTING(TUI.BanCode, section_tui_features, BanCode) + REGISTER_STRING_SETTING(TUI.CopyCode, section_tui_features, CopyCode) + REGISTER_STRING_SETTING(TUI.SearchCode, section_tui_features, SearchCode) + */ } } // namespace RC diff --git a/UE4SS/src/Signatures.cpp b/UE4SS/src/Signatures.cpp index 147feec75..e841b64bc 100644 --- a/UE4SS/src/Signatures.cpp +++ b/UE4SS/src/Signatures.cpp @@ -18,7 +18,7 @@ namespace RC { } - auto scan_from_lua_script(std::wstring& script_file_path_and_name, + auto scan_from_lua_script(SystemStringType& script_file_path_and_name, std::vector& signature_containers, LuaScriptMatchFoundFunc& match_found_func, LuaScriptScanCompleteFunc& scan_complete_func) -> void @@ -31,14 +31,14 @@ namespace RC lua.register_function("DerefToInt32", LuaLibrary::deref_to_int32); lua.register_function("dereftoint32", LuaLibrary::deref_to_int32); - lua.execute_file(script_file_path_and_name); + lua.execute_file(to_lua(script_file_path_and_name)); constexpr const char* global_register_func_name = "Register"; constexpr const char* global_on_match_found_func_name = "OnMatchFound"; if (!lua.is_global_function(global_register_func_name) || !lua.is_global_function(global_on_match_found_func_name)) { - Output::send(STR("Lua functions 'Register' and 'OnMatchFound' must be present in {}\n"), script_file_path_and_name); + Output::send(SYSSTR("Lua functions 'Register' and 'OnMatchFound' must be present in {}\n"), script_file_path_and_name); throw std::runtime_error{"See error message above"}; } @@ -91,7 +91,7 @@ namespace RC auto setup_lua_scan_overrides(std::filesystem::path& working_directory, Unreal::UnrealInitializer::Config& config) -> void { - std::wstring lua_guobjectarray_scan_script = working_directory / "UE4SS_Signatures/GUObjectArray.lua"; + auto lua_guobjectarray_scan_script = to_system(working_directory / "UE4SS_Signatures/GUObjectArray.lua"); if (std::filesystem::exists(lua_guobjectarray_scan_script)) { config.ScanOverrides.guobjectarray = [lua_guobjectarray_scan_script](std::vector& signature_containers, @@ -100,7 +100,7 @@ namespace RC lua_guobjectarray_scan_script, signature_containers, [](void* address) { - Output::send(STR("GUObjectArray address: {} <- Lua Script\n"), address); + Output::send(SYSSTR("GUObjectArray address: {} <- Lua Script\n"), address); Unreal::UObjectArray::SetupGUObjectArrayAddress(address); return DidLuaScanSucceed::Yes; }, @@ -113,7 +113,7 @@ namespace RC }; } - std::wstring lua_fts_scan_script = working_directory / "UE4SS_Signatures/FName_ToString.lua"; + auto lua_fts_scan_script = to_system(working_directory / "UE4SS_Signatures/FName_ToString.lua"); if (std::filesystem::exists(lua_fts_scan_script)) { config.ScanOverrides.fname_to_string = [lua_fts_scan_script](std::vector& signature_containers, @@ -122,7 +122,7 @@ namespace RC lua_fts_scan_script, signature_containers, [](void* address) { - Output::send(STR("FName::ToString address: {} <- Lua Script\n"), address); + Output::send(SYSSTR("FName::ToString address: {} <- Lua Script\n"), address); Unreal::FName::ToStringInternal.assign_address(address); return DidLuaScanSucceed::Yes; }, @@ -135,7 +135,7 @@ namespace RC }; } - std::wstring lua_fnc_scan_script = working_directory / "UE4SS_Signatures/FName_Constructor.lua"; + auto lua_fnc_scan_script = to_system(working_directory / "UE4SS_Signatures/FName_Constructor.lua"); if (std::filesystem::exists(lua_fnc_scan_script)) { config.ScanOverrides.fname_constructor = [lua_fnc_scan_script](std::vector& signature_containers, @@ -144,11 +144,11 @@ namespace RC lua_fnc_scan_script, signature_containers, [&scan_result](void* address) { - Unreal::FName name = Unreal::FName(L"bCanBeDamaged", Unreal::FNAME_Find, address); + Unreal::FName name = Unreal::FName(STR("bCanBeDamaged"), Unreal::FNAME_Find, address); - if (name == L"bCanBeDamaged") + if (name == STR("bCanBeDamaged")) { - Output::send(STR("FName::FName address: {} <- Lua Script\n"), address); + Output::send(SYSSTR("FName::FName address: {} <- Lua Script\n"), address); Unreal::FName::ConstructorInternal.assign_address(address); return DidLuaScanSucceed::Yes; } @@ -168,8 +168,8 @@ namespace RC } // For compatibility, we look for 'FMemory_Free.lua' if 'GMalloc.lua' doesn't exist. - std::wstring lua_ffree_scan_script_new = working_directory / "UE4SS_Signatures/GMalloc.lua"; - std::wstring lua_ffree_scan_script_compat = working_directory / "UE4SS_Signatures/FMemory_Free.lua"; + auto lua_ffree_scan_script_new = to_system(working_directory / "UE4SS_Signatures/GMalloc.lua"); + auto lua_ffree_scan_script_compat = to_system(working_directory / "UE4SS_Signatures/FMemory_Free.lua"); auto lua_ffree_scan_script = std::filesystem::exists(lua_ffree_scan_script_new) ? lua_ffree_scan_script_new : lua_ffree_scan_script_compat; if (std::filesystem::exists(lua_ffree_scan_script)) { @@ -179,7 +179,7 @@ namespace RC lua_ffree_scan_script, signature_containers, [](void* address) { - Output::send(STR("GMalloc address: {} <- Lua Script\n"), address); + Output::send(SYSSTR("GMalloc address: {} <- Lua Script\n"), address); Unreal::FMalloc::UnrealStaticGMalloc = static_cast(address); Unreal::GMalloc = *Unreal::FMalloc::UnrealStaticGMalloc; return DidLuaScanSucceed::Yes; @@ -193,7 +193,7 @@ namespace RC }; } - std::wstring lua_sco_scan_script = working_directory / "UE4SS_Signatures/StaticConstructObject.lua"; + auto lua_sco_scan_script = to_system(working_directory / "UE4SS_Signatures/StaticConstructObject.lua"); if (std::filesystem::exists(lua_sco_scan_script)) { config.ScanOverrides.static_construct_object = [lua_sco_scan_script](std::vector& signature_containers, @@ -202,7 +202,7 @@ namespace RC lua_sco_scan_script, signature_containers, [](void* address) { - Output::send(STR("StaticConstructObject_Internal address: {} <- Lua Script\n"), address); + Output::send(SYSSTR("StaticConstructObject_Internal address: {} <- Lua Script\n"), address); Unreal::UObjectGlobals::SetupStaticConstructObjectInternalAddress(address); return DidLuaScanSucceed::Yes; }, @@ -215,7 +215,7 @@ namespace RC }; } - std::wstring lua_ftc_scan_script = working_directory / "UE4SS_Signatures/FText_Constructor.lua"; + auto lua_ftc_scan_script = to_system(working_directory / "UE4SS_Signatures/FText_Constructor.lua"); if (std::filesystem::exists(lua_ftc_scan_script)) { config.ScanOverrides.ftext_constructor = [lua_ftc_scan_script](std::vector& signature_containers, @@ -224,11 +224,11 @@ namespace RC lua_ftc_scan_script, signature_containers, [&scan_result](void* address) { - Unreal::FText text = Unreal::FText(L"bCanBeDamaged", address); + Unreal::FText text = Unreal::FText(STR("bCanBeDamaged"), address); - if (text == L"bCanBeDamaged") + if (text == STR("bCanBeDamaged")) { - Output::send(STR("FText::FText address: {} <- Lua Script\n"), address); + Output::send(SYSSTR("FText::FText address: {} <- Lua Script\n"), address); Unreal::FText::ConstructorInternal.assign_address(address); return DidLuaScanSucceed::Yes; } diff --git a/UE4SS/src/UE4SSProgram.cpp b/UE4SS/src/UE4SSProgram.cpp index e753c31d3..75bb0bfd3 100644 --- a/UE4SS/src/UE4SSProgram.cpp +++ b/UE4SS/src/UE4SSProgram.cpp @@ -1,6 +1,7 @@ +#ifdef WIN32 #define NOMINMAX - #include +#endif #ifdef TEXT #undef TEXT @@ -16,11 +17,12 @@ #include #include #include +#ifdef HAS_UI #include #include #include +#endif #include -#include #include #include #include @@ -55,7 +57,11 @@ #include #include +#ifdef WIN32 #include +#endif + +#include namespace RC { @@ -94,12 +100,12 @@ namespace RC #define OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(StructName) \ for (const auto& [name, offset] : Unreal::StructName::MemberOffsets) \ { \ - Output::send(STR(#StructName "::{} = 0x{:X}\n"), name, offset); \ + Output::send(SYSSTR(#StructName "::{} = 0x{:X}\n"), name, offset); \ } auto output_all_member_offsets() -> void { - Output::send(STR("\n##### MEMBER OFFSETS START #####\n\n")); + Output::send(SYSSTR("\n##### MEMBER OFFSETS START #####\n\n")); OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(UObjectBase); OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(UScriptStruct); OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(UScriptStruct::ICppStructOps); @@ -126,14 +132,15 @@ namespace RC OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(FSoftClassProperty); OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(FInterfaceProperty); OUTPUT_MEMBER_OFFSETS_FOR_STRUCT(FFieldPathProperty); - Output::send(STR("\n##### MEMBER OFFSETS END #####\n\n")); + Output::send(SYSSTR("\n##### MEMBER OFFSETS END #####\n\n")); } +#ifdef WIN32 void* HookedLoadLibraryA(const char* dll_name) { UE4SSProgram& program = UE4SSProgram::get_program(); HMODULE lib = PLH::FnCast(program.m_hook_trampoline_load_library_a, &LoadLibraryA)(dll_name); - program.fire_dll_load_for_cpp_mods(to_wstring(dll_name)); + program.fire_dll_load_for_cpp_mods(to_system(dll_name)); return lib; } @@ -141,7 +148,7 @@ namespace RC { UE4SSProgram& program = UE4SSProgram::get_program(); HMODULE lib = PLH::FnCast(program.m_hook_trampoline_load_library_ex_a, &LoadLibraryExA)(dll_name, file, flags); - program.fire_dll_load_for_cpp_mods(to_wstring(dll_name)); + program.fire_dll_load_for_cpp_mods(to_system(dll_name)); return lib; } @@ -160,8 +167,9 @@ namespace RC program.fire_dll_load_for_cpp_mods(dll_name); return lib; } +#endif - UE4SSProgram::UE4SSProgram(const std::wstring& moduleFilePath, std::initializer_list options) : MProgram(options) + UE4SSProgram::UE4SSProgram(const SystemStringType& moduleFilePath, std::initializer_list options) : MProgram(options) { ProfilerScope(); s_program = this; @@ -170,99 +178,114 @@ namespace RC { setup_paths(moduleFilePath); + // Setup the log file + auto& file_device = Output::set_default_devices(); + file_device.set_file_name_and_path(to_system(m_log_directory / m_log_file_name)); + + create_simple_console(); + try { settings_manager.deserialize(m_settings_path_and_file); } catch (std::exception& e) { - create_emergency_console_for_early_error(std::format(STR("The IniParser failed to parse: {}"), to_wstring(e.what()))); + set_error("The IniParser failed to parse: %s", to_system(e.what()).data()); return; } +#ifdef WIN32 if (settings_manager.CrashDump.EnableDumping) { m_crash_dumper.enable(); } m_crash_dumper.set_full_memory_dump(settings_manager.CrashDump.FullMemoryDump); +#endif +#ifdef HAS_UI m_debugging_gui.set_gfx_backend(settings_manager.Debug.GraphicsAPI); +#endif - // Setup the log file - auto& file_device = Output::set_default_devices(); - file_device.set_file_name_and_path(m_log_directory / m_log_file_name); +#ifdef HAS_INPUT + m_input_handler.init(); +#endif - create_simple_console(); +#ifdef HAS_UI if (settings_manager.Debug.DebugConsoleEnabled) { m_console_device = &Output::set_default_devices(); - m_console_device->set_formatter([](File::StringViewType string) -> File::StringType { - return std::format(STR("[{}] {}"), std::format(STR("{:%X}"), std::chrono::system_clock::now()), string); + m_console_device->set_formatter([](SystemStringViewType string) -> SystemStringType { + return std::format(SYSSTR("[{}] {}"), std::format(SYSSTR("{:%X}"), std::chrono::system_clock::now()), string); }); if (settings_manager.Debug.DebugConsoleVisible) { m_render_thread = std::jthread{&GUI::gui_thread, &m_debugging_gui}; } } - +#endif // This is experimental code that's here only for future reference /* Unreal::UnrealInitializer::SetupUnrealModules(); constexpr const wchar_t* str_to_find = STR("Allocator: %s"); void* string_address = SinglePassScanner::string_scan(str_to_find, ScanTarget::Core); - Output::send(STR("\n\nFound string '{}' at {}\n\n"), std::wstring_view{str_to_find}, string_address); + Output::send(SYSSTR("\n\nFound string '{}' at {}\n\n"), SystemStringViewType{str_to_find}, string_address); //*/ - Output::send(STR("Console created\n")); - Output::send(STR("UE4SS - v{}.{}.{}{}{} - Git SHA #{}\n"), + Output::send(SYSSTR("dir: {}\n"), (m_log_directory / m_log_file_name)); + + Output::send(SYSSTR("Console created\n")); + Output::send(SYSSTR("UE4SS - v{}.{}.{}{}{} - Git SHA #{}\n"), UE4SS_LIB_VERSION_MAJOR, UE4SS_LIB_VERSION_MINOR, UE4SS_LIB_VERSION_HOTFIX, - std::format(L"{}", UE4SS_LIB_VERSION_PRERELEASE == 0 ? L"" : std::format(L" PreRelease #{}", UE4SS_LIB_VERSION_PRERELEASE)), - std::format(L"{}", - UE4SS_LIB_BETA_STARTED == 0 ? L"" : (UE4SS_LIB_IS_BETA == 0 ? L" Beta #?" : std::format(L" Beta #{}", UE4SS_LIB_VERSION_BETA))), - to_wstring(UE4SS_LIB_BUILD_GITSHA)); + std::format(SYSSTR("{}"), + UE4SS_LIB_VERSION_PRERELEASE == 0 ? SYSSTR("") : std::format(SYSSTR(" PreRelease #{}"), UE4SS_LIB_VERSION_PRERELEASE)), + std::format(SYSSTR("{}"), + UE4SS_LIB_BETA_STARTED == 0 + ? SYSSTR("") + : (UE4SS_LIB_IS_BETA == 0 ? SYSSTR(" Beta #?") : std::format(SYSSTR(" Beta #{}"), UE4SS_LIB_VERSION_BETA))), + to_system(UE4SS_LIB_BUILD_GITSHA)); #ifdef __clang__ -#define UE4SS_COMPILER L"Clang" +#define UE4SS_COMPILER SYSSTR("Clang") #else -#define UE4SS_COMPILER L"MSVC" +#define UE4SS_COMPILER SYSSTR("MSVC") #endif - Output::send(STR("UE4SS Build Configuration: {} ({})\n"), to_wstring(UE4SS_CONFIGURATION), UE4SS_COMPILER); - + Output::send(SYSSTR("UE4SS Build Configuration: {} ({})\n"), to_system(UE4SS_CONFIGURATION), UE4SS_COMPILER); +#ifdef WIN32 m_load_library_a_hook = std::make_unique("kernel32.dll", "LoadLibraryA", std::bit_cast(&HookedLoadLibraryA), &m_hook_trampoline_load_library_a, - L""); + SYSSTR("")); m_load_library_a_hook->hook(); m_load_library_ex_a_hook = std::make_unique("kernel32.dll", "LoadLibraryExA", std::bit_cast(&HookedLoadLibraryExA), &m_hook_trampoline_load_library_ex_a, - L""); + SYSSTR("")); m_load_library_ex_a_hook->hook(); m_load_library_w_hook = std::make_unique("kernel32.dll", "LoadLibraryW", std::bit_cast(&HookedLoadLibraryW), &m_hook_trampoline_load_library_w, - L""); + SYSSTR("")); m_load_library_w_hook->hook(); m_load_library_ex_w_hook = std::make_unique("kernel32.dll", "LoadLibraryExW", std::bit_cast(&HookedLoadLibraryExW), &m_hook_trampoline_load_library_ex_w, - L""); + SYSSTR("")); m_load_library_ex_w_hook->hook(); - - Unreal::UnrealInitializer::SetupUnrealModules(); +#endif + Unreal::UnrealInitializer::Platform::SetupUnrealModules(); setup_mods(); install_cpp_mods(); @@ -272,21 +295,21 @@ namespace RC if (m_has_game_specific_config) { - Output::send(STR("Found configuration for game: {}\n"), m_mods_directory.parent_path().filename().c_str()); + Output::send(SYSSTR("Found configuration for game: {}\n"), m_mods_directory.parent_path().filename().c_str()); } else { - Output::send(STR("No specific game configuration found, using default configuration file\n")); + Output::send(SYSSTR("No specific game configuration found, using default configuration file\n")); } - Output::send(STR("Config: {}\n\n"), m_settings_path_and_file.c_str()); - Output::send(STR("root directory: {}\n"), m_root_directory.c_str()); - Output::send(STR("working directory: {}\n"), m_working_directory.c_str()); - Output::send(STR("game executable directory: {}\n"), m_game_executable_directory.c_str()); - Output::send(STR("game executable: {} ({} bytes)\n\n\n"), m_game_path_and_exe_name.c_str(), std::filesystem::file_size(m_game_path_and_exe_name)); - Output::send(STR("mods directory: {}\n"), m_mods_directory.c_str()); - Output::send(STR("log directory: {}\n"), m_log_directory.c_str()); - Output::send(STR("object dumper directory: {}\n\n\n"), m_object_dumper_output_directory.c_str()); + Output::send(SYSSTR("Config: {}\n\n"), m_settings_path_and_file.c_str()); + Output::send(SYSSTR("root directory: {}\n"), m_root_directory.c_str()); + Output::send(SYSSTR("working directory: {}\n"), m_working_directory.c_str()); + Output::send(SYSSTR("game executable directory: {}\n"), m_game_executable_directory.c_str()); + Output::send(SYSSTR("game executable: {} ({} bytes)\n\n\n"), m_game_path_and_exe_name.c_str(), std::filesystem::file_size(m_game_path_and_exe_name)); + Output::send(SYSSTR("mods directory: {}\n"), m_mods_directory.c_str()); + Output::send(SYSSTR("log directory: {}\n"), m_log_directory.c_str()); + Output::send(SYSSTR("object dumper directory: {}\n\n\n"), m_object_dumper_output_directory.c_str()); } catch (std::runtime_error& e) { @@ -317,11 +340,14 @@ namespace RC ProfilerSetThreadName("UE4SS-InitThread"); ProfilerScope(); + Output::send(SYSSTR("Initializing ue4ss program\n")); + try { setup_unreal(); - Output::send(STR("Unreal Engine modules ({}):\n"), SigScannerStaticData::m_is_modular ? STR("modular") : STR("non-modular")); + Output::send(SYSSTR("Unreal Engine modules ({}):\n"), SigScannerStaticData::m_is_modular ? SYSSTR("modular") : SYSSTR("non-modular")); +#ifdef WIN32 auto& main_exe_ptr = SigScannerStaticData::m_modules_info.array[static_cast(ScanTarget::MainExe)].lpBaseOfDll; for (size_t i = 0; i < static_cast(ScanTarget::Max); ++i) { @@ -329,11 +355,26 @@ namespace RC // only log modules with unique addresses (non-modular builds have everything in MainExe) if (i == static_cast(ScanTarget::MainExe) || main_exe_ptr != module.lpBaseOfDll) { - auto module_name = to_wstring(ScanTargetToString(i)); - Output::send(STR("{} @ {} size={:#x}\n"), module_name.c_str(), module.lpBaseOfDll, module.SizeOfImage); + auto module_name = to_system(ScanTargetToString(i)); + Output::send(SYSSTR("{} @ {} size={:#x}\n"), module_name.c_str(), module.lpBaseOfDll, module.SizeOfImage); } } +#elif defined(LINUX) + auto& main_exe_ptr = SigScannerStaticData::m_modules_info.array[static_cast(ScanTarget::MainExe)].base_address; + for (size_t i = 0; i < static_cast(ScanTarget::Max); ++i) + { + auto& module = SigScannerStaticData::m_modules_info.array[i]; + // only log modules with unique addresses (non-modular builds have everything in MainExe) + if (i == static_cast(ScanTarget::MainExe) || main_exe_ptr != module.base_address) + { + auto module_name = to_system(ScanTargetToString(i)); + // FIXME: FIX Why this won't WORK? + // Output::send(SYSSTR("{} @ {} size={:#x}\n"), module_name, module.base_address, module.size); + } + } +#else +#endif fire_unreal_init_for_cpp_mods(); setup_unreal_properties(); UAssetRegistry::SetMaxMemoryUsageDuringAssetLoading(settings_manager.Memory.MaxMemoryUsageDuringAssetLoading); @@ -367,21 +408,22 @@ namespace RC } } - auto UE4SSProgram::setup_paths(const std::wstring& moduleFilePathString) -> void + auto UE4SSProgram::setup_paths(const SystemStringType& moduleFilePathString) -> void { ProfilerScope(); const std::filesystem::path moduleFilePath = std::filesystem::path(moduleFilePathString); - m_root_directory = moduleFilePath.parent_path().wstring(); - m_module_file_path = moduleFilePath.wstring(); + m_root_directory = to_system(moduleFilePath.parent_path()); + m_module_file_path = to_system(moduleFilePath); // The default working directory is the root directory // Can be changed by creating a directory in the root directory // At that point, the working directory will be "root/" m_working_directory = m_root_directory; - wchar_t exe_path_buffer[1024]; - GetModuleFileNameW(GetModuleHandle(nullptr), exe_path_buffer, 1023); - std::filesystem::path game_exe_path = exe_path_buffer; + // Default file to open if there is no game specific config + m_default_settings_path_and_file = m_root_directory / m_settings_file_name; + + std::filesystem::path game_exe_path = get_executable_path(); std::filesystem::path game_directory_path = game_exe_path.parent_path(); m_legacy_root_directory = game_directory_path; @@ -392,8 +434,8 @@ namespace RC m_game_path_and_exe_name = game_exe_path; m_object_dumper_output_directory = m_working_directory; - // Allow loading of DLLs from the game directory - AddDllDirectory(game_exe_path.c_str()); + // Allow loading of DLLs from mod folders + add_dlsearch_folder(game_exe_path); for (const auto& item : std::filesystem::directory_iterator(m_root_directory)) { @@ -406,7 +448,7 @@ namespace RC { m_has_game_specific_config = true; m_working_directory = item.path(); - m_mods_directory = item.path().wstring() + L"\\Mods"; + m_mods_directory = item.path() / SYSSTR("Mods"); m_settings_path_and_file = std::move(item.path()); m_log_directory = m_working_directory; m_object_dumper_output_directory = m_working_directory; @@ -416,24 +458,34 @@ namespace RC } m_log_directory = m_working_directory; - m_settings_path_and_file.append(m_settings_file_name); - - // Check for legacy locations and update paths accordingly - if (std::filesystem::exists(m_legacy_root_directory / m_settings_file_name) && !std::filesystem::exists(m_settings_path_and_file)) + auto resolaved_settings_file = File::get_path_if_exists(m_settings_path_and_file, m_settings_file_name); + if (resolaved_settings_file) { - m_settings_path_and_file = m_legacy_root_directory / m_settings_file_name; + m_settings_path_and_file = *resolaved_settings_file; } + else + { + // Check for legacy locations and update paths accordingly + auto resolaved_legacy_settings_file = File::get_path_if_exists(m_legacy_root_directory, m_settings_file_name); + if (resolaved_legacy_settings_file) + { + m_settings_path_and_file = *resolaved_legacy_settings_file; + } else { + throw std::runtime_error{"UE4SS-Settings.ini file not found"}; + } + } + if (std::filesystem::exists(m_legacy_root_directory / "Mods") && !std::filesystem::exists(m_mods_directory)) { m_mods_directory = m_legacy_root_directory / "Mods"; } } - auto UE4SSProgram::create_emergency_console_for_early_error(File::StringViewType error_message) -> void + auto UE4SSProgram::create_emergency_console_for_early_error(SystemStringViewType error_message) -> void { settings_manager.Debug.SimpleConsoleEnabled = true; create_simple_console(); - printf_s("%S\n", error_message.data()); + printf_s(SystemStringPrint "\n", error_message.data()); } auto UE4SSProgram::setup_mod_directory_path() -> void @@ -456,10 +508,10 @@ namespace RC { m_debug_console_device = &Output::set_default_devices(); Output::set_default_log_level(); - m_debug_console_device->set_formatter([](File::StringViewType string) -> File::StringType { - return std::format(STR("[{}] {}"), std::format(STR("{:%X}"), std::chrono::system_clock::now()), string); + m_debug_console_device->set_formatter([](SystemStringViewType string) -> SystemStringType { + return std::format(SYSSTR("[{}] {}"), std::format(SYSSTR("{:%X}"), std::chrono::system_clock::now()), string); }); - +#ifdef WIN32 if (AllocConsole()) { FILE* stdin_filename; @@ -469,6 +521,7 @@ namespace RC freopen_s(&stdout_filename, "CONOUT$", "w", stdout); freopen_s(&stderr_filename, "CONOUT$", "w", stderr); } +#endif } } @@ -478,10 +531,11 @@ namespace RC if (std::filesystem::exists(file_path)) { auto file = File::open(file_path); - if (auto file_contents = file.read_all(); !file_contents.empty()) + if (auto file_contents = file.read_file_all(); !file_contents.empty()) { Ini::Parser parser; - parser.parse(file_contents); + auto content = to_system_string(file_contents); + parser.parse(content); file.close(); // The following code is auto-generated. @@ -493,8 +547,10 @@ namespace RC auto UE4SSProgram::setup_unreal() -> void { ProfilerScope(); + Output::send(SYSSTR("Setting up unreal\n")); + // Retrieve offsets from the config file - const std::wstring offset_overrides_section{L"OffsetOverrides"}; + const SystemStringType offset_overrides_section{SYSSTR("OffsetOverrides")}; load_unreal_offsets_from_file(); @@ -562,7 +618,8 @@ namespace RC // Virtual function offset overrides TRY([&]() { ProfilerScopeNamed("loading virtual function offset overrides"); - static File::StringType virtual_function_offset_override_file{(m_working_directory / STR("VTableLayout.ini")).wstring()}; + fprintf(stderr, "Loading offset\n"); + static auto virtual_function_offset_override_file{m_working_directory / SYSSTR("VTableLayout.ini")}; if (std::filesystem::exists(virtual_function_offset_override_file)) { auto file = @@ -570,72 +627,75 @@ namespace RC Ini::Parser parser; parser.parse(file); - Output::send(STR("Getting ordered lists from ini file\n")); + Output::send(SYSSTR("Getting ordered lists from ini file\n")); auto calculate_virtual_function_offset = [](uint32_t current_index, BaseSizes... base_sizes) -> uint32_t { return current_index == 0 ? 0 : (current_index + (base_sizes + ...)) * 8; }; - auto retrieve_vtable_layout_from_ini = [&](const File::StringType& section_name, auto callable) -> uint32_t { + auto retrieve_vtable_layout_from_ini = [&](const SystemStringType& section_name, auto callable) -> uint32_t { auto list = parser.get_ordered_list(section_name); uint32_t vtable_size = list.size() - 1; - list.for_each([&](uint32_t index, File::StringType& item) { - callable(index, item); + list.for_each([&](uint32_t index, SystemStringType& item) { + auto ue_str = to_ue(item); + callable(index, item, ue_str); }); return vtable_size; }; - Output::send(STR("UObjectBase\n")); - uint32_t uobjectbase_size = retrieve_vtable_layout_from_ini(STR("UObjectBase"), [&](uint32_t index, File::StringType& item) { - uint32_t offset = calculate_virtual_function_offset(index, 0); - Output::send(STR("UObjectBase::{} = 0x{:X}\n"), item, offset); - Unreal::UObjectBase::VTableLayoutMap.emplace(item, offset); - }); - - Output::send(STR("UObjectBaseUtility\n")); - uint32_t uobjectbaseutility_size = retrieve_vtable_layout_from_ini(STR("UObjectBaseUtility"), [&](uint32_t index, File::StringType& item) { - uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size); - Output::send(STR("UObjectBaseUtility::{} = 0x{:X}\n"), item, offset); - Unreal::UObjectBaseUtility::VTableLayoutMap.emplace(item, offset); - }); - - Output::send(STR("UObject\n")); - uint32_t uobject_size = retrieve_vtable_layout_from_ini(STR("UObject"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("UObjectBase\n")); + uint32_t uobjectbase_size = + retrieve_vtable_layout_from_ini(SYSSTR("UObjectBase"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { + uint32_t offset = calculate_virtual_function_offset(index, 0); + Output::send(SYSSTR("UObjectBase::{} = 0x{:X}\n"), item, offset); + Unreal::UObjectBase::VTableLayoutMap.emplace(item_ue, offset); + }); + + Output::send(SYSSTR("UObjectBaseUtility\n")); + uint32_t uobjectbaseutility_size = + retrieve_vtable_layout_from_ini(SYSSTR("UObjectBaseUtility"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { + uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size); + Output::send(SYSSTR("UObjectBaseUtility::{} = 0x{:X}\n"), item, offset); + Unreal::UObjectBaseUtility::VTableLayoutMap.emplace(item_ue, offset); + }); + + Output::send(SYSSTR("UObject\n")); + uint32_t uobject_size = retrieve_vtable_layout_from_ini(SYSSTR("UObject"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size); - Output::send(STR("UObject::{} = 0x{:X}\n"), item, offset); - Unreal::UObject::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("UObject::{} = 0x{:X}\n"), item, offset); + Unreal::UObject::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("UField\n")); - uint32_t ufield_size = retrieve_vtable_layout_from_ini(STR("UField"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("UField\n")); + uint32_t ufield_size = retrieve_vtable_layout_from_ini(SYSSTR("UField"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size); - Output::send(STR("UField::{} = 0x{:X}\n"), item, offset); - Unreal::UField::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("UField::{} = 0x{:X}\n"), item, offset); + Unreal::UField::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("UEngine\n")); - uint32_t uengine_size = retrieve_vtable_layout_from_ini(STR("UEngine"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("UEngine\n")); + uint32_t uengine_size = retrieve_vtable_layout_from_ini(SYSSTR("UEngine"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size); - Output::send(STR("UEngine::{} = 0x{:X}\n"), item, offset); - Unreal::UEngine::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("UEngine::{} = 0x{:X}\n"), item, offset); + Unreal::UEngine::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("UScriptStruct::ICppStructOps\n")); - retrieve_vtable_layout_from_ini(STR("UScriptStruct::ICppStructOps"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("UScriptStruct::ICppStructOps\n")); + retrieve_vtable_layout_from_ini(SYSSTR("UScriptStruct::ICppStructOps"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, 0); - Output::send(STR("UScriptStruct::ICppStructOps::{} = 0x{:X}\n"), item, offset); - Unreal::UScriptStruct::ICppStructOps::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("UScriptStruct::ICppStructOps::{} = 0x{:X}\n"), item, offset); + Unreal::UScriptStruct::ICppStructOps::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("FField\n")); - uint32_t ffield_size = retrieve_vtable_layout_from_ini(STR("FField"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FField\n")); + uint32_t ffield_size = retrieve_vtable_layout_from_ini(SYSSTR("FField"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, 0); - Output::send(STR("FField::{} = 0x{:X}\n"), item, offset); - Unreal::FField::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FField::{} = 0x{:X}\n"), item, offset); + Unreal::FField::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("FProperty\n")); - uint32_t fproperty_size = retrieve_vtable_layout_from_ini(STR("FProperty"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FProperty\n")); + uint32_t fproperty_size = retrieve_vtable_layout_from_ini(SYSSTR("FProperty"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset{}; if (Unreal::Version::IsBelow(4, 25)) { @@ -645,8 +705,8 @@ namespace RC { offset = calculate_virtual_function_offset(index, ffield_size); } - Output::send(STR("FProperty::{} = 0x{:X}\n"), item, offset); - Unreal::FProperty::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FProperty::{} = 0x{:X}\n"), item, offset); + Unreal::FProperty::VTableLayoutMap.emplace(item_ue, offset); }); // If the engine version is <4.25 then the inheritance is different and we must take that into consideration. @@ -659,66 +719,67 @@ namespace RC fproperty_size = ffield_size + fproperty_size; } - Output::send(STR("FNumericProperty\n")); - retrieve_vtable_layout_from_ini(STR("FNumericProperty"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FNumericProperty\n")); + retrieve_vtable_layout_from_ini(SYSSTR("FNumericProperty"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, fproperty_size); - Output::send(STR("FNumericProperty::{} = 0x{:X}\n"), item, offset); - Unreal::FNumericProperty::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FNumericProperty::{} = 0x{:X}\n"), item, offset); + Unreal::FNumericProperty::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("FMulticastDelegateProperty\n")); - retrieve_vtable_layout_from_ini(STR("FMulticastDelegateProperty"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FMulticastDelegateProperty\n")); + retrieve_vtable_layout_from_ini(SYSSTR("FMulticastDelegateProperty"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, fproperty_size); - Output::send(STR("FMulticastDelegateProperty::{} = 0x{:X}\n"), item, offset); - Unreal::FMulticastDelegateProperty::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FMulticastDelegateProperty::{} = 0x{:X}\n"), item, offset); + Unreal::FMulticastDelegateProperty::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("FObjectPropertyBase\n")); - retrieve_vtable_layout_from_ini(STR("FObjectPropertyBase"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FObjectPropertyBase\n")); + retrieve_vtable_layout_from_ini(SYSSTR("FObjectPropertyBase"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, fproperty_size); - Output::send(STR("FObjectPropertyBase::{} = 0x{:X}\n"), item, offset); - Unreal::FObjectPropertyBase::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FObjectPropertyBase::{} = 0x{:X}\n"), item, offset); + Unreal::FObjectPropertyBase::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("UStruct\n")); - retrieve_vtable_layout_from_ini(STR("UStruct"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("UStruct\n")); + retrieve_vtable_layout_from_ini(SYSSTR("UStruct"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size, ufield_size); - Output::send(STR("UStruct::{} = 0x{:X}\n"), item, offset); - Unreal::UStruct::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("UStruct::{} = 0x{:X}\n"), item, offset); + Unreal::UStruct::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("FOutputDevice\n")); - retrieve_vtable_layout_from_ini(STR("FOutputDevice"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FOutputDevice\n")); + retrieve_vtable_layout_from_ini(SYSSTR("FOutputDevice"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, 0); - Output::send(STR("FOutputDevice::{} = 0x{:X}\n"), item, offset); - Unreal::FOutputDevice::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FOutputDevice::{} = 0x{:X}\n"), item, offset); + Unreal::FOutputDevice::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("FMalloc\n")); - retrieve_vtable_layout_from_ini(STR("FMalloc"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("FMalloc\n")); + retrieve_vtable_layout_from_ini(SYSSTR("FMalloc"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { // We don't support FExec, so we're manually telling it the size. static constexpr uint32_t fexec_size = 1; uint32_t offset = calculate_virtual_function_offset(index, fexec_size); - Output::send(STR("FMalloc::{} = 0x{:X}\n"), item, offset); - Unreal::FMalloc::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("FMalloc::{} = 0x{:X}\n"), item, offset); + Unreal::FMalloc::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("AActor\n")); - uint32_t aactor_size = retrieve_vtable_layout_from_ini(STR("AActor"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("AActor\n")); + uint32_t aactor_size = retrieve_vtable_layout_from_ini(SYSSTR("AActor"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size); - Output::send(STR("AActor::{} = 0x{:X}\n"), item, offset); - Unreal::AActor::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("AActor::{} = 0x{:X}\n"), item, offset); + Unreal::AActor::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("AGameModeBase\n")); - uint32_t agamemodebase_size = retrieve_vtable_layout_from_ini(STR("AGameModeBase"), [&](uint32_t index, File::StringType& item) { - uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size, aactor_size); - Output::send(STR("AGameModeBase::{} = 0x{:X}\n"), item, offset); - Unreal::AGameModeBase::VTableLayoutMap.emplace(item, offset); - }); + Output::send(SYSSTR("AGameModeBase\n")); + uint32_t agamemodebase_size = + retrieve_vtable_layout_from_ini(SYSSTR("AGameModeBase"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { + uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size, aactor_size); + Output::send(SYSSTR("AGameModeBase::{} = 0x{:X}\n"), item, offset); + Unreal::AGameModeBase::VTableLayoutMap.emplace(item_ue, offset); + }); - Output::send(STR("AGameMode\n")); - retrieve_vtable_layout_from_ini(STR("AGameMode"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("AGameMode\n")); + retrieve_vtable_layout_from_ini(SYSSTR("AGameMode"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, Unreal::Version::IsAtLeast(4, 14) ? uobjectbase_size, @@ -730,28 +791,31 @@ namespace RC uobjectbaseutility_size, uobject_size, aactor_size); - Output::send(STR("AGameMode::{} = 0x{:X}\n"), item, offset); - Unreal::AGameMode::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("AGameMode::{} = 0x{:X}\n"), item, offset); + Unreal::AGameMode::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("UPlayer\n")); - uint32_t uplayer_size = retrieve_vtable_layout_from_ini(STR("UPlayer"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("UPlayer\n")); + uint32_t uplayer_size = retrieve_vtable_layout_from_ini(SYSSTR("UPlayer"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size); - Output::send(STR("UPlayer::{} = 0x{:X}\n"), item, offset); - Unreal::UPlayer::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("UPlayer::{} = 0x{:X}\n"), item, offset); + Unreal::UPlayer::VTableLayoutMap.emplace(item_ue, offset); }); - Output::send(STR("ULocalPlayer\n")); - retrieve_vtable_layout_from_ini(STR("ULocalPlayer"), [&](uint32_t index, File::StringType& item) { + Output::send(SYSSTR("ULocalPlayer\n")); + retrieve_vtable_layout_from_ini(SYSSTR("ULocalPlayer"), [&](uint32_t index, SystemStringType& item, UEStringType& item_ue) { uint32_t offset = calculate_virtual_function_offset(index, uobjectbase_size, uobjectbaseutility_size, uobject_size, uplayer_size); - Output::send(STR("ULocalPlayer::{} = 0x{:X}\n"), item, offset); - Unreal::ULocalPlayer::VTableLayoutMap.emplace(item, offset); + Output::send(SYSSTR("ULocalPlayer::{} = 0x{:X}\n"), item, offset); + Unreal::ULocalPlayer::VTableLayoutMap.emplace(item_ue, offset); }); file.close(); } + + fprintf(stderr, "Loading offset done\n"); }); + fprintf(stderr, "Setting hooks\n"); config.bHookProcessInternal = settings_manager.Hooks.HookProcessInternal; config.bHookProcessLocalScriptFunction = settings_manager.Hooks.HookProcessLocalScriptFunction; config.bHookLoadMap = settings_manager.Hooks.HookLoadMap; @@ -761,23 +825,23 @@ namespace RC config.bHookLocalPlayerExec = settings_manager.Hooks.HookLocalPlayerExec; config.bHookAActorTick = settings_manager.Hooks.HookAActorTick; config.FExecVTableOffsetInLocalPlayer = settings_manager.Hooks.FExecVTableOffsetInLocalPlayer; - + fprintf(stderr, "Before UnrealInitializer::Initialize\n"); Unreal::UnrealInitializer::Initialize(config); bool can_create_custom_events{true}; if (!UObject::ProcessLocalScriptFunctionInternal.is_ready() && Unreal::Version::IsAtLeast(4, 22)) { can_create_custom_events = false; - Output::send(STR("ProcessLocalScriptFunction is not available, the following features will be unavailable:\n")); + Output::send(SYSSTR("ProcessLocalScriptFunction is not available, the following features will be unavailable:\n")); } else if (!UObject::ProcessInternalInternal.is_ready() && Unreal::Version::IsBelow(4, 22)) { can_create_custom_events = false; - Output::send(STR("ProcessInternal is not available, the following features will be unavailable:\n")); + Output::send(SYSSTR("ProcessInternal is not available, the following features will be unavailable:\n")); } if (!can_create_custom_events) { - Output::send(STR("\n")); + Output::send(SYSSTR("\n")); } } @@ -787,7 +851,7 @@ namespace RC m_shared_functions.set_script_variable_default_data_function = &LuaLibrary::set_script_variable_default_data; m_shared_functions.call_script_function_function = &LuaLibrary::call_script_function; m_shared_functions.is_ue4ss_initialized_function = &LuaLibrary::is_ue4ss_initialized; - Output::send(STR("m_shared_functions: {}\n"), static_cast(&m_shared_functions)); + Output::send(SYSSTR("m_shared_functions: {}\n"), static_cast(&m_shared_functions)); } auto UE4SSProgram::on_program_start() -> void @@ -799,7 +863,7 @@ namespace RC /* UObjectArray::AddUObjectCreateListener(&FUEDeathListener::UEDeathListener); //*/ - +#ifdef HAS_UI if (settings_manager.Debug.DebugConsoleEnabled) { if (settings_manager.General.UseUObjectArrayCache) @@ -810,7 +874,7 @@ namespace RC { m_debugging_gui.get_live_view().set_listeners_allowed(false); } - +#ifdef HAS_INPUT m_input_handler.register_keydown_event(Input::Key::O, {Input::ModifierKey::CONTROL}, [&]() { TRY([&] { auto was_gui_open = get_debugging_ui().is_open(); @@ -822,7 +886,9 @@ namespace RC } }); }); +#endif } +#endif #ifdef TIME_FUNCTION_MACRO_ENABLED m_input_handler.register_keydown_event(Input::Key::Y, {Input::ModifierKey::CONTROL}, [&]() { @@ -830,19 +896,19 @@ namespace RC { FunctionTimerFrame::stop_profiling(); FunctionTimerFrame::dump_profile(); - Output::send(STR("Profiler stopped & dumped\n")); + Output::send(SYSSTR("Profiler stopped & dumped\n")); } else { FunctionTimerFrame::start_profiling(); - Output::send(STR("Profiler started\n")); + Output::send(SYSSTR("Profiler started\n")); } }); #endif TRY([&] { ObjectDumper::init(); - +#ifdef HAS_INPUT if (settings_manager.General.EnableHotReloadSystem) { m_input_handler.register_keydown_event(Input::Key::R, {Input::ModifierKey::CONTROL}, [&]() { @@ -851,13 +917,27 @@ namespace RC }); }); } - +#endif if ((settings_manager.ObjectDumper.LoadAllAssetsBeforeDumpingObjects || settings_manager.CXXHeaderGenerator.LoadAllAssetsBeforeGeneratingCXXHeaders) && Unreal::Version::IsBelow(4, 17)) { Output::send( - STR("FAssetData not available in <4.17, ignoring 'LoadAllAssetsBeforeDumpingObjects' & 'LoadAllAssetsBeforeGeneratingCXXHeaders'.")); + SYSSTR("FAssetData not available in <4.17, ignoring 'LoadAllAssetsBeforeDumpingObjects' & 'LoadAllAssetsBeforeGeneratingCXXHeaders'.")); + } + +#ifdef HAS_INPUT + if (!settings_manager.General.InputSource.empty()) + { + if (m_input_handler.set_input_source(to_string(settings_manager.General.InputSource))) + { + Output::send(SYSSTR("Input source set to: {}\n"), m_input_handler.get_current_input_source()); + } + else + { + Output::send(SYSSTR("Failed to set input source to: {}\n"), settings_manager.General.InputSource); + } } +#endif install_lua_mods(); LuaMod::on_program_start(); @@ -865,12 +945,14 @@ namespace RC start_lua_mods(); }); +#ifdef HAS_INPUT if (settings_manager.General.EnableDebugKeyBindings) { m_input_handler.register_keydown_event(Input::Key::NUM_NINE, {Input::ModifierKey::CONTROL}, [&]() { generate_uht_compatible_headers(); }); } +#endif } auto UE4SSProgram::update() -> void @@ -879,7 +961,7 @@ namespace RC on_program_start(); - Output::send(STR("Event loop start\n")); + Output::send(SYSSTR("Event loop start\n")); for (m_processing_events = true; m_processing_events;) { if (m_pause_events_processing || UE4SSProgram::unreal_is_shutting_down) @@ -929,9 +1011,9 @@ namespace RC } } //*/ - +#ifdef HAS_INPUT m_input_handler.process_event(); - +#endif { ProfilerScopeNamed("mod update processing"); @@ -947,40 +1029,40 @@ namespace RC std::this_thread::sleep_for(std::chrono::milliseconds(5)); ProfilerFrameMark(); } - Output::send(STR("Event loop end\n")); + Output::send(SYSSTR("Event loop end\n")); } auto UE4SSProgram::setup_unreal_properties() -> void { - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"ObjectProperty").GetComparisonIndex(), &LuaType::push_objectproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"ClassProperty").GetComparisonIndex(), &LuaType::push_classproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"Int8Property").GetComparisonIndex(), &LuaType::push_int8property); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"Int16Property").GetComparisonIndex(), &LuaType::push_int16property); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"IntProperty").GetComparisonIndex(), &LuaType::push_intproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"Int64Property").GetComparisonIndex(), &LuaType::push_int64property); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"ByteProperty").GetComparisonIndex(), &LuaType::push_byteproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"UInt16Property").GetComparisonIndex(), &LuaType::push_uint16property); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"UInt32Property").GetComparisonIndex(), &LuaType::push_uint32property); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"UInt64Property").GetComparisonIndex(), &LuaType::push_uint64property); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"StructProperty").GetComparisonIndex(), &LuaType::push_structproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"ArrayProperty").GetComparisonIndex(), &LuaType::push_arrayproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"FloatProperty").GetComparisonIndex(), &LuaType::push_floatproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"DoubleProperty").GetComparisonIndex(), &LuaType::push_doubleproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"BoolProperty").GetComparisonIndex(), &LuaType::push_boolproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"EnumProperty").GetComparisonIndex(), &LuaType::push_enumproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"WeakObjectProperty").GetComparisonIndex(), &LuaType::push_weakobjectproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"NameProperty").GetComparisonIndex(), &LuaType::push_nameproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"TextProperty").GetComparisonIndex(), &LuaType::push_textproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"StrProperty").GetComparisonIndex(), &LuaType::push_strproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"SoftClassProperty").GetComparisonIndex(), &LuaType::push_softclassproperty); - LuaType::StaticState::m_property_value_pushers.emplace(FName(L"InterfaceProperty").GetComparisonIndex(), &LuaType::push_interfaceproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("ObjectProperty")).GetComparisonIndex(), &LuaType::push_objectproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("ClassProperty")).GetComparisonIndex(), &LuaType::push_classproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("Int8Property")).GetComparisonIndex(), &LuaType::push_int8property); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("Int16Property")).GetComparisonIndex(), &LuaType::push_int16property); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("IntProperty")).GetComparisonIndex(), &LuaType::push_intproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("Int64Property")).GetComparisonIndex(), &LuaType::push_int64property); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("ByteProperty")).GetComparisonIndex(), &LuaType::push_byteproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("UInt16Property")).GetComparisonIndex(), &LuaType::push_uint16property); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("UInt32Property")).GetComparisonIndex(), &LuaType::push_uint32property); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("UInt64Property")).GetComparisonIndex(), &LuaType::push_uint64property); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("StructProperty")).GetComparisonIndex(), &LuaType::push_structproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("ArrayProperty")).GetComparisonIndex(), &LuaType::push_arrayproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("FloatProperty")).GetComparisonIndex(), &LuaType::push_floatproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("DoubleProperty")).GetComparisonIndex(), &LuaType::push_doubleproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("BoolProperty")).GetComparisonIndex(), &LuaType::push_boolproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("EnumProperty")).GetComparisonIndex(), &LuaType::push_enumproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("WeakObjectProperty")).GetComparisonIndex(), &LuaType::push_weakobjectproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("NameProperty")).GetComparisonIndex(), &LuaType::push_nameproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("TextProperty")).GetComparisonIndex(), &LuaType::push_textproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("StrProperty")).GetComparisonIndex(), &LuaType::push_strproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("SoftClassProperty")).GetComparisonIndex(), &LuaType::push_softclassproperty); + LuaType::StaticState::m_property_value_pushers.emplace(FName(STR("InterfaceProperty")).GetComparisonIndex(), &LuaType::push_interfaceproperty); } auto UE4SSProgram::setup_mods() -> void { ProfilerScope(); - Output::send(STR("Setting up mods...\n")); + Output::send(SYSSTR("Setting up mods...\n")); if (!std::filesystem::exists(m_mods_directory)) { @@ -1001,20 +1083,26 @@ namespace RC set_error("is_directory ran into error %d", ec.value()); } - std::wstring directory_lowercase = sub_directory.path().stem().wstring(); + SystemStringType directory_lowercase = to_system_string(sub_directory.path().stem()); std::transform(directory_lowercase.begin(), directory_lowercase.end(), directory_lowercase.begin(), std::towlower); - if (directory_lowercase == L"shared") + if (directory_lowercase == SYSSTR("shared")) { // Do stuff when shared libraries have been implemented } else { // Create the mod but don't install it yet - if (std::filesystem::exists(sub_directory.path() / "scripts")) - m_mods.emplace_back(std::make_unique(*this, sub_directory.path().stem().wstring(), sub_directory.path().wstring())); - if (std::filesystem::exists(sub_directory.path() / "dlls")) - m_mods.emplace_back(std::make_unique(*this, sub_directory.path().stem().wstring(), sub_directory.path().wstring())); + auto scripts_directory = File::get_path_if_exists(sub_directory.path(), "Scripts"); + auto dlls_directory = File::get_path_if_exists(sub_directory.path(), "dlls"); + if (scripts_directory) + { + m_mods.emplace_back(std::make_unique(*this, to_system_string(sub_directory.path().stem()), to_system_string(sub_directory.path()))); + } + if (dlls_directory) + { + m_mods.emplace_back(std::make_unique(*this, to_system_string(sub_directory.path().stem()), to_system_string(sub_directory.path()))); + } } } } @@ -1037,19 +1125,19 @@ namespace RC if (mod_name_is_taken) { mod->set_installable(false); - Output::send(STR("Mod name '{}' is already in use.\n"), mod->get_name()); + Output::send(SYSSTR("Mod name '{}' is already in use.\n"), mod->get_name()); continue; } if (mod->is_installed()) { - Output::send(STR("Tried to install a mod that was already installed, Mod: '{}'\n"), mod->get_name()); + Output::send(SYSSTR("Tried to install a mod that was already installed, Mod: '{}'\n"), mod->get_name()); continue; } if (!mod->is_installable()) { - Output::send(STR("Was unable to install mod '{}' for unknown reasons. Mod is not installable.\n"), mod->get_name()); + Output::send(SYSSTR("Was unable to install mod '{}' for unknown reasons. Mod is not installable.\n"), mod->get_name()); continue; } @@ -1059,12 +1147,12 @@ namespace RC auto UE4SSProgram::install_cpp_mods() -> void { - install_mods(m_mods); + install_mods(get_program().m_mods); } auto UE4SSProgram::install_lua_mods() -> void { - install_mods(m_mods); + install_mods(get_program().m_mods); } auto UE4SSProgram::fire_unreal_init_for_cpp_mods() -> void @@ -1106,7 +1194,7 @@ namespace RC } } - auto UE4SSProgram::fire_dll_load_for_cpp_mods(std::wstring_view dll_name) -> void + auto UE4SSProgram::fire_dll_load_for_cpp_mods(SystemStringViewType dll_name) -> void { for (const auto& mod : m_mods) { @@ -1122,24 +1210,24 @@ namespace RC { ProfilerScope(); // Part #1: Start all mods that are enabled in mods.txt. - Output::send(STR("Starting mods (from mods.txt load order)...\n")); + Output::send(SYSSTR("Starting mods (from mods.txt load order)...\n")); std::filesystem::path mods_directory = UE4SSProgram::get_program().get_mods_directory(); - std::wstring enabled_mods_file{mods_directory / "mods.txt"}; - if (!std::filesystem::exists(enabled_mods_file)) + auto enabled_mods_file = File::get_path_if_exists(mods_directory, "mods.txt"); + if (!enabled_mods_file.has_value()) { - Output::send(STR("No mods.txt file found...\n")); + Output::send(SYSSTR("No mods.txt file found...\n")); } else { // 'mods.txt' exists, lets parse it - std::wifstream mods_stream{enabled_mods_file}; + SystemStreamType mods_stream{*enabled_mods_file}; - std::wstring current_line; + SystemStringType current_line; while (std::getline(mods_stream, current_line)) { // Don't parse any lines with ';' - if (current_line.find(L";") != current_line.npos) + if (current_line.find(SYSSTR(";")) != current_line.npos) { continue; } @@ -1151,12 +1239,12 @@ namespace RC } // Remove all spaces - auto end = std::remove(current_line.begin(), current_line.end(), L' '); + auto end = std::remove(current_line.begin(), current_line.end(), SYSSTR(' ')); current_line.erase(end, current_line.end()); // Parse the line into something that can be converted into proper data - std::wstring mod_name = explode_by_occurrence(current_line, L':', 1); - std::wstring mod_enabled = explode_by_occurrence(current_line, L':', ExplodeType::FromEnd); + SystemStringType mod_name = explode_by_occurrence(current_line, SYSSTR(':'), 1); + SystemStringType mod_enabled = explode_by_occurrence(current_line, SYSSTR(':'), ExplodeType::FromEnd); auto mod = UE4SSProgram::find_mod_by_name(mod_name, UE4SSProgram::IsInstalled::Yes); if (!mod || !dynamic_cast(mod)) @@ -1164,20 +1252,20 @@ namespace RC continue; } - if (!mod_enabled.empty() && mod_enabled[0] == L'1') + if (!mod_enabled.empty() && mod_enabled[0] == SYSSTR('1')) { - Output::send(STR("Starting {} mod '{}'\n"), std::is_same_v ? STR("Lua") : STR("C++"), mod->get_name().data()); + Output::send(SYSSTR("Starting {} mod '{}'\n"), std::is_same_v ? SYSSTR("Lua") : SYSSTR("C++"), mod->get_name().data()); mod->start_mod(); } else { - Output::send(STR("Mod '{}' disabled in mods.txt.\n"), mod_name); + Output::send(SYSSTR("Mod '{}' disabled in mods.txt.\n"), mod_name); } } } // Part #2: Start all mods that have enabled.txt present in the mod directory. - Output::send(STR("Starting mods (from enabled.txt, no defined load order)...\n")); + Output::send(SYSSTR("Starting mods (from enabled.txt, no defined load order)...\n")); for (const auto& mod_directory : std::filesystem::directory_iterator(mods_directory)) { @@ -1192,23 +1280,21 @@ namespace RC return std::format("is_directory ran into error {}", ec.value()); } - if (!std::filesystem::exists(mod_directory.path() / "enabled.txt", ec)) + auto enabled = File::get_path_if_exists(mod_directory.path(), "enabled.txt"); + + if (!enabled.has_value()) { continue; } - if (ec.value() != 0) - { - return std::format("exists ran into error {}", ec.value()); - } - auto mod = UE4SSProgram::find_mod_by_name(mod_directory.path().stem().c_str(), UE4SSProgram::IsInstalled::Yes); + auto mod = UE4SSProgram::find_mod_by_name(to_system_string(mod_directory.path().stem()), UE4SSProgram::IsInstalled::Yes); if (!dynamic_cast(mod)) { continue; } if (!mod) { - Output::send(STR("Found a mod with enabled.txt but mod has not been installed properly.\n")); + Output::send(SYSSTR("Found a mod with enabled.txt but mod has not been installed properly.\n")); continue; } @@ -1217,7 +1303,7 @@ namespace RC continue; } - Output::send(STR("Mod '{}' has enabled.txt, starting mod.\n"), mod->get_name().data()); + Output::send(SYSSTR("Mod '{}' has enabled.txt, starting mod.\n"), mod->get_name().data()); mod->start_mod(); } @@ -1242,6 +1328,8 @@ namespace RC { set_error(error_message.c_str()); } + +#ifdef HAS_UI // If this is the initial startup, notify mods that the UI has initialized. // This isn't completely accurate since the UI will usually have started a while ago. // However, we can't immediately notify mods of this because no mods have been started at that point. @@ -1250,11 +1338,13 @@ namespace RC { fire_ui_init_for_cpp_mods(); } +#endif } auto UE4SSProgram::uninstall_mods() -> void { ProfilerScope(); + std::vector cpp_mods{}; std::vector lua_mods{}; for (auto& mod : m_mods) @@ -1279,7 +1369,6 @@ namespace RC { mod->uninstall(); } - m_mods.clear(); LuaMod::global_uninstall(); } @@ -1292,20 +1381,20 @@ namespace RC auto UE4SSProgram::reinstall_mods() -> void { ProfilerScope(); - Output::send(STR("Re-installing all mods\n")); + Output::send(SYSSTR("Re-installing all mods\n")); // Stop processing events while stuff isn't properly setup m_pause_events_processing = true; uninstall_mods(); - // Remove key binds that were set from Lua scripts - auto& key_events = m_input_handler.get_events(); - std::erase_if(key_events, [](Input::KeySet& input_event) -> bool { - bool were_all_events_registered_from_lua = true; - for (auto& [key, vector_of_key_data] : input_event.key_data) - { - std::erase_if(vector_of_key_data, [&](Input::KeyData& key_data) -> bool { +// Remove key binds that were set from Lua scripts +#ifdef HAS_INPUT + m_input_handler.get_events_safe([&](auto& key_set) { + std::erase_if(key_set.key_data, [&](auto& item) -> bool { + auto& [_, key_data] = item; + bool were_all_events_registered_from_lua = true; + std::erase_if(key_data, [&](Input::KeyData& key_data) -> bool { // custom_data == 1: Bind came from Lua, and custom_data2 is nullptr. // custom_data == 2: Bind came from C++, and custom_data2 is a pointer to KeyDownEventData. Must free it. if (key_data.custom_data == 1) @@ -1318,10 +1407,11 @@ namespace RC return false; } }); - } - return were_all_events_registered_from_lua; + return were_all_events_registered_from_lua; + }); }); +#endif // Remove all custom properties // Uncomment when custom properties are working @@ -1348,38 +1438,42 @@ namespace RC fire_program_start_for_cpp_mods(); } - Output::send(STR("All mods re-installed\n")); + Output::send(SYSSTR("All mods re-installed\n")); } - auto UE4SSProgram::get_module_directory() -> File::StringViewType + auto UE4SSProgram::get_module_directory() -> SystemStringViewType { - return m_module_file_path.c_str(); + m_module_file_path_str = to_system_string(m_module_file_path); + return m_module_file_path_str; } - auto UE4SSProgram::get_game_executable_directory() -> File::StringViewType + auto UE4SSProgram::get_working_directory() -> SystemStringViewType { - return m_game_executable_directory.c_str(); + m_working_directory_str = to_system_string(m_working_directory); + return m_working_directory_str; } - auto UE4SSProgram::get_working_directory() -> File::StringViewType + auto UE4SSProgram::get_mods_directory() -> SystemStringViewType { - return m_working_directory.c_str(); + m_mods_directory_str = to_system_string(m_mods_directory); + return m_mods_directory_str; } - auto UE4SSProgram::get_mods_directory() -> File::StringViewType + auto UE4SSProgram::get_legacy_root_directory() -> SystemStringViewType { - return m_mods_directory.c_str(); + return m_legacy_root_directory.c_str(); } - - auto UE4SSProgram::get_legacy_root_directory() -> File::StringViewType + + auto UE4SSProgram::get_game_directory() -> SystemStringViewType { - return m_legacy_root_directory.c_str(); + m_game_executable_str = to_system_string(m_game_executable_directory); + return m_game_executable_str; } auto UE4SSProgram::generate_uht_compatible_headers() -> void { ProfilerScope(); - Output::send(STR("Generating UHT compatible headers...\n")); + Output::send(SYSSTR("Generating UHT compatible headers...\n")); double generator_duration{}; { @@ -1390,7 +1484,7 @@ namespace RC HeaderGenerator.dump_native_packages(); } - Output::send(STR("Generating UHT compatible headers took {} seconds\n"), generator_duration); + Output::send(SYSSTR("Generating UHT compatible headers took {} seconds\n"), generator_duration); } auto UE4SSProgram::generate_cxx_headers(const std::filesystem::path& output_dir) -> void @@ -1398,7 +1492,7 @@ namespace RC ProfilerScope(); if (settings_manager.CXXHeaderGenerator.LoadAllAssetsBeforeGeneratingCXXHeaders) { - Output::send(STR("Loading all assets...\n")); + Output::send(SYSSTR("Loading all assets...\n")); double asset_loading_duration{}; { ProfilerScopeNamed("loading all assets"); @@ -1406,7 +1500,7 @@ namespace RC UAssetRegistry::LoadAllAssets(); } - Output::send(STR("Loading all assets took {} seconds\n"), asset_loading_duration); + Output::send(SYSSTR("Loading all assets took {} seconds\n"), asset_loading_duration); } double generator_duration; @@ -1416,11 +1510,11 @@ namespace RC UEGenerator::generate_cxx_headers(output_dir); - Output::send(STR("Unloading all forcefully loaded assets\n")); + Output::send(SYSSTR("Unloading all forcefully loaded assets\n")); } UAssetRegistry::FreeAllForcefullyLoadedAssets(); - Output::send(STR("SDK generated in {} seconds.\n"), generator_duration); + Output::send(SYSSTR("SDK generated in {} seconds.\n"), generator_duration); } auto UE4SSProgram::generate_lua_types(const std::filesystem::path& output_dir) -> void @@ -1428,7 +1522,7 @@ namespace RC ProfilerScope(); if (settings_manager.CXXHeaderGenerator.LoadAllAssetsBeforeGeneratingCXXHeaders) { - Output::send(STR("Loading all assets...\n")); + Output::send(SYSSTR("Loading all assets...\n")); double asset_loading_duration{}; { ProfilerScopeNamed("loading all assets"); @@ -1436,7 +1530,7 @@ namespace RC UAssetRegistry::LoadAllAssets(); } - Output::send(STR("Loading all assets took {} seconds\n"), asset_loading_duration); + Output::send(SYSSTR("Loading all assets took {} seconds\n"), asset_loading_duration); } double generator_duration; @@ -1446,13 +1540,13 @@ namespace RC UEGenerator::generate_lua_types(output_dir); - Output::send(STR("Unloading all forcefully loaded assets\n")); + Output::send(SYSSTR("Unloading all forcefully loaded assets\n")); } UAssetRegistry::FreeAllForcefullyLoadedAssets(); - Output::send(STR("SDK generated in {} seconds.\n"), generator_duration); + Output::send(SYSSTR("SDK generated in {} seconds.\n"), generator_duration); } - +#ifdef HAS_UI auto UE4SSProgram::stop_render_thread() -> void { if (m_render_thread.joinable()) @@ -1471,6 +1565,7 @@ namespace RC { m_debugging_gui.remove_tab(tab); } +#endif auto UE4SSProgram::queue_event(EventCallable callable, void* data) -> void { @@ -1488,6 +1583,7 @@ namespace RC return m_queued_events.empty(); } +#ifdef HAS_INPUT auto UE4SSProgram::register_keydown_event(Input::Key key, const Input::EventCallbackCallable& callback, uint8_t custom_data, void* custom_data2) -> void { m_input_handler.register_keydown_event(key, callback, custom_data, custom_data2); @@ -1511,11 +1607,11 @@ namespace RC { return m_input_handler.is_keydown_event_registered(key, modifier_keys); } - - auto UE4SSProgram::find_mod_by_name_internal(std::wstring_view mod_name, IsInstalled is_installed, IsStarted is_started, FMBNI_ExtraPredicate extra_predicate) +#endif + auto UE4SSProgram::find_mod_by_name_internal(SystemStringViewType mod_name, IsInstalled is_installed, IsStarted is_started, FMBNI_ExtraPredicate extra_predicate) -> Mod* { - auto mod_exists_with_name = std::find_if(m_mods.begin(), m_mods.end(), [&](auto& elem) -> bool { + auto mod_exists_with_name = std::find_if(get_program().m_mods.begin(), get_program().m_mods.end(), [&](auto& elem) -> bool { bool found = true; if (!extra_predicate(elem.get())) @@ -1539,7 +1635,7 @@ namespace RC }); // clang-format off - if (mod_exists_with_name == m_mods.end()) + if (mod_exists_with_name == get_program().m_mods.end()) { return nullptr; } @@ -1550,22 +1646,12 @@ namespace RC } } - auto UE4SSProgram::find_lua_mod_by_name(std::string_view mod_name, UE4SSProgram::IsInstalled installed_only, IsStarted is_started) -> LuaMod* - { - return static_cast(find_mod_by_name(mod_name, installed_only, is_started)); - } - - auto UE4SSProgram::find_lua_mod_by_name(std::wstring_view mod_name, UE4SSProgram::IsInstalled installed_only, IsStarted is_started) -> LuaMod* - { - return static_cast(find_mod_by_name(mod_name, installed_only, is_started)); - } - - auto UE4SSProgram::get_object_dumper_output_directory() -> const File::StringType + auto UE4SSProgram::get_object_dumper_output_directory() -> const SystemStringType { - return m_object_dumper_output_directory.c_str(); + return to_system_string(m_object_dumper_output_directory); } - auto UE4SSProgram::dump_uobject(UObject* object, std::unordered_set* in_dumped_fields, StringType& out_line, bool is_below_425) -> void + auto UE4SSProgram::dump_uobject(UObject* object, std::unordered_set* in_dumped_fields, SystemStringType& out_line, bool is_below_425) -> void { bool owns_dumped_fields{}; auto dumped_fields_ptr = [&] { @@ -1599,7 +1685,7 @@ namespace RC // Dump UObject ObjectDumper::get_to_string(typed_class)(object, out_line); - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); if (!is_below_425 && ObjectDumper::to_string_complex_exists(typed_class)) { @@ -1620,7 +1706,7 @@ namespace RC { // A type-specific implementation does not exist so lets call the default implementation for UObjects instead ObjectDumper::object_to_string(object, out_line); - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); } // If the UClass of the UObject has any properties then dump them @@ -1649,50 +1735,50 @@ namespace RC } } - auto UE4SSProgram::dump_xproperty(FProperty* property, StringType& out_line) -> void + auto UE4SSProgram::dump_xproperty(FProperty* property, SystemStringType& out_line) -> void { auto typed_prop_class = property->GetClass().HashObject(); if (ObjectDumper::to_string_exists(typed_prop_class)) { ObjectDumper::get_to_string(typed_prop_class)(property, out_line); - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); if (ObjectDumper::to_string_complex_exists(typed_prop_class)) { ObjectDumper::get_to_string_complex(typed_prop_class)(property, out_line, [&]([[maybe_unused]] void* prop) { - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); }); } } else { ObjectDumper::property_to_string(property, out_line); - out_line.append(L"\n"); + out_line.append(SYSSTR("\n")); } } - auto UE4SSProgram::dump_all_objects_and_properties(const File::StringType& output_path_and_file_name) -> void + auto UE4SSProgram::dump_all_objects_and_properties(const SystemStringType& output_path_and_file_name) -> void { /* - Output::send(STR("Test msg with no fmt args, and no optional arg\n")); - Output::send(STR("Test msg with no fmt args, and one optional arg [Normal]\n"), LogLevel::Normal); - Output::send(STR("Test msg with no fmt args, and one optional arg [Verbose]\n"), LogLevel::Verbose); - Output::send(STR("Test msg with one fmt arg [{}], and one optional arg [Warning]\n"), LogLevel::Warning, 33); - Output::send(STR("Test msg with two fmt args [{}, {}], and one optional arg [Error]\n"), LogLevel::Error, 33, 44); + Output::send(SYSSTR("Test msg with no fmt args, and no optional arg\n")); + Output::send(SYSSTR("Test msg with no fmt args, and one optional arg [Normal]\n"), LogLevel::Normal); + Output::send(SYSSTR("Test msg with no fmt args, and one optional arg [Verbose]\n"), LogLevel::Verbose); + Output::send(SYSSTR("Test msg with one fmt arg [{}], and one optional arg [Warning]\n"), LogLevel::Warning, 33); + Output::send(SYSSTR("Test msg with two fmt args [{}, {}], and one optional arg [Error]\n"), LogLevel::Error, 33, 44); //*/ // Object & Property Dumper -> START if (settings_manager.ObjectDumper.LoadAllAssetsBeforeDumpingObjects) { - Output::send(STR("Loading all assets...\n")); + Output::send(SYSSTR("Loading all assets...\n")); double asset_loading_duration{}; { ScopedTimer loading_timer{&asset_loading_duration}; UAssetRegistry::LoadAllAssets(); } - Output::send(STR("Loading all assets took {} seconds\n"), asset_loading_duration); + Output::send(SYSSTR("Loading all assets took {} seconds\n"), asset_loading_duration); } double dumper_duration{}; @@ -1718,16 +1804,16 @@ namespace RC Output::Targets scoped_dumper_out; auto& file_device = scoped_dumper_out.get_device(); file_device.set_file_name_and_path(output_path_and_file_name); - file_device.set_formatter([](File::StringViewType string) -> File::StringType { - return File::StringType{string}; + file_device.set_formatter([](SystemStringViewType string) -> SystemStringType { + return SystemStringType{string}; }); // Make string & reserve massive amounts of space to hopefully not reach the end of the string and require more // dynamic allocations - std::wstring out_line; + SystemStringType out_line; out_line.reserve(200000000); - Output::send(STR("Dumping all objects & properties in GUObjectArray\n")); + Output::send(SYSSTR("Dumping all objects & properties in GUObjectArray\n")); UObjectGlobals::ForEachUObject([&](void* object, [[maybe_unused]] int32_t chunk_index, [[maybe_unused]] int32_t object_index) { dump_uobject(static_cast(object), &dumped_fields, out_line, is_below_425); return LoopAction::Continue; @@ -1738,11 +1824,11 @@ namespace RC // Reset the dumped_fields set, otherwise no fields will be dumped in subsequent dumps dumped_fields.clear(); - Output::send(STR("Done iterating GUObjectArray\n")); + Output::send(SYSSTR("Done iterating GUObjectArray\n")); } UAssetRegistry::FreeAllForcefullyLoadedAssets(); - Output::send(STR("Dumping GUObjectArray took {} seconds\n"), dumper_duration); + Output::send(SYSSTR("Dumping GUObjectArray took {} seconds\n"), dumper_duration); // Object & Property Dumper -> END } diff --git a/UE4SS/src/USMapGenerator/Generator.cpp b/UE4SS/src/USMapGenerator/Generator.cpp index 78d5517c8..d1e783ec3 100644 --- a/UE4SS/src/USMapGenerator/Generator.cpp +++ b/UE4SS/src/USMapGenerator/Generator.cpp @@ -22,6 +22,8 @@ #include #include "UE4SSProgram.hpp" +#undef min +#undef max namespace RC::OutTheShade { @@ -200,8 +202,8 @@ namespace RC::OutTheShade auto generate_usmap() -> void { - Output::send(STR("Mappings Generator by OutTheShade\nAttempting to dump mappings...\nPort of https://github.com/OutTheShade/UnrealMappingsDumper " - "Commit SHA 4da8c66\n")); + Output::send(SYSSTR("Mappings Generator by OutTheShade\nAttempting to dump mappings...\nPort of https://github.com/OutTheShade/UnrealMappingsDumper " + "Commit SHA 4da8c66\n")); StreamWriter Buffer; std::unordered_map NameMap; @@ -299,11 +301,11 @@ namespace RC::OutTheShade if (Object->GetClassPrivate() == UClass::StaticClass() || Object->GetClassPrivate() == UScriptStruct::StaticClass() || Object->GetClassPrivate() == UEnum::StaticClass()) { - std::wstring RawPathName = Object->GetPathName(); - std::wstring::size_type PathNameStart = + UEStringType RawPathName = Object->GetPathName(); + UEStringType::size_type PathNameStart = 0; // include first bit (Script/Game) to avoid ambiguity; to drop it, replace with RawPathName.find_first_of('/', 1) + 1; - std::wstring::size_type PathNameLength = RawPathName.find_last_of('.') - PathNameStart; - std::wstring FinalPathStr = RawPathName.substr(PathNameStart, PathNameLength); + UEStringType::size_type PathNameLength = RawPathName.find_last_of('.') - PathNameStart; + UEStringType FinalPathStr = RawPathName.substr(PathNameStart, PathNameLength); FName FinalPathName = FName(FinalPathStr); NameMap.insert_or_assign(FinalPathName, 0); @@ -518,6 +520,6 @@ namespace RC::OutTheShade FileOutput.Write(UsmapData.data(), UsmapData.size()); - Output::send(STR("Mappings Generation Completed Successfully!\n")); + Output::send(SYSSTR("Mappings Generation Completed Successfully!\n")); } } // namespace RC::OutTheShade diff --git a/UE4SS/xmake.lua b/UE4SS/xmake.lua index 8ccee3fcf..0b5af959b 100644 --- a/UE4SS/xmake.lua +++ b/UE4SS/xmake.lua @@ -1,11 +1,22 @@ -includes("proxy_generator") - -add_requires("imgui v1.89", { debug = is_mode_debug(), configs = { win32 = true, dx11 = true, opengl3 = true, glfw_opengl3 = true , runtimes = get_mode_runtimes()} } ) -add_requires("ImGuiTextEdit v1.0", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) -add_requires("IconFontCppHeaders v1.0", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()}}) -add_requires("glfw 3.3.9", { debug = is_mode_debug() , configs = {runtimes = get_mode_runtimes()}}) -add_requires("opengl", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) -add_requires("glaze", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) +local hasWindows = is_plat("windows") +local uiMode = get_config("ue4ssUI") + +if uiMode ~= nil and uiMode ~= "None" then + local isTUI = uiMode == "TUI" + add_requires("ImGuiTextEdit v1.0", { debug = is_mode_debug(), configs = { tui = isTUI, runtimes = get_mode_runtimes() } }) + + if uiMode == "GUI" then + local imguiConfig = { win32 = hasWindows, dx11 = hasWindows, opengl3 = true, glfw = true , runtimes = get_mode_runtimes()} + add_requires("imgui v1.89", { alias = "ue4ssImGui", debug = is_mode_debug(), configs = imguiConfig } ) + add_requireconfs("ImGuiTextEdit.imgui", { configs = imguiConfig }) + + add_requires("IconFontCppHeaders v1.0", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()}}) + add_requires("glfw 3.3.9", { debug = is_mode_debug() , configs = {runtimes = get_mode_runtimes()}}) + add_requires("opengl", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) + elseif uiMode == "TUI" then + add_requires("imtui v1.0.5", { debug = is_mode_debug(), configs = { runtimes = get_mode_runtimes() } }) + end +end option("ue4ssBetaIsStarted") set_default(true) @@ -23,6 +34,48 @@ option("ue4ssIsBeta") set_description("Is this a beta release") +option("ue4ssUI") + set_default("GUI") + set_showmenu(true) + + set_values("None", "GUI", "TUI") + + set_description("UE4SS GUI Modes. TUI is not available on windows") + + after_check(function (option) + local value = option:value() + + if value == "TUI" and is_plat("windows") then + raise("TUI is not available on windows") + end + + if value ~= "None" then + option:add("defines", "HAS_UI") + end + + if value == "GUI" then + option:add("defines", "HAS_GUI") + elseif value == "TUI" then + option:add("defines", "HAS_TUI") + end + end) + +option("ue4ssInput") + set_default(true) + set_showmenu(true) + + add_deps("ue4ssUI") + add_defines("HAS_INPUT") + + set_description("Enable the input system.") + + after_check(function (option) + local noUI = option:dep("ue4ssUI"):value() == "None" + if noUI and not is_plat("windows") then + option:enable(false) + end + end) + local projectName = "UE4SS" local function parse_version_string(str) @@ -46,29 +99,64 @@ target(projectName) set_exceptions("cxx") set_default(true) add_rules("ue4ss.defines.exports") - add_options("ue4ssBetaIsStarted", "ue4ssIsBeta") + add_options("ue4ssBetaIsStarted", "ue4ssIsBeta", "ue4ssUI", "ue4ssInput") add_includedirs("include", { public = true }) add_includedirs("generated_include", { public = true }) add_headerfiles("include/**.hpp") add_headerfiles("generated_include/*.hpp") - add_files("src/**.cpp") + add_files("src/**.cpp|Platform/**.cpp|GUI/**.cpp") + + if is_plat("windows") then + add_files("src/Platform/Win32/CrashDumper.cpp", "src/Platform/Win32/EntryWin32.cpp") + add_links("dbghelp", "psapi", { public = true }) + elseif is_plat("linux") then + add_files("src/Platform/Linux/EntryLinux.cpp") + add_shflags("-Wl,-soname,libUE4SS.so") + end + + if uiMode ~= "None" then + add_files("src/GUI/*.cpp") + + add_packages("ImGuiTextEdit", { public = true }) + + if uiMode == "GUI" then + add_files("src/GUI/Platform/GLFW/**.cpp") + + add_defines("HAS_GLFW", { public = true }) + + add_deps("glad", { public = true }) + + add_packages("ue4ssImGui", "IconFontCppHeaders", "glfw", "opengl", { public = true }) + + if is_plat("windows") then + add_files("src/GUI/Platform/D3D11/**.cpp") + add_files("src/GUI/Platform/Windows/**.cpp") + + add_defines("HAS_D3D11", { public = true }) + + add_links("d3d11", { public = true }) + end + elseif uiMode == "TUI" then + add_files("src/GUI/Platform/TUI/**.cpp") + + add_packages("imtui", { public = true }) + end + end add_deps( "File", "DynamicOutput", "Unreal", "SinglePassSigScanner", "LuaMadeSimple", "Function", - "IniParser", "JSON", "Input", + "IniParser", "JSON", "Constructs", "Helpers", "MProgram", "ScopedTimer", "Profiler", "patternsleuth_bind", - "glad", { public = true } + { public = true } ) - add_packages("imgui", "ImGuiTextEdit", "IconFontCppHeaders", "glfw", "opengl", { public = true }) + add_deps("Input", { public = true }) add_packages("glaze", "polyhook_2", { public = true }) - add_links("dbghelp", "psapi", "d3d11", { public = true }) - after_load(function (target) local projectRoot = get_config("ue4ssRoot") local version_string = io.readfile(path.join(target:scriptdir(), "generated_src/version.cache")) diff --git a/UVTD/include/UVTD/ExceptionHandling.hpp b/UVTD/include/UVTD/ExceptionHandling.hpp index ed3c0747f..a691a453d 100644 --- a/UVTD/include/UVTD/ExceptionHandling.hpp +++ b/UVTD/include/UVTD/ExceptionHandling.hpp @@ -21,7 +21,7 @@ namespace RC { if (!Output::has_internal_error()) { - Output::send(STR("Error: {}\n"), to_wstring(e.what())); + Output::send(SYSSTR("Error: {}\n"), to_wstring(e.what())); } else { diff --git a/UVTD/include/UVTD/Helpers.hpp b/UVTD/include/UVTD/Helpers.hpp index f1087eec0..fdb1d9da9 100644 --- a/UVTD/include/UVTD/Helpers.hpp +++ b/UVTD/include/UVTD/Helpers.hpp @@ -16,23 +16,23 @@ namespace RC::UVTD { static std::filesystem::path vtable_gen_output_path = "GeneratedVTables"; - static std::filesystem::path vtable_gen_output_include_path = vtable_gen_output_path / "generated_include"; + static std::filesystem::path vtable_gen_output_include_path = vtable_gen_output_path / "generated_include" / "Windows"; static std::filesystem::path vtable_gen_output_src_path = vtable_gen_output_path / "generated_src"; static std::filesystem::path vtable_gen_output_function_bodies_path = vtable_gen_output_include_path / "FunctionBodies"; static std::filesystem::path vtable_templates_output_path = "VTableLayoutTemplates"; static std::filesystem::path virtual_gen_output_path = "GeneratedVirtualImplementations"; - static std::filesystem::path virtual_gen_output_include_path = virtual_gen_output_path / "generated_include"; + static std::filesystem::path virtual_gen_output_include_path = virtual_gen_output_path / "generated_include" / "Windows"; static std::filesystem::path virtual_gen_function_bodies_path = virtual_gen_output_include_path / "FunctionBodies"; static std::filesystem::path member_variable_layouts_gen_output_path = "GeneratedMemberVariableLayouts"; - static std::filesystem::path member_variable_layouts_gen_output_include_path = member_variable_layouts_gen_output_path / "generated_include"; + static std::filesystem::path member_variable_layouts_gen_output_include_path = member_variable_layouts_gen_output_path / "generated_include" / "Windows"; static std::filesystem::path member_variable_layouts_gen_output_src_path = member_variable_layouts_gen_output_path / "generated_src"; static std::filesystem::path member_variable_layouts_gen_function_bodies_path = member_variable_layouts_gen_output_include_path / "FunctionBodies"; static std::filesystem::path member_variable_layouts_templates_output_path = "MemberVarLayoutTemplates"; struct ObjectItem { - File::StringType name{}; + UEStringType name{}; ValidForVTable valid_for_vtable{}; ValidForMemberVars valid_for_member_vars{}; }; @@ -105,7 +105,7 @@ namespace RC::UVTD {STR("FFixedUObjectArray"), ValidForVTable::No, ValidForMemberVars::Yes}, {STR("FUObjectItem"), ValidForVTable::No, ValidForMemberVars::Yes}}; - static inline std::unordered_map> s_private_variables{ + static inline std::unordered_map> s_private_variables{ {STR("FField"), { STR("ClassPrivate"), @@ -115,73 +115,73 @@ namespace RC::UVTD }}, }; - static std::unordered_set s_non_case_preserving_variants{ + static std::unordered_set s_non_case_preserving_variants{ {STR("4_27")}, }; - static std::unordered_set s_case_preserving_variants{ + static std::unordered_set s_case_preserving_variants{ {STR("4_27_CasePreserving")}, }; - static inline std::unordered_set s_valid_udt_names{STR("UScriptStruct::ICppStructOps"), - STR("UObjectBase"), - STR("UObjectBaseUtility"), - STR("UObject"), - STR("UStruct"), - STR("UGameViewportClient"), - STR("UScriptStruct"), - STR("FOutputDevice"), - // STR("UConsole"), - STR("FMalloc"), - STR("FArchive"), - STR("FArchiveState"), - STR("AGameModeBase"), - STR("AGameMode"), - STR("AActor"), - STR("AHUD"), - STR("UPlayer"), - STR("ULocalPlayer"), - STR("FExec"), - STR("UField"), - STR("FField"), - STR("FProperty"), - STR("UProperty"), - STR("FNumericProperty"), - STR("UNumericProperty"), - STR("FMulticastDelegateProperty"), - STR("UMulticastDelegateProperty"), - STR("FObjectPropertyBase"), - STR("UObjectPropertyBase"), - STR("UStructProperty"), - STR("FStructProperty"), - STR("UArrayProperty"), - STR("FArrayProperty"), - STR("UMapProperty"), - STR("FMapProperty"), - STR("UWorld"), - STR("UFunction"), - STR("FBoolProperty"), - STR("UClass"), - STR("UEnum"), - STR("UBoolProperty"), - STR("FByteProperty"), - STR("UByteProperty"), - STR("FEnumProperty"), - STR("UEnumProperty"), - STR("FClassProperty"), - STR("UClassProperty"), - STR("FSoftClassProperty"), - STR("USoftClassProperty"), - STR("FDelegateProperty"), - STR("UDelegateProperty"), - STR("FInterfaceProperty"), - STR("UInterfaceProperty"), - STR("FFieldPathProperty"), - STR("FSetProperty"), - STR("USetProperty"), - STR("FFrame")}; - - static inline std::vector s_uprefix_to_fprefix{ + static inline std::unordered_set s_valid_udt_names{STR("UScriptStruct::ICppStructOps"), + STR("UObjectBase"), + STR("UObjectBaseUtility"), + STR("UObject"), + STR("UStruct"), + STR("UGameViewportClient"), + STR("UScriptStruct"), + STR("FOutputDevice"), + // STR("UConsole"), + STR("FMalloc"), + STR("FArchive"), + STR("FArchiveState"), + STR("AGameModeBase"), + STR("AGameMode"), + STR("AActor"), + STR("AHUD"), + STR("UPlayer"), + STR("ULocalPlayer"), + STR("FExec"), + STR("UField"), + STR("FField"), + STR("FProperty"), + STR("UProperty"), + STR("FNumericProperty"), + STR("UNumericProperty"), + STR("FMulticastDelegateProperty"), + STR("UMulticastDelegateProperty"), + STR("FObjectPropertyBase"), + STR("UObjectPropertyBase"), + STR("UStructProperty"), + STR("FStructProperty"), + STR("UArrayProperty"), + STR("FArrayProperty"), + STR("UMapProperty"), + STR("FMapProperty"), + STR("UWorld"), + STR("UFunction"), + STR("FBoolProperty"), + STR("UClass"), + STR("UEnum"), + STR("UBoolProperty"), + STR("FByteProperty"), + STR("UByteProperty"), + STR("FEnumProperty"), + STR("UEnumProperty"), + STR("FClassProperty"), + STR("UClassProperty"), + STR("FSoftClassProperty"), + STR("USoftClassProperty"), + STR("FDelegateProperty"), + STR("UDelegateProperty"), + STR("FInterfaceProperty"), + STR("UInterfaceProperty"), + STR("FFieldPathProperty"), + STR("FSetProperty"), + STR("USetProperty"), + STR("FFrame")}; + + static inline std::vector s_uprefix_to_fprefix{ STR("UProperty"), STR("UMulticastDelegateProperty"), STR("UObjectPropertyBase"), @@ -200,17 +200,17 @@ namespace RC::UVTD }; // Any members by the name 'LHS' will instead use 'RHS' in generated code. - static inline std::unordered_map s_member_rename_map{ + static inline std::unordered_map s_member_rename_map{ {STR("Class"), STR("ClassPrivate")}, {STR("Name"), STR("NamePrivate")}, {STR("Outer"), STR("OuterPrivate")}, }; - auto to_string_type(const char* c_str) -> File::StringType; - auto change_prefix(File::StringType input, bool is_425_plus) -> std::optional; + auto to_string_type(const char* c_str) -> UEStringType; + auto change_prefix(UEStringType input, bool is_425_plus) -> std::optional; // Workaround that lets us have a unified 'TUObjectArray' struct regardless if the engine version uses a chunked or non-chunked variant of TUObjectArray. - auto unify_uobject_array_if_needed(StringType& out_variable_type) -> bool; + auto unify_uobject_array_if_needed(UEStringType& out_variable_type) -> bool; } // namespace RC::UVTD #endif diff --git a/UVTD/include/UVTD/MemberVarsDumper.hpp b/UVTD/include/UVTD/MemberVarsDumper.hpp index f6868fcf5..4637d4560 100644 --- a/UVTD/include/UVTD/MemberVarsDumper.hpp +++ b/UVTD/include/UVTD/MemberVarsDumper.hpp @@ -24,12 +24,12 @@ namespace RC::UVTD private: auto process_class(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* class_record, - const File::StringType& class_name, + const UEStringType& class_name, const SymbolNameInfo& name_info) -> void; auto process_member(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::FieldList* field_record, Class& class_entry) -> void; private: - auto dump_member_variable_layouts(std::unordered_map& names) -> void; + auto dump_member_variable_layouts(std::unordered_map& names) -> void; public: auto generate_code() -> void; diff --git a/UVTD/include/UVTD/Symbols.hpp b/UVTD/include/UVTD/Symbols.hpp index dfcf8f577..4c1797b58 100644 --- a/UVTD/include/UVTD/Symbols.hpp +++ b/UVTD/include/UVTD/Symbols.hpp @@ -8,6 +8,9 @@ #include +#include +#include + #include #include #include @@ -45,25 +48,25 @@ namespace RC::UVTD struct MemberVariable { - File::StringType type; - File::StringType name; + UEStringType type; + UEStringType name; int32_t offset; }; struct FunctionParam { - File::StringType type; + UEStringType type; auto to_string() const -> File::StringType { - return std::format(STR("{}"), type); + return std::format(IOSTR("{}"), to_file(type)); } }; struct MethodSignature { - File::StringType return_type; - File::StringType name; + UEStringType return_type; + UEStringType name; std::vector params; bool const_qualifier; @@ -74,16 +77,16 @@ namespace RC::UVTD for (size_t i = 0; i < params.size(); i++) { bool should_add_comma = i < params.size() - 1; - params_string.append(std::format(STR("{}{}"), params[i].to_string(), should_add_comma ? STR(", ") : STR(""))); + params_string.append(fmtfile(IOSTR("{}{}"), params[i].to_string(), should_add_comma ? IOSTR(", ") : IOSTR(""))); } - return std::format(STR("{} {}({}){};"), return_type, name, params_string, const_qualifier ? STR("const") : STR("")); + return fmtfile(IOSTR("{} {}({}){};"), return_type, name, params_string, const_qualifier ? IOSTR("const") : IOSTR("")); } }; struct MethodBody { - File::StringType name; + UEStringType name; MethodSignature signature; uint32_t offset; bool is_overload; @@ -91,11 +94,11 @@ namespace RC::UVTD struct Class { - File::StringType class_name; - File::StringType class_name_clean; + UEStringType class_name; + UEStringType class_name_clean; std::map functions; // Key: Variable name - std::map variables; + std::map variables; uint32_t last_virtual_offset{}; ValidForVTable valid_for_vtable{ValidForVTable::No}; ValidForMemberVars valid_for_member_vars{ValidForMemberVars::No}; @@ -106,24 +109,24 @@ namespace RC::UVTD public: struct MemberVariable { - File::StringType type; + UEStringType type; int32_t offset; }; struct EnumEntry { - File::StringType name; - File::StringType name_clean; - std::map variables; + UEStringType name; + UEStringType name_clean; + std::map variables; }; struct Class { - File::StringType class_name; - File::StringType class_name_clean; + UEStringType class_name; + UEStringType class_name_clean; std::map functions; // Key: Variable name - std::map variables; + std::map variables; uint32_t last_virtual_offset; ValidForVTable valid_for_vtable{ValidForVTable::No}; ValidForMemberVars valid_for_member_vars{ValidForMemberVars::No}; @@ -151,18 +154,18 @@ namespace RC::UVTD Symbols& operator=(const Symbols& other); public: - auto get_or_create_enum_entry(const File::StringType& symbol_name, const File::StringType& symbol_name_clean) -> EnumEntry&; - auto get_or_create_class_entry(const File::StringType& symbol_name, const File::StringType& symbol_name_clean, const SymbolNameInfo& name_info) -> Class&; + auto get_or_create_enum_entry(const UEStringType& symbol_name, const UEStringType& symbol_name_clean) -> EnumEntry&; + auto get_or_create_class_entry(const UEStringType& symbol_name, const UEStringType& symbol_name_clean, const SymbolNameInfo& name_info) -> Class&; - auto generate_method_signature(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* function_record, File::StringType method_name) + auto generate_method_signature(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* function_record, UEStringType method_name) -> MethodSignature; public: - auto static get_type_name(const PDB::TPIStream& tpi_stream, uint32_t record_index, bool check_valid = false) -> File::StringType; - auto static get_method_name(const PDB::CodeView::TPI::FieldList* method_record) -> File::StringType; - auto static get_leaf_name(const char* data, PDB::CodeView::TPI::TypeRecordKind kind) -> File::StringType; + auto static get_type_name(const PDB::TPIStream& tpi_stream, uint32_t record_index, bool check_valid = false) -> UEStringType; + auto static get_method_name(const PDB::CodeView::TPI::FieldList* method_record) -> UEStringType; + auto static get_leaf_name(const char* data, PDB::CodeView::TPI::TypeRecordKind kind) -> UEStringType; - auto static clean_name(File::StringType name) -> File::StringType; + auto static clean_name(UEStringType name) -> UEStringType; auto static is_virtual(PDB::CodeView::TPI::MemberAttributes attributes) -> bool; diff --git a/UVTD/include/UVTD/TemplateClassParser.hpp b/UVTD/include/UVTD/TemplateClassParser.hpp index 1e6467747..f7ccf5e6f 100644 --- a/UVTD/include/UVTD/TemplateClassParser.hpp +++ b/UVTD/include/UVTD/TemplateClassParser.hpp @@ -9,14 +9,14 @@ namespace RC::UVTD { struct ParsedTemplateClass { - File::StringType class_name; - std::vector template_args; + UEStringType class_name; + std::vector template_args; }; class TemplateClassParser { public: - static ParsedTemplateClass Parse(File::StringViewType input); + static ParsedTemplateClass Parse(UEStringViewType input); }; } // namespace RC::UVTD diff --git a/UVTD/include/UVTD/TypeContainer.hpp b/UVTD/include/UVTD/TypeContainer.hpp index 21d483ce2..1106669f1 100644 --- a/UVTD/include/UVTD/TypeContainer.hpp +++ b/UVTD/include/UVTD/TypeContainer.hpp @@ -11,7 +11,7 @@ namespace RC::UVTD class TypeContainer { private: - using ClassEntries = std::unordered_map; + using ClassEntries = std::unordered_map; ClassEntries class_entries; @@ -25,7 +25,7 @@ namespace RC::UVTD } public: - auto get_or_create_class_entry(const File::StringType& symbol_name, const File::StringType& symbol_name_clean, const SymbolNameInfo& name_info) -> Class&; + auto get_or_create_class_entry(const UEStringType& symbol_name, const UEStringType& symbol_name_clean, const SymbolNameInfo& name_info) -> Class&; }; } // namespace RC::UVTD diff --git a/UVTD/include/UVTD/UnrealVirtualGenerator.hpp b/UVTD/include/UVTD/UnrealVirtualGenerator.hpp index a6a90df91..0a7547dfe 100644 --- a/UVTD/include/UVTD/UnrealVirtualGenerator.hpp +++ b/UVTD/include/UVTD/UnrealVirtualGenerator.hpp @@ -8,13 +8,13 @@ namespace RC::UVTD class UnrealVirtualGenerator { private: - File::StringType pdb_name; + SystemStringType pdb_name; TypeContainer type_container; public: UnrealVirtualGenerator() = delete; - explicit UnrealVirtualGenerator(File::StringType pdb_name, TypeContainer container) + explicit UnrealVirtualGenerator(SystemStringType pdb_name, TypeContainer container) : pdb_name(std::move(pdb_name)), type_container(std::move(container)) { } diff --git a/UVTD/include/UVTD/VTableDumper.hpp b/UVTD/include/UVTD/VTableDumper.hpp index e4993d7b3..f0c3848c8 100644 --- a/UVTD/include/UVTD/VTableDumper.hpp +++ b/UVTD/include/UVTD/VTableDumper.hpp @@ -39,14 +39,14 @@ namespace RC::UVTD private: auto process_class(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* class_record, - const File::StringType& class_name, + const UEStringType& class_name, const SymbolNameInfo& name_info) -> void; auto process_method_overload_list(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::FieldList* method_record, Class& class_entry) -> void; auto process_onemethod(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::FieldList* onemethod_record, Class& class_entry) -> void; private: - auto dump_vtable_for_symbol(std::unordered_map& names) -> void; + auto dump_vtable_for_symbol(std::unordered_map& names) -> void; public: static auto output_cleanup() -> void; diff --git a/UVTD/scripts/HeaderGen.py b/UVTD/scripts/HeaderGen.py new file mode 100755 index 000000000..aff549837 --- /dev/null +++ b/UVTD/scripts/HeaderGen.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python3 +# generate header file for C++, using DRAWF information from .debug files +# e.g., ./HeaderGen.py -s -g -t ../../deps/first/Unreal/generated_include/Linux/FunctionBodies //UnrealGame-Linux-Shipping.debug +import os +import sys +import re +import pickle + +DEFAULT_FILENAME = "./UnrealGame-Linux-Shipping.debug" +DEFAULT_SEARCH_DIR = "../../deps/first/Unreal/generated_include/Windows" +DEFAULT_GENERATED_DIR = "../generated" + +FUNCTION_TEMPLATE = """if (auto it = {ClassName}::VTableLayoutMap.find(STR("{FunctionName}")); it == {ClassName}::VTableLayoutMap.end()) +{{ + {ClassName}::VTableLayoutMap.emplace(STR("{FunctionName}"), 0x{FunctionOffset:02x}); +}} +""" + +MEMBER_TEMPLATE = """if (auto it = {ClassName}::MemberOffsets.find(STR("{MemberName}")); it == {ClassName}::MemberOffsets.end()) +{{ + {ClassName}::MemberOffsets.emplace(STR("{MemberName}"), 0x{MemberOffset:02x}); +}} +""" + +Version = "5_01" + +class CMember: + def __init__(self, name, offset): + self.name = name + self.offset = offset + + def GenerateMember(self, className, count = None): + generated_name = self.name + if self.name == "EnumFlags": + generated_name = "EnumFlags_Internal" + if count and count != 0: + return MEMBER_TEMPLATE.format(ClassName = className, MemberName = f"{generated_name}_{count}", MemberOffset = self.offset) + else: + return MEMBER_TEMPLATE.format(ClassName = className, MemberName = generated_name, MemberOffset = self.offset) + +class CVFunc: + def __init__(self, mangledname, name, slotoffset): + self.mangledname = mangledname + self.name = name + self.slotoffset = slotoffset + + def GenerateVFunc(self, className, count = None): + if count and count != 0: + return FUNCTION_TEMPLATE.format(ClassName = className, FunctionName = f"{self.name}_{count}", FunctionOffset = self.slotoffset) + else: + return FUNCTION_TEMPLATE.format(ClassName = className, FunctionName = self.name, FunctionOffset = self.slotoffset) + +class CClass: + def __init__(self, name): + self.name = name + self.members = {} + self.vfuncs = {} + + def AddMember(self, member, offset): + if member not in self.members: + self.members[member] = CMember(member, offset) + + def AddVFunc(self, mangledname, vfunc, offset): + if mangledname not in self.vfuncs: + self.vfuncs[mangledname] = CVFunc(mangledname, vfunc, offset) + + def GenerateMember(self): + vals = list(self.members.values()) + vals.sort(key = lambda x: x.offset) + return "\n".join([member.GenerateMember(self.name) for member in vals]) + + def GenerateVTable(self): + generatedName = {} + results = [] + vals = list(self.vfuncs.values()) + vals.sort(key = lambda x: x.slotoffset) + for vfunc in vals: + if vfunc.name in generatedName: + generatedName[vfunc.name] += 1 + else: + generatedName[vfunc.name] = 0 + results.append(vfunc.GenerateVFunc(self.name, generatedName[vfunc.name])) + return "\n".join(results) + + def __str__(self): + return "\n".join([ + f"Class {self.name}{{", + "\t// Members", + "\n".join([f"\t{member.name} @ 0x{member.offset:02x}" for member in self.members.values()]), + "\t// VFuncs", + "\n".join([f"\t{vfunc.name} @ 0x{vfunc.slotoffset:02x} // {vunfc.mangledname}" for vfunc in self.vfuncs.values()]), + "}" + ]) + + def HasMember(self): + return len(self.members) > 0 + + def HasVFunc(self): + return len(self.vfuncs) > 0 + +class Database: + def __init__(self): + self.classes = {} + + def GetClass(self, name): + if name not in self.classes: + self.classes[name] = CClass(name) + return self.classes[name] + + def GenerateMember(self): + return { name: cls.GenerateMember() for name, cls in self.classes.items() } + + def GenerateVTable(self): + return { name: cls.GenerateVTable() for name, cls in self.classes.items() } + +def GetClassNames(search_dir = DEFAULT_SEARCH_DIR): + # glob all file with pattern "MemberVariableLayout_HeaderWrapper_{ClassName}.hpp" + # return list of class names + + import glob + tabA = [re.search(rf"{Version}_VTableOffsets_(.*)_FunctionBody\.cpp", file).group(1) for file in glob.glob(f"{search_dir}/FunctionBodies/{Version}_VTableOffsets_*.cpp")] + tabB = [re.search(rf"{Version}_MemberVariableLayout_DefaultSetter_(.*)\.cpp", file).group(1) for file in glob.glob(f"{search_dir}/FunctionBodies/{Version}_MemberVariableLayout_DefaultSetter_*.cpp")] + l = list(dict.fromkeys(tabA + tabB)) + # replace _ to : + return [x.replace("_", ":") for x in l] + + # return ['FArchiveState', 'UDataTable', 'FConsoleManager', "UEngine", 'UPlayer', 'UObjectBaseUtility', 'AGameMode', 'FMalloc', 'UField', 'UGameViewportClient', 'FField', 'FNumericProperty', 'IConsoleVariable', 'AGameModeBase', 'FMulticastDelegateProperty', 'UObject', 'AActor', 'IConsoleManager', 'IConsoleObject', 'FArchive', 'FConsoleVariableBase', 'FOutputDevice', 'ITextData', 'FProperty', 'FObjectPropertyBase', 'ULocalPlayer', 'UScriptStruct::ICppStructOps', 'UObjectBase', 'FConsoleCommandBase', 'UConsole', 'UStruct', 'IConsoleCommand', 'FInterfaceProperty', 'FByteProperty', 'FSoftClassProperty', 'FStructProperty', 'UEnum', 'FBoolProperty', 'UWorld', 'UClass', 'FEnumProperty', 'FClassProperty', 'FFieldPathProperty', 'FArrayProperty', 'FMapProperty', 'FSetProperty', 'UScriptStruct', 'FDelegateProperty', 'UFunction'] + +AdditonalClasses = [] + +from elftools.dwarf.die import DIE +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection + +def SubClassMatch(name, classes): + newclasses = [] + for i in classes: + if i.startswith(name + "::"): + # remove i:: + newclasses.append(i[len(name) + 2:]) + if len(newclasses) == 0: + return None + return newclasses + +# test +# print(SubClassMatch("UScriptStruct", GetClassNames())) + +def ScanCU(database, classes, citer, prefix = []): + for ch in citer: + if ch.tag == 'DW_TAG_class_type' or ch.tag == 'DW_TAG_structure_type' or ch.tag == 'DW_TAG_union_type': + if ch.attributes.get('DW_AT_name'): + name = ch.attributes['DW_AT_name'].value.decode('utf-8') + if name in classes: + longname = "::".join(prefix + [name]) + print(longname) + klass = database.GetClass(longname) + for m in ch.iter_children(): + name_m = m.attributes.get('DW_AT_name') + if not name_m: + continue + name_m = name_m.value.decode('utf-8') + if m.tag == 'DW_TAG_member': + if m.attributes.get('DW_AT_data_member_location'): + print(f" member: {name_m} @ {m.attributes['DW_AT_data_member_location'].value}") + klass.AddMember(name_m, m.attributes['DW_AT_data_member_location'].value) + if m.tag == "DW_TAG_subprogram": + # check if DW_AT_vtable_elem_location exists + if m.attributes.get('DW_AT_vtable_elem_location'): + # is a virtual function + link_name = m.attributes.get('DW_AT_linkage_name') + if link_name: + link_name = link_name.value.decode('utf-8') + else: + link_name = name_m + + print(f" vfunc: {name_m} aka {link_name} @ {m.attributes['DW_AT_vtable_elem_location'].value[1] * 8}") + klass.AddVFunc(link_name, name_m, m.attributes['DW_AT_vtable_elem_location'].value[1] * 8) + print() + if (subclasses := SubClassMatch(name, classes)) is not None: + ScanCU(database, subclasses, ch.iter_children(), prefix + [name]) + +def RunSingle(conn, dwarfinfo, outfile, cus, idx, target_class): + # if database_.pkl exists, load it and resume from last progress + started = True + if os.path.exists(outfile): + with open(outfile, "rb") as f: + database = pickle.load(f) + progress = pickle.load(f) + else: + database = Database() + progress = 0 + + # iter from idx + for i, cu in enumerate(cus): + if i < progress: + continue + if i < idx: + continue + print(f"Scanning {cu.get_top_DIE().attributes['DW_AT_name'].value.decode('utf-8')} ...") + ScanCU(database, target_class, cu.get_top_DIE().iter_children()) + print(f"{cu.get_top_DIE().attributes['DW_AT_name'].value.decode('utf-8')} done...") + # pickle dump + with open(outfile, "wb") as f: + pickle.dump(database, f) + pickle.dump(i + 1, f) + conn.send(i + 1) + conn.close() + return + conn.send(i + 1) + conn.close() + + +from multiprocessing import Process, Pipe + +def ProcessFile(filename = DEFAULT_FILENAME, outfile= "database.pkl", target_class = GetClassNames()): + with open(filename, 'rb') as f: + elffile = ELFFile(f) + dwarfinfo = elffile.get_dwarf_info() + + target_class += AdditonalClasses + + cus = list(dwarfinfo.iter_CUs()) + idx = 0 + while idx < len(cus): + parent_conn, child_conn = Pipe() + p = Process(target=RunSingle, args=(child_conn, dwarfinfo, outfile, cus, idx, target_class)) + p.start() + idx = parent_conn.recv() + p.join() + + print("CU scan done") + +def GenerateHeaderFile(target_dir = DEFAULT_GENERATED_DIR, database = "database.pkl", target_class = GetClassNames()): + print("Generating header files...") + if isinstance(database, str): + with open(database, "rb") as f: + import pickle + database = pickle.load(f) + # generate header files + VFunc = "Linux_{Version}_VTableOffsets_{ClassName}_FunctionBody.cpp" + Member = "Linux_{Version}_MemberVariableLayout_DefaultSetter_{ClassName}.cpp" + GeneratedVFuncs = set() + GeneratedMembers = set() + TargetClasses = set(target_class) + for c in database.classes.values(): + if c.name not in TargetClasses: + continue + print(f"Generating {c.name}...") + if c.HasVFunc(): + with open(target_dir + "/" + VFunc.format(ClassName=c.name.replace(':', "_"), Version=Version), "w") as f: + f.write(c.GenerateVTable()) + GeneratedVFuncs.add(c.name) + print(f" {VFunc.format(ClassName=c.name, Version=Version)} done") + if c.HasMember(): + with open(target_dir + "/" + Member.format(ClassName=c.name.replace(':', "_"), Version=Version), "w") as f: + f.write(c.GenerateMember()) + print(f" {Member.format(ClassName=c.name, Version=Version)} done") + GeneratedMembers.add(c.name) + print("Header files gerenation done.") + print("Summary:") + print(" Generated VFuncs: ", len(GeneratedVFuncs)) + print(" Generated Members: ", len(GeneratedMembers)) + # print classes not generated + print("Classes not generated:") + print(" VFuncs: ", TargetClasses - GeneratedVFuncs) + print(" Members: ", TargetClasses - GeneratedMembers) + + +def resolve_path(path): + return os.path.abspath(os.path.expanduser(path)) + +if __name__ == "__main__": + # -help/--help for usage + # -s in scan mode, args are [scan debug file] -o for output file -c for target class, can have a -g for generate header file + # -g for generate header mode, args are [database.pkl] -t for target directory + import argparse + parser = argparse.ArgumentParser(description="Generate header file for C++, using DRAWF information from .debug files") + parser.add_argument("-s", "--scan", help="Scan mode", action="store_true") + parser.add_argument("-g", "--generate", help="Generate header file", action="store_true") + # -p only one class + parser.add_argument("-p", "--print", help="Print definition for class", nargs="?", default=None) + + parser.add_argument("-i", "--info", help="Print infos", action="store_true") + parser.add_argument("-v", "--version", help="Set version", nargs="?", default=Version) + parser.add_argument("-a", "--addition", help="Add additional classes for scan", nargs="+", default=AdditonalClasses) + + parser.add_argument("-o", "--output", help="Output file", nargs="?", default="database.pkl") + parser.add_argument("-c", "--class", help="Target class", nargs="+", dest="class_", default=GetClassNames()) + parser.add_argument("-t", "--target", help="Target directory", nargs="?", default=DEFAULT_GENERATED_DIR) + parser.add_argument("filename", help="Filename", nargs="?", default=DEFAULT_FILENAME) + args = parser.parse_args() + + if args.version: + # valid version = "\d_\d\d" + if not re.match(r"\d_\d\d", args.version): + print("Invalid version") + sys.exit(1) + Version = args.version + + if args.info: + print(" Class names: ", GetClassNames()) + print(" Default search dir: ", resolve_path(DEFAULT_SEARCH_DIR)) + print(" Default generated dir: ", resolve_path(DEFAULT_GENERATED_DIR)) + sys.exit(0) + + if args.print: + fn = args.filename + if fn == DEFAULT_FILENAME: + fn = "database.pkl" + if not os.path.exists(fn): + print("Database file not found") + sys.exit(1) + with open(fn, "rb") as f: + database = pickle.load(f) + if args.print not in database.classes: + print(f"Class {args.print} not found") + sys.exit(1) + print(str(database.classes[args.print])) + sys.exit(0) + if args.scan: + if not args.output: + print("Output file not specified") + sys.exit(1) + if not args.class_: + print("Target class not specified") + sys.exit(1) + if args.generate: + if not args.target: + print("Target directory not specified") + ProcessFile(args.filename, args.output, args.class_) + if args.generate: + GenerateHeaderFile(args.target, args.output, args.class_) + else: + if args.generate: + if not args.target: + print("Target directory not specified") + fn = args.filename + if fn == DEFAULT_FILENAME: + fn = "database.pkl" + if not os.path.exists(fn): + print("Database file not found") + sys.exit(1) + GenerateHeaderFile(args.target, args.filename, args.class_) diff --git a/UVTD/src/Helpers.cpp b/UVTD/src/Helpers.cpp index ddb97c8d8..8f8baff47 100644 --- a/UVTD/src/Helpers.cpp +++ b/UVTD/src/Helpers.cpp @@ -5,26 +5,12 @@ namespace RC::UVTD { - auto to_string_type(const char* c_str) -> File::StringType + auto to_string_type(const char* c_str) -> UEStringType { -#if RC_IS_ANSI == 1 - return File::StringType(c_str); -#else - size_t count = strlen(c_str) + 1; - wchar_t* converted_method_name = new wchar_t[count]; - - size_t num_of_char_converted = 0; - mbstowcs_s(&num_of_char_converted, converted_method_name, count, c_str, count); - - auto converted = File::StringType(converted_method_name); - - delete[] converted_method_name; - - return converted; -#endif + return to_ue(c_str); } - auto change_prefix(File::StringType input, bool is_425_plus) -> std::optional + auto change_prefix(UEStringType input, bool is_425_plus) -> std::optional { for (const auto& prefixed : s_uprefix_to_fprefix) { @@ -39,16 +25,17 @@ namespace RC::UVTD return input; } - auto unify_uobject_array_if_needed(StringType& out_variable_type) -> bool + auto unify_uobject_array_if_needed(UEStringType& out_variable_type) -> bool { - static constexpr StringViewType fixed_uobject_array_string = STR("FFixedUObjectArray"); - static constexpr StringViewType chunked_fixed_uobject_array_string = STR("FChunkedFixedUObjectArray"); + static constexpr UEStringViewType fixed_uobject_array_string = STR("FFixedUObjectArray"); + static constexpr UEStringViewType chunked_fixed_uobject_array_string = STR("FChunkedFixedUObjectArray"); if (auto fixed_uobject_array_pos = out_variable_type.find(fixed_uobject_array_string); fixed_uobject_array_pos != out_variable_type.npos) { out_variable_type.replace(fixed_uobject_array_pos, fixed_uobject_array_string.length(), STR("TUObjectArray")); return true; } - else if (auto chunked_fixed_uobject_array_pos = out_variable_type.find(chunked_fixed_uobject_array_string); chunked_fixed_uobject_array_pos != out_variable_type.npos) + else if (auto chunked_fixed_uobject_array_pos = out_variable_type.find(chunked_fixed_uobject_array_string); + chunked_fixed_uobject_array_pos != out_variable_type.npos) { out_variable_type.replace(chunked_fixed_uobject_array_pos, chunked_fixed_uobject_array_string.length(), STR("TUObjectArray")); return true; diff --git a/UVTD/src/MemberVarsDumper.cpp b/UVTD/src/MemberVarsDumper.cpp index 5e6c8ab5f..faad159c0 100644 --- a/UVTD/src/MemberVarsDumper.cpp +++ b/UVTD/src/MemberVarsDumper.cpp @@ -7,7 +7,7 @@ namespace RC::UVTD { - static inline std::vector s_types_to_not_dump{ + static inline std::vector s_types_to_not_dump{ STR("FUnversionedStructSchema"), STR("ELifetimeCondition"), STR("UAISystemBase"), @@ -110,14 +110,14 @@ namespace RC::UVTD auto MemberVarsDumper::process_class(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* class_record, - const File::StringType& name, + const UEStringType& name, const SymbolNameInfo& name_info) -> void { auto changed = change_prefix(name, symbols.is_425_plus); if (!changed.has_value()) return; - File::StringType class_name = *changed; - File::StringType class_name_clean = Symbols::clean_name(class_name); + auto class_name = *changed; + auto class_name_clean = Symbols::clean_name(class_name); auto& class_entry = type_container.get_or_create_class_entry(class_name, class_name_clean, name_info); auto fields = tpi_stream.GetTypeRecord(class_record->data.LF_CLASS.field); @@ -136,11 +136,11 @@ namespace RC::UVTD auto MemberVarsDumper::process_member(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::FieldList* field_record, Class& class_entry) -> void { - File::StringType member_name = Symbols::get_leaf_name(field_record->data.LF_STMEMBER.name, field_record->data.LF_MEMBER.lfEasy.kind); + auto member_name = Symbols::get_leaf_name(field_record->data.LF_STMEMBER.name, field_record->data.LF_MEMBER.lfEasy.kind); auto changed = change_prefix(Symbols::get_type_name(tpi_stream, field_record->data.LF_MEMBER.index), symbols.is_425_plus); if (!changed.has_value()) return; - File::StringType type_name = *changed; + auto type_name = *changed; for (const auto& type_to_not_dump : s_types_to_not_dump) { @@ -156,9 +156,9 @@ namespace RC::UVTD variable.offset = *(uint16_t*)field_record->data.LF_MEMBER.offset; } - auto MemberVarsDumper::dump_member_variable_layouts(std::unordered_map& names) -> void + auto MemberVarsDumper::dump_member_variable_layouts(std::unordered_map& names) -> void { - Output::send(STR("Dumping {} symbols for {}\n"), names.size(), symbols.pdb_file_path.filename().stem().wstring()); + Output::send(SYSSTR("Dumping {} symbols for {}\n"), names.size(), symbols.pdb_file_path.filename().stem().wstring()); const PDB::TPIStream tpi_stream = PDB::CreateTPIStream(symbols.pdb_file); @@ -169,7 +169,7 @@ namespace RC::UVTD { if (type_record->data.LF_CLASS.property.fwdref) continue; - const File::StringType class_name = Symbols::get_leaf_name(type_record->data.LF_CLASS.data, type_record->data.LF_CLASS.lfEasy.kind); + const SystemStringType class_name = Symbols::get_leaf_name(type_record->data.LF_CLASS.data, type_record->data.LF_CLASS.lfEasy.kind); if (!names.contains(class_name)) continue; const auto name_info = names.find(class_name); @@ -184,7 +184,7 @@ namespace RC::UVTD auto MemberVarsDumper::generate_code() -> void { - std::unordered_map member_vars_names; + std::unordered_map member_vars_names; for (ObjectItem& item : s_object_items) { @@ -197,28 +197,28 @@ namespace RC::UVTD auto MemberVarsDumper::generate_files() -> void { - File::StringType pdb_name = symbols.pdb_file_path.filename().stem(); + SystemStringType pdb_name = symbols.pdb_file_path.filename().stem(); auto default_template_file = std::filesystem::path{STR("MemberVariableLayout.ini")}; - Output::send(STR("Generating file '{}'\n"), default_template_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), default_template_file.wstring()); Output::Targets default_ini_dumper; auto& default_ini_file_device = default_ini_dumper.get_device(); default_ini_file_device.set_file_name_and_path(member_variable_layouts_templates_output_path / default_template_file); - default_ini_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + default_ini_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); - auto template_file = std::format(STR("MemberVariableLayout_{}_Template.ini"), pdb_name); + auto template_file = std::format(SYSSTR("MemberVariableLayout_{}_Template.ini"), pdb_name); - Output::send(STR("Generating file '{}'\n"), template_file); + Output::send(SYSSTR("Generating file '{}'\n"), template_file); Output::Targets ini_dumper; auto& ini_file_device = ini_dumper.get_device(); ini_file_device.set_file_name_and_path(member_variable_layouts_templates_output_path / template_file); - ini_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + ini_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); auto pdb_name_no_underscore = pdb_name; @@ -232,15 +232,15 @@ namespace RC::UVTD } auto default_setter_src_file = member_variable_layouts_gen_function_bodies_path / - std::format(STR("{}_MemberVariableLayout_DefaultSetter_{}.cpp"), pdb_name, class_entry.class_name_clean); + std::format(SYSSTR("{}_MemberVariableLayout_DefaultSetter_{}.cpp"), pdb_name, to_system(class_entry.class_name_clean)); - Output::send(STR("Generating file '{}'\n"), default_setter_src_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), default_setter_src_file.wstring()); Output::Targets default_setter_src_dumper; auto& default_setter_src_file_device = default_setter_src_dumper.get_device(); default_setter_src_file_device.set_file_name_and_path(default_setter_src_file); - default_setter_src_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + default_setter_src_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); ini_dumper.send(STR("[{}]\n"), class_entry.class_name); @@ -251,8 +251,8 @@ namespace RC::UVTD ini_dumper.send(STR("{} = 0x{:X}\n"), variable.name, variable.offset); default_ini_dumper.send(STR("{} = -1\n"), variable.name); - File::StringType final_variable_name = variable.name; - File::StringType final_class_name = class_entry.class_name; + SystemStringType final_variable_name = variable.name; + SystemStringType final_class_name = class_entry.class_name; if (variable.name == STR("EnumFlags")) { diff --git a/UVTD/src/MemberVarsWrapperGenerator.cpp b/UVTD/src/MemberVarsWrapperGenerator.cpp index a408c9c6e..93670d3a0 100644 --- a/UVTD/src/MemberVarsWrapperGenerator.cpp +++ b/UVTD/src/MemberVarsWrapperGenerator.cpp @@ -10,13 +10,13 @@ namespace RC::UVTD { auto macro_setter_file = std::filesystem::path{STR("MacroSetter.hpp")}; - Output::send(STR("Generating file '{}'\n"), macro_setter_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), macro_setter_file.wstring()); Output::Targets macro_setter_dumper; auto& macro_setter_file_device = macro_setter_dumper.get_device(); macro_setter_file_device.set_file_name_and_path(macro_setter_file); - macro_setter_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + macro_setter_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); for (const auto& [class_name, class_entry] : type_container.get_class_entries()) @@ -27,33 +27,33 @@ namespace RC::UVTD } auto wrapper_header_file = member_variable_layouts_gen_output_include_path / - std::format(STR("MemberVariableLayout_HeaderWrapper_{}.hpp"), class_entry.class_name_clean); + std::format(SYSSTR("MemberVariableLayout_HeaderWrapper_{}.hpp"), class_entry.class_name_clean); - Output::send(STR("Generating file '{}'\n"), wrapper_header_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), wrapper_header_file.wstring()); Output::Targets header_wrapper_dumper; auto& wrapper_header_file_device = header_wrapper_dumper.get_device(); wrapper_header_file_device.set_file_name_and_path(wrapper_header_file); - wrapper_header_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + wrapper_header_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); - auto wrapper_src_file = - member_variable_layouts_gen_output_include_path / std::format(STR("MemberVariableLayout_SrcWrapper_{}.hpp"), class_entry.class_name_clean); + auto wrapper_src_file = member_variable_layouts_gen_output_include_path / + std::format(SYSSTR("MemberVariableLayout_SrcWrapper_{}.hpp"), class_entry.class_name_clean); - Output::send(STR("Generating file '{}'\n"), wrapper_src_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), wrapper_src_file.wstring()); Output::Targets wrapper_src_dumper; auto& wrapper_src_file_device = wrapper_src_dumper.get_device(); wrapper_src_file_device.set_file_name_and_path(wrapper_src_file); - wrapper_src_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + wrapper_src_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); auto final_class_name = class_entry.class_name; unify_uobject_array_if_needed(final_class_name); - header_wrapper_dumper.send(STR("static std::unordered_map MemberOffsets;\n\n")); - wrapper_src_dumper.send(STR("std::unordered_map {}::MemberOffsets{{}};\n\n"), final_class_name); + header_wrapper_dumper.send(STR("static std::unordered_map MemberOffsets;\n\n")); + wrapper_src_dumper.send(STR("std::unordered_map {}::MemberOffsets{{}};\n\n"), final_class_name); auto private_variables_for_class = s_private_variables.find(class_entry.class_name); @@ -83,8 +83,8 @@ namespace RC::UVTD bool is_private{private_variables_for_class != s_private_variables.end() && private_variables_for_class->second.find(variable.name) != private_variables_for_class->second.end()}; - File::StringType final_variable_name = variable.name; - File::StringType final_type_name = variable.type; + UEStringType final_variable_name = variable.name; + UEStringType final_type_name = variable.type; if (variable.name == STR("EnumFlags")) { @@ -151,7 +151,7 @@ namespace RC::UVTD wrapper_src_dumper.send(STR("}\n\n")); } - macro_setter_dumper.send(STR("if (auto val = parser.get_int64(STR(\"{}\"), STR(\"{}\"), -1); val != -1)\n"), + macro_setter_dumper.send(STR("if (auto val = parser.get_int64(SYSSTR(\"{}\"), SYSSTR(\"{}\"), -1); val != -1)\n"), final_class_name, final_variable_name); macro_setter_dumper.send(STR(" Unreal::{}::MemberOffsets.emplace(STR(\"{}\"), static_cast(val));\n"), diff --git a/UVTD/src/SolBindingsGenerator.cpp b/UVTD/src/SolBindingsGenerator.cpp index 45e7f2206..d1ac24751 100644 --- a/UVTD/src/SolBindingsGenerator.cpp +++ b/UVTD/src/SolBindingsGenerator.cpp @@ -26,19 +26,19 @@ namespace RC::UVTD auto final_class_name = class_name; - auto wrapper_header_file = sol_bindings_output_path / std::format(STR("SolBindings_{}.hpp"), final_class_name_clean); + auto wrapper_header_file = sol_bindings_output_path / std::format(SYSSTR("SolBindings_{}.hpp"), to_system(final_class_name_clean)); - Output::send(STR("Generating file '{}'\n"), wrapper_header_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), wrapper_header_file); Output::Targets header_wrapper_dumper; auto& wrapper_header_file_device = header_wrapper_dumper.get_device(); wrapper_header_file_device.set_file_name_and_path(wrapper_header_file); - wrapper_header_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + wrapper_header_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); - header_wrapper_dumper.send(STR("auto sol_class_{} = sol().new_usertype<{}>(\"{}\""), final_class_name, final_class_name, final_class_name); + header_wrapper_dumper.send(SYSSTR("auto sol_class_{} = sol().new_usertype<{}>(\"{}\""), final_class_name, final_class_name, final_class_name); for (const auto& [variable_name, variable] : class_entry.variables) { @@ -67,7 +67,7 @@ namespace RC::UVTD continue; } - File::StringType final_variable_name = variable.name; + UEStringType final_variable_name = variable.name; if (variable.name == STR("EnumFlags")) { diff --git a/UVTD/src/Symbols.cpp b/UVTD/src/Symbols.cpp index 59e43c2da..13fd5d233 100644 --- a/UVTD/src/Symbols.cpp +++ b/UVTD/src/Symbols.cpp @@ -56,7 +56,7 @@ namespace RC::UVTD return *this; } - auto Symbols::generate_method_signature(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* function_record, File::StringType method_name) + auto Symbols::generate_method_signature(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* function_record, SystemStringType method_name) -> MethodSignature { MethodSignature signature{}; @@ -81,7 +81,7 @@ namespace RC::UVTD return signature; } - auto Symbols::get_type_name(const PDB::TPIStream& tpi_stream, uint32_t record_index, bool check_valid) -> File::StringType + auto Symbols::get_type_name(const PDB::TPIStream& tpi_stream, uint32_t record_index, bool check_valid) -> UEStringType { if (record_index < tpi_stream.GetFirstTypeIndex()) { @@ -214,7 +214,7 @@ namespace RC::UVTD { case PDB::CodeView::TPI::TypeRecordKind::LF_CLASS: case PDB::CodeView::TPI::TypeRecordKind::LF_STRUCTURE: { - File::StringType name = get_leaf_name(record->data.LF_CLASS.data, record->data.LF_CLASS.lfEasy.kind); + UEStringType name = get_leaf_name(record->data.LF_CLASS.data, record->data.LF_CLASS.lfEasy.kind); ParsedTemplateClass parsed = TemplateClassParser::Parse(name); if (parsed.class_name == STR("TMap")) @@ -228,11 +228,11 @@ namespace RC::UVTD return to_string_type(record->data.LF_ENUM.name); case PDB::CodeView::TPI::TypeRecordKind::LF_MODIFIER: { const auto modifier_attr = record->data.LF_MODIFIER.attr; - std::vector modifiers{}; + std::vector modifiers{}; if (modifier_attr.MOD_volatile) modifiers.push_back(STR("volatile")); - File::StringType modifier_string{}; + UEStringType modifier_string{}; for (const auto& modifier : modifiers) { modifier_string += modifier + STR(" "); @@ -244,17 +244,18 @@ namespace RC::UVTD return get_type_name(tpi_stream, record->data.LF_POINTER.utype, check_valid) + STR("*"); case PDB::CodeView::TPI::TypeRecordKind::LF_MFUNCTION: case PDB::CodeView::TPI::TypeRecordKind::LF_PROCEDURE: { - File::StringType return_type = get_type_name(tpi_stream, record->data.LF_PROCEDURE.rvtype, true); - File::StringType args = get_type_name(tpi_stream, record->data.LF_PROCEDURE.arglist, check_valid); - return std::format(STR("std::function<{}({})>"), return_type, args); + UEStringType return_type = get_type_name(tpi_stream, record->data.LF_PROCEDURE.rvtype, true); + UEStringType args = get_type_name(tpi_stream, record->data.LF_PROCEDURE.arglist, check_valid); + return std::format(SYSSTR("std::function<{}({})>"), return_type, args); } case PDB::CodeView::TPI::TypeRecordKind::LF_ARGLIST: { - File::StringType args{}; + UEStringType args{}; for (size_t i = 0; i < record->data.LF_ARGLIST.count; i++) { bool should_add_comma = i < record->data.LF_ARGLIST.count - 1; - args.append(std::format(STR("{}{}"), get_type_name(tpi_stream, record->data.LF_ARGLIST.arg[i], true), should_add_comma ? STR(", ") : STR(""))); + // args.append(std::format(SYSSTR("{}{}"), get_type_name(tpi_stream, record->data.LF_ARGLIST.arg[i], true), should_add_comma ? STR(", ") : STR(""))); + args.append(get_type_name(tpi_stream, record->data.LF_ARGLIST.arg[i], true) + (should_add_comma ? STR(", ") : STR(""))); } return args; @@ -267,7 +268,7 @@ namespace RC::UVTD } } - auto Symbols::get_method_name(const PDB::CodeView::TPI::FieldList* method_record) -> File::StringType + auto Symbols::get_method_name(const PDB::CodeView::TPI::FieldList* method_record) -> UEStringType { auto methodAttributes = static_cast(method_record->data.LF_ONEMETHOD.attributes.mprop); switch (methodAttributes) @@ -311,7 +312,7 @@ namespace RC::UVTD return 0; } - auto Symbols::get_leaf_name(const char* data, PDB::CodeView::TPI::TypeRecordKind kind) -> File::StringType + auto Symbols::get_leaf_name(const char* data, PDB::CodeView::TPI::TypeRecordKind kind) -> UEStringType { auto name = to_string_type(&data[get_leaf_size(kind)]); if (auto it = s_member_rename_map.find(name); it != s_member_rename_map.end()) @@ -324,7 +325,7 @@ namespace RC::UVTD } } - auto Symbols::clean_name(File::StringType name) -> File::StringType + auto Symbols::clean_name(UEStringType name) -> UEStringType { std::replace(name.begin(), name.end(), ':', '_'); std::replace(name.begin(), name.end(), '~', '$'); diff --git a/UVTD/src/TemplateClassParser.cpp b/UVTD/src/TemplateClassParser.cpp index f69004ccd..49f966fac 100644 --- a/UVTD/src/TemplateClassParser.cpp +++ b/UVTD/src/TemplateClassParser.cpp @@ -2,22 +2,22 @@ namespace RC::UVTD { - ParsedTemplateClass TemplateClassParser::Parse(File::StringViewType input) + ParsedTemplateClass TemplateClassParser::Parse(UEStringViewType input) { ParsedTemplateClass parsed{}; - for (const File::StringType::value_type character : input) + for (const UEStringType::value_type character : input) { if (character == STR('<')) break; parsed.class_name += character; } size_t nesting_level = 0; - File::StringType current_param{}; + UEStringType current_param{}; for (size_t i = parsed.class_name.size(); i < input.size(); i++) { - const File::StringType::value_type character = input[i]; + const UEStringType::value_type character = input[i]; bool is_nesting_character = false; if (character == STR('>') && nesting_level == 1) break; diff --git a/UVTD/src/TypeContainer.cpp b/UVTD/src/TypeContainer.cpp index 772aeceb5..dec0bba6c 100644 --- a/UVTD/src/TypeContainer.cpp +++ b/UVTD/src/TypeContainer.cpp @@ -21,22 +21,18 @@ namespace RC::UVTD } } - auto TypeContainer::get_or_create_class_entry(const File::StringType& symbol_name, const File::StringType& symbol_name_clean, const SymbolNameInfo& name_info) - -> Class& + auto TypeContainer::get_or_create_class_entry(const UEStringType& symbol_name, const UEStringType& symbol_name_clean, const SymbolNameInfo& name_info) -> Class& { - auto& class_entry = [&]() -> auto& - { + auto& class_entry = [&]() -> auto& { if (auto it = class_entries.find(symbol_name); it != class_entries.end()) { return it->second; } else { - return class_entries.emplace(symbol_name_clean, Class{.class_name = File::StringType{symbol_name}, .class_name_clean = symbol_name_clean}) - .first->second; + return class_entries.emplace(symbol_name_clean, Class{.class_name = UEStringType{symbol_name}, .class_name_clean = symbol_name_clean}).first->second; } - } - (); + }(); class_entry.valid_for_member_vars = name_info.valid_for_member_vars; class_entry.valid_for_vtable = name_info.valid_for_vtable; diff --git a/UVTD/src/UVTD.cpp b/UVTD/src/UVTD.cpp index b65488a5e..52464c910 100644 --- a/UVTD/src/UVTD.cpp +++ b/UVTD/src/UVTD.cpp @@ -25,7 +25,7 @@ namespace RC::UVTD { bool processing_events{false}; - Input::Handler input_handler{L"ConsoleWindowClass", L"UnrealWindow"}; + Input::Handler input_handler{}; auto static event_loop_update() -> void { @@ -38,6 +38,8 @@ namespace RC::UVTD auto main(DumpSettings dump_settings) -> void { + input_handler.init(); + input_handler.set_input_source("Win32Async"); static std::vector pdbs_to_dump{ "PDBs/4_10.pdb", "PDBs/4_11.pdb", @@ -111,13 +113,13 @@ namespace RC::UVTD generator.generate_files(); } - File::StringType pdb_name = pdb.filename().stem(); + SystemStringType pdb_name = pdb.filename().stem(); UnrealVirtualGenerator virtual_generator(pdb_name, run_container); virtual_generator.generate_files(); shared_container.join(run_container); - Output::send(STR("Code generated.\n")); + Output::send(SYSSTR("Code generated.\n")); } }); } @@ -128,9 +130,9 @@ namespace RC::UVTD MemberVarsWrapperGenerator wrapper_generator{std::move(shared_container)}; wrapper_generator.generate_files(); - Output::send(STR("Code generated.\n")); + Output::send(SYSSTR("Code generated.\n")); } - Output::send(STR("All done.\n")); + Output::send(SYSSTR("All done.\n")); } } // namespace RC::UVTD diff --git a/UVTD/src/UnrealVirtualGenerator.cpp b/UVTD/src/UnrealVirtualGenerator.cpp index f3ab28c48..2c77d96d7 100644 --- a/UVTD/src/UnrealVirtualGenerator.cpp +++ b/UVTD/src/UnrealVirtualGenerator.cpp @@ -10,26 +10,26 @@ namespace RC::UVTD auto pdb_name_no_underscore = pdb_name; pdb_name_no_underscore.replace(pdb_name_no_underscore.find(STR('_')), 1, STR("")); - auto virtual_header_file = virtual_gen_output_include_path / std::format(STR("UnrealVirtual{}.hpp"), pdb_name_no_underscore); + auto virtual_header_file = virtual_gen_output_include_path / std::format(SYSSTR("UnrealVirtual{}.hpp"), pdb_name_no_underscore); - Output::send(STR("Generating file '{}'\n"), virtual_header_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), virtual_header_file.wstring()); Output::Targets virtual_header_dumper; auto& virtual_header_file_device = virtual_header_dumper.get_device(); virtual_header_file_device.set_file_name_and_path(virtual_header_file); - virtual_header_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + virtual_header_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); - auto virtual_src_file = virtual_gen_function_bodies_path / std::format(STR("UnrealVirtual{}.cpp"), pdb_name_no_underscore); + auto virtual_src_file = virtual_gen_function_bodies_path / std::format(SYSSTR("UnrealVirtual{}.cpp"), pdb_name_no_underscore); - Output::send(STR("Generating file '{}'\n"), virtual_src_file.wstring()); + Output::send(SYSSTR("Generating file '{}'\n"), virtual_src_file); Output::Targets virtual_src_dumper; auto& virtual_src_file_device = virtual_src_dumper.get_device(); virtual_src_file_device.set_file_name_and_path(virtual_src_file); - virtual_src_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + virtual_src_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); bool is_case_preserving_pdb = !(s_case_preserving_variants.find(pdb_name) == s_case_preserving_variants.end()); @@ -37,77 +37,77 @@ namespace RC::UVTD if (!is_case_preserving_pdb) { - virtual_header_dumper.send(STR("#ifndef RC_UNREAL_UNREAL_VIRTUAL{}_HPP\n"), pdb_name_no_underscore); - virtual_header_dumper.send(STR("#define RC_UNREAL_UNREAL_VIRTUAL{}_HPP\n"), pdb_name_no_underscore); - virtual_header_dumper.send(STR("#include \n\n")); - virtual_header_dumper.send(STR("namespace RC::Unreal\n")); - virtual_header_dumper.send(STR("{\n")); - virtual_header_dumper.send(STR(" class UnrealVirtual{} : public UnrealVirtualBaseVC\n"), pdb_name_no_underscore); - virtual_header_dumper.send(STR(" {\n")); - virtual_header_dumper.send(STR(" auto set_virtual_offsets() -> void override;\n")); - virtual_header_dumper.send(STR(" };\n")); - virtual_header_dumper.send(STR("}\n\n\n")); - virtual_header_dumper.send(STR("#endif //RC_UNREAL_UNREAL_VIRTUAL{}_HPP\n"), pdb_name_no_underscore); - - virtual_src_dumper.send(STR("#include \n\n"), pdb_name_no_underscore); - virtual_src_dumper.send(STR("#include \n\n")); - virtual_src_dumper.send(STR("// These are all the structs that have virtuals that need to have their offset set\n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("#include \n")); - // virtual_src_dumper.send(STR("#include \n")); - virtual_src_dumper.send(STR("\n")); - virtual_src_dumper.send(STR("namespace RC::Unreal\n")); - virtual_src_dumper.send(STR("{\n")); - virtual_src_dumper.send(STR(" void UnrealVirtual{}::set_virtual_offsets()\n"), pdb_name_no_underscore); - virtual_src_dumper.send(STR(" {\n")); + virtual_header_dumper.send(SYSSTR("#ifndef RC_UNREAL_UNREAL_VIRTUAL{}_HPP\n"), pdb_name_no_underscore); + virtual_header_dumper.send(SYSSTR("#define RC_UNREAL_UNREAL_VIRTUAL{}_HPP\n"), pdb_name_no_underscore); + virtual_header_dumper.send(SYSSTR("#include \n\n")); + virtual_header_dumper.send(SYSSTR("namespace RC::Unreal\n")); + virtual_header_dumper.send(SYSSTR("{\n")); + virtual_header_dumper.send(SYSSTR(" class UnrealVirtual{} : public UnrealVirtualBaseVC\n"), pdb_name_no_underscore); + virtual_header_dumper.send(SYSSTR(" {\n")); + virtual_header_dumper.send(SYSSTR(" auto set_virtual_offsets() -> void override;\n")); + virtual_header_dumper.send(SYSSTR(" };\n")); + virtual_header_dumper.send(SYSSTR("}\n\n\n")); + virtual_header_dumper.send(SYSSTR("#endif //RC_UNREAL_UNREAL_VIRTUAL{}_HPP\n"), pdb_name_no_underscore); + + virtual_src_dumper.send(SYSSTR("#include \n\n"), pdb_name_no_underscore); + virtual_src_dumper.send(SYSSTR("#include \n\n")); + virtual_src_dumper.send(SYSSTR("// These are all the structs that have virtuals that need to have their offset set\n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("#include \n")); + // virtual_src_dumper.send(SYSSTR("#include \n")); + virtual_src_dumper.send(SYSSTR("\n")); + virtual_src_dumper.send(SYSSTR("namespace RC::Unreal\n")); + virtual_src_dumper.send(SYSSTR("{\n")); + virtual_src_dumper.send(SYSSTR(" void UnrealVirtual{}::set_virtual_offsets()\n"), pdb_name_no_underscore); + virtual_src_dumper.send(SYSSTR(" {\n")); } for (const auto& [class_name, class_entry] : type_container.get_class_entries()) { if (!class_entry.functions.empty() && class_entry.valid_for_vtable == ValidForVTable::Yes && !is_case_preserving_pdb) { - virtual_src_dumper.send(STR("#include \n"), pdb_name, class_name); + virtual_src_dumper.send(SYSSTR("#include \n"), pdb_name, class_name); } } if (!is_case_preserving_pdb) { - virtual_src_dumper.send(STR("\n")); + virtual_src_dumper.send(SYSSTR("\n")); // Second & third passes just to separate VTable includes and MemberOffsets includes. if (is_non_case_preserving_pdb) { - virtual_src_dumper.send(STR("#ifdef WITH_CASE_PRESERVING_NAME\n")); + virtual_src_dumper.send(SYSSTR("#ifdef WITH_CASE_PRESERVING_NAME\n")); for (const auto& [class_name, class_entry] : type_container.get_class_entries()) { if (class_entry.variables.empty()) @@ -117,7 +117,9 @@ namespace RC::UVTD if (class_entry.valid_for_member_vars == ValidForMemberVars::Yes) { - virtual_src_dumper.send(STR("#include \n"), pdb_name, class_name); + virtual_src_dumper.send(SYSSTR("#include \n"), + pdb_name, + class_name); } } virtual_src_dumper.send(STR("#else\n")); diff --git a/UVTD/src/VTableDumper.cpp b/UVTD/src/VTableDumper.cpp index 485c547cc..6b94bd71c 100644 --- a/UVTD/src/VTableDumper.cpp +++ b/UVTD/src/VTableDumper.cpp @@ -25,14 +25,14 @@ namespace RC::UVTD { auto VTableDumper::process_class(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::Record* class_record, - const File::StringType& name, + const UEStringType& name, const SymbolNameInfo& name_info) -> void { auto changed = change_prefix(name, symbols.is_425_plus); if (!changed.has_value()) return; - File::StringType class_name = *changed; - File::StringType class_name_clean = Symbols::clean_name(class_name); + UEStringType class_name = *changed; + UEStringType class_name_clean = Symbols::clean_name(class_name); auto& class_entry = type_container.get_or_create_class_entry(class_name, class_name_clean, name_info); @@ -65,8 +65,8 @@ namespace RC::UVTD { auto list = tpi_stream.GetTypeRecord(method_record->data.LF_METHOD.mList); - File::StringType method_name = Symbols::get_method_name(method_record); - File::StringType method_name_clean = Symbols::clean_name(method_name); + UEStringType method_name = Symbols::get_method_name(method_record); + UEStringType method_name_clean = Symbols::clean_name(method_name); // this is required because METHOD struct size is not constant :) size_t next_offset = 0; @@ -87,10 +87,10 @@ namespace RC::UVTD if (!Symbols::is_virtual(overload_record->METHOD.attributes)) continue; if (!function_record || function_record->header.kind != PDB::CodeView::TPI::TypeRecordKind::LF_MFUNCTION) continue; - File::StringType overload_name = method_name_clean; + UEStringType overload_name = method_name_clean; if (overload_index != 0) { - overload_name += std::format(STR("_{}"), overload_index); + overload_name += std::format(SYSSTR("_{}"), overload_index); } overload_index++; @@ -104,13 +104,13 @@ namespace RC::UVTD auto VTableDumper::process_onemethod(const PDB::TPIStream& tpi_stream, const PDB::CodeView::TPI::FieldList* method_record, Class& class_entry) -> void { - static std::unordered_map> functions_already_dumped{}; + static std::unordered_map> functions_already_dumped{}; const auto is_virtual = method_record->data.LF_ONEMETHOD.attributes.mprop == (uint16_t)PDB::CodeView::TPI::MethodProperty::Intro || method_record->data.LF_ONEMETHOD.attributes.mprop == (uint16_t)PDB::CodeView::TPI::MethodProperty::PureIntro; if (!is_virtual) return; - File::StringType method_name = Symbols::get_method_name(method_record); + UEStringType method_name = Symbols::get_method_name(method_record); int32_t vtable_offset = method_record->data.LF_ONEMETHOD.vbaseoff[0]; auto function_record = tpi_stream.GetTypeRecord(method_record->data.LF_ONEMETHOD.index); @@ -120,14 +120,14 @@ namespace RC::UVTD { if (auto it2 = it->second.find(method_name); it2 != it->second.end()) { - method_name.append(std::format(STR("_{}"), ++it2->second)); + method_name.append(std::format(SYSSTR("_{}"), ++it2->second)); is_overload = true; } } - Output::send(STR(" method {} offset {}\n"), method_name, vtable_offset); + Output::send(SYSSTR(" method {} offset {}\n"), method_name, vtable_offset); - File::StringType method_name_clean = Symbols::clean_name(method_name); + UEStringType method_name_clean = Symbols::clean_name(method_name); auto& function = class_entry.functions[vtable_offset]; function.name = method_name_clean; @@ -137,9 +137,9 @@ namespace RC::UVTD functions_already_dumped.emplace(method_name, 1); } - auto VTableDumper::dump_vtable_for_symbol(std::unordered_map& names) -> void + auto VTableDumper::dump_vtable_for_symbol(std::unordered_map& names) -> void { - Output::send(STR("Dumping {} struct symbols for {}\n"), names.size(), symbols.pdb_file_path.filename().stem().wstring()); + Output::send(SYSSTR("Dumping {} struct symbols for {}\n"), names.size(), symbols.pdb_file_path.filename().stem().wstring()); const PDB::TPIStream tpi_stream = PDB::CreateTPIStream(symbols.pdb_file); @@ -150,7 +150,7 @@ namespace RC::UVTD { if (type_record->data.LF_CLASS.property.fwdref) continue; - const File::StringType class_name = Symbols::get_leaf_name(type_record->data.LF_CLASS.data, type_record->data.LF_CLASS.lfEasy.kind); + const UEStringType class_name = Symbols::get_leaf_name(type_record->data.LF_CLASS.data, type_record->data.LF_CLASS.lfEasy.kind); if (!names.contains(class_name)) continue; const auto name_info = names.find(class_name); @@ -164,7 +164,7 @@ namespace RC::UVTD auto VTableDumper::generate_code() -> void { - std::unordered_map vtable_names; + std::unordered_map vtable_names; for (const auto& object_item : s_object_items) { if (object_item.valid_for_vtable != ValidForVTable::Yes) continue; @@ -177,17 +177,17 @@ namespace RC::UVTD auto VTableDumper::generate_files() -> void { - File::StringType pdb_name = symbols.pdb_file_path.filename().stem(); + UEStringType pdb_name = symbols.pdb_file_path.filename().stem(); for (const auto& [class_name, class_entry] : type_container.get_class_entries()) { - Output::send(STR("Generating file '{}_VTableOffsets_{}_FunctionBody.cpp'\n"), pdb_name, class_entry.class_name_clean); + Output::send(SYSSTR("Generating file '{}_VTableOffsets_{}_FunctionBody.cpp'\n"), pdb_name, class_entry.class_name_clean); Output::Targets function_body_dumper; auto& function_body_file_device = function_body_dumper.get_device(); function_body_file_device.set_file_name_and_path(vtable_gen_output_function_bodies_path / - std::format(STR("{}_VTableOffsets_{}_FunctionBody.cpp"), pdb_name, class_name)); - function_body_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + std::format(SYSSTR("{}_VTableOffsets_{}_FunctionBody.cpp"), pdb_name, class_name)); + function_body_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); for (const auto& [function_index, function_entry] : class_entry.functions) @@ -198,39 +198,42 @@ namespace RC::UVTD local_class_name.replace(0, 1, STR("F")); } - function_body_dumper.send(STR("if (auto it = {}::VTableLayoutMap.find(STR(\"{}\")); it == {}::VTableLayoutMap.end())\n"), + function_body_dumper.send(SYSSTR("if (auto it = {}::VTableLayoutMap.find(STR(\"{}\")); it == {}::VTableLayoutMap.end())\n"), local_class_name, function_entry.name, local_class_name); - function_body_dumper.send(STR("{\n")); - function_body_dumper.send(STR(" {}::VTableLayoutMap.emplace(STR(\"{}\"), 0x{:X});\n"), local_class_name, function_entry.name, function_entry.offset); - function_body_dumper.send(STR("}\n\n")); + function_body_dumper.send(SYSSTR("{\n")); + function_body_dumper.send(SYSSTR(" {}::VTableLayoutMap.emplace(STR(\"{}\"), 0x{:X});\n"), + local_class_name, + function_entry.name, + function_entry.offset); + function_body_dumper.send(SYSSTR("}\n\n")); } } - auto template_file = std::format(STR("VTableLayout_{}_Template.ini"), pdb_name); - Output::send(STR("Generating file '{}'\n"), template_file); + auto template_file = std::format(SYSSTR("VTableLayout_{}_Template.ini"), pdb_name); + Output::send(SYSSTR("Generating file '{}'\n"), template_file); Output::Targets ini_dumper; auto& ini_file_device = ini_dumper.get_device(); ini_file_device.set_file_name_and_path(vtable_templates_output_path / template_file); - ini_file_device.set_formatter([](File::StringViewType string) { - return File::StringType{string}; + ini_file_device.set_formatter([](SystemStringViewType string) { + return SystemStringType{string}; }); for (const auto& [class_name, class_entry] : type_container.get_class_entries()) { - ini_dumper.send(STR("[{}]\n"), class_entry.class_name); + ini_dumper.send(SYSSTR("[{}]\n"), class_entry.class_name); for (const auto& [function_index, function_entry] : class_entry.functions) { if (function_entry.is_overload) { - ini_dumper.send(STR("; {}\n"), function_entry.signature.to_string()); + ini_dumper.send(SYSSTR("; {}\n"), function_entry.signature.to_string()); } - ini_dumper.send(STR("{}\n"), function_entry.name); + ini_dumper.send(SYSSTR("{}\n"), function_entry.name); } - ini_dumper.send(STR("\n")); + ini_dumper.send(SYSSTR("\n")); } } diff --git a/UVTD/src/main.cpp b/UVTD/src/main.cpp index 1a63c13c8..f5d65895a 100644 --- a/UVTD/src/main.cpp +++ b/UVTD/src/main.cpp @@ -12,12 +12,12 @@ using namespace RC; auto static get_user_selection() -> int32_t { - Output::send(STR("What would you like to do ?\n")); - Output::send(STR("1. Generate VTable layouts\n")); - Output::send(STR("2. Generate class/struct member variable layouts\n")); - Output::send(STR("3. Generate sol bindings\n")); - Output::send(STR("4. Everything\n")); - Output::send(STR("0. Exit\n")); + Output::send(SYSSTR("What would you like to do ?\n")); + Output::send(SYSSTR("1. Generate VTable layouts\n")); + Output::send(SYSSTR("2. Generate class/struct member variable layouts\n")); + Output::send(SYSSTR("3. Generate sol bindings\n")); + Output::send(SYSSTR("4. Everything\n")); + Output::send(SYSSTR("0. Exit\n")); int32_t selection{}; std::cin >> selection; @@ -53,7 +53,7 @@ auto thread_dll_start([[maybe_unused]] LPVOID thread_param) -> unsigned long try { - Output::send(STR("Unreal Virtual Table Dumper -> START\n")); + Output::send(SYSSTR("Unreal Virtual Table Dumper -> START\n")); for (int32_t selection = get_user_selection(); selection != 1337; selection = get_user_selection()) { @@ -64,22 +64,22 @@ auto thread_dll_start([[maybe_unused]] LPVOID thread_param) -> unsigned long } else if (selection == 1) { - Output::send(STR("Generating VTable layouts...\n")); + Output::send(SYSSTR("Generating VTable layouts...\n")); settings.should_dump_vtable = true; } else if (selection == 2) { - Output::send(STR("Generating class/struct member variable layouts...\n")); + Output::send(SYSSTR("Generating class/struct member variable layouts...\n")); settings.should_dump_member_vars = true; } else if (selection == 3) { - Output::send(STR("Generating sol bindings...\n")); + Output::send(SYSSTR("Generating sol bindings...\n")); settings.should_dump_sol_bindings = true; } else if (selection == 4) { - Output::send(STR("Generating VTable layouts and class/struct member variable layouts...\n")); + Output::send(SYSSTR("Generating VTable layouts and class/struct member variable layouts...\n")); settings.should_dump_vtable = true; settings.should_dump_member_vars = true; } @@ -89,7 +89,7 @@ auto thread_dll_start([[maybe_unused]] LPVOID thread_param) -> unsigned long } catch (std::exception& e) { - Output::send(STR("Exception caught: {}\n"), to_wstring(e.what())); + Output::send(SYSSTR("Exception caught: {}\n"), e.what()); } return 0; diff --git a/assets/UE4SS-settings.ini b/assets/UE4SS-settings.ini index 20e8657e1..ba5e098d5 100644 --- a/assets/UE4SS-settings.ini +++ b/assets/UE4SS-settings.ini @@ -4,7 +4,7 @@ ModsFolderPath = [General] -EnableHotReloadSystem = 1 +EnableHotReloadSystem = $EnableHotReloadSystem ; Whether the cache system for AOBs will be used. ; Default: 1 @@ -60,7 +60,7 @@ IgnoreAllCoreEngineModules = 0 ; Whether to skip generating the "Engine" and "CoreUObject" packages ; Default: 1 -IgnoreEngineAndCoreUObject = 0 +IgnoreEngineAndCoreUObject = $IgnoreEngineAndCoreUObject ; Whether to force all UFUNCTION macros to have "BlueprintCallable" ; Note: This will cause some errors in the generated headers that you will need to manually fix @@ -84,9 +84,9 @@ MakeAllConfigsEngineConfig = 1 [Debug] ; Whether to enable the external UE4SS debug console. -ConsoleEnabled = 1 +ConsoleEnabled = $ConsoleEnabled GuiConsoleEnabled = 1 -GuiConsoleVisible = 1 +GuiConsoleVisible = $GuiConsoleVisible ; Multiplier for Font Size within the Debug Gui ; Default: 1 @@ -95,7 +95,11 @@ GuiConsoleFontScaling = 1 ; The API that will be used to render the GUI debug window. ; Valid values (case-insensitive): dx11, d3d11, opengl ; Default: opengl +${if os == "linux64"} +GraphicsAPI = tui +${elsif os == "win64"} GraphicsAPI = opengl +${endif} [Threads] ; The number of threads that the sig scanner will use (not real cpu threads, can be over your physical & hyperthreading max) @@ -116,7 +120,7 @@ SigScannerMultithreadingModuleSizeThreshold = 16777216 ; The maximum memory usage (in percentage, see Task Manager %) allowed before asset loading (when LoadAllAssetsBefore* is 1) cannot happen. ; Once this percentage is reached, the asset loader will stop loading and whatever operation was in progress (object dump, or cxx generator) will continue. ; Default: 85 -MaxMemoryUsageDuringAssetLoading = 85 +MaxMemoryUsageDuringAssetLoading = $MaxMemoryUsageDuringAssetLoading [Hooks] HookProcessInternal = 1 @@ -135,4 +139,11 @@ FullMemoryDump = 0 [ExperimentalFeatures] ; Only enable these features if you know what you are doing. -GUIUFunctionCaller = 1 +GUIUFunctionCaller = $GUIUFunctionCaller + +${if os == "linux64"} +[TUI] +TUINerdFont=0 +TERMINFO=/usr/share/terminfo +LCALL=en_US.UTF-8 +${endif} diff --git a/deps/first/ASMHelper/include/ASMHelper/Common.hpp b/deps/first/ASMHelper/include/ASMHelper/Common.hpp index 1cf9fedad..c65782a06 100644 --- a/deps/first/ASMHelper/include/ASMHelper/Common.hpp +++ b/deps/first/ASMHelper/include/ASMHelper/Common.hpp @@ -1,5 +1,6 @@ #pragma once +#ifdef WIN32 #ifndef RC_ASM_HELPER_EXPORTS #ifndef RC_ASM_HELPER_BUILD_STATIC #ifndef RC_ASM_API @@ -15,3 +16,11 @@ #define RC_ASM_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_ASM_API +#define RC_ASM_API +#endif + +#endif diff --git a/deps/first/ASMHelper/src/ASMHelper.cpp b/deps/first/ASMHelper/src/ASMHelper.cpp index bf59ba5cd..8633dec55 100644 --- a/deps/first/ASMHelper/src/ASMHelper.cpp +++ b/deps/first/ASMHelper/src/ASMHelper.cpp @@ -4,6 +4,8 @@ #include #include +#include + namespace RC::ASM { auto get_first_instruction_at_address(void* in_instruction_ptr) -> Instruction @@ -56,7 +58,7 @@ namespace RC::ASM } else { - Output::send(STR("Was unable to resolve JMP instruction @ {}\n"), instruction.address); + Output::send(SYSSTR("Was unable to resolve JMP instruction @ {}\n"), instruction.address); return nullptr; } } diff --git a/deps/first/Constructs/include/Constructs/Annotated.hpp b/deps/first/Constructs/include/Constructs/Annotated.hpp index c22380ebb..53ee07a4b 100644 --- a/deps/first/Constructs/include/Constructs/Annotated.hpp +++ b/deps/first/Constructs/include/Constructs/Annotated.hpp @@ -24,7 +24,7 @@ namespace RC * Annotated, int> annotated_integer(123); * * for (const auto& comment : comments) { - * Output::send(STR("{}\n"), comment); + * Output::send(SYSSTR("{}\n"), comment); * } * @endcode */ diff --git a/deps/first/Constructs/include/Constructs/Generator.hpp b/deps/first/Constructs/include/Constructs/Generator.hpp index 6edae7e5d..f3f7196dd 100644 --- a/deps/first/Constructs/include/Constructs/Generator.hpp +++ b/deps/first/Constructs/include/Constructs/Generator.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +// #include #include namespace RC diff --git a/deps/first/Constructs/include/Constructs/Views/EnumerateView.hpp b/deps/first/Constructs/include/Constructs/Views/EnumerateView.hpp index 409dc7e36..cf921b6aa 100644 --- a/deps/first/Constructs/include/Constructs/Views/EnumerateView.hpp +++ b/deps/first/Constructs/include/Constructs/Views/EnumerateView.hpp @@ -1,5 +1,7 @@ #pragma once +#ifndef __clang__ + #include #include @@ -158,3 +160,95 @@ namespace RC inline constexpr details::EnumerateViewClosure enumerate; } } // namespace RC + +#else + +#include + +// A simple enumerate impl for clang to avoid https://github.com/llvm/llvm-project/issues/60704 +namespace RC +{ + + namespace views + { + template + struct internal_enumerate + { + T& iterable; + size_t index; + + template + struct iterator : std::iterator> + { + using iterator_category = std::input_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + size_t index; + It iter; + + explicit iterator(size_t index, It iter) : index(index), iter(iter) + { + } + + iterator& operator++() + { + ++index; + ++iter; + return *this; + } + + bool operator!=(const iterator& other) const + { + return iter != other.iter; + } + + value_type operator*() const + { + return {*iter, index}; + } + }; + + auto begin() + { + return iterator>(0, iterable.begin()); + } + + auto end() + { + return iterator>(0, iterable.end()); + } + + explicit internal_enumerate(T& iterable) : iterable(iterable), index(0) + { + } + + internal_enumerate operator|(T& iterable) + { + return internal_enumerate(iterable); + } + }; + + struct enumerate_op + { + }; + // operator | + template + internal_enumerate operator|(T& iterable, enumerate_op) + { + return internal_enumerate(iterable); + } + + // T&& version + template + internal_enumerate operator|(T&& iterable, enumerate_op) + { + return internal_enumerate(iterable); + } + + inline constexpr enumerate_op enumerate; + }; // namespace views +} // namespace RC +#endif // __clang__ diff --git a/deps/first/DynamicOutput/include/DynamicOutput/Common.hpp b/deps/first/DynamicOutput/include/DynamicOutput/Common.hpp index 173e00f16..1f8302b5c 100644 --- a/deps/first/DynamicOutput/include/DynamicOutput/Common.hpp +++ b/deps/first/DynamicOutput/include/DynamicOutput/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_DYNAMIC_OUTPUT_EXPORTS #ifndef RC_DYNAMIC_OUTPUT_BUILD_STATIC #ifndef RC_DYNOUT_API @@ -15,3 +17,11 @@ #define RC_DYNOUT_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_DYNOUT_API +#define RC_DYNOUT_API +#endif + +#endif \ No newline at end of file diff --git a/deps/first/DynamicOutput/include/DynamicOutput/DebugConsoleDevice.hpp b/deps/first/DynamicOutput/include/DynamicOutput/DebugConsoleDevice.hpp index b62cdf320..4538c9904 100644 --- a/deps/first/DynamicOutput/include/DynamicOutput/DebugConsoleDevice.hpp +++ b/deps/first/DynamicOutput/include/DynamicOutput/DebugConsoleDevice.hpp @@ -14,8 +14,9 @@ namespace RC::Output mutable bool m_windows_console_mode_set{}; private: +#ifdef WIN32 auto set_windows_console_out_mode_if_needed() const -> void; - +#endif public: public: #if ENABLE_OUTPUT_DEVICE_DEBUG_MODE @@ -34,8 +35,8 @@ namespace RC::Output public: auto has_optional_arg() const -> bool override; - auto receive(File::StringViewType fmt) const -> void override; - auto receive_with_optional_arg(File::StringViewType fmt, int32_t optional_arg = 0) const -> void override; + auto receive(SystemStringViewType fmt) const -> void override; + auto receive_with_optional_arg(SystemStringViewType fmt, int32_t optional_arg = 0) const -> void override; }; } // namespace RC::Output diff --git a/deps/first/DynamicOutput/include/DynamicOutput/FileDevice.hpp b/deps/first/DynamicOutput/include/DynamicOutput/FileDevice.hpp index a212e4080..5ce4e9f70 100644 --- a/deps/first/DynamicOutput/include/DynamicOutput/FileDevice.hpp +++ b/deps/first/DynamicOutput/include/DynamicOutput/FileDevice.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace RC::Output { @@ -57,7 +58,7 @@ namespace RC::Output { if (m_always_create_file) { - m_file = File::open(m_file_name_and_path, File::OpenFor::Appending, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); + m_file = File::open(m_file_name_and_path, File::OpenFor::Writing, File::OverwriteExistingFile::Yes, File::CreateIfNonExistent::Yes); } else { @@ -72,7 +73,7 @@ namespace RC::Output // Due to the design of the Output system the opening of the file is done in receive instead of in the constructor // It's opened only once and stays open until the Output object (not the device) leaves scope // The destructor is responsible for closing the file - auto receive(File::StringViewType fmt) const -> void override + auto receive(SystemStringViewType fmt) const -> void override { if (!m_is_device_ready) { @@ -82,11 +83,11 @@ namespace RC::Output // Do file output stuff here // File should already be open & be ready for writing (happens in constructor) - m_file.write_string_to_file(m_formatter(fmt)); + m_file.write_file_string_to_file(to_file(m_formatter(fmt))); } // OutputDevice Interface -> END - auto set_file_name_and_path(const File::StringType& file_name_and_path) -> void + auto set_file_name_and_path(const SystemStringType& file_name_and_path) -> void { m_file_name_and_path = file_name_and_path; } diff --git a/deps/first/DynamicOutput/include/DynamicOutput/Output.hpp b/deps/first/DynamicOutput/include/DynamicOutput/Output.hpp index ee2e0c087..0ef508aac 100644 --- a/deps/first/DynamicOutput/include/DynamicOutput/Output.hpp +++ b/deps/first/DynamicOutput/include/DynamicOutput/Output.hpp @@ -2,24 +2,32 @@ #define UE4SS_REWRITTEN_OUTPUT_HPP #include -#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include + +// #include +// #include #if RC_IS_ANSI == 1 #define RC_STD_MAKE_FORMAT_ARGS std::make_format_args #else +#ifdef WIN32 #define RC_STD_MAKE_FORMAT_ARGS std::make_wformat_args +#else +#define RC_STD_MAKE_FORMAT_ARGS std::make_format_args +#endif #endif namespace RC::Output @@ -29,6 +37,20 @@ namespace RC::Output using OutputDevicesContainerType = std::vector>; + static constexpr auto apply_formatting(auto&& content, auto&&... args) + { + // Must switch on the 'content' type at compile-time, otherwise std::string formatting + // won't work on Windows since RC_STD_MAKE_FORMAT_ARGS is set to std::wstring. + if constexpr (not_std_string_like_t>::value) + { + return std::vformat(content, std::make_wformat_args(args...)); + } + else + { + return std::vformat(content, std::make_format_args(args...)); + } + } + auto RC_DYNOUT_API has_internal_error() -> bool; template @@ -109,7 +131,7 @@ namespace RC::Output }; template - auto send(File::StringViewType content, OptionalArg optional_arg) -> void + auto send(SystemStringViewType content, OptionalArg optional_arg) -> void { if (m_opened_devices.empty()) { @@ -122,61 +144,65 @@ namespace RC::Output if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content), RC_STD_MAKE_FORMAT_ARGS(static_cast(optional_arg))); + device->receive_with_optional_arg(content, static_cast(optional_arg)); } else { - device->receive(std::vformat(content)); + device->receive(content); } } } template - auto send(File::StringViewType content, FmtArgs... fmt_args) -> void + auto send(SystemStringViewType&& content, FmtArgs&&... fmt_args) -> void { if (m_opened_devices.empty()) { THROW_INTERNAL_FILE_ERROR("[Output::send] Attempted to send but there were no opened devices."); } + auto formated = apply_formatting(content, to_system(std::forward(fmt_args))...); + for (const auto& device : m_opened_devices) { ASSERT_OUTPUT_DEVICE_IS_VALID(device) if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...)), 0); + device->receive_with_optional_arg(formated, 0); } else { - device->receive(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...))); + device->receive(formated); } } } template - auto send(File::StringViewType content, OptionalArg optional_arg, FmtArgs... fmt_args) -> void + auto send(SystemStringViewType content, OptionalArg optional_arg, FmtArgs&&... fmt_args) -> void { if (m_opened_devices.empty()) { THROW_INTERNAL_FILE_ERROR("[Output::send] Attempted to send but there were no opened devices."); } + auto formated = apply_formatting(content, to_system(std::forward(fmt_args))...); + for (const auto& device : m_opened_devices) { ASSERT_OUTPUT_DEVICE_IS_VALID(device) if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content, fmt_args...), RC_STD_MAKE_FORMAT_ARGS(static_cast(optional_arg))); + device->receive_with_optional_arg(formated, static_cast(optional_arg)); } else { - device->receive(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...))); + device->receive(formated); } } } - auto send(const File::StringType& content) -> void + auto send(const SystemStringType& content) -> void { if (m_opened_devices.empty()) { @@ -199,29 +225,31 @@ namespace RC::Output } template - auto send(File::StringViewType content, FmtArg fmt_arg, FmtArgs... fmt_args) -> void + auto send(SystemStringViewType content, FmtArg&& fmt_arg, FmtArgs&&... fmt_args) -> void { if (m_opened_devices.empty()) { THROW_INTERNAL_FILE_ERROR("[Output::send] Attempted to send but there were no opened devices."); } + auto formated = std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(to_system(std::forward(fmt_arg), std::forward(fmt_args))...)); + for (const auto& device : m_opened_devices) { ASSERT_OUTPUT_DEVICE_IS_VALID(device) if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_arg, fmt_args...)), optional_arg); + device->receive_with_optional_arg(formated, optional_arg); } else { - device->receive(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...))); + device->receive(formated); } } } template - auto send(const File::StringType& content) -> void + auto send(const SystemStringType& content) -> void { if (m_opened_devices.empty()) { @@ -278,47 +306,52 @@ namespace RC::Output DefaultTargets::set_default_log_level(log_level); } - template - auto send(File::StringViewType content, FmtArgs... fmt_args) -> void + template + auto send(StringView _content, FmtArgs&&... fmt_args) -> void { + auto content = to_system(_content); + auto formated = apply_formatting(content, to_system(std::forward(fmt_args))...); for (const auto& device : DefaultTargets::get_default_devices_ref()) { ASSERT_DEFAULT_OUTPUT_DEVICE_IS_VALID(device) if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...)), 0); + device->receive_with_optional_arg(formated, 0); } else { - device->receive(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...))); + device->receive(formated); } } } - template - auto send(File::StringViewType content, OptionalArg optional_arg, FmtArgs... fmt_args) -> void + template + auto send(StringView _content, OptionalArg optional_arg, FmtArgs&&... fmt_args) -> void { + auto content = to_system(_content); + auto formated = std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(to_system(std::forward(fmt_args))...)); for (const auto& device : DefaultTargets::get_default_devices_ref()) { ASSERT_DEFAULT_OUTPUT_DEVICE_IS_VALID(device) if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...)), static_cast(optional_arg)); + device->receive_with_optional_arg(formated, static_cast(optional_arg)); } else { - device->receive(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...))); + device->receive(formated); } } } - auto RC_DYNOUT_API send(File::StringViewType content) -> void; + auto RC_DYNOUT_API send(SystemStringViewType content) -> void; - template - auto send(File::StringViewType content, OptionalArg optional_arg) -> void + template + auto send(StringView _content, OptionalArg optional_arg) -> void { + auto content = to_system(_content); for (const auto& device : DefaultTargets::get_default_devices_ref()) { ASSERT_DEFAULT_OUTPUT_DEVICE_IS_VALID(device) @@ -334,27 +367,30 @@ namespace RC::Output } } - template - auto send(File::StringViewType content, FmtArgs... fmt_args) -> void + template + auto send(StringView _content, FmtArgs&&... fmt_args) -> void { + auto content = to_system(_content); + auto formated = apply_formatting(content, to_system(std::forward(fmt_args))...); for (const auto& device : DefaultTargets::get_default_devices_ref()) { ASSERT_DEFAULT_OUTPUT_DEVICE_IS_VALID(device) if (device->has_optional_arg()) { - device->receive_with_optional_arg(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...)), optional_arg); + device->receive_with_optional_arg(formated, optional_arg); } else { - device->receive(std::vformat(content, RC_STD_MAKE_FORMAT_ARGS(fmt_args...))); + device->receive(formated); } } } - template - auto send(File::StringViewType content) -> void + template + auto send(StringView _content) -> void { + auto content = to_system(_content); for (const auto& device : DefaultTargets::get_default_devices_ref()) { ASSERT_DEFAULT_OUTPUT_DEVICE_IS_VALID(device) diff --git a/deps/first/DynamicOutput/include/DynamicOutput/OutputDevice.hpp b/deps/first/DynamicOutput/include/DynamicOutput/OutputDevice.hpp index c7d65d6b0..5c69f46ba 100644 --- a/deps/first/DynamicOutput/include/DynamicOutput/OutputDevice.hpp +++ b/deps/first/DynamicOutput/include/DynamicOutput/OutputDevice.hpp @@ -46,7 +46,7 @@ namespace RC::Output mutable bool m_is_device_ready{}; // Formatter function - using Formatter = File::StringType (*)(File::StringViewType); + using Formatter = SystemStringType (*)(SystemStringViewType); Formatter m_formatter{&default_format_string}; public: @@ -55,10 +55,10 @@ namespace RC::Output public: virtual auto has_optional_arg() const -> bool; - virtual auto receive(File::StringViewType fmt) const -> void = 0; + virtual auto receive(SystemStringViewType fmt) const -> void = 0; // The 'optional_arg' type should be cast to the proper enum by the derived class - virtual auto receive_with_optional_arg(File::StringViewType fmt, int32_t optional_arg = 0) const -> void; + virtual auto receive_with_optional_arg(SystemStringViewType fmt, int32_t optional_arg = 0) const -> void; virtual auto lock() const -> void{}; @@ -68,8 +68,8 @@ namespace RC::Output auto set_formatter(Formatter new_formatter) -> void; protected: - auto static get_now_as_string() -> const File::StringType; - auto static default_format_string(File::StringViewType) -> File::StringType; + auto static get_now_as_string() -> const SystemStringType; + auto static default_format_string(SystemStringViewType) -> SystemStringType; }; } // namespace RC::Output diff --git a/deps/first/DynamicOutput/include/DynamicOutput/TestDevice.hpp b/deps/first/DynamicOutput/include/DynamicOutput/TestDevice.hpp index f8b8b371e..01859086e 100644 --- a/deps/first/DynamicOutput/include/DynamicOutput/TestDevice.hpp +++ b/deps/first/DynamicOutput/include/DynamicOutput/TestDevice.hpp @@ -38,12 +38,12 @@ namespace RC::Output return true; } - auto receive(File::StringViewType fmt) const -> void override + auto receive(SystemStringViewType fmt) const -> void override { receive_with_optional_arg(fmt, 0); } - auto receive_with_optional_arg(File::StringViewType fmt, int32_t optional_arg) const -> void override + auto receive_with_optional_arg(SystemStringViewType fmt, int32_t optional_arg) const -> void override { OptionalArgTest typed_optional_arg = static_cast(optional_arg); switch (typed_optional_arg) @@ -63,9 +63,9 @@ namespace RC::Output } #if ENABLE_OUTPUT_DEVICE_DEBUG_MODE - printf_s("TestDevice received: %S", fmt.c_str()); + printf_s("TestDevice received: " SystemStringPrint, fmt.c_str()); #else - printf_s("%S", fmt.data()); + printf_s(SystemStringPrint, fmt.data()); #endif } }; diff --git a/deps/first/DynamicOutput/src/DebugConsoleDevice.cpp b/deps/first/DynamicOutput/src/DebugConsoleDevice.cpp index 71ef287d6..06322c1d7 100644 --- a/deps/first/DynamicOutput/src/DebugConsoleDevice.cpp +++ b/deps/first/DynamicOutput/src/DebugConsoleDevice.cpp @@ -4,8 +4,11 @@ #include #include +#ifdef WIN32 #define NOMINMAX #include +#endif + #ifdef TEXT #undef TEXT #endif @@ -36,6 +39,7 @@ namespace RC::Output return "\033[0;0m"; } +#ifdef WIN32 auto DebugConsoleDevice::set_windows_console_out_mode_if_needed() const -> void { if (m_windows_console_mode_set) @@ -51,25 +55,33 @@ namespace RC::Output } m_windows_console_mode_set = true; } +#endif auto DebugConsoleDevice::has_optional_arg() const -> bool { return true; } - auto DebugConsoleDevice::receive(File::StringViewType fmt) const -> void + auto DebugConsoleDevice::receive(SystemStringViewType fmt) const -> void { receive_with_optional_arg(fmt, Color::NoColor); } - auto DebugConsoleDevice::receive_with_optional_arg(File::StringViewType fmt, [[maybe_unused]] int32_t optional_arg) const -> void + auto DebugConsoleDevice::receive_with_optional_arg(SystemStringViewType fmt, [[maybe_unused]] int32_t optional_arg) const -> void { +#ifdef WIN32 set_windows_console_out_mode_if_needed(); +#endif #if ENABLE_OUTPUT_DEVICE_DEBUG_MODE printf_s("DebugConsoleDevice received: %S", m_formatter(fmt).c_str()); #else - printf_s("%s%S\033[0m", log_level_to_color(static_cast(optional_arg)).c_str(), m_formatter(fmt).c_str()); +#ifdef WIN32 + printf_s("%s" SystemStringPrint "\033[0m", log_level_to_color(static_cast(optional_arg)).c_str(), m_formatter(fmt).c_str()); +#else + fprintf(stderr, "%s" SystemStringPrint "\033[0m", log_level_to_color(static_cast(optional_arg)).c_str(), m_formatter(fmt).c_str()); + +#endif #endif } } // namespace RC::Output diff --git a/deps/first/DynamicOutput/src/Output.cpp b/deps/first/DynamicOutput/src/Output.cpp index 1c6d7a73b..d2e2c9732 100644 --- a/deps/first/DynamicOutput/src/Output.cpp +++ b/deps/first/DynamicOutput/src/Output.cpp @@ -28,7 +28,7 @@ namespace RC::Output default_devices.clear(); } - auto send(File::StringViewType content) -> void + auto send(SystemStringViewType content) -> void { for (const auto& device : DefaultTargets::get_default_devices_ref()) { diff --git a/deps/first/DynamicOutput/src/OutputDevice.cpp b/deps/first/DynamicOutput/src/OutputDevice.cpp index 7f20019d3..df93faab6 100644 --- a/deps/first/DynamicOutput/src/OutputDevice.cpp +++ b/deps/first/DynamicOutput/src/OutputDevice.cpp @@ -10,7 +10,7 @@ namespace RC::Output return false; } - auto OutputDevice::receive_with_optional_arg([[maybe_unused]] File::StringViewType fmt, [[maybe_unused]] int32_t optional_arg) const -> void + auto OutputDevice::receive_with_optional_arg([[maybe_unused]] SystemStringViewType fmt, [[maybe_unused]] int32_t optional_arg) const -> void { // This only exists to make it not required to implement // Most devices probably won't use this @@ -22,15 +22,15 @@ namespace RC::Output m_formatter = new_formatter; } - auto OutputDevice::get_now_as_string() -> const File::StringType + auto OutputDevice::get_now_as_string() -> const SystemStringType { auto now = std::chrono::system_clock::now(); - const File::StringType when_as_string = std::format(STR("{:%Y-%m-%d %X}"), now); + const SystemStringType when_as_string = std::format(SYSSTR("{:%Y-%m-%d %X}"), now); return when_as_string; } - auto OutputDevice::default_format_string(File::StringViewType string_to_format) -> File::StringType + auto OutputDevice::default_format_string(SystemStringViewType string_to_format) -> SystemStringType { - return std::format(STR("[{}] {}"), get_now_as_string(), string_to_format); + return std::format(SYSSTR("[{}] {}"), get_now_as_string(), string_to_format); } } // namespace RC::Output diff --git a/deps/first/DynamicOutput/xmake.lua b/deps/first/DynamicOutput/xmake.lua index 52f91b97a..9078849a1 100644 --- a/deps/first/DynamicOutput/xmake.lua +++ b/deps/first/DynamicOutput/xmake.lua @@ -11,4 +11,13 @@ target(projectName) add_files("src/**.cpp") - add_deps("File") \ No newline at end of file + add_deps("File", "Helpers") + + on_load(function (target) + import("target_helpers", { rootdir = get_config("scriptsRoot") }) + + print("Project: " .. projectName .. " (STATIC)") + + target:add("defines", target_helpers.project_name_to_exports_define(projectName)) + target:add("defines", target_helpers.project_name_to_build_static_define(projectName)) + end) diff --git a/deps/first/File/include/File/Common.hpp b/deps/first/File/include/File/Common.hpp index 0aaf2cec5..caad17e9c 100644 --- a/deps/first/File/include/File/Common.hpp +++ b/deps/first/File/include/File/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_FILE_EXPORTS #ifndef RC_FILE_BUILD_STATIC #ifndef RC_FILE_API @@ -15,3 +17,11 @@ #define RC_FILE_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_FILE_API +#define RC_FILE_API +#endif + +#endif \ No newline at end of file diff --git a/deps/first/File/include/File/File.hpp b/deps/first/File/include/File/File.hpp index c0e38bf13..9f5049dbc 100644 --- a/deps/first/File/include/File/File.hpp +++ b/deps/first/File/include/File/File.hpp @@ -13,4 +13,8 @@ namespace RC::File CreateIfNonExistent = CreateIfNonExistent::No) -> Handle; RC_FILE_API auto delete_file(const std::filesystem::path& file_path_and_name) -> void; + + // Search a path that can access `base / tail`, where the tail part is allowed to be case insensitive. + // Returns the path found or std::nullopt if no path was found. + RC_FILE_API auto get_path_if_exists(const std::filesystem::path& base, const std::filesystem::path& tail) -> std::optional; } // namespace RC::File diff --git a/deps/first/File/include/File/FileDef.hpp b/deps/first/File/include/File/FileDef.hpp index 19efd9129..bf1a5258d 100644 --- a/deps/first/File/include/File/FileDef.hpp +++ b/deps/first/File/include/File/FileDef.hpp @@ -3,13 +3,31 @@ namespace RC::File { #ifndef RC_DETECTED_OS +#ifdef WIN32 #ifdef _WIN32 #define RC_DETECTED_OS _WIN32 #else static_assert(false, "Could not setup the 'Handle' typedef because a supported OS was not detected."); #endif + +#else + +#ifdef LINUX +#define RC_DETECTED_OS LINUX +#else + static_assert(false, "Could not setup the 'Handle' typedef because a supported OS was not detected."); #endif +#endif +#endif + +#if RC_DETECTED_OS == LINUX +#ifndef RC_OS_FILE_TYPE_INCLUDE_FILE +#define RC_OS_FILE_TYPE_INCLUDE_FILE +#else + static_assert(false, "Could not setup the 'RC_OS_FILE_TYPE_INCLUDE_FILE' macro because a supported OS was not detected."); +#endif +#endif #if RC_DETECTED_OS == _WIN32 #ifndef RC_OS_FILE_TYPE_INCLUDE_FILE #define RC_OS_FILE_TYPE_INCLUDE_FILE diff --git a/deps/first/File/include/File/FileType/All.hpp b/deps/first/File/include/File/FileType/All.hpp index 4c9ce44db..a9d5f52e2 100644 --- a/deps/first/File/include/File/FileType/All.hpp +++ b/deps/first/File/include/File/FileType/All.hpp @@ -1,3 +1,6 @@ #pragma once - +#ifdef WIN32 #include +#else +#include +#endif \ No newline at end of file diff --git a/deps/first/File/include/File/FileType/FileBase.hpp b/deps/first/File/include/File/FileType/FileBase.hpp index b648c3695..51332e3a7 100644 --- a/deps/first/File/include/File/FileType/FileBase.hpp +++ b/deps/first/File/include/File/FileType/FileBase.hpp @@ -74,7 +74,8 @@ namespace RC::File // Write a string to the currently opened file // Throws std::runtime_error if an error occurred - virtual auto write_string_to_file(StringViewType) -> void = 0; + // Keep this function use UEStringViewType so it's wstring_view and compatible with old FileInterface + virtual auto write_string_to_file(UEStringViewType) -> void = 0; // Returns whether the currently opened file is the same as another opened file // Throws std::runtime_error if an error occurred @@ -82,7 +83,8 @@ namespace RC::File // Returns the entire contents of the currently opened file as a string // Throws std::runtime_error if an error occurred - virtual auto read_all() const -> StringType = 0; + // Keep this function use UEStringViewType so it's wstring_view and compatible with old FileInterface + virtual auto read_all() const -> UEStringType = 0; virtual auto memory_map() -> std::span = 0; @@ -91,6 +93,16 @@ namespace RC::File // Throws std::runtime_error if an error occurred auto static open_file(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> InternalFileType; */ + + // Write a string to the currently opened file + // Throws std::runtime_error if an error occurred + // This is the actual function uses StringType. + virtual auto write_file_string_to_file(StringViewType) -> void = 0; + + // Returns the entire contents of the currently opened file as a string + // Throws std::runtime_error if an error occurred + // This is the actual function uses StringType. + virtual auto read_file_all() const -> StringType = 0; }; template diff --git a/deps/first/File/include/File/FileType/StdFile.hpp b/deps/first/File/include/File/FileType/StdFile.hpp new file mode 100644 index 000000000..b44e98deb --- /dev/null +++ b/deps/first/File/include/File/FileType/StdFile.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace RC::File +{ + class StdFile : public FileInterface + { + private: + using HANDLE = int; // ;FILE*; + + struct IdentifyingProperties + { + struct stat file_stat + { + }; + }; + + private: + HANDLE m_file{}; + uint8_t* m_memory_map{}; + size_t m_memory_map_size{}; + OpenProperties m_open_properties{}; + std::filesystem::path m_file_path_and_name{}; + std::filesystem::path m_serialization_file_path_and_name{}; + IdentifyingProperties m_identifying_properties{}; + constexpr static inline size_t cache_size = 0x500; + unsigned char m_cache[cache_size]{}; + size_t m_offset_to_next_serialized_item{}; + bool m_has_cache_in_memory{}; + bool m_has_cached_identifying_properties{}; + bool m_is_file_open{}; + + public: + ~StdFile() override = default; + + private: + auto static create_all_directories(const std::filesystem::path& file_name_and_path) -> void; + auto static calc_open_flags(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> int; + + private: + auto close_file() -> void; + + public: + [[nodiscard]] auto is_file_open() const -> bool; + + public: + RC_FILE_API auto set_file(HANDLE new_file) -> void; + RC_FILE_API auto set_is_file_open(bool new_is_open) -> void; + RC_FILE_API auto get_file() -> HANDLE; + RC_FILE_API auto serialization_file_exists() -> bool; + + // File Interface -> START + RC_FILE_API auto is_valid() noexcept -> bool override; + RC_FILE_API auto invalidate_file() noexcept -> void override; + RC_FILE_API auto static delete_file(const std::filesystem::path&) -> void; + RC_FILE_API auto delete_file() -> void override; + RC_FILE_API auto get_raw_handle() noexcept -> void* override; + [[nodiscard]] RC_FILE_API auto get_file_path() const noexcept -> const std::filesystem::path& override; + RC_FILE_API auto set_serialization_output_file(const std::filesystem::path& output_file) noexcept -> void override; + RC_FILE_API auto serialize_identifying_properties() -> void override; + RC_FILE_API auto deserialize_identifying_properties() -> void override; + RC_FILE_API auto is_deserialized_and_live_equal() -> bool override; + RC_FILE_API auto invalidate_serialization() -> void override; + RC_FILE_API auto serialize_item(const GenericItemData& data, bool is_internal_item = false) -> void override; + RC_FILE_API auto get_serialized_item(size_t data_size, bool is_internal_item = false) -> void* override; + RC_FILE_API auto close_current_file() -> void override; + RC_FILE_API auto write_string_to_file(UEStringViewType string_to_write) -> void override; + RC_FILE_API auto is_same_as(StdFile& other_file) -> bool override; + [[nodiscard]] RC_FILE_API auto read_all() const -> UEStringType override; + [[nodiscard]] RC_FILE_API auto memory_map() -> std::span override; + [[nodiscard]] RC_FILE_API auto static open_file(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> StdFile; + + [[nodiscard]] RC_FILE_API auto read_file_all() const -> SystemStringType override; + RC_FILE_API auto write_file_string_to_file(StringViewType) -> void override; + // File Interface -> END + }; + + // This file is automatically included ONLY if Windows is detected + // Therefore, it's not necessary to do any checks here + template + class HandleTemplate; + using Handle = HandleTemplate; +} // namespace RC::File diff --git a/deps/first/File/include/File/FileType/WinFile.hpp b/deps/first/File/include/File/FileType/WinFile.hpp index 4c630dcb9..89d075330 100644 --- a/deps/first/File/include/File/FileType/WinFile.hpp +++ b/deps/first/File/include/File/FileType/WinFile.hpp @@ -75,11 +75,13 @@ namespace RC::File RC_FILE_API auto serialize_item(const GenericItemData& data, bool is_internal_item = false) -> void override; RC_FILE_API auto get_serialized_item(size_t data_size, bool is_internal_item = false) -> void* override; RC_FILE_API auto close_current_file() -> void override; - RC_FILE_API auto write_string_to_file(StringViewType string_to_write) -> void override; + RC_FILE_API auto write_string_to_file(UEStringViewType string_to_write) -> void override; + RC_FILE_API auto write_file_string_to_file(StringViewType string_to_write) -> void override; RC_FILE_API auto is_same_as(WinFile& other_file) -> bool override; - [[nodiscard]] RC_FILE_API auto read_all() const -> StringType override; + [[nodiscard]] RC_FILE_API auto read_all() const -> UEStringType override; [[nodiscard]] RC_FILE_API auto memory_map() -> std::span override; [[nodiscard]] RC_FILE_API auto static open_file(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> WinFile; + [[nodiscard]] RC_FILE_API auto read_file_all() const -> StringType override; // File Interface -> END }; diff --git a/deps/first/File/include/File/HandleTemplate.hpp b/deps/first/File/include/File/HandleTemplate.hpp index 9cebc64a5..388a92194 100644 --- a/deps/first/File/include/File/HandleTemplate.hpp +++ b/deps/first/File/include/File/HandleTemplate.hpp @@ -159,16 +159,26 @@ namespace RC::File return *data; } - auto write_string_to_file(StringViewType string_to_write) -> void + auto write_string_to_file(UEStringViewType string_to_write) -> void { m_internal_handle.write_string_to_file(string_to_write); } - [[nodiscard]] auto read_all() const -> StringType + auto write_file_string_to_file(StringViewType string_to_write) -> void + { + m_internal_handle.write_file_string_to_file(string_to_write); + } + + [[nodiscard]] auto read_all() const -> UEStringType { return m_internal_handle.read_all(); } + [[nodiscard]] auto read_file_all() const -> StringType + { + return m_internal_handle.read_file_all(); + } + [[nodiscard]] auto memory_map() -> std::span { return m_internal_handle.memory_map(); diff --git a/deps/first/File/include/File/Macros.hpp b/deps/first/File/include/File/Macros.hpp index fb27b48a3..c8db3fd0c 100644 --- a/deps/first/File/include/File/Macros.hpp +++ b/deps/first/File/include/File/Macros.hpp @@ -1,14 +1,33 @@ #pragma once +#include +#include +#include + // Set this to 1 to use ANSI (char*) instead of wide strings (wchar_t*) #ifndef RC_IS_ANSI #define RC_IS_ANSI 0 #endif -#if RC_IS_ANSI == 1 +#ifdef LINUX +#define SYSSTR(str) str +#define IOSTR(str) str #define STR(str) u##str #else +#if RC_IS_ANSI == 0 +#define SYSSTR(str) L##str +#define IOSTR(str) str #define STR(str) L##str +#else +#define SYSSTR(str) str +#define IOSTR(str) str +#endif +#endif + +#ifdef S +static_assert(false, "UE4SS define 'S' is already defined, please solve this"); +#else +// #define S(str) STR(str) #endif #define THROW_INTERNAL_FILE_ERROR(msg) \ @@ -28,34 +47,93 @@ construct a Targets object and supply your own devices.") \ namespace RC::File { -#if RC_IS_ANSI == 1 using StringType = std::string; using StringViewType = std::string_view; using CharType = char; using StreamType = std::ifstream; - using ToString = std::tostring; + using IStreamType = std::ifstream; + using OStreamType = std::ofstream; + /* + #if RC_IS_ANSI == 1 + using StringType = std::string; + using StringViewType = std::string_view; + using CharType = char; + using StreamType = std::ifstream; + #else + // System String Types + #ifdef WIN32 + using StringType = std::wstring; + using StringViewType = std::wstring_view; + using CharType = std::wstring::value_type; + using StreamType = std::wifstream; + #else + // on linux, use utf8 + using StringType = std::string; + using StringViewType = std::string_view; + using CharType = char; + using StreamType = std::ifstream; + #endif // WIN32 + #endif // RC_IS_ANSI + */ +} // namespace RC::File +namespace RC +{ + // Should find a better place for these definitions + // System = C++ String Types +#if RC_IS_ANSI == 1 + using SystemStringType = std::string; + using SystemStringViewType = std::string_view; + using SystemCharType = char; + using SystemStreamType = std::ifstream; constexpr auto ToString = [](auto&& numeric_value) constexpr -> decltype(auto) { return std::to_string(std::forward(numeric_value)); }; +#define SystemStringPrint "%s" #else - using StringType = std::wstring; - using StringViewType = std::wstring_view; - using CharType = wchar_t; - using StreamType = std::wifstream; - +// System String Types +#ifdef WIN32 + using SystemStringType = std::wstring; + using SystemStringViewType = std::wstring_view; + using SystemCharType = std::wstring::value_type; + using SystemStreamType = std::wifstream; constexpr auto ToString = [](auto&& numeric_value) constexpr -> decltype(auto) { return std::to_wstring(std::forward(numeric_value)); }; +#define SystemStringPrint "%S" +#else + // on linux, use utf8 + using SystemStringType = std::string; + using SystemStringViewType = std::string_view; + using SystemCharType = char; + using SystemStreamType = std::ifstream; + constexpr auto ToString = [](auto&& numeric_value) constexpr -> decltype(auto) { + return std::to_string(std::forward(numeric_value)); + }; +#define SystemStringPrint "%s" +#endif // WIN32 #endif -} // namespace RC::File -namespace RC -{ - using StringType = File::StringType; - using StringViewType = File::StringViewType; - using CharType = File::CharType; - using StreamType = File::StreamType; +#if RC_IS_ANSI == 1 + using UEStringType = std::string; + using UEStringViewType = std::string_view; + using UECharType = std::string::value_type; +#define UEStringPrint "%s" +#else +#ifdef WIN32 + using UEStringType = std::wstring; + using UEStringViewType = std::wstring_view; + using UECharType = std::wstring::value_type; + using UEIStreamType = std::wifstream; + using UEOStreamType = std::wofstream; +#else + using UEStringType = std::u16string; + using UEStringViewType = std::u16string_view; + using UECharType = std::u16string::value_type; + using UEIStreamType = std::basic_ifstream; + using UEOStreamType = std::basic_ofstream; +#endif // WIN32 +#define UEStringPrint "%S" +#endif // RC_IS_ANSI - constexpr auto ToString = File::ToString; } // namespace RC diff --git a/deps/first/File/src/File.cpp b/deps/first/File/src/File.cpp index 6869475cb..5f2bd8103 100644 --- a/deps/first/File/src/File.cpp +++ b/deps/first/File/src/File.cpp @@ -1,4 +1,7 @@ #include +#include + +#include namespace RC::File { @@ -26,4 +29,46 @@ namespace RC::File { Handle::FileType::delete_file(file_path_and_name); } + + auto get_path_if_exists(const std::filesystem::path& base, const std::filesystem::path& tail) -> std::optional + { + if (!std::filesystem::exists(base)) + { + throw std::runtime_error("Base path does not exist: " + base.string()); + } + if (tail.is_absolute()) + { + throw std::runtime_error("Tail path must be relative: " + tail.string()); + } + auto normalized_tail = tail.lexically_normal(); + auto full_path = base.lexically_normal(); + for (const auto& part : normalized_tail) + { + if (std::filesystem::exists(full_path / part)) + { + full_path /= part; + } + else + { + bool found = false; + for (const auto& entry : std::filesystem::directory_iterator(full_path)) + { + // case-insensitive comparison + auto entry_filename = entry.path().filename().string(); + auto part_filename = part.string(); + if (String::iequal(entry_filename, part_filename)) + { + full_path /= entry.path().filename(); + found = true; + break; + } + } + if (!found) + { + return std::nullopt; + } + } + } + return full_path; + } } // namespace RC::File \ No newline at end of file diff --git a/deps/first/File/src/FileType/StdFile.cpp b/deps/first/File/src/FileType/StdFile.cpp new file mode 100644 index 000000000..70ca0e889 --- /dev/null +++ b/deps/first/File/src/FileType/StdFile.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include +#include +#include + +#include +#include + +namespace RC::File +{ + auto StdFile::create_all_directories(const std::filesystem::path& file_name_and_path) -> void + { + if (file_name_and_path.parent_path().empty()) + { + return; + } + + try + { + std::filesystem::create_directories(file_name_and_path.parent_path()); + } + catch (const std::filesystem::filesystem_error& e) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::create_all_directories] Tried creating directories '{}' but encountered an error. Error: {}", + file_name_and_path.string(), + e.what())) + } + } + + auto StdFile::calc_open_flags(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> int + { + + int flags{}; + int rwflags{}; + switch (open_properties.open_for) + { + case OpenFor::Writing: + rwflags = O_WRONLY; + break; + case OpenFor::Appending: + rwflags = O_WRONLY; + flags = O_APPEND; + break; + case OpenFor::Reading: + rwflags = O_RDONLY; + break; + default: + printf("open_for: %d\n", open_properties.open_for); + THROW_INTERNAL_FILE_ERROR("[StdFile::open_file] Tried to open file but received invalid data for the 'OpenFor' parameter.") + } + + if (open_properties.overwrite_existing_file == OverwriteExistingFile::Yes) + { + flags |= O_CREAT | O_TRUNC; + if (rwflags == O_RDONLY) + { + rwflags = O_RDWR; + } + create_all_directories(file_name_and_path); + } + else if (open_properties.create_if_non_existent == CreateIfNonExistent::Yes) + { + flags |= O_CREAT; + if (rwflags == O_RDONLY) + { + rwflags = O_RDWR; + } + create_all_directories(file_name_and_path); + } + else + { + if (!std::filesystem::exists(file_name_and_path)) + { + printf("file_name_and_path: %s not exists\n", file_name_and_path.string().c_str()); + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::open_file] Tried opening file but file does not exist: {}", file_name_and_path.string())) + } + } + + return rwflags | flags; + } + + auto StdFile::is_valid() noexcept -> bool + { + return m_file > 0; + } + + auto StdFile::invalidate_file() noexcept -> void + { + m_file = 0; + } + + auto StdFile::delete_file(const std::filesystem::path& file_path_and_name) -> void + { + std::filesystem::remove(file_path_and_name); + } + + auto StdFile::delete_file() -> void + { + if (m_is_file_open) + { + close_file(); + } + + delete_file(m_file_path_and_name); + } + + auto StdFile::set_file(HANDLE new_file) -> void + { + m_file = new_file; + } + + auto StdFile::get_file() -> HANDLE + { + return m_file; + } + + auto StdFile::set_is_file_open(bool new_is_open) -> void + { + m_is_file_open = new_is_open; + } + + auto StdFile::get_raw_handle() noexcept -> void* + { + long a = m_file; + return (void*)(a); + } + + auto StdFile::get_file_path() const noexcept -> const std::filesystem::path& + { + return m_file_path_and_name; + } + + auto StdFile::set_serialization_output_file(const std::filesystem::path& output_file) noexcept -> void + { + m_serialization_file_path_and_name = output_file; + } + + auto StdFile::serialization_file_exists() -> bool + { + return std::filesystem::exists(m_serialization_file_path_and_name); + } + + template + auto write_to_file(StdFile& file, DataType* data, size_t num_bytes_to_write) -> void + { + if (!file.is_file_open()) + { + THROW_INTERNAL_FILE_ERROR("[StdFile::write_to_file] Tried writing to file but the file is not open") + } + + size_t bytes_written = write(file.get_file(), data, sizeof(DataType) * num_bytes_to_write); + if (bytes_written < 0) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::write_to_file] Tried writing to file but was unable to complete operation. Error: {}", bytes_written)); + } + } + + auto StdFile::serialize_identifying_properties() -> void + { + if (m_serialization_file_path_and_name.empty()) + { + THROW_INTERNAL_FILE_ERROR("[StdFile::serialize_identifying_properties]: Path & file name for serialization file is empty, please call " + "'set_serialization_output_file'") + } + + struct stat file_info + { + }; + fstat(m_file, &file_info); + + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_dev}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_ino}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_mode}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_nlink}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_uid}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_gid}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::UnsignedLong, .data_ulong = file_info.st_rdev}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::SignedLong, .data_long = file_info.st_size}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::SignedLong, .data_long = file_info.st_atime}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::SignedLong, .data_long = file_info.st_mtime}, true); + serialize_item(GenericItemData{.data_type = GenericDataType::SignedLong, .data_long = file_info.st_ctime}, true); + } + + auto StdFile::deserialize_identifying_properties() -> void + { + m_identifying_properties.file_stat.st_dev = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_ino = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_mode = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_nlink = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_uid = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_gid = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_rdev = *static_cast(get_serialized_item(sizeof(unsigned long), true)); + m_identifying_properties.file_stat.st_size = *static_cast(get_serialized_item(sizeof(signed long), true)); + m_identifying_properties.file_stat.st_atime = *static_cast(get_serialized_item(sizeof(signed long), true)); + m_identifying_properties.file_stat.st_mtime = *static_cast(get_serialized_item(sizeof(signed long), true)); + m_identifying_properties.file_stat.st_ctime = *static_cast(get_serialized_item(sizeof(signed long), true)); + m_offset_to_next_serialized_item = 11 * 8; + + m_has_cached_identifying_properties = true; + } + + auto StdFile::is_deserialized_and_live_equal() -> bool + { + if (!m_has_cached_identifying_properties) + { + if (!std::filesystem::exists(m_serialization_file_path_and_name)) + { + return false; + } + else + { + deserialize_identifying_properties(); + } + } + + return true; + } + + auto StdFile::invalidate_serialization() -> void + { + if (m_serialization_file_path_and_name.empty()) + { + THROW_INTERNAL_FILE_ERROR("[StdFile::invalidate_serialization] Could not invalidate serialization file because " + "'m_serialization_file_path_and_name' was empty, please call 'set_serialization_output_file'") + } + + if (std::filesystem::exists(m_serialization_file_path_and_name)) + { + delete_file(m_serialization_file_path_and_name); + } + } + + template + auto serialize_typed_item(DataType data, Handle& output_file) -> void + { + write_to_file(output_file.get_underlying_type(), &data, sizeof(DataType)); + } + + auto StdFile::serialize_item(const GenericItemData& data, bool is_internal_item) -> void + { + if (m_serialization_file_path_and_name.empty()) + { + THROW_INTERNAL_FILE_ERROR( + "[StdFile::serialize_item]: Path & file name for serialization file is empty, please call 'set_serialization_output_file'") + } + + if (!serialization_file_exists() && !is_internal_item) + { + // If the serialization cache file doesn't exist & this is not an identifying property item, + // then we need to serialize the identifying properties before continuing + serialize_identifying_properties(); + } + + Handle serialization_file = open(m_serialization_file_path_and_name, OpenFor::Appending, OverwriteExistingFile::No, CreateIfNonExistent::Yes); + + switch (data.data_type) + { + case GenericDataType::UnsignedLong: + serialize_typed_item(data.data_ulong, serialization_file); + serialization_file.get_underlying_type().m_offset_to_next_serialized_item += sizeof(unsigned long); + break; + case GenericDataType::SignedLong: + serialize_typed_item(data.data_long, serialization_file); + serialization_file.get_underlying_type().m_offset_to_next_serialized_item += sizeof(signed long); + break; + case GenericDataType::UnsignedLongLong: + serialize_typed_item(data.data_ulonglong, serialization_file); + serialization_file.get_underlying_type().m_offset_to_next_serialized_item += sizeof(unsigned long long); + break; + case GenericDataType::SignedLongLong: + serialize_typed_item(data.data_longlong, serialization_file); + serialization_file.get_underlying_type().m_offset_to_next_serialized_item += sizeof(signed long long); + break; + } + + serialization_file.close(); + } + + auto StdFile::get_serialized_item(size_t data_size, bool is_internal_item) -> void* + { + if (!m_has_cache_in_memory) + { + if (m_serialization_file_path_and_name.empty()) + { + THROW_INTERNAL_FILE_ERROR( + "[StdFile::get_serialized_item]: Path & file name for serialization file is empty, please call 'set_serialization_output_file'") + } + + Handle cache_file = open(m_serialization_file_path_and_name); + auto bytes_read = read(reinterpret_cast(cache_file.get_raw_handle()), &m_cache, cache_size); + if (bytes_read < 0) + { + THROW_INTERNAL_FILE_ERROR( + std::format("[StdFile::get_serialized_item] Tried deserializing file but was unable to complete operation. Error: {}", bytes_read)) + } + + cache_file.close(); + + m_has_cache_in_memory = true; + } + + if (!m_has_cached_identifying_properties && !is_internal_item) + { + deserialize_identifying_properties(); + } + + void* data_ptr = &m_cache[m_offset_to_next_serialized_item]; + m_offset_to_next_serialized_item += data_size; + return data_ptr; + } + + auto StdFile::close_current_file() -> void + { + close_file(); + } + + auto StdFile::close_file() -> void + { + if (m_memory_map) + { + size_t aligned_size = m_memory_map_size & ~(sysconf(_SC_PAGE_SIZE) - 1); + if (munmap(m_memory_map, aligned_size) != 0) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::close_file] Was unable to unmap file, error: {}", errno)) + } + else + { + m_memory_map = nullptr; + m_memory_map_size = 0; + } + } + + if (!is_valid() || !is_file_open()) + { + return; + } + + if (close(m_file) == -1) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::close_file] Was unable to close file, error: {}", errno)) + } + else + { + m_file = 0; + set_is_file_open(false); + } + } + + auto StdFile::is_file_open() const -> bool + { + return m_is_file_open; + } + + auto StdFile::write_string_to_file(UEStringViewType string_to_write) -> void + { + try + { + static std::wstring_convert, char16_t> converter{}; + auto utf8_string = converter.to_bytes(string_to_write.begin(), string_to_write.begin() + string_to_write.size()); + write_file_string_to_file(utf8_string); + } + catch (const std::exception& e) + { + THROW_INTERNAL_FILE_ERROR( + std::format("[StdFile::write_string_to_file] Tried writing string to file but could not convert to utf-8. Error: {}", e.what())) + } + } + + auto StdFile::write_file_string_to_file(StringViewType string_to_write) -> void + { + try + { + write_to_file(*this, string_to_write.data(), string_to_write.size()); + } + catch (const std::exception& e) + { + THROW_INTERNAL_FILE_ERROR( + std::format("[StdFile::write_string_to_file] Tried writing string to file but could not convert to utf-8. Error: {}", e.what())) + } + } + + auto StdFile::is_same_as(StdFile& other_file) -> bool + { + struct stat file_info + { + }; + if (fstat(m_file, &file_info) != 0) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::is_same_as] Tried retrieving file information by handle. Error: {}", errno)) + } + + struct stat other_file_info + { + }; + if (fstat(other_file.get_file(), &other_file_info) != 0) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::is_same_as] Tried retrieving file information by handle. Error: {}", errno)) + } + + if (file_info.st_dev != other_file_info.st_dev) + { + return false; + } + + if (file_info.st_ino != other_file_info.st_ino) + { + return false; + } + + if (file_info.st_mode != other_file_info.st_mode) + { + return false; + } + + if (file_info.st_nlink != other_file_info.st_nlink) + { + return false; + } + + if (file_info.st_uid != other_file_info.st_uid) + { + return false; + } + + if (file_info.st_gid != other_file_info.st_gid) + { + return false; + } + + if (file_info.st_rdev != other_file_info.st_rdev) + { + return false; + } + + if (file_info.st_size != other_file_info.st_size) + { + return false; + } + + if (file_info.st_mtime != other_file_info.st_mtime) + { + return false; + } + + if (file_info.st_ctime != other_file_info.st_ctime) + { + return false; + } + + return true; + } + + auto StdFile::read_file_all() const -> StringType + { + auto file_name_and_path = get_file_path(); + int flags = O_RDONLY; + int readfd = ::open(file_name_and_path.string().c_str(), flags); + if (readfd < 0) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::read_all] Tried to read entire file but returned error {}", errno)) + } + else + { + // Strip the BOM if it exists + File::CharType bom[3]{}; + read(readfd, bom, 3); + off_t start = 0; + if ((unsigned char)bom[0] == 0xEF && (unsigned char)bom[1] == 0xBB && (unsigned char)bom[2] == 0xBF) + { + // BOM: UTF-8 + start = 3; + } + + File::StringType file_contents; + ssize_t size = lseek(readfd, 0, SEEK_END); + lseek(readfd, start, SEEK_SET); + if (size == -1) + { + return {}; + } + file_contents.resize(size - start); + read(readfd, &file_contents[0], size - start); + close(readfd); + return file_contents; + } + } + + auto StdFile::read_all() const -> UEStringType + { + auto utf8_string = read_file_all(); + return std::wstring_convert, char16_t>{}.from_bytes(utf8_string); + } + + auto StdFile::memory_map() -> std::span + { + int flags = PROT_NONE; + switch (m_open_properties.open_for) + { + case OpenFor::Writing: + case OpenFor::Appending: + flags |= PROT_WRITE; + case OpenFor::Reading: + flags |= PROT_READ; + break; + default: + THROW_INTERNAL_FILE_ERROR("[StdFile::memory_map] Tried to memory map file but 'm_open_properties' contains invalid data.") + } + + // seek to get size to map while keep original file position + auto original_position = lseek(m_file, 0, SEEK_CUR); + auto file_size = lseek(m_file, 0, SEEK_END); + lseek(m_file, original_position, SEEK_SET); + + auto res = mmap(nullptr, file_size, flags, MAP_SHARED, m_file, 0); + if (res == (void*)-1) + { + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::memory_map] Tried to memory map file but 'mmap' returned error: {}", errno)) + } + + m_memory_map = (uint8_t*)res; + m_memory_map_size = file_size; + + return std::span(m_memory_map, file_size); + } + + auto StdFile::open_file(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> StdFile + { + // Reminder: std::filesystem::canonical() will get the full path & file name on the drive + // It only works if the directory already exists so check that first + // Should CreateFile take the canonical path ? + + if (file_name_and_path.empty()) + { + THROW_INTERNAL_FILE_ERROR("[StdFile::open_file] Tried to open file but file_name_and_path was empty.") + } + + int flags = calc_open_flags(file_name_and_path, open_properties); + + StdFile file{}; + + // This very badly named API may create a new file or it may not but it will always open a file (unless there's an error) + int fd = ::open(file_name_and_path.string().c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + + if (fd > 0) + { + file.set_file(fd); + } + else + { + std::string_view open_type = open_properties.open_for == OpenFor::Writing || open_properties.open_for == OpenFor::Appending ? "writing" : "reading"; + + fprintf(stderr, "open file %s failed, error: %d\n", file_name_and_path.string().c_str(), errno); + THROW_INTERNAL_FILE_ERROR(std::format("[StdFile::open_file] Tried opening file for {} but encountered an error. Path & File: {} | errno = {}\n", + open_type, + file_name_and_path.string(), + errno)) + } + + file.m_file_path_and_name = file_name_and_path; + file.set_is_file_open(true); + file.m_open_properties = open_properties; + + return file; + } +} // namespace RC::File diff --git a/deps/first/File/src/FileType/WinFile.cpp b/deps/first/File/src/FileType/WinFile.cpp index 0cbfd2f84..c1f247fa8 100644 --- a/deps/first/File/src/FileType/WinFile.cpp +++ b/deps/first/File/src/FileType/WinFile.cpp @@ -26,7 +26,7 @@ namespace RC::File auto WinFile::delete_file(const std::filesystem::path& file_path_and_name) -> void { - if constexpr (sizeof(CharType) > 1) + if constexpr (sizeof(SystemCharType) > 1) { if (DeleteFileW(file_path_and_name.wstring().c_str()) == 0) { @@ -35,7 +35,7 @@ namespace RC::File } else { - if (DeleteFileA(file_path_and_name.string().c_str()) != 0) + if (DeleteFileA(file_path_and_name.string().c_str()) == 0) { THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::delete_file] Was unable to delete file, error: {}", GetLastError())) } @@ -380,7 +380,7 @@ namespace RC::File return m_is_file_open; } - auto WinFile::write_string_to_file(StringViewType string_to_write) -> void + auto WinFile::write_string_to_file(UEStringViewType string_to_write) -> void { int string_size = WideCharToMultiByte(CP_UTF8, 0, string_to_write.data(), static_cast(string_to_write.size()), NULL, 0, NULL, NULL); if (string_size == 0) @@ -399,6 +399,11 @@ namespace RC::File write_to_file(*this, string_converted_to_utf8.c_str(), string_size); } + auto WinFile::write_file_string_to_file(StringViewType string_to_write) -> void + { + write_to_file(*this, string_to_write.data(), string_to_write.length()); + } + auto WinFile::is_same_as(WinFile& other_file) -> bool { BY_HANDLE_FILE_INFORMATION file_info{}; @@ -452,38 +457,85 @@ namespace RC::File return true; } - auto WinFile::read_all() const -> StringType + auto WinFile::read_all() const -> UEStringType { - StreamType stream{get_file_path(), std::ios::in | std::ios::binary}; - if (!stream) + auto file_contents = read_file_all(); + int string_size = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)file_contents.c_str(), static_cast(file_contents.size()), NULL, 0); + if (string_size == 0) { - THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_all] Tried to read entire file but returned error {}", errno)) + THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_all] Tried reading entire file but could not convert to utf-16. Error: {}", GetLastError())) } - else + + UEStringType string_converted_to_utf16(string_size, 0); + if (MultiByteToWideChar(CP_UTF8, 0, (LPCCH)file_contents.c_str(), static_cast(file_contents.size()), &string_converted_to_utf16[0], string_size) == 0) + { + THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_all] Tried reading entire file but could not convert to utf-16. Error: {}", GetLastError())) + } + + return string_converted_to_utf16; + } + + auto WinFile::read_file_all() const -> StringType + { + // get file handle, const_cast is safe here because we will restore the file pointer + auto handle = const_cast(this)->get_file(); + + // backup file pointer + auto file_pointer = SetFilePointer(handle, 0, nullptr, FILE_CURRENT); + + LONG file_size{}; + if (!GetFileSizeEx(handle, (PLARGE_INTEGER)&file_size)) { - // Strip the BOM if it exists - File::StreamType::off_type start{}; - File::CharType bom[3]{}; - stream.read(bom, 3); - if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) + THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_file_all] Tried reading entire file but could not get file size. Error: {}", GetLastError())) + } + + // move pointer to begin + SetFilePointer(handle, 0, nullptr, FILE_BEGIN); + + if (file_size >= 3ul) + { + // check BOM + constexpr size_t bom_size = 3; + char bom[bom_size]; + DWORD bytes_read{}; + + if (!ReadFile(handle, bom, bom_size, &bytes_read, nullptr)) { - // BOM: UTF-8 - start = 3; + // try restoring file pointer + SetFilePointer(handle, file_pointer, nullptr, FILE_BEGIN); + THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_file_all] Tried reading entire file but could not read BOM. Error: {}", GetLastError())) } - - StringType file_contents; - stream.seekg(0, std::ios::end); - auto size = stream.tellg(); - if (size == -1) + if (bytes_read != bom_size) + { + // try restoring file pointer + SetFilePointer(handle, file_pointer, nullptr, FILE_BEGIN); + THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_file_all] Tried reading entire file but could not read BOM. Error: {}", GetLastError())) + } + if (bom[0] == '\xEF' && bom[1] == '\xBB' && bom[2] == '\xBF') { - return {}; + file_size -= 3; + } + else + { + // roll back + SetFilePointer(handle, 0, nullptr, FILE_BEGIN); } - file_contents.resize(size); - stream.seekg(start, std::ios::beg); - stream.read(&file_contents[0], file_contents.size()); - stream.close(); - return file_contents; } + + StringType file_contents(file_size, 0); + DWORD bytes_read{}; + if (!ReadFile(handle, &file_contents[0], file_size, &bytes_read, nullptr)) + { + // try restoring file pointer + SetFilePointer(handle, file_pointer, nullptr, FILE_BEGIN); + THROW_INTERNAL_FILE_ERROR(std::format("[WinFile::read_file_all] Tried reading entire file but could not read file contents. Error: {}", GetLastError())) + } + + file_contents[bytes_read] = 0; + + // restore file pointer + SetFilePointer(handle, file_pointer, nullptr, FILE_BEGIN); + return file_contents; } auto WinFile::memory_map() -> std::span @@ -572,7 +624,7 @@ namespace RC::File WinFile file{}; // This very badly named API may create a new file or it may not but it will always open a file (unless there's an error) - if constexpr (sizeof(CharType) > 1) + if constexpr (sizeof(SystemCharType) > 1) { file.set_file(CreateFileW(file_name_and_path.wstring().c_str(), desired_access, diff --git a/deps/first/File/xmake.lua b/deps/first/File/xmake.lua index 4d8b77072..2c4c90b23 100644 --- a/deps/first/File/xmake.lua +++ b/deps/first/File/xmake.lua @@ -9,4 +9,21 @@ target(projectName) add_includedirs("include", { public = true }) add_headerfiles("include/**.hpp") - add_files("src/**.cpp") \ No newline at end of file + add_files("src/File.cpp") + + if is_plat("windows") then + add_files("src/FileType/WinFile.cpp") + elseif is_plat("linux") then + add_files("src/FileType/StdFile.cpp") + end + + add_deps("Helpers") + + on_load(function (target) + import("target_helpers", { rootdir = get_config("scriptsRoot") }) + + print("Project: " .. projectName .. " (STATIC)") + + target:add("defines", target_helpers.project_name_to_exports_define(projectName)) + target:add("defines", target_helpers.project_name_to_build_static_define(projectName)) + end) diff --git a/deps/first/Function/include/Function/Function.hpp b/deps/first/Function/include/Function/Function.hpp index b3491cd74..55be64fd5 100644 --- a/deps/first/Function/include/Function/Function.hpp +++ b/deps/first/Function/include/Function/Function.hpp @@ -3,6 +3,7 @@ #include #include #include +#include // Note: When we assign to 'm_function_address' from a function pointer, we're probably breaking some rules. // Though it's likely, a function is not guaranteed to be represented as a pointer. diff --git a/deps/first/Helpers/include/Helpers/Casting.hpp b/deps/first/Helpers/include/Helpers/Casting.hpp index 176b37aee..739e06faa 100644 --- a/deps/first/Helpers/include/Helpers/Casting.hpp +++ b/deps/first/Helpers/include/Helpers/Casting.hpp @@ -2,13 +2,21 @@ #include #include +#include +#ifdef WIN32 #ifdef _WIN32 #define WINDOWS #define NOMINMAX #include "Windows.h" #endif +#else + +#include + +#endif + namespace RC::Helper::Casting { template @@ -67,5 +75,40 @@ namespace RC::Helper::Casting { return ptr_cast_deref_safe(base_ptr, offset, process_handle); } +#else + // use process_vm_readv + template + auto ptr_cast_deref_safe(From ptr, int32_t offset, pid_t process_handle) -> To + { + static_assert(std::is_pointer_v, "ptr_cast_deref_safe can only cast from pointer types"); + + // For this example, To == UObject* + // Therefore: To* == UObject** + To* data_ptr = std::bit_cast(std::bit_cast(ptr) + offset); + + size_t bytes_read; + uintptr_t is_valid_ptr_buffer; + struct iovec local[1]; + struct iovec remote[1]; + local[0].iov_base = &is_valid_ptr_buffer; + local[0].iov_len = sizeof(is_valid_ptr_buffer); + remote[0].iov_base = data_ptr; + remote[0].iov_len = sizeof(is_valid_ptr_buffer); + if (process_vm_readv(process_handle, local, 1, remote, 1, 0) < 0) + { + return 0; + } + else + { + return *data_ptr; + } + } + + // Compatibility with older code from before 'ptr_cast' existed + template + static auto offset_deref_safe(From* base_ptr, int32_t offset, pid_t process_handle) -> To + { + return ptr_cast_deref_safe(base_ptr, offset, process_handle); + } #endif } // namespace RC::Helper::Casting diff --git a/deps/first/Helpers/include/Helpers/Format.hpp b/deps/first/Helpers/include/Helpers/Format.hpp index 329f91292..49b2858c0 100644 --- a/deps/first/Helpers/include/Helpers/Format.hpp +++ b/deps/first/Helpers/include/Helpers/Format.hpp @@ -1,56 +1,22 @@ #pragma once #include +#include + +#include +#include namespace RC { - template - auto static fmt(const char* fmt, Args... args) -> std::string + template + auto static fmt(const std::string_view&& fmt, FmtArgs&&... fmt_args) -> std::string { - constexpr size_t out_string_length = 1000; - char out_string[out_string_length]; - - size_t msg_len = strlen(fmt); - - // Attempt to give a hint if the buffer is too small - if (msg_len > out_string_length) - { - fmt = "An error occurred but the message was too long for the buffer."; - msg_len = strlen(fmt); - } - - // If the buffer is too small for the hint message then I guess we do nothing - // The default message will be used which can't be too small since it's calculated at compile-time - if (msg_len < out_string_length) - { - sprintf_s(out_string, out_string_length, fmt, args...); - } - - return out_string; + return Output::apply_formatting(fmt, to_stdstr(std::forward(fmt_args))...); } - template - auto static fmt(const wchar_t* fmt, Args... args) -> std::wstring + template + auto static fmtfile(const File::StringViewType&& fmt, FmtArgs&&... fmt_args) -> std::string { - constexpr size_t out_string_length = 1000; - wchar_t out_string[out_string_length]; - - size_t msg_len = wcslen(fmt); - - // Attempt to give a hint if the buffer is too small - if (msg_len > out_string_length) - { - fmt = L"An error occurred but the message was too long for the buffer."; - msg_len = wcslen(fmt); - } - - // If the buffer is too small for the hint message then I guess we do nothing - // The default message will be used which can't be too small since it's calculated at compile-time - if (msg_len < out_string_length) - { - swprintf_s(out_string, out_string_length, fmt, args...); - } - - return out_string; + return Output::apply_formatting(fmt, to_file(std::forward(fmt_args))...); } -} // namespace RC +} // namespace RC \ No newline at end of file diff --git a/deps/first/Helpers/include/Helpers/String.hpp b/deps/first/Helpers/include/Helpers/String.hpp index e8f964060..32cc44532 100644 --- a/deps/first/Helpers/include/Helpers/String.hpp +++ b/deps/first/Helpers/include/Helpers/String.hpp @@ -7,6 +7,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -44,6 +48,23 @@ namespace RC return return_value; } + auto inline explode_by_occurrence(const std::u16string& in_str_wide, const char16_t delimiter, ExplodeType start_or_end) -> std::u16string + { + size_t occurrence = (start_or_end == ExplodeType::FromStart ? in_str_wide.find_first_of(delimiter) : in_str_wide.find_last_of(delimiter)); + + std::u16string return_value; + if (occurrence != std::wstring::npos) + { + return_value = start_or_end == ExplodeType::FromEnd ? in_str_wide.substr(occurrence + 1, std::u16string::npos) : in_str_wide.substr(0, occurrence); + } + else + { + return_value = in_str_wide; + } + + return return_value; + } + auto inline explode_by_occurrence(const std::string& in_str, const char delimiter, ExplodeType start_or_end) -> std::string { size_t occurrence = (start_or_end == ExplodeType::FromStart ? in_str.find_first_of(delimiter) : in_str.find_last_of(delimiter)); @@ -131,6 +152,33 @@ namespace RC return result; } + auto inline explode_by_occurrence(const std::u16string& in_str_wide, const char16_t delimiter) -> std::vector + { + std::vector result; + + size_t counter{}; + size_t start_offset{}; + + for (const char16_t* current_char = in_str_wide.c_str(); *current_char; ++current_char) + { + if (*current_char == delimiter || counter == in_str_wide.length() - 1) + { + std::u16string sub_str = in_str_wide.substr(start_offset, counter - start_offset + (counter == in_str_wide.length() - 1 ? 1 : 0)); + if (start_offset > 0) + { + sub_str.erase(0, 1); + } + result.emplace_back(sub_str); + + start_offset = counter; + } + + ++counter; + } + + return result; + } + auto inline explode_by_occurrence(const std::wstring& in_str, const wchar_t delimiter, const int32_t occurrence) -> std::wstring { size_t found_occurrence{}; @@ -148,12 +196,97 @@ namespace RC } /* explode_by_occurrence -> END */ - auto inline to_wstring(std::string& input) -> std::wstring + auto inline explode_by_occurrence(const std::u16string& in_str, const char16_t delimiter, const int32_t occurrence) -> std::u16string + { + size_t found_occurrence{}; + for (int64_t i = 0; i < std::count(in_str.begin(), in_str.end(), delimiter); i++) + { + found_occurrence = in_str.find(delimiter, found_occurrence + 1); + if (i + 1 == occurrence) + { + return in_str.substr(0, found_occurrence); + } + } + + // No occurrence was found, returning empty string for now + return {}; + } +/* explode_by_occurrence -> END */ + +// ----------------------------- // +#define STRING_DISPATCH_NOERR(STRING_T, ts, tw, tu16) \ + if constexpr (std::is_same_v) \ + { \ + return ts(std::forward(input)); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + return tw(std::forward(input)); \ + } \ + else if constexpr (std::is_same_v) \ + { \ + return tu16(std::forward(input)); \ + } + +#define STRING_DISPATCH_ERROR(STRING_T) \ + else \ + { \ + static_assert(dependent_false::value, "Unsupported " #STRING_T "."); \ + } + +#define STRING_DISPATCH(STRING_T, ts, tw, tu16) \ + STRING_DISPATCH_NOERR(STRING_T, ts, tw, tu16) \ + STRING_DISPATCH_ERROR(STRING_T) + +// ----------------------------- // +#define PATH_QUIRK(STRINGT) \ + if constexpr (std::is_same_v, std::filesystem::path> || std::is_same_v, const std::filesystem::path>) \ + { \ + STRING_DISPATCH(STRINGT, to_string_path, to_wstring_path, to_u16string_path); \ + } + // ----------------------------- // + +#define TO_STRING_QUIRK_DISPATCH(STRINGT) \ + PATH_QUIRK(STRINGT) \ + else \ + { \ + STRING_DISPATCH(STRINGT, to_string, to_wstring, to_u16string); \ + } + // ----------------------------- // + + auto inline to_string_path(const std::filesystem::path& input) -> std::string + { + return input.string(); + } + + auto inline to_wstring_path(const std::filesystem::path& input) -> std::wstring + { + return input.wstring(); + } + + auto inline to_u16string_path(const std::filesystem::path& input) -> std::u16string + { + return input.u16string(); + } + + auto inline to_wstring(std::string_view input) -> std::wstring { +#if WIN32 #pragma warning(disable : 4996) static std::wstring_convert> converter{}; - return converter.from_bytes(input); + return converter.from_bytes(input.data(), input.data() + input.length()); #pragma warning(default : 4996) +#else +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + static std::wstring_convert> converter{}; + return converter.from_bytes(input.data(), input.data() + input.length()); +#endif +#if __clang__ +#pragma clang diagnostic pop +#endif } auto inline to_const_wstring(std::string_view input) -> const std::wstring& @@ -178,80 +311,424 @@ namespace RC } } - auto inline to_wstring(std::string_view input) -> std::wstring + auto inline to_wstring(std::wstring_view input) -> std::wstring { - auto temp_input = std::string{input}; - return to_wstring(temp_input); + return std::wstring{input}; + } + + auto inline to_wstring(std::wstring& input) -> std::wstring + { + return std::wstring{input}; } auto inline to_wstring(std::u16string& input) -> std::wstring { +#ifdef WIN32 return {input.begin(), input.end()}; +#else + throw std::runtime_error{"There is no reason to use this function on non-Windows platforms"}; +#endif } auto inline to_wstring(std::u16string_view input) -> std::wstring { - auto temp_input = std::u16string{input}; - return to_wstring(temp_input); +#ifdef WIN32 + return {input.begin(), input.end()}; +#else + throw std::runtime_error{"There is no reason to use this function on non-Windows platforms"}; +#endif } - auto inline to_string(std::wstring& input) -> std::string + /* + auto inline to_string(std::u16string& input) -> std::string { #pragma warning(disable : 4996) - static std::wstring_convert> converter{}; + static std::wstring_convert, char16_t> converter{}; return converter.to_bytes(input); #pragma warning(default : 4996) } + */ auto inline to_string(std::wstring_view input) -> std::string { - auto temp_input = std::wstring{input}; - return to_string(temp_input); +#ifdef WIN32 +#pragma warning(disable : 4996) + static std::wstring_convert> converter{}; + return converter.to_bytes(input.data(), input.data() + input.length()); +#pragma warning(default : 4996) +#else +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + static std::wstring_convert> converter{}; + return converter.to_bytes(input.data(), input.data() + input.length()); +#if __clang__ +#pragma clang diagnostic pop +#endif +#endif + } + + auto inline to_string(std::u16string_view input) -> std::string + { +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif +#pragma warning(disable : 4996) + static std::wstring_convert, char16_t> converter{}; + return converter.to_bytes(input.data(), input.data() + input.length()); +#pragma warning(default : 4996) +#if __clang__ +#pragma clang diagnostic pop +#endif + } + + auto inline to_string(std::string_view input) -> std::string + { + return std::string{input}; + } + + auto inline to_string(std::string& input) -> std::string + { + return std::string{input}; } auto inline to_u16string(std::wstring& input) -> std::u16string { +#ifdef WIN32 return {input.begin(), input.end()}; +#else + throw std::runtime_error{"There is no reason to use this function on non-Windows platforms"}; +#endif } auto inline to_u16string(std::wstring_view input) -> std::u16string { - auto temp_input = std::wstring{input}; - return to_u16string(temp_input); + return to_u16string(std::wstring(input)); } + /* auto inline to_u16string(std::string& input) -> std::u16string { - return {input.begin(), input.end()}; + // codecvt_utf8_utf16 + #pragma warning(disable : 4996) + static std::wstring_convert, char16_t> converter {}; + return converter.from_bytes(input); + #pragma warning(default : 4996) } + */ auto inline to_u16string(std::string_view input) -> std::u16string { + /* auto temp_input = std::string{input}; - return to_u16string(temp_input); + return to_u16string(temp_input);*/ + // codecvt_utf8_utf16 +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif +#pragma warning(disable : 4996) + static std::wstring_convert, char16_t> converter{}; + return converter.from_bytes(input.data(), input.data() + input.length()); +#pragma warning(default : 4996) +#if __clang__ +#pragma clang diagnostic pop +#endif + } + + auto inline to_u16string(std::u16string_view input) -> std::u16string + { + return std::u16string{input}; + } + + auto inline to_u16string(std::u16string& input) -> std::u16string + { + return std::u16string{input}; + } + + // Type traits to check if T is a string type that needs conversion + template + struct is_string_like_t : std::false_type + { + }; + + // Specializations for the types that need conversion + template <> + struct is_string_like_t : std::true_type + { + }; + template <> + struct is_string_like_t : std::true_type + { + }; + template <> + struct is_string_like_t : std::true_type + { + }; + template <> + struct is_string_like_t : std::true_type + { + }; + template <> + struct is_string_like_t : std::true_type + { + }; + template <> + struct is_string_like_t : std::true_type + { + }; + + template + struct dependent_false : std::false_type + { + }; + + template + struct dependent_ensure : std::true_type + { + }; + template + struct dependent_ensure : std::false_type + { + }; + + template + auto stringviewify(T&& tp) + { + if constexpr (std::is_same_v, char*> || std::is_same_v, const char*>) + { + return std::string_view{tp}; + } + else if constexpr (std::is_same_v, wchar_t*> || std::is_same_v, const wchar_t*>) + { + return std::wstring_view{tp}; + } + else if constexpr (std::is_same_v, char16_t*> || std::is_same_v, const char16_t*>) + { + return std::u16string_view{tp}; + } + else + { + return std::forward(tp); + } + } + + template + struct _can_be_string_view_t : std::false_type + { + }; + template <> + struct _can_be_string_view_t : std::true_type + { + }; + template <> + struct _can_be_string_view_t : std::true_type + { + }; + template <> + struct _can_be_string_view_t : std::true_type + { + }; + template <> + struct _can_be_string_view_t : std::true_type + { + }; + template <> + struct _can_be_string_view_t : std::true_type + { + }; + template <> + struct _can_be_string_view_t : std::true_type + { + }; + + template + struct can_be_string_view_t : _can_be_string_view_t> + { + }; + + template + struct is_file_string_type : std::disjunction, std::is_same> + { + }; + + template + struct not_file_string_like_t : std::conjunction>, std::negation>>> + { + }; + + template + auto inline to_file_string(T&& input) -> File::StringType + { + TO_STRING_QUIRK_DISPATCH(File::StringType); } - auto inline to_generic_string(const auto& input) -> StringType + template + auto to_file(T&& arg) { - if constexpr (std::is_same_v>>, StringViewType>) + if constexpr (std::is_same_v, std::filesystem::path> || std::is_same_v, const std::filesystem::path>) + { + return to_file_string(std::forward(arg)); + } + else if constexpr (can_be_string_view_t::value) { - return StringType{input}; + return to_file(stringviewify(std::forward(arg))); } - else if constexpr (std::is_same_v>>, StringType> || - std::is_same_v>>, CharType>) + else if constexpr (not_file_string_like_t>::value) { - return input; + return to_file_string(std::forward(arg)); } else { -#if RC_IS_ANSI == 1 - return to_string(input); -#else - return to_wstring(input); -#endif + return std::forward(arg); + } + } + + template + struct is_std_string_type : std::disjunction, std::is_same> + { + }; + + template + struct not_std_string_like_t : std::conjunction>, std::negation>>> + { + }; + + template + auto inline to_std_string(T&& input) -> std::string + { + return to_string(std::forward(input)); + } + + template + auto to_stdstr(T&& arg) + { + if constexpr (std::is_same_v, std::filesystem::path> || std::is_same_v, const std::filesystem::path>) + { + return to_std_string(std::forward(arg)); + } + else if constexpr (can_be_string_view_t::value) + { + return to_stdstr(stringviewify(std::forward(arg))); + } + else if constexpr (not_std_string_like_t>::value) + { + return to_std_string(std::forward(arg)); + } + else + { + return std::forward(arg); } } + template + struct is_system_string_type : std::disjunction, std::is_same> + { + }; + + template + struct not_system_string_like_t : std::conjunction>, std::negation>>> + { + }; + + template + auto inline to_system_string(T&& input) -> SystemStringType + { + TO_STRING_QUIRK_DISPATCH(SystemStringType); + } + + template + auto to_system(T&& arg) + { + if constexpr (std::is_same_v, std::filesystem::path> || std::is_same_v, const std::filesystem::path>) + { + return to_system_string(std::forward(arg)); + } + else if constexpr (can_be_string_view_t::value) + { + return to_system(stringviewify(std::forward(arg))); + } + else if constexpr (not_system_string_like_t>::value) + { + return to_system_string(std::forward(arg)); + } + else + { + return std::forward(arg); + } + } + + template + struct is_ue_string_type : std::disjunction, std::is_same> + { + }; + + template + struct not_ue_string_like_t : std::conjunction>, std::negation>>> + { + }; + + template + auto inline to_ue_string(T&& input) -> UEStringType + { + TO_STRING_QUIRK_DISPATCH(UEStringType); + } + + template + auto to_ue(T&& arg) + { + if constexpr (std::is_same_v, std::filesystem::path> || std::is_same_v, const std::filesystem::path>) + { + return to_ue_string(std::forward(arg)); + } + else if constexpr (can_be_string_view_t::value) + { + return to_ue(stringviewify(std::forward(arg))); + } + else if constexpr (not_ue_string_like_t>::value) + { + return to_ue_string(std::forward(arg)); + } + else + { + return std::forward(arg); + } + } + + template + struct is_lua_string_type : std::disjunction, std::is_same> + { + }; + + template + struct not_lua_string_like_t : std::negation>> + { + }; + + template + auto to_lua(T&& arg) + { + if constexpr (can_be_string_view_t::value || not_lua_string_like_t>::value) + { + return to_string(std::forward(arg)); + } + else + { + return std::forward(arg); + } + } + + // TODO: add an option to allow compile failure if to_XXXX failed. + // e.g., to_ue -> must be able to convert to uestring, not passthrough. + +#define csfor_lua(x) (to_lua((x)).c_str()) + +#undef TO_STRING_QUIRK_DISPATCH +#undef PATH_QUIRK +#undef STRING_DISPATCH + namespace String { auto inline iequal(std::wstring_view a, std::wstring_view b) @@ -261,16 +738,33 @@ namespace RC }); } + auto inline iequal(std::u16string_view a, std::u16string_view b) + { + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), [](const char16_t a_char, const char16_t b_char) { + return std::towlower(a_char) == std::towlower(b_char); + }); + } + auto inline iequal(std::wstring& a, const wchar_t* b) { return iequal(a, std::wstring_view{b}); } + auto inline iequal(std::u16string& a, const char16_t* b) + { + return iequal(a, std::u16string_view{b}); + } + auto inline iequal(const wchar_t* a, std::wstring& b) { return iequal(std::wstring_view{a}, b); } + auto inline iequal(const char16_t* a, std::u16string& b) + { + return iequal(std::u16string_view{a}, b); + } + auto inline iequal(std::string_view a, std::string_view b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), [](const char a_char, const char b_char) { diff --git a/deps/first/IniParser/include/IniParser/Common.hpp b/deps/first/IniParser/include/IniParser/Common.hpp index 5077118ed..3196c27b4 100644 --- a/deps/first/IniParser/include/IniParser/Common.hpp +++ b/deps/first/IniParser/include/IniParser/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_INI_PARSER_EXPORTS #ifndef RC_INI_PARSER_BUILD_STATIC #ifndef RC_INI_PARSER_API @@ -15,3 +17,11 @@ #define RC_INI_PARSER_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_INI_PARSER_API +#define RC_INI_PARSER_API +#endif + +#endif diff --git a/deps/first/IniParser/include/IniParser/Experimental.hpp b/deps/first/IniParser/include/IniParser/Experimental.hpp index 38c8039eb..370640b7c 100644 --- a/deps/first/IniParser/include/IniParser/Experimental.hpp +++ b/deps/first/IniParser/include/IniParser/Experimental.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace RC::Parser::Experimental { enum TokenType : int @@ -24,47 +26,47 @@ namespace RC::Parser::Experimental class RuleOne : public Parser::TokenRule { public: - [[nodiscard]] auto to_string() const -> std::wstring override + [[nodiscard]] auto to_string() const -> SystemCharType override { - return L"RuleOne"; + return SYSSTR("RuleOne"); } }; class TokenMustEndWithOppositeToken : public Parser::TokenRule { public: - TokenMustEndWithOppositeToken() : TokenRule(L"TokenMustEndWithOppositeToken") + TokenMustEndWithOppositeToken() : TokenRule(SYSSTR("TokenMustEndWithOppositeToken")) { } - auto exec(const Parser::Token& token, const wchar_t* start_of_token, size_t current_cursor_location, Tokenizer& tokenizer) -> int override + auto exec(const Parser::Token& token, const SystemCharType* start_of_token, size_t current_cursor_location, Tokenizer& tokenizer) -> int override { - printf_s("TokenMustEndWithOppositeToken::exec [%S]\n", token.to_string().c_str()); + printf_s("TokenMustEndWithOppositeToken::exec [" SystemStringPrint "]\n", token.to_string().c_str()); return 0; } - [[nodiscard]] auto to_string() const -> std::wstring override + [[nodiscard]] auto to_string() const -> SystemStringType override { - return L"TokenMustEndWithOppositeToken"; + return SYSSTR("TokenMustEndWithOppositeToken"); } }; class TokenMustHaveCharsBeforeEnd : public Parser::TokenRule { public: - TokenMustHaveCharsBeforeEnd() : TokenRule(L"TokenMustHaveCharsBeforeEnd") + TokenMustHaveCharsBeforeEnd() : TokenRule(SYSSTR("TokenMustHaveCharsBeforeEnd")) { } - auto exec(const Parser::Token& token, const wchar_t* start_of_token, size_t current_cursor_location, Tokenizer& tokenizer) -> int override + auto exec(const Parser::Token& token, const SystemCharType* start_of_token, size_t current_cursor_location, Tokenizer& tokenizer) -> int override { - printf_s("TokenMustHaveCharsBeforeEnd::exec [%S]\n", token.to_string().c_str()); + printf_s("TokenMustHaveCharsBeforeEnd::exec [" SystemStringPrint "]\n", token.to_string().c_str()); return 0; } - [[nodiscard]] auto to_string() const -> std::wstring override + [[nodiscard]] auto to_string() const -> SystemStringType override { - return L"TokenMustHaveCharsBeforeEnd"; + return SYSSTR("TokenMustHaveCharsBeforeEnd"); } }; @@ -76,35 +78,35 @@ namespace RC::Parser::Experimental // Always use "ref->value" instead of "value" // That way it returns properly if this value is a refernece to another variable // The "ref->value" member is set to self if it doesn't refer to another variable - std::wstring value{}; + SystemStringType value{}; const Value* ref{}; }; struct Section { - std::unordered_map key_value_pairs{}; + std::unordered_map key_value_pairs{}; }; private: - using SectionContainer = std::unordered_map; + using SectionContainer = std::unordered_map; SectionContainer& m_output; Section* m_current_section{}; Value* m_variable_to_assign_to{}; - std::wstring m_temporary{}; + SystemStringType m_temporary{}; public: - ExperimentalTokenParser(const Parser::Tokenizer& tokenizer, std::wstring input, std::unordered_map& output) + ExperimentalTokenParser(const Parser::Tokenizer& tokenizer, SystemStringType input, std::unordered_map& output) : TokenParser(tokenizer, input), m_output(output) { } public: - auto static find_variable_by_name(Section* section, const std::wstring& name) -> std::optional>; - auto static find_variable_by_name(SectionContainer& sections, const std::wstring& name) -> std::optional>; + auto static find_variable_by_name(Section* section, const SystemStringType& name) -> std::optional>; + auto static find_variable_by_name(SectionContainer& sections, const SystemStringType& name) -> std::optional>; private: - auto find_variable_by_name(const std::wstring& name) -> std::optional>; + auto find_variable_by_name(const SystemStringType& name) -> std::optional>; auto try_set_section_value(Value& pair_value, const Parser::Token& token, bool is_space_valid = true) -> void; auto handle_operator_equals(const Parser::Token& token) -> void; auto handle_operator_plus(const Parser::Token& token) -> void; @@ -120,16 +122,16 @@ namespace RC::Parser::Experimental class ExperimentalParser { private: - std::unordered_map m_sections; + std::unordered_map m_sections; public: - ExperimentalParser(const std::wstring& input); + ExperimentalParser(const SystemStringType& input); private: auto create_available_tokens_for_tokenizer() -> Parser::TokenContainer; public: - auto get_string(const std::wstring& section, const std::wstring& key, const std::wstring& default_value) -> std::wstring; + auto get_string(const SystemStringType& section, const SystemStringType& key, const SystemStringType& default_value) -> SystemStringType; }; auto test() -> void; diff --git a/deps/first/IniParser/include/IniParser/Ini.hpp b/deps/first/IniParser/include/IniParser/Ini.hpp index fd9034aea..acfafbeae 100644 --- a/deps/first/IniParser/include/IniParser/Ini.hpp +++ b/deps/first/IniParser/include/IniParser/Ini.hpp @@ -23,32 +23,32 @@ namespace RC::Ini }; private: - std::unordered_map m_sections; + std::unordered_map m_sections; bool m_parsing_is_complete{false}; public: Parser() = default; private: - RC_INI_PARSER_API auto parse_internal(File::StringType& input) -> void; + RC_INI_PARSER_API auto parse_internal(SystemStringType& input) -> void; RC_INI_PARSER_API auto create_available_tokens_for_tokenizer() -> ParserBase::TokenContainer; - RC_INI_PARSER_API auto get_value(const File::StringType& section, const File::StringType& key, CanThrow = CanThrow::Yes) const + RC_INI_PARSER_API auto get_value(const SystemStringType& section, const SystemStringType& key, CanThrow = CanThrow::Yes) const -> std::optional>; public: - RC_INI_PARSER_API auto parse(File::StringType& input) -> void; + RC_INI_PARSER_API auto parse(SystemStringType& input) -> void; RC_INI_PARSER_API auto parse(const File::Handle&) -> void; - RC_INI_PARSER_API auto get_list(const File::StringType& section) -> List; - RC_INI_PARSER_API auto get_ordered_list(const File::StringType& section) -> List; - RC_INI_PARSER_API auto get_string(const File::StringType& section, const File::StringType& key, const File::StringType& default_value) const noexcept - -> const File::StringType&; - RC_INI_PARSER_API auto get_string(const File::StringType& section, const File::StringType& key) const -> const File::StringType&; + RC_INI_PARSER_API auto get_list(const SystemStringType& section) -> List; + RC_INI_PARSER_API auto get_ordered_list(const SystemStringType& section) -> List; + RC_INI_PARSER_API auto get_string(const SystemStringType& section, const SystemStringType& key, const SystemStringType& default_value) const noexcept + -> const SystemStringType&; + RC_INI_PARSER_API auto get_string(const SystemStringType& section, const SystemStringType& key) const -> const SystemStringType&; // Should there be more integer getters ? They'd confirm size and convert explicitly (or throw?), no implicit conversions. - RC_INI_PARSER_API auto get_int64(const File::StringType& section, const File::StringType& key, int64_t default_value) const noexcept -> int64_t; - RC_INI_PARSER_API auto get_int64(const File::StringType& section, const File::StringType& key) const -> int64_t; - RC_INI_PARSER_API auto get_float(const File::StringType& section, const File::StringType& key, float default_value) const noexcept -> float; - RC_INI_PARSER_API auto get_float(const File::StringType& section, const File::StringType& key) const -> float; - RC_INI_PARSER_API auto get_bool(const File::StringType& section, const File::StringType& key, bool default_value) const noexcept -> bool; - RC_INI_PARSER_API auto get_bool(const File::StringType& section, const File::StringType& key) const -> bool; + RC_INI_PARSER_API auto get_int64(const SystemStringType& section, const SystemStringType& key, int64_t default_value) const noexcept -> int64_t; + RC_INI_PARSER_API auto get_int64(const SystemStringType& section, const SystemStringType& key) const -> int64_t; + RC_INI_PARSER_API auto get_float(const SystemStringType& section, const SystemStringType& key, float default_value) const noexcept -> float; + RC_INI_PARSER_API auto get_float(const SystemStringType& section, const SystemStringType& key) const -> float; + RC_INI_PARSER_API auto get_bool(const SystemStringType& section, const SystemStringType& key, bool default_value) const noexcept -> bool; + RC_INI_PARSER_API auto get_bool(const SystemStringType& section, const SystemStringType& key) const -> bool; }; } // namespace RC::Ini diff --git a/deps/first/IniParser/include/IniParser/JSON.hpp b/deps/first/IniParser/include/IniParser/JSON.hpp index fc6d763bc..1ef447b73 100644 --- a/deps/first/IniParser/include/IniParser/JSON.hpp +++ b/deps/first/IniParser/include/IniParser/JSON.hpp @@ -6,6 +6,8 @@ #include #include +#include + namespace RC::Parser { namespace JSONInternal @@ -24,31 +26,31 @@ namespace RC::Parser EndOfFile, }; - auto static token_type_to_string(const TokenType token_type) -> File::StringType + auto static token_type_to_string(const TokenType token_type) -> std::string { switch (token_type) { case Default: - return STR("Default"); + return std::string("Default"); case OpenCurlyBrace: - return STR("OpenCurlyBrace"); + return std::string("OpenCurlyBrace"); case CloseCurlyBrace: - return STR("CloseCurlyBrace"); + return std::string("CloseCurlyBrace"); case OpenSquareBracket: - return STR("OpenSquareBracket"); + return std::string("OpenSquareBracket"); case CloseSquareBracket: - return STR("CloseSquareBracket"); + return std::string("CloseSquareBracket"); case Colon: - return STR("Colon"); + return std::string("Colon"); case Comma: - return STR("Comma"); + return std::string("Comma"); break; case DoubleQuote: - return STR("DoubleQuote"); + return std::string("DoubleQuote"); case Characters: - return STR("Characters"); + return std::string("Characters"); case EndOfFile: - return STR("EndOfFile"); + return std::string("EndOfFile"); } } @@ -63,29 +65,29 @@ namespace RC::Parser class ItemBase { public: - File::StringType m_name{STR("--UNNAMED-ITEM--")}; + UEStringType m_name{STR("--UNNAMED-ITEM--")}; bool m_is_global_scope{false}; public: - auto get_name() -> File::StringViewType; + auto get_name() -> SystemStringType; virtual ~ItemBase() = default; - virtual auto to_string() -> File::StringType = 0; + virtual auto to_string() -> SystemStringType = 0; virtual auto get_type() -> ItemType = 0; }; class StringItem : public ItemBase { public: - File::StringType m_value{}; + SystemStringType m_value{}; public: StringItem() = default; - StringItem(const File::StringType& value) : m_value(value) + StringItem(const SystemStringType& value) : m_value(value) { } - auto to_string() -> File::StringType override; + auto to_string() -> SystemStringType override; auto get_type() -> ItemType override { return ItemType::String; @@ -100,7 +102,7 @@ namespace RC::Parser size_t m_previous_column_without_comma{}; public: - auto to_string() -> File::StringType override; + auto to_string() -> SystemStringType override; auto get_type() -> ItemType override { return ItemType::Object; @@ -113,7 +115,7 @@ namespace RC::Parser std::vector> m_members{}; public: - auto to_string() -> File::StringType override; + auto to_string() -> SystemStringType override; auto get_type() -> ItemType override { return ItemType::Array; @@ -160,18 +162,18 @@ namespace RC::Parser ItemType m_next_item_expected{}; TokenType m_next_token_expected{TokenType::Default}; TokenTypeStack m_processed_token_types{}; - File::StringType m_string_value_buffer{}; + SystemStringType m_string_value_buffer{}; bool m_double_quote_opened{false}; bool m_double_quote_successfully_closed{false}; public: - TokenParser(const Parser::Tokenizer& tokenizer, File::StringType& input) : Parser::TokenParser(tokenizer, input) + TokenParser(const Parser::Tokenizer& tokenizer, SystemStringType& input) : Parser::TokenParser(tokenizer, input) { } private: - auto token_to_string(const Token& token) -> File::StringType; + auto token_to_string(const Token& token) -> SystemStringType; auto skip_all_spaces() -> void; auto parse_open_curly_brace(const Token& token) -> void; @@ -198,10 +200,10 @@ namespace RC::Parser JSON() = default; private: - auto parse_internal(File::StringType& input) -> void; + auto parse_internal(SystemStringType& input) -> void; public: - auto parse(File::StringType& input) -> void; + auto parse(SystemStringType& input) -> void; auto parse(File::Handle&) -> void; auto release_contents() -> std::vector>; diff --git a/deps/first/IniParser/include/IniParser/Section.hpp b/deps/first/IniParser/include/IniParser/Section.hpp index b6d9797ff..18e7b170c 100644 --- a/deps/first/IniParser/include/IniParser/Section.hpp +++ b/deps/first/IniParser/include/IniParser/Section.hpp @@ -9,13 +9,13 @@ namespace RC::Ini { struct Section { - std::unordered_map key_value_pairs{}; - std::vector ordered_list{}; + std::unordered_map key_value_pairs{}; + std::vector ordered_list{}; bool is_ordered_list{}; }; template - concept CallableWithKeyValuePair = std::invocable; + concept CallableWithKeyValuePair = std::invocable; class List { diff --git a/deps/first/IniParser/include/IniParser/TokenParser.hpp b/deps/first/IniParser/include/IniParser/TokenParser.hpp index a7192852a..930377540 100644 --- a/deps/first/IniParser/include/IniParser/TokenParser.hpp +++ b/deps/first/IniParser/include/IniParser/TokenParser.hpp @@ -22,30 +22,30 @@ namespace RC::Ini SetSectionValue, }; - auto state_to_string(State) -> File::StringType; + auto state_to_string(State) -> SystemStringType; class TokenParser : public ParserBase::TokenParser { private: - using SectionContainer = std::unordered_map; + using SectionContainer = std::unordered_map; SectionContainer& m_output; Section* m_current_section{}; Value* m_current_value{}; - File::StringType m_current_character_data{}; + SystemStringType m_current_character_data{}; State m_current_state{State::StartOfFile}; public: - TokenParser(const ParserBase::Tokenizer& tokenizer, File::StringType& input, std::unordered_map& output) + TokenParser(const ParserBase::Tokenizer& tokenizer, SystemStringType& input, std::unordered_map& output) : ParserBase::TokenParser(tokenizer, input), m_output(output) { } public: - RC_INI_PARSER_API auto static find_variable_by_name(Section* section, const File::StringType& name) -> std::optional>; + RC_INI_PARSER_API auto static find_variable_by_name(Section* section, const SystemStringType& name) -> std::optional>; private: - RC_INI_PARSER_API auto find_variable_by_name(const File::StringType& name) -> std::optional>; - RC_INI_PARSER_API auto characters_to_string(const ParserBase::Token& characters_token) -> File::StringType; + RC_INI_PARSER_API auto find_variable_by_name(const SystemStringType& name) -> std::optional>; + RC_INI_PARSER_API auto characters_to_string(const ParserBase::Token& characters_token) -> SystemStringType; RC_INI_PARSER_API auto handle_opening_square_bracket_token(const ParserBase::Token& token) -> void; RC_INI_PARSER_API auto handle_closing_square_bracket_token(const ParserBase::Token& token) -> void; RC_INI_PARSER_API auto handle_space_token(const ParserBase::Token& token) -> void; diff --git a/deps/first/IniParser/include/IniParser/Value.hpp b/deps/first/IniParser/include/IniParser/Value.hpp index 9c302619c..dcd36b54b 100644 --- a/deps/first/IniParser/include/IniParser/Value.hpp +++ b/deps/first/IniParser/include/IniParser/Value.hpp @@ -26,7 +26,7 @@ namespace RC::Ini // Always use "m_ref->value" instead of "value" // That way it returns properly if this value is a reference to another variable // The "m_ref->value" member is set to self if it doesn't refer to another variable - File::StringType m_string_value; + SystemStringType m_string_value; int64_t m_int64_value{}; float m_float_value{}; bool m_bool_value{}; @@ -57,14 +57,14 @@ namespace RC::Ini return is_valid_type(); } - RC_INI_PARSER_API auto get_string_value() const -> const File::StringType&; + RC_INI_PARSER_API auto get_string_value() const -> const SystemStringType&; RC_INI_PARSER_API auto get_int64_value() const -> int64_t; RC_INI_PARSER_API auto get_float_value() const -> float; RC_INI_PARSER_API auto get_bool_value() const -> bool; - RC_INI_PARSER_API auto add_string_value(File::StringViewType data) -> void; - RC_INI_PARSER_API auto add_int64_value(const File::StringType& data, int base = 10) -> void; - RC_INI_PARSER_API auto add_float_value(const File::StringType& data) -> void; + RC_INI_PARSER_API auto add_string_value(SystemStringViewType data) -> void; + RC_INI_PARSER_API auto add_int64_value(const SystemStringType& data, int base = 10) -> void; + RC_INI_PARSER_API auto add_float_value(const SystemStringType& data) -> void; RC_INI_PARSER_API auto add_bool_value(bool data) -> void; private: diff --git a/deps/first/IniParser/src/Experimental.cpp b/deps/first/IniParser/src/Experimental.cpp index 9e143cb02..dd9855920 100644 --- a/deps/first/IniParser/src/Experimental.cpp +++ b/deps/first/IniParser/src/Experimental.cpp @@ -2,7 +2,7 @@ namespace RC::Parser::Experimental { - auto ExperimentalTokenParser::find_variable_by_name(Section* section, const std::wstring& name) -> std::optional> + auto ExperimentalTokenParser::find_variable_by_name(Section* section, const SystemStringType& name) -> std::optional> { auto const& var = section->key_value_pairs.find(name); if (var != section->key_value_pairs.end()) @@ -15,7 +15,7 @@ namespace RC::Parser::Experimental } } - auto ExperimentalTokenParser::find_variable_by_name(SectionContainer& sections, const std::wstring& name) -> std::optional> + auto ExperimentalTokenParser::find_variable_by_name(SectionContainer& sections, const SystemStringType& name) -> std::optional> { std::optional> value_found = [&]() -> std::optional> { for (auto& [_, section] : sections) @@ -33,7 +33,7 @@ namespace RC::Parser::Experimental return value_found; } - auto ExperimentalTokenParser::find_variable_by_name(const std::wstring& name) -> std::optional> + auto ExperimentalTokenParser::find_variable_by_name(const SystemStringType& name) -> std::optional> { size_t occurrence_of_dot = name.find_first_of(L'.'); if (occurrence_of_dot == name.npos || occurrence_of_dot + 1 > name.size()) @@ -49,7 +49,7 @@ namespace RC::Parser::Experimental } else { - const std::wstring requested_variable_name = name.substr(occurrence_of_dot + 1, name.size()); + const SystemStringType requested_variable_name = name.substr(occurrence_of_dot + 1, name.size()); return find_variable_by_name(&requested_section->second, requested_variable_name); } } @@ -60,12 +60,12 @@ namespace RC::Parser::Experimental const auto token_type = token.get_type(); if (token_type == TokenType::EndOfFile || token_type == TokenType::NewLine) { - pair_value.value = L""; + pair_value.value = SYSSTR(""); pair_value.ref = &pair_value; } else if (token_type == TokenType::Characters) { - std::wstring value_data{}; + SystemStringType value_data{}; consume_continually([&](const Parser::Token& token) { const auto token_type = token.get_type(); if (token_type == TokenType::EndOfFile || token_type == TokenType::NewLine) @@ -85,7 +85,7 @@ namespace RC::Parser::Experimental else if (token_type == TokenType::Space) { // Append space - value_data.append(L" "); + value_data.append(SYSSTR(" ")); // Consume another token return false; @@ -161,7 +161,7 @@ namespace RC::Parser::Experimental } else if (previous_token.get_type() == TokenType::Characters) { - std::wstring key_name = get_data(previous_token); + SystemStringType key_name = get_data(previous_token); const auto& key_value_pair_iter = m_current_section->key_value_pairs.find(key_name); if (key_value_pair_iter == m_current_section->key_value_pairs.end()) { @@ -188,7 +188,7 @@ namespace RC::Parser::Experimental } else if (characters_token.get_type() == TokenType::Characters) { - const std::wstring key_name = get_data(characters_token); + const SystemStringType key_name = get_data(characters_token); const auto& maybe_variable = find_variable_by_name(key_name); if (maybe_variable.has_value()) { @@ -250,7 +250,7 @@ namespace RC::Parser::Experimental // May be a variable or a temporary (Characters token) bool lhs_is_variable{}; bool lhs_is_temporary{}; - std::wstring lhs = [&]() { + SystemStringType lhs = [&]() { if (!m_temporary.empty()) { lhs_is_temporary = true; @@ -265,7 +265,7 @@ namespace RC::Parser::Experimental } else { - const std::wstring lhs_data = get_data(lhs_token); + const SystemStringType lhs_data = get_data(lhs_token); auto maybe_variable = find_variable_by_name(lhs_data); if (maybe_variable.has_value()) { @@ -306,8 +306,8 @@ namespace RC::Parser::Experimental // } // Find rhs - std::wstring rhs = [&]() { - std::wstring rhs_temporary{}; + SystemStringType rhs = [&]() { + SystemStringType rhs_temporary{}; const auto& next_token = peek(); if (next_token.get_type() == TokenType::EndOfFile) @@ -332,7 +332,7 @@ namespace RC::Parser::Experimental } else if (next_token.get_type() == TokenType::Space) { - rhs_temporary += L" "; + rhs_temporary += SYSSTR(" "); consume(); // Consume the first Space consume_until(TokenType::Characters, [&](const Parser::Token& token) { @@ -346,7 +346,7 @@ namespace RC::Parser::Experimental } else { - rhs_temporary += L" "; + rhs_temporary += SYSSTR(" "); return false; } } @@ -476,7 +476,7 @@ namespace RC::Parser::Experimental } }(); - std::wstring section_name = get_data(section_name_token); + SystemStringType section_name = get_data(section_name_token); if (auto section = m_output.find(section_name); section != m_output.end()) { m_current_section = §ion->second; @@ -515,7 +515,7 @@ namespace RC::Parser::Experimental } } - ExperimentalParser::ExperimentalParser(const std::wstring& input) + ExperimentalParser::ExperimentalParser(const SystemStringType& input) { // Tokenize -> START Parser::Tokenizer tokenizer; @@ -533,28 +533,29 @@ namespace RC::Parser::Experimental { Parser::TokenContainer tc; - tc.add(Parser::Token::create(TokenType::CarriageReturn, L"CarriageReturn", L"\r")); - tc.add(Parser::Token::create(TokenType::NewLine, L"NewLine", L"\n")); - tc.add(Parser::Token::create(TokenType::Space, L"Space", L" ")); + tc.add(Parser::Token::create(TokenType::CarriageReturn, SYSSTR("CarriageReturn"), SYSSTR("\r"))); + tc.add(Parser::Token::create(TokenType::NewLine, SYSSTR("NewLine"), SYSSTR("\n"))); + tc.add(Parser::Token::create(TokenType::Space, SYSSTR("Space"), SYSSTR(" "))); tc.add(Parser::Token::create(TokenType::Characters, - L"Characters", - L"", + SYSSTR("Characters"), + SYSSTR(""), Parser::Token::HasData::Yes)); // Empty identifier will match everything that no other token identifier matches - tc.add(Parser::Token::create(TokenType::Equals, L"Equals", L"=")); - // tc.add(Parser::Token::create(IniFileToken::Plus, L"Plus", L"+")); + tc.add(Parser::Token::create(TokenType::Equals, SYSSTR("Equals"), SYSSTR("="))); + // tc.add(Parser::Token::create(IniFileToken::Plus, SYSSTR("Plus"), SYSSTR("+"))); - auto close_square_bracket_token = tc.add(Parser::Token::create(TokenType::ClosingSquareBracket, L"CloseSquareBracket", L"]")); + auto close_square_bracket_token = tc.add(Parser::Token::create(TokenType::ClosingSquareBracket, SYSSTR("CloseSquareBracket"), SYSSTR("]"))); - auto token = - Parser::Token::create(TokenType::OpeningSquareBracket, L"OpenSquareBracket", L"["); + auto token = Parser::Token::create(TokenType::OpeningSquareBracket, + SYSSTR("OpenSquareBracket"), + SYSSTR("[")); tc.add(std::move(token)); - tc.add(Parser::Token::create(TokenType::SemiColon, L"SemiColon", L";")); + tc.add(Parser::Token::create(TokenType::SemiColon, SYSSTR("SemiColon"), SYSSTR(";"))); return tc; } - auto ExperimentalParser::get_string(const std::wstring& section, const std::wstring& key, const std::wstring& default_value) -> std::wstring + auto ExperimentalParser::get_string(const SystemStringType& section, const SystemStringType& key, const SystemStringType& default_value) -> SystemStringType { auto section_iter = m_sections.find(section); if (section_iter == m_sections.end()) @@ -590,7 +591,7 @@ var6 = hello + + world var7 = 1 + 2 + 3 + 4 */ - std::wstring config_str = LR"( + SystemStringType config_str = R"( [ SectionOne] ; This is a comment and as such should be ignored by the parser var1 = 2 @@ -624,30 +625,30 @@ var5 = a string with spaces printf_s("Creating experimental parser\n"); ExperimentalParser parser{config_str}; - constexpr wchar_t default_value[] = L""; - printf_s("SectionOne.var1 = %S\n", parser.get_string(L"SectionOne", L"var1", default_value).c_str()); - printf_s("SectionOne.var2 = %S\n", parser.get_string(L"SectionOne", L"var2", default_value).c_str()); - printf_s("SectionOne.var3 = %S\n", parser.get_string(L"SectionOne", L"var3", default_value).c_str()); - printf_s("SectionOne.var4 = %S\n", parser.get_string(L"SectionOne", L"var4", default_value).c_str()); - printf_s("SectionOne.var5 = %S\n", parser.get_string(L"SectionOne", L"var5", default_value).c_str()); - printf_s("SectionOne.var6 = %S\n", parser.get_string(L"SectionOne", L"var6", default_value).c_str()); - printf_s("SectionOne.var7 = %S\n", parser.get_string(L"SectionOne", L"var7", default_value).c_str()); + constexpr char16_t default_value[] = SYSSTR(""); + printf_s("SectionOne.var1 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var1"), default_value).c_str()); + printf_s("SectionOne.var2 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var2"), default_value).c_str()); + printf_s("SectionOne.var3 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var3"), default_value).c_str()); + printf_s("SectionOne.var4 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var4"), default_value).c_str()); + printf_s("SectionOne.var5 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var5"), default_value).c_str()); + printf_s("SectionOne.var6 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var6"), default_value).c_str()); + printf_s("SectionOne.var7 = %S\n", parser.get_string(SYSSTR("SectionOne"), SYSSTR("var7"), default_value).c_str()); printf_s("\n"); - printf_s("SectionTwo.var1 = %S\n", parser.get_string(L"SectionTwo", L"var1", default_value).c_str()); - printf_s("SectionTwo.var2 = %S\n", parser.get_string(L"SectionTwo", L"var2", default_value).c_str()); - printf_s("SectionTwo.var3 = %S\n", parser.get_string(L"SectionTwo", L"var3", default_value).c_str()); - printf_s("SectionTwo.var4 = %S\n", parser.get_string(L"SectionTwo", L"var4", default_value).c_str()); - printf_s("SectionTwo.var5 = %S\n", parser.get_string(L"SectionTwo", L"var5", default_value).c_str()); - printf_s("SectionTwo.var6 = %S\n", parser.get_string(L"SectionTwo", L"var6", default_value).c_str()); - printf_s("SectionTwo.var7 = %S\n", parser.get_string(L"SectionTwo", L"var7", default_value).c_str()); - printf_s("SectionTwo.var8 = %S\n", parser.get_string(L"SectionTwo", L"var8", default_value).c_str()); - printf_s("SectionTwo.var9 = %S\n", parser.get_string(L"SectionTwo", L"var9", default_value).c_str()); - printf_s("SectionTwo.var10 = %S\n", parser.get_string(L"SectionTwo", L"var10", default_value).c_str()); + printf_s("SectionTwo.var1 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var1"), default_value).c_str()); + printf_s("SectionTwo.var2 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var2"), default_value).c_str()); + printf_s("SectionTwo.var3 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var3"), default_value).c_str()); + printf_s("SectionTwo.var4 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var4"), default_value).c_str()); + printf_s("SectionTwo.var5 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var5"), default_value).c_str()); + printf_s("SectionTwo.var6 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var6"), default_value).c_str()); + printf_s("SectionTwo.var7 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var7"), default_value).c_str()); + printf_s("SectionTwo.var8 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var8"), default_value).c_str()); + printf_s("SectionTwo.var9 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var9"), default_value).c_str()); + printf_s("SectionTwo.var10 = %S\n", parser.get_string(SYSSTR("SectionTwo"), SYSSTR("var10"), default_value).c_str()); printf_s("\n"); - printf_s("AnotherSection.var1 = %S\n", parser.get_string(L"AnotherSection", L"var1", default_value).c_str()); - printf_s("AnotherSection.var2 = %S\n", parser.get_string(L"AnotherSection", L"var2", default_value).c_str()); - printf_s("AnotherSection.var3 = %S\n", parser.get_string(L"AnotherSection", L"var3", default_value).c_str()); - printf_s("AnotherSection.var4 = %S\n", parser.get_string(L"AnotherSection", L"var4", default_value).c_str()); - printf_s("AnotherSection.var5 = %S\n", parser.get_string(L"AnotherSection", L"var5", default_value).c_str()); + printf_s("AnotherSection.var1 = %S\n", parser.get_string(SYSSTR("AnotherSection"), SYSSTR("var1"), default_value).c_str()); + printf_s("AnotherSection.var2 = %S\n", parser.get_string(SYSSTR("AnotherSection"), SYSSTR("var2"), default_value).c_str()); + printf_s("AnotherSection.var3 = %S\n", parser.get_string(SYSSTR("AnotherSection"), SYSSTR("var3"), default_value).c_str()); + printf_s("AnotherSection.var4 = %S\n", parser.get_string(SYSSTR("AnotherSection"), SYSSTR("var4"), default_value).c_str()); + printf_s("AnotherSection.var5 = %S\n", parser.get_string(SYSSTR("AnotherSection"), SYSSTR("var5"), default_value).c_str()); } } // namespace RC::Parser::Experimental diff --git a/deps/first/IniParser/src/Ini.cpp b/deps/first/IniParser/src/Ini.cpp index 20670ccab..8d2bcf5ca 100644 --- a/deps/first/IniParser/src/Ini.cpp +++ b/deps/first/IniParser/src/Ini.cpp @@ -1,10 +1,11 @@ #include #include #include +#include namespace RC::Ini { - auto Parser::parse_internal(File::StringType& input) -> void + auto Parser::parse_internal(SystemStringType& input) -> void { // Tokenize -> START ParserBase::Tokenizer tokenizer; @@ -24,24 +25,24 @@ namespace RC::Ini { ParserBase::TokenContainer tc; - tc.add(ParserBase::Token::create(IniTokenType::CarriageReturn, STR("CarriageReturn"), STR("\r"))); - tc.add(ParserBase::Token::create(IniTokenType::NewLine, STR("NewLine"), STR("\n"))); - tc.add(ParserBase::Token::create(IniTokenType::Space, STR("Space"), STR(" "))); + tc.add(ParserBase::Token::create(IniTokenType::CarriageReturn, SYSSTR("CarriageReturn"), SYSSTR("\r"))); + tc.add(ParserBase::Token::create(IniTokenType::NewLine, SYSSTR("NewLine"), SYSSTR("\n"))); + tc.add(ParserBase::Token::create(IniTokenType::Space, SYSSTR("Space"), SYSSTR(" "))); tc.add(ParserBase::Token::create(IniTokenType::Characters, - STR("Characters"), - STR(""), + SYSSTR("Characters"), + SYSSTR(""), ParserBase::Token::HasData::Yes)); // Empty identifier will match everything that no other token identifier matches - tc.add(ParserBase::Token::create /**/ (IniTokenType::Equals, STR("Equals"), STR("="))); - tc.add(ParserBase::Token::create(IniTokenType::ClosingSquareBracket, STR("CloseSquareBracket"), STR("]"))); - tc.add(ParserBase::Token::create(IniTokenType::OpeningSquareBracket, STR("OpenSquareBracket"), STR("["))); - tc.add(ParserBase::Token::create(IniTokenType::SemiColon, STR("SemiColon"), STR(";"))); + tc.add(ParserBase::Token::create /**/ (IniTokenType::Equals, SYSSTR("Equals"), SYSSTR("="))); + tc.add(ParserBase::Token::create(IniTokenType::ClosingSquareBracket, SYSSTR("CloseSquareBracket"), SYSSTR("]"))); + tc.add(ParserBase::Token::create(IniTokenType::OpeningSquareBracket, SYSSTR("OpenSquareBracket"), SYSSTR("["))); + tc.add(ParserBase::Token::create(IniTokenType::SemiColon, SYSSTR("SemiColon"), SYSSTR(";"))); tc.set_eof_token(IniTokenType::EndOfFile); return tc; } - auto Parser::get_value(const File::StringType& section, const File::StringType& key, CanThrow can_throw) const + auto Parser::get_value(const SystemStringType& section, const SystemStringType& key, CanThrow can_throw) const -> std::optional> { if (!m_parsing_is_complete) @@ -76,18 +77,18 @@ namespace RC::Ini } } - auto Parser::parse(File::StringType& input) -> void + auto Parser::parse(SystemStringType& input) -> void { parse_internal(input); } auto Parser::parse(const File::Handle& file) -> void { - auto input = file.read_all(); + auto input = to_system_string(file.read_file_all()); parse_internal(input); } - auto Parser::get_list(const File::StringType& section) -> List + auto Parser::get_list(const SystemStringType& section) -> List { const auto& section_iter = m_sections.find(section); @@ -101,13 +102,13 @@ namespace RC::Ini } } - auto Parser::get_ordered_list(const File::StringType& section) -> List + auto Parser::get_ordered_list(const SystemStringType& section) -> List { return get_list(section); } - auto Parser::get_string(const File::StringType& section, const File::StringType& key, const File::StringType& default_value) const noexcept - -> const File::StringType& + auto Parser::get_string(const SystemStringType& section, const SystemStringType& key, const SystemStringType& default_value) const noexcept + -> const SystemStringType& { const auto maybe_value = get_value(section, key, CanThrow::No); if (!maybe_value.has_value()) @@ -128,7 +129,7 @@ namespace RC::Ini } } - auto Parser::get_string(const File::StringType& section, const File::StringType& key) const -> const File::StringType& + auto Parser::get_string(const SystemStringType& section, const SystemStringType& key) const -> const SystemStringType& { const auto maybe_value = get_value(section, key); if (!maybe_value.has_value()) @@ -149,7 +150,7 @@ namespace RC::Ini } } - auto Parser::get_int64(const File::StringType& section, const File::StringType& key, int64_t default_value) const noexcept -> int64_t + auto Parser::get_int64(const SystemStringType& section, const SystemStringType& key, int64_t default_value) const noexcept -> int64_t { const auto maybe_value = get_value(section, key, CanThrow::No); if (!maybe_value.has_value()) @@ -170,7 +171,7 @@ namespace RC::Ini } } - auto Parser::get_int64(const File::StringType& section, const File::StringType& key) const -> int64_t + auto Parser::get_int64(const SystemStringType& section, const SystemStringType& key) const -> int64_t { const auto maybe_value = get_value(section, key); if (!maybe_value.has_value()) @@ -191,7 +192,7 @@ namespace RC::Ini } } - auto Parser::get_float(const File::StringType& section, const File::StringType& key, float default_value) const noexcept -> float + auto Parser::get_float(const SystemStringType& section, const SystemStringType& key, float default_value) const noexcept -> float { const auto maybe_value = get_value(section, key, CanThrow::No); if (!maybe_value.has_value()) @@ -212,7 +213,7 @@ namespace RC::Ini } } - auto Parser::get_float(const File::StringType& section, const File::StringType& key) const -> float + auto Parser::get_float(const SystemStringType& section, const SystemStringType& key) const -> float { const auto maybe_value = get_value(section, key); if (!maybe_value.has_value()) @@ -233,7 +234,7 @@ namespace RC::Ini } } - auto Parser::get_bool(const File::StringType& section, const File::StringType& key, bool default_value) const noexcept -> bool + auto Parser::get_bool(const SystemStringType& section, const SystemStringType& key, bool default_value) const noexcept -> bool { const auto maybe_value = get_value(section, key, CanThrow::No); if (!maybe_value.has_value()) @@ -254,7 +255,7 @@ namespace RC::Ini } } - auto Parser::get_bool(const File::StringType& section, const File::StringType& key) const -> bool + auto Parser::get_bool(const SystemStringType& section, const SystemStringType& key) const -> bool { const auto maybe_value = get_value(section, key); if (!maybe_value.has_value()) diff --git a/deps/first/IniParser/src/JSON.cpp b/deps/first/IniParser/src/JSON.cpp index a0a71f85d..033e494a7 100644 --- a/deps/first/IniParser/src/JSON.cpp +++ b/deps/first/IniParser/src/JSON.cpp @@ -11,7 +11,7 @@ namespace RC::Parser return std::string{in.begin(), in.end()}; } - static auto has_only_spaces(const File::StringType& data) -> bool + static auto has_only_spaces(const UEStringType& data) -> bool { if (std::all_of(data.begin(), data.end(), [](File::CharType c) { return std::isspace(c) || c == '\n'; @@ -27,7 +27,7 @@ namespace RC::Parser } } - auto JSONInternal::ItemBase::get_name() -> File::StringViewType + auto JSONInternal::ItemBase::get_name() -> UEStringViewType { if (m_is_global_scope) { @@ -39,14 +39,14 @@ namespace RC::Parser } } - auto JSONInternal::StringItem::to_string() -> File::StringType + auto JSONInternal::StringItem::to_string() -> UEStringType { return std::format(STR("String = \"{}\""), m_value); } - auto JSONInternal::ObjectScope::to_string() -> File::StringType + auto JSONInternal::ObjectScope::to_string() -> UEStringType { - File::StringType str = std::format(STR("Object = \"{}\""), get_name()); + UEStringType str = std::format(STR("Object = \"{}\""), get_name()); for (const auto& member : m_members) { @@ -56,14 +56,14 @@ namespace RC::Parser return str; } - auto JSONInternal::ArrayScope::to_string() -> File::StringType + auto JSONInternal::ArrayScope::to_string() -> UEStringType { return std::format(STR("Array = \"{}\""), get_name()); } - auto JSONInternal::TokenParser::token_to_string(const Token& token) -> File::StringType + auto JSONInternal::TokenParser::token_to_string(const Token& token) -> UEStringType { - File::StringType string{}; + UEStringType string{}; // TODO: Support manual escaping of double quotes with '\' while (peek().get_type() != TokenType::DoubleQuote) @@ -342,7 +342,7 @@ namespace RC::Parser } } - auto JSON::parse_internal(File::StringType& input) -> void + auto JSON::parse_internal(UEStringType& input) -> void { Tokenizer tokenizer; TokenContainer tc; @@ -362,14 +362,14 @@ namespace RC::Parser m_token_parser->parse(); } - auto JSON::parse(File::StringType& input) -> void + auto JSON::parse(SystemStringType& input) -> void { parse_internal(input); } auto JSON::parse(File::Handle& file) -> void { - auto input = file.read_all(); + auto input = file.read_file_all(); parse_internal(input); } @@ -391,7 +391,7 @@ namespace RC::Parser */ /**/ - File::StringType input = LR"( + UEStringType input = LR"( { "my key": "my string, value", "my second key": "my other string value", diff --git a/deps/first/IniParser/src/TokenParser.cpp b/deps/first/IniParser/src/TokenParser.cpp index dd633366e..a107a1589 100644 --- a/deps/first/IniParser/src/TokenParser.cpp +++ b/deps/first/IniParser/src/TokenParser.cpp @@ -28,25 +28,25 @@ namespace RC::Ini bool is_bool{false}; }; - auto static is_int(File::StringViewType data) -> Int + auto static is_int(SystemStringType data) -> Int { bool has_0x_prefix = [&]() { - return (data.size() > 2 && data[0] == L'0' && (data[1] == L'x' || data[1] == L'X')); + return (data.size() > 2 && data[0] == SYSSTR('0') && (data[1] == SYSSTR('x') || data[1] == SYSSTR('X'))); }(); - if (!has_0x_prefix && data[0] != L'-' && std::iswdigit(data[0]) == 0) + if (!has_0x_prefix && data[0] != SYSSTR('-') && std::iswdigit(data[0]) == 0) { return Int{0, 10, false}; } else { - File::StringViewType string = has_0x_prefix ? File::StringViewType{data.begin() + 2, data.end()} : data; - if (!has_0x_prefix && data[0] == STR('-')) + SystemStringType string = has_0x_prefix ? SystemStringType{data.begin() + 2, data.end()} : data; + if (!has_0x_prefix && data[0] == SYSSTR('-')) { - string = File::StringViewType{string.begin() + 1, string.end()}; + string = SystemStringType{string.begin() + 1, string.end()}; } - bool is_int = std::ranges::all_of(string.begin(), string.end(), [&](const File::CharType c) { - if constexpr (std::is_same_v) + bool is_int = std::ranges::all_of(string.begin(), string.end(), [&](const UECharType c) { + if constexpr (std::is_same_v) { return has_0x_prefix ? std::iswxdigit(c) : std::iswdigit(c) != 0; } @@ -60,10 +60,10 @@ namespace RC::Ini } } - auto static is_float(File::StringViewType data) -> Float + auto static is_float(SystemStringType data) -> Float { bool has_decimal_or_negative_prefix = [&]() { - return data.size() > 1 && data[0] == L'.' || data[0] == L'-'; + return data.size() > 1 && data[0] == SYSSTR('.') || data[0] == SYSSTR('-'); }(); if (!has_decimal_or_negative_prefix && std::iswdigit(data[0]) == 0) @@ -72,19 +72,19 @@ namespace RC::Ini } else { - File::StringViewType string = data.ends_with(STR('f')) ? File::StringViewType{data.begin(), data.end() - 1} : data; + SystemStringType string = data.ends_with(SYSSTR('f')) ? SystemStringType{data.begin(), data.end() - 1} : data; if (has_decimal_or_negative_prefix) { - string = File::StringViewType{string.begin() + 1, string.end()}; + string = SystemStringType{string.begin() + 1, string.end()}; } - bool is_float = std::ranges::all_of(string.begin(), string.end(), [&](const File::CharType c) { - if constexpr (std::is_same_v) + bool is_float = std::ranges::all_of(string.begin(), string.end(), [&](const UECharType c) { + if constexpr (std::is_same_v) { - return has_decimal_or_negative_prefix ? std::iswxdigit(c) : std::iswdigit(c) != 0 || c == STR('.'); + return has_decimal_or_negative_prefix ? std::iswxdigit(c) : std::iswdigit(c) != 0 || c == SYSSTR('.'); } else { - return has_decimal_or_negative_prefix ? std::isxdigit(c) : std::isdigit(c) != 0 || c == STR('.'); + return has_decimal_or_negative_prefix ? std::isxdigit(c) : std::isdigit(c) != 0 || c == SYSSTR('.'); } }); @@ -92,20 +92,17 @@ namespace RC::Ini } } - auto static is_bool(const File::StringType& data) -> Bool + auto static is_bool(const SystemStringType& data) -> Bool { - File::StringType all_lower_string_data = data; - // TODO: This to_lower implementation is not string-type agnostic - // A code change would be required if 'File::StringType' is defined as a char instead of a wchar_t - // Solution: Make two overloads in the string helper library, one for 'std::string' and one for 'std::wstring' - std::transform(all_lower_string_data.begin(), all_lower_string_data.end(), all_lower_string_data.begin(), [](wchar_t c) { + SystemStringType all_lower_string_data = data; + std::transform(all_lower_string_data.begin(), all_lower_string_data.end(), all_lower_string_data.begin(), [](UECharType c) { return std::towlower(c); }); - if (all_lower_string_data == STR("true") || all_lower_string_data == STR("1")) + if (all_lower_string_data == SYSSTR("true") || all_lower_string_data == SYSSTR("1")) { return Bool{.value = true, .is_bool = true}; } - else if (all_lower_string_data == STR("false") || all_lower_string_data == STR("0")) + else if (all_lower_string_data == SYSSTR("false") || all_lower_string_data == SYSSTR("0")) { return Bool{.value = false, .is_bool = true}; } @@ -115,7 +112,7 @@ namespace RC::Ini } } - auto TokenParser::find_variable_by_name(Section* section, const RC::StringType& name) -> std::optional> + auto TokenParser::find_variable_by_name(Section* section, const RC::SystemStringType& name) -> std::optional> { auto const& var = section->key_value_pairs.find(name); if (var != section->key_value_pairs.end()) @@ -128,9 +125,9 @@ namespace RC::Ini } } - auto TokenParser::find_variable_by_name(const StringType& name) -> std::optional> + auto TokenParser::find_variable_by_name(const SystemStringType& name) -> std::optional> { - size_t occurrence_of_dot = name.find_first_of(L'.'); + size_t occurrence_of_dot = name.find_first_of(SYSSTR('.')); if (occurrence_of_dot == name.npos || occurrence_of_dot + 1 > name.size()) { return find_variable_by_name(m_current_section, name); @@ -144,38 +141,38 @@ namespace RC::Ini } else { - const File::StringType requested_variable_name = name.substr(occurrence_of_dot + 1, name.size()); + const SystemStringType requested_variable_name = name.substr(occurrence_of_dot + 1, name.size()); return find_variable_by_name(&requested_section->second, requested_variable_name); } } } - auto state_to_string(State state) -> File::StringType + auto state_to_string(State state) -> SystemStringType { switch (state) { case State::StartOfFile: - return STR("StartOfFile"); + return SYSSTR("StartOfFile"); case State::SetSectionValue: - return STR("SetSectionValue"); + return SYSSTR("SetSectionValue"); case State::NewLineStarted: - return STR("NewLineStarted"); + return SYSSTR("NewLineStarted"); case State::CreateNewOrSetCurrentSection: - return STR("CreateNewOrSetCurrentSection"); + return SYSSTR("CreateNewOrSetCurrentSection"); case State::CreateSectionKey: - return STR("CreateSectionKey"); + return SYSSTR("CreateSectionKey"); case State::StoreSectionKey: - return STR("StoreSectionKey"); + return SYSSTR("StoreSectionKey"); } - return STR("UnknownState"); + return SYSSTR("UnknownState"); } - auto TokenParser::characters_to_string(const ParserBase::Token& characters_token) -> File::StringType + auto TokenParser::characters_to_string(const ParserBase::Token& characters_token) -> SystemStringType { auto* current_token = &characters_token; - File::StringType full_value{}; + SystemStringType full_value{}; while (true) { const auto token_type = current_token->get_type(); @@ -185,7 +182,7 @@ namespace RC::Ini } else if (token_type == IniTokenType::Space) { - full_value.append(STR(" ")); + full_value.append(SYSSTR(" ")); } else if (token_type == IniTokenType::Characters) { @@ -193,7 +190,7 @@ namespace RC::Ini } else if (token_type == IniTokenType::Equals) { - full_value.append(STR("=")); + full_value.append(SYSSTR("=")); } else if (token_type == IniTokenType::ClosingSquareBracket) { @@ -204,15 +201,15 @@ namespace RC::Ini // The next token is the last token on this line and since it's ] we don't want to include it in the string break; } - full_value.append(STR("]")); + full_value.append(SYSSTR("]")); } else if (token_type == IniTokenType::OpeningSquareBracket) { - full_value.append(STR("[")); + full_value.append(SYSSTR("[")); } else if (token_type == IniTokenType::SemiColon) { - full_value.append(STR(";")); + full_value.append(SYSSTR(";")); } // Exit early and let the state machine deal with the new line @@ -293,7 +290,7 @@ namespace RC::Ini { if (m_current_state == State::CreateNewOrSetCurrentSection) { - m_current_character_data.append(STR(" ")); + m_current_character_data.append(SYSSTR(" ")); } } @@ -384,7 +381,7 @@ namespace RC::Ini // Create the value with the correct key and an empty value and store a pointer to it so that the value can be set later m_current_value = &m_current_section->key_value_pairs.emplace(m_current_character_data, Value{}).first->second; - m_current_value->add_string_value(STR("")); + m_current_value->add_string_value(SYSSTR("")); m_current_value->set_ref(m_current_value); m_current_character_data.clear(); diff --git a/deps/first/IniParser/src/Value.cpp b/deps/first/IniParser/src/Value.cpp index 05e79ee57..36a23fa59 100644 --- a/deps/first/IniParser/src/Value.cpp +++ b/deps/first/IniParser/src/Value.cpp @@ -20,7 +20,7 @@ namespace RC::Ini m_ref = new_ref; } - auto Value::get_string_value() const -> const File::StringType& + auto Value::get_string_value() const -> const SystemStringType& { return m_string_value; } @@ -40,7 +40,7 @@ namespace RC::Ini return m_bool_value; } - auto Value::add_string_value(File::StringViewType data) -> void + auto Value::add_string_value(SystemStringViewType data) -> void { m_string_value = data; @@ -52,7 +52,7 @@ namespace RC::Ini add_type(); } - auto Value::add_int64_value(const StringType& data, int base) -> void + auto Value::add_int64_value(const SystemStringType& data, int base) -> void { m_int64_value = std::stoi(data, nullptr, base); @@ -64,7 +64,7 @@ namespace RC::Ini add_type(); } - auto Value::add_float_value(const StringType& data) -> void + auto Value::add_float_value(const SystemStringType& data) -> void { m_float_value = std::stof(data, nullptr); diff --git a/deps/first/Input/include/Input/Common.hpp b/deps/first/Input/include/Input/Common.hpp index ca6c5f89a..c2ab71801 100644 --- a/deps/first/Input/include/Input/Common.hpp +++ b/deps/first/Input/include/Input/Common.hpp @@ -1,6 +1,9 @@ #pragma once +#ifdef WIN32 + #ifndef RC_INPUT_EXPORTS + #ifndef RC_INPUT_BUILD_STATIC #ifndef RC_INPUT_API #define RC_INPUT_API __declspec(dllimport) @@ -10,8 +13,17 @@ #define RC_INPUT_API #endif #endif + #else #ifndef RC_INPUT_API #define RC_INPUT_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_INPUT_API +#define RC_INPUT_API +#endif + +#endif diff --git a/deps/first/Input/include/Input/Handler.hpp b/deps/first/Input/include/Input/Handler.hpp index ebc1ad592..53c428346 100644 --- a/deps/first/Input/include/Input/Handler.hpp +++ b/deps/first/Input/include/Input/Handler.hpp @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include #include @@ -15,12 +18,16 @@ namespace RC::Input { using EventCallbackCallable = std::function; - auto is_modifier_key_required(ModifierKey, std::vector) -> bool; + struct InputEvent + { + Key key; + ModifierKeys modifier_keys{}; + }; struct KeyData { - std::vector required_modifier_keys{}; - std::vector callbacks{}; + ModifierKeys required_modifier_keys{}; + EventCallbackCallable callback{}; uint8_t custom_data{}; void* custom_data2{}; bool requires_modifier_keys{}; @@ -32,48 +39,30 @@ namespace RC::Input std::unordered_map> key_data; }; + class PlatformInputSource; class RC_INPUT_API Handler { private: - std::vector m_active_window_classes{}; - std::vector m_key_sets{}; - std::unordered_map m_modifier_keys_down{}; - bool m_any_keys_are_down{}; + // std::vector m_key_sets{}; + KeySet m_key_set{}; bool m_allow_input{true}; + std::array m_subscribed_keys{}; - public: - Handler() = delete; - template - explicit Handler(WindowClasses... window_classes) - { - static_assert(std::conjunction...>::value, "WindowClasses must be of type const wchar_t*"); - - m_modifier_keys_down.emplace(ModifierKey::SHIFT, false); - m_modifier_keys_down.emplace(ModifierKey::CONTROL, false); - m_modifier_keys_down.emplace(ModifierKey::ALT, false); - - register_window_classes(window_classes...); - } + std::shared_ptr m_platform_handler; + std::mutex m_event_mutex; - private: - template - auto register_window_classes(WindowClass window_class) -> void - { - m_active_window_classes.emplace_back(window_class); - } - - template - auto register_window_classes(WindowClass window_class, WindowClasses... window_classes) -> void - { - m_active_window_classes.emplace_back(window_class); - register_window_classes(window_classes...); - } - - auto are_modifier_keys_down(const std::vector&) -> bool; - auto is_program_focused() -> bool; + public: + Handler(){}; + // Input source and event processing public: + auto set_input_source(std::string source) -> bool; auto process_event() -> void; + + // Interfaces for UE4SS and ModSystem for event registration + public: + auto init() -> void; + auto register_keydown_event(Input::Key, EventCallbackCallable, uint8_t custom_data = 0, void* custom_data2 = nullptr) -> void; using ModifierKeyArray = std::array; @@ -83,9 +72,34 @@ namespace RC::Input auto is_keydown_event_registered(Input::Key) -> bool; auto is_keydown_event_registered(Input::Key, const ModifierKeyArray&) -> bool; - auto get_events() -> std::vector&; + auto get_events_safe(std::function) -> void; + auto clear_subscribed_keys() -> void; + auto clear_subscribed_key(Key k) -> void; + + auto has_event_on_key(Input::Key key) -> bool; + auto get_subscribed_keys() const -> const std::array& + { + return m_subscribed_keys; + } + auto get_allow_input() -> bool; auto set_allow_input(bool new_value) -> void; + + auto get_current_input_source() -> std::string; + + private: + static std::unordered_map> m_input_sources_store; + static auto register_input_source(std::shared_ptr input_source) -> void; + + public: + static auto get_input_source(std::string source) -> std::shared_ptr + { + if (m_input_sources_store.find(source) != m_input_sources_store.end()) + { + return m_input_sources_store[source]; + } + return nullptr; + } }; } // namespace RC::Input diff --git a/deps/first/Input/include/Input/KeyDef.hpp b/deps/first/Input/include/Input/KeyDef.hpp index 02ec12976..219c15f79 100644 --- a/deps/first/Input/include/Input/KeyDef.hpp +++ b/deps/first/Input/include/Input/KeyDef.hpp @@ -1,13 +1,13 @@ #pragma once #include - +#include +#include namespace RC::Input { static constexpr uint32_t max_callbacks_per_event = 30; static constexpr uint8_t max_keys = 0xFF; -#ifdef _WIN32 enum Key : uint8_t { RESERVED_START_OF_ENUM = 0x0, @@ -244,11 +244,62 @@ namespace RC::Input MODIFIER_KEYS_MAX, }; + static inline bool is_modify_key_valid(ModifierKey key) + { + return (key < MODIFIER_KEYS_MAX) && (key > MOD_KEY_START_OF_ENUM); + } + + struct ModifierKeys + { + /// SAFETY: This is a bitfield, following static_assert ensures that the bitfield is not larger than 32 bits + uint32_t keys; + + // allow ops between keys + + auto operator|(const ModifierKeys& key) -> ModifierKeys&; + auto operator|=(const ModifierKeys& key) -> ModifierKeys&; + auto operator|(const ModifierKey& key) -> ModifierKeys&; + auto operator|=(const ModifierKey& key) -> ModifierKeys&; + + auto operator==(const ModifierKeys& key) const -> bool; + auto operator!=(const ModifierKeys& key) const -> bool; + + auto operator<(const ModifierKeys& key) const -> bool; + auto operator>(const ModifierKeys& key) const -> bool; + + ModifierKeys(const ModifierKey key) : keys{is_modify_key_valid(key) ? (1u << key) : 0} {}; + ModifierKeys(const ModifierKeys& other) : keys{other.keys} {}; + + ModifierKeys() : keys{0} {}; + + template + ModifierKeys(ModifierKey key, Args... args) : keys{(is_modify_key_valid(key) ? (1 << key) : 0) | ModifierKeys(args...).keys} {}; + + ModifierKeys(std::initializer_list keys); + + template + ModifierKeys(const TArray& keys) : keys{0} + { + for (auto key : keys) + { + if (is_modify_key_valid(key)) + { + this->keys |= (1 << key); + } + } + } + + bool empty() const + { + return keys == 0; + } + }; + static constexpr uint8_t max_modifier_keys = MODIFIER_KEYS_MAX; + static_assert(max_modifier_keys < 32, "Modifier keys cannot exceed 32"); -#else - static_assert(false, "The input library only works on Windows."); -#endif + auto operator&(const ModifierKeys& keys, const ModifierKey& key) -> bool; + auto operator&(const ModifierKeys& keys, const ModifierKeys& key) -> bool; auto operator++(Input::Key& key) -> Input::Key&; } // namespace RC::Input diff --git a/deps/first/Input/include/Input/Platform/GLFW3InputSource.hpp b/deps/first/Input/include/Input/Platform/GLFW3InputSource.hpp new file mode 100644 index 000000000..333673b5f --- /dev/null +++ b/deps/first/Input/include/Input/Platform/GLFW3InputSource.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace RC::Input +{ + + class GLFW3InputSource : public QueueInputSource + { + public: + auto begin_frame() -> void; + auto receive_input(int key, int action, int mods) -> void; + auto end_frame() -> void; + + private: + Key m_translate_key[512]; + + public: + auto is_available() -> bool override; + const char* get_name() override + { + return "GLFW3"; + } + GLFW3InputSource(); + }; + +} // namespace RC::Input diff --git a/deps/first/Input/include/Input/Platform/NcursesInputSource.hpp b/deps/first/Input/include/Input/Platform/NcursesInputSource.hpp new file mode 100644 index 000000000..624f32d50 --- /dev/null +++ b/deps/first/Input/include/Input/Platform/NcursesInputSource.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace RC::Input +{ + + class NcursesInputSource : public QueueInputSource + { + public: + auto begin_frame() -> void; + auto receive_input(int input) -> void; + auto end_frame() -> void; + + private: + /// SAFETY: Only update inside begin_frame ... receive_input(...) ... end_frame + std::vector m_key_cur{}; + std::vector m_key_last{}; + bool m_is_alt_down; + + public: + NcursesInputSource() : m_is_alt_down(false) + { + m_key_cur.resize(256, false); + m_key_last.resize(256, false); + } + + auto is_available() -> bool override; + const char* get_name() override + { + return "Ncurses"; + } + }; + +} // namespace RC::Input diff --git a/deps/first/Input/include/Input/Platform/QueueInputSource.hpp b/deps/first/Input/include/Input/Platform/QueueInputSource.hpp new file mode 100644 index 000000000..b184cc9e3 --- /dev/null +++ b/deps/first/Input/include/Input/Platform/QueueInputSource.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include + +namespace RC::Input +{ + class QueueInputSource : public PlatformInputSource + { + private: + static constexpr int max_inputs = 256; + RingBufferSPSC m_input_queue; + + protected: + bool m_activated{false}; + + private: + /// SAFETY: Only update and return m_input_events + /// in process_event and flush_events functions + std::vector m_input_events; + + public: + ~QueueInputSource() = default; + + // QueeueInputSource is not a implemented input source + // and should not be used directly + bool is_available() override + { + return false; + }; + + bool activate() override + { + return m_activated = true; + }; + + bool deactivate() override + { + m_activated = false; + return true; + }; + + std::vector& process_event(Handler* handler) override; + + int source_priority() override + { + return 0; + } + + const char* get_name() override + { + return "Queue"; + } + + public: + auto push_input_event(const InputEvent& event) -> void; + }; + +}; // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/include/Input/Platform/Win32AsyncInputSource.hpp b/deps/first/Input/include/Input/Platform/Win32AsyncInputSource.hpp new file mode 100644 index 000000000..0564de08a --- /dev/null +++ b/deps/first/Input/include/Input/Platform/Win32AsyncInputSource.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace RC::Input +{ + class Win32AsyncInputSource : public PlatformInputSource + { + private: + std::vector m_active_window_classes{}; + std::unordered_map m_modifier_keys_down{}; + bool m_any_keys_are_down{}; + bool m_activated{false}; + std::array m_key_down{}; + + private: + /// SAFETY: Only update and return m_input_events + /// in the process_event function + std::vector m_input_events{}; + + public: + template + explicit Win32AsyncInputSource(WindowClasses... window_classes) + { + static_assert(std::conjunction...>::value, "WindowClasses must be of type const wchar_t*"); + + m_modifier_keys_down.emplace(ModifierKey::SHIFT, false); + m_modifier_keys_down.emplace(ModifierKey::CONTROL, false); + m_modifier_keys_down.emplace(ModifierKey::ALT, false); + + register_window_classes(window_classes...); + } + + private: + template + auto register_window_classes(WindowClass window_class) -> void + { + m_active_window_classes.emplace_back(window_class); + } + + template + auto register_window_classes(WindowClass window_class, WindowClasses... window_classes) -> void + { + m_active_window_classes.emplace_back(window_class); + register_window_classes(window_classes...); + } + + auto are_modifier_keys_down(const std::vector&) -> bool; + auto is_program_focused() -> bool; + + public: + bool is_available() override + { + return true; + }; + + bool activate() override + { + m_key_down.fill(false); + return m_activated = true; + }; + + bool deactivate() override + { + m_activated = false; + return true; + }; + + std::vector& process_event(Handler* handler) override; + ~Win32AsyncInputSource() = default; + int source_priority() override + { + return 0; + } + + const char* get_name() override + { + return "Win32Async"; + } + }; +}; // namespace RC::Input diff --git a/deps/first/Input/include/Input/PlatformInputSource.hpp b/deps/first/Input/include/Input/PlatformInputSource.hpp new file mode 100644 index 000000000..387311b9a --- /dev/null +++ b/deps/first/Input/include/Input/PlatformInputSource.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace RC::Input +{ + + class PlatformInputSource + { + public: + /// Check if the input source is available + virtual bool is_available() + { + return false; + }; + + /// Initialize the input source + virtual bool activate() + { + return false; + }; + + /// Initialize the input source + virtual bool deactivate() + { + return false; + }; + + /// Get the priority of the input source, smaller number means higher priority + virtual int source_priority() + { + return 999; + }; + + /// Process the event and return the input events in the frame + virtual std::vector& process_event(Handler* handler) = 0; + + virtual ~PlatformInputSource() = default; + + virtual const char* get_name() + { + return "Unknown"; + } + }; + +}; // namespace RC::Input diff --git a/deps/first/Input/include/Input/RingBuffer.hpp b/deps/first/Input/include/Input/RingBuffer.hpp new file mode 100644 index 000000000..923d36ec9 --- /dev/null +++ b/deps/first/Input/include/Input/RingBuffer.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +namespace RC::Input +{ + /// SAFETY: This is ONLY a single producer, single consumer lock-free queue + template + struct RingBufferSPSC + { + T m_queue[max_buffer]; + std::atomic m_head{0}; + std::atomic m_tail{0}; + + // tail belongs to the producer + auto push(const T& event) -> bool + { + auto current_tail = m_tail.load(std::memory_order_relaxed); + int next_tail = (current_tail + 1) % max_buffer; + if (next_tail == m_head.load(std::memory_order_acquire)) + { + // Queue is full + return false; + } + + m_queue[current_tail] = event; + m_tail.store(next_tail, std::memory_order_release); + return true; + } + + // head belongs to the consumer + auto pop() -> std::optional + { + auto current_head = m_head.load(std::memory_order_relaxed); + if (current_head == m_tail.load(std::memory_order_acquire)) + { + // Queue is empty + return std::nullopt; + } + + T event = m_queue[current_head]; + m_head.store((current_head + 1) % max_buffer, std::memory_order_release); + return event; + } + }; +} // namespace RC::Input diff --git a/deps/first/Input/src/Handler.cpp b/deps/first/Input/src/Handler.cpp index 8533a9aec..0165e16ff 100644 --- a/deps/first/Input/src/Handler.cpp +++ b/deps/first/Input/src/Handler.cpp @@ -1,345 +1,209 @@ #include +#include +#include #include - -#define NOMINMAX -#include - +#include namespace RC::Input { - auto is_modifier_key_required(ModifierKey modifier_key, std::vector modifier_keys) -> bool - { - for (const auto& required_modifier_key : modifier_keys) - { - if (required_modifier_key == ModifierKey::MOD_KEY_START_OF_ENUM) - { - continue; - } - - if (modifier_key == required_modifier_key) - { - return true; - } - } - - return false; - } - - auto Handler::are_modifier_keys_down(const std::vector& required_modifier_keys) -> bool - { - bool are_required_modifier_keys_down = true; - - for (const auto& [modifier_key, modifier_key_is_down] : m_modifier_keys_down) - { - for (const auto& required_modifier_key : required_modifier_keys) - { - if (modifier_key == required_modifier_key && !modifier_key_is_down) - { - are_required_modifier_keys_down = false; - } - - if (modifier_key != required_modifier_key && modifier_key_is_down && !is_modifier_key_required(modifier_key, required_modifier_keys)) - { - return false; - } - } - } - - return are_required_modifier_keys_down; - } - - auto Handler::is_program_focused() -> bool - { - HWND hwnd = GetForegroundWindow(); - if (!hwnd) return false; - - wchar_t current_window_class_name[MAX_PATH]; - if (!GetClassNameW(hwnd, current_window_class_name, MAX_PATH)) return false; - - for (const auto& active_window_class : m_active_window_classes) - { - if (wcscmp(current_window_class_name, active_window_class) == 0) - { - return true; - } - } - - return false; - } + std::unordered_map> Handler::m_input_sources_store; auto Handler::process_event() -> void { - if (!is_program_focused()) + if (m_platform_handler == nullptr) { return; } std::vector callbacks_to_call{}; - bool skip_this_frame = !get_allow_input(); - bool is_any_modifier_keys_down = false; - bool any_keys_are_down = false; + auto events = m_platform_handler->process_event(this); - if (m_any_keys_are_down) { - skip_this_frame = true; - } - - // Check if any modifier keys are down - for (auto& [modifier_key, key_is_down] : m_modifier_keys_down) - { - if (GetAsyncKeyState(modifier_key)) - { - is_any_modifier_keys_down = true; - key_is_down = true; - } - else + // Lock the event mutex to access the key_set + auto event_update_lock = std::lock_guard(m_event_mutex); + for (auto& event : events) { - key_is_down = false; - } - } - - for (auto& key_set_data : m_key_sets) - { - for (auto& [key, key_data_array] : key_set_data.key_data) - { - for (auto& key_data : key_data_array) + auto key_set_array = m_key_set.key_data[event.key]; + for (auto& key_data : key_set_array) { - if (GetAsyncKeyState(key) && !key_data.is_down) + if (key_data.required_modifier_keys == event.modifier_keys) { - any_keys_are_down = true; - bool should_propagate = true; - - if (key_data.requires_modifier_keys) - { - if (!are_modifier_keys_down(key_data.required_modifier_keys)) - { - should_propagate = false; - } - } - - if (!key_data.requires_modifier_keys && is_any_modifier_keys_down) - { - should_propagate = false; - } - - if (should_propagate) - { - key_data.is_down = true; - for (const auto& callback : key_data.callbacks) - { - callbacks_to_call.emplace_back(callback); - } - } - } - else if (!GetAsyncKeyState(key) && key_data.is_down) - { - key_data.is_down = false; + callbacks_to_call.emplace_back(key_data.callback); } } } } - if (any_keys_are_down) - { - m_any_keys_are_down = true; - } - else - { - m_any_keys_are_down = false; - } - + // No need to lock the event mutex to call the callbacks + // this avoids key registration inside the callback for (const auto& callback : callbacks_to_call) { - if (skip_this_frame) - { - return; - } callback(); } } auto Handler::register_keydown_event(Input::Key key, EventCallbackCallable callback, uint8_t custom_data, void* custom_data2) -> void { - KeySet& key_set = [&]() -> KeySet& { - for (auto& key_set : m_key_sets) - { - if (key_set.key_data.contains(key)) - { - return key_set; - } - } - - return m_key_sets.emplace_back(KeySet{}); - }(); - - KeyData& key_data = key_set.key_data[key].emplace_back(); - key_data.callbacks.emplace_back(callback); + auto event_update_lock = std::lock_guard(m_event_mutex); + KeyData& key_data = m_key_set.key_data[key].emplace_back(); + key_data.callback = callback; key_data.custom_data = custom_data; key_data.custom_data2 = custom_data2; + m_subscribed_keys[key] = true; } auto Handler::register_keydown_event( Input::Key key, const ModifierKeyArray& modifier_keys, const EventCallbackCallable& callback, uint8_t custom_data, void* custom_data2) -> void { - KeySet& key_set = [&]() -> KeySet& { - for (auto& key_set : m_key_sets) - { - if (key_set.key_data.contains(key)) - { - return key_set; - } - } - - return m_key_sets.emplace_back(KeySet{}); - }(); - - KeyData& key_data = key_set.key_data[key].emplace_back(); - key_data.callbacks.emplace_back(callback); + auto event_update_lock = std::lock_guard(m_event_mutex); + KeyData& key_data = m_key_set.key_data[key].emplace_back(); + key_data.callback = callback; key_data.custom_data = custom_data; key_data.custom_data2 = custom_data2; key_data.requires_modifier_keys = true; + key_data.required_modifier_keys = modifier_keys; + m_subscribed_keys[key] = true; + } - for (const auto& modifier_key : modifier_keys) + auto Handler::is_keydown_event_registered(Input::Key key) -> bool + { + auto event_update_lock = std::lock_guard(m_event_mutex); + auto key_data = m_key_set.key_data.find(key); + if (key_data == m_key_set.key_data.end()) { - if (modifier_key != ModifierKey::MOD_KEY_START_OF_ENUM) + return false; + } + for (const auto& key_data_container : key_data->second) + { + if (key_data_container.required_modifier_keys.empty()) { - key_data.required_modifier_keys.emplace_back(modifier_key); + return true; } } + return false; } - auto Handler::is_keydown_event_registered(Input::Key key) -> bool + auto Handler::is_keydown_event_registered(Input::Key key, const ModifierKeyArray& modifier_keys_array) -> bool { - bool is_key_registered{}; - bool is_key_registered_with_no_modifier_keys = true; - for (const auto& key_set : m_key_sets) + auto event_update_lock = std::lock_guard(m_event_mutex); + auto key_data = m_key_set.key_data.find(key); + auto modifier_keys = ModifierKeys(modifier_keys_array); + if (key_data == m_key_set.key_data.end()) { - for (const auto& [key_registered, key_data_container] : key_set.key_data) - { - if (key_registered == key) - { - is_key_registered = true; - } - else - { - continue; - } - - for (const auto& key_data : key_data_container) - { - if (key_data.requires_modifier_keys) - { - is_key_registered_with_no_modifier_keys = false; - break; - } - } - - if (!is_key_registered_with_no_modifier_keys) - { - break; - } - } - - if (!is_key_registered_with_no_modifier_keys) + return false; + } + for (const auto& key_data_container : key_data->second) + { + if (key_data_container.required_modifier_keys == modifier_keys) { - break; + return true; } } - - return is_key_registered && is_key_registered_with_no_modifier_keys; + return false; } - auto Handler::is_keydown_event_registered(Input::Key key, const ModifierKeyArray& modifier_keys) -> bool + auto Handler::has_event_on_key(Input::Key key) -> bool { - bool is_key_registered{}; - bool all_modifier_keys_match{}; - for (const auto& key_set : m_key_sets) - { - for (const auto& [key_registered, key_data_container] : key_set.key_data) - { - if (key_registered == key) - { - is_key_registered = true; - } - else - { - continue; - } + return m_subscribed_keys[key]; + } - all_modifier_keys_match = false; - for (const auto& key_data : key_data_container) - { - if (!key_data.requires_modifier_keys && !modifier_keys.empty()) - { - all_modifier_keys_match = false; - continue; - } + auto Handler::get_events_safe(std::function callback) -> void + { + auto event_update_lock = std::lock_guard(m_event_mutex); + callback(m_key_set); + } - if (!key_data.requires_modifier_keys && modifier_keys.empty()) - { - all_modifier_keys_match = true; - break; - } + auto Handler::clear_subscribed_keys() -> void + { + m_subscribed_keys.fill(false); + } - for (const auto& modifier_key : key_data.required_modifier_keys) - { - for (const auto modifier_key_to_check : modifier_keys) - { - if (modifier_key_to_check == ModifierKey::MOD_KEY_START_OF_ENUM) - { - continue; - } + auto Handler::clear_subscribed_key(Key k) -> void + { + m_subscribed_keys[k] = false; + } - if (modifier_key_to_check == modifier_key) - { - all_modifier_keys_match = true; - } - else - { - all_modifier_keys_match = false; - } - } + auto Handler::get_allow_input() -> bool + { + return m_allow_input; + } - if (all_modifier_keys_match) - { - break; - } - } + auto Handler::set_allow_input(bool new_value) -> void + { + m_allow_input = new_value; + } - if (all_modifier_keys_match) - { - break; - } + /// Set the input source to the given source + /// SAFETY: Only call this function from the main thread + auto Handler::set_input_source(std::string source) -> bool + { + auto event_update_lock = std::lock_guard(m_event_mutex); + std::shared_ptr next_input_source = nullptr; + if (source == "Default") + { + // find the highest priority input source + int highest_priority = INT_MAX; + for (auto& input_source : m_input_sources_store) + { + if (!input_source.second->is_available()) + { + continue; } - - if (all_modifier_keys_match) + auto priority = input_source.second->source_priority(); + if (priority < highest_priority) { - break; + next_input_source = input_source.second; + highest_priority = priority; } } - - if (all_modifier_keys_match) + if (highest_priority == INT_MAX) { - break; + return false; } } - - return is_key_registered && all_modifier_keys_match; - } - - auto Handler::get_events() -> std::vector& - { - return m_key_sets; + else + { + auto input_source = m_input_sources_store.find(source); + if (input_source == m_input_sources_store.end()) + { + return false; + } + next_input_source = input_source->second; + if (!next_input_source->is_available()) + { + return false; + } + } + if (next_input_source != m_platform_handler) + { + if (m_platform_handler != nullptr) + { + m_platform_handler->deactivate(); + } + next_input_source->activate(); + m_platform_handler = next_input_source; + return true; + } + return true; } - auto Handler::get_allow_input() -> bool + // register the input source to the input source store + auto Handler::register_input_source(std::shared_ptr input_source) -> void { - return m_allow_input; + std::string name = input_source->get_name(); + if (m_input_sources_store.find(name) == m_input_sources_store.end()) + { + m_input_sources_store[name] = input_source; + } } - auto Handler::set_allow_input(bool new_value) -> void + auto Handler::get_current_input_source() -> std::string { - m_allow_input = new_value; + if (m_platform_handler == nullptr) + { + return "None"; + } + return m_platform_handler->get_name(); } } // namespace RC::Input diff --git a/deps/first/Input/src/KeyDef.cpp b/deps/first/Input/src/KeyDef.cpp index 5e73dbbc1..9057adc0f 100644 --- a/deps/first/Input/src/KeyDef.cpp +++ b/deps/first/Input/src/KeyDef.cpp @@ -1,4 +1,5 @@ #include +#include namespace RC::Input { @@ -14,4 +15,71 @@ namespace RC::Input return key; } + + auto ModifierKeys::operator|(const ModifierKeys& rkeys) -> ModifierKeys& + { + keys |= rkeys.keys; + return *this; + } + + auto ModifierKeys::operator|(const ModifierKey& key) -> ModifierKeys& + { + if (is_modify_key_valid(key)) + { + keys |= (1 << key); + } + return *this; + } + + auto ModifierKeys::operator|=(const ModifierKeys& rkeys) -> ModifierKeys& + { + return *this = *this | rkeys; + } + + auto ModifierKeys::operator|=(const ModifierKey& key) -> ModifierKeys& + { + return *this = *this | key; + } + + auto ModifierKeys::operator==(const ModifierKeys& key) const -> bool + { + return keys == key.keys; + } + + auto ModifierKeys::operator<(const ModifierKeys& rkeys) const -> bool + { + return keys < rkeys.keys; + } + + auto ModifierKeys::operator>(const ModifierKeys& rkeys) const -> bool + { + return keys > rkeys.keys; + } + + auto ModifierKeys::operator!=(const ModifierKeys& key) const -> bool + { + return keys != key.keys; + } + + ModifierKeys::ModifierKeys(std::initializer_list keys) + { + for (auto key : keys) + { + if (is_modify_key_valid(key)) + { + this->keys |= (1 << key); + } + } + } + + auto operator&(const ModifierKeys& keys, const ModifierKey& key) -> bool + { + return !!(keys.keys & (1 << key)); + } + + auto operator&(const ModifierKeys& keys, const ModifierKeys& key) -> bool + { + return !!(keys.keys & key.keys); + } + } // namespace RC::Input diff --git a/deps/first/Input/src/Platform/GLFW3InputSource.cpp b/deps/first/Input/src/Platform/GLFW3InputSource.cpp new file mode 100644 index 000000000..776276513 --- /dev/null +++ b/deps/first/Input/src/Platform/GLFW3InputSource.cpp @@ -0,0 +1,349 @@ +#include + +#define GLFW_KEY_SPACE 32 + +#define GLFW_KEY_APOSTROPHE 39 /* ' */ + +#define GLFW_KEY_COMMA 44 /* , */ + +#define GLFW_KEY_MINUS 45 /* - */ + +#define GLFW_KEY_PERIOD 46 /* . */ + +#define GLFW_KEY_SLASH 47 /* / */ + +#define GLFW_KEY_0 48 + +#define GLFW_KEY_1 49 + +#define GLFW_KEY_2 50 + +#define GLFW_KEY_3 51 + +#define GLFW_KEY_4 52 + +#define GLFW_KEY_5 53 + +#define GLFW_KEY_6 54 + +#define GLFW_KEY_7 55 + +#define GLFW_KEY_8 56 + +#define GLFW_KEY_9 57 + +#define GLFW_KEY_SEMICOLON 59 /* ; */ + +#define GLFW_KEY_EQUAL 61 /* = */ + +#define GLFW_KEY_A 65 + +#define GLFW_KEY_B 66 + +#define GLFW_KEY_C 67 + +#define GLFW_KEY_D 68 + +#define GLFW_KEY_E 69 + +#define GLFW_KEY_F 70 + +#define GLFW_KEY_G 71 + +#define GLFW_KEY_H 72 + +#define GLFW_KEY_I 73 + +#define GLFW_KEY_J 74 + +#define GLFW_KEY_K 75 + +#define GLFW_KEY_L 76 + +#define GLFW_KEY_M 77 + +#define GLFW_KEY_N 78 + +#define GLFW_KEY_O 79 + +#define GLFW_KEY_P 80 + +#define GLFW_KEY_Q 81 + +#define GLFW_KEY_R 82 + +#define GLFW_KEY_S 83 + +#define GLFW_KEY_T 84 + +#define GLFW_KEY_U 85 + +#define GLFW_KEY_V 86 + +#define GLFW_KEY_W 87 + +#define GLFW_KEY_X 88 + +#define GLFW_KEY_Y 89 + +#define GLFW_KEY_Z 90 + +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ + +#define GLFW_KEY_BACKSLASH 92 /* \ */ + +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ + +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ + +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ + +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +#define GLFW_KEY_ESCAPE 256 + +#define GLFW_KEY_ENTER 257 + +#define GLFW_KEY_TAB 258 + +#define GLFW_KEY_BACKSPACE 259 + +#define GLFW_KEY_INSERT 260 + +#define GLFW_KEY_DELETE 261 + +#define GLFW_KEY_RIGHT 262 + +#define GLFW_KEY_LEFT 263 + +#define GLFW_KEY_DOWN 264 + +#define GLFW_KEY_UP 265 + +#define GLFW_KEY_PAGE_UP 266 + +#define GLFW_KEY_PAGE_DOWN 267 + +#define GLFW_KEY_HOME 268 + +#define GLFW_KEY_END 269 + +#define GLFW_KEY_CAPS_LOCK 280 + +#define GLFW_KEY_SCROLL_LOCK 281 + +#define GLFW_KEY_NUM_LOCK 282 + +#define GLFW_KEY_PRINT_SCREEN 283 + +#define GLFW_KEY_PAUSE 284 + +#define GLFW_KEY_F1 290 + +#define GLFW_KEY_F2 291 + +#define GLFW_KEY_F3 292 + +#define GLFW_KEY_F4 293 + +#define GLFW_KEY_F5 294 + +#define GLFW_KEY_F6 295 + +#define GLFW_KEY_F7 296 + +#define GLFW_KEY_F8 297 + +#define GLFW_KEY_F9 298 + +#define GLFW_KEY_F10 299 + +#define GLFW_KEY_F11 300 + +#define GLFW_KEY_F12 301 + +#define GLFW_KEY_F13 302 + +#define GLFW_KEY_F14 303 + +#define GLFW_KEY_F15 304 + +#define GLFW_KEY_F16 305 + +#define GLFW_KEY_F17 306 + +#define GLFW_KEY_F18 307 + +#define GLFW_KEY_F19 308 + +#define GLFW_KEY_F20 309 + +#define GLFW_KEY_F21 310 + +#define GLFW_KEY_F22 311 + +#define GLFW_KEY_F23 312 + +#define GLFW_KEY_F24 313 + +#define GLFW_KEY_F25 314 + +#define GLFW_KEY_KP_0 320 + +#define GLFW_KEY_KP_1 321 + +#define GLFW_KEY_KP_2 322 + +#define GLFW_KEY_KP_3 323 + +#define GLFW_KEY_KP_4 324 + +#define GLFW_KEY_KP_5 325 + +#define GLFW_KEY_KP_6 326 + +#define GLFW_KEY_KP_7 327 + +#define GLFW_KEY_KP_8 328 + +#define GLFW_KEY_KP_9 329 + +#define GLFW_KEY_KP_DECIMAL 330 + +#define GLFW_KEY_KP_DIVIDE 331 + +#define GLFW_KEY_KP_MULTIPLY 332 + +#define GLFW_KEY_KP_SUBTRACT 333 + +#define GLFW_KEY_KP_ADD 334 + +#define GLFW_KEY_KP_ENTER 335 + +#define GLFW_KEY_KP_EQUAL 336 + +#define GLFW_KEY_LEFT_SHIFT 340 + +#define GLFW_KEY_LEFT_CONTROL 341 + +#define GLFW_KEY_LEFT_ALT 342 + +#define GLFW_KEY_LEFT_SUPER 343 + +#define GLFW_KEY_RIGHT_SHIFT 344 + +#define GLFW_KEY_RIGHT_CONTROL 345 + +#define GLFW_KEY_RIGHT_ALT 346 + +#define GLFW_KEY_RIGHT_SUPER 347 + +#define GLFW_KEY_MENU 348 + +#define GLFW_KEY_LAST GLFW_KEY_MENU + +#define GLFW_MOD_SHIFT 0x0001 +#define GLFW_MOD_CONTROL 0x0002 +#define GLFW_MOD_ALT 0x0004 + +#define GLFW_RELEASE 0 +#define GLFW_PRESS 1 +#define GLFW_REPEAT 2 + +namespace RC::Input +{ + auto GLFW3InputSource::begin_frame() -> void + { + } + + auto GLFW3InputSource::receive_input(int key, int action, int mods) -> void + { + auto modifier_keys = ModifierKeys{}; + modifier_keys |= (mods & GLFW_MOD_CONTROL ? ModifierKey::CONTROL : ModifierKey::MOD_KEY_START_OF_ENUM); + modifier_keys |= (mods & GLFW_MOD_SHIFT ? ModifierKey::SHIFT : ModifierKey::MOD_KEY_START_OF_ENUM); + modifier_keys |= (mods & GLFW_MOD_ALT ? ModifierKey::ALT : ModifierKey::MOD_KEY_START_OF_ENUM); + if (action == GLFW_PRESS) + { + // translate key + Key input = m_translate_key[key]; + if (input != RESERVED_START_OF_ENUM) + { + push_input_event({input, modifier_keys}); + } + } + } + + auto GLFW3InputSource::end_frame() -> void + { + } + + auto GLFW3InputSource::is_available() -> bool + { + return true; + } + + GLFW3InputSource::GLFW3InputSource() + { + // init to 0 + for (int i = 0; i < 512; ++i) + { + m_translate_key[i] = static_cast(0); + } + // Alphanumeric keys + for (int key = GLFW_KEY_A; key <= GLFW_KEY_Z; ++key) + { + m_translate_key[key] = static_cast(key); + } + for (int key = GLFW_KEY_0; key <= GLFW_KEY_9; ++key) + { + m_translate_key[key] = static_cast(key + 22); + } + + // Symbol keys + m_translate_key[GLFW_KEY_SPACE] = Key::SPACE; + m_translate_key[GLFW_KEY_APOSTROPHE] = Key::OEM_SEVEN; + m_translate_key[GLFW_KEY_COMMA] = Key::OEM_COMMA; + m_translate_key[GLFW_KEY_MINUS] = Key::OEM_MINUS; + m_translate_key[GLFW_KEY_PERIOD] = Key::OEM_PERIOD; + m_translate_key[GLFW_KEY_SLASH] = Key::OEM_TWO; + m_translate_key[GLFW_KEY_SEMICOLON] = Key::OEM_ONE; + m_translate_key[GLFW_KEY_EQUAL] = Key::OEM_PLUS; + m_translate_key[GLFW_KEY_LEFT_BRACKET] = Key::OEM_FOUR; + m_translate_key[GLFW_KEY_BACKSLASH] = Key::OEM_FIVE; + m_translate_key[GLFW_KEY_RIGHT_BRACKET] = Key::OEM_SIX; + m_translate_key[GLFW_KEY_GRAVE_ACCENT] = Key::OEM_THREE; + + // Control keys + m_translate_key[GLFW_KEY_ESCAPE] = Key::ESCAPE; + m_translate_key[GLFW_KEY_ENTER] = Key::RETURN; + m_translate_key[GLFW_KEY_TAB] = Key::TAB; + m_translate_key[GLFW_KEY_BACKSPACE] = Key::BACKSPACE; + m_translate_key[GLFW_KEY_INSERT] = Key::INS; + m_translate_key[GLFW_KEY_DELETE] = Key::DEL; + m_translate_key[GLFW_KEY_RIGHT] = Key::RIGHT_ARROW; + m_translate_key[GLFW_KEY_LEFT] = Key::LEFT_ARROW; + m_translate_key[GLFW_KEY_DOWN] = Key::DOWN_ARROW; + m_translate_key[GLFW_KEY_UP] = Key::UP_ARROW; + m_translate_key[GLFW_KEY_PAGE_UP] = Key::PAGE_UP; + m_translate_key[GLFW_KEY_PAGE_DOWN] = Key::PAGE_DOWN; + m_translate_key[GLFW_KEY_HOME] = Key::HOME; + m_translate_key[GLFW_KEY_END] = Key::END; + m_translate_key[GLFW_KEY_CAPS_LOCK] = Key::CAPS_LOCK; + m_translate_key[GLFW_KEY_SCROLL_LOCK] = Key::SCROLL_LOCK; + m_translate_key[GLFW_KEY_NUM_LOCK] = Key::NUM_LOCK; + m_translate_key[GLFW_KEY_PRINT_SCREEN] = Key::PRINT_SCREEN; + m_translate_key[GLFW_KEY_PAUSE] = Key::PAUSE; + + // Function keys + for (int key = GLFW_KEY_F1; key <= GLFW_KEY_F25; ++key) + { + m_translate_key[key] = static_cast(key + 111); + } + + // Numeric keypad + for (int key = GLFW_KEY_KP_0; key <= GLFW_KEY_KP_EQUAL; ++key) + { + m_translate_key[key] = static_cast(key + 208); + } + } +} // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/src/Platform/NcursesInputSource.cpp b/deps/first/Input/src/Platform/NcursesInputSource.cpp new file mode 100644 index 000000000..f7d98c496 --- /dev/null +++ b/deps/first/Input/src/Platform/NcursesInputSource.cpp @@ -0,0 +1,295 @@ +#include +#include + +namespace RC::Input +{ + auto NcursesInputSource::begin_frame() -> void + { + std::fill(m_key_cur.begin(), m_key_cur.end(), false); + } + + static Key translate_control_key(int k) + { + switch (k) + { + case 0: + return Key::SPACE; + case 1: + return Key::A; + case 2: + return Key::B; + case 3: + return Key::C; + case 4: + return Key::D; + case 5: + return Key::E; + case 6: + return Key::F; + case 7: + return Key::G; + case 8: + return Key::H; + case 9: + return Key::I; + case 10: + return Key::J; + case 11: + return Key::K; + case 12: + return Key::L; + case 13: + return Key::M; + case 14: + return Key::N; + case 15: + return Key::O; + case 16: + return Key::P; + case 17: + return Key::Q; + case 18: + return Key::R; + case 19: + return Key::S; + case 20: + return Key::T; + case 21: + return Key::U; + case 22: + return Key::V; + case 23: + return Key::W; + case 24: + return Key::X; + case 25: + return Key::Y; + case 26: + return Key::Z; + // case 27: return Key::Escape; + case 28: + return Key::BACKSPACE; + // case 29: return Key::CtrlRightBracket; + // case 30: return Key::CtrlCircumflex; + // case 31: return Key::CtrlUnderscore; + default: + return Key::RESERVED_START_OF_ENUM; + } + } + +#define KEY_DOWN 0402 /* down-arrow key */ +#define KEY_UP 0403 /* up-arrow key */ +#define KEY_LEFT 0404 /* left-arrow key */ +#define KEY_RIGHT 0405 /* right-arrow key */ +#define KEY_HOME 0406 /* home key */ +#define KEY_BACKSPACE 0407 /* backspace key */ +#define KEY_F0 0410 /* Function keys. Space for 64 */ +#define KEY_F(n) (KEY_F0 + (n)) /* Value of function key n */ +#define KEY_DC 0512 /* delete-character key */ +#define KEY_IC 0513 /* insert-character key */ + + static Key translate_key_may_shift(int ch, bool& shifted) + { + shifted = false; + if (ch >= 97 && ch <= 122) + { + return static_cast(ch - 97 + static_cast(Key::A)); + } + if (ch >= 65 && ch <= 90) + { + // capital letter + shifted = true; + return static_cast(ch - 65 + static_cast(Key::A)); + } + + if (ch >= 48 && ch <= 57) + { + return static_cast(ch - 48 + static_cast(Key::ZERO)); + } + switch (ch) + { + case 32: + return Key::SPACE; + // case 10: return Key::ENTER; + // case 27: return Key::ESCAPE; + case 127: + return Key::BACKSPACE; + case KEY_DOWN: + return Key::DOWN_ARROW; + case KEY_UP: + return Key::UP_ARROW; + case KEY_LEFT: + return Key::LEFT_ARROW; + case KEY_RIGHT: + return Key::RIGHT_ARROW; + case KEY_HOME: + return Key::HOME; + case KEY_BACKSPACE: + return Key::BACKSPACE; + case KEY_F(1): + return Key::F1; + case KEY_F(2): + return Key::F2; + case KEY_F(3): + return Key::F3; + case KEY_F(4): + return Key::F4; + case KEY_F(5): + return Key::F5; + case KEY_F(6): + return Key::F6; + case KEY_F(7): + return Key::F7; + case KEY_F(8): + return Key::F8; + case KEY_F(9): + return Key::F9; + case KEY_F(10): + return Key::F10; + case KEY_F(11): + return Key::F11; + case KEY_F(12): + return Key::F12; + case KEY_DC: + return Key::DEL; + case KEY_IC: + return Key::INS; + } +#define CASE_CHAR(normal_ch, shifted_ch, key) \ + case normal_ch: \ + shifted = false; \ + case shifted_ch: \ + return key; + shifted = true; + switch (ch) + { + case '!': + return Key::ONE; + case '@': + return Key::TWO; + case '#': + return Key::THREE; + case '$': + return Key::FOUR; + case '%': + return Key::FIVE; + case '^': + return Key::SIX; + case '&': + return Key::SEVEN; + case '*': + return Key::EIGHT; + case '(': + return Key::NINE; + case ')': + return Key::ZERO; // Shift+0 produces ')' + CASE_CHAR('`', '~', Key::OEM_THREE) // key ~ is oem 3 on windows + CASE_CHAR('-', '_', Key::OEM_MINUS) // key _ is oem minus on windows + CASE_CHAR('=', '+', Key::OEM_PLUS) // key + is oem plus on windows + CASE_CHAR('[', '{', Key::OEM_FOUR) // key { is oem four on windows + CASE_CHAR(']', '}', Key::OEM_SIX) // key } is oem 6 on windows + CASE_CHAR('\\', '|', Key::OEM_FIVE) // key | is oem 5 on windows + CASE_CHAR(';', ':', Key::OEM_ONE) // key : is oem semicolon on windows + CASE_CHAR('\'', '"', Key::OEM_SEVEN) // key " is oem apostrophe on windows + CASE_CHAR(',', '<', Key::OEM_COMMA) // key < is oem comma on windows + CASE_CHAR('.', '>', Key::OEM_PERIOD) // key > is oem period on windows + CASE_CHAR('/', '?', Key::OEM_TWO) // key ? is oem 2 on windows + default: + shifted = false; + return Key::RESERVED_START_OF_ENUM; + } + } + + auto NcursesInputSource::receive_input(int input) -> void + { + if (!m_activated) + { + return; + } + + // input is ncurse's key code + if (input == 27) + { + // esc key + m_is_alt_down = true; + } + else + { + auto control_down = false; + auto shift_down = false; + auto key_code = -1; + if (input >= 0 && input < 32) + { + Key input_key = translate_control_key(input); + if (input_key != Key::RESERVED_START_OF_ENUM) + { + key_code = static_cast(input_key); + control_down = true; + } + } + else + { + Key input_key = translate_key_may_shift(input, shift_down); + if (input_key != Key::RESERVED_START_OF_ENUM) + { + key_code = static_cast(input_key); + } + } + if (key_code != -1) + { + m_key_cur[key_code] = true; + if (!m_key_last[key_code]) + { + // keydown + ModifierKeys modifier_keys{}; + modifier_keys |= (control_down ? ModifierKey::CONTROL : ModifierKey::MOD_KEY_START_OF_ENUM); + modifier_keys |= (shift_down ? ModifierKey::SHIFT : ModifierKey::MOD_KEY_START_OF_ENUM); + modifier_keys |= (m_is_alt_down ? ModifierKey::ALT : ModifierKey::MOD_KEY_START_OF_ENUM); + push_input_event({static_cast(key_code), modifier_keys}); + auto modify_to_string = [](ModifierKeys modifier_keys) -> std::string { + std::string result; + if (modifier_keys & ModifierKey::CONTROL) + { + result += "Control"; + } + if (modifier_keys & ModifierKey::SHIFT) + { + if (!result.empty()) + { + result += "+"; + } + result += "Shift"; + } + if (modifier_keys & ModifierKey::ALT) + { + if (!result.empty()) + { + result += "+"; + } + result += "Alt"; + } + return result; + }; + Output::send(SYSSTR("NcursesInputSource::receive_input: key down {} {}"), + static_cast(input), + modify_to_string(modifier_keys)); + } + else + { + Output::send(SYSSTR("NcursesInputSource::receive_input: key repeat {}"), static_cast(input)); + } + } + m_is_alt_down = false; + } + } + + auto NcursesInputSource::end_frame() -> void + { + std::swap(m_key_cur, m_key_last); + } + + auto NcursesInputSource::is_available() -> bool + { + return true; + } +} // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/src/Platform/QueueInputSource.cpp b/deps/first/Input/src/Platform/QueueInputSource.cpp new file mode 100644 index 000000000..f7170101e --- /dev/null +++ b/deps/first/Input/src/Platform/QueueInputSource.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include + +namespace RC::Input +{ + auto QueueInputSource::push_input_event(const InputEvent& event) -> void + { + if (m_activated) + { + m_input_queue.push(event); + Output::send(SYSSTR("QueueInputSource::push_input_event: {}"), (int)event.key); + } + } + + // even if not activated, we still consume the remaining events in the queue + std::vector& QueueInputSource::process_event(Handler* handler) + { + m_input_events.clear(); + + auto event = m_input_queue.pop(); + auto& key_set = handler->get_subscribed_keys(); + while (event) + { + Output::send(SYSSTR("QueueInputSource::reveive key event: {}"), (int)event->key); + if (key_set[event->key]) + { + m_input_events.emplace_back(*event); + } + event = m_input_queue.pop(); + } + + if (!m_activated) + { + m_input_events.clear(); + } + + if (!handler->get_allow_input()) + { + m_input_events.clear(); + } + + return m_input_events; + } +}; // namespace RC::Input diff --git a/deps/first/Input/src/Platform/Win32AsyncInputSource.cpp b/deps/first/Input/src/Platform/Win32AsyncInputSource.cpp new file mode 100644 index 000000000..7169ae55f --- /dev/null +++ b/deps/first/Input/src/Platform/Win32AsyncInputSource.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#define NOMINMAX +#include + +namespace RC::Input +{ + + auto Win32AsyncInputSource::is_program_focused() -> bool + { + HWND hwnd = GetForegroundWindow(); + if (!hwnd) return false; + wchar_t current_window_class_name[MAX_PATH]; + if (!GetClassNameW(hwnd, current_window_class_name, MAX_PATH)) return false; + for (const auto& active_window_class : m_active_window_classes) + { + if (wcscmp(current_window_class_name, active_window_class) == 0) + { + return true; + } + } + return false; + } + + std::vector& Win32AsyncInputSource::process_event(Handler* handler) + { + m_input_events.clear(); + + if (!is_program_focused()) + { + return m_input_events; + } + + if (!m_activated) + { + return m_input_events; + } + + bool skip_this_frame = !handler->get_allow_input(); + + if (m_any_keys_are_down) + { + skip_this_frame = true; + } + + bool any_keys_are_down = false; + + // Check if any modifier keys are down + ModifierKeys modifier_keys{}; + for (auto& [modifier_key, key_is_down] : m_modifier_keys_down) + { + if (GetAsyncKeyState(modifier_key)) + { + modifier_keys |= modifier_key; + key_is_down = true; + } + else + { + key_is_down = false; + } + } + + auto& subscribed_keys = handler->get_subscribed_keys(); + + for (int key = 0; key < max_keys; ++key) + { + if (subscribed_keys[key]) + { + auto keyed = GetAsyncKeyState(key); + if (keyed && !m_key_down[key]) + { + any_keys_are_down = true; + m_key_down[key] = true; + m_input_events.emplace_back(InputEvent{static_cast(key), modifier_keys}); + } + else if (!keyed && m_key_down[key]) + { + m_key_down[key] = false; + } + } + } + + if (any_keys_are_down) + { + m_any_keys_are_down = true; + } + else + { + m_any_keys_are_down = false; + } + + if (skip_this_frame) + { + m_input_events.clear(); + } + return m_input_events; + } + +} // namespace RC::Input diff --git a/deps/first/Input/src/PlatformInit.cpp b/deps/first/Input/src/PlatformInit.cpp new file mode 100644 index 000000000..7d34ac42b --- /dev/null +++ b/deps/first/Input/src/PlatformInit.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +namespace RC::Input +{ + auto Handler::init() -> void + { +#ifdef WIN32 + register_input_source(std::make_shared(L"ConsoleWindowClass", L"UnrealWindow")); +#endif +#ifdef LINUX +#ifdef HAS_TUI + register_input_source(std::make_shared()); +#endif +#ifdef HAS_GLFW + register_input_source(std::make_shared()); +#endif +#endif + } +} // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/xmake.lua b/deps/first/Input/xmake.lua index 02b2c633f..10a5afe1c 100644 --- a/deps/first/Input/xmake.lua +++ b/deps/first/Input/xmake.lua @@ -9,4 +9,27 @@ target(projectName) add_includedirs("include", { public = true }) add_headerfiles("include/**.hpp") - add_files("src/**.cpp") \ No newline at end of file + add_files("src/**.cpp|Platform/**.cpp") + + add_deps("DynamicOutput") + + if is_plat("windows") then + add_files("src/Platform/Win32AsyncInputSource.cpp") + add_files("src/Platform/GLFW3InputSource.cpp") + add_files("src/Platform/QueueInputSource.cpp") + end + + if is_plat("linux") then + add_files("src/Platform/GLFW3InputSource.cpp") + add_files("src/Platform/NcursesInputSource.cpp") + add_files("src/Platform/QueueInputSource.cpp") + end + + on_load(function (target) + import("target_helpers", { rootdir = get_config("scriptsRoot") }) + + print("Project: " .. projectName .. " (STATIC)") + + target:add("defines", target_helpers.project_name_to_exports_define(projectName)) + target:add("defines", target_helpers.project_name_to_build_static_define(projectName)) + end) diff --git a/deps/first/JSON/include/JSON/Array.hpp b/deps/first/JSON/include/JSON/Array.hpp index 979b31aab..e4cd241b8 100644 --- a/deps/first/JSON/include/JSON/Array.hpp +++ b/deps/first/JSON/include/JSON/Array.hpp @@ -41,7 +41,7 @@ namespace RC::JSON public: auto new_object() -> class Object&; auto new_array() -> class Array&; - auto new_string(const StringType& value) -> void; + auto new_string(const SystemStringType& value) -> void; auto new_null() -> void; auto new_bool(bool value) -> void; @@ -87,7 +87,7 @@ namespace RC::JSON } public: - auto serialize(ShouldFormat should_format = ShouldFormat::No, int32_t* indent_level = nullptr) -> StringType override; + auto serialize(ShouldFormat should_format = ShouldFormat::No, int32_t* indent_level = nullptr) -> SystemStringType override; auto get_type() const -> Type override { return Type::Array; diff --git a/deps/first/JSON/include/JSON/Bool.hpp b/deps/first/JSON/include/JSON/Bool.hpp index 31dcfa978..fc599f6cc 100644 --- a/deps/first/JSON/include/JSON/Bool.hpp +++ b/deps/first/JSON/include/JSON/Bool.hpp @@ -23,7 +23,8 @@ namespace RC::JSON } public: - auto serialize([[maybe_unused]] ShouldFormat should_format = ShouldFormat::No, [[maybe_unused]] int32_t* indent_level = nullptr) -> StringType override; + auto serialize([[maybe_unused]] ShouldFormat should_format = ShouldFormat::No, [[maybe_unused]] int32_t* indent_level = nullptr) + -> SystemStringType override; auto get_type() const -> Type override { return Type::Bool; diff --git a/deps/first/JSON/include/JSON/Common.hpp b/deps/first/JSON/include/JSON/Common.hpp index f0aaf684f..99dc44be8 100644 --- a/deps/first/JSON/include/JSON/Common.hpp +++ b/deps/first/JSON/include/JSON/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_JSON_EXPORTS #ifndef RC_JSON_BUILD_STATIC #ifndef RC_JSON_API @@ -16,6 +18,14 @@ #endif #endif +#else + +#ifndef RC_JSON_API +#define RC_JSON_API +#endif + +#endif + #include #include @@ -29,7 +39,7 @@ namespace RC::JSON class ScopeStack; } - auto inline indent(int32_t* indent_level, File::StringType& string) -> void + auto inline indent(int32_t* indent_level, SystemStringType& string) -> void { if (!indent_level) { @@ -38,7 +48,7 @@ namespace RC::JSON for (int32_t i = 0; i < *indent_level; ++i) { - string.append(STR(" ")); + string.append(SYSSTR(" ")); } } } // namespace RC::JSON diff --git a/deps/first/JSON/include/JSON/KeyValuePair.hpp b/deps/first/JSON/include/JSON/KeyValuePair.hpp index 208901e75..8c665ab32 100644 --- a/deps/first/JSON/include/JSON/KeyValuePair.hpp +++ b/deps/first/JSON/include/JSON/KeyValuePair.hpp @@ -5,13 +5,13 @@ namespace RC::JSON template struct TypedKeyValuePair { - const StringType& key; + const SystemStringType& key; T* value{}; }; struct KeyValuePair { - StringType key{}; + SystemStringType key{}; std::unique_ptr value{}; template @@ -126,13 +126,13 @@ type_to_string(get_type()), type_to_string(JSONElementType::static_type()))}; template struct JSONTypedKeyValuePair { - const StringType& key; + const SystemStringType& key; T* value{}; }; struct JSONKeyValuePair { - StringType key{}; + SystemStringType key{}; std::unique_ptr value{}; template @@ -191,7 +191,7 @@ type_to_string(get_type()), type_to_string(JSONElementType::static_type()))}; auto get(StringViewType key) const -> ValueType& { auto value = find_value_by_key(key); - if (!value) { throw std::runtime_error{to_string(std::format(STR("No key in JSON object with name {}"), key))}; } + if (!value) { throw std::runtime_error{to_string(std::format(SYSSTR("No key in JSON object with name {}"), key))}; } return *static_cast(value); } @@ -199,7 +199,7 @@ type_to_string(get_type()), type_to_string(JSONElementType::static_type()))}; auto get(StringViewType key) -> ValueType& { auto value = find_value_by_key(key); - if (!value) { throw std::runtime_error{to_string(std::format(STR("No key in JSON object with name {}"), key))}; } + if (!value) { throw std::runtime_error{to_string(std::format(SYSSTR("No key in JSON object with name {}"), key))}; } return *static_cast(value); } }; @@ -222,16 +222,16 @@ type_to_string(get_type()), type_to_string(JSONElementType::static_type()))}; struct JSONString : JSONElementBase { private: - StringType value{}; + SystemStringType value{}; public: JSONString() = default; - JSONString(StringType value) : value(std::move(value)) { } + JSONString(SystemStringType value) : value(std::move(value)) { } public: constexpr static auto static_type() -> JSONElementType { return JSONElementType::String; } - auto get() -> StringType& { return value; } - auto get() const -> const StringType& { return value; } + auto get() -> SystemStringType& { return value; } + auto get() const -> const SystemStringType& { return value; } auto get_view() -> StringViewType { return value; } auto get_view() const -> StringViewType { return value; } auto get_type() const -> JSONElementType override { return JSONElementType::String; } diff --git a/deps/first/JSON/include/JSON/Null.hpp b/deps/first/JSON/include/JSON/Null.hpp index 17943bf1f..d23353a2e 100644 --- a/deps/first/JSON/include/JSON/Null.hpp +++ b/deps/first/JSON/include/JSON/Null.hpp @@ -13,7 +13,8 @@ namespace RC::JSON ~Null() override = default; public: - auto serialize([[maybe_unused]] ShouldFormat should_format = ShouldFormat::No, [[maybe_unused]] int32_t* indent_level = nullptr) -> StringType override; + auto serialize([[maybe_unused]] ShouldFormat should_format = ShouldFormat::No, [[maybe_unused]] int32_t* indent_level = nullptr) + -> SystemStringType override; auto get_type() const -> Type override { return Type::Null; diff --git a/deps/first/JSON/include/JSON/Number.hpp b/deps/first/JSON/include/JSON/Number.hpp index 897b60e99..59a876d15 100644 --- a/deps/first/JSON/include/JSON/Number.hpp +++ b/deps/first/JSON/include/JSON/Number.hpp @@ -167,7 +167,8 @@ namespace RC::JSON } public: - auto serialize([[maybe_unused]] ShouldFormat should_format = ShouldFormat::No, [[maybe_unused]] int32_t* indent_level = nullptr) -> StringType override; + auto serialize([[maybe_unused]] ShouldFormat should_format = ShouldFormat::No, [[maybe_unused]] int32_t* indent_level = nullptr) + -> SystemStringType override; auto get_type() const -> JSON::Type override { return JSON::Type::Number; diff --git a/deps/first/JSON/include/JSON/Object.hpp b/deps/first/JSON/include/JSON/Object.hpp index f3f5dd609..207225cc9 100644 --- a/deps/first/JSON/include/JSON/Object.hpp +++ b/deps/first/JSON/include/JSON/Object.hpp @@ -30,7 +30,7 @@ namespace RC::JSON #pragma warning(disable : 4251) private: - std::unordered_map> m_members{}; + std::unordered_map> m_members{}; IsGlobalObject m_is_global_object{IsGlobalObject::No}; #pragma warning(default : 4251) @@ -44,17 +44,17 @@ namespace RC::JSON auto operator=(const Object&) -> Object& = delete; private: - auto find_value_by_key(const StringType& key) const -> Value*; - auto find_value_by_key(const StringType& key) -> Value*; + auto find_value_by_key(const SystemStringType& key) const -> Value*; + auto find_value_by_key(const SystemStringType& key) -> Value*; public: - auto new_object(StringType name) -> Object&; - auto new_array(StringType name) -> class Array&; - auto new_string(StringType name, const StringType& value) -> void; - auto new_null(StringType name) -> void; - auto new_bool(StringType name, bool value) -> void; + auto new_object(SystemStringType name) -> Object&; + auto new_array(SystemStringType name) -> class Array&; + auto new_string(SystemStringType name, const SystemStringType& value) -> void; + auto new_null(SystemStringType name) -> void; + auto new_bool(SystemStringType name, bool value) -> void; - auto add_object(StringType name, std::unique_ptr) -> void; + auto add_object(SystemStringType name, std::unique_ptr) -> void; auto get() -> decltype(m_members)& { @@ -66,34 +66,34 @@ namespace RC::JSON }; template - auto get(const StringType& key) const -> ValueType& + auto get(const SystemStringType& key) const -> ValueType& { auto value = find_value_by_key(key); if (!value) { - throw std::runtime_error{to_string(std::format(STR("No key in JSON object with name {}"), key))}; + throw std::runtime_error{to_string(std::format(SYSSTR("No key in JSON object with name {}"), key))}; } return *static_cast(value); } template - auto get(const StringType& key) -> ValueType& + auto get(const SystemStringType& key) -> ValueType& { auto value = find_value_by_key(key); if (!value) { - throw std::runtime_error{to_string(std::format(STR("No key in JSON object with name {}"), key))}; + throw std::runtime_error{to_string(std::format(SYSSTR("No key in JSON object with name {}"), key))}; } return *static_cast(value); } template - auto new_number(StringType name, number_type value) -> void + auto new_number(SystemStringType name, number_type value) -> void { m_members.emplace(std::move(name), std::make_unique(value)); } - auto serialize(ShouldFormat should_format = ShouldFormat::No, int32_t* indent_level = nullptr) -> StringType override; + auto serialize(ShouldFormat should_format = ShouldFormat::No, int32_t* indent_level = nullptr) -> SystemStringType override; auto get_type() const -> Type override { return Type::Object; diff --git a/deps/first/JSON/include/JSON/Parser/Parser.hpp b/deps/first/JSON/include/JSON/Parser/Parser.hpp index a3324df5d..5efa7f690 100644 --- a/deps/first/JSON/include/JSON/Parser/Parser.hpp +++ b/deps/first/JSON/include/JSON/Parser/Parser.hpp @@ -10,6 +10,7 @@ namespace RC::JSON::Parser { - RC_JSON_API auto parse(StringType& input) -> std::unique_ptr; + RC_JSON_API auto parse(File::StringType& input) -> std::unique_ptr; + RC_JSON_API auto parse(UEStringType& input) -> std::unique_ptr; RC_JSON_API auto parse(const File::Handle&) -> std::unique_ptr; } // namespace RC::JSON::Parser diff --git a/deps/first/JSON/include/JSON/Parser/TokenParser.hpp b/deps/first/JSON/include/JSON/Parser/TokenParser.hpp index b28cdc9d4..35e003afa 100644 --- a/deps/first/JSON/include/JSON/Parser/TokenParser.hpp +++ b/deps/first/JSON/include/JSON/Parser/TokenParser.hpp @@ -79,7 +79,7 @@ namespace RC::JSON::Parser } } template - auto add_element_to_top(StringType key, std::unique_ptr value) -> ValueType* + auto add_element_to_top(SystemStringType key, std::unique_ptr value) -> ValueType* { auto& top = peek_top(); if (top.is()) @@ -107,7 +107,7 @@ namespace RC::JSON::Parser private: std::unique_ptr m_global_object; ScopeStack m_scope_stack{}; - StringType m_last_key{}; + SystemStringType m_last_key{}; std::unique_ptr m_last_value{}; State m_current_state{State::StartOfFile}; TokenType m_expected_token{}; @@ -115,7 +115,7 @@ namespace RC::JSON::Parser bool m_defer_element_creation{}; public: - TokenParser(const ParserBase::Tokenizer& tokenizer, File::StringType& input) : ParserBase::TokenParser(tokenizer, input) + TokenParser(const ParserBase::Tokenizer& tokenizer, SystemStringType& input) : ParserBase::TokenParser(tokenizer, input) { } virtual ~TokenParser() = default; diff --git a/deps/first/JSON/include/JSON/String.hpp b/deps/first/JSON/include/JSON/String.hpp index c3edc1fd9..f62602ddf 100644 --- a/deps/first/JSON/include/JSON/String.hpp +++ b/deps/first/JSON/include/JSON/String.hpp @@ -14,25 +14,25 @@ namespace RC::JSON RC_JSON_API constexpr static Type static_type = Type::String; private: - StringType m_data{}; + SystemStringType m_data{}; public: RC_JSON_API String() = default; - RC_JSON_API explicit String(StringViewType string); + RC_JSON_API explicit String(SystemStringViewType string); RC_JSON_API ~String() override = default; public: - RC_JSON_API auto get() -> StringType& + RC_JSON_API auto get() -> SystemStringType& { return m_data; } - RC_JSON_API auto get_view() const -> StringViewType + RC_JSON_API auto get_view() const -> SystemStringViewType { return m_data; } public: - RC_JSON_API auto serialize(ShouldFormat should_format = ShouldFormat::No, int32_t* indent_level = nullptr) -> StringType override; + RC_JSON_API auto serialize(ShouldFormat should_format = ShouldFormat::No, int32_t* indent_level = nullptr) -> SystemStringType override; RC_JSON_API auto get_type() const -> Type override { return Type::String; diff --git a/deps/first/JSON/include/JSON/Value.hpp b/deps/first/JSON/include/JSON/Value.hpp index 5914d163f..e7fa78b18 100644 --- a/deps/first/JSON/include/JSON/Value.hpp +++ b/deps/first/JSON/include/JSON/Value.hpp @@ -50,7 +50,7 @@ namespace RC::JSON virtual ~Value() = default; public: - virtual auto serialize(ShouldFormat, int32_t* indent_level) -> StringType = 0; + virtual auto serialize(ShouldFormat, int32_t* indent_level) -> SystemStringType = 0; virtual auto get_type() const -> Type = 0; public: diff --git a/deps/first/JSON/src/Array.cpp b/deps/first/JSON/src/Array.cpp index 0f02e46f9..413d103f8 100644 --- a/deps/first/JSON/src/Array.cpp +++ b/deps/first/JSON/src/Array.cpp @@ -16,7 +16,7 @@ namespace RC::JSON return static_cast(*m_members.emplace_back(std::make_unique())); } - auto Array::new_string(const StringType& value) -> void + auto Array::new_string(const SystemStringType& value) -> void { m_members.emplace_back(std::make_unique(value)); } @@ -36,22 +36,22 @@ namespace RC::JSON m_members.emplace_back(std::move(object)); } - auto Array::serialize(ShouldFormat should_format, int32_t* indent_level) -> StringType + auto Array::serialize(ShouldFormat should_format, int32_t* indent_level) -> SystemStringType { if (!indent_level) { throw std::runtime_error{"Must supply an indent_level pointer"}; }; - StringType array_as_string{}; + SystemStringType array_as_string{}; if (should_format == ShouldFormat::Yes && !m_members.empty()) { - array_as_string.append(STR("[\n")); + array_as_string.append(SYSSTR("[\n")); } else { - array_as_string.append(STR("[")); + array_as_string.append(SYSSTR("[")); } ++(*indent_level); @@ -70,11 +70,11 @@ namespace RC::JSON { if (should_format == ShouldFormat::Yes) { - array_as_string.append(STR(",\n")); + array_as_string.append(SYSSTR(",\n")); } else { - array_as_string.append(STR(",")); + array_as_string.append(SYSSTR(",")); } } @@ -85,13 +85,13 @@ namespace RC::JSON if (should_format == ShouldFormat::Yes && !m_members.empty()) { - array_as_string.append(STR("\n")); + array_as_string.append(SYSSTR("\n")); indent(indent_level, array_as_string); - array_as_string.append(STR("]")); + array_as_string.append(SYSSTR("]")); } else { - array_as_string.append(STR("]")); + array_as_string.append(SYSSTR("]")); } if (!m_members.empty()) diff --git a/deps/first/JSON/src/Bool.cpp b/deps/first/JSON/src/Bool.cpp index 79ff91871..62b894131 100644 --- a/deps/first/JSON/src/Bool.cpp +++ b/deps/first/JSON/src/Bool.cpp @@ -6,10 +6,10 @@ namespace RC::JSON { } - auto Bool::serialize(ShouldFormat should_format, int32_t* indent_level) -> StringType + auto Bool::serialize(ShouldFormat should_format, int32_t* indent_level) -> SystemStringType { (void)should_format; (void)indent_level; - return m_underlying_value ? STR("true") : STR("false"); + return m_underlying_value ? SYSSTR("true") : SYSSTR("false"); } } // namespace RC::JSON diff --git a/deps/first/JSON/src/Null.cpp b/deps/first/JSON/src/Null.cpp index 867dd612c..0cbda75f9 100644 --- a/deps/first/JSON/src/Null.cpp +++ b/deps/first/JSON/src/Null.cpp @@ -2,10 +2,10 @@ namespace RC::JSON { - auto Null::serialize(ShouldFormat should_format, int32_t* indent_level) -> StringType + auto Null::serialize(ShouldFormat should_format, int32_t* indent_level) -> SystemStringType { (void)should_format; (void)indent_level; - return STR("null"); + return SYSSTR("null"); } } // namespace RC::JSON diff --git a/deps/first/JSON/src/Number.cpp b/deps/first/JSON/src/Number.cpp index 4628a8948..eb97ade3b 100644 --- a/deps/first/JSON/src/Number.cpp +++ b/deps/first/JSON/src/Number.cpp @@ -42,7 +42,7 @@ namespace RC::JSON m_stored_type = Type::Double; } - auto Number::serialize(ShouldFormat should_format, int32_t* indent_level) -> StringType + auto Number::serialize(ShouldFormat should_format, int32_t* indent_level) -> SystemStringType { (void)should_format; (void)indent_level; diff --git a/deps/first/JSON/src/Object.cpp b/deps/first/JSON/src/Object.cpp index e4c1c265b..172ed07cd 100644 --- a/deps/first/JSON/src/Object.cpp +++ b/deps/first/JSON/src/Object.cpp @@ -6,7 +6,7 @@ namespace RC::JSON { - auto Object::find_value_by_key(const StringType& look_for_key) const -> Value* + auto Object::find_value_by_key(const SystemStringType& look_for_key) const -> Value* { if (auto it = m_members.find(look_for_key); it != m_members.end()) { @@ -17,7 +17,7 @@ namespace RC::JSON return nullptr; } } - auto Object::find_value_by_key(const StringType& look_for_key) -> Value* + auto Object::find_value_by_key(const SystemStringType& look_for_key) -> Value* { if (auto it = m_members.find(look_for_key); it != m_members.end()) { @@ -29,52 +29,52 @@ namespace RC::JSON } } - auto Object::new_object(StringType name) -> Object& + auto Object::new_object(SystemStringType name) -> Object& { return *static_cast(m_members.emplace(std::move(name), std::make_unique()).first->second.get()); } - auto Object::new_array(StringType name) -> Array& + auto Object::new_array(SystemStringType name) -> Array& { return *static_cast(m_members.emplace(std::move(name), std::make_unique()).first->second.get()); } - auto Object::new_string(StringType name, const StringType& value) -> void + auto Object::new_string(SystemStringType name, const SystemStringType& value) -> void { m_members.emplace(std::move(name), std::make_unique(value)); } - auto Object::new_null(StringType name) -> void + auto Object::new_null(SystemStringType name) -> void { m_members.emplace(std::move(name), std::make_unique()); } - auto Object::new_bool(StringType name, bool value) -> void + auto Object::new_bool(SystemStringType name, bool value) -> void { m_members.emplace(std::move(name), std::make_unique(value)); } - auto Object::add_object(StringType name, std::unique_ptr object) -> void + auto Object::add_object(SystemStringType name, std::unique_ptr object) -> void { m_members.emplace(std::move(name), std::move(object)); } - auto Object::serialize(ShouldFormat should_format, int32_t* indent_level) -> StringType + auto Object::serialize(ShouldFormat should_format, int32_t* indent_level) -> SystemStringType { if (!indent_level) { throw std::runtime_error{"Must supply an indent_level pointer"}; }; - StringType object_as_string{}; + SystemStringType object_as_string{}; if (should_format == ShouldFormat::Yes && !m_members.empty()) { - object_as_string.append(STR("{\n")); + object_as_string.append(SYSSTR("{\n")); } else { - object_as_string.append(STR("{")); + object_as_string.append(SYSSTR("{")); } ++(*indent_level); @@ -87,18 +87,18 @@ namespace RC::JSON indent(indent_level, object_as_string); } - object_as_string.append(std::format(STR("\"{}\":"), key)); + object_as_string.append(std::format(SYSSTR("\"{}\":"), key)); object_as_string.append(value->serialize(should_format, indent_level)); if (member_count + 1 < m_members.size()) { if (should_format == ShouldFormat::Yes) { - object_as_string.append(STR(",\n")); + object_as_string.append(SYSSTR(",\n")); } else { - object_as_string.append(STR(",")); + object_as_string.append(SYSSTR(",")); } } @@ -109,13 +109,13 @@ namespace RC::JSON if (should_format == ShouldFormat::Yes && !m_members.empty()) { - object_as_string.append(STR("\n")); + object_as_string.append(SYSSTR("\n")); indent(indent_level, object_as_string); - object_as_string.append(STR("}")); + object_as_string.append(SYSSTR("}")); } else { - object_as_string.append(STR("}")); + object_as_string.append(SYSSTR("}")); } if (!m_members.empty() || m_is_global_object == IsGlobalObject::Yes) diff --git a/deps/first/JSON/src/Parser/Parser.cpp b/deps/first/JSON/src/Parser/Parser.cpp index 4dbc3beff..96ba288ac 100644 --- a/deps/first/JSON/src/Parser/Parser.cpp +++ b/deps/first/JSON/src/Parser/Parser.cpp @@ -11,28 +11,28 @@ namespace RC::JSON::Parser { ParserBase::TokenContainer tc; - tc.add(ParserBase::Token::create(TokenType::CarriageReturn, STR("CarriageReturn"), STR("\r"))); - tc.add(ParserBase::Token::create(TokenType::NewLine, STR("NewLine"), STR("\n"))); - tc.add(ParserBase::Token::create(TokenType::DoubleQuote, STR("DoubleQuote"), STR("\""))); + tc.add(ParserBase::Token::create(TokenType::CarriageReturn, SYSSTR("CarriageReturn"), SYSSTR("\r"))); + tc.add(ParserBase::Token::create(TokenType::NewLine, SYSSTR("NewLine"), SYSSTR("\n"))); + tc.add(ParserBase::Token::create(TokenType::DoubleQuote, SYSSTR("DoubleQuote"), SYSSTR("\""))); tc.add(ParserBase::Token::create(TokenType::Characters, - STR("Characters"), - STR(""), + SYSSTR("Characters"), + SYSSTR(""), ParserBase::Token::HasData::Yes)); // Empty identifier will match everything that no other token identifier matches - tc.add(ParserBase::Token::create(TokenType::ClosingCurlyBrace, STR("ClosingCurlyBrace"), STR("}"))); - tc.add(ParserBase::Token::create(TokenType::OpeningCurlyBrace, STR("OpeningCurlyBrace"), STR("{"))); - tc.add(ParserBase::Token::create(TokenType::ClosingSquareBracket, STR("ClosingSquareBracket"), STR("]"))); - tc.add(ParserBase::Token::create(TokenType::OpeningSquareBracket, STR("OpeningSquareBracket"), STR("["))); - tc.add(ParserBase::Token::create(TokenType::Comma, STR("Comma"), STR(","))); - tc.add(ParserBase::Token::create(TokenType::Colon, STR("Colon"), STR(":"))); - tc.add(ParserBase::Token::create(TokenType::True, STR("True"), STR("true"))); - tc.add(ParserBase::Token::create(TokenType::False, STR("False"), STR("false"))); + tc.add(ParserBase::Token::create(TokenType::ClosingCurlyBrace, SYSSTR("ClosingCurlyBrace"), SYSSTR("}"))); + tc.add(ParserBase::Token::create(TokenType::OpeningCurlyBrace, SYSSTR("OpeningCurlyBrace"), SYSSTR("{"))); + tc.add(ParserBase::Token::create(TokenType::ClosingSquareBracket, SYSSTR("ClosingSquareBracket"), SYSSTR("]"))); + tc.add(ParserBase::Token::create(TokenType::OpeningSquareBracket, SYSSTR("OpeningSquareBracket"), SYSSTR("["))); + tc.add(ParserBase::Token::create(TokenType::Comma, SYSSTR("Comma"), SYSSTR(","))); + tc.add(ParserBase::Token::create(TokenType::Colon, SYSSTR("Colon"), SYSSTR(":"))); + tc.add(ParserBase::Token::create(TokenType::True, SYSSTR("True"), SYSSTR("true"))); + tc.add(ParserBase::Token::create(TokenType::False, SYSSTR("False"), SYSSTR("false"))); tc.set_eof_token(TokenType::EndOfFile); return tc; } - static auto parse_internal(File::StringType& input) -> std::unique_ptr + static auto parse_internal(SystemStringType& input) -> std::unique_ptr { // Tokenize -> START ParserBase::Tokenizer tokenizer; @@ -56,12 +56,19 @@ namespace RC::JSON::Parser auto parse(File::StringType& input) -> std::unique_ptr { - return Internal::parse_internal(input); + auto sys_input = to_system(input); + return Internal::parse_internal(sys_input); + } + + auto parse(UEStringType& input) -> std::unique_ptr + { + auto sys_input = to_system(input); + return Internal::parse_internal(sys_input); } auto parse(const File::Handle& file) -> std::unique_ptr { - auto input = file.read_all(); + auto input = to_system_string(file.read_file_all()); return Internal::parse_internal(input); } } // namespace RC::JSON::Parser diff --git a/deps/first/JSON/src/Parser/TokenParser.cpp b/deps/first/JSON/src/Parser/TokenParser.cpp index df0b83912..0d3728175 100644 --- a/deps/first/JSON/src/Parser/TokenParser.cpp +++ b/deps/first/JSON/src/Parser/TokenParser.cpp @@ -32,17 +32,10 @@ namespace RC::JSON::Parser throw std::runtime_error{e}; \ } - static auto is_number(StringViewType data) -> bool + static auto is_number(SystemStringViewType data) -> bool { - return std::ranges::all_of(data.begin(), data.end(), [&](const CharType c) { - if constexpr (std::is_same_v) - { - return std::iswdigit(c) != 0; - } - else - { - return std::isdigit(c) != 0; - } + return std::ranges::all_of(data.begin(), data.end(), [&](const SystemCharType c) { + return std::iswdigit((wchar_t)c) != 0; }); } @@ -71,10 +64,10 @@ namespace RC::JSON::Parser return m_storage.back(); } - static auto has_only_spaces(const File::StringType& data) -> bool + static auto has_only_spaces(const SystemStringType& data) -> bool { - return std::all_of(data.begin(), data.end(), [](File::CharType c) { - return std::isspace(c) || c == '\n'; + return std::all_of(data.begin(), data.end(), [](SystemCharType c) { + return std::iswspace(c) || c == SYSSTR('\n'); }); } @@ -171,7 +164,7 @@ namespace RC::JSON::Parser else if (m_current_state == State::ReadValue) { auto data_raw = get_data(token); - StringType data_no_spaces = data_raw; + SystemStringType data_no_spaces = data_raw; data_no_spaces.erase(std::remove_if(data_no_spaces.begin(), data_no_spaces.end(), [](wchar_t c) { @@ -185,7 +178,7 @@ namespace RC::JSON::Parser { do_comma_verification(); - m_last_value = std::make_unique(std::stoll(data_no_spaces, nullptr)); + m_last_value = std::make_unique((uint64_t)std::stoll(data_no_spaces, nullptr)); } else if (!m_string_started) { diff --git a/deps/first/JSON/src/String.cpp b/deps/first/JSON/src/String.cpp index 5001c7c2c..6a89d1165 100644 --- a/deps/first/JSON/src/String.cpp +++ b/deps/first/JSON/src/String.cpp @@ -2,12 +2,12 @@ namespace RC::JSON { - String::String(StringViewType string) : m_data(string) + String::String(SystemStringViewType string) : m_data(string) { } - auto String::serialize([[maybe_unused]] ShouldFormat should_format, [[maybe_unused]] int32_t* indent_level) -> StringType + auto String::serialize([[maybe_unused]] ShouldFormat should_format, [[maybe_unused]] int32_t* indent_level) -> SystemStringType { - return std::format(STR("\"{}\""), m_data); + return std::format(SYSSTR("\"{}\""), m_data); } } // namespace RC::JSON diff --git a/deps/first/LuaMadeSimple/include/LuaMadeSimple/Common.hpp b/deps/first/LuaMadeSimple/include/LuaMadeSimple/Common.hpp index 466233e24..59a7e1284 100644 --- a/deps/first/LuaMadeSimple/include/LuaMadeSimple/Common.hpp +++ b/deps/first/LuaMadeSimple/include/LuaMadeSimple/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_LUA_MADE_SIMPLE_EXPORTS #ifndef RC_LUA_MADE_SIMPLE_BUILD_STATIC #ifndef RC_LMS_API @@ -15,3 +17,11 @@ #define RC_LMS_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_LMS_API +#define RC_LMS_API +#endif + +#endif \ No newline at end of file diff --git a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp index a4ca10f2f..04ddf3f94 100644 --- a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp +++ b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include @@ -26,10 +29,10 @@ namespace RC::LuaMadeSimple /** * @tparam StackIndex Valid values are -1 for table key and -2 for table value */ - template + template struct RC_LMS_API LuaTableData { - const class Lua* lua{}; + const LuaT* lua{}; [[nodiscard]] auto is_nil() -> bool { @@ -85,12 +88,14 @@ namespace RC::LuaMadeSimple /** * Helper for dealing with Lua tables passed from Lua to C++ */ + template struct RC_LMS_API LuaTableReference { - LuaTableData<-1> key; - LuaTableData<-2> value; + LuaTableData<-1, LuaT> key; + LuaTableData<-2, LuaT> value; }; + class Lua; using PostFunctionProcessCallback = void (*)(const Lua&); /** @@ -292,7 +297,7 @@ namespace RC::LuaMadeSimple { lua_pushboolean(get_lua_instance().get_lua_state(), value); } - else if constexpr (std::is_same_v || std::is_same_v) + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { lua_pushinteger(get_lua_instance().get_lua_state(), value); } @@ -571,7 +576,7 @@ namespace RC::LuaMadeSimple [[nodiscard]] RC_LMS_API auto is_table(int32_t force_index = 1) const -> bool; [[nodiscard]] RC_LMS_API auto get_table() const -> Table; - using ForEachInTableCallable = std::function; + using ForEachInTableCallable = std::function)>; // If you use a lambda then make absolutely sure to capture the 'LuaMadeSimple::Lua' object by reference // If you don't then it will be improperly copied and things, including nested for_each calls, will break diff --git a/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp b/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp index 411ddfe7d..35cfa56b6 100644 --- a/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp +++ b/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp @@ -662,7 +662,7 @@ namespace RC::LuaMadeSimple // It's up to the 'callable' to fetch the key/value from the stack // This makes dealing with different types easier // TODO: Make it so you don't need to pass the 'this' pointer to both the key & value struct - LuaTableReference table{.key = {this}, .value = {this}}; + LuaTableReference table{.key = {this}, .value = {this}}; if (callable(table)) { lua_pop(get_lua_state(), 3); diff --git a/deps/first/LuaRaw/src/luauser.c b/deps/first/LuaRaw/src/luauser.c index 889de0178..ad134b81e 100644 --- a/deps/first/LuaRaw/src/luauser.c +++ b/deps/first/LuaRaw/src/luauser.c @@ -1,3 +1,4 @@ +#ifdef WIN32 #include #include "lua.h" @@ -37,4 +38,36 @@ void LuaUnlock(lua_State* L) { /* Release control of mutex */ LeaveCriticalSection(&Gl.LockSct); -} \ No newline at end of file +} +#else +#include +#include +#include "lua.h" + +static struct { + pthread_mutex_t mutex; + bool init; +} lua_lock_mutex; + +void LuaLock(lua_State * L){ + pthread_mutex_lock(&lua_lock_mutex.mutex); +} + +void LuaUnlock(lua_State * L){ + pthread_mutex_unlock(&lua_lock_mutex.mutex); +} + +void LuaLockInitial(lua_State * L){ + if (!lua_lock_mutex.init){ + pthread_mutex_init(&lua_lock_mutex.mutex, NULL); + lua_lock_mutex.init = true; + } +} + +void LuaLockFinal(lua_State * L){ + if (lua_lock_mutex.init) { + LuaUnlock(L); + } +} + +#endif \ No newline at end of file diff --git a/deps/first/MProgram/include/ErrorObject.hpp b/deps/first/MProgram/include/ErrorObject.hpp index 8e8702956..63bd1e670 100644 --- a/deps/first/MProgram/include/ErrorObject.hpp +++ b/deps/first/MProgram/include/ErrorObject.hpp @@ -1,6 +1,12 @@ #pragma once #include +#include + +#ifdef LINUX +#define sprintf_s snprintf +#define strncpy_s(dest, destsz, src, count) strncpy((dest), (src), (((count) < (destsz)) ? (count) : (destsz))) +#endif namespace RC { @@ -49,7 +55,7 @@ namespace RC // The default message will be used which can't be too small since it's calculated at compile-time if (msg_len < sizeof(m_message)) { - strncpy_s(m_message, message, msg_len); + strncpy_s(m_message, msg_len, message, msg_len); } } diff --git a/deps/first/ParserBase/include/ParserBase/Common.hpp b/deps/first/ParserBase/include/ParserBase/Common.hpp index fe00aac44..42daab5ea 100644 --- a/deps/first/ParserBase/include/ParserBase/Common.hpp +++ b/deps/first/ParserBase/include/ParserBase/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_PARSER_BASE_EXPORTS #ifndef RC_PARSER_BASE_BUILD_STATIC #ifndef RC_PB_API @@ -15,3 +17,11 @@ #define RC_PB_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_PB_API +#define RC_PB_API +#endif + +#endif \ No newline at end of file diff --git a/deps/first/ParserBase/include/ParserBase/Token.hpp b/deps/first/ParserBase/include/ParserBase/Token.hpp index 35c12ccd8..39c31c25b 100644 --- a/deps/first/ParserBase/include/ParserBase/Token.hpp +++ b/deps/first/ParserBase/include/ParserBase/Token.hpp @@ -12,20 +12,20 @@ namespace RC::ParserBase class TokenRule { private: - File::StringType m_debug_name; + SystemStringType m_debug_name; public: - explicit TokenRule(File::StringViewType rule_name) : m_debug_name(rule_name) + explicit TokenRule(SystemStringViewType rule_name) : m_debug_name(rule_name) { } virtual ~TokenRule() = default; public: - RC_PB_API virtual auto exec(const class Token& token, const File::CharType* start_of_token, size_t current_cursor_location, class Tokenizer&) -> int = 0; + RC_PB_API virtual auto exec(const class Token& token, const SystemCharType* start_of_token, size_t current_cursor_location, class Tokenizer&) -> int = 0; - [[nodiscard]] RC_PB_API virtual auto to_string() const -> File::StringType + [[nodiscard]] RC_PB_API virtual auto to_string() const -> SystemStringType { - return STR("BaseTokenRule (invalid)"); + return SYSSTR("BaseTokenRule (invalid)"); } }; @@ -42,8 +42,8 @@ namespace RC::ParserBase }; private: - File::StringType m_debug_name; - File::StringType m_identifier; + SystemStringType m_debug_name; + SystemStringType m_identifier; std::vector> m_rules; int m_type; // To be cast to an enum before use. This is to avoid using a template which forces everything to be in the header file. mutable size_t m_start{}; @@ -53,7 +53,7 @@ namespace RC::ParserBase HasData m_has_data; public: - RC_PB_API Token(int type, File::StringViewType name, File::StringViewType identifier, HasData has_data = HasData::No); + RC_PB_API Token(int type, SystemStringViewType name, SystemStringViewType identifier, HasData has_data = HasData::No); public: RC_PB_API auto get_type() const -> int; @@ -63,7 +63,7 @@ namespace RC::ParserBase RC_PB_API auto get_start() const -> size_t; RC_PB_API auto set_end(size_t) -> void; RC_PB_API auto get_end() const -> size_t; - RC_PB_API auto get_identifier() const -> File::StringViewType; + RC_PB_API auto get_identifier() const -> SystemStringViewType; RC_PB_API auto get_line() const -> size_t; RC_PB_API auto get_column() const -> size_t; @@ -74,7 +74,7 @@ namespace RC::ParserBase } RC_PB_API auto get_rules() const -> const std::vector>&; - [[nodiscard]] RC_PB_API auto to_string() const -> File::StringType; + [[nodiscard]] RC_PB_API auto to_string() const -> SystemStringType; public: template @@ -92,10 +92,10 @@ namespace RC::ParserBase create_internal(token); } - RC_PB_API auto static create(int type, File::StringViewType name, File::StringViewType identifier, HasData = HasData::No) -> Token; + RC_PB_API auto static create(int type, SystemStringViewType name, SystemStringViewType identifier, HasData = HasData::No) -> Token; template - auto static create(int type, File::StringViewType name, File::StringViewType identifier, HasData has_data = HasData::No) -> Token + auto static create(int type, SystemStringViewType name, SystemStringViewType identifier, HasData has_data = HasData::No) -> Token { Token token{type, name, identifier, has_data}; create_internal(token); @@ -103,7 +103,7 @@ namespace RC::ParserBase } template - auto static create(int type, File::StringViewType name, File::StringViewType identifier, HasData has_data = HasData::No) -> Token + auto static create(int type, SystemStringViewType name, SystemStringViewType identifier, HasData has_data = HasData::No) -> Token { Token token{type, name, identifier, has_data}; create_internal(token); diff --git a/deps/first/ParserBase/include/ParserBase/TokenParser.hpp b/deps/first/ParserBase/include/ParserBase/TokenParser.hpp index 708d4a743..2bd3a306b 100644 --- a/deps/first/ParserBase/include/ParserBase/TokenParser.hpp +++ b/deps/first/ParserBase/include/ParserBase/TokenParser.hpp @@ -34,7 +34,7 @@ namespace RC::ParserBase private: const class Tokenizer& m_tokenizer; - mutable File::StringType m_data; + mutable SystemStringType m_data; protected: mutable size_t m_current_token_index_being_parsed{0}; @@ -42,7 +42,7 @@ namespace RC::ParserBase public: // Investigate whether I want to std::move the input here - RC_PB_API TokenParser(const class Tokenizer&, File::StringType& input); + RC_PB_API TokenParser(const class Tokenizer&, SystemStringType& input); RC_PB_API virtual ~TokenParser() = default; protected: @@ -61,7 +61,7 @@ namespace RC::ParserBase protected: RC_PB_API auto get_token(size_t index) const -> const class Token&; - RC_PB_API auto get_data(const class Token&) const -> const File::StringType; + RC_PB_API auto get_data(const class Token&) const -> const SystemStringType; // auto find_token_with_data(int token_type, File::StringViewType data) const -> std::optional>; diff --git a/deps/first/ParserBase/include/ParserBase/Tokenizer.hpp b/deps/first/ParserBase/include/ParserBase/Tokenizer.hpp index 1ea87a5f7..8812f1ddb 100644 --- a/deps/first/ParserBase/include/ParserBase/Tokenizer.hpp +++ b/deps/first/ParserBase/include/ParserBase/Tokenizer.hpp @@ -43,7 +43,7 @@ namespace RC::ParserBase public: RC_PB_API auto set_available_tokens(TokenContainer&&) -> void; // TODO: Maybe the constructor should take the input instead of 'tokenize' - RC_PB_API auto tokenize(const File::StringType& input) -> void; + RC_PB_API auto tokenize(const SystemStringType& input) -> void; [[nodiscard]] RC_PB_API auto get_tokens() const -> const std::vector&; [[nodiscard]] RC_PB_API auto get_last_token() const -> const Token&; }; diff --git a/deps/first/ParserBase/src/Token.cpp b/deps/first/ParserBase/src/Token.cpp index 1a11b8d12..386816ec8 100644 --- a/deps/first/ParserBase/src/Token.cpp +++ b/deps/first/ParserBase/src/Token.cpp @@ -2,7 +2,7 @@ namespace RC::ParserBase { - Token::Token(int type, File::StringViewType name, File::StringViewType identifier, Token::HasData has_data) + Token::Token(int type, SystemStringViewType name, SystemStringViewType identifier, Token::HasData has_data) : m_debug_name(name), m_identifier(identifier), m_type(type), m_has_data(has_data) { } @@ -42,7 +42,7 @@ namespace RC::ParserBase return m_end; } - auto Token::get_identifier() const -> File::StringViewType + auto Token::get_identifier() const -> SystemStringViewType { return m_identifier; } @@ -62,12 +62,12 @@ namespace RC::ParserBase return m_rules; } - auto Token::to_string() const -> File::StringType + auto Token::to_string() const -> SystemStringType { return m_debug_name; } - auto Token::create(int type, File::StringViewType name, File::StringViewType identifier, HasData has_data) -> Token + auto Token::create(int type, SystemStringViewType name, SystemStringViewType identifier, HasData has_data) -> Token { Token token{type, name, identifier, has_data}; return token; diff --git a/deps/first/ParserBase/src/TokenParser.cpp b/deps/first/ParserBase/src/TokenParser.cpp index 5e75b9d19..750ca15eb 100644 --- a/deps/first/ParserBase/src/TokenParser.cpp +++ b/deps/first/ParserBase/src/TokenParser.cpp @@ -2,9 +2,11 @@ #include #include +#include + namespace RC::ParserBase { - TokenParser::TokenParser(const Tokenizer& tokenizer, File::StringType& input) : m_tokenizer(tokenizer), m_data(input) + TokenParser::TokenParser(const Tokenizer& tokenizer, SystemStringType& input) : m_tokenizer(tokenizer), m_data(input) { } @@ -13,7 +15,7 @@ namespace RC::ParserBase return m_tokenizer.get_tokens()[index]; } - auto TokenParser::get_data(const Token& token) const -> const File::StringType + auto TokenParser::get_data(const Token& token) const -> const SystemStringType { if (!token.has_data()) { diff --git a/deps/first/ParserBase/src/Tokenizer.cpp b/deps/first/ParserBase/src/Tokenizer.cpp index 40e57bfec..68630bbf1 100644 --- a/deps/first/ParserBase/src/Tokenizer.cpp +++ b/deps/first/ParserBase/src/Tokenizer.cpp @@ -41,7 +41,7 @@ namespace RC::ParserBase m_token_container = std::move(token_container); } - auto Tokenizer::tokenize(const File::StringType& input) -> void + auto Tokenizer::tokenize(const SystemStringType& input) -> void { // printf_s("Tokenizer::tokenize()\n\n"); @@ -75,12 +75,12 @@ namespace RC::ParserBase throw std::runtime_error{"[Tokenizer::tokenize] Input was empty"}; } - File::StringType a; + SystemStringType a; - const File::CharType* input_array = input.c_str(); + const SystemCharType* input_array = input.c_str(); size_t global_cursor{}; - auto peek = [&](File::StringType& out_str, const File::CharType* character, size_t num_chars) -> void { + auto peek = [&](SystemStringType& out_str, const SystemCharType* character, size_t num_chars) -> void { if (global_cursor + num_chars <= input.size()) { for (size_t i = 0; i < num_chars; ++i) @@ -97,12 +97,12 @@ namespace RC::ParserBase bool matched_anything{false}; }; - File::StringType current_empty_token{}; - File::StringType empty_token_data{}; + SystemStringType current_empty_token{}; + SystemStringType empty_token_data{}; TokenFoundWrapper empty_token{}; size_t start_of_empty_token{}; bool start_of_empty_token_set{}; - const File::CharType* c = input_array; + const SystemCharType* c = input_array; auto deal_with_possible_empty_token = [&]() { if (empty_token.token) @@ -137,8 +137,8 @@ namespace RC::ParserBase int advance_cursor_by{-1}; bool all_rules_obeyed{true}; - File::StringViewType identifier_to_find = token.get_identifier(); - File::StringType compare_to; + SystemStringViewType identifier_to_find = token.get_identifier(); + SystemStringType compare_to; size_t identifier_size = identifier_to_find.size(); peek(compare_to, c, identifier_size); bool identifier_should_match_all = identifier_to_find.empty(); @@ -213,7 +213,7 @@ namespace RC::ParserBase // This is required because the loop above stops at end-of-file without processing it deal_with_possible_empty_token(); - m_tokens_in_input.emplace_back(Token{m_token_container.m_eof_token_type, STR("EndOfFile"), STR("--EOF-DO-NOT-PARSE--")}); + m_tokens_in_input.emplace_back(Token{m_token_container.m_eof_token_type, SYSSTR("EndOfFile"), SYSSTR("--EOF-DO-NOT-PARSE--")}); } auto Tokenizer::get_tokens() const -> const std::vector& diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/Common.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/Common.hpp index c06dffd05..08b03f765 100644 --- a/deps/first/SinglePassSigScanner/include/SigScanner/Common.hpp +++ b/deps/first/SinglePassSigScanner/include/SigScanner/Common.hpp @@ -1,5 +1,7 @@ #pragma once +#ifdef WIN32 + #ifndef RC_SINGLE_PASS_SIG_SCANNER_EXPORTS #ifndef RC_SINGLE_PASS_SIG_SCANNER_BUILD_STATIC #ifndef RC_SPSS_API @@ -15,3 +17,11 @@ #define RC_SPSS_API __declspec(dllexport) #endif #endif + +#else + +#ifndef RC_SPSS_API +#define RC_SPSS_API +#endif + +#endif diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp index 9cd281a16..93b07951b 100644 --- a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp +++ b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScanner.hpp @@ -7,27 +7,17 @@ #include +#ifdef WIN32 +#include "SinglePassSigScannerWin32.hpp" +#else +#include "SinglePassSigScannerLinux.hpp" +#endif + #define HI_NIBBLE(b) (((b) >> 4) & 0x0F) #define LO_NIBBLE(b) ((b)&0x0F) -// Windows.h forward declarations -struct _SYSTEM_INFO; -typedef _SYSTEM_INFO SYSTEM_INFO; -struct _MODULEINFO; -typedef _MODULEINFO MODULEINFO; - namespace RC { - // Windows structs, to prevent the need to include Windows.h in this header - struct WIN_MODULEINFO - { - void* lpBaseOfDll; - unsigned long SizeOfImage; - void* EntryPoint; - - RC_SPSS_API auto operator=(MODULEINFO) -> WIN_MODULEINFO&; - }; - enum class ScanTarget { MainExe, @@ -175,9 +165,9 @@ namespace RC class RC_SPSS_API ScanTargetArray { public: - std::array(ScanTarget::Max)> array{}; + std::array(ScanTarget::Max)> array{}; - auto operator[](ScanTarget index) -> MODULEINFO&; + auto operator[](ScanTarget index) -> ModuleOS&; }; // Static storage to be used across all sig scanner types @@ -322,22 +312,38 @@ namespace RC RC_SPSS_API auto static format_aob_strings(std::vector& signature_containers) -> void; public: - RC_SPSS_API auto static scanner_work_thread(uint8_t* start_address, - uint8_t* end_address, - SYSTEM_INFO& info, - std::vector& signature_containers) -> void; + RC_SPSS_API auto static scanner_work_thread(uint8_t* start_address, uint8_t* end_address, SystemInfo* info, std::vector& signature_containers) + -> void; RC_SPSS_API auto static scanner_work_thread_scalar(uint8_t* start_address, uint8_t* end_address, - SYSTEM_INFO& info, + SystemInfo* info, std::vector& signature_containers) -> void; RC_SPSS_API auto static scanner_work_thread_stdfind(uint8_t* start_address, uint8_t* end_address, - SYSTEM_INFO& info, + SystemInfo* info, std::vector& signature_containers) -> void; using SignatureContainerMap = std::unordered_map>; RC_SPSS_API auto static start_scan(SignatureContainerMap& signature_containers) -> void; - RC_SPSS_API auto static string_scan(std::wstring_view string_to_scan_for, ScanTarget = ScanTarget::MainExe) -> void*; }; -} // namespace RC + + namespace Platform + { + // Get the system info + auto get_system_info() -> SystemInfo*; + + // Get the start address of the system + auto get_start_address(SystemInfo* info) -> uint8_t*; + + // Get the end address of the system + auto get_end_address(SystemInfo* info) -> uint8_t*; + + // Get the size of the module + auto get_module_size(ModuleOS* info) -> uint32_t; + + // Get the base address of the module + auto get_module_base(ModuleOS* info) -> uint8_t*; + }; // namespace Platform + +}; // namespace RC diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp new file mode 100644 index 000000000..eff263c91 --- /dev/null +++ b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerLinux.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +namespace RC +{ + struct DLData + { + std::string libname; + uint8_t* base_address; + uint64_t map_start, map_end; + size_t size; + // sorted by start address + std::set> phdrs; + std::tuple find_phdr(uint8_t* addr) + { + for (auto& phdr : phdrs) + { + // use end address to find the phdr + if (std::get<0>(phdr) + std::get<1>(phdr) > addr) + { + return phdr; + } + } + return std::make_tuple(nullptr, 0, 0); + } + }; + + using ModuleInfo = DLData; + using ModuleOS = DLData; + using SystemInfo = DLData; +} // namespace RC diff --git a/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp new file mode 100644 index 000000000..ed27eeedf --- /dev/null +++ b/deps/first/SinglePassSigScanner/include/SigScanner/SinglePassSigScannerWin32.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +// Windows.h forward declarations +struct _SYSTEM_INFO; +typedef _SYSTEM_INFO SYSTEM_INFO; +struct _MODULEINFO; +typedef _MODULEINFO MODULEINFO; + +namespace RC +{ + // Windows structs, to prevent the need to include Windows.h in this header + struct WIN_MODULEINFO + { + void* lpBaseOfDll; + unsigned long SizeOfImage; + void* EntryPoint; + RC_SPSS_API auto operator=(MODULEINFO) -> WIN_MODULEINFO&; + }; + using ModuleInfo = WIN_MODULEINFO; + using ModuleOS = MODULEINFO; + using SystemInfo = SYSTEM_INFO; +} // namespace RC diff --git a/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp b/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp index ca8e76830..2050b5bb6 100644 --- a/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp +++ b/deps/first/SinglePassSigScanner/src/SinglePassSigScanner.cpp @@ -1,10 +1,10 @@ #include #include #include +#include +#include -#define NOMINMAX -#include -#include +#include #include #include @@ -19,19 +19,6 @@ namespace RC uint32_t SinglePassScanner::m_multithreading_module_size_threshold = 0x1000000; std::mutex SinglePassScanner::m_scanner_mutex{}; - auto WIN_MODULEINFO::operator=(MODULEINFO other) -> WIN_MODULEINFO& - { - lpBaseOfDll = other.lpBaseOfDll; - SizeOfImage = other.SizeOfImage; - EntryPoint = other.EntryPoint; - return *this; - } - - auto ScanTargetArray::operator[](ScanTarget index) -> MODULEINFO& - { - return *std::bit_cast(&array[static_cast(index)]); - } - auto ScanTargetToString(ScanTarget scan_target) -> std::string { switch (scan_target) @@ -358,56 +345,6 @@ namespace RC return vector_of_signatures; } - auto SinglePassScanner::string_scan(std::wstring_view string_to_scan_for, ScanTarget scan_target) -> void* - { - auto module = SigScannerStaticData::m_modules_info[scan_target]; - - auto start_address = static_cast(module.lpBaseOfDll); - auto end_address = static_cast(module.lpBaseOfDll) + module.SizeOfImage; - - MEMORY_BASIC_INFORMATION memory_info{}; - DWORD protect_flags = PAGE_GUARD | PAGE_NOCACHE | PAGE_NOACCESS; - - void* address_found{}; - - for (uint8_t* i = start_address; i < end_address; ++i) - { - if (VirtualQuery(i, &memory_info, sizeof(memory_info))) - { - if (memory_info.Protect & protect_flags || !(memory_info.State & MEM_COMMIT)) - { - i += memory_info.RegionSize; - continue; - } - - uint8_t* region_end = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; - - for (uint8_t* region_start = static_cast(memory_info.BaseAddress); region_start < region_end; ++region_start) - { - if (region_start > end_address || region_start + string_to_scan_for.size() > end_address) - { - break; - } - - std::wstring_view maybe_string = std::wstring_view((const wchar_t*)region_start, string_to_scan_for.size()); - if (maybe_string == string_to_scan_for) - { - address_found = region_start; - break; - } - } - - if (address_found) - { - break; - } - i = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; - } - } - - return address_found; - } - struct PatternData { std::vector pattern{}; @@ -487,7 +424,7 @@ namespace RC return pattern_data; } - auto SinglePassScanner::scanner_work_thread(uint8_t* start_address, uint8_t* end_address, SYSTEM_INFO& info, std::vector& signature_containers) + auto SinglePassScanner::scanner_work_thread(uint8_t* start_address, uint8_t* end_address, SystemInfo* info, std::vector& signature_containers) -> void { ProfilerSetThreadName("UE4SS-ScannerWorkThread"); @@ -504,154 +441,6 @@ namespace RC } } - auto SinglePassScanner::scanner_work_thread_scalar(uint8_t* start_address, - uint8_t* end_address, - SYSTEM_INFO& info, - std::vector& signature_containers) -> void - { - ProfilerScope(); - if (!start_address) - { - start_address = static_cast(info.lpMinimumApplicationAddress); - } - if (!end_address) - { - start_address = static_cast(info.lpMaximumApplicationAddress); - } - - MEMORY_BASIC_INFORMATION memory_info{}; - DWORD protect_flags = PAGE_GUARD | PAGE_NOCACHE | PAGE_NOACCESS; - - // TODO: Nasty nasty nasty. Come up with a better solution... wtf - // It should ideally be able to work with the char* directly instead of converting to to vectors of ints - // The reason why working directly with the char* is a problem is that it's expensive to convert a hex char to an int - // Also look at the comments below - std::vector>> vector_of_sigs; - - // const std::regex signature_validity_regex(R"(^([0-9A-F\?] )([0-9A-F\?]\/[0-9A-F\?] )*([0-9A-F\?])$)"); - - // Making a vector here to be identical to the SignatureContainer vector - // The difference is that it stores the sigs converted from char* to std::vector - // This makes it easier to work with even if it's wasteful - // This should not be done in the nested loops below because this operation is very slow - vector_of_sigs.reserve(signature_containers.size()); - for (const auto& container : signature_containers) - { - // Only continue if the signature is properly formatted - // Bring this code back when both: - // A. The regex has been updated to take into consideration. - // B. The threads have been synced before the scan to verify that all threads are scanning for valid signatures. - // for (const auto& signature_data : container.signatures) - //{ - // if (!std::regex_search(signature_data.signature, signature_validity_regex)) - // { - // throw std::runtime_error{std::format("[SinglePassSigScanner::start_scan] A signature is improperly formatted. Signature: {}", signature_data.signature)}; - // } - //} - - // Signatures for this container - vector_of_sigs.emplace_back(string_to_vector(container.signatures)); - } - - // Loop everything - for (uint8_t* i = start_address; i < end_address; ++i) - { - // Populate memory_info if VirtualQuery doesn't fail - if (VirtualQuery(i, &memory_info, sizeof(memory_info))) - { - // If the "protect flags" or state are undesired for this region then skip to the next iteration of the loop - if (memory_info.Protect & protect_flags || !(memory_info.State & MEM_COMMIT)) - { - i += memory_info.RegionSize; - continue; - } - - uint8_t* region_end = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; - - for (uint8_t* region_start = static_cast(memory_info.BaseAddress); region_start < region_end; ++region_start) - { - if (region_start > end_address) - { - break; - } - - bool skip_to_next_container{}; - - for (size_t container_index = 0; const auto& int_container : vector_of_sigs) - { - for (size_t signature_index = 0; const auto& sig : int_container) - { - // If the container is refusing more calls then skip to the next container - if (signature_containers[container_index].ignore) - { - break; - } - - // Skip if we're about to dereference uninitialized memory - if (region_start + sig.size() > region_end) - { - break; - } - - for (size_t sig_i = 0; sig_i < sig.size(); sig_i += 2) - { - if (sig.at(sig_i) != -1 && sig.at(sig_i) != HI_NIBBLE(*(byte*)(region_start + (sig_i / 2))) || - sig.at(sig_i + 1) != -1 && sig.at(sig_i + 1) != LO_NIBBLE(*(byte*)(region_start + (sig_i / 2)))) - { - break; - } - - if (sig_i + 2 == sig.size()) - { - { - std::lock_guard safe_scope(m_scanner_mutex); - - // Checking for the second time if the container is refusing more calls - // This is required when multi-threading is enabled - if (signature_containers[container_index].ignore) - { - skip_to_next_container = true; - break; - } - - // One of the signatures have found a full match so lets forward the details to the callable - signature_containers[container_index].index_into_signatures = signature_index; - signature_containers[container_index].match_address = region_start; - signature_containers[container_index].match_signature_size = sig.size() / 2; - - skip_to_next_container = signature_containers[container_index].on_match_found(signature_containers[container_index]); - signature_containers[container_index].ignore = skip_to_next_container; - - // Store results if the container at the containers request - if (signature_containers[container_index].store_results) - { - signature_containers[container_index].result_store.emplace_back( - SignatureContainerLight{.index_into_signatures = signature_index, .match_address = region_start}); - } - } - - break; - } - } - - if (skip_to_next_container) - { - // A match was found and signaled to skip to the next container - break; - } - - ++signature_index; - } - - ++container_index; - } - } - - i = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; - } - } - } - static auto format_aob_string(std::string& str) -> void { if (str.size() < 4) @@ -686,18 +475,18 @@ namespace RC auto SinglePassScanner::scanner_work_thread_stdfind(uint8_t* start_address, uint8_t* end_address, - SYSTEM_INFO& info, + SystemInfo* info, std::vector& signature_containers) -> void { ProfilerScope(); if (!start_address) { - start_address = static_cast(info.lpMinimumApplicationAddress); + start_address = Platform::get_start_address(info); } if (!end_address) { - start_address = static_cast(info.lpMaximumApplicationAddress); + end_address = Platform::get_end_address(info); } format_aob_strings(signature_containers); @@ -787,21 +576,21 @@ namespace RC auto SinglePassScanner::start_scan(SignatureContainerMap& signature_containers) -> void { - SYSTEM_INFO info{}; - GetSystemInfo(&info); + SystemInfo* info = Platform::get_system_info(); // If not modular then the containers get merged into one scan target // That way there are no extra scans // If modular then loop the containers and retrieve the scan target for each and pass everything to the do_scan() lambda - + fprintf(stderr, "signature_containers.size() = %d\n", signature_containers.size()); if (!SigScannerStaticData::m_is_modular) { - MODULEINFO merged_module_info{}; + ModuleOS* merged_module_info{}; std::vector merged_containers; for (const auto& [scan_target, outer_container] : signature_containers) { - merged_module_info = *std::bit_cast(&SigScannerStaticData::m_modules_info[scan_target]); + merged_module_info = std::bit_cast(&SigScannerStaticData::m_modules_info[scan_target]); + fprintf(stderr, "outer_container len = %d\n", outer_container.size()); for (const auto& signature_container : outer_container) { merged_containers.emplace_back(signature_container); @@ -810,20 +599,22 @@ namespace RC if (merged_containers.empty()) { - throw std::runtime_error{"[SinglePassScanner::start_scan] Could not merge containers. Either there were not containers to merge or there was " - "an internal error."}; + fprintf(stderr, "No containers to merge\n"); + return; + // throw std::runtime_error{"[SinglePassScanner::start_scan] Could not merge containers. Either there were not containers to merge or there was " + // "an internal error."}; } - uint8_t* module_start_address = static_cast(merged_module_info.lpBaseOfDll); + uint8_t* module_start_address = Platform::get_module_base(merged_module_info); - if (merged_module_info.SizeOfImage >= m_multithreading_module_size_threshold) + if (Platform::get_module_size(merged_module_info) >= m_multithreading_module_size_threshold) { // Module is large enough to make it overall faster to scan with multiple threads std::vector> scan_threads; // Higher values are better for debugging // You will get a bigger diminishing returns the faster your computer is (especially in release mode) - uint32_t range = merged_module_info.SizeOfImage / m_num_threads; + uint32_t range = Platform::get_module_size(merged_module_info) / m_num_threads; // Calculating the ranges for each thread to scan and starting the scan uint32_t last_range{}; @@ -833,7 +624,7 @@ namespace RC &scanner_work_thread, module_start_address + last_range, module_start_address + last_range + range, - std::ref(info), + info, std::ref(merged_containers))); last_range += range; @@ -847,7 +638,7 @@ namespace RC else { // Module is too small to make it overall faster to scan with multiple threads - uint8_t* module_end_address = static_cast(module_start_address + merged_module_info.SizeOfImage); + uint8_t* module_end_address = static_cast(module_start_address + Platform::get_module_size(merged_module_info)); scanner_work_thread(module_start_address, module_end_address, info, merged_containers); } @@ -861,10 +652,12 @@ namespace RC // This ranged for loop is performing a copy of unordered_map> // Is this required ? Would it be worth trying to avoid copying here ? // Right now it can't be auto& or const auto& because the do_scan function takes a non-const since it needs to mutate the values inside the vector + auto info = Platform::get_system_info(); for (auto& [scan_target, signature_container] : signature_containers) { - uint8_t* module_start_address = static_cast(SigScannerStaticData::m_modules_info[scan_target].lpBaseOfDll); - uint8_t* module_end_address = static_cast(module_start_address + SigScannerStaticData::m_modules_info[scan_target].SizeOfImage); + uint8_t* module_start_address = static_cast(Platform::get_module_base(&SigScannerStaticData::m_modules_info[scan_target])); + uint8_t* module_end_address = + static_cast(module_start_address + Platform::get_module_size(&SigScannerStaticData::m_modules_info[scan_target])); scanner_work_thread(module_start_address, module_end_address, info, signature_container); @@ -875,4 +668,4 @@ namespace RC } } } -} // namespace RC \ No newline at end of file +}; // namespace RC diff --git a/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp new file mode 100644 index 000000000..f0442052c --- /dev/null +++ b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerLinux.cpp @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace RC +{ + + auto ScanTargetArray::operator[](ScanTarget index) -> ModuleOS& + { + return array[static_cast(index)]; + } + + namespace Platform + { + auto get_system_info() -> SystemInfo* + { + return &SigScannerStaticData::m_modules_info[ScanTarget::MainExe]; + } + + auto get_start_address(DLData* info) -> uint8_t* + { + return info->base_address; + } + + auto get_end_address(DLData* info) -> uint8_t* + { + return info->base_address + info->size; + } + + auto get_module_size(DLData* info) -> uint32_t + { + return info->size; + } + + auto get_module_base(DLData* info) -> uint8_t* + { + return info->base_address; + } + }; // namespace Platform + + auto SinglePassScanner::string_scan(std::wstring_view string_to_scan_for, ScanTarget scan_target) -> void* + { + throw std::runtime_error{"[SinglePassSigScanner::string_scan] Not implemented for Linux"}; + } + + // This function may have problem becasue we're not checking the memory region's permissions + auto SinglePassScanner::scanner_work_thread_scalar(uint8_t* start_address, uint8_t* end_address, DLData* info, std::vector& signature_containers) + -> void + { + ProfilerScope(); + if (!start_address) + { + start_address = Platform::get_start_address(info); + } + if (!end_address) + { + end_address = Platform::get_end_address(info); + } + + // TODO: Nasty nasty nasty. Come up with a better solution... wtf + // It should ideally be able to work with the char* directly instead of converting to to vectors of ints + // The reason why working directly with the char* is a problem is that it's expensive to convert a hex char to an int + // Also look at the comments below + std::vector>> vector_of_sigs; + + // const std::regex signature_validity_regex(R"(^([0-9A-F\?] )([0-9A-F\?]\/[0-9A-F\?] )*([0-9A-F\?])$)"); + + // Making a vector here to be identical to the SignatureContainer vector + // The difference is that it stores the sigs converted from char* to std::vector + // This makes it easier to work with even if it's wasteful + // This should not be done in the nested loops below because this operation is very slow + vector_of_sigs.reserve(signature_containers.size()); + for (const auto& container : signature_containers) + { + // Only continue if the signature is properly formatted + // Bring this code back when both: + // A. The regex has been updated to take into consideration. + // B. The threads have been synced before the scan to verify that all threads are scanning for valid signatures. + // for (const auto& signature_data : container.signatures) + //{ + // if (!std::regex_search(signature_data.signature, signature_validity_regex)) + // { + // throw std::runtime_error{std::format("[SinglePassSigScanner::start_scan] A signature is improperly formatted. Signature: {}", signature_data.signature)}; + // } + //} + + // Signatures for this container + vector_of_sigs.emplace_back(string_to_vector(container.signatures)); + } + + // Loop everything + for (uint8_t* i = start_address; i < end_address; ++i) + { + auto addr = info->find_phdr(i); + if (addr == std::make_tuple(nullptr, 0, 0)) + { + continue; + } + if (std::get<0>(addr) > i) + { + i = std::get<0>(addr); + } + uint8_t* region_end = static_cast(std::get<0>(addr)) + std::get<1>(addr); + if ((std::get<2>(addr) & PF_R) == 0) + { + i = region_end; + continue; + } + + uint8_t* scan_end = std::min(end_address, region_end); + for (uint8_t* region_start = std::get<0>(addr); region_start < scan_end; ++region_start) + { + bool skip_to_next_container{}; + + for (size_t container_index = 0; const auto& int_container : vector_of_sigs) + { + for (size_t signature_index = 0; const auto& sig : int_container) + { + // If the container is refusing more calls then skip to the next container + if (signature_containers[container_index].ignore) + { + break; + } + + // Skip if we're about to dereference uninitialized memory + if (region_start + sig.size() > region_end) + { + break; + } + + for (size_t sig_i = 0; sig_i < sig.size(); sig_i += 2) + { + if (sig.at(sig_i) != -1 && sig.at(sig_i) != HI_NIBBLE(*(uint8_t*)(region_start + (sig_i / 2))) || + sig.at(sig_i + 1) != -1 && sig.at(sig_i + 1) != LO_NIBBLE(*(uint8_t*)(region_start + (sig_i / 2)))) + { + break; + } + + if (sig_i + 2 == sig.size()) + { + { + std::lock_guard safe_scope(m_scanner_mutex); + + // Checking for the second time if the container is refusing more calls + // This is required when multi-threading is enabled + if (signature_containers[container_index].ignore) + { + skip_to_next_container = true; + break; + } + + // One of the signatures have found a full match so lets forward the details to the callable + signature_containers[container_index].index_into_signatures = signature_index; + signature_containers[container_index].match_address = region_start; + signature_containers[container_index].match_signature_size = sig.size() / 2; + + skip_to_next_container = signature_containers[container_index].on_match_found(signature_containers[container_index]); + signature_containers[container_index].ignore = skip_to_next_container; + + // Store results if the container at the containers request + if (signature_containers[container_index].store_results) + { + signature_containers[container_index].result_store.emplace_back( + SignatureContainerLight{.index_into_signatures = signature_index, .match_address = region_start}); + } + } + + break; + } + } + + if (skip_to_next_container) + { + // A match was found and signaled to skip to the next container + break; + } + + ++signature_index; + } + + ++container_index; + } + } + + i = scan_end; + } + } +} // namespace RC \ No newline at end of file diff --git a/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp new file mode 100644 index 000000000..abbb8f13b --- /dev/null +++ b/deps/first/SinglePassSigScanner/src/SinglePassSigScannerWin32.cpp @@ -0,0 +1,263 @@ +#include +#include +#include + +#include + +#ifdef WIN32 +#define NOMINMAX +#include +#include +#endif + +#include +#include + +namespace RC +{ + + auto ScanTargetArray::operator[](ScanTarget index) -> ModuleOS& + { + return *std::bit_cast(&array[static_cast(index)]); + } + + namespace Platform + { + auto get_system_info() -> SystemInfo* + { + static SYSTEM_INFO info{}; + GetSystemInfo(&info); + return &info; + } + + // Get the start address of the system + auto get_start_address(SystemInfo* info) -> uint8_t* + { + return static_cast(info->lpMinimumApplicationAddress); + } + + // Get the end address of the system + auto get_end_address(SystemInfo* info) -> uint8_t* + { + return static_cast(info->lpMaximumApplicationAddress); + } + + // Get the size of the module + auto get_module_size(ModuleOS* info) -> uint32_t + { + return info->SizeOfImage; + } + + // Get the base address of the module + auto get_module_base(ModuleOS* info) -> uint8_t* + { + return static_cast(info->lpBaseOfDll); + } + }; // namespace Platform + + auto WIN_MODULEINFO::operator=(MODULEINFO other) -> WIN_MODULEINFO& + { + lpBaseOfDll = other.lpBaseOfDll; + SizeOfImage = other.SizeOfImage; + EntryPoint = other.EntryPoint; + return *this; + } + + auto SinglePassScanner::string_scan(std::wstring_view string_to_scan_for, ScanTarget scan_target) -> void* + { + auto module = SigScannerStaticData::m_modules_info[scan_target]; + + auto start_address = static_cast(module.lpBaseOfDll); + auto end_address = static_cast(module.lpBaseOfDll) + module.SizeOfImage; + + MEMORY_BASIC_INFORMATION memory_info{}; + DWORD protect_flags = PAGE_GUARD | PAGE_NOCACHE | PAGE_NOACCESS; + + void* address_found{}; + + for (uint8_t* i = start_address; i < end_address; ++i) + { + if (VirtualQuery(i, &memory_info, sizeof(memory_info))) + { + if (memory_info.Protect & protect_flags || !(memory_info.State & MEM_COMMIT)) + { + i += memory_info.RegionSize; + continue; + } + + uint8_t* region_end = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; + + for (uint8_t* region_start = static_cast(memory_info.BaseAddress); region_start < region_end; ++region_start) + { + if (region_start > end_address || region_start + string_to_scan_for.size() > end_address) + { + break; + } + + auto maybe_string = std::wstring_view((const wchar_t*)region_start, string_to_scan_for.size()); + if (maybe_string == string_to_scan_for) + { + address_found = region_start; + break; + } + } + + if (address_found) + { + break; + } + i = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; + } + } + + return address_found; + } + + auto SinglePassScanner::scanner_work_thread_scalar(uint8_t* start_address, + uint8_t* end_address, + SYSTEM_INFO* info, + std::vector& signature_containers) -> void + { + ProfilerScope(); + if (!start_address) + { + start_address = static_cast(info->lpMinimumApplicationAddress); + } + if (!end_address) + { + start_address = static_cast(info->lpMaximumApplicationAddress); + } + + MEMORY_BASIC_INFORMATION memory_info{}; + DWORD protect_flags = PAGE_GUARD | PAGE_NOCACHE | PAGE_NOACCESS; + + // TODO: Nasty nasty nasty. Come up with a better solution... wtf + // It should ideally be able to work with the char* directly instead of converting to to vectors of ints + // The reason why working directly with the char* is a problem is that it's expensive to convert a hex char to an int + // Also look at the comments below + std::vector>> vector_of_sigs; + + // const std::regex signature_validity_regex(R"(^([0-9A-F\?] )([0-9A-F\?]\/[0-9A-F\?] )*([0-9A-F\?])$)"); + + // Making a vector here to be identical to the SignatureContainer vector + // The difference is that it stores the sigs converted from char* to std::vector + // This makes it easier to work with even if it's wasteful + // This should not be done in the nested loops below because this operation is very slow + vector_of_sigs.reserve(signature_containers.size()); + for (const auto& container : signature_containers) + { + // Only continue if the signature is properly formatted + // Bring this code back when both: + // A. The regex has been updated to take into consideration. + // B. The threads have been synced before the scan to verify that all threads are scanning for valid signatures. + // for (const auto& signature_data : container.signatures) + //{ + // if (!std::regex_search(signature_data.signature, signature_validity_regex)) + // { + // throw std::runtime_error{std::format("[SinglePassSigScanner::start_scan] A signature is improperly formatted. Signature: {}", signature_data.signature)}; + // } + //} + + // Signatures for this container + vector_of_sigs.emplace_back(string_to_vector(container.signatures)); + } + + // Loop everything + for (uint8_t* i = start_address; i < end_address; ++i) + { + // Populate memory_info if VirtualQuery doesn't fail + if (VirtualQuery(i, &memory_info, sizeof(memory_info))) + { + // If the "protect flags" or state are undesired for this region then skip to the next iteration of the loop + if (memory_info.Protect & protect_flags || !(memory_info.State & MEM_COMMIT)) + { + i += memory_info.RegionSize; + continue; + } + + uint8_t* region_end = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; + + for (uint8_t* region_start = static_cast(memory_info.BaseAddress); region_start < region_end; ++region_start) + { + if (region_start > end_address) + { + break; + } + + bool skip_to_next_container{}; + + for (size_t container_index = 0; const auto& int_container : vector_of_sigs) + { + for (size_t signature_index = 0; const auto& sig : int_container) + { + // If the container is refusing more calls then skip to the next container + if (signature_containers[container_index].ignore) + { + break; + } + + // Skip if we're about to dereference uninitialized memory + if (region_start + sig.size() > region_end) + { + break; + } + + for (size_t sig_i = 0; sig_i < sig.size(); sig_i += 2) + { + if (sig.at(sig_i) != -1 && sig.at(sig_i) != HI_NIBBLE(*(byte*)(region_start + (sig_i / 2))) || + sig.at(sig_i + 1) != -1 && sig.at(sig_i + 1) != LO_NIBBLE(*(byte*)(region_start + (sig_i / 2)))) + { + break; + } + + if (sig_i + 2 == sig.size()) + { + { + std::lock_guard safe_scope(m_scanner_mutex); + + // Checking for the second time if the container is refusing more calls + // This is required when multi-threading is enabled + if (signature_containers[container_index].ignore) + { + skip_to_next_container = true; + break; + } + + // One of the signatures have found a full match so lets forward the details to the callable + signature_containers[container_index].index_into_signatures = signature_index; + signature_containers[container_index].match_address = region_start; + signature_containers[container_index].match_signature_size = sig.size() / 2; + + skip_to_next_container = signature_containers[container_index].on_match_found(signature_containers[container_index]); + signature_containers[container_index].ignore = skip_to_next_container; + + // Store results if the container at the containers request + if (signature_containers[container_index].store_results) + { + signature_containers[container_index].result_store.emplace_back( + SignatureContainerLight{.index_into_signatures = signature_index, .match_address = region_start}); + } + } + + break; + } + } + + if (skip_to_next_container) + { + // A match was found and signaled to skip to the next container + break; + } + + ++signature_index; + } + + ++container_index; + } + } + + i = static_cast(memory_info.BaseAddress) + memory_info.RegionSize; + } + } + } +} // namespace RC \ No newline at end of file diff --git a/deps/first/SinglePassSigScanner/xmake.lua b/deps/first/SinglePassSigScanner/xmake.lua index 91b913900..9eb5b40e9 100644 --- a/deps/first/SinglePassSigScanner/xmake.lua +++ b/deps/first/SinglePassSigScanner/xmake.lua @@ -9,6 +9,24 @@ target(projectName) add_includedirs("include", { public = true }) add_headerfiles("include/**.hpp") - add_files("src/**.cpp") + -- *? is not supported? + add_files("src/**.cpp|SinglePassSigScannerWin32.cpp|SinglePassSigScannerLinux.cpp") - add_deps("Profiler") \ No newline at end of file + if is_plat("windows") then + add_headerfiles("include/SigScanner/SinglePassSigScannerWin32.hpp") + add_files("src/SinglePassSigScannerWin32.cpp") + elseif is_plat("linux") then + add_headerfiles("include/SigScanner/SinglePassSigScannerLinux.hpp") + add_files("src/SinglePassSigScannerLinux.cpp") + end + + add_deps("Profiler") + + on_load(function (target) + import("target_helpers", { rootdir = get_config("scriptsRoot") }) + + print("Project: " .. projectName .. " (STATIC)") + + target:add("defines", target_helpers.project_name_to_exports_define(projectName)) + target:add("defines", target_helpers.project_name_to_build_static_define(projectName)) + end) diff --git a/deps/first/Unreal b/deps/first/Unreal index ebfddd128..946180f2e 160000 --- a/deps/first/Unreal +++ b/deps/first/Unreal @@ -1 +1 @@ -Subproject commit ebfddd128dc9e4b1ef76108348d70e57e1639b88 +Subproject commit 946180f2e93bb1679ab1bd1edc015e4352b57278 diff --git a/deps/first/patternsleuth b/deps/first/patternsleuth index 33e731e99..0d5175345 160000 --- a/deps/first/patternsleuth +++ b/deps/first/patternsleuth @@ -1 +1 @@ -Subproject commit 33e731e99f2a6bb7f65a8e95e89fd1c06ce9d1d2 +Subproject commit 0d5175345a4827f98bc51df893cf2079ebf53306 diff --git a/deps/first/patternsleuth_bind/Cargo.lock b/deps/first/patternsleuth_bind/Cargo.lock index dd07ac157..f2e2e9568 100644 --- a/deps/first/patternsleuth_bind/Cargo.lock +++ b/deps/first/patternsleuth_bind/Cargo.lock @@ -119,6 +119,18 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "flate2" version = "1.0.28" @@ -230,6 +242,17 @@ dependencies = [ "slab", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -251,6 +274,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "indexmap" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inventory" version = "0.3.14" @@ -349,6 +382,7 @@ dependencies = [ "anyhow", "futures", "futures-scopes", + "gimli", "iced-x86", "inventory", "itertools", @@ -494,6 +528,12 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/deps/first/patternsleuth_bind/Cargo.toml b/deps/first/patternsleuth_bind/Cargo.toml index 25f01784b..9efe6cffd 100644 --- a/deps/first/patternsleuth_bind/Cargo.toml +++ b/deps/first/patternsleuth_bind/Cargo.toml @@ -3,5 +3,8 @@ name = "patternsleuth_bind" version = "0.1.0" edition = "2021" -[dependencies] -patternsleuth = { path = "../patternsleuth/patternsleuth", features = ["process-internal"] } \ No newline at end of file +[target.'cfg( target_os = "linux" )'.dependencies] +patternsleuth = { path = "../patternsleuth/patternsleuth", features = ["process-internal", "image-elf"] } + +[target.'cfg( target_os = "windows" )'.dependencies] +patternsleuth = { path = "../patternsleuth/patternsleuth", features = ["process-internal", "image-pe"] } diff --git a/deps/first/xmake.lua b/deps/first/xmake.lua index 875d9ce34..c9da56077 100644 --- a/deps/first/xmake.lua +++ b/deps/first/xmake.lua @@ -1,4 +1,4 @@ -includes("ArgsParser") +-- includes("ArgsParser") includes("ASMHelper") includes("Constructs") includes("DynamicOutput") @@ -28,13 +28,18 @@ if is_config("patternsleuth", "local") then add_extrafiles("patternsleuth/**.rs") end --- This option allows users to choose if patternsleuth should be installed as a package --- or if patternsleuth should be built as a dependency by xmake. The `package` option --- should be used if you don't intend on ever modifying the patternsleuth source. --- The `local` option should be used if you want changes in the patternsleuth --- submodule to be included as part of the UE4SS build. -option("patternsleuth") - set_default("package") - set_showmenu(true) - set_values("package", "local") - set_description("Install patternsleuth as a package or build it as a dependency.", "package", "local") +add_requires("cargo::patternsleuth_bind", { debug = is_mode_debug(), configs = { cargo_toml = path.join(os.scriptdir(), "patternsleuth_bind/Cargo.toml"), runtimes = get_mode_runtimes() } }) + +target("patternsleuth_bind") + set_kind("static") + set_values("rust.cratetype", "staticlib") + set_values("rust.edition", "2021") + add_files("patternsleuth_bind/src/lib.rs") + if is_plat("linux") and is_host("windows") then + add_rcflags("--target=x86_64-unknown-linux-gnu", {force = true}) + end + add_packages("cargo::patternsleuth_bind") + if is_plat("windows") then + add_links("ws2_32", "advapi32", "userenv", "ntdll", "oleaut32", "bcrypt", "ole32", { public = true }) + end +-- Patternsleuth -> END diff --git a/deps/third-repo/packages/i/imguitextedit/xmake.lua b/deps/third-repo/packages/i/imguitextedit/xmake.lua index bcb2c93ef..cb665962f 100644 --- a/deps/third-repo/packages/i/imguitextedit/xmake.lua +++ b/deps/third-repo/packages/i/imguitextedit/xmake.lua @@ -4,12 +4,27 @@ package("ImGuiTextEdit") add_versions("v1.0", "master") - add_deps("cmake", "imgui v1.89") + add_configs("tui", { description = "Enable TUI.", default = false, type = "boolean" }) add_includedirs("include", { public = true }) + local function _get_imgui_name(isTUI) + if isTUI then + return { name = "imtui", version = "v1.0.5" } + else + return { name = "imgui", version = "v1.89" } + end + end + + on_load(function (package) + local imgui = _get_imgui_name(package:config("tui")) + package:add("deps", string.format("%s %s", imgui.name, imgui.version)) + end) + on_install(function (package) - local imgui = package:dep("imgui") + local imguiName = _get_imgui_name(package:config("tui")).name + local imgui = package:dep(imguiName) + local configs = imgui:requireinfo().configs if configs then @@ -19,7 +34,7 @@ package("ImGuiTextEdit") local xmake_lua = ([[ add_rules("mode.debug", "mode.release") - add_requires("imgui %s", { configs = %s }) + add_requires("%s %s", { configs = %s }) target("ImGuiTextEdit") set_kind("static") @@ -30,10 +45,30 @@ package("ImGuiTextEdit") add_files("*.cpp") - add_packages("imgui") - ]]):format(imgui:version_str(), configs) + add_packages("%s") + ]]):format(imguiName, imgui:version_str(), configs, imguiName) io.writefile("xmake.lua", xmake_lua) import("package.tools.xmake").install(package) end) -package_end() \ No newline at end of file +package_end() + +package("IconFontCppHeaders") + add_urls("git@github.com:juliettef/IconFontCppHeaders.git") + add_urls("https://github.com/juliettef/IconFontCppHeaders.git") + set_kind("library", { headeronly = true }) + + add_versions("v1.0", "main") + + on_install(function (package) + os.cp("**.h", package:installdir("include")) + end) + + on_test(function (package) + assert(package:check_cxxsnippets({ test = [[ + void test() { + ICON_FA_TERMINAL; + } + ]]}, { includes = { "IconsFontAwesome5.h" } })) + end) +package_end() diff --git a/deps/third-repo/packages/i/imtui/fix-size-cjk-and-mouse.patch b/deps/third-repo/packages/i/imtui/fix-size-cjk-and-mouse.patch new file mode 100644 index 000000000..0f039c337 --- /dev/null +++ b/deps/third-repo/packages/i/imtui/fix-size-cjk-and-mouse.patch @@ -0,0 +1,412 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 9376c4a..5e24536 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -64,6 +64,8 @@ if (IMTUI_SUPPORT_NCURSES) + if (MINGW) + set(CURSES_LIBRARIES pdcurses) + else() ++ set(CURSES_NEED_NCURSES TRUE) ++ set(CURSES_NEED_WIDE TRUE) + find_package(Curses REQUIRED) + CHECK_LIBRARY_EXISTS("${CURSES_NCURSES_LIBRARY}" + nodelay "" CURSES_NCURSES_HAS_NODELAY) +diff --git a/include/imtui/imtui-impl-ncurses.h b/include/imtui/imtui-impl-ncurses.h +index 51e64d3..5a05359 100644 +--- a/include/imtui/imtui-impl-ncurses.h ++++ b/include/imtui/imtui-impl-ncurses.h +@@ -4,6 +4,8 @@ + + #pragma once + ++#include ++ + namespace ImTui { + struct TScreen; + } +@@ -19,7 +21,7 @@ ImTui::TScreen * ImTui_ImplNcurses_Init(bool mouseSupport, float fps_active = 60 + void ImTui_ImplNcurses_Shutdown(); + + // returns true if there is any user input from the keyboard/mouse +-bool ImTui_ImplNcurses_NewFrame(); ++bool ImTui_ImplNcurses_NewFrame(std::function inputCallback); + + // active - specify which redraw rate to use: fps_active or fps_idle + void ImTui_ImplNcurses_DrawScreen(bool active = true); +diff --git a/include/imtui/imtui.h b/include/imtui/imtui.h +index b77c342..100a4d8 100644 +--- a/include/imtui/imtui.h ++++ b/include/imtui/imtui.h +@@ -21,7 +21,12 @@ using TColor = unsigned char; + // 0x0000FFFF - char + // 0x00FF0000 - foreground color + // 0xFF000000 - background color +-using TCell = uint32_t; ++//using TCell = uint32_t; ++ ++// 0x00000000FFFFFFFF - char ++// 0x000000FF00000000 - foreground color ++// 0x0000FF0000000000 - background color ++using TCell = uint64_t; + + struct TScreen { + int nx = 0; +diff --git a/src/imtui-impl-ncurses.cpp b/src/imtui-impl-ncurses.cpp +index 1d454ba..40adeee 100644 +--- a/src/imtui-impl-ncurses.cpp ++++ b/src/imtui-impl-ncurses.cpp +@@ -27,6 +27,9 @@ + #include + #endif + ++#include ++#include ++ + #include + #include + #include +@@ -101,7 +104,8 @@ ImTui::TScreen * ImTui_ImplNcurses_Init(bool mouseSupport, float fps_active, flo + } + fps_idle = std::min(fps_active, fps_idle); + g_vsync = VSync(fps_active, fps_idle); +- ++ ++ setlocale(LC_ALL,""); + initscr(); + use_default_colors(); + start_color(); +@@ -150,15 +154,57 @@ ImTui::TScreen * ImTui_ImplNcurses_Init(bool mouseSupport, float fps_active, flo + + getmaxyx(stdscr, screenSizeY, screenSizeX); + ImGui::GetIO().DisplaySize = ImVec2(screenSizeX, screenSizeY); ++ static char encoding_table[64]; ++ for (int i = 0; i < 26; i++) { ++ encoding_table[i] = 'A' + i; ++ encoding_table[i + 26] = 'a' + i; ++ } ++ for (int i = 0; i < 10; i++) { ++ encoding_table[i + 52] = '0' + i; ++ } ++ encoding_table[62] = '+'; ++ encoding_table[63] = '/'; ++ ImGui::GetIO().SetClipboardTextFn = [](void* user_data, const char* text) { ++ // ncurses clipboard ++ int length = strlen(text); ++ refresh(); ++ printf("\e]52;c;"); ++ for (int i = 0; i < length; ) { ++ uint32_t c = 0; ++ for (int j = 0; j < 3; j++) { ++ c = (c << 8) | (i < length ? (uint8_t) text[i++] : 0); ++ } ++ for (int j = 3; j >= 0; j--) { ++ printf("%c", encoding_table[(c >> (6*j)) & 0x3F]); ++ } ++ } ++ int pad = (length + 2) % 3; ++ for (int i = 0; i < pad; i++) { ++ printf("="); ++ } ++ printf("\a"); ++ fflush(stdout); ++ refresh(); ++ }; + + return g_screen; + } + ++void reset_terminal_modes() { ++ struct termios term; ++ tcgetattr(STDIN_FILENO, &term); ++ term.c_iflag |= (ICRNL | INLCR); // Translate CR to NL on input ++ term.c_oflag |= (ONLCR); // Translate NL to CR-NL on output ++ tcsetattr(STDIN_FILENO, TCSANOW, &term); ++} ++ + void ImTui_ImplNcurses_Shutdown() { + // ref #11 : https://github.com/ggerganov/imtui/issues/11 + printf("\033[?1003l\n"); // Disable mouse movement events, as l = low + + endwin(); ++ reset_shell_mode(); ++ reset_terminal_modes(); + + if (g_screen) { + delete g_screen; +@@ -167,7 +213,7 @@ void ImTui_ImplNcurses_Shutdown() { + g_screen = nullptr; + } + +-bool ImTui_ImplNcurses_NewFrame() { ++bool ImTui_ImplNcurses_NewFrame(std::function inputCallback) { + bool hasInput = false; + + int screenSizeX = 0; +@@ -178,8 +224,10 @@ bool ImTui_ImplNcurses_NewFrame() { + + static int mx = 0; + static int my = 0; +- static int lbut = 0; +- static int rbut = 0; ++ static int lbutdown = 0; ++ static int rbutdown = 0; ++ static int lbutup = 0; ++ static int rbutup = 0; + static unsigned long mstate = 0; + static char input[3]; + +@@ -196,8 +244,8 @@ bool ImTui_ImplNcurses_NewFrame() { + + if (c == ERR) { + if ((mstate & 0xf) == 0x1) { +- lbut = 0; +- rbut = 0; ++ lbutdown = lbutup = 0; ++ rbutdown = rbutup = 0; + } + break; + } else if (c == KEY_MOUSE) { +@@ -206,10 +254,14 @@ bool ImTui_ImplNcurses_NewFrame() { + mx = event.x; + my = event.y; + mstate = event.bstate; +- if ((mstate & 0x000f) == 0x0002) lbut = 1; +- if ((mstate & 0x000f) == 0x0001) lbut = 0; +- if ((mstate & 0xf000) == 0x2000) rbut = 1; +- if ((mstate & 0xf000) == 0x1000) rbut = 0; ++ if ((mstate & BUTTON1_PRESSED)) {lbutdown = 1; lbutup = 0; } ++ if ((mstate & BUTTON1_RELEASED)) {lbutdown = 0; lbutup = 1; } ++ if ((mstate & BUTTON3_PRESSED)) {rbutdown = 1; rbutup = 0; } ++ if ((mstate & BUTTON3_RELEASED)) {rbutdown = 0; rbutup = 1; } ++ ImGui::GetIO().MouseWheel = 0; ++ if ((mstate & BUTTON4_PRESSED)) {ImGui::GetIO().MouseWheel = 1; } ++ if ((mstate & BUTTON5_PRESSED)) {ImGui::GetIO().MouseWheel = -1; } ++ + //printf("mstate = 0x%016lx\n", mstate); + ImGui::GetIO().KeyCtrl |= ((mstate & 0x0F000000) == 0x01000000); + } +@@ -252,8 +304,8 @@ bool ImTui_ImplNcurses_NewFrame() { + } else { + keysDown[c] = true; + } ++ inputCallback(c); + } +- + hasInput = true; + } + +@@ -266,8 +318,12 @@ bool ImTui_ImplNcurses_NewFrame() { + + ImGui::GetIO().MousePos.x = mx; + ImGui::GetIO().MousePos.y = my; +- ImGui::GetIO().MouseDown[0] = lbut; +- ImGui::GetIO().MouseDown[1] = rbut; ++ ++ ImGui::GetIO().MouseDown[0] = lbutdown; ++ ImGui::GetIO().MouseDown[1] = rbutdown; ++ ++ ImGui::GetIO().MouseReleased[0] = lbutup; ++ ImGui::GetIO().MouseReleased[1] = rbutup; + + ImGui::GetIO().DeltaTime = g_vsync.delta_s(); + +@@ -315,8 +371,10 @@ void ImTui_ImplNcurses_DrawScreen(bool active) { + move(y, 0); + for (int x = 0; x < nx; ++x) { + const auto cell = g_screen->data[y*nx + x]; +- const uint16_t f = (cell & 0x00FF0000) >> 16; +- const uint16_t b = (cell & 0xFF000000) >> 24; ++ // const uint16_t f = (cell & 0x00FF0000) >> 16; ++ // const uint16_t b = (cell & 0xFF000000) >> 24; ++ const uint16_t f = ((cell >> 32) & 0xff); ++ const uint16_t b = ((cell >> 40) & 0xff); + const uint16_t p = b*256 + f; + + if (colPairs[p].first == false) { +@@ -337,8 +395,25 @@ void ImTui_ImplNcurses_DrawScreen(bool active) { + lastp = p; + } + +- const uint16_t c = cell & 0x0000FFFF; +- curs[ic++] = c > 0 ? c : ' '; ++ const uint32_t c = cell & 0xFFFFFFFF; ++ if (c > 0) { ++ // unicode to utf-8 ++ if (c < 0x80) { ++ curs[ic++] = (char) c; ++ } else if (c < 0x800) { ++ curs[ic++] = (char) (0xC0 | (c >> 6)); ++ curs[ic++] = (char) (0x80 | (c & 0x3F)); ++ } else if (c < 0x10000) { ++ curs[ic++] = (char) (0xE0 | (c >> 12)); ++ curs[ic++] = (char) (0x80 | ((c >> 6) & 0x3F)); ++ curs[ic++] = (char) (0x80 | (c & 0x3F)); ++ } else { ++ curs[ic++] = (char) (0xF0 | (c >> 18)); ++ curs[ic++] = (char) (0x80 | ((c >> 12) & 0x3F)); ++ curs[ic++] = (char) (0x80 | ((c >> 6) & 0x3F)); ++ curs[ic++] = (char) (0x80 | (c & 0x3F)); ++ } ++ } else curs[ic++] = ' '; + } + + if (curs.size() > 0) { +diff --git a/src/imtui-impl-text.cpp b/src/imtui-impl-text.cpp +index 48f0e70..afaa68f 100644 +--- a/src/imtui-impl-text.cpp ++++ b/src/imtui-impl-text.cpp +@@ -92,9 +92,12 @@ void drawTriangle(ImVec2 p0, ImVec2 p1, ImVec2 p2, unsigned char col, ImTui::TSc + while (len--) { + if (x >= 0 && x < screen->nx && y + ymin >= 0 && y + ymin < screen->ny) { + auto & cell = screen->data[(y + ymin)*screen->nx + x]; +- cell &= 0x00FF0000; ++ cell &= ~0x0000ff00fffffffful; + cell |= ' '; +- cell |= ((ImTui::TCell)(col) << 24); ++ cell |= ((uint64_t)col) << 40; ++ //cell &= 0x00FF0000; ++ //cell |= ' '; ++ //cell |= ((ImTui::TCell)(col) << 24); + } + ++x; + } +@@ -102,7 +105,7 @@ void drawTriangle(ImVec2 p0, ImVec2 p1, ImVec2 p2, unsigned char col, ImTui::TSc + } + } + +-inline ImTui::TColor rgbToAnsi256(ImU32 col, bool doAlpha) { ++ImTui::TColor rgbToAnsi256(ImU32 col, bool doAlpha) { + ImTui::TColor r = col & 0x000000FF; + ImTui::TColor g = (col & 0x0000FF00) >> 8; + ImTui::TColor b = (col & 0x00FF0000) >> 16; +@@ -196,8 +199,8 @@ void ImTui_ImplText_RenderDrawData(ImDrawData * drawData, ImTui::TScreen * scree + auto uv2 = cmd_list->VtxBuffer[vidx2].uv; + + auto col0 = cmd_list->VtxBuffer[vidx0].col; +- //auto col1 = cmd_list->VtxBuffer[vidx1].col; +- //auto col2 = cmd_list->VtxBuffer[vidx2].col; ++ auto col1 = cmd_list->VtxBuffer[vidx1].col; ++ auto col2 = cmd_list->VtxBuffer[vidx2].col; + + if (uv0.x != uv1.x || uv0.x != uv2.x || uv1.x != uv2.x || + uv0.y != uv1.y || uv0.y != uv2.y || uv1.y != uv2.y) { +@@ -225,9 +228,9 @@ void ImTui_ImplText_RenderDrawData(ImDrawData * drawData, ImTui::TScreen * scree + if (xx < clip_rect.x || xx >= clip_rect.z || yy < clip_rect.y || yy >= clip_rect.w) { + } else { + auto & cell = screen->data[yy*screen->nx + xx]; +- cell &= 0xFF000000; +- cell |= (col0 & 0xff000000) >> 24; +- cell |= ((ImTui::TCell)(rgbToAnsi256(col0, false)) << 16); ++ cell &= ~0xFFFFFFFFFFul; ++ cell |= (((uint64_t)rgbToAnsi256(col0, false)) << 32) ; // set color ++ cell |= (col0 >> 24) | (col1 >> 24 << 8) | (col2 >> 24 << 16); // set character + } + i += 3; + } else { +diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt +index b9c1f67..9d7f201 100644 +--- a/third-party/CMakeLists.txt ++++ b/third-party/CMakeLists.txt +@@ -18,6 +18,7 @@ add_library(imgui-for-imtui STATIC + imgui/imgui/imgui_demo.cpp + imgui/imgui/imgui_widgets.cpp + imgui/imgui/imgui_tables.cpp ++ imgui/imgui/misc/cpp/imgui_stdlib.cpp + ) + + target_include_directories(imgui-for-imtui INTERFACE +Submodule third-party/imgui/imgui contains modified content +diff --git a/third-party/imgui/imgui/imgui.cpp b/third-party/imgui/imgui/imgui.cpp +index c3f7d376..50c8acaf 100644 +--- a/third-party/imgui/imgui/imgui.cpp ++++ b/third-party/imgui/imgui/imgui.cpp +@@ -8457,9 +8457,9 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) + ImVec2 ref_pos = NavCalcPreferredRefPos(); + ImRect r_avoid; + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) +- r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); ++ r_avoid = ImRect(ref_pos.x - 1, ref_pos.y - 1, ref_pos.x + 1, ref_pos.y + 1); + else +- r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. ++ r_avoid = ImRect(ref_pos.x - 1, ref_pos.y - 1, ref_pos.x + 1 * sc, ref_pos.y + 1 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. + return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); + } + IM_ASSERT(0); +diff --git a/third-party/imgui/imgui/imgui_draw.cpp b/third-party/imgui/imgui/imgui_draw.cpp +index dc719e68..295fb2da 100644 +--- a/third-party/imgui/imgui/imgui_draw.cpp ++++ b/third-party/imgui/imgui/imgui_draw.cpp +@@ -3490,7 +3490,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col + } + + col &= 0x00FFFFFF; +- col |= (c << 24); ++ ImU32 c0 = (c & 0xFF) << 24; ++ ImU32 c1 = ((c >> 8) & 0xFF) << 24; ++ ImU32 c2 = ((c >> 16) & 0xFF) << 24; + // Support for untinted glyphs + ImU32 glyph_col = glyph->Colored ? col_untinted : col; + +@@ -3498,9 +3500,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col + { + idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); + idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); +- vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; +- vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; +- vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; ++ vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col | c0; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; ++ vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col | c1; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; ++ vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col | c2; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; + vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; + vtx_write += 4; + vtx_current_idx += 4; +diff --git a/third-party/imgui/imgui/imgui_widgets.cpp b/third-party/imgui/imgui/imgui_widgets.cpp +index 85a1d6bc..f10d9c55 100644 +--- a/third-party/imgui/imgui/imgui_widgets.cpp ++++ b/third-party/imgui/imgui/imgui_widgets.cpp +@@ -678,7 +678,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags + pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; + //ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y + style.FramePadding.y * 2.0f); +- ++ size.x += 4; + const ImRect bb(pos, pos + size); + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, id)) +@@ -1073,7 +1073,7 @@ bool ImGui::Checkbox(const char* label, bool* v) + + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; +- const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); ++ const ImRect total_bb(pos, pos + ImVec2(square_sz * 3 + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id)) + { +@@ -1089,7 +1089,7 @@ bool ImGui::Checkbox(const char* label, bool* v) + MarkItemEdited(id); + } + +- const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); ++ const ImRect check_bb(pos, pos + ImVec2(square_sz * 3, square_sz)); + RenderNavHighlight(total_bb, id); + RenderFrame(check_bb.Min, check_bb.Max + ImVec2(0.5f, 0.0f), GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); +@@ -1101,13 +1101,16 @@ bool ImGui::Checkbox(const char* label, bool* v) + //ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f))); + //window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); + const float pad = 0.5f; +- RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f, "O"); ++ RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f, "[O]"); + } + else if (*v) + { + //const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); + const float pad = 0.5f; +- RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); ++ RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f, "[X]"); ++ } else { ++ const float pad = 0.5f; ++ RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f, "[ ]"); + } + + ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); diff --git a/deps/third-repo/packages/i/imtui/xmake.lua b/deps/third-repo/packages/i/imtui/xmake.lua new file mode 100644 index 000000000..1bcc5fd76 --- /dev/null +++ b/deps/third-repo/packages/i/imtui/xmake.lua @@ -0,0 +1,32 @@ +package("imtui") + add_urls("https://github.com/ggerganov/imtui.git") + + set_policy("package.install_always", true) + + add_versions("v1.0.5", "9f39c3e090c9b1e15557eac38a2f4389462f59df") + add_patches("v1.0.5", "fix-size-cjk-and-mouse.patch") + + -- WARN: xmake cannot distinguish ncursesw and ncurses, this may cause problem on other systems + add_deps("cmake") + add_deps("ncurses 6.1") + + add_includedirs("include", "include/imgui", "include/imgui-for-imtui/", "include/imgui-for-imtui/imgui", "include/imtui", {public = true}) + + -- imtui, imtui-ncurses and imgui-for-imtui + on_install(function (package) + local configs = {} + table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) + table.insert(configs, "-DIMTUI_STANDALONE=ON") + table.insert(configs, "-DIMTUI_SUPPORT_NCURSES=ON") + table.insert(configs, "-DIMTUI_BUILD_EXAMPLES=OFF") + print(package:dep("ncurses"):installdir("include")) + local cxflags = {} + table.insert(cxflags, "-I" .. package:dep("ncurses"):installdir("include") .. "/ncursesw") + import("package.tools.cmake").install(package, configs, {cxflags = cxflags}) + print(package:cachedir() .. "/source/imtui/") + print(package:installdir()) + os.cp("third-party/imgui/imgui/misc/cpp/imgui_stdlib.h", package:installdir("include/imgui-for-imtui/imgui/misc/cpp")) + os.cp("third-party/imgui/imgui/imgui_internal.h", package:installdir("include/imgui-for-imtui/imgui")) + os.cp("third-party/imgui/imgui/imstb_textedit.h", package:installdir("include/imgui-for-imtui/imgui")) + end) +package_end() diff --git a/deps/third-repo/packages/p/polyhook_2/xmake.lua b/deps/third-repo/packages/p/polyhook_2/xmake.lua index 7cccc343c..5704f7952 100644 --- a/deps/third-repo/packages/p/polyhook_2/xmake.lua +++ b/deps/third-repo/packages/p/polyhook_2/xmake.lua @@ -5,11 +5,19 @@ package("polyhook_2") on_install(function (package) local configs = {} - table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=OFF") - + if is_plat("linux") then + table.insert(configs, "-DZYDIS_INCLUDE_DIR=" .. package:dep("zydis"):installdir("include")) + table.insert(configs, "-DZYCORE_INCLUDE_DIR=" .. package:dep("zycore"):installdir("include")) + table.insert(configs, "-DZYCORE_LIBRARY=" .. package:dep("zycore"):installdir("libzycore.a")) + table.insert(configs, "-DZYDIS_LIBRARY=" .. package:dep("zydis"):installdir("libzydis.a")) + end import("package.tools.cmake").install(package, configs, { packagedeps = { "zycore", "zydis" } }) - + print(package:get("links")) + package:add("links", "PolyHook_2") + package:add("links", "asmtk") + package:add("links", "asmjit") + print(package:get("links")) end) package_end() \ No newline at end of file diff --git a/deps/third-repo/packages/z/zigcc/xmake.lua b/deps/third-repo/packages/z/zigcc/xmake.lua new file mode 100644 index 000000000..04e0a6c60 --- /dev/null +++ b/deps/third-repo/packages/z/zigcc/xmake.lua @@ -0,0 +1,34 @@ +package("zigcc") + set_kind("toolchain") + set_homepage("https://www.ziglang.org/") + set_description("Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.") + + on_source(function (package) + local jsonfile = os.tmpfile() .. ".json" + import("net.http") + http.download("https://ziglang.org/download/index.json", jsonfile) + import("core.base.json") + local json = json.loadfile(jsonfile)["master"] + -- print(json) + if is_host("windows") then + -- print("Windows: " .. json["x86_64-windows"]["tarball"]) + package:set("urls", json["x86_64-windows"]["tarball"]) + package:add("versions", "v0.13.0", json["x86_64-windows"]["shasum"]) + elseif is_host("linux") then + -- print("Linux: " .. json["x86_64-linux"]["tarball"]) + package:set("urls", json["x86_64-linux"]["tarball"]) + package:add("versions", "v0.13.0", json["x86_64-linux"]["shasum"]) + else + print("Unsupported platform!") + end + end) + + on_install("@macosx", "@linux", "@windows", "@msys", "@bsd", function (package) + print("Install?" .. package:installdir()) + os.cp("*", package:installdir()) + package:addenv("PATH", package:installdir()) + end) + + on_test(function (package) + os.vrun("zig version") + end) \ No newline at end of file diff --git a/tools/buildscripts/build.py b/tools/buildscripts/build.py index 3b193dc5e..94430f3db 100755 --- a/tools/buildscripts/build.py +++ b/tools/buildscripts/build.py @@ -6,6 +6,7 @@ import subprocess import argparse from datetime import datetime +from template import EnhancedTemplate # Change dir to repo root os.chdir(os.path.join(os.path.dirname(__file__), '..', '..')) @@ -56,6 +57,20 @@ def release_commit(args): github_output('release_tag', version) +def get_os_files(os): + if os == 'win64': + return { + "dev": ['ue4ss.pdb','ue4ss.dll','dwmapi.dll'], + "release": ['ue4ss.dll', 'dwmapi.dll'] + } + elif os == 'linux64': + return { + "dev": ['libue4ss.so', 'libue4ss.so.debug'], + "release": ['libue4ss.so'] + } + else: + raise Exception(f'unknown os: {os}') + def package(args): is_experimental = args.e @@ -87,7 +102,7 @@ def make_staging_dirs(is_dev_release: bool): # Builds a release version of /assets by copying the directory and then # removing and disabling dev-only settings and files exclude_files = [ - 'Mods/shared/Types.lua', + # 'Mods/shared/Types.lua', 'UE4SS_Signatures', 'VTableLayoutTemplates', 'MemberVarLayoutTemplates', @@ -104,6 +119,15 @@ def make_staging_dirs(is_dev_release: bool): 'GUIUFunctionCaller': 0, } + settings_to_modify_in_dev = { + 'GuiConsoleVisible': 1, + 'ConsoleEnabled': 1, + 'EnableHotReloadSystem': 1, + 'IgnoreEngineAndCoreUObject': 0, + 'MaxMemoryUsageDuringAssetLoading': 85, + 'GUIUFunctionCaller': 1, + } + change_modstxt = { 'LineTraceMod': 0, } @@ -129,19 +153,25 @@ def make_staging_dirs(is_dev_release: bool): except: shutil.rmtree(path) - # Change UE4SS-settings.ini - config_path = os.path.join(staging_dir, 'UE4SS-settings.ini') + # change UE4SS-settings.ini + config_path_dev = os.path.join(staging_dev, 'UE4SS-settings.ini') + config_path_release = os.path.join(staging_release, 'UE4SS-settings.ini') + + with open(config_path_release, mode='r', encoding='utf-8-sig') as file: + content_template = EnhancedTemplate(file.read()) + + # apply settings + combined_variables = {**settings_to_modify_in_release, "release_type": "release", "os": args.os} + content_release = content_template.substitute(combined_variables) - if not is_dev_release: - with open(config_path, mode='r', encoding='utf-8-sig') as file: - content = file.read() + combined_variables = {**settings_to_modify_in_dev, "release_type": "dev", "os": args.os} + content_dev = content_template.substitute(combined_variables) - for key, value in settings_to_modify_in_release.items(): - pattern = rf'(^{key}\s*=).*?$' - content = re.sub(pattern, rf'\1 {value}', content, flags=re.MULTILINE) + with open(config_path_release, mode='w', encoding='utf-8-sig') as file: + file.write(content_release) - with open(config_path, mode='w', encoding='utf-8-sig') as file: - file.write(content) + with open(config_path_dev, mode='w', encoding='utf-8-sig') as file: + file.write(content_dev) # Change Mods/mods.txt mods_path = os.path.join(staging_dir, 'Mods/mods.txt') @@ -168,83 +198,35 @@ def make_staging_dirs(is_dev_release: bool): os.makedirs(os.path.join(staging_dir, 'Mods', mod_name, 'config'), exist_ok=True) def package_release(is_dev_release: bool): - version = subprocess.check_output(['git', 'describe', '--tags']).decode('utf-8').strip() + try: + version = subprocess.check_output(['git', 'describe', '--tags']).decode('utf-8').strip() + except: + version = '0.0.0' if is_dev_release: + os_files = get_os_files(args.os)['dev'] main_zip_name = f'zDEV-UE4SS_{version}' staging_dir = staging_dev else: + os_files = get_os_files(args.os)['release'] main_zip_name = f'UE4SS_{version}' staging_dir = staging_release - - ue4ss_dll_path = '' - ue4ss_pdb_path = '' - dwmapi_dll_path = '' - # CPP mods paths - cpp_mods_paths = {mod: '' for mod in CPPMods if is_dev_release or CPPMods[mod]['include_in_release']} - + target_paths = [] scan_start_dir = '.' if str(args.d) != 'None': scan_start_dir = str(args.d) for root, dirs, files in os.walk(scan_start_dir): for file in files: - if file.lower() == "ue4ss.dll": - ue4ss_dll_path = os.path.join(root, file) - if file.lower() == "ue4ss.pdb": - ue4ss_pdb_path = os.path.join(root, file) - if file.lower() == "dwmapi.dll": - dwmapi_dll_path = os.path.join(root, file) - # Find CPP Mod DLLs - for mod_name in cpp_mods_paths: - if file.lower() == mod_name.lower() + '.dll': - cpp_mods_paths[mod_name] = os.path.join(root, file) - - # Create the ue4ss folder in staging_dir - ue4ss_dir = os.path.join(staging_dir, 'ue4ss') - os.makedirs(ue4ss_dir, exist_ok=True) - - # Move all files from assets folder to the ue4ss folder except dwmapi.dll and Mods folder - for root, _, files in os.walk('assets'): - for file in files: - if file.lower() != 'dwmapi.dll' and not os.path.join(root, file).startswith(os.path.join('assets', 'Mods')): - src_path = os.path.join(root, file) - dst_path = os.path.join(ue4ss_dir, os.path.relpath(src_path, 'assets')) - os.makedirs(os.path.dirname(dst_path), exist_ok=True) - shutil.copy(src_path, dst_path) - - # Copy the Mods folder separately to avoid nesting - mods_src = os.path.join('assets', 'Mods') - mods_dst = os.path.join(ue4ss_dir, 'Mods') - shutil.copytree(mods_src, mods_dst, dirs_exist_ok=True) - - # Main dll and pdb - shutil.copy(ue4ss_dll_path, ue4ss_dir) - - # CPP mods - for mod_name, dll_path in cpp_mods_paths.items(): - mod_dir = os.path.join(ue4ss_dir, 'Mods', mod_name, 'dlls') - os.makedirs(mod_dir, exist_ok=True) - shutil.copy(dll_path, os.path.join(mod_dir, 'main.dll')) + if file.lower() in os_files: + target_paths.append(os.path.join(root, file)) - # Create config folder if needed - if is_dev_release or CPPMods[mod_name]['include_in_release']: - if CPPMods[mod_name]['create_config']: - os.makedirs(os.path.join(ue4ss_dir, 'Mods', mod_name, 'config'), exist_ok=True) - - # Proxy - shutil.copy(dwmapi_dll_path, staging_dir) + for target in target_paths: + shutil.copy(target, staging_dir) if is_dev_release: - shutil.copy(ue4ss_pdb_path, ue4ss_dir) - if os.path.exists(os.path.join(scan_start_dir, 'docs')): - shutil.copytree('docs', os.path.join(ue4ss_dir, 'Docs')) - - # Move remaining files to the ue4ss dir - dont_move = ['dwmapi.dll', 'docs', 'ue4ss'] - for file in os.listdir(staging_dir): - if file.lower() not in dont_move: - shutil.move(os.path.join(staging_dir, file), os.path.join(ue4ss_dir, file)) + if os.path.exists(os.path.join(staging_dir, 'docs')): + shutil.copytree('docs', os.path.join(staging_dir, 'docs')) output = os.path.join(release_output, main_zip_name) shutil.make_archive(output, 'zip', staging_dir) @@ -281,16 +263,16 @@ def package_release(is_dev_release: bool): def main(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='command', required=True) - package_parser = subparsers.add_parser('package') package_parser.add_argument('-e', action='store_true') package_parser.add_argument('-d', action='store') - + # -s for operating system + package_parser.add_argument('-s', '--os', action='store', default='win64', choices=['win64', 'linux64']) release_commit_parser = subparsers.add_parser('release_commit') release_commit_parser.add_argument('username', nargs='?') - args = parser.parse_args() commands[args.command](args) + if __name__ == "__main__": main() diff --git a/tools/buildscripts/template.py b/tools/buildscripts/template.py new file mode 100644 index 000000000..0635ade4e --- /dev/null +++ b/tools/buildscripts/template.py @@ -0,0 +1,298 @@ +import string +import ast +import operator + +class SafeEvaluator: + """A simple and safe expression evaluator supporting basic operations.""" + ALLOWED_OPERATORS = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.Mod: operator.mod, + ast.Pow: operator.pow, + ast.Eq: operator.eq, + ast.NotEq: operator.ne, + ast.Lt: operator.lt, + ast.LtE: operator.le, + ast.Gt: operator.gt, + ast.GtE: operator.ge, + ast.And: operator.and_, + ast.Or: operator.or_, + ast.Not: operator.not_, + } + + def evaluate(self, expression, variables): + """Evaluate an expression safely.""" + try: + tree = ast.parse(expression, mode='eval') + return self._eval(tree.body, variables) + except: + return False + + def _eval(self, node, variables): + if isinstance(node, ast.Expression): + return self._eval(node.body, variables) + elif isinstance(node, ast.BoolOp): + op = self.ALLOWED_OPERATORS[type(node.op)] + return op(*[self._eval(value, variables) for value in node.values]) + elif isinstance(node, ast.BinOp): + return self.ALLOWED_OPERATORS[type(node.op)](self._eval(node.left, variables), self._eval(node.right, variables)) + elif isinstance(node, ast.UnaryOp): + return self.ALLOWED_OPERATORS[type(node.op)](self._eval(node.operand, variables)) + elif isinstance(node, ast.Compare): + left = self._eval(node.left, variables) + right = self._eval(node.comparators[0], variables) + return self.ALLOWED_OPERATORS[type(node.ops[0])](left, right) + elif isinstance(node, ast.Num): + return node.n + elif isinstance(node, ast.Name): + return variables.get(node.id, False) + elif isinstance(node, ast.Constant): + return node.value + else: + return False +import re +class EnhancedTemplate(string.Template): + delimiter = '$' + + def substitute(self, mapping): + template = self.template + evaluator = SafeEvaluator() + processed_template = self._process_conditionals(template, mapping, evaluator) + return string.Template(processed_template).substitute(mapping) + + def _active(self, cond): + for c, _taken, _kw in cond: + if c == False: + return False + return True + + def _process_conditionals(self, template, mapping, evaluator): + # Improved regex to include elsif and else + pattern = re.compile(r'\$\{(if|elsif|else)(.*?)}\r?\n?|\$\{endif\}\r?\n?') + blocks = [] + cursor = 0 + skip_block = False + conditions = [] + for match in pattern.finditer(template): + start, end = match.span() + keyword, condition = match.groups() + + # Add non-conditional text + if cursor < start: + if self._active(conditions): + blocks.append(template[cursor:start]) + + if keyword == 'if': + cursor = end + cond = evaluator.evaluate(condition.strip(), mapping) + # active, taken + conditions.append((cond, cond, 'if')) + elif keyword == 'elsif': + if len(conditions) == 0: + raise ValueError("elseif without a matching if") + c, taken, kw = conditions[-1] + if kw == 'else': + raise ValueError("elseif after else") + if not taken: + cond = evaluator.evaluate(condition.strip(), mapping) + conditions[-1] = (cond, cond, 'elsif') + else: + conditions[-1] = (False, True, 'elsif') + elif keyword == 'else': + if len(conditions) == 0: + raise ValueError("else without a matching if") + c, taken, kw = conditions[-1] + if not taken: + conditions[-1] = (True, True, 'else') + else: + conditions[-1] = (False, True, 'else') + cursor = end + continue + elif match.group(0).strip() == '${endif}': + if len(conditions) == 0: + raise ValueError("endif without a matching if") + conditions.pop() + cursor = end + continue + + if self._active(conditions): + blocks.append(template[end:match.end()]) + + cursor = match.end() + + if len(conditions) > 0: + print(conditions) + raise ValueError("Unmatched if") + # Add any remaining template content + if cursor < len(template): + blocks.append(template[cursor:]) + + return ''.join(blocks) + + + +def test(): + test_cases = [ + # Simple if + {"template": "Test ${if True}passed${endif}.", "variables": {}, "expected": "Test passed."}, + # if with variable + {"template": "${if condition}Condition met.${endif}", "variables": {"condition": True}, "expected": "Condition met."}, + # if-else (with elsif used as else) + {"template": "${if condition}True.${elsif True}False.${endif}", "variables": {"condition": False}, "expected": "False."}, + # Unmatched condition + {"template": "Always visible ${if condition}Condition met.${endif}", "variables": {"condition": False}, "expected": "Always visible "}, + # Complex condition + {"template": "${if num > 10}Greater.${endif}", "variables": {"num": 15}, "expected": "Greater."}, + # Nested conditions not applicable, adding more varied conditions instead + # Multiple conditions with and + {"template": "${if condition1 and condition2}Both true.${endif}", "variables": {"condition1": True, "condition2": True}, "expected": "Both true."}, + # Multiple conditions with or + {"template": "${if condition1 or condition2}At least one true.${endif}", "variables": {"condition1": False, "condition2": True}, "expected": "At least one true."}, + # Inline if + {"template": "Inline ${if condition}passed${endif}, indeed.", "variables": {"condition": True}, "expected": "Inline passed, indeed."}, + # if-elsif-endif + {"template": "${if condition1}First.${elsif condition2}Second.${endif}", "variables": {"condition1": False, "condition2": True}, "expected": "Second."}, + # if-elsif-elsif-endif + {"template": "${if condition1}First.${elsif condition2}Second.${elsif condition3}Third.${endif}", "variables": {"condition1": False, "condition2": False, "condition3": True}, "expected": "Third."}, + # if-false + {"template": "${if condition}True.${endif}", "variables": {"condition": False}, "expected": ""}, + # Expression with variables + {"template": "${if var1 + var2 == 10}Sum is 10.${endif}", "variables": {"var1": 3, "var2": 7}, "expected": "Sum is 10."}, + # Inequality + {"template": "${if var != 5}Not five.${endif}", "variables": {"var": 3}, "expected": "Not five."}, + # Greater than or equal + {"template": "${if var >= 5}Greater or equal.${endif}", "variables": {"var": 5}, "expected": "Greater or equal."}, + # Less than + {"template": "${if var < 5}Less.${endif}", "variables": {"var": 3}, "expected": "Less."}, + # Not condition + {"template": "${if not condition}Not True.${endif}", "variables": {"condition": False}, "expected": "Not True."}, + # And + Or + {"template": "${if condition1 and (condition2 or condition3)}Complex condition met.${endif}", "variables": {"condition1": True, "condition2": False, "condition3": True}, "expected": "Complex condition met."}, + # Inline with text around + {"template": "Start ${if condition}middle${endif} end.", "variables": {"condition": True}, "expected": "Start middle end."}, + # Using else (via elsif as a workaround) + {"template": "${if condition}First.${elsif True}Else.${endif}", "variables": {"condition": False}, "expected": "Else."}, + # Multiple elseif + {"template": "${if condition1}One.${elsif condition2}Two.${elsif condition3}Three.${endif}", "variables": {"condition1": False, "condition2": False, "condition3": True}, "expected": "Three."}, + # Fallback else + {"template": "${if condition1}One.${elsif condition2}Two.${elsif True}Default.${endif}", "variables": {"condition1": False, "condition2": False}, "expected": "Default."}, + { + "template": """Before conditional block. +${if condition1} +Line 1: Condition 1 is true. +${elsif condition2} +Line 2: Condition 2 is true. + Further line still within Condition 2 block. +${elsif condition3} +Line 3: Condition 3 is true. +${else} +None of the conditions were met. +${endif} +After conditional block.""", + "variables": {"condition1": False, "condition2": True, "condition3": False}, + "expected": """Before conditional block. +Line 2: Condition 2 is true. + Further line still within Condition 2 block. +After conditional block.""" + }, + { + "template": """ +${if condition1} + ${if conditionA}A is true.${else}A is false.${endif} +${elsif condition2} + ${if conditionB} + ${if conditionX}B and X are true.${elsif True}B is true, X is default.${endif} + ${else} + B is false. + ${endif} +${else} + ${if conditionC} + C is true. + ${else} + ${if conditionY}Y is true, but C is false.${else}Default case.${endif} + ${endif} +${endif} +""", + "variables": { + "condition1": False, + "condition2": True, + "conditionA": False, + "conditionB": True, + "conditionC": False, + "conditionX": False, + "conditionY": False + }, + "expected": """ + B is true, X is default.""" + }, + { + "template": """ +${if condition1} + Level 1: Condition 1 is true. + ${if conditionA} + Level 2: Condition A is true. + ${if conditionX}Level 3: X.${elsif conditionY}Level 3: Y.${else}Level 3: Neither X nor Y.${endif} + ${elsif conditionB} + Level 2: Condition B is true. + ${if conditionZ}Level 3: Z is true.${else}Level 3: Z is false.${endif} + ${else} + Level 2: Neither A nor B is true. + ${endif} +${elsif condition2} + Level 1: Condition 2 is true. + ${if conditionC} + Level 2: Condition C is true. + ${else} + Level 2: Condition C is false. + ${if conditionW}Level 3: W.${else}Level 3: Not W.${endif} + ${endif} +${else} + Level 1: Neither condition 1 nor 2 is true. + ${if conditionD} + Level 2: D is true. Default path. + ${else} + Level 2: D is false. Ultimate fallback. + ${endif} +${endif} +""", + "variables": { + "condition1": False, + "condition2": True, + "conditionA": False, + "conditionB": False, + "conditionC": False, + "conditionX": False, + "conditionY": False, + "conditionZ": False, + "conditionW": True, + "conditionD": False + }, + "expected": """ + Level 1: Condition 2 is true. + Level 2: Condition C is false. + Level 3: W.""" + } + ] + def normalize_space(s): + # Replace multiple spaces with a single space + s = re.sub(r'[ ]+', ' ', s) + # Replace multiple newlines with a single newline + s = re.sub(r'(\n\s*)+\n', '\n', s) + # Trim leading and trailing whitespace + s = s.strip() + return s + def compare_strings(s1, s2): + normalized_s1 = normalize_space(s1) + normalized_s2 = normalize_space(s2) + + return normalized_s1 == normalized_s2 + for i, test in enumerate(test_cases, 1): + template = EnhancedTemplate(test["template"]) + output = template.substitute(test["variables"]) + assert compare_strings(output.strip(), test["expected"].strip()), f"Test {i} failed. \n\tTemplate: '{test['template']}'\n\tInput: {test['variables']}\n\tExpected: '{test['expected']}',\n\tGot: '{output}'" + print(f"Test {i} passed.") + +if __name__ == "__main__": + test() \ No newline at end of file diff --git a/tools/xmakescripts/build_configs.lua b/tools/xmakescripts/build_configs.lua index d715260fd..21c340606 100644 --- a/tools/xmakescripts/build_configs.lua +++ b/tools/xmakescripts/build_configs.lua @@ -2,6 +2,228 @@ -- It helps people with old mod templates upgrade to the new template. local build_configs = build_configs or {} +local gameDefines = { "UE_GAME" } + +TARGET_TYPES = { + ["Game"] = { + ["defines"] = { + table.unpack(gameDefines) + } + }, + ["CasePreserving"] = { + ["defines"] = { + "WITH_CASE_PRESERVING_NAME", + table.unpack(gameDefines) + } + } +} + +CONFIG_TYPES = { + ["Dev"] = { + ["symbols"] = {"debug"}, + ["defines"] = { + "UE_BUILD_DEVELOPMENT", + "STATS" + }, + ["optimize"] = {"none"} + }, + ["Debug"] = { + ["symbols"] = {"debug"}, + ["defines"] = { + "UE_BUILD_DEBUG" + }, + ["optimize"] = {"none"} + }, + ["Shipping"] = { + ["symbols"] = {"debug"}, + ["defines"] = { + "UE_BUILD_SHIPPING" + }, + ["optimize"] = {"fastest"} + }, + ["Test"] = { + ["symbols"] = {"debug"}, + ["defines"] = { + "UE_BUILD_TEST", + "STATS" + }, + ["optimize"] = {"none"} + } +} + +PLATFORM_TYPES = { + ["Win64"] = { + ["defines"] = { + "PLATFORM_WINDOWS", + "PLATFORM_MICROSOFT", + "OVERRIDE_PLATFORM_HEADER_NAME=Windows", + "UBT_COMPILED_PLATFORM=Win64", + "UNICODE", + "_UNICODE", + "DLLEXT=.dll" + }, + ["cxflags"] = { + "clang_cl::-gcodeview" + } + }, + ["Linux"] = { + ["defines"] = { + "PLATFORM_LINUX", + "PLATFORM_UNIX", + "LINUX", + "OVERRIDE_PLATFORM_HEADER_NAME=Linux", + "UBT_COMPILED_PLATFORM=Linux", + "printf_s=printf", + "DLLEXT=.so" + }, + ["cxflags"] = { + "clang::-fno-delete-null-pointer-checks" + } + } +} + +CLANG_COMPILE_OPTIONS = { + ["cxflags"] = { + "-g", + "-fcolor-diagnostics", + "-Wno-unknown-pragmas", + "-Wno-unused-parameter", + "-fms-extensions", + "-Wignored-attributes", + "-fPIC" + }, + ["ldflags"] = { + "-g" + }, + ["shflags"] = { + "-g" + } +} + +GNU_COMPILE_OPTIONS = { + ["cxflags"] = { + "-fms-extensions" + } +} + +MSVC_COMPILE_OPTIONS = { + ["cxflags"] = { + "/MP", + "/W3", + "/wd4005", + "/wd4251", + "/wd4068", + "/Zc:inline", + "/Zc:strictStrings", + "/Zc:preprocessor" + }, + ["ldflags"] = { + "/DEBUG:FULL" + }, + ["shflags"] = { + "/DEBUG:FULL" + } +} + +-- Get target types +function get_target_types() + return TARGET_TYPES +end + +-- Get config types +function get_config_types() + return CONFIG_TYPES +end + +-- Get platform types +function get_platform_types() + return PLATFORM_TYPES +end + +-- Get clang compile options +function get_clang_compile_options() + return CLANG_COMPILE_OPTIONS +end + +-- Get gnu compile options +function get_gnu_compile_options() + return GNU_COMPILE_OPTIONS +end + +-- Get msvc compile options +function get_msvc_compile_options() + return MSVC_COMPILE_OPTIONS +end + +-- Apply targe options +function apply_target_options(self, target, options) + for option, values in pairs(options) do + target:add(option, values, { public = true }) + end +end + +-- Returns a list of supported compilation modes +-- CasePreserving__Shipping_Win64, Game_Debug_Win64, etc. +function get_compilation_modes() + local comp_modes = {} + + for target_type, _ in pairs(TARGET_TYPES) do + for config_type, _ in pairs(CONFIG_TYPES) do + for platform_type, _ in pairs(PLATFORM_TYPES) do + local config_name = target_type .. "__" .. config_type .. "__" .. platform_type + table.insert(comp_modes, config_name) + end + end + end + + return comp_modes +end + +-- Get unreal rules +function get_unreal_rules() + local unreal_rules = {} + + for _, config_name in ipairs(get_compilation_modes()) do + local rule_name = "mode." .. config_name + table.insert(unreal_rules, rule_name) + + rule(rule_name) + rule_end() + end + + return unreal_rules +end + +-- Parse mode string into modes +function mode_string_to_modes(self, str) + local modes = {} + for t in string.gmatch(str, "(%w+)") do + table.insert(modes, t) + end + + return { + ["target"] = modes[1], + ["config"] = modes[2], + ["platform"] = modes[3] + } +end + +-- Apply compiler options +function apply_compiler_options(self, target) + for option, values in pairs(self:get_gnu_compile_options()) do + target:add(option, values, { tools = { "gcc", "ld" } }) + end + + for option, values in pairs(self:get_clang_compile_options()) do + target:add(option, values, { tools = { "clang", "lld" } }) + end + + for option, values in pairs(self:get_msvc_compile_options()) do + target:add(option, values, { tools = { "clang_cl", "cl", "link" } }) + end +end + +-- Run on configure step for each target that wants unreal rules function config(self, target) if target:name() == "Unreal" then _warn_unreal_submod_outdated() @@ -26,13 +248,14 @@ function clean_output_dir(self, target) end end -function _warn_mod_template_outdated(target) - print("SUGGESTED TEMPLATE:") - print(format('target("%s")', target:name())) - print(' add_rules("ue4ss.mod")') - print(' add_includedirs(".")') - print(' add_files("*.cpp")') - raise("Your mod's xmake.lua file needs updating.") +-- Get runtime for current mode +function get_mode_runtimes() + local is_debug = is_mode_debug() + if is_plat("windows") then + return is_debug and "MDd" or "MD" + end + -- we don't care about runtime on linux + return "" end function _warn_unreal_submod_outdated() diff --git a/tools/xmakescripts/modules/mode_builder.lua b/tools/xmakescripts/modules/mode_builder.lua index a75792472..da8dc2c8a 100644 --- a/tools/xmakescripts/modules/mode_builder.lua +++ b/tools/xmakescripts/modules/mode_builder.lua @@ -15,4 +15,13 @@ function apply_compiler_options(target, opts, tools) for option, values in pairs(opts) do target:add(option, values, { tools = tools }) end +end + +--- Applies build options to a target. +---@param target any xmake target +---@param opts table Map of xmake flag types to flags. Ex. ["ldflags"] = {"/DEBUG:FULL"} +function apply_compiler_options_all(target, opts) + for option, values in pairs(opts) do + target:add(option, values, { force = true }) + end end \ No newline at end of file diff --git a/tools/xmakescripts/rules/build_rules.lua b/tools/xmakescripts/rules/build_rules.lua index 194d2849d..d21b41d1c 100644 --- a/tools/xmakescripts/rules/build_rules.lua +++ b/tools/xmakescripts/rules/build_rules.lua @@ -56,12 +56,31 @@ local PLATFORM_TYPES = { ["defines"] = { "PLATFORM_WINDOWS", "PLATFORM_MICROSOFT", + "WIN32", "OVERRIDE_PLATFORM_HEADER_NAME=Windows", "UBT_COMPILED_PLATFORM=Win64", "UNICODE", - "_UNICODE" + "_UNICODE", + "DLLEXT=.dll" }, - + ["cxflags"] = { + "clang::-gcodeview" + } + }, + ["Linux"] = { + ["defines"] = { + "PLATFORM_LINUX", + "PLATFORM_UNIX", + "LINUX", + "OVERRIDE_PLATFORM_HEADER_NAME=Linux", + "UBT_COMPILED_PLATFORM=Linux", + "printf_s=printf", + "DLLEXT=.so" + }, + ["cxflags"] = { + "clang::-fno-delete-null-pointer-checks", + "clang::-gdwarf" + } } } @@ -72,12 +91,12 @@ local PLATFORM_TYPES = { local CLANG_COMPILE_OPTIONS = { ["cxflags"] = { "-g", - "-gcodeview", "-fcolor-diagnostics", "-Wno-unknown-pragmas", "-Wno-unused-parameter", "-fms-extensions", - "-Wignored-attributes" + "-Wignored-attributes", + "-fPIC" }, ["ldflags"] = { "-g" @@ -87,6 +106,19 @@ local CLANG_COMPILE_OPTIONS = { } } +-- option to use libc++ +option("libcxx") + set_default(false) + set_showmenu(true) + set_description("Use libc++ instead of libstdc++") + +if has_config("libcxx") then + table.insert(CLANG_COMPILE_OPTIONS["cxflags"], "-stdlib=libc++") + table.insert(CLANG_COMPILE_OPTIONS["cxflags"], "-fexperimental-library") + table.insert(CLANG_COMPILE_OPTIONS["ldflags"], "-static-libstdc++") + table.insert(CLANG_COMPILE_OPTIONS["ldflags"], "-l:libc++abi.a") +end + local GNU_COMPILE_OPTIONS = { ["cxflags"] = { "-fms-extensions" @@ -176,7 +208,7 @@ function get_mode_runtimes() return is_mode_debug() and "MDd" or "MD" end - return {} + return "" end --- This local function wraps the global get_mode_runtimes() function. We have to locally wrap the function @@ -198,6 +230,9 @@ rule("ue4ss.base") -- Compiler flags are set in this rule since unreal modes currently do not change any compiler flags. mode_builder.apply_compiler_options(target, GNU_COMPILE_OPTIONS, {"gcc", "ld"}) mode_builder.apply_compiler_options(target, CLANG_COMPILE_OPTIONS, {"clang", "lld"}) + if has_config("zig") then + mode_builder.apply_compiler_options_all(target, CLANG_COMPILE_OPTIONS) + end mode_builder.apply_compiler_options(target, MSVC_COMPILE_OPTIONS, { "clang_cl", "cl", "link" }) end) diff --git a/tools/zig/zig-ar b/tools/zig/zig-ar new file mode 100755 index 000000000..0cdd6fc93 --- /dev/null +++ b/tools/zig/zig-ar @@ -0,0 +1,2 @@ +#!/bin/bash +exec zig ar "$@" \ No newline at end of file diff --git a/tools/zig/zig-ar.bat b/tools/zig/zig-ar.bat new file mode 100755 index 000000000..266092cf6 --- /dev/null +++ b/tools/zig/zig-ar.bat @@ -0,0 +1 @@ +zig ar %* \ No newline at end of file diff --git a/tools/zig/zig-c++ b/tools/zig/zig-c++ new file mode 100755 index 000000000..454db2008 --- /dev/null +++ b/tools/zig/zig-c++ @@ -0,0 +1,2 @@ +#!/bin/bash +exec zig c++ -target x86_64-linux-gnu "$@" \ No newline at end of file diff --git a/tools/zig/zig-c++.bat b/tools/zig/zig-c++.bat new file mode 100755 index 000000000..01c4650a7 --- /dev/null +++ b/tools/zig/zig-c++.bat @@ -0,0 +1 @@ +zig c++ -target x86_64-linux-gnu %* \ No newline at end of file diff --git a/tools/zig/zig-cc b/tools/zig/zig-cc new file mode 100755 index 000000000..a6bfd0ffb --- /dev/null +++ b/tools/zig/zig-cc @@ -0,0 +1,2 @@ +#!/bin/bash +exec zig cc -target x86_64-linux-gnu "$@" \ No newline at end of file diff --git a/tools/zig/zig-cc-rust.bat b/tools/zig/zig-cc-rust.bat new file mode 100755 index 000000000..17197ca77 --- /dev/null +++ b/tools/zig/zig-cc-rust.bat @@ -0,0 +1,7 @@ +#!/bin/bash +# 2>nul || @goto :batch +exec zig cc -target x86_64-linux-gnu "$@" +goto :EOF + +:batch +zig cc -target x86_64-linux-gnu %* \ No newline at end of file diff --git a/tools/zig/zig-cc.bat b/tools/zig/zig-cc.bat new file mode 100755 index 000000000..4fcd3c418 --- /dev/null +++ b/tools/zig/zig-cc.bat @@ -0,0 +1 @@ +zig cc -target x86_64-linux-gnu %* \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 5c3ee1296..d8f8f9df4 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,4 +1,3 @@ -set_xmakever("2.9.2") -- We should use `get_config("ue4ssRoot")` instead of `os.projectdir()` or `$(projectdir)`. -- This is because os.projectdir() will return a higher parent dir -- when UE4SS is sub-moduled/`include("UE4SS")` in another xmake project. @@ -12,8 +11,8 @@ set_config("buildir", "Intermediates") -- /modules/rules/my_module.lua import("rules.my_module") add_moduledirs("tools/xmakescripts/modules") --- Load our rule files into the global scope. -includes("tools/xmakescripts/rules/**.lua") +-- Load the build_rules file into the global scope. +includes("tools/xmakescripts/rules/build_rules.lua") -- Generate the mode rules. local modes = generate_compilation_modes() @@ -33,13 +32,36 @@ add_rules("ue4ss.core") -- Restrict the compilation modes/configs. -- These restrictions are inherited upstream and downstream. -- Any project that `includes("UE4SS")` will inherit these global restrictions. -set_allowedplats("windows") -set_allowedarchs("x64") +set_allowedplats("windows", "linux") +set_allowedarchs("x64", "x86_64", "x86_64-unknown-linux-gnu") set_allowedmodes(modes) -if is_host("windows") then - set_defaultmode("Game__Shipping__Win64") -end +option("zig") + set_showmenu(true) + set_description("Use the Zig compiler.") + set_default(false) +option_end() + +toolchain("zigcross") + if is_host("windows") then + set_toolset("cc", os.scriptdir() .. "/tools/zig/zig-cc.bat") + set_toolset("cxx", os.scriptdir() .. "/tools/zig/zig-c++.bat") + set_toolset("ld", os.scriptdir() .. "/tools/zig/zig-c++.bat") + set_toolset("sh", os.scriptdir() .. "/tools/zig/zig-c++.bat") + set_toolset("ar", os.scriptdir() .. "/tools/zig/zig-ar.bat") + else + set_toolset("cc", os.scriptdir() .. "/tools/zig/zig-cc") + set_toolset("cxx", os.scriptdir() .. "/tools/zig/zig-c++") + set_toolset("ld", os.scriptdir() .. "/tools/zig/zig-c++") + set_toolset("sh", os.scriptdir() .. "/tools/zig/zig-c++") + set_toolset("ar", os.scriptdir() .. "/tools/zig/zig-ar") + end + add_cxflags("-fexperimental-library") + add_cxflags("-fno-delete-null-pointer-checks") + add_cxflags("-gdwarf") + add_cxflags("-fno-sanitize=undefined") -- can also use O2 to avoid this, but I'd prefer getting clear binary for now + add_shflags("-z", "lazy") +toolchain_end() -- Override the `xmake install` behavior for all targets. -- Targets can re-override the on_install() function to implement custom installation behavior. @@ -47,9 +69,32 @@ on_install(function(target) end) includes("deps") includes("UE4SS") -includes("UVTD") -includes("cppmods") +-- includes("UVTD") -- TODO: Remove this before the next release. It only exists to maintain backwards compat -- warnings for older mod templates. -set_config("scriptsRoot", path.join(os.scriptdir(), "tools/xmakescripts")) \ No newline at end of file +set_config("scriptsRoot", path.join(os.scriptdir(), "tools/xmakescripts")) + +-- Global initialization for UE4SS, run it in the topmost xmake.lua file. +function ue4ss_init() + if is_plat("linux") then + if has_config("zig") then + -- add_requires("zigcc", {system = false}) + -- set_toolchains("zigcross@zigcc", "rust") + set_toolchains("zigcross", "rust") + if is_host("windows") then + set_arch("x86_64-unknown-linux-gnu") + add_rcflags("-C", "linker=" .. os.scriptdir() .. "/tools/zig/zig-cc.bat", {force = true}) + end + else + set_toolchains("clang", "rust") + end + set_defaultmode("Game__Shipping__Linux") + end + + if is_plat("windows") then + set_defaultmode("Game__Shipping__Win64") + end +end + +ue4ss_init() \ No newline at end of file