Compare commits

..

No commits in common. "canary_experimental" and "0dc0f18" have entirely different histories.

515 changed files with 16358 additions and 20303 deletions

View File

@ -3,9 +3,6 @@ BasedOnStyle: Google
DerivePointerAlignment: false DerivePointerAlignment: false
PointerAlignment: Left PointerAlignment: Left
SortIncludes: true SortIncludes: true
KeepEmptyLines:
AtStartOfFile: false
InsertNewlineAtEOF: true
# Regroup causes unnecessary noise due to clang-format bug. # Regroup causes unnecessary noise due to clang-format bug.
IncludeBlocks: Preserve IncludeBlocks: Preserve

View File

@ -1,65 +0,0 @@
name: Create release
on:
workflow_call:
inputs:
os:
required: true
type: string
secrets:
RELEASE_TOKEN:
required: true
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@main
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GH_REPO: ${{ github.repository_owner }}/xenia-canary-releases
notes: ${{ github.event.head_commit.message }}
run: |
rm -rf **/*.pdb
ls -R
case ${{ inputs.os }} in
windows)
assets=xenia_canary_windows.zip
7z a $assets './xenia_canary_windows/*'
;;
linux)
for dir in xenia_canary_${{ inputs.os }}*; do
cd $dir
chmod +x xenia_canary
asset=${dir}.tar.gz
assets+=($asset)
tar -czvpf ../${asset} *
cd -
done
;;
esac
for asset in ${assets[@]}; do
if [ ! -f $asset ]; then
echo "::error::$asset doesn't exist!"
exit 1
fi
if [ $(stat -c%s $asset) -lt 100000 ]; then
echo "::error::$asset is too small!"
exit 1
fi
done
create_or_edit_release() {
local tag=$1
local title=$2
if gh release view $tag; then
gh release edit $tag -t $title -n "$notes"
gh release upload $tag ${assets[@]} --clobber
else
gh release create $tag ${assets[@]} --target 925ed98d5dce604b651027c36fb522dc1ff0fa55 -t $title -n "$notes"
fi
}
tag=${GITHUB_SHA::7}
create_or_edit_release $tag ${tag}_$GITHUB_REF_NAME
create_or_edit_release $GITHUB_REF_NAME $tag

View File

@ -7,14 +7,13 @@ on:
- '.drone.star' - '.drone.star'
- '.gitattributes' - '.gitattributes'
- '.gitignore' - '.gitignore'
- '.gdbinit'
- '.github/*' - '.github/*'
- '.github/workflows/Windows_build.yml' - '.github/workflows/Windows_build.yml'
- '.github/*_TEMPLATE/**' - '.github/*_TEMPLATE/**'
- '*.bat'
- '*.md' - '*.md'
- '*.ps1'
- '*.txt'
- '*.yml' - '*.yml'
- '*.txt'
- 'docs/**' - 'docs/**'
- 'src/**/*_windows.*' - 'src/**/*_windows.*'
- 'src/**/*_android.*' - 'src/**/*_android.*'
@ -26,14 +25,13 @@ on:
- '.drone.star' - '.drone.star'
- '.gitattributes' - '.gitattributes'
- '.gitignore' - '.gitignore'
- '.gdbinit'
- '.github/*' - '.github/*'
- '.github/workflows/Windows_build.yml' - '.github/workflows/Windows_build.yml'
- '.github/*_TEMPLATE/**' - '.github/*_TEMPLATE/**'
- '*.bat'
- '*.md' - '*.md'
- '*.ps1'
- '*.txt'
- '*.yml' - '*.yml'
- '*.txt'
- 'docs/**' - 'docs/**'
- 'src/**/*_windows.*' - 'src/**/*_windows.*'
- 'src/**/*_android.*' - 'src/**/*_android.*'
@ -44,117 +42,36 @@ on:
jobs: jobs:
lint: lint:
name: Lint name: Lint
runs-on: ubuntu-24.04 runs-on: windows-latest
outputs:
#LLVM_VERSION: ${{ steps.setup.outputs.LLVM_VERSION }}
UBUNTU_BASE: ${{ steps.setup.outputs.UBUNTU_BASE }}
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Setup - name: Check Clang-Format Version
id: setup run: clang-format --version
env:
LLVM_VERSION: 19 # Same as Windows
run: |
UBUNTU_BASE=$(lsb_release -cs)
#echo "LLVM_VERSION=$LLVM_VERSION" >> "$GITHUB_OUTPUT"
echo "UBUNTU_BASE=$UBUNTU_BASE" >> "$GITHUB_OUTPUT"
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo apt-add-repository "deb http://apt.llvm.org/${UBUNTU_BASE}/ llvm-toolchain-${UBUNTU_BASE}-$LLVM_VERSION main"
sudo apt-get -y update
sudo apt-get -y install clang-format-$LLVM_VERSION
- name: Lint - name: Lint
run: ./xenia-build.py lint --all run: ./xb lint --all
build-clang: build-linux:
name: Build (Clang ${{ matrix.LLVM_VERSION }}) name: Build (Linux) # runner.os can't be used here
needs: lint needs: lint
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
LLVM_VERSION: [20] # '${{ needs.lint.outputs.LLVM_VERSION }}'
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set environment variables
run: |
LLVM_VERSION=19
echo "LLVM_VERSION=$LLVM_VERSION" >> $GITHUB_ENV
echo "UBUNTU_BASE=jammy" >> $GITHUB_ENV
echo "CC=clang-${LLVM_VERSION}" >> $GITHUB_ENV
echo "CXX=clang++-${LLVM_VERSION}" >> $GITHUB_ENV
echo "AR=llvm-ar-${LLVM_VERSION}" >> $GITHUB_ENV
- name: Setup - name: Setup
env:
UBUNTU_BASE: ${{ needs.lint.outputs.UBUNTU_BASE }}
run: | run: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo apt-add-repository "deb http://apt.llvm.org/${UBUNTU_BASE}/ llvm-toolchain-${UBUNTU_BASE}-${{ matrix.LLVM_VERSION }} main" sudo apt-add-repository "deb http://apt.llvm.org/${UBUNTU_BASE}/ llvm-toolchain-${UBUNTU_BASE}-$LLVM_VERSION main"
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install mesa-vulkan-drivers valgrind libc++-dev libc++abi-dev libgtk-3-dev libsdl2-dev libvulkan-dev libx11-xcb-dev clang-${{ matrix.LLVM_VERSION }} ninja-build sudo apt-get -y install mesa-vulkan-drivers valgrind libc++-dev libc++abi-dev libgtk-3-dev libsdl2-dev libvulkan-dev libx11-dev libx11-xcb-dev clang-$LLVM_VERSION clang-format-$LLVM_VERSION llvm-$LLVM_VERSION
./xenia-build.py setup ./xb setup
- name: Build - name: Build
env: run: ./xb build --config=Release
CC: clang-${{ matrix.LLVM_VERSION }}
CXX: clang++-${{ matrix.LLVM_VERSION }}
run: ./xenia-build.py build --config=Release
- name: Prepare artifacts
id: prepare_artifacts
run: |
if [ $(stat -c %s build/bin/Linux/Release/xenia_canary) -le 100000 ]; then
echo "::error::Binary is too small."
fi
mkdir -p artifacts
cp -r build/bin/Linux/Release/xenia_canary LICENSE artifacts
- name: Upload xenia canary artifacts
if: steps.prepare_artifacts.outcome == 'success'
uses: actions/upload-artifact@main
with:
name: xenia_canary_linux
path: artifacts
if-no-files-found: error
# build-gcc:
# name: Build (GCC ${{ matrix.GCC_VERSION }})
# needs: lint
# runs-on: ubuntu-24.04
# strategy:
# fail-fast: false
# matrix:
# GCC_VERSION: [14]
# steps:
# - uses: actions/checkout@main
# with:
# fetch-depth: 0
# - name: Setup
# run: |
# sudo apt-get -y update
# sudo apt-get -y install mesa-vulkan-drivers valgrind libc++-dev libc++abi-dev libgtk-3-dev libsdl2-dev libvulkan-dev libx11-xcb-dev g++-${{ matrix.GCC_VERSION }}
# ./xenia-build.py setup
# - name: Build
# env:
# CC: gcc-${{ matrix.GCC_VERSION }}
# CXX: g++-${{ matrix.GCC_VERSION }}
# # AR: ar
# run: ./xenia-build.py build --config=Release
# - name: Prepare artifacts
# id: prepare_artifacts
# run: |
# if [ $(stat -c %s build/bin/Linux/Release/xenia_canary) -le 100000 ]; then
# echo "::error::Binary is too small."
# fi
# mkdir -p artifacts
# cp -r build/bin/Linux/Release/xenia_canary LICENSE artifacts
# - name: Upload xenia canary artifacts
# if: steps.prepare_artifacts.outcome == 'success'
# uses: actions/upload-artifact@main
# with:
# name: xenia_canary_linux_gcc
# path: artifacts
# if-no-files-found: error
create-release:
name: Create release
needs: [lint, build-clang] #build-gcc
if: |
github.repository == 'xenia-canary/xenia-canary' &&
github.event.action != 'pull_request' &&
github.ref == 'refs/heads/canary_experimental'
uses: ./.github/workflows/Create_release.yml
with:
os: linux
secrets:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}

View File

@ -51,17 +51,21 @@ jobs:
lint: lint:
name: Lint name: Lint
runs-on: windows-latest runs-on: windows-latest
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Check Clang-Format Version
run: clang-format --version
- name: Lint - name: Lint
run: python xenia-build.py lint --all run: .\xb lint --all
build: build-windows:
name: Build name: Build (Windows, VS${{ matrix.vsver }}) # runner.os can't be used here
needs: lint needs: lint
runs-on: windows-2025 strategy:
fail-fast: false
matrix:
vsver: [2022]
runs-on: windows-${{ matrix.vsver }}
env: env:
POWERSHELL_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1
steps: steps:
@ -69,37 +73,45 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup - name: Setup
run: python xenia-build.py setup run: .\xb setup
- name: Build - name: Build
run: python xenia-build.py build --no_premake --config=Release --target=src\xenia-app run: .\xb build --config=Release --target=src\xenia-app
- name: Prepare artifacts - name: Prepare artifacts
id: prepare_artifacts id: prepare_artifacts
run: | run: |
foreach ($file in 'build\bin\Windows\Release\xenia_canary.exe','build\bin\Windows\Release\xenia_canary.pdb') {
if ((get-item $file).Length -le 100000) {
echo "::error::$file is too small."
}
}
robocopy . build\bin\Windows\Release LICENSE /r:0 /w:0 robocopy . build\bin\Windows\Release LICENSE /r:0 /w:0
robocopy build\bin\Windows\Release artifacts\xenia_canary xenia_canary.exe xenia_canary.pdb LICENSE /r:0 /w:0 robocopy build\bin\Windows\Release artifacts\xenia_canary xenia_canary.exe xenia_canary.pdb LICENSE /r:0 /w:0
If ($LastExitCode -le 7) { echo "LastExitCode = $LastExitCode";$LastExitCode = 0 } If ($LastExitCode -le 7) { echo "LastExitCode = $LastExitCode";$LastExitCode = 0 }
- name: Upload xenia canary artifacts - name: Upload xenia canary artifacts
if: steps.prepare_artifacts.outcome == 'success' if: steps.prepare_artifacts.outcome == 'success'
id: upload_artifacts
uses: actions/upload-artifact@main uses: actions/upload-artifact@main
with: with:
name: xenia_canary_windows name: xenia_canary_vs${{ matrix.vsver }}
path: artifacts\xenia_canary path: artifacts\xenia_canary
if-no-files-found: error if-no-files-found: error
- name: Create release
create-release:
name: Create release
needs: [lint, build]
if: | if: |
github.repository == 'xenia-canary/xenia-canary' && github.repository == 'xenia-canary/xenia-canary' &&
github.event.action != 'pull_request' && github.event.action != 'pull_request' &&
github.ref == 'refs/heads/canary_experimental' github.ref == 'refs/heads/canary_experimental' &&
uses: ./.github/workflows/Create_release.yml steps.upload_artifacts.outcome == 'success'
with: env:
os: windows GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
secrets: run: |
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} $asset="xenia_canary.zip"
rm -recurse -force artifacts\xenia_canary\*.pdb # Ideally this would use xr, but I can't get it to work
7z a $asset .\artifacts\xenia_canary\*
If ($(Get-Item $asset).length -le 100000) {
Throw "Error: Archive $asset too small!"
}
$tag=$env:GITHUB_SHA.SubString(0,7)
$branch=$($env:GITHUB_REF -replace 'refs/heads/')
$title="${tag}_$branch"
gh release create $tag $asset --target $env:GITHUB_SHA -t $title
# Remove canary_ to prevent conflicts from tag
$tag=$($branch -replace 'canary_')
gh release delete $tag -y
git push --delete origin $tag
git tag -d $tag
gh release create $tag $asset --target $env:GITHUB_SHA -t $branch

18
.gitmodules vendored
View File

@ -15,8 +15,7 @@
url = https://github.com/premake/premake-core.git url = https://github.com/premake/premake-core.git
[submodule "third_party/snappy"] [submodule "third_party/snappy"]
path = third_party/snappy path = third_party/snappy
url = https://github.com/google/snappy.git url = https://github.com/xenia-project/snappy.git
ignore = dirty
[submodule "third_party/premake-export-compile-commands"] [submodule "third_party/premake-export-compile-commands"]
path = third_party/premake-export-compile-commands path = third_party/premake-export-compile-commands
url = https://github.com/xenia-project/premake-export-compile-commands.git url = https://github.com/xenia-project/premake-export-compile-commands.git
@ -43,7 +42,7 @@
url = https://github.com/libsdl-org/SDL.git url = https://github.com/libsdl-org/SDL.git
[submodule "third_party/utfcpp"] [submodule "third_party/utfcpp"]
path = third_party/utfcpp path = third_party/utfcpp
url = https://github.com/nemtrif/utfcpp.git url = https://github.com/xenia-project/utfcpp.git
[submodule "third_party/fmt"] [submodule "third_party/fmt"]
path = third_party/fmt path = third_party/fmt
url = https://github.com/fmtlib/fmt.git url = https://github.com/fmtlib/fmt.git
@ -98,16 +97,9 @@
[submodule "third_party/rapidcsv"] [submodule "third_party/rapidcsv"]
path = third_party/rapidcsv path = third_party/rapidcsv
url = https://github.com/d99kris/rapidcsv.git url = https://github.com/d99kris/rapidcsv.git
[submodule "third_party/zlib-ng"] [submodule "third_party/zlib"]
path = third_party/zlib-ng path = third_party/zlib
url = https://github.com/zlib-ng/zlib-ng.git url = https://github.com/madler/zlib.git
ignore = dirty
[submodule "third_party/pugixml"] [submodule "third_party/pugixml"]
path = third_party/pugixml path = third_party/pugixml
url = https://github.com/zeux/pugixml.git url = https://github.com/zeux/pugixml.git
[submodule "third_party/libusb"]
path = third_party/libusb
url = https://github.com/libusb/libusb.git
[submodule "third_party/cpplint"]
path = third_party/cpplint
url = https://github.com/cpplint/cpplint.git

View File

@ -20,7 +20,7 @@ Discussing illegal activities will get you banned.
Buildbot | Status | Releases Buildbot | Status | Releases
-------- | ------ | -------- -------- | ------ | --------
Windows | [![CI](https://github.com/xenia-canary/xenia-canary/actions/workflows/Windows_build.yml/badge.svg?branch=canary_experimental)](https://github.com/xenia-canary/xenia-canary/actions/workflows/Windows_build.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/cd506034fd8148309a45034925648499)](https://app.codacy.com/gh/xenia-canary/xenia-canary/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) | [Latest](https://github.com/xenia-canary/xenia-canary-releases/releases/latest) ◦ [All](https://github.com/xenia-canary/xenia-canary-releases/releases) ◦ [Old](https://github.com/xenia-canary/xenia-canary/releases) Windows | [![CI](https://github.com/xenia-canary/xenia-canary/actions/workflows/Windows_build.yml/badge.svg?branch=canary_experimental)](https://github.com/xenia-canary/xenia-canary/actions/workflows/Windows_build.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/cd506034fd8148309a45034925648499)](https://app.codacy.com/gh/xenia-canary/xenia-canary/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) | [Latest](https://github.com/xenia-canary/xenia-canary/releases/latest) ◦ [All](https://github.com/xenia-canary/xenia-canary/releases)
Linux | [![CI](https://github.com/xenia-canary/xenia-canary/actions/workflows/Linux_build.yml/badge.svg?branch=canary_experimental)](https://github.com/xenia-canary/xenia-canary/actions/workflows/Linux_build.yml) Linux | [![CI](https://github.com/xenia-canary/xenia-canary/actions/workflows/Linux_build.yml/badge.svg?branch=canary_experimental)](https://github.com/xenia-canary/xenia-canary/actions/workflows/Linux_build.yml)
Netplay Build | | [Latest](https://github.com/AdrianCassar/xenia-canary/releases/latest) Netplay Build | | [Latest](https://github.com/AdrianCassar/xenia-canary/releases/latest)

View File

@ -9,11 +9,10 @@ drivers.
### Windows ### Windows
* Windows 10 or later * Windows 10 or later
* [Visual Studio 2022](https://www.visualstudio.com/downloads/) * [Visual Studio 2022, Visual Studio 2019](https://www.visualstudio.com/downloads/)
* CMake 3.10+ (or C++ CMake tools for Windows) * [Python 3.9+](https://www.python.org/downloads/)
* Windows 11 SDK version 10.0.22000.0 (for Visual Studio 2022, this or any newer version)
* [Python 3.9+ 64-bit](https://www.python.org/downloads/)
* Ensure Python is in PATH. * Ensure Python is in PATH.
* Windows 11 SDK version 10.0.22000.0 (for Visual Studio 2019, this or any newer version)
``` ```
git clone https://github.com/xenia-canary/xenia-canary.git git clone https://github.com/xenia-canary/xenia-canary.git
@ -89,24 +88,18 @@ get helpful spacers/movs in the disassembly.
Linux support is extremely experimental and presently incomplete. Linux support is extremely experimental and presently incomplete.
The build script uses Clang 19. GCC while it should work in theory, is not easily The build script uses LLVM/Clang 9. GCC while it should work in theory, is not easily
interchangeable right now. interchangeable right now.
* Normal building via `xb build` uses CMake+Ninja. * Normal building via `xb build` uses Make.
* Environment variables: * [CodeLite](https://codelite.org) is supported. `xb devenv` will generate a workspace and attempt to open it. Your distribution's version may be out of date so check their website.
Name | Default Value * Experimental CMake generation is available to facilitate use of other IDEs such as [CLion](https://www.jetbrains.com/clion/). If `clion` is available inside `$PATH`, `xb devenv` will start it. Otherwise `build/CMakeLists.txt` needs to be generated by invoking `xb premake --devenv=cmake` manually.
----- | -------------
`CC` | `clang`
`CXX` | `clang++`
<!--* [CodeLite](https://codelite.org) is supported. `xb devenv` will generate a workspace and attempt to open it. Your distribution's version may be out of date so check their website. Clang-9 or newer should be available from system repositories on all up to date distributions.
* Experimental CMake generation is available to facilitate use of other IDEs such as [CLion](https://www.jetbrains.com/clion/). If `clion` is available inside `$PATH`, `xb devenv` will start it. Otherwise `build/CMakeLists.txt` needs to be generated by invoking `xb premake --devenv=cmake` manually.-->
Clang-19 or newer should be available from system repositories on all up to date distributions.
You will also need some development libraries. To get them on an Ubuntu system: You will also need some development libraries. To get them on an Ubuntu system:
```sh ```bash
sudo apt-get install build-essential mesa-vulkan-drivers valgrind libc++-dev libc++abi-dev libgtk-3-dev liblz4-dev libsdl2-dev libvulkan-dev libx11-xcb-dev clang-19 llvm-19 ninja-build sudo apt-get install libgtk-3-dev libpthread-stubs0-dev liblz4-dev libx11-dev libx11-xcb-dev libvulkan-dev libsdl2-dev libiberty-dev libunwind-dev libc++-dev libc++abi-dev
``` ```
In addition, you will need up to date Vulkan libraries and drivers for your hardware, which most distributions have in their standard repositories nowadays. In addition, you will need up to date Vulkan libraries and drivers for your hardware, which most distributions have in their standard repositories nowadays.

View File

@ -7,11 +7,6 @@ location(build_root)
targetdir(build_bin) targetdir(build_bin)
objdir(build_obj) objdir(build_obj)
-- Define variables for enabling specific submodules
-- Todo: Add changing from xb command
enableTests = false
enableMiscSubprojects = false
-- Define an ARCH variable -- Define an ARCH variable
-- Only use this to enable architecture-specific functionality. -- Only use this to enable architecture-specific functionality.
if os.istarget("linux") then if os.istarget("linux") then
@ -27,18 +22,14 @@ includedirs({
}) })
defines({ defines({
"IMGUI_DISABLE_OBSOLETE_FUNCTIONS", "_UNICODE",
"IMGUI_DISABLE_DEFAULT_FONT", "UNICODE",
--"IMGUI_USE_WCHAR32",
"IMGUI_USE_STB_SPRINTF",
--"IMGUI_ENABLE_FREETYPE",
"USE_CPP17", -- Tabulate
}) })
cdialect("C17")
cppdialect("C++20") cppdialect("C++20")
exceptionhandling("On")
rtti("On")
symbols("On") symbols("On")
fatalwarnings("All")
-- TODO(DrChat): Find a way to disable this on other architectures. -- TODO(DrChat): Find a way to disable this on other architectures.
if ARCH ~= "ppc64" then if ARCH ~= "ppc64" then
@ -47,6 +38,11 @@ if ARCH ~= "ppc64" then
filter({}) filter({})
end end
characterset("Unicode")
flags({
"FatalWarnings", -- Treat warnings as errors.
})
filter("kind:StaticLib") filter("kind:StaticLib")
defines({ defines({
"_LIB", "_LIB",
@ -54,27 +50,23 @@ filter("kind:StaticLib")
filter("configurations:Checked") filter("configurations:Checked")
runtime("Debug") runtime("Debug")
sanitize("Address")
flags("NoIncrementalLink")
editandcontinue("Off")
staticruntime("Off")
optimize("Off") optimize("Off")
defines({ defines({
"DEBUG", "DEBUG",
}) })
filter({"configurations:Checked", "platforms:Windows"})
filter({"configurations:Checked", "platforms:Windows"}) -- "toolset:msc"
buildoptions({ buildoptions({
"/RTCsu", -- Full Run-Time Checks. "/RTCsu", -- Full Run-Time Checks.
}) })
filter({"configurations:Checked", "platforms:Linux"})
filter({"configurations:Checked or Debug", "platforms:Linux"})
defines({ defines({
"_GLIBCXX_DEBUG", -- libstdc++ debug mode "_GLIBCXX_DEBUG", -- libstdc++ debug mode
}) })
filter({"configurations:Release", "platforms:Windows"})
filter({"configurations:Checked or Debug", "platforms:Windows"}) -- "toolset:msc" buildoptions({
symbols("Full") "/Gw",
"/Ob3",
})
filter("configurations:Debug") filter("configurations:Debug")
runtime("Release") runtime("Release")
@ -83,6 +75,10 @@ filter("configurations:Debug")
"DEBUG", "DEBUG",
"_NO_DEBUG_HEAP=1", "_NO_DEBUG_HEAP=1",
}) })
filter({"configurations:Debug", "platforms:Linux"})
defines({
"_GLIBCXX_DEBUG", -- make dbg symbols work on some distros
})
filter("configurations:Release") filter("configurations:Release")
runtime("Release") runtime("Release")
@ -91,36 +87,23 @@ filter("configurations:Release")
"_NO_DEBUG_HEAP=1", "_NO_DEBUG_HEAP=1",
}) })
optimize("Speed") optimize("Speed")
flags({
"NoBufferSecurityCheck"
})
inlining("Auto") inlining("Auto")
editandcontinue("Off") flags({
"LinkTimeOptimization",
"NoBufferSecurityCheck",
})
-- Not using floatingpoint("Fast") - NaN checks are used in some places -- Not using floatingpoint("Fast") - NaN checks are used in some places
-- (though rarely), overall preferable to avoid any functional differences -- (though rarely), overall preferable to avoid any functional differences
-- between debug and release builds, and to have calculations involved in GPU -- between debug and release builds, and to have calculations involved in GPU
-- (especially anything that may affect vertex position invariance) and CPU -- (especially anything that may affect vertex position invariance) and CPU
-- (such as constant propagation) emulation as predictable as possible, -- (such as constant propagation) emulation as predictable as possible,
-- including handling of specials since games make assumptions about them. -- including handling of specials since games make assumptions about them.
filter({"configurations:Release", "platforms:not Windows"})
symbols("Off")
filter({"configurations:Release", "platforms:Windows"}) -- "toolset:msc"
linktimeoptimization("On")
buildoptions({
"/Gw",
"/Ob3",
-- "/Qpar", -- TODO: Test this.
})
filter("platforms:Linux") filter("platforms:Linux")
system("linux") system("linux")
toolset("clang") toolset("clang")
vectorextensions("AVX2") buildoptions({
--buildoptions({
-- "-mlzcnt", -- (don't) Assume lzcnt is supported. -- "-mlzcnt", -- (don't) Assume lzcnt is supported.
--}) })
pkg_config.all("gtk+-x11-3.0") pkg_config.all("gtk+-x11-3.0")
links({ links({
"stdc++fs", "stdc++fs",
@ -130,26 +113,21 @@ filter("platforms:Linux")
"rt", "rt",
}) })
filter({"platforms:Linux"})
vectorextensions("AVX2")
filter({"platforms:Linux", "kind:*App"}) filter({"platforms:Linux", "kind:*App"})
linkgroups("On") linkgroups("On")
filter({"language:C++", "toolset:clang or gcc"}) -- "platforms:Linux" filter({"platforms:Linux", "language:C++", "toolset:gcc"})
disablewarnings({
"switch",
"attributes",
})
filter({"language:C++", "toolset:gcc"}) -- "platforms:Linux"
disablewarnings({ disablewarnings({
"unused-result", "unused-result",
"volatile", "deprecated-volatile",
"template-id-cdtor", "switch",
"return-type", "deprecated-enum-enum-conversion",
"deprecated",
}) })
filter("toolset:gcc") -- "platforms:Linux" filter({"platforms:Linux", "toolset:gcc"})
removefatalwarnings("All") -- HACK
if ARCH == "ppc64" then if ARCH == "ppc64" then
buildoptions({ buildoptions({
"-m32", "-m32",
@ -159,35 +137,19 @@ filter("toolset:gcc") -- "platforms:Linux"
"-m32", "-m32",
"-mpowerpc64" "-mpowerpc64"
}) })
else
buildoptions({
"-fpermissive", -- HACK
})
linkoptions({
"-fpermissive", -- HACK
})
end end
filter({"language:C++", "toolset:clang"}) -- "platforms:Linux" filter({"platforms:Linux", "language:C++", "toolset:clang"})
disablewarnings({ disablewarnings({
"deprecated-register", "deprecated-register",
"deprecated-volatile", "deprecated-volatile",
"switch",
"deprecated-enum-enum-conversion", "deprecated-enum-enum-conversion",
}) })
CLANG_BIN = os.getenv("CC") or _OPTIONS["cc"] or "clang" filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"})
if os.istarget("linux") and string.contains(CLANG_BIN, "clang") then buildoptions({
if tonumber(string.match(os.outputof(CLANG_BIN.." --version"), "version (%d%d)")) >= 20 then "-stdlib=libstdc++",
filter({"language:C++", "toolset:clang"}) -- "platforms:Linux" "-std=c++20", -- clang doesn't respect cppdialect(?)
disablewarnings({
"deprecated-literal-operator", -- Needed only for tabulate
"nontrivial-memcall",
})
end
end
filter({"language:C", "toolset:clang or gcc"}) -- "platforms:Linux"
disablewarnings({
"implicit-function-declaration",
}) })
filter("platforms:Android-*") filter("platforms:Android-*")
@ -212,8 +174,16 @@ filter("platforms:Windows")
toolset("msc") toolset("msc")
buildoptions({ buildoptions({
"/utf-8", -- 'build correctly on systems with non-Latin codepages'. "/utf-8", -- 'build correctly on systems with non-Latin codepages'.
-- Mark warnings as severe
"/w14839", -- non-standard use of class 'type' as an argument to a variadic function
"/w14840", -- non-portable use of class 'type' as an argument to a variadic function
-- Disable warnings -- Disable warnings
"/wd4100", -- Unreferenced parameters are ok.
"/wd4201", -- Nameless struct/unions are ok. "/wd4201", -- Nameless struct/unions are ok.
"/wd4512", -- 'assignment operator was implicitly defined as deleted'.
"/wd4127", -- 'conditional expression is constant'.
"/wd4324", -- 'structure was padded due to alignment specifier'.
"/wd4189", -- 'local variable is initialized but not referenced'.
}) })
flags({ flags({
"MultiProcessorCompile", -- Multiprocessor compilation. "MultiProcessorCompile", -- Multiprocessor compilation.
@ -278,7 +248,10 @@ workspace("xenia")
-- 10.0.15063.0: ID3D12GraphicsCommandList1::SetSamplePositions. -- 10.0.15063.0: ID3D12GraphicsCommandList1::SetSamplePositions.
-- 10.0.19041.0: D3D12_HEAP_FLAG_CREATE_NOT_ZEROED. -- 10.0.19041.0: D3D12_HEAP_FLAG_CREATE_NOT_ZEROED.
-- 10.0.22000.0: DWMWA_WINDOW_CORNER_PREFERENCE. -- 10.0.22000.0: DWMWA_WINDOW_CORNER_PREFERENCE.
systemversion("latest") filter("action:vs2017")
systemversion("10.0.22000.0")
filter("action:vs2019")
systemversion("10.0")
filter({}) filter({})
end end
end end
@ -299,13 +272,9 @@ workspace("xenia")
include("third_party/xxhash.lua") include("third_party/xxhash.lua")
include("third_party/zarchive.lua") include("third_party/zarchive.lua")
include("third_party/zstd.lua") include("third_party/zstd.lua")
include("third_party/zlib-ng.lua") include("third_party/zlib.lua")
include("third_party/pugixml.lua") include("third_party/pugixml.lua")
if os.istarget("windows") then
include("third_party/libusb.lua")
end
if not os.istarget("android") then if not os.istarget("android") then
-- SDL2 requires sdl2-config, and as of November 2020 isn't high-quality on -- SDL2 requires sdl2-config, and as of November 2020 isn't high-quality on
-- Android yet, most importantly in game controllers - the keycode and axis -- Android yet, most importantly in game controllers - the keycode and axis
@ -323,7 +292,9 @@ workspace("xenia")
removefiles({ removefiles({
"src/xenia/base/app_win32.manifest" "src/xenia/base/app_win32.manifest"
}) })
removefatalwarnings("All") removeflags({
"FatalWarnings",
})
end end
include("src/xenia") include("src/xenia")
@ -340,7 +311,6 @@ workspace("xenia")
include("src/xenia/gpu/vulkan") include("src/xenia/gpu/vulkan")
include("src/xenia/hid") include("src/xenia/hid")
include("src/xenia/hid/nop") include("src/xenia/hid/nop")
include("src/xenia/hid/skylander")
include("src/xenia/kernel") include("src/xenia/kernel")
include("src/xenia/patcher") include("src/xenia/patcher")
include("src/xenia/ui") include("src/xenia/ui")

View File

@ -9,6 +9,8 @@ project("xenia-app-discord")
links({ links({
"discord-rpc" "discord-rpc"
}) })
defines({
})
includedirs({ includedirs({
project_root.."/third_party/discord-rpc/src" project_root.."/third_party/discord-rpc/src"
}) })

View File

@ -9,6 +9,13 @@
#include "xenia/app/emulator_window.h" #include "xenia/app/emulator_window.h"
#include <filesystem>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include "third_party/imgui/imgui.h" #include "third_party/imgui/imgui.h"
#include "third_party/stb/stb_image_write.h" #include "third_party/stb/stb_image_write.h"
#include "third_party/tomlplusplus/toml.hpp" #include "third_party/tomlplusplus/toml.hpp"
@ -160,8 +167,8 @@ using xe::ui::UIEvent;
using namespace xe::hid; using namespace xe::hid;
using namespace xe::gpu; using namespace xe::gpu;
constexpr std::string_view kRecentlyPlayedTitlesFilename = "recent.toml"; const std::string kRecentlyPlayedTitlesFilename = "recent.toml";
constexpr std::string_view kBaseTitle = "Xenia-canary"; const std::string kBaseTitle = "Xenia-canary";
EmulatorWindow::EmulatorWindow(Emulator* emulator, EmulatorWindow::EmulatorWindow(Emulator* emulator,
ui::WindowedAppContext& app_context, ui::WindowedAppContext& app_context,
@ -174,7 +181,7 @@ EmulatorWindow::EmulatorWindow(Emulator* emulator,
std::make_unique<ui::ImGuiDrawer>(window_.get(), kZOrderImGui)), std::make_unique<ui::ImGuiDrawer>(window_.get(), kZOrderImGui)),
display_config_game_config_load_callback_( display_config_game_config_load_callback_(
new DisplayConfigGameConfigLoadCallback(*emulator, *this)) { new DisplayConfigGameConfigLoadCallback(*emulator, *this)) {
base_title_ = std::string(kBaseTitle) + base_title_ = kBaseTitle +
#ifdef DEBUG #ifdef DEBUG
#if _NO_DEBUG_HEAP == 1 #if _NO_DEBUG_HEAP == 1
" DEBUG" " DEBUG"
@ -562,96 +569,6 @@ void EmulatorWindow::DisplayConfigDialog::OnDraw(ImGuiIO& io) {
} }
} }
void EmulatorWindow::ContentInstallDialog::OnDraw(ImGuiIO& io) {
ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(20, 20), ImGuiCond_FirstUseEver);
bool dialog_open = true;
if (!ImGui::Begin(
fmt::format("Installation Progress###{}", window_id_).c_str(),
&dialog_open,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_HorizontalScrollbar)) {
Close();
ImGui::End();
return;
}
bool is_everything_installed = true;
for (const auto& entry : *installation_entries_) {
ImGui::BeginTable(fmt::format("table_{}", entry.name_).c_str(), 2);
ImGui::TableNextRow(0);
ImGui::TableSetColumnIndex(0);
if (entry.icon_) {
ImGui::Image(reinterpret_cast<ImTextureID>(entry.icon_.get()),
ui::default_image_icon_size);
} else {
ImGui::Dummy(ui::default_image_icon_size);
}
ImGui::TableNextColumn();
ImGui::Text("Name: %s", entry.name_.c_str());
ImGui::Text("Installation Path:");
ImGui::SameLine();
if (ImGui::TextLink(
xe::path_to_utf8(entry.data_installation_path_).c_str())) {
LaunchFileExplorer(emulator_window_.emulator_->content_root() /
entry.data_installation_path_);
}
if (entry.content_type_ != xe::XContentType::kInvalid) {
ImGui::Text("Content Type: %s",
XContentTypeMap.at(entry.content_type_).c_str());
}
std::string result = fmt::format(
"Status: {}", xe::Emulator::installStateStringName[static_cast<uint8_t>(
entry.installation_state_)]);
if (entry.installation_state_ == xe::Emulator::InstallState::failed) {
result += fmt::format(" - {} ({:08X})",
entry.installation_error_message_.c_str(),
entry.installation_result_);
}
ImGui::Text("%s", result.c_str());
ImGui::EndTable();
if (entry.content_size_ > 0) {
ImGui::ProgressBar(static_cast<float>(entry.currently_installed_size_) /
entry.content_size_);
if (entry.currently_installed_size_ != entry.content_size_ &&
entry.installation_result_ == X_ERROR_SUCCESS) {
is_everything_installed = false;
}
} else {
ImGui::ProgressBar(0.0f);
}
if (installation_entries_->size() > 1) {
ImGui::Separator();
}
}
ImGui::Spacing();
ImGui::BeginDisabled(!is_everything_installed);
if (ImGui::Button("Close")) {
ImGui::EndDisabled();
Close();
ImGui::End();
return;
}
ImGui::EndDisabled();
if (!dialog_open && is_everything_installed) {
Close();
ImGui::End();
return;
}
ImGui::End();
}
bool EmulatorWindow::Initialize() { bool EmulatorWindow::Initialize() {
window_->AddListener(&window_listener_); window_->AddListener(&window_listener_);
window_->AddInputListener(&window_listener_, kZOrderEmulatorWindowInput); window_->AddInputListener(&window_listener_, kZOrderEmulatorWindowInput);
@ -1018,16 +935,11 @@ void EmulatorWindow::OnMouseUp(const ui::MouseEvent& e) {
void EmulatorWindow::TakeScreenshot() { void EmulatorWindow::TakeScreenshot() {
xe::ui::RawImage image; xe::ui::RawImage image;
imgui_drawer_->EnableNotifications(false);
if (!GetGraphicsSystemPresenter()->CaptureGuestOutput(image) || if (!GetGraphicsSystemPresenter()->CaptureGuestOutput(image) ||
GetGraphicsSystemPresenter() == nullptr) { GetGraphicsSystemPresenter() == nullptr) {
XELOGE("Failed to capture guest output for screenshot"); XELOGE("Failed to capture guest output for screenshot");
return; return;
} }
imgui_drawer_->EnableNotifications(true);
ExportScreenshot(image); ExportScreenshot(image);
} }
@ -1094,9 +1006,9 @@ void EmulatorWindow::ToggleFullscreenOnDoubleClick() {
// this function tests if user has double clicked. // this function tests if user has double clicked.
// if double click was achieved the fullscreen gets toggled // if double click was achieved the fullscreen gets toggled
const auto now = steady_clock::now(); // current mouse event time const auto now = steady_clock::now(); // current mouse event time
constexpr int16_t mouse_down_max_threshold = 250; const int16_t mouse_down_max_threshold = 250;
constexpr int16_t mouse_up_max_threshold = 250; const int16_t mouse_up_max_threshold = 250;
constexpr int16_t mouse_up_down_max_delta = 100; const int16_t mouse_up_down_max_delta = 100;
// max delta to prevent 'chaining' of double clicks with next mouse events // max delta to prevent 'chaining' of double clicks with next mouse events
const auto last_mouse_down_delta = diff_in_ms(now, last_mouse_down); const auto last_mouse_down_delta = diff_in_ms(now, last_mouse_down);
@ -1171,27 +1083,62 @@ void EmulatorWindow::InstallContent() {
return; return;
} }
std::shared_ptr<std::vector<Emulator::ContentInstallEntry>> using content_installation_data =
content_installation_status = std::tuple<X_STATUS, std::string, std::string>;
std::make_shared<std::vector<Emulator::ContentInstallEntry>>(); std::map<XContentType, std::vector<content_installation_data>>
content_installation_details;
for (const auto& path : paths) { for (const auto& path : paths) {
content_installation_status->push_back({path}); // Normalize the path and make absolute.
auto abs_path = std::filesystem::absolute(path);
Emulator::ContentInstallationInfo installation_info;
auto result = emulator_->InstallContentPackage(abs_path, installation_info);
auto entry =
content_installation_details.find(installation_info.content_type);
// There is no entry with that specific type of XContent, so we must add it.
if (entry == content_installation_details.end()) {
content_installation_details.insert({installation_info.content_type, {}});
entry = content_installation_details.find(installation_info.content_type);
};
entry->second.push_back({result, installation_info.content_name,
installation_info.installation_path});
} }
for (auto& entry : *content_installation_status) { // Prepare installation process summary message
emulator_->ProcessContentPackageHeader(entry.path_, entry); std::string summary = "Installation result: \n";
for (const auto& content_type : content_installation_details) {
if (XContentTypeMap.find(content_type.first) != XContentTypeMap.cend()) {
summary += XContentTypeMap.at(content_type.first) + ":\n";
} else {
summary += "Unknown:\n";
} }
auto installationThread = std::thread([this, content_installation_status] { for (const auto& content_installation_entry : content_type.second) {
for (auto& entry : *content_installation_status) { const std::string status =
emulator_->InstallContentPackage(entry.path_, entry); std::get<0>(content_installation_entry) == X_STATUS_SUCCESS
} ? "Success"
}); : fmt::format("Failed (0x{:08X})",
installationThread.detach(); std::get<0>(content_installation_entry));
new ContentInstallDialog(imgui_drawer_.get(), *this, summary += fmt::format("\t{} - {} => {}\n", status,
content_installation_status); std::get<1>(content_installation_entry),
std::get<2>(content_installation_entry));
}
summary += "\n";
}
if (content_installation_details.count(XContentType::kProfile)) {
emulator_->kernel_state()->xam_state()->profile_manager()->ReloadProfiles();
}
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(),
"Content Installation Summary", summary);
} }
void EmulatorWindow::ExtractZarchive() { void EmulatorWindow::ExtractZarchive() {
@ -1477,12 +1424,6 @@ void EmulatorWindow::ToggleControllerVibration() {
auto input_lock = input_sys->lock(); auto input_lock = input_sys->lock();
input_sys->ToggleVibration(); input_sys->ToggleVibration();
if (emulator_->kernel_state()) {
emulator_->kernel_state()->BroadcastNotification(
kXNotificationSystemProfileSettingChanged,
static_cast<uint32_t>(input_sys->GetConnectedSlots().count()));
}
} }
} }
@ -1683,7 +1624,7 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
} }
// Hotkey cool-down to prevent toggling too fast // Hotkey cool-down to prevent toggling too fast
constexpr std::chrono::milliseconds delay(75); const std::chrono::milliseconds delay(75);
// If the Xbox Gamebar is enabled or the Guide button is disabled then // If the Xbox Gamebar is enabled or the Guide button is disabled then
// replace the Guide button with the Back button without redeclaring the key // replace the Guide button with the Back button without redeclaring the key
@ -1853,9 +1794,8 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
} }
if (!notificationTitle.empty()) { if (!notificationTitle.empty()) {
app_context_.CallInUIThread( app_context_.CallInUIThread([&]() {
[imgui_drawer = imgui_drawer(), notificationTitle, notificationDesc]() { new xe::ui::HostNotificationWindow(imgui_drawer(), notificationTitle,
new xe::ui::HostNotificationWindow(imgui_drawer, notificationTitle,
notificationDesc, 0); notificationDesc, 0);
}); });
} }
@ -1868,7 +1808,7 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys, void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
uint32_t user_index, uint32_t user_index,
bool toggle_rumble) { bool toggle_rumble) {
constexpr std::chrono::milliseconds rumble_duration(100); const std::chrono::milliseconds rumble_duration(100);
// Hold lock while sleeping this thread for the duration of the rumble, // Hold lock while sleeping this thread for the duration of the rumble,
// otherwise the rumble may fail. // otherwise the rumble may fail.
@ -1890,7 +1830,7 @@ void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
void EmulatorWindow::GamepadHotKeys() { void EmulatorWindow::GamepadHotKeys() {
X_INPUT_STATE state; X_INPUT_STATE state;
constexpr std::chrono::milliseconds thread_delay(75); const std::chrono::milliseconds thread_delay(75);
auto input_sys = emulator_->input_system(); auto input_sys = emulator_->input_system();
@ -1949,7 +1889,8 @@ void EmulatorWindow::DisplayHotKeysConfig() {
if (!guide_enabled) { if (!guide_enabled) {
pretty_text = std::regex_replace( pretty_text = std::regex_replace(
pretty_text, std::regex("Guide", std::regex_constants::icase), pretty_text,
std::regex("Guide", std::regex_constants::syntax_option_type::icase),
"Back"); "Back");
} }

View File

@ -167,34 +167,6 @@ class EmulatorWindow {
EmulatorWindow& emulator_window_; EmulatorWindow& emulator_window_;
}; };
class ContentInstallDialog final : public ui::ImGuiDialog {
public:
ContentInstallDialog(
ui::ImGuiDrawer* imgui_drawer, EmulatorWindow& emulator_window,
std::shared_ptr<std::vector<Emulator::ContentInstallEntry>> entries)
: ui::ImGuiDialog(imgui_drawer),
emulator_window_(emulator_window),
installation_entries_(entries) {
window_id_ = GetWindowId();
}
~ContentInstallDialog() {
for (auto& entry : *installation_entries_) {
entry.icon_.release();
}
}
protected:
void OnDraw(ImGuiIO& io) override;
private:
uint64_t window_id_;
EmulatorWindow& emulator_window_;
std::shared_ptr<std::vector<Emulator::ContentInstallEntry>>
installation_entries_;
};
class DisplayConfigDialog final : public ui::ImGuiDialog { class DisplayConfigDialog final : public ui::ImGuiDialog {
public: public:
DisplayConfigDialog(ui::ImGuiDrawer* imgui_drawer, DisplayConfigDialog(ui::ImGuiDrawer* imgui_drawer,

View File

@ -54,13 +54,11 @@ project("xenia-app")
-- Unified library containing all apps as StaticLibs, not just the main -- Unified library containing all apps as StaticLibs, not just the main
-- emulator windowed app. -- emulator windowed app.
kind("SharedLib") kind("SharedLib")
if enableMiscSubprojects then
links({ links({
"xenia-gpu-vulkan-trace-viewer", "xenia-gpu-vulkan-trace-viewer",
"xenia-hid-demo", "xenia-hid-demo",
"xenia-ui-window-vulkan-demo", "xenia-ui-window-vulkan-demo",
}) })
end
filter(NOT_SINGLE_LIBRARY_FILTER) filter(NOT_SINGLE_LIBRARY_FILTER)
kind("WindowedApp") kind("WindowedApp")
@ -116,17 +114,21 @@ project("xenia-app")
"xenia-ui-d3d12", "xenia-ui-d3d12",
}) })
if enableMiscSubprojects then
filter({"platforms:Windows", SINGLE_LIBRARY_FILTER}) filter({"platforms:Windows", SINGLE_LIBRARY_FILTER})
links({ links({
"xenia-gpu-d3d12-trace-viewer", "xenia-gpu-d3d12-trace-viewer",
"xenia-ui-window-d3d12-demo", "xenia-ui-window-d3d12-demo",
}) })
end -- filter({"configurations:Release", "platforms:Windows"})
-- buildoptions({
-- "/O1",
-- })
filter("platforms:Windows") filter("platforms:Windows")
-- Only create the .user file if it doesn't already exist. -- Only create the .user file if it doesn't already exist.
local user_file = project_root.."/build/xenia-app.vcxproj.user" local user_file = project_root.."/build/xenia-app.vcxproj.user"
if not os.isfile(user_file) then if not os.isfile(user_file) then
debugdir(project_root) debugdir(project_root)
debugargs({
})
end end

View File

@ -6,22 +6,83 @@
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/app/profile_dialogs.h" #include "xenia/app/profile_dialogs.h"
#include <algorithm>
#include "xenia/app/emulator_window.h" #include "xenia/app/emulator_window.h"
#include "xenia/base/png_utils.h"
#include "xenia/base/system.h" #include "xenia/base/system.h"
#include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_ui.h"
#include "xenia/ui/file_picker.h"
#include "xenia/kernel/xam/ui/create_profile_ui.h"
#include "xenia/kernel/xam/ui/gamercard_ui.h"
#include "xenia/kernel/xam/ui/signin_ui.h"
#include "xenia/kernel/xam/ui/title_info_ui.h"
namespace xe { namespace xe {
namespace kernel {
namespace xam {
extern bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer,
const uint64_t xuid, const uint8_t user_index,
const X_XAMACCOUNTINFO* account,
uint64_t* selected_xuid);
}
} // namespace kernel
namespace app { namespace app {
void CreateProfileDialog::OnDraw(ImGuiIO& io) {
if (!has_opened_) {
ImGui::OpenPopup("Create Profile");
has_opened_ = true;
}
auto profile_manager = emulator_window_->emulator()
->kernel_state()
->xam_state()
->profile_manager();
bool dialog_open = true;
if (!ImGui::BeginPopupModal("Create Profile", &dialog_open,
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_HorizontalScrollbar)) {
Close();
return;
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
!ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0)) {
ImGui::SetKeyboardFocusHere(0);
}
ImGui::TextUnformatted("Gamertag:");
ImGui::InputText("##Gamertag", gamertag_, sizeof(gamertag_));
const std::string gamertag_string = std::string(gamertag_);
bool valid = profile_manager->IsGamertagValid(gamertag_string);
ImGui::BeginDisabled(!valid);
if (ImGui::Button("Create")) {
bool autologin = (profile_manager->GetAccountCount() == 0);
if (profile_manager->CreateProfile(gamertag_string, autologin,
migration_) &&
migration_) {
emulator_window_->emulator()->DataMigration(0xB13EBABEBABEBABE);
}
std::fill(std::begin(gamertag_), std::end(gamertag_), '\0');
dialog_open = false;
}
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
std::fill(std::begin(gamertag_), std::end(gamertag_), '\0');
dialog_open = false;
}
if (!dialog_open) {
ImGui::CloseCurrentPopup();
Close();
ImGui::EndPopup();
return;
}
ImGui::EndPopup();
}
void NoProfileDialog::OnDraw(ImGuiIO& io) { void NoProfileDialog::OnDraw(ImGuiIO& io) {
auto profile_manager = emulator_window_->emulator() auto profile_manager = emulator_window_->emulator()
->kernel_state() ->kernel_state()
@ -63,13 +124,13 @@ void NoProfileDialog::OnDraw(ImGuiIO& io) {
if (content_files.empty()) { if (content_files.empty()) {
if (ImGui::Button("Create Profile")) { if (ImGui::Button("Create Profile")) {
new kernel::xam::ui::CreateProfileUI(emulator_window_->imgui_drawer(), new CreateProfileDialog(emulator_window_->imgui_drawer(),
emulator_window_->emulator()); emulator_window_);
} }
} else { } else {
if (ImGui::Button("Create profile & migrate data")) { if (ImGui::Button("Create profile & migrate data")) {
new kernel::xam::ui::CreateProfileUI(emulator_window_->imgui_drawer(), new CreateProfileDialog(emulator_window_->imgui_drawer(),
emulator_window_->emulator(), true); emulator_window_, true);
} }
} }
@ -88,57 +149,6 @@ void NoProfileDialog::OnDraw(ImGuiIO& io) {
ImGui::End(); ImGui::End();
} }
void ProfileConfigDialog::LoadProfileIcon() {
if (!emulator_window_) {
return;
}
for (uint8_t user_index = 0; user_index < XUserMaxUserCount; user_index++) {
const auto profile = emulator_window_->emulator()
->kernel_state()
->xam_state()
->profile_manager()
->GetProfile(user_index);
if (!profile) {
continue;
}
LoadProfileIcon(profile->xuid());
}
}
void ProfileConfigDialog::LoadProfileIcon(const uint64_t xuid) {
if (!emulator_window_) {
return;
}
const auto profile_manager = emulator_window_->emulator()
->kernel_state()
->xam_state()
->profile_manager();
if (!profile_manager) {
return;
}
const auto profile = profile_manager->GetProfile(xuid);
if (!profile) {
if (profile_icon_.contains(xuid)) {
profile_icon_[xuid].release();
}
return;
}
const auto profile_icon =
profile->GetProfileIcon(kernel::xam::XTileType::kGamerTile);
if (profile_icon.empty()) {
return;
}
profile_icon_[xuid].release();
profile_icon_[xuid] = imgui_drawer()->LoadImGuiIcon(profile_icon);
}
void ProfileConfigDialog::OnDraw(ImGuiIO& io) { void ProfileConfigDialog::OnDraw(ImGuiIO& io) {
if (!emulator_window_->emulator() || if (!emulator_window_->emulator() ||
!emulator_window_->emulator()->kernel_state() || !emulator_window_->emulator()->kernel_state() ||
@ -174,120 +184,28 @@ void ProfileConfigDialog::OnDraw(ImGuiIO& io) {
ImGui::Separator(); ImGui::Separator();
} }
const ImVec2 next_window_position =
ImVec2(ImGui::GetWindowPos().x + ImGui::GetWindowSize().x + 20.f,
ImGui::GetWindowPos().y);
for (auto& [xuid, account] : *profiles) { for (auto& [xuid, account] : *profiles) {
ImGui::PushID(static_cast<int>(xuid)); ImGui::PushID(static_cast<int>(xuid));
const uint8_t user_index = const uint8_t user_index =
profile_manager->GetUserIndexAssignedToProfile(xuid); profile_manager->GetUserIndexAssignedToProfile(xuid);
const auto profile_icon = profile_icon_.find(xuid) != profile_icon_.cend() if (!kernel::xam::xeDrawProfileContent(imgui_drawer(), xuid, user_index,
? profile_icon_[xuid].get() &account, &selected_xuid_)) {
: nullptr;
auto context_menu_fun = [=, this]() -> bool {
if (ImGui::BeginPopupContextItem("Profile Menu")) {
//*selected_xuid = xuid;
if (user_index == XUserIndexAny) {
if (ImGui::MenuItem("Login")) {
profile_manager->Login(xuid);
if (!profile_manager->GetProfile(xuid)
->GetProfileIcon(kernel::xam::XTileType::kGamerTile)
.empty()) {
LoadProfileIcon(xuid);
}
}
if (ImGui::BeginMenu("Login to slot:")) {
for (uint8_t i = 1; i <= XUserMaxUserCount; i++) {
if (ImGui::MenuItem(fmt::format("slot {}", i).c_str())) {
profile_manager->Login(xuid, i - 1);
}
}
ImGui::EndMenu();
}
} else {
if (ImGui::MenuItem("Logout")) {
profile_manager->Logout(user_index);
LoadProfileIcon(xuid);
}
}
if (ImGui::MenuItem("Modify")) {
new kernel::xam::ui::GamercardUI(
emulator_window_->window(), emulator_window_->imgui_drawer(),
emulator_window_->emulator()->kernel_state(), xuid);
}
const bool is_signedin = profile_manager->GetProfile(xuid) != nullptr;
ImGui::BeginDisabled(!is_signedin);
if (ImGui::MenuItem("Show Played Titles")) {
new kernel::xam::ui::TitleListUI(
emulator_window_->imgui_drawer(), next_window_position,
profile_manager->GetProfile(user_index));
}
ImGui::EndDisabled();
if (ImGui::MenuItem("Show Content Directory")) {
const auto path = profile_manager->GetProfileContentPath(
xuid, emulator_window_->emulator()->kernel_state()->title_id());
if (!std::filesystem::exists(path)) {
std::filesystem::create_directories(path);
}
std::thread path_open(LaunchFileExplorer, path);
path_open.detach();
}
if (!emulator_window_->emulator()->is_title_open()) {
ImGui::Separator();
if (ImGui::BeginMenu("Delete Profile")) {
ImGui::BeginTooltip();
ImGui::TextUnformatted(
fmt::format(
"You're about to delete profile: {} (XUID: {:016X}). "
"This will remove all data assigned to this profile "
"including savefiles. Are you sure?",
account.GetGamertagString(), xuid)
.c_str());
ImGui::EndTooltip();
if (ImGui::MenuItem("Yes, delete it!")) {
profile_manager->DeleteProfile(xuid);
ImGui::EndMenu();
ImGui::EndPopup();
return false;
}
ImGui::EndMenu();
}
}
ImGui::EndPopup();
}
return true;
};
if (!kernel::xam::xeDrawProfileContent(
imgui_drawer(), xuid, user_index, &account, profile_icon,
context_menu_fun, [=, this]() { LoadProfileIcon(xuid); },
&selected_xuid_)) {
ImGui::PopID(); ImGui::PopID();
ImGui::End(); ImGui::End();
return; return;
} }
ImGui::PopID(); ImGui::PopID();
ImGui::Spacing();
ImGui::Separator(); ImGui::Separator();
} }
ImGui::Spacing(); ImGui::Spacing();
if (ImGui::Button("Create Profile")) { if (ImGui::Button("Create Profile")) {
new kernel::xam::ui::CreateProfileUI(emulator_window_->imgui_drawer(), new CreateProfileDialog(emulator_window_->imgui_drawer(), emulator_window_);
emulator_window_->emulator());
} }
ImGui::End(); ImGui::End();

View File

@ -19,6 +19,26 @@ namespace app {
class EmulatorWindow; class EmulatorWindow;
class CreateProfileDialog final : public ui::ImGuiDialog {
public:
CreateProfileDialog(ui::ImGuiDrawer* imgui_drawer,
EmulatorWindow* emulator_window,
bool with_migration = false)
: ui::ImGuiDialog(imgui_drawer),
emulator_window_(emulator_window),
migration_(with_migration) {
memset(gamertag_, 0, sizeof(gamertag_));
}
protected:
void OnDraw(ImGuiIO& io) override;
bool has_opened_ = false;
bool migration_ = false;
char gamertag_[16] = "";
EmulatorWindow* emulator_window_;
};
class NoProfileDialog final : public ui::ImGuiDialog { class NoProfileDialog final : public ui::ImGuiDialog {
public: public:
NoProfileDialog(ui::ImGuiDrawer* imgui_drawer, NoProfileDialog(ui::ImGuiDrawer* imgui_drawer,
@ -35,19 +55,12 @@ class ProfileConfigDialog final : public ui::ImGuiDialog {
public: public:
ProfileConfigDialog(ui::ImGuiDrawer* imgui_drawer, ProfileConfigDialog(ui::ImGuiDrawer* imgui_drawer,
EmulatorWindow* emulator_window) EmulatorWindow* emulator_window)
: ui::ImGuiDialog(imgui_drawer), emulator_window_(emulator_window) { : ui::ImGuiDialog(imgui_drawer), emulator_window_(emulator_window) {}
LoadProfileIcon();
}
protected: protected:
void OnDraw(ImGuiIO& io) override; void OnDraw(ImGuiIO& io) override;
private: private:
void LoadProfileIcon();
void LoadProfileIcon(const uint64_t xuid);
std::map<uint64_t, std::unique_ptr<ui::ImmediateTexture>> profile_icon_;
uint64_t selected_xuid_ = 0; uint64_t selected_xuid_ = 0;
EmulatorWindow* emulator_window_; EmulatorWindow* emulator_window_;
}; };

View File

@ -13,6 +13,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector>
#include "xenia/app/discord/discord_presence.h" #include "xenia/app/discord/discord_presence.h"
#include "xenia/app/emulator_window.h" #include "xenia/app/emulator_window.h"
@ -94,15 +95,9 @@ UPDATE_from_bool(mount_cache, 2024, 8, 31, 20, false);
DEFINE_transient_path(target, "", DEFINE_transient_path(target, "",
"Specifies the target .xex or .iso to execute.", "Specifies the target .xex or .iso to execute.",
"General"); "General");
#ifndef XE_PLATFORM_WIN32
DEFINE_transient_bool(portable, false,
"Specifies if Xenia should run in portable mode.",
"General");
#else
DEFINE_transient_bool(portable, true, DEFINE_transient_bool(portable, true,
"Specifies if Xenia should run in portable mode.", "Specifies if Xenia should run in portable mode.",
"General"); "General");
#endif
DECLARE_bool(debug); DECLARE_bool(debug);
@ -426,7 +421,7 @@ bool EmulatorApp::OnInitialize() {
if (!cvars::portable && if (!cvars::portable &&
!std::filesystem::exists(storage_root / "portable.txt")) { !std::filesystem::exists(storage_root / "portable.txt")) {
storage_root = xe::filesystem::GetUserFolder(); storage_root = xe::filesystem::GetUserFolder();
#if defined(XE_PLATFORM_WIN32) || defined(XE_PLATFORM_LINUX) #if defined(XE_PLATFORM_WIN32) || defined(XE_PLATFORM_GNU_LINUX)
storage_root = storage_root / "Xenia"; storage_root = storage_root / "Xenia";
#else #else
// TODO(Triang3l): Point to the app's external storage "files" directory // TODO(Triang3l): Point to the app's external storage "files" directory

View File

@ -18,12 +18,12 @@ namespace apu {
class AudioDriver { class AudioDriver {
public: public:
static constexpr uint32_t kFrameFrequencyDefault = 48000; static const uint32_t kFrameFrequencyDefault = 48000;
static constexpr uint32_t kFrameChannelsDefault = 6; static const uint32_t kFrameChannelsDefault = 6;
static constexpr uint32_t kChannelSamplesDefault = 256; static const uint32_t kChannelSamplesDefault = 256;
static constexpr uint32_t kFrameSamplesMax = static const uint32_t kFrameSamplesMax =
kFrameChannelsDefault * kChannelSamplesDefault; kFrameChannelsDefault * kChannelSamplesDefault;
static constexpr uint32_t kFrameSizeMax = sizeof(float) * kFrameSamplesMax; static const uint32_t kFrameSizeMax = sizeof(float) * kFrameSamplesMax;
virtual ~AudioDriver(); virtual ~AudioDriver();

View File

@ -412,8 +412,7 @@ bool AudioMediaPlayer::LoadSongToMemory(std::vector<uint8_t>* buffer) {
buffer->resize(vfs_file->entry()->size()); buffer->resize(vfs_file->entry()->size());
size_t bytes_read = 0; size_t bytes_read = 0;
result = vfs_file->ReadSync( result = vfs_file->ReadSync(buffer->data(), vfs_file->entry()->size(), 0,
std::span<uint8_t>(buffer->data(), vfs_file->entry()->size()), 0,
&bytes_read); &bytes_read);
return !result; return !result;

View File

@ -10,7 +10,6 @@
#ifndef XENIA_APU_AUDIO_MEDIA_PLAYER_H_ #ifndef XENIA_APU_AUDIO_MEDIA_PLAYER_H_
#define XENIA_APU_AUDIO_MEDIA_PLAYER_H_ #define XENIA_APU_AUDIO_MEDIA_PLAYER_H_
#include "xenia/apu/audio_driver.h"
#include "xenia/apu/audio_system.h" #include "xenia/apu/audio_system.h"
#include "xenia/kernel/xam/apps/xmp_app.h" #include "xenia/kernel/xam/apps/xmp_app.h"

View File

@ -32,7 +32,7 @@ class AudioSystem {
public: public:
// TODO(gibbed): respect XAUDIO2_MAX_QUEUED_BUFFERS somehow (ie min(64, // TODO(gibbed): respect XAUDIO2_MAX_QUEUED_BUFFERS somehow (ie min(64,
// XAUDIO2_MAX_QUEUED_BUFFERS)) // XAUDIO2_MAX_QUEUED_BUFFERS))
static constexpr size_t kMaximumQueuedFrames = 64; static const size_t kMaximumQueuedFrames = 64;
virtual ~AudioSystem(); virtual ~AudioSystem();
@ -81,7 +81,7 @@ class AudioSystem {
kernel::object_ref<kernel::XHostThread> worker_thread_; kernel::object_ref<kernel::XHostThread> worker_thread_;
xe::global_critical_region global_critical_region_; xe::global_critical_region global_critical_region_;
static constexpr size_t kMaximumClientCount = 8; static const size_t kMaximumClientCount = 8;
struct { struct {
AudioDriver* driver; AudioDriver* driver;
uint32_t callback; uint32_t callback;

View File

@ -10,4 +10,6 @@ project("xenia-apu-nop")
"xenia-base", "xenia-base",
"xenia-apu", "xenia-apu",
}) })
defines({
})
local_platform_files() local_platform_files()

View File

@ -12,6 +12,8 @@ project("xenia-apu")
"libavformat", "libavformat",
"xenia-base", "xenia-base",
}) })
defines({
})
includedirs({ includedirs({
project_root.."/third_party/FFmpeg/", project_root.."/third_party/FFmpeg/",
}) })

View File

@ -12,5 +12,7 @@ project("xenia-apu-sdl")
"xenia-helper-sdl", "xenia-helper-sdl",
"SDL2", "SDL2",
}) })
defines({
})
local_platform_files() local_platform_files()
sdl2_include() sdl2_include()

View File

@ -9,6 +9,7 @@
#include "xenia/apu/sdl/sdl_audio_driver.h" #include "xenia/apu/sdl/sdl_audio_driver.h"
#include <array>
#include <cstring> #include <cstring>
#include "xenia/apu/apu_flags.h" #include "xenia/apu/apu_flags.h"

View File

@ -10,4 +10,6 @@ project("xenia-apu-xaudio2")
"xenia-base", "xenia-base",
"xenia-apu", "xenia-apu",
}) })
defines({
})
local_platform_files() local_platform_files()

View File

@ -137,10 +137,10 @@ bool XAudio2AudioDriver::InitializeObjects(Objects& objects) {
api::XAUDIO2_DEBUG_CONFIGURATION config; api::XAUDIO2_DEBUG_CONFIGURATION config;
config.TraceMask = api::XE_XAUDIO2_LOG_ERRORS | api::XE_XAUDIO2_LOG_WARNINGS; config.TraceMask = api::XE_XAUDIO2_LOG_ERRORS | api::XE_XAUDIO2_LOG_WARNINGS;
config.BreakMask = 0; config.BreakMask = 0;
config.LogThreadID = false; config.LogThreadID = FALSE;
config.LogTiming = true; config.LogTiming = TRUE;
config.LogFunctionName = true; config.LogFunctionName = TRUE;
config.LogFileline = true; config.LogFileline = TRUE;
objects.audio->SetDebugConfiguration(&config); objects.audio->SetDebugConfiguration(&config);
hr = objects.audio->CreateMasteringVoice(&objects.mastering_voice); hr = objects.audio->CreateMasteringVoice(&objects.mastering_voice);
@ -165,7 +165,7 @@ bool XAudio2AudioDriver::InitializeObjects(Objects& objects) {
waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample;
static constexpr DWORD kChannelMasks[] = { static const DWORD kChannelMasks[] = {
0, 0,
0, 0,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,

View File

@ -106,7 +106,7 @@ class XAudio2AudioDriver : public AudioDriver {
uint32_t frame_size_; uint32_t frame_size_;
bool need_format_conversion_; bool need_format_conversion_;
static constexpr uint32_t frame_count_ = api::XE_XAUDIO2_MAX_QUEUED_BUFFERS; static const uint32_t frame_count_ = api::XE_XAUDIO2_MAX_QUEUED_BUFFERS;
float frames_[frame_count_][kFrameSamplesMax]; float frames_[frame_count_][kFrameSamplesMax];
uint32_t current_frame_ = 0; uint32_t current_frame_ = 0;

View File

@ -9,6 +9,7 @@
#include "xenia/apu/xma_context.h" #include "xenia/apu/xma_context.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include "xenia/apu/xma_decoder.h" #include "xenia/apu/xma_decoder.h"

View File

@ -14,6 +14,7 @@
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
// #include <vector>
#include "xenia/memory.h" #include "xenia/memory.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
@ -159,23 +160,23 @@ static_assert_size(Xma2ExtraData, 34);
class XmaContext { class XmaContext {
public: public:
static constexpr uint32_t kBytesPerPacket = 2048; static const uint32_t kBytesPerPacket = 2048;
static constexpr uint32_t kBitsPerPacket = kBytesPerPacket * 8; static const uint32_t kBitsPerPacket = kBytesPerPacket * 8;
static constexpr uint32_t kBitsPerHeader = 32; static const uint32_t kBitsPerHeader = 32;
static constexpr uint32_t kBytesPerSample = 2; static const uint32_t kBytesPerSample = 2;
static constexpr uint32_t kSamplesPerFrame = 512; static const uint32_t kSamplesPerFrame = 512;
static constexpr uint32_t kSamplesPerSubframe = 128; static const uint32_t kSamplesPerSubframe = 128;
static constexpr uint32_t kBytesPerFrameChannel = static const uint32_t kBytesPerFrameChannel =
kSamplesPerFrame * kBytesPerSample; kSamplesPerFrame * kBytesPerSample;
static constexpr uint32_t kBytesPerSubframeChannel = static const uint32_t kBytesPerSubframeChannel =
kSamplesPerSubframe * kBytesPerSample; kSamplesPerSubframe * kBytesPerSample;
// static const uint32_t kOutputBytesPerBlock = 256; // static const uint32_t kOutputBytesPerBlock = 256;
// static const uint32_t kOutputMaxSizeBytes = 31 * kOutputBytesPerBlock; // static const uint32_t kOutputMaxSizeBytes = 31 * kOutputBytesPerBlock;
explicit XmaContext(); explicit XmaContext();
virtual ~XmaContext(); ~XmaContext();
virtual int Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { virtual int Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) {
return 0; return 0;

View File

@ -10,6 +10,8 @@
#include "xenia/apu/xma_context_new.h" #include "xenia/apu/xma_context_new.h"
#include "xenia/apu/xma_helpers.h" #include "xenia/apu/xma_helpers.h"
#include <algorithm>
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#include "xenia/base/profiling.h" #include "xenia/base/profiling.h"

View File

@ -45,28 +45,28 @@ static constexpr int kIdToSampleRate[4] = {24000, 32000, 44100, 48000};
class XmaContextNew : public XmaContext { class XmaContextNew : public XmaContext {
public: public:
static constexpr uint32_t kBytesPerPacket = 2048; static const uint32_t kBytesPerPacket = 2048;
static constexpr uint32_t kBytesPerPacketHeader = 4; static const uint32_t kBytesPerPacketHeader = 4;
static constexpr uint32_t kBytesPerPacketData = static const uint32_t kBytesPerPacketData =
kBytesPerPacket - kBytesPerPacketHeader; kBytesPerPacket - kBytesPerPacketHeader;
static constexpr uint32_t kBitsPerPacket = kBytesPerPacket * 8; static const uint32_t kBitsPerPacket = kBytesPerPacket * 8;
static constexpr uint32_t kBitsPerPacketHeader = 32; static const uint32_t kBitsPerPacketHeader = 32;
static constexpr uint32_t kBitsPerFrameHeader = 15; static const uint32_t kBitsPerFrameHeader = 15;
static constexpr uint32_t kBytesPerSample = 2; static const uint32_t kBytesPerSample = 2;
static constexpr uint32_t kSamplesPerFrame = 512; static const uint32_t kSamplesPerFrame = 512;
static constexpr uint32_t kSamplesPerSubframe = 128; static const uint32_t kSamplesPerSubframe = 128;
static constexpr uint32_t kBytesPerFrameChannel = static const uint32_t kBytesPerFrameChannel =
kSamplesPerFrame * kBytesPerSample; kSamplesPerFrame * kBytesPerSample;
static constexpr uint32_t kBytesPerSubframeChannel = static const uint32_t kBytesPerSubframeChannel =
kSamplesPerSubframe * kBytesPerSample; kSamplesPerSubframe * kBytesPerSample;
static constexpr uint32_t kOutputBytesPerBlock = 256; static const uint32_t kOutputBytesPerBlock = 256;
static constexpr uint32_t kOutputMaxSizeBytes = 31 * kOutputBytesPerBlock; static const uint32_t kOutputMaxSizeBytes = 31 * kOutputBytesPerBlock;
static constexpr uint32_t kLastFrameMarker = 0x7FFF; static const uint32_t kLastFrameMarker = 0x7FFF;
static constexpr uint32_t kMaxFrameSizeinBits = 0x4000 - kBitsPerPacketHeader; static const uint32_t kMaxFrameSizeinBits = 0x4000 - kBitsPerPacketHeader;
explicit XmaContextNew(); explicit XmaContextNew();
~XmaContextNew(); ~XmaContextNew();

View File

@ -9,6 +9,7 @@
#include "xenia/apu/xma_context_old.h" #include "xenia/apu/xma_context_old.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include "xenia/apu/xma_decoder.h" #include "xenia/apu/xma_decoder.h"

View File

@ -18,7 +18,7 @@ namespace xe {
namespace apu { namespace apu {
namespace xma { namespace xma {
static constexpr uint32_t kMaxFrameLength = 0x7FFF; static const uint32_t kMaxFrameLength = 0x7FFF;
// Get number of frames that /begin/ in this packet. This is valid only for XMA2 // Get number of frames that /begin/ in this packet. This is valid only for XMA2
// packets // packets

View File

@ -32,7 +32,7 @@ class XmaRegisterFile {
static const XmaRegisterInfo* GetRegisterInfo(uint32_t index); static const XmaRegisterInfo* GetRegisterInfo(uint32_t index);
static constexpr size_t kRegisterCount = (0xFFFF + 1) / 4; static const size_t kRegisterCount = (0xFFFF + 1) / 4;
uint32_t values[kRegisterCount]; uint32_t values[kRegisterCount];
uint32_t operator[](uint32_t reg) const { return values[reg]; } uint32_t operator[](uint32_t reg) const { return values[reg]; }

View File

@ -10,6 +10,7 @@
#include "xenia/base/arena.h" #include "xenia/base/arena.h"
#include <cstring> #include <cstring>
#include <memory>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"

View File

@ -46,8 +46,8 @@ class BitMap {
std::vector<uint64_t>& data() { return data_; } std::vector<uint64_t>& data() { return data_; }
private: private:
constexpr static size_t kDataSize = 8; const static size_t kDataSize = 8;
constexpr static size_t kDataSizeBits = kDataSize * 8; const static size_t kDataSizeBits = kDataSize * 8;
std::vector<uint64_t> data_; std::vector<uint64_t> data_;
inline size_t TryAcquireAt(size_t i); inline size_t TryAcquireAt(size_t i);
}; };

View File

@ -30,7 +30,7 @@ std::pair<size_t, size_t> NextUnsetRange(const Block* bits, size_t first,
return std::make_pair(size_t(first), size_t(0)); return std::make_pair(size_t(first), size_t(0));
} }
size_t last = first + length - 1; size_t last = first + length - 1;
constexpr size_t block_bits = sizeof(Block) * CHAR_BIT; const size_t block_bits = sizeof(Block) * CHAR_BIT;
size_t block_first = first / block_bits; size_t block_first = first / block_bits;
size_t block_last = last / block_bits; size_t block_last = last / block_bits;
size_t range_start = SIZE_MAX; size_t range_start = SIZE_MAX;
@ -80,7 +80,7 @@ void SetRange(Block* bits, size_t first, size_t length) {
return; return;
} }
size_t last = first + length - 1; size_t last = first + length - 1;
constexpr size_t block_bits = sizeof(Block) * CHAR_BIT; const size_t block_bits = sizeof(Block) * CHAR_BIT;
size_t block_first = first / block_bits; size_t block_first = first / block_bits;
size_t block_last = last / block_bits; size_t block_last = last / block_bits;
Block set_first = ~((Block(1) << (first & (block_bits - 1))) - 1); Block set_first = ~((Block(1) << (first & (block_bits - 1))) - 1);

View File

@ -9,6 +9,8 @@
#include "xenia/base/clock.h" #include "xenia/base/clock.h"
#include <algorithm>
#include <limits>
#include <mutex> #include <mutex>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"

View File

@ -53,5 +53,4 @@ uint64_t Clock::QueryHostUptimeMillis() {
return host_tick_count_platform() * 1000 / host_tick_frequency_platform(); return host_tick_count_platform() * 1000 / host_tick_frequency_platform();
} }
uint64_t Clock::QueryHostInterruptTime() { return host_tick_count_platform(); }
} // namespace xe } // namespace xe

View File

@ -7,11 +7,14 @@
****************************************************************************** ******************************************************************************
*/ */
#include <string>
#include <vector>
#include "xenia/base/console_app_main.h" #include "xenia/base/console_app_main.h"
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
int main(int argc, char** argv) { extern "C" int main(int argc, char** argv) {
xe::ConsoleAppEntryInfo entry_info = xe::GetConsoleAppEntryInfo(); xe::ConsoleAppEntryInfo entry_info = xe::GetConsoleAppEntryInfo();
if (!entry_info.transparent_options) { if (!entry_info.transparent_options) {

View File

@ -8,6 +8,7 @@
*/ */
#include <cstdlib> #include <cstdlib>
#include <vector>
#include "xenia/base/console_app_main.h" #include "xenia/base/console_app_main.h"
#include "xenia/base/main_win.h" #include "xenia/base/main_win.h"

View File

@ -9,7 +9,7 @@
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include <iostream> #include <iostream>
#define UTF_CPP_CPLUSPLUS 202002L #define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h" #include "third_party/utfcpp/source/utf8.h"
#include "xenia/base/console.h" #include "xenia/base/console.h"
@ -125,9 +125,9 @@ std::string EscapeBasicString(const std::string_view view) {
result += "\\\\"; result += "\\\\";
} else if (c < 0x20 || c == 0x7F) { } else if (c < 0x20 || c == 0x7F) {
if (c <= 0xFFFF) { if (c <= 0xFFFF) {
result += fmt::format("\\u{:04X}", static_cast<uint32_t>(c)); result += fmt::format("\\u{:04X}", c);
} else { } else {
result += fmt::format("\\u{:08X}", static_cast<uint32_t>(c)); result += fmt::format("\\u{:08X}", c);
} }
} else { } else {
utfcpp::append(static_cast<char32_t>(c), result); utfcpp::append(static_cast<char32_t>(c), result);
@ -159,7 +159,7 @@ std::string EscapeMultilineBasicString(const std::string_view view) {
if (c == '\b') { if (c == '\b') {
result += "\\b"; result += "\\b";
} else if (c == '\t' || c == '\n') { } else if (c == '\t' || c == '\n') {
result += static_cast<uint32_t>(c); result += c;
} else if (c == '\f') { } else if (c == '\f') {
result += "\\f"; result += "\\f";
} else if (c == '\r') { } else if (c == '\r') {
@ -171,9 +171,9 @@ std::string EscapeMultilineBasicString(const std::string_view view) {
result += "\\\\"; result += "\\\\";
} else if (c < 0x20 || c == 0x7F) { } else if (c < 0x20 || c == 0x7F) {
if (c <= 0xFFFF) { if (c <= 0xFFFF) {
result += fmt::format("\\u{:04X}", static_cast<uint32_t>(c)); result += fmt::format("\\u{:04X}", c);
} else { } else {
result += fmt::format("\\u{:08X}", static_cast<uint32_t>(c)); result += fmt::format("\\u{:08X}", c);
} }
} else { } else {
utfcpp::append(static_cast<char32_t>(c), result); utfcpp::append(static_cast<char32_t>(c), result);
@ -189,8 +189,8 @@ std::string EscapeMultilineBasicString(const std::string_view view) {
} }
std::string EscapeString(const std::string_view view) { std::string EscapeString(const std::string_view view) {
constexpr auto multiline_chars = std::string_view("\r\n"); const auto multiline_chars = std::string_view("\r\n");
constexpr auto escape_chars = std::string_view( const auto escape_chars = std::string_view(
"\0\b\v\f" "\0\b\v\f"
"\x01\x02\x03\x04\x05\x06\x07\x0E\x0F" "\x01\x02\x03\x04\x05\x06\x07\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"

View File

@ -10,6 +10,7 @@
#include "xenia/base/exception_handler.h" #include "xenia/base/exception_handler.h"
#include <signal.h> #include <signal.h>
#include <ucontext.h>
#include <cstdint> #include <cstdint>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
@ -167,7 +168,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
mcontext.gregs[REG_EFL] = greg_t(thread_context.eflags); mcontext.gregs[REG_EFL] = greg_t(thread_context.eflags);
uint32_t modified_register_index; uint32_t modified_register_index;
// The order must match the order in X64Register. // The order must match the order in X64Register.
static constexpr size_t kIntRegisterMap[] = { static const size_t kIntRegisterMap[] = {
REG_RAX, REG_RCX, REG_RDX, REG_RBX, REG_RSP, REG_RBP, REG_RAX, REG_RCX, REG_RDX, REG_RBX, REG_RSP, REG_RBP,
REG_RSI, REG_RDI, REG_R8, REG_R9, REG_R10, REG_R11, REG_RSI, REG_RDI, REG_R8, REG_R9, REG_R10, REG_R11,
REG_R12, REG_R13, REG_R14, REG_R15, REG_R12, REG_R13, REG_R14, REG_R15,

View File

@ -9,6 +9,8 @@
#include "xenia/base/filesystem.h" #include "xenia/base/filesystem.h"
#include <algorithm>
namespace xe { namespace xe {
namespace filesystem { namespace filesystem {
@ -22,42 +24,5 @@ bool CreateParentFolder(const std::filesystem::path& path) {
return true; return true;
} }
std::error_code CreateFolder(const std::filesystem::path& path) {
if (std::filesystem::exists(path)) {
return {};
}
std::error_code ec;
if (std::filesystem::create_directories(path, ec)) {
return {};
}
return ec;
}
std::vector<FileInfo> ListDirectories(const std::filesystem::path& path) {
std::vector<FileInfo> files = ListFiles(path);
std::vector<FileInfo> directories = {};
std::copy_if(files.cbegin(), files.cend(), std::back_inserter(directories),
[](const FileInfo& file) {
return file.type == FileInfo::Type::kDirectory;
});
return std::move(directories);
}
std::vector<FileInfo> FilterByName(const std::vector<FileInfo>& files,
const std::regex pattern) {
std::vector<FileInfo> filtered_entries = {};
std::copy_if(
files.cbegin(), files.cend(), std::back_inserter(filtered_entries),
[pattern](const FileInfo& file) {
return std::regex_match(file.name.filename().string(), pattern);
});
return std::move(filtered_entries);
}
} // namespace filesystem } // namespace filesystem
} // namespace xe } // namespace xe

View File

@ -45,10 +45,6 @@ std::filesystem::path GetUserFolder();
// attempting to create it. // attempting to create it.
bool CreateParentFolder(const std::filesystem::path& path); bool CreateParentFolder(const std::filesystem::path& path);
// Creates folder on specified path.
// If folder already exists it returns success (no error).
std::error_code CreateFolder(const std::filesystem::path& path);
// Creates an empty file at the given path, overwriting if it exists. // Creates an empty file at the given path, overwriting if it exists.
bool CreateEmptyFile(const std::filesystem::path& path); bool CreateEmptyFile(const std::filesystem::path& path);
@ -69,14 +65,14 @@ bool TruncateStdioFile(FILE* file, uint64_t length);
struct FileAccess { struct FileAccess {
// Implies kFileReadData. // Implies kFileReadData.
static constexpr uint32_t kGenericRead = 0x80000000; static const uint32_t kGenericRead = 0x80000000;
// Implies kFileWriteData. // Implies kFileWriteData.
static constexpr uint32_t kGenericWrite = 0x40000000; static const uint32_t kGenericWrite = 0x40000000;
static constexpr uint32_t kGenericExecute = 0x20000000; static const uint32_t kGenericExecute = 0x20000000;
static constexpr uint32_t kGenericAll = 0x10000000; static const uint32_t kGenericAll = 0x10000000;
static constexpr uint32_t kFileReadData = 0x00000001; static const uint32_t kFileReadData = 0x00000001;
static constexpr uint32_t kFileWriteData = 0x00000002; static const uint32_t kFileWriteData = 0x00000002;
static constexpr uint32_t kFileAppendData = 0x00000004; static const uint32_t kFileAppendData = 0x00000004;
}; };
class FileHandle { class FileHandle {

View File

@ -214,7 +214,7 @@ bool IsAndroidContentUri(const std::string_view source) {
// still including // in the comparison to distinguish from a file with a name // still including // in the comparison to distinguish from a file with a name
// starting from content: (as this is the main purpose of this code - // starting from content: (as this is the main purpose of this code -
// separating URIs from file paths) more clearly. // separating URIs from file paths) more clearly.
static constexpr char kContentSchema[] = "content://"; static const char kContentSchema[] = "content://";
constexpr size_t kContentSchemaLength = xe::countof(kContentSchema) - 1; constexpr size_t kContentSchemaLength = xe::countof(kContentSchema) - 1;
return source.size() >= kContentSchemaLength && return source.size() >= kContentSchemaLength &&
!xe_strncasecmp(source.data(), kContentSchema, kContentSchemaLength); !xe_strncasecmp(source.data(), kContentSchema, kContentSchemaLength);

View File

@ -16,12 +16,14 @@
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <ftw.h> #include <ftw.h>
#include <libgen.h>
#include <pwd.h> #include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <iostream>
namespace xe { namespace xe {
@ -190,26 +192,20 @@ std::unique_ptr<FileHandle> FileHandle::OpenExisting(
return std::make_unique<PosixFileHandle>(path, handle); return std::make_unique<PosixFileHandle>(path, handle);
} }
std::optional<FileInfo> GetInfo(const std::filesystem::path& path) { bool GetInfo(const std::filesystem::path& path, FileInfo* out_info) {
FileInfo info{};
struct stat st; struct stat st;
if (stat(path.c_str(), &st) == 0) { if (stat(path.c_str(), &st) == 0) {
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
info.type = FileInfo::Type::kDirectory; out_info->type = FileInfo::Type::kDirectory;
// On Linux st.st_size can have non-zero size (generally 4096) so make 0
info.total_size = 0;
} else { } else {
info.type = FileInfo::Type::kFile; out_info->type = FileInfo::Type::kFile;
info.total_size = st.st_size;
} }
info.path = path.parent_path(); out_info->create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
info.name = path.filename(); out_info->access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime); out_info->write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime); return true;
info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
return std::move(info);
} }
return {}; return false;
} }
std::vector<FileInfo> ListFiles(const std::filesystem::path& path) { std::vector<FileInfo> ListFiles(const std::filesystem::path& path) {
@ -244,7 +240,7 @@ std::vector<FileInfo> ListFiles(const std::filesystem::path& path) {
result.push_back(info); result.push_back(info);
} }
closedir(dir); closedir(dir);
return std::move(result); return result;
} }
bool SetAttributes(const std::filesystem::path& path, uint64_t attributes) { bool SetAttributes(const std::filesystem::path& path, uint64_t attributes) {

View File

@ -9,6 +9,8 @@
#include "xenia/base/filesystem_wildcard.h" #include "xenia/base/filesystem_wildcard.h"
#include <algorithm>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"

View File

@ -263,6 +263,30 @@ std::vector<FileInfo> ListFiles(const std::filesystem::path& path) {
return result; return result;
} }
std::vector<FileInfo> ListDirectories(const std::filesystem::path& path) {
std::vector<FileInfo> files = ListFiles(path);
std::vector<FileInfo> directories = {};
std::copy_if(
files.cbegin(), files.cend(), std::back_inserter(directories),
[](FileInfo file) { return file.type == FileInfo::Type::kDirectory; });
return directories;
}
std::vector<FileInfo> FilterByName(const std::vector<FileInfo>& files,
const std::regex pattern) {
std::vector<FileInfo> filtered_entries = {};
std::copy_if(files.cbegin(), files.cend(),
std::back_inserter(filtered_entries), [pattern](FileInfo file) {
return std::regex_match(file.name.filename().string(),
pattern);
});
return filtered_entries;
}
bool SetAttributes(const std::filesystem::path& path, uint64_t attributes) { bool SetAttributes(const std::filesystem::path& path, uint64_t attributes) {
return SetFileAttributes(path.c_str(), static_cast<DWORD>(attributes)); return SetFileAttributes(path.c_str(), static_cast<DWORD>(attributes));
} }

View File

@ -11,6 +11,7 @@
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
#include <iostream>
// TODO(gibbed): UTF8 support. // TODO(gibbed): UTF8 support.

View File

@ -14,23 +14,23 @@
namespace xe::literals { namespace xe::literals {
constexpr size_t operator""_KiB(const unsigned long long int x) { constexpr size_t operator""_KiB(unsigned long long int x) {
return 1024ULL * x; return 1024ULL * x;
} }
constexpr size_t operator""_MiB(const unsigned long long int x) { constexpr size_t operator""_MiB(unsigned long long int x) {
return 1024_KiB * x; return 1024_KiB * x;
} }
constexpr size_t operator""_GiB(const unsigned long long int x) { constexpr size_t operator""_GiB(unsigned long long int x) {
return 1024_MiB * x; return 1024_MiB * x;
} }
constexpr size_t operator""_TiB(const unsigned long long int x) { constexpr size_t operator""_TiB(unsigned long long int x) {
return 1024_GiB * x; return 1024_GiB * x;
} }
constexpr size_t operator""_PiB(const unsigned long long int x) { constexpr size_t operator""_PiB(unsigned long long int x) {
return 1024_TiB * x; return 1024_TiB * x;
} }

View File

@ -10,8 +10,11 @@
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include <algorithm> #include <algorithm>
#include <atomic>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <mutex>
#include <vector>
#include "third_party/disruptorplus/include/disruptorplus/multi_threaded_claim_strategy.hpp" #include "third_party/disruptorplus/include/disruptorplus/multi_threaded_claim_strategy.hpp"
#include "third_party/disruptorplus/include/disruptorplus/ring_buffer.hpp" #include "third_party/disruptorplus/include/disruptorplus/ring_buffer.hpp"
@ -241,12 +244,12 @@ class Logger {
} }
private: private:
static constexpr size_t kBufferSize = 8_MiB; static const size_t kBufferSize = 8_MiB;
uint8_t buffer_[kBufferSize]; uint8_t buffer_[kBufferSize];
static constexpr size_t kBlockSize = 256; static const size_t kBlockSize = 256;
static constexpr size_t kBlockCount = kBufferSize / kBlockSize; static const size_t kBlockCount = kBufferSize / kBlockSize;
static constexpr size_t kBlockIndexMask = kBlockCount - 1; static const size_t kBlockIndexMask = kBlockCount - 1;
static const size_t kClaimStrategyFootprint = static const size_t kClaimStrategyFootprint =
sizeof(std::atomic<dp::sequence_t>[kBlockCount]); sizeof(std::atomic<dp::sequence_t>[kBlockCount]);
@ -350,14 +353,14 @@ class Logger {
? line_range.second[line_range.second_length - 1] ? line_range.second[line_range.second_length - 1]
: line_range.first[line_range.first_length - 1]; : line_range.first[line_range.first_length - 1];
if (last_char != '\n') { if (last_char != '\n') {
constexpr char suffix[1] = {'\n'}; const char suffix[1] = {'\n'};
Write(suffix, 1); Write(suffix, 1);
} }
rb.EndRead(std::move(line_range)); rb.EndRead(std::move(line_range));
} else { } else {
// Always ensure there is a newline. // Always ensure there is a newline.
constexpr char suffix[1] = {'\n'}; const char suffix[1] = {'\n'};
Write(suffix, 1); Write(suffix, 1);
} }

View File

@ -1,46 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2023 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include <xbyak/xbyak/xbyak_util.h>
#include "xenia/ui/window_gtk.h"
class StartupCpuFeatureCheck {
public:
StartupCpuFeatureCheck() {
Xbyak::util::Cpu cpu;
const char* error_message = nullptr;
if (!cpu.has(Xbyak::util::Cpu::tAVX)) {
error_message =
"Your CPU does not support AVX, which is required by Xenia. See "
"the "
"FAQ for system requirements at https://xenia.jp";
}
if (error_message == nullptr) {
return;
} else {
GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
auto dialog =
gtk_message_dialog_new(nullptr, flags, GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE, "%s", error_message);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
exit(1);
}
}
};
// This is a hack to get an instance of StartupAvxCheck
// constructed before any initialization code,
// where the AVX check then happens in the constructor.
// Ref:
// https://reviews.llvm.org/D12689#243295
__attribute__((
init_priority(101))) static StartupCpuFeatureCheck gStartupAvxCheck;

View File

@ -7,6 +7,7 @@
****************************************************************************** ******************************************************************************
*/ */
#include <malloc.h>
#include <cstring> #include <cstring>
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
@ -59,7 +60,7 @@ static void RequestWin32HighResolutionTimer() {
ULONG minimum_resolution, maximum_resolution, current_resolution; ULONG minimum_resolution, maximum_resolution, current_resolution;
nt_query_timer_resolution(&minimum_resolution, &maximum_resolution, nt_query_timer_resolution(&minimum_resolution, &maximum_resolution,
&current_resolution); &current_resolution);
nt_set_timer_resolution(maximum_resolution, true, &current_resolution); nt_set_timer_resolution(maximum_resolution, TRUE, &current_resolution);
} }
static void RequestWin32MMCSS() { static void RequestWin32MMCSS() {
@ -73,7 +74,7 @@ static void RequestWin32MMCSS() {
dwm_enable_mmcss = reinterpret_cast<decltype(dwm_enable_mmcss)>( dwm_enable_mmcss = reinterpret_cast<decltype(dwm_enable_mmcss)>(
GetProcAddress(dwmapi_module, "DwmEnableMMCSS")); GetProcAddress(dwmapi_module, "DwmEnableMMCSS"));
if (dwm_enable_mmcss) { if (dwm_enable_mmcss) {
dwm_enable_mmcss(true); dwm_enable_mmcss(TRUE);
} }
FreeLibrary(dwmapi_module); FreeLibrary(dwmapi_module);
} }

View File

@ -7,7 +7,9 @@
****************************************************************************** ******************************************************************************
*/ */
#include <memory>
#include <mutex> #include <mutex>
#include <vector>
#include "third_party/fmt/include/fmt/format.h" #include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
@ -27,7 +29,7 @@ class Win32MappedMemory : public MappedMemory {
public: public:
// CreateFile returns INVALID_HANDLE_VALUE in case of failure. // CreateFile returns INVALID_HANDLE_VALUE in case of failure.
// chrispy: made inline const to get around clang error // chrispy: made inline const to get around clang error
static inline constexpr HANDLE kFileHandleInvalid = INVALID_HANDLE_VALUE; static inline const HANDLE kFileHandleInvalid = INVALID_HANDLE_VALUE;
// CreateFileMapping returns nullptr in case of failure. // CreateFileMapping returns nullptr in case of failure.
static constexpr HANDLE kMappingHandleInvalid = nullptr; static constexpr HANDLE kMappingHandleInvalid = nullptr;

View File

@ -16,6 +16,8 @@
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
#include <algorithm>
DEFINE_bool( DEFINE_bool(
writable_executable_memory, true, writable_executable_memory, true,
"Allow mapping memory with both write and execute access, for simulating " "Allow mapping memory with both write and execute access, for simulating "
@ -471,7 +473,7 @@ void copy_and_swap_16_unaligned(void* dst_ptr, const void* src_ptr,
auto dst = reinterpret_cast<uint8_t*>(dst_ptr); auto dst = reinterpret_cast<uint8_t*>(dst_ptr);
auto src = reinterpret_cast<const uint8_t*>(src_ptr); auto src = reinterpret_cast<const uint8_t*>(src_ptr);
constexpr uint8x16_t tbl_idx = const uint8x16_t tbl_idx =
vcombine_u8(vcreate_u8(UINT64_C(0x0607040502030001)), vcombine_u8(vcreate_u8(UINT64_C(0x0607040502030001)),
vcreate_u8(UINT64_C(0x0E0F0C0D0A0B0809))); vcreate_u8(UINT64_C(0x0E0F0C0D0A0B0809)));
@ -505,7 +507,7 @@ void copy_and_swap_32_unaligned(void* dst_ptr, const void* src_ptr,
auto dst = reinterpret_cast<uint8_t*>(dst_ptr); auto dst = reinterpret_cast<uint8_t*>(dst_ptr);
auto src = reinterpret_cast<const uint8_t*>(src_ptr); auto src = reinterpret_cast<const uint8_t*>(src_ptr);
constexpr uint8x16_t tbl_idx = const uint8x16_t tbl_idx =
vcombine_u8(vcreate_u8(UINT64_C(0x405060700010203)), vcombine_u8(vcreate_u8(UINT64_C(0x405060700010203)),
vcreate_u8(UINT64_C(0x0C0D0E0F08090A0B))); vcreate_u8(UINT64_C(0x0C0D0E0F08090A0B)));
@ -537,7 +539,7 @@ void copy_and_swap_64_unaligned(void* dst_ptr, const void* src_ptr,
auto dst = reinterpret_cast<uint8_t*>(dst_ptr); auto dst = reinterpret_cast<uint8_t*>(dst_ptr);
auto src = reinterpret_cast<const uint8_t*>(src_ptr); auto src = reinterpret_cast<const uint8_t*>(src_ptr);
constexpr uint8x16_t tbl_idx = const uint8x16_t tbl_idx =
vcombine_u8(vcreate_u8(UINT64_C(0x0001020304050607)), vcombine_u8(vcreate_u8(UINT64_C(0x0001020304050607)),
vcreate_u8(UINT64_C(0x08090A0B0C0D0E0F))); vcreate_u8(UINT64_C(0x08090A0B0C0D0E0F)));

View File

@ -813,12 +813,12 @@ template <unsigned Size>
static void smallset_const(void* destination, unsigned char fill_value) { static void smallset_const(void* destination, unsigned char fill_value) {
#if XE_ARCH_AMD64 == 1 && XE_COMPILER_MSVC == 1 #if XE_ARCH_AMD64 == 1 && XE_COMPILER_MSVC == 1
if constexpr ((Size & 7) == 0) { if constexpr ((Size & 7) == 0) {
static unsigned long long fill = unsigned long long fill =
static_cast<unsigned long long>(fill_value) * 0x0101010101010101ULL; static_cast<unsigned long long>(fill_value) * 0x0101010101010101ULL;
__stosq((unsigned long long*)destination, fill, Size / 8); __stosq((unsigned long long*)destination, fill, Size / 8);
} else if constexpr ((Size & 3) == 0) { } else if constexpr ((Size & 3) == 0) {
static unsigned long fill = static constexpr unsigned long fill =
static_cast<unsigned long>(fill_value) * 0x01010101U; static_cast<unsigned long>(fill_value) * 0x01010101U;
__stosd((unsigned long*)destination, fill, Size / 4); __stosd((unsigned long*)destination, fill, Size / 4);
// dont even bother with movsw, i think the operand size override prefix // dont even bother with movsw, i think the operand size override prefix

View File

@ -13,9 +13,6 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#include <cstddef> #include <cstddef>
#include <fstream>
#include <mutex>
#include <sstream>
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
@ -82,50 +79,19 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
} }
} }
PageAccess ToXeniaProtectFlags(const char* protection) {
if (protection[0] == 'r' && protection[1] == 'w' && protection[2] == 'x') {
return PageAccess::kExecuteReadWrite;
}
if (protection[0] == 'r' && protection[1] == '-' && protection[2] == 'x') {
return PageAccess::kExecuteReadOnly;
}
if (protection[0] == 'r' && protection[1] == 'w' && protection[2] == '-') {
return PageAccess::kReadWrite;
}
if (protection[0] == 'r' && protection[1] == '-' && protection[2] == '-') {
return PageAccess::kReadOnly;
}
return PageAccess::kNoAccess;
}
bool IsWritableExecutableMemorySupported() { return true; } bool IsWritableExecutableMemorySupported() { return true; }
struct MappedFileRange {
uintptr_t region_begin;
uintptr_t region_end;
};
std::vector<MappedFileRange> mapped_file_ranges;
std::mutex g_mapped_file_ranges_mutex;
void* AllocFixed(void* base_address, size_t length, void* AllocFixed(void* base_address, size_t length,
AllocationType allocation_type, PageAccess access) { AllocationType allocation_type, PageAccess access) {
// mmap does not support reserve / commit, so ignore allocation_type. // mmap does not support reserve / commit, so ignore allocation_type.
uint32_t prot = ToPosixProtectFlags(access); uint32_t prot = ToPosixProtectFlags(access);
int flags = MAP_PRIVATE | MAP_ANONYMOUS; int flags = 0;
if (base_address != nullptr) { if (base_address != nullptr) {
if (allocation_type == AllocationType::kCommit) { flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
if (Protect(base_address, length, access)) { } else {
return base_address; flags = MAP_PRIVATE | MAP_ANONYMOUS;
} }
return nullptr;
}
flags |= MAP_FIXED_NOREPLACE;
}
void* result = mmap(base_address, length, prot, flags, -1, 0); void* result = mmap(base_address, length, prot, flags, -1, 0);
if (result != MAP_FAILED) { if (result != MAP_FAILED) {
return result; return result;
} }
@ -134,90 +100,19 @@ void* AllocFixed(void* base_address, size_t length,
bool DeallocFixed(void* base_address, size_t length, bool DeallocFixed(void* base_address, size_t length,
DeallocationType deallocation_type) { DeallocationType deallocation_type) {
const auto region_begin = reinterpret_cast<uintptr_t>(base_address);
const uintptr_t region_end =
reinterpret_cast<uintptr_t>(base_address) + length;
std::lock_guard guard(g_mapped_file_ranges_mutex);
for (const auto& mapped_range : mapped_file_ranges) {
if (region_begin >= mapped_range.region_begin &&
region_end <= mapped_range.region_end) {
switch (deallocation_type) {
case DeallocationType::kDecommit:
return Protect(base_address, length, PageAccess::kNoAccess);
case DeallocationType::kRelease:
assert_always("Error: Tried to release mapped memory!");
default:
assert_unhandled_case(deallocation_type);
}
}
}
switch (deallocation_type) {
case DeallocationType::kDecommit:
return Protect(base_address, length, PageAccess::kNoAccess);
case DeallocationType::kRelease:
return munmap(base_address, length) == 0; return munmap(base_address, length) == 0;
default:
assert_unhandled_case(deallocation_type);
}
} }
bool Protect(void* base_address, size_t length, PageAccess access, bool Protect(void* base_address, size_t length, PageAccess access,
PageAccess* out_old_access) { PageAccess* out_old_access) {
if (out_old_access) { // Linux does not have a syscall to query memory permissions.
size_t length_copy = length; assert_null(out_old_access);
QueryProtect(base_address, length_copy, *out_old_access);
}
uint32_t prot = ToPosixProtectFlags(access); uint32_t prot = ToPosixProtectFlags(access);
return mprotect(base_address, length, prot) == 0; return mprotect(base_address, length, prot) == 0;
} }
bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) { bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
// No generic POSIX solution exists. The Linux solution should work on all
// Linux kernel based OS, including Android.
std::ifstream memory_maps;
memory_maps.open("/proc/self/maps", std::ios_base::in);
std::string maps_entry_string;
while (std::getline(memory_maps, maps_entry_string)) {
std::stringstream entry_stream(maps_entry_string);
uintptr_t map_region_begin, map_region_end;
char separator, protection[4];
entry_stream >> std::hex >> map_region_begin >> separator >>
map_region_end >> protection;
if (map_region_begin <= reinterpret_cast<uintptr_t>(base_address) &&
map_region_end > reinterpret_cast<uintptr_t>(base_address)) {
length = map_region_end - reinterpret_cast<uintptr_t>(base_address);
access_out = ToXeniaProtectFlags(protection);
// Look at the next consecutive mappings
while (std::getline(memory_maps, maps_entry_string)) {
std::stringstream next_entry_stream(maps_entry_string);
uintptr_t next_map_region_begin, next_map_region_end;
char next_protection[4];
next_entry_stream >> std::hex >> next_map_region_begin >> separator >>
next_map_region_end >> next_protection;
if (map_region_end == next_map_region_begin &&
access_out == ToXeniaProtectFlags(next_protection)) {
length =
next_map_region_end - reinterpret_cast<uintptr_t>(base_address);
continue;
}
break;
}
memory_maps.close();
return true;
}
}
memory_maps.close();
return false; return false;
} }
@ -287,41 +182,12 @@ void CloseFileMappingHandle(FileMappingHandle handle,
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
PageAccess access, size_t file_offset) { PageAccess access, size_t file_offset) {
uint32_t prot = ToPosixProtectFlags(access); uint32_t prot = ToPosixProtectFlags(access);
return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle,
int flags = MAP_SHARED; file_offset);
if (base_address != nullptr) {
flags |= MAP_FIXED_NOREPLACE;
}
void* result = mmap(base_address, length, prot, flags, handle, file_offset);
if (result != MAP_FAILED) {
std::lock_guard guard(g_mapped_file_ranges_mutex);
mapped_file_ranges.push_back(
{reinterpret_cast<uintptr_t>(result),
reinterpret_cast<uintptr_t>(result) + length});
return result;
}
return nullptr;
} }
bool UnmapFileView(FileMappingHandle handle, void* base_address, bool UnmapFileView(FileMappingHandle handle, void* base_address,
size_t length) { size_t length) {
std::lock_guard guard(g_mapped_file_ranges_mutex);
for (auto mapped_range = mapped_file_ranges.begin();
mapped_range != mapped_file_ranges.end();) {
if (mapped_range->region_begin ==
reinterpret_cast<uintptr_t>(base_address) &&
mapped_range->region_end ==
reinterpret_cast<uintptr_t>(base_address) + length) {
mapped_file_ranges.erase(mapped_range);
return munmap(base_address, length) == 0;
}
++mapped_range;
}
// TODO: Implement partial file unmapping.
assert_always("Error: Partial unmapping of files not yet supported.");
return munmap(base_address, length) == 0; return munmap(base_address, length) == 0;
} }

View File

@ -1,193 +0,0 @@
/* Xenia: minor tweaks to bring up to date with winnt.h */
#include <cstdint>
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* NT image format (to be used when the Win32 SDK version of WINNT.H cannot) */
#define XIMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
#define XIMAGE_OS2_SIGNATURE 0x454E /* NE */
#define XIMAGE_OS2_SIGNATURE_LE 0x454C /* LE */
#define XIMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
typedef struct _XIMAGE_DOS_HEADER { /* DOS .EXE header */
uint16_t e_magic; /* Magic number */
uint16_t e_cblp; /* Bytes on last page of file */
uint16_t e_cp; /* Pages in file */
uint16_t e_crlc; /* Relocations */
uint16_t e_cparhdr; /* Size of header in paragraphs */
uint16_t e_minalloc; /* Minimum extra paragraphs needed */
uint16_t e_maxalloc; /* Maximum extra paragraphs needed */
uint16_t e_ss; /* Initial (relative) SS value */
uint16_t e_sp; /* Initial SP value */
uint16_t e_csum; /* Checksum */
uint16_t e_ip; /* Initial IP value */
uint16_t e_cs; /* Initial (relative) CS value */
uint16_t e_lfarlc; /* File address of relocation table */
uint16_t e_ovno; /* Overlay number */
uint16_t e_res[4]; /* Reserved words */
uint16_t e_oemid; /* OEM identifier (for e_oeminfo) */
uint16_t e_oeminfo; /* OEM information; e_oemid specific */
uint16_t e_res2[10]; /* Reserved words */
int32_t e_lfanew; /* File address of new exe header */
} XIMAGE_DOS_HEADER, *PXIMAGE_DOS_HEADER;
typedef struct _XIMAGE_OS2_HEADER { /* OS/2 .EXE header */
uint16_t ne_magic; /* Magic number */
int8_t ne_ver; /* Version number */
int8_t ne_rev; /* Revision number */
uint16_t ne_enttab; /* Offset of Entry Table */
uint16_t ne_cbenttab; /* Number of bytes in Entry Table */
int32_t ne_crc; /* Checksum of whole file */
uint16_t ne_flags; /* Flag word */
uint16_t ne_autodata; /* Automatic data segment number */
uint16_t ne_heap; /* Initial heap allocation */
uint16_t ne_stack; /* Initial stack allocation */
int32_t ne_csip; /* Initial CS:IP setting */
int32_t ne_sssp; /* Initial SS:SP setting */
uint16_t ne_cseg; /* Count of file segments */
uint16_t ne_cmod; /* Entries in Module Reference Table */
uint16_t ne_cbnrestab; /* Size of non-resident name table */
uint16_t ne_segtab; /* Offset of Segment Table */
uint16_t ne_rsrctab; /* Offset of Resource Table */
uint16_t ne_restab; /* Offset of resident name table */
uint16_t ne_modtab; /* Offset of Module Reference Table */
uint16_t ne_imptab; /* Offset of Imported Names Table */
int32_t ne_nrestab; /* Offset of Non-resident Names Table */
uint16_t ne_cmovent; /* Count of movable entries */
uint16_t ne_align; /* Segment alignment shift count */
uint16_t ne_cres; /* Count of resource segments */
uint8_t ne_exetyp; /* Target Operating system */
uint8_t ne_flagsothers; /* Other .EXE flags */
uint16_t ne_pretthunks; /* offset to return thunks */
uint16_t ne_psegrefbytes; /* offset to segment ref. bytes */
uint16_t ne_swaparea; /* Minimum code swap area size */
uint16_t ne_expver; /* Expected Windows version number */
} XIMAGE_OS2_HEADER, *PXIMAGE_OS2_HEADER;
typedef struct _XIMAGE_FILE_HEADER {
uint16_t Machine;
uint16_t NumberOfSections;
uint32_t TimeDateStamp;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
uint16_t SizeOfOptionalHeader;
uint16_t Characteristics;
} XIMAGE_FILE_HEADER, *PXIMAGE_FILE_HEADER;
#define XIMAGE_FILE_32BIT_MACHINE 0x0100 /* 32 bit word machine. */
#define XIMAGE_FILE_MACHINE_POWERPCBE 0x01F2 // IBM PowerPC Big-Endian
typedef struct _XIMAGE_DATA_DIRECTORY {
uint32_t VirtualAddress;
uint32_t Size;
} XIMAGE_DATA_DIRECTORY, *PXIMAGE_DATA_DIRECTORY;
#define XIMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
/*
* Optional header format.
*/
typedef struct _XIMAGE_OPTIONAL_HEADER {
/*
* Standard fields.
*/
uint16_t Magic;
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint;
uint32_t BaseOfCode;
uint32_t BaseOfData;
/*
* NT additional fields.
*/
uint32_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Reserved1;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t CheckSum;
uint16_t Subsystem;
uint16_t DllCharacteristics;
uint32_t SizeOfStackReserve;
uint32_t SizeOfStackCommit;
uint32_t SizeOfHeapReserve;
uint32_t SizeOfHeapCommit;
uint32_t LoaderFlags;
uint32_t NumberOfRvaAndSizes;
XIMAGE_DATA_DIRECTORY DataDirectory[XIMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} XIMAGE_OPTIONAL_HEADER, *PXIMAGE_OPTIONAL_HEADER;
typedef XIMAGE_OPTIONAL_HEADER XIMAGE_OPTIONAL_HEADER32;
#define XIMAGE_SIZEOF_NT_OPTIONAL_HEADER 224
#define XIMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
typedef struct _XIMAGE_NT_HEADERS {
uint32_t Signature;
XIMAGE_FILE_HEADER FileHeader;
XIMAGE_OPTIONAL_HEADER OptionalHeader;
} XIMAGE_NT_HEADERS, *PXIMAGE_NT_HEADERS;
typedef XIMAGE_NT_HEADERS XIMAGE_NT_HEADERS32;
#define XIMAGE_FIRST_SECTION(ntheader) \
((PXIMAGE_SECTION_HEADER)((uint8_t*)ntheader + \
offsetof(XIMAGE_NT_HEADERS, OptionalHeader) + \
((PXIMAGE_NT_HEADERS)(ntheader)) \
->FileHeader.SizeOfOptionalHeader))
#define XIMAGE_SUBSYSTEM_XBOX 14
/*
* Section header format.
*/
#define XIMAGE_SIZEOF_SHORT_NAME 8
typedef struct _XIMAGE_SECTION_HEADER {
uint8_t Name[XIMAGE_SIZEOF_SHORT_NAME];
union {
uint32_t PhysicalAddress;
uint32_t VirtualSize;
} Misc;
uint32_t VirtualAddress;
uint32_t SizeOfRawData;
uint32_t PointerToRawData;
uint32_t PointerToRelocations;
uint32_t PointerToLinenumbers;
uint16_t NumberOfRelocations;
uint16_t NumberOfLinenumbers;
uint32_t Characteristics;
} XIMAGE_SECTION_HEADER, *PXIMAGE_SECTION_HEADER;
#define XIMAGE_SIZEOF_SECTION_HEADER 40
typedef struct _XIMAGE_EXPORT_DIRECTORY {
uint32_t Characteristics;
uint32_t TimeDateStamp;
uint16_t MajorVersion;
uint16_t MinorVersion;
uint32_t Name;
uint32_t Base;
uint32_t NumberOfFunctions;
uint32_t NumberOfNames;
uint32_t** AddressOfFunctions;
uint32_t** AddressOfNames;
uint16_t** AddressOfNameOrdinals;
} XIMAGE_EXPORT_DIRECTORY, *PXIMAGE_EXPORT_DIRECTORY;

View File

@ -181,12 +181,12 @@
namespace xe { namespace xe {
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
constexpr char kPathSeparator = '\\'; const char kPathSeparator = '\\';
#else #else
constexpr char kPathSeparator = '/'; const char kPathSeparator = '/';
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
constexpr char kGuestPathSeparator = '\\'; const char kGuestPathSeparator = '\\';
} // namespace xe } // namespace xe
#if XE_ARCH_AMD64 == 1 #if XE_ARCH_AMD64 == 1

View File

@ -1,68 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2025 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/png_utils.h"
#include "xenia/base/filesystem.h"
#include "third_party/stb/stb_image.h"
namespace xe {
bool IsFilePngImage(const std::filesystem::path& file_path) {
FILE* file = xe::filesystem::OpenFile(file_path, "rb");
if (!file) {
return false;
}
constexpr uint8_t magic_size = 4;
char magic[magic_size];
if (fread(&magic, 1, magic_size, file) != magic_size) {
return false;
}
fclose(file);
if (magic[1] != 'P' || magic[2] != 'N' || magic[3] != 'G') {
return false;
}
return true;
}
std::pair<uint16_t, uint16_t> GetImageResolution(
const std::filesystem::path& file_path) {
FILE* file = xe::filesystem::OpenFile(file_path, "rb");
if (!file) {
return {};
}
int width, height, channels;
if (!stbi_info_from_file(file, &width, &height, &channels)) {
return {};
}
fclose(file);
return {width, height};
}
std::vector<uint8_t> ReadPngFromFile(const std::filesystem::path& file_path) {
FILE* file = xe::filesystem::OpenFile(file_path, "rb");
if (!file) {
return {};
}
const auto file_size = std::filesystem::file_size(file_path);
std::vector<uint8_t> data(file_size);
fread(data.data(), 1, file_size, file);
fclose(file);
return data;
}
} // namespace xe

View File

@ -1,27 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2025 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_PNG_UTILS_H_
#define XENIA_BASE_PNG_UTILS_H_
#include <filesystem>
#include <utility>
#include <vector>
namespace xe {
bool IsFilePngImage(const std::filesystem::path& file_path);
std::pair<uint16_t, uint16_t> GetImageResolution(
const std::filesystem::path& file_path);
std::vector<uint8_t> ReadPngFromFile(const std::filesystem::path& file_path);
} // namespace xe
#endif // XENIA_BASE_PNG_UTILS_H_

View File

@ -8,6 +8,8 @@ project("xenia-base")
links({ links({
"fmt", "fmt",
}) })
defines({
})
local_platform_files() local_platform_files()
removefiles({"console_app_main_*.cc"}) removefiles({"console_app_main_*.cc"})
removefiles({"main_init_*.cc"}) removefiles({"main_init_*.cc"})
@ -15,6 +17,4 @@ project("xenia-base")
"debug_visualizers.natvis", "debug_visualizers.natvis",
}) })
if enableTests then
include("testing") include("testing")
end

View File

@ -7,6 +7,9 @@
****************************************************************************** ******************************************************************************
*/ */
#include <algorithm>
#include <string>
// NOTE: this must be included before microprofile as macro expansion needs // NOTE: this must be included before microprofile as macro expansion needs
// XELOGI. // XELOGI.
#include "xenia/base/logging.h" #include "xenia/base/logging.h"

View File

@ -8,6 +8,7 @@
*/ */
#include "xenia/base/ring_buffer.h" #include "xenia/base/ring_buffer.h"
#include <algorithm>
#include <cstring> #include <cstring>
namespace xe { namespace xe {

View File

@ -10,6 +10,7 @@
#include "xenia/base/socket.h" #include "xenia/base/socket.h"
#include <mutex> #include <mutex>
#include <thread>
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
@ -95,7 +96,7 @@ class Win32Socket : public Socket {
// Keepalive for a looong time, as we may be paused by the debugger/etc. // Keepalive for a looong time, as we may be paused by the debugger/etc.
struct tcp_keepalive alive; struct tcp_keepalive alive;
alive.onoff = true; alive.onoff = TRUE;
alive.keepalivetime = 7200000; alive.keepalivetime = 7200000;
alive.keepaliveinterval = 6000; alive.keepaliveinterval = 6000;
DWORD bytes_returned; DWORD bytes_returned;
@ -208,7 +209,7 @@ class Win32SocketServer : public SocketServer {
SOCKET socket = socket_; SOCKET socket = socket_;
socket_ = INVALID_SOCKET; socket_ = INVALID_SOCKET;
linger so_linger; linger so_linger;
so_linger.l_onoff = true; so_linger.l_onoff = TRUE;
so_linger.l_linger = 30; so_linger.l_linger = 30;
setsockopt(socket, SOL_SOCKET, SO_LINGER, setsockopt(socket, SOL_SOCKET, SO_LINGER,
reinterpret_cast<const char*>(&so_linger), sizeof(so_linger)); reinterpret_cast<const char*>(&so_linger), sizeof(so_linger));
@ -230,7 +231,7 @@ class Win32SocketServer : public SocketServer {
return false; return false;
} }
struct tcp_keepalive alive; struct tcp_keepalive alive;
alive.onoff = true; alive.onoff = TRUE;
alive.keepalivetime = 7200000; alive.keepalivetime = 7200000;
alive.keepaliveinterval = 6000; alive.keepaliveinterval = 6000;
DWORD bytes_returned; DWORD bytes_returned;

View File

@ -10,6 +10,8 @@
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include <string.h> #include <string.h>
#include <algorithm>
#include <locale>
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
@ -20,7 +22,7 @@
#include <strings.h> #include <strings.h>
#endif // !XE_PLATFORM_WIN32 #endif // !XE_PLATFORM_WIN32
#define UTF_CPP_CPLUSPLUS 202002L #define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h" #include "third_party/utfcpp/source/utf8.h"
namespace utfcpp = utf8; namespace utfcpp = utf8;

View File

@ -9,6 +9,7 @@
#include "xenia/base/string_buffer.h" #include "xenia/base/string_buffer.h"
#include <algorithm>
#include <cstdarg> #include <cstdarg>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"

View File

@ -10,7 +10,6 @@
#ifndef XENIA_BASE_STRING_KEY_H_ #ifndef XENIA_BASE_STRING_KEY_H_
#define XENIA_BASE_STRING_KEY_H_ #define XENIA_BASE_STRING_KEY_H_
#include <algorithm>
#include <string> #include <string>
#include <variant> #include <variant>
@ -59,41 +58,6 @@ struct string_key : internal::string_key_base {
}; };
}; };
struct string_key_insensitive : internal::string_key_base {
public:
explicit string_key_insensitive(const std::string_view value)
: string_key_base(value) {}
explicit string_key_insensitive(std::string value) : string_key_base(value) {}
static string_key_insensitive create(const std::string_view value) {
return string_key_insensitive(std::string(value));
}
static string_key_insensitive create(std::string value) {
return string_key_insensitive(value);
}
bool operator==(const string_key_insensitive& other) const {
return other.view().size() == view().size() &&
std::ranges::equal(
other.view(), view(), {},
[](char ch) {
return std::tolower(static_cast<unsigned char>(ch));
},
[](char ch) {
return std::tolower(static_cast<unsigned char>(ch));
});
}
size_t hash() const { return utf8::hash_fnv1a(view()); }
struct Hash {
size_t operator()(const string_key_insensitive& t) const {
return t.hash();
}
};
};
struct string_key_case : internal::string_key_base { struct string_key_case : internal::string_key_base {
public: public:
explicit string_key_case(const std::string_view value) explicit string_key_case(const std::string_view value)
@ -127,13 +91,6 @@ struct hash<xe::string_key> {
std::size_t operator()(const xe::string_key& t) const { return t.hash(); } std::size_t operator()(const xe::string_key& t) const { return t.hash(); }
}; };
template <>
struct hash<xe::string_key_insensitive> {
std::size_t operator()(const xe::string_key_insensitive& t) const {
return t.hash();
}
};
template <> template <>
struct hash<xe::string_key_case> { struct hash<xe::string_key_case> {
std::size_t operator()(const xe::string_key_case& t) const { std::size_t operator()(const xe::string_key_case& t) const {

View File

@ -16,7 +16,6 @@
#include <cstring> #include <cstring>
#include <regex> #include <regex>
#include <string> #include <string>
#include <variant>
#include "third_party/fmt/include/fmt/format.h" #include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
@ -429,28 +428,6 @@ inline vec128_t from_string<vec128_t>(const std::string_view value,
return v; return v;
} }
inline std::u16string read_u16string_and_swap(const char16_t* string_ptr) {
std::u16string input_str = std::u16string(string_ptr);
std::u16string output_str = {};
output_str.resize(input_str.size() + 1);
copy_and_swap_truncating(output_str.data(), input_str, input_str.size() + 1);
return output_str;
}
inline size_t size_in_bytes(std::variant<std::string, std::u16string> string,
bool include_terminator = true) {
if (std::holds_alternative<std::string>(string)) {
return std::get<std::string>(string).size() + include_terminator;
} else if (std::holds_alternative<std::u16string>(string)) {
return (std::get<std::u16string>(string).size() + include_terminator) *
sizeof(char16_t);
} else {
assert_always();
}
return 0;
}
} // namespace string_util } // namespace string_util
} // namespace xe } // namespace xe

View File

@ -29,11 +29,7 @@ void LaunchWebBrowser(const std::string_view url) {
system(cmd.c_str()); system(cmd.c_str());
} }
void LaunchFileExplorer(const std::filesystem::path& path) { void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); }
auto cmd = std::string("xdg-open ");
cmd.append(path);
system(cmd.c_str());
}
void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) { void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
void* libsdl2 = dlopen("libSDL2.so", RTLD_LAZY | RTLD_LOCAL); void* libsdl2 = dlopen("libSDL2.so", RTLD_LAZY | RTLD_LOCAL);

View File

@ -65,8 +65,8 @@ bool SetProcessPriorityClass(const uint32_t priority_class) {
} }
bool IsUseNexusForGameBarEnabled() { bool IsUseNexusForGameBarEnabled() {
constexpr LPCWSTR reg_path = L"SOFTWARE\\Microsoft\\GameBar"; const LPCWSTR reg_path = L"SOFTWARE\\Microsoft\\GameBar";
constexpr LPCWSTR key = L"UseNexusForGameBarEnabled"; const LPCWSTR key = L"UseNexusForGameBarEnabled";
DWORD value = 0; DWORD value = 0;
DWORD dataSize = sizeof(value); DWORD dataSize = sizeof(value);

View File

@ -516,7 +516,7 @@ TEST_CASE("create_and_close_file_mapping", "Virtual Memory Mapping") {
TEST_CASE("map_view", "[virtual_memory_mapping]") { TEST_CASE("map_view", "[virtual_memory_mapping]") {
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount()); auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
constexpr size_t length = 0x100; const size_t length = 0x100;
auto memory = xe::memory::CreateFileMappingHandle( auto memory = xe::memory::CreateFileMappingHandle(
path, length, xe::memory::PageAccess::kReadWrite, true); path, length, xe::memory::PageAccess::kReadWrite, true);
REQUIRE(memory != xe::memory::kFileMappingHandleInvalid); REQUIRE(memory != xe::memory::kFileMappingHandleInvalid);
@ -532,7 +532,7 @@ TEST_CASE("map_view", "[virtual_memory_mapping]") {
} }
TEST_CASE("read_write_view", "[virtual_memory_mapping]") { TEST_CASE("read_write_view", "[virtual_memory_mapping]") {
constexpr size_t length = 0x100; const size_t length = 0x100;
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount()); auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
auto memory = xe::memory::CreateFileMappingHandle( auto memory = xe::memory::CreateFileMappingHandle(
path, length, xe::memory::PageAccess::kReadWrite, true); path, length, xe::memory::PageAccess::kReadWrite, true);

View File

@ -404,19 +404,19 @@ class Timer : public WaitHandle {
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
struct ThreadPriority { struct ThreadPriority {
static constexpr int32_t kLowest = -2; static const int32_t kLowest = -2;
static constexpr int32_t kBelowNormal = -1; static const int32_t kBelowNormal = -1;
static constexpr int32_t kNormal = 0; static const int32_t kNormal = 0;
static constexpr int32_t kAboveNormal = 1; static const int32_t kAboveNormal = 1;
static constexpr int32_t kHighest = 2; static const int32_t kHighest = 2;
}; };
#else #else
struct ThreadPriority { struct ThreadPriority {
static constexpr int32_t kLowest = 1; static const int32_t kLowest = 1;
static constexpr int32_t kBelowNormal = 8; static const int32_t kBelowNormal = 8;
static constexpr int32_t kNormal = 16; static const int32_t kNormal = 16;
static constexpr int32_t kAboveNormal = 24; static const int32_t kAboveNormal = 24;
static constexpr int32_t kHighest = 32; static const int32_t kHighest = 32;
}; };
#endif #endif

View File

@ -17,11 +17,15 @@
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <signal.h> #include <signal.h>
#include <sys/eventfd.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <ctime> #include <ctime>
#include <memory>
#include "logging.h" #include "logging.h"
@ -78,7 +82,8 @@ void AndroidShutdown() {
#endif #endif
template <typename _Rep, typename _Period> template <typename _Rep, typename _Period>
timespec DurationToTimeSpec(std::chrono::duration<_Rep, _Period> duration) { inline timespec DurationToTimeSpec(
std::chrono::duration<_Rep, _Period> duration) {
auto nanoseconds = auto nanoseconds =
std::chrono::duration_cast<std::chrono::nanoseconds>(duration); std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
auto div = ldiv(nanoseconds.count(), 1000000000L); auto div = ldiv(nanoseconds.count(), 1000000000L);
@ -156,8 +161,6 @@ void Sleep(std::chrono::microseconds duration) {
} while (ret == -1 && errno == EINTR); } while (ret == -1 && errno == EINTR);
} }
void NanoSleep(int64_t duration) { Sleep(std::chrono::nanoseconds(duration)); }
// TODO(bwrsandman) Implement by allowing alert interrupts from IO operations // TODO(bwrsandman) Implement by allowing alert interrupts from IO operations
thread_local bool alertable_state_ = false; thread_local bool alertable_state_ = false;
SleepResult AlertableSleep(std::chrono::microseconds duration) { SleepResult AlertableSleep(std::chrono::microseconds duration) {
@ -175,25 +178,28 @@ TlsHandle AllocateTlsHandle() {
return static_cast<TlsHandle>(key); return static_cast<TlsHandle>(key);
} }
bool FreeTlsHandle(TlsHandle handle) { return pthread_key_delete(handle) == 0; } bool FreeTlsHandle(TlsHandle handle) {
return pthread_key_delete(static_cast<pthread_key_t>(handle)) == 0;
}
uintptr_t GetTlsValue(TlsHandle handle) { uintptr_t GetTlsValue(TlsHandle handle) {
return reinterpret_cast<uintptr_t>(pthread_getspecific(handle)); return reinterpret_cast<uintptr_t>(
pthread_getspecific(static_cast<pthread_key_t>(handle)));
} }
bool SetTlsValue(TlsHandle handle, uintptr_t value) { bool SetTlsValue(TlsHandle handle, uintptr_t value) {
return pthread_setspecific(handle, reinterpret_cast<void*>(value)) == 0; return pthread_setspecific(static_cast<pthread_key_t>(handle),
reinterpret_cast<void*>(value)) == 0;
} }
class PosixConditionBase { class PosixConditionBase {
public: public:
virtual ~PosixConditionBase() = default;
virtual bool Signal() = 0; virtual bool Signal() = 0;
WaitResult Wait(std::chrono::milliseconds timeout) { WaitResult Wait(std::chrono::milliseconds timeout) {
bool executed; bool executed;
auto predicate = [this] { return this->signaled(); }; auto predicate = [this] { return this->signaled(); };
auto lock = std::unique_lock(mutex_); auto lock = std::unique_lock<std::mutex>(mutex_);
if (predicate()) { if (predicate()) {
executed = true; executed = true;
} else { } else {
@ -207,14 +213,15 @@ class PosixConditionBase {
if (executed) { if (executed) {
post_execution(); post_execution();
return WaitResult::kSuccess; return WaitResult::kSuccess;
} } else {
return WaitResult::kTimeout; return WaitResult::kTimeout;
} }
}
static std::pair<WaitResult, size_t> WaitMultiple( static std::pair<WaitResult, size_t> WaitMultiple(
std::vector<PosixConditionBase*>&& handles, bool wait_all, std::vector<PosixConditionBase*>&& handles, bool wait_all,
std::chrono::milliseconds timeout) { std::chrono::milliseconds timeout) {
assert_true(!handles.empty()); assert_true(handles.size() > 0);
// Construct a condition for all or any depending on wait_all // Construct a condition for all or any depending on wait_all
std::function<bool()> predicate; std::function<bool()> predicate;
@ -232,16 +239,17 @@ class PosixConditionBase {
// TODO(bwrsandman, Triang3l) This is controversial, see issue #1677 // TODO(bwrsandman, Triang3l) This is controversial, see issue #1677
// This will probably cause a deadlock on the next thread doing any waiting // This will probably cause a deadlock on the next thread doing any waiting
// if the thread is suspended between locking and waiting // if the thread is suspended between locking and waiting
std::unique_lock lock(mutex_); std::unique_lock<std::mutex> lock(PosixConditionBase::mutex_);
bool wait_success = true; bool wait_success = true;
// If the timeout is infinite, wait without timeout. // If the timeout is infinite, wait without timeout.
// The predicate will be checked before beginning the wait // The predicate will be checked before beginning the wait
if (timeout == std::chrono::milliseconds::max()) { if (timeout == std::chrono::milliseconds::max()) {
cond_.wait(lock, predicate); PosixConditionBase::cond_.wait(lock, predicate);
} else { } else {
// Wait with timeout. // Wait with timeout.
wait_success = cond_.wait_for(lock, timeout, predicate); wait_success =
PosixConditionBase::cond_.wait_for(lock, timeout, predicate);
} }
if (wait_success) { if (wait_success) {
auto first_signaled = std::numeric_limits<size_t>::max(); auto first_signaled = std::numeric_limits<size_t>::max();
@ -256,16 +264,15 @@ class PosixConditionBase {
} }
assert_true(std::numeric_limits<size_t>::max() != first_signaled); assert_true(std::numeric_limits<size_t>::max() != first_signaled);
return std::make_pair(WaitResult::kSuccess, first_signaled); return std::make_pair(WaitResult::kSuccess, first_signaled);
} } else {
return std::make_pair<WaitResult, size_t>(WaitResult::kTimeout, 0); return std::make_pair<WaitResult, size_t>(WaitResult::kTimeout, 0);
} }
[[nodiscard]] virtual void* native_handle() const {
return cond_.native_handle();
} }
virtual void* native_handle() const { return cond_.native_handle(); }
protected: protected:
[[nodiscard]] inline virtual bool signaled() const = 0; inline virtual bool signaled() const = 0;
inline virtual void post_execution() = 0; inline virtual void post_execution() = 0;
static std::condition_variable cond_; static std::condition_variable cond_;
static std::mutex mutex_; static std::mutex mutex_;
@ -286,23 +293,23 @@ class PosixCondition<Event> : public PosixConditionBase {
public: public:
PosixCondition(bool manual_reset, bool initial_state) PosixCondition(bool manual_reset, bool initial_state)
: signal_(initial_state), manual_reset_(manual_reset) {} : signal_(initial_state), manual_reset_(manual_reset) {}
~PosixCondition() override = default; virtual ~PosixCondition() = default;
bool Signal() override { bool Signal() override {
auto lock = std::unique_lock(mutex_); auto lock = std::unique_lock<std::mutex>(mutex_);
signal_ = true; signal_ = true;
cond_.notify_all(); cond_.notify_all();
return true; return true;
} }
void Reset() { void Reset() {
auto lock = std::unique_lock(mutex_); auto lock = std::unique_lock<std::mutex>(mutex_);
signal_ = false; signal_ = false;
} }
private: private:
[[nodiscard]] bool signaled() const override { return signal_; } inline bool signaled() const override { return signal_; }
void post_execution() override { inline void post_execution() override {
if (!manual_reset_) { if (!manual_reset_) {
signal_ = false; signal_ = false;
} }
@ -312,7 +319,7 @@ class PosixCondition<Event> : public PosixConditionBase {
}; };
template <> template <>
class PosixCondition<Semaphore> final : public PosixConditionBase { class PosixCondition<Semaphore> : public PosixConditionBase {
public: public:
PosixCondition(uint32_t initial_count, uint32_t maximum_count) PosixCondition(uint32_t initial_count, uint32_t maximum_count)
: count_(initial_count), maximum_count_(maximum_count) {} : count_(initial_count), maximum_count_(maximum_count) {}
@ -321,7 +328,7 @@ class PosixCondition<Semaphore> final : public PosixConditionBase {
bool Release(uint32_t release_count, int* out_previous_count) { bool Release(uint32_t release_count, int* out_previous_count) {
if (maximum_count_ - count_ >= release_count) { if (maximum_count_ - count_ >= release_count) {
auto lock = std::unique_lock(mutex_); auto lock = std::unique_lock<std::mutex>(mutex_);
if (out_previous_count) *out_previous_count = count_; if (out_previous_count) *out_previous_count = count_;
count_ += release_count; count_ += release_count;
cond_.notify_all(); cond_.notify_all();
@ -331,8 +338,8 @@ class PosixCondition<Semaphore> final : public PosixConditionBase {
} }
private: private:
[[nodiscard]] bool signaled() const override { return count_ > 0; } inline bool signaled() const override { return count_ > 0; }
void post_execution() override { inline void post_execution() override {
count_--; count_--;
cond_.notify_all(); cond_.notify_all();
} }
@ -341,7 +348,7 @@ class PosixCondition<Semaphore> final : public PosixConditionBase {
}; };
template <> template <>
class PosixCondition<Mutant> final : public PosixConditionBase { class PosixCondition<Mutant> : public PosixConditionBase {
public: public:
explicit PosixCondition(bool initial_owner) : count_(0) { explicit PosixCondition(bool initial_owner) : count_(0) {
if (initial_owner) { if (initial_owner) {
@ -354,7 +361,7 @@ class PosixCondition<Mutant> final : public PosixConditionBase {
bool Release() { bool Release() {
if (owner_ == std::this_thread::get_id() && count_ > 0) { if (owner_ == std::this_thread::get_id() && count_ > 0) {
auto lock = std::unique_lock(mutex_); auto lock = std::unique_lock<std::mutex>(mutex_);
--count_; --count_;
// Free to be acquired by another thread // Free to be acquired by another thread
if (count_ == 0) { if (count_ == 0) {
@ -365,15 +372,13 @@ class PosixCondition<Mutant> final : public PosixConditionBase {
return false; return false;
} }
[[nodiscard]] void* native_handle() const override { void* native_handle() const override { return mutex_.native_handle(); }
return mutex_.native_handle();
}
private: private:
[[nodiscard]] bool signaled() const override { inline bool signaled() const override {
return count_ == 0 || owner_ == std::this_thread::get_id(); return count_ == 0 || owner_ == std::this_thread::get_id();
} }
void post_execution() override { inline void post_execution() override {
count_++; count_++;
owner_ = std::this_thread::get_id(); owner_ = std::this_thread::get_id();
} }
@ -382,15 +387,15 @@ class PosixCondition<Mutant> final : public PosixConditionBase {
}; };
template <> template <>
class PosixCondition<Timer> final : public PosixConditionBase { class PosixCondition<Timer> : public PosixConditionBase {
public: public:
explicit PosixCondition(bool manual_reset) explicit PosixCondition(bool manual_reset)
: callback_(nullptr), signal_(false), manual_reset_(manual_reset) {} : callback_(nullptr), signal_(false), manual_reset_(manual_reset) {}
~PosixCondition() override { Cancel(); } virtual ~PosixCondition() { Cancel(); }
bool Signal() override { bool Signal() override {
std::lock_guard lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
signal_ = true; signal_ = true;
cond_.notify_all(); cond_.notify_all();
return true; return true;
@ -400,7 +405,7 @@ class PosixCondition<Timer> final : public PosixConditionBase {
std::function<void()> opt_callback) { std::function<void()> opt_callback) {
Cancel(); Cancel();
std::lock_guard lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
callback_ = std::move(opt_callback); callback_ = std::move(opt_callback);
signal_ = false; signal_ = false;
@ -412,7 +417,7 @@ class PosixCondition<Timer> final : public PosixConditionBase {
std::function<void()> opt_callback) { std::function<void()> opt_callback) {
Cancel(); Cancel();
std::lock_guard lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
callback_ = std::move(opt_callback); callback_ = std::move(opt_callback);
signal_ = false; signal_ = false;
@ -420,13 +425,13 @@ class PosixCondition<Timer> final : public PosixConditionBase {
QueueTimerRecurring(&CompletionRoutine, this, due_time, period); QueueTimerRecurring(&CompletionRoutine, this, due_time, period);
} }
void Cancel() const { void Cancel() {
if (auto wait_item = wait_item_.lock()) { if (auto wait_item = wait_item_.lock()) {
wait_item->Disarm(); wait_item->Disarm();
} }
} }
[[nodiscard]] void* native_handle() const override { void* native_handle() const override {
assert_always(); assert_always();
return nullptr; return nullptr;
} }
@ -434,12 +439,12 @@ class PosixCondition<Timer> final : public PosixConditionBase {
private: private:
static void CompletionRoutine(void* userdata) { static void CompletionRoutine(void* userdata) {
assert_not_null(userdata); assert_not_null(userdata);
auto timer = static_cast<PosixCondition*>(userdata); auto timer = reinterpret_cast<PosixCondition<Timer>*>(userdata);
timer->Signal(); timer->Signal();
// As the callback may reset the timer, store local. // As the callback may reset the timer, store local.
std::function<void()> callback; std::function<void()> callback;
{ {
std::lock_guard lock(timer->mutex_); std::lock_guard<std::mutex> lock(timer->mutex_);
callback = timer->callback_; callback = timer->callback_;
} }
if (callback) { if (callback) {
@ -447,8 +452,9 @@ class PosixCondition<Timer> final : public PosixConditionBase {
} }
} }
[[nodiscard]] bool signaled() const override { return signal_; } private:
void post_execution() override { inline bool signaled() const override { return signal_; }
inline void post_execution() override {
if (!manual_reset_) { if (!manual_reset_) {
signal_ = false; signal_ = false;
} }
@ -466,7 +472,7 @@ struct ThreadStartData {
}; };
template <> template <>
class PosixCondition<Thread> final : public PosixConditionBase { class PosixCondition<Thread> : public PosixConditionBase {
enum class State { enum class State {
kUninitialized, kUninitialized,
kRunning, kRunning,
@ -520,18 +526,13 @@ class PosixCondition<Thread> final : public PosixConditionBase {
: thread_(thread), : thread_(thread),
signaled_(false), signaled_(false),
exit_code_(0), exit_code_(0),
state_(State::kRunning), state_(State::kRunning) {
suspend_count_(0) {
#if XE_PLATFORM_ANDROID #if XE_PLATFORM_ANDROID
android_pre_api_26_name_[0] = '\0'; android_pre_api_26_name_[0] = '\0';
#endif #endif
} }
~PosixCondition() override { virtual ~PosixCondition() {
// FIXME(RodoMa92): This causes random crashes.
// The proper way to handle them according to the webs is properly shutdown
// instead on relying on killing them using pthread_cancel.
/*
if (thread_ && !signaled_) { if (thread_ && !signaled_) {
#if XE_PLATFORM_ANDROID #if XE_PLATFORM_ANDROID
if (pthread_kill(thread_, if (pthread_kill(thread_,
@ -547,7 +548,6 @@ class PosixCondition<Thread> final : public PosixConditionBase {
assert_always(); assert_always();
} }
} }
*/
} }
bool Signal() override { return true; } bool Signal() override { return true; }
@ -555,7 +555,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
std::string name() const { std::string name() const {
WaitStarted(); WaitStarted();
auto result = std::array<char, 17>{'\0'}; auto result = std::array<char, 17>{'\0'};
std::unique_lock lock(state_mutex_); std::unique_lock<std::mutex> lock(state_mutex_);
if (state_ != State::kUninitialized && state_ != State::kFinished) { if (state_ != State::kUninitialized && state_ != State::kFinished) {
#if XE_PLATFORM_ANDROID #if XE_PLATFORM_ANDROID
// pthread_getname_np was added in API 26 - below that, store the name in // pthread_getname_np was added in API 26 - below that, store the name in
@ -579,7 +579,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
return std::string(result.data()); return std::string(result.data());
} }
void set_name(const std::string& name) const { void set_name(const std::string& name) {
WaitStarted(); WaitStarted();
std::unique_lock<std::mutex> lock(state_mutex_); std::unique_lock<std::mutex> lock(state_mutex_);
if (state_ != State::kUninitialized && state_ != State::kFinished) { if (state_ != State::kUninitialized && state_ != State::kFinished) {
@ -603,7 +603,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
uint32_t system_id() const { return static_cast<uint32_t>(thread_); } uint32_t system_id() const { return static_cast<uint32_t>(thread_); }
uint64_t affinity_mask() const { uint64_t affinity_mask() {
WaitStarted(); WaitStarted();
cpu_set_t cpu_set; cpu_set_t cpu_set;
#if XE_PLATFORM_ANDROID #if XE_PLATFORM_ANDROID
@ -625,7 +625,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
return result; return result;
} }
void set_affinity_mask(uint64_t mask) const { void set_affinity_mask(uint64_t mask) {
WaitStarted(); WaitStarted();
cpu_set_t cpu_set; cpu_set_t cpu_set;
CPU_ZERO(&cpu_set); CPU_ZERO(&cpu_set);
@ -646,7 +646,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
#endif #endif
} }
int priority() const { int priority() {
WaitStarted(); WaitStarted();
int policy; int policy;
sched_param param{}; sched_param param{};
@ -658,7 +658,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
return param.sched_priority; return param.sched_priority;
} }
void set_priority(int new_priority) const { void set_priority(int new_priority) {
WaitStarted(); WaitStarted();
sched_param param{}; sched_param param{};
param.sched_priority = new_priority; param.sched_priority = new_priority;
@ -678,7 +678,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
void QueueUserCallback(std::function<void()> callback) { void QueueUserCallback(std::function<void()> callback) {
WaitStarted(); WaitStarted();
std::unique_lock lock(callback_mutex_); std::unique_lock<std::mutex> lock(callback_mutex_);
user_callback_ = std::move(callback); user_callback_ = std::move(callback);
sigval value{}; sigval value{};
value.sival_ptr = this; value.sival_ptr = this;
@ -691,8 +691,8 @@ class PosixCondition<Thread> final : public PosixConditionBase {
#endif #endif
} }
void CallUserCallback() const { void CallUserCallback() {
std::unique_lock lock(callback_mutex_); std::unique_lock<std::mutex> lock(callback_mutex_);
user_callback_(); user_callback_();
} }
@ -701,7 +701,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
*out_previous_suspend_count = 0; *out_previous_suspend_count = 0;
} }
WaitStarted(); WaitStarted();
std::unique_lock lock(state_mutex_); std::unique_lock<std::mutex> lock(state_mutex_);
if (state_ != State::kSuspended) return false; if (state_ != State::kSuspended) return false;
if (out_previous_suspend_count) { if (out_previous_suspend_count) {
*out_previous_suspend_count = suspend_count_; *out_previous_suspend_count = suspend_count_;
@ -731,7 +731,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
void Terminate(int exit_code) { void Terminate(int exit_code) {
bool is_current_thread = pthread_self() == thread_; bool is_current_thread = pthread_self() == thread_;
{ {
std::unique_lock lock(state_mutex_); std::unique_lock<std::mutex> lock(state_mutex_);
if (state_ == State::kFinished) { if (state_ == State::kFinished) {
if (is_current_thread) { if (is_current_thread) {
// This is really bad. Some thread must have called Terminate() on us // This is really bad. Some thread must have called Terminate() on us
@ -747,7 +747,7 @@ class PosixCondition<Thread> final : public PosixConditionBase {
} }
{ {
std::lock_guard lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
exit_code_ = exit_code; exit_code_ = exit_code;
signaled_ = true; signaled_ = true;
@ -755,10 +755,10 @@ class PosixCondition<Thread> final : public PosixConditionBase {
} }
if (is_current_thread) { if (is_current_thread) {
pthread_exit(reinterpret_cast<void*>(exit_code)); pthread_exit(reinterpret_cast<void*>(exit_code));
} } else {
#ifdef XE_PLATFORM_ANDROID #ifdef XE_PLATFORM_ANDROID
if (pthread_kill(thread_, GetSystemSignal(SignalType::kThreadTerminate)) != if (pthread_kill(thread_,
0) { GetSystemSignal(SignalType::kThreadTerminate)) != 0) {
assert_always(); assert_always();
} }
#else #else
@ -767,16 +767,17 @@ class PosixCondition<Thread> final : public PosixConditionBase {
} }
#endif #endif
} }
}
void WaitStarted() const { void WaitStarted() const {
std::unique_lock lock(state_mutex_); std::unique_lock<std::mutex> lock(state_mutex_);
state_signal_.wait(lock, state_signal_.wait(lock,
[this] { return state_ != State::kUninitialized; }); [this] { return state_ != State::kUninitialized; });
} }
/// Set state to suspended and wait until it reset by another thread /// Set state to suspended and wait until it reset by another thread
void WaitSuspended() { void WaitSuspended() {
std::unique_lock lock(state_mutex_); std::unique_lock<std::mutex> lock(state_mutex_);
state_signal_.wait(lock, [this] { return suspend_count_ == 0; }); state_signal_.wait(lock, [this] { return suspend_count_ == 0; });
state_ = State::kRunning; state_ = State::kRunning;
} }
@ -787,8 +788,8 @@ class PosixCondition<Thread> final : public PosixConditionBase {
private: private:
static void* ThreadStartRoutine(void* parameter); static void* ThreadStartRoutine(void* parameter);
bool signaled() const override { return signaled_; } inline bool signaled() const override { return signaled_; }
void post_execution() override { inline void post_execution() override {
if (thread_) { if (thread_) {
pthread_join(thread_, nullptr); pthread_join(thread_, nullptr);
} }
@ -812,7 +813,6 @@ class PosixCondition<Thread> final : public PosixConditionBase {
class PosixWaitHandle { class PosixWaitHandle {
public: public:
virtual ~PosixWaitHandle() = default;
virtual PosixConditionBase& condition() = 0; virtual PosixConditionBase& condition() = 0;
}; };
@ -829,9 +829,7 @@ class PosixConditionHandle : public T, public PosixWaitHandle {
~PosixConditionHandle() override = default; ~PosixConditionHandle() override = default;
PosixCondition<T>& condition() override { return handle_; } PosixCondition<T>& condition() override { return handle_; }
[[nodiscard]] void* native_handle() const override { void* native_handle() const override { return handle_.native_handle(); }
return handle_.native_handle();
}
protected: protected:
PosixCondition<T> handle_; PosixCondition<T> handle_;
@ -912,7 +910,7 @@ std::pair<WaitResult, size_t> WaitMultiple(WaitHandle* wait_handles[],
return result; return result;
} }
class PosixEvent final : public PosixConditionHandle<Event> { class PosixEvent : public PosixConditionHandle<Event> {
public: public:
PosixEvent(bool manual_reset, bool initial_state) PosixEvent(bool manual_reset, bool initial_state)
: PosixConditionHandle(manual_reset, initial_state) {} : PosixConditionHandle(manual_reset, initial_state) {}
@ -941,7 +939,7 @@ std::unique_ptr<Event> Event::CreateAutoResetEvent(bool initial_state) {
return std::make_unique<PosixEvent>(false, initial_state); return std::make_unique<PosixEvent>(false, initial_state);
} }
class PosixSemaphore final : public PosixConditionHandle<Semaphore> { class PosixSemaphore : public PosixConditionHandle<Semaphore> {
public: public:
PosixSemaphore(int initial_count, int maximum_count) PosixSemaphore(int initial_count, int maximum_count)
: PosixConditionHandle(static_cast<uint32_t>(initial_count), : PosixConditionHandle(static_cast<uint32_t>(initial_count),
@ -965,7 +963,7 @@ std::unique_ptr<Semaphore> Semaphore::Create(int initial_count,
return std::make_unique<PosixSemaphore>(initial_count, maximum_count); return std::make_unique<PosixSemaphore>(initial_count, maximum_count);
} }
class PosixMutant final : public PosixConditionHandle<Mutant> { class PosixMutant : public PosixConditionHandle<Mutant> {
public: public:
explicit PosixMutant(bool initial_owner) explicit PosixMutant(bool initial_owner)
: PosixConditionHandle(initial_owner) {} : PosixConditionHandle(initial_owner) {}
@ -977,9 +975,9 @@ std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) {
return std::make_unique<PosixMutant>(initial_owner); return std::make_unique<PosixMutant>(initial_owner);
} }
class PosixTimer final : public PosixConditionHandle<Timer> { class PosixTimer : public PosixConditionHandle<Timer> {
using WClock_ = WClock_; using WClock_ = Timer::WClock_;
using GClock_ = GClock_; using GClock_ = Timer::GClock_;
public: public:
explicit PosixTimer(bool manual_reset) : PosixConditionHandle(manual_reset) {} explicit PosixTimer(bool manual_reset) : PosixConditionHandle(manual_reset) {}
@ -1032,7 +1030,7 @@ std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() {
return std::make_unique<PosixTimer>(false); return std::make_unique<PosixTimer>(false);
} }
class PosixThread final : public PosixConditionHandle<Thread> { class PosixThread : public PosixConditionHandle<Thread> {
public: public:
PosixThread() = default; PosixThread() = default;
explicit PosixThread(pthread_t thread) : PosixConditionHandle(thread) {} explicit PosixThread(pthread_t thread) : PosixConditionHandle(thread) {}
@ -1104,14 +1102,14 @@ void* PosixCondition<Thread>::ThreadStartRoutine(void* parameter) {
current_thread_ = thread; current_thread_ = thread;
{ {
std::unique_lock lock(thread->handle_.state_mutex_); std::unique_lock<std::mutex> lock(thread->handle_.state_mutex_);
thread->handle_.state_ = thread->handle_.state_ =
create_suspended ? State::kSuspended : State::kRunning; create_suspended ? State::kSuspended : State::kRunning;
thread->handle_.state_signal_.notify_all(); thread->handle_.state_signal_.notify_all();
} }
if (create_suspended) { if (create_suspended) {
std::unique_lock lock(thread->handle_.state_mutex_); std::unique_lock<std::mutex> lock(thread->handle_.state_mutex_);
thread->handle_.suspend_count_ = 1; thread->handle_.suspend_count_ = 1;
thread->handle_.state_signal_.wait( thread->handle_.state_signal_.wait(
lock, [thread] { return thread->handle_.suspend_count_ == 0; }); lock, [thread] { return thread->handle_.suspend_count_ == 0; });
@ -1120,11 +1118,11 @@ void* PosixCondition<Thread>::ThreadStartRoutine(void* parameter) {
start_routine(); start_routine();
{ {
std::unique_lock lock(thread->handle_.state_mutex_); std::unique_lock<std::mutex> lock(thread->handle_.state_mutex_);
thread->handle_.state_ = State::kFinished; thread->handle_.state_ = State::kFinished;
} }
std::unique_lock lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
thread->handle_.exit_code_ = 0; thread->handle_.exit_code_ = 0;
thread->handle_.signaled_ = true; thread->handle_.signaled_ = true;
cond_.notify_all(); cond_.notify_all();

View File

@ -7,6 +7,7 @@
****************************************************************************** ******************************************************************************
*/ */
#include <algorithm>
#include <forward_list> #include <forward_list>
#include "third_party/disruptorplus/include/disruptorplus/blocking_wait_strategy.hpp" #include "third_party/disruptorplus/include/disruptorplus/blocking_wait_strategy.hpp"

View File

@ -170,7 +170,7 @@ void Sleep(std::chrono::microseconds duration) {
} }
SleepResult AlertableSleep(std::chrono::microseconds duration) { SleepResult AlertableSleep(std::chrono::microseconds duration) {
if (SleepEx(static_cast<DWORD>(duration.count() / 1000), true) == if (SleepEx(static_cast<DWORD>(duration.count() / 1000), TRUE) ==
WAIT_IO_COMPLETION) { WAIT_IO_COMPLETION) {
return SleepResult::kAlerted; return SleepResult::kAlerted;
} }
@ -211,7 +211,7 @@ WaitResult Wait(WaitHandle* wait_handle, bool is_alertable,
HANDLE handle = wait_handle->native_handle(); HANDLE handle = wait_handle->native_handle();
DWORD result; DWORD result;
DWORD timeout_dw = DWORD(timeout.count()); DWORD timeout_dw = DWORD(timeout.count());
BOOL bAlertable = is_alertable ? true : false; BOOL bAlertable = is_alertable ? TRUE : FALSE;
// todo: we might actually be able to use NtWaitForSingleObject even if its // todo: we might actually be able to use NtWaitForSingleObject even if its
// alertable, just need to study whether // alertable, just need to study whether
// RtlDeactivateActivationContextUnsafeFast/RtlActivateActivationContext are // RtlDeactivateActivationContextUnsafeFast/RtlActivateActivationContext are
@ -251,7 +251,7 @@ WaitResult SignalAndWait(WaitHandle* wait_handle_to_signal,
HANDLE handle_to_wait_on = wait_handle_to_wait_on->native_handle(); HANDLE handle_to_wait_on = wait_handle_to_wait_on->native_handle();
DWORD result = DWORD result =
SignalObjectAndWait(handle_to_signal, handle_to_wait_on, SignalObjectAndWait(handle_to_signal, handle_to_wait_on,
DWORD(timeout.count()), is_alertable ? true : false); DWORD(timeout.count()), is_alertable ? TRUE : FALSE);
switch (result) { switch (result) {
case WAIT_OBJECT_0: case WAIT_OBJECT_0:
return WaitResult::kSuccess; return WaitResult::kSuccess;
@ -278,8 +278,8 @@ std::pair<WaitResult, size_t> WaitMultiple(WaitHandle* wait_handles[],
handles[i] = wait_handles[i]->native_handle(); handles[i] = wait_handles[i]->native_handle();
} }
DWORD result = WaitForMultipleObjectsEx( DWORD result = WaitForMultipleObjectsEx(
static_cast<DWORD>(wait_handle_count), handles, wait_all ? true : false, static_cast<DWORD>(wait_handle_count), handles, wait_all ? TRUE : FALSE,
DWORD(timeout.count()), is_alertable ? true : false); DWORD(timeout.count()), is_alertable ? TRUE : FALSE);
if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + wait_handle_count) { if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + wait_handle_count) {
return std::pair<WaitResult, size_t>(WaitResult::kSuccess, return std::pair<WaitResult, size_t>(WaitResult::kSuccess,
result - WAIT_OBJECT_0); result - WAIT_OBJECT_0);
@ -339,7 +339,7 @@ class Win32Event : public Win32Handle<Event> {
std::unique_ptr<Event> Event::CreateManualResetEvent(bool initial_state) { std::unique_ptr<Event> Event::CreateManualResetEvent(bool initial_state) {
HANDLE handle = HANDLE handle =
CreateEvent(nullptr, true, initial_state ? true : false, nullptr); CreateEvent(nullptr, TRUE, initial_state ? TRUE : FALSE, nullptr);
if (handle) { if (handle) {
return std::make_unique<Win32Event>(handle); return std::make_unique<Win32Event>(handle);
} else { } else {
@ -351,7 +351,7 @@ std::unique_ptr<Event> Event::CreateManualResetEvent(bool initial_state) {
std::unique_ptr<Event> Event::CreateAutoResetEvent(bool initial_state) { std::unique_ptr<Event> Event::CreateAutoResetEvent(bool initial_state) {
HANDLE handle = HANDLE handle =
CreateEvent(nullptr, false, initial_state ? true : false, nullptr); CreateEvent(nullptr, FALSE, initial_state ? TRUE : FALSE, nullptr);
if (handle) { if (handle) {
return std::make_unique<Win32Event>(handle); return std::make_unique<Win32Event>(handle);
} else { } else {
@ -397,7 +397,7 @@ class Win32Mutant : public Win32Handle<Mutant> {
}; };
std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) { std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) {
HANDLE handle = CreateMutex(nullptr, initial_owner ? true : false, nullptr); HANDLE handle = CreateMutex(nullptr, initial_owner ? TRUE : FALSE, nullptr);
if (handle) { if (handle) {
return std::make_unique<Win32Mutant>(handle); return std::make_unique<Win32Mutant>(handle);
} else { } else {
@ -433,7 +433,7 @@ class Win32Timer : public Win32Handle<Timer> {
callback_ ? reinterpret_cast<PTIMERAPCROUTINE>(CompletionRoutine) callback_ ? reinterpret_cast<PTIMERAPCROUTINE>(CompletionRoutine)
: NULL; : NULL;
return SetWaitableTimer(handle_, &due_time_li, 0, completion_routine, this, return SetWaitableTimer(handle_, &due_time_li, 0, completion_routine, this,
false) FALSE)
? true ? true
: false; : false;
} }
@ -461,7 +461,7 @@ class Win32Timer : public Win32Handle<Timer> {
callback_ ? reinterpret_cast<PTIMERAPCROUTINE>(CompletionRoutine) callback_ ? reinterpret_cast<PTIMERAPCROUTINE>(CompletionRoutine)
: NULL; : NULL;
return SetWaitableTimer(handle_, &due_time_li, int32_t(period.count()), return SetWaitableTimer(handle_, &due_time_li, int32_t(period.count()),
completion_routine, this, false) completion_routine, this, FALSE)
? true ? true
: false; : false;
} }
@ -490,7 +490,7 @@ class Win32Timer : public Win32Handle<Timer> {
}; };
std::unique_ptr<Timer> Timer::CreateManualResetTimer() { std::unique_ptr<Timer> Timer::CreateManualResetTimer() {
HANDLE handle = CreateWaitableTimer(NULL, true, NULL); HANDLE handle = CreateWaitableTimer(NULL, TRUE, NULL);
if (handle) { if (handle) {
return std::make_unique<Win32Timer>(handle); return std::make_unique<Win32Timer>(handle);
} else { } else {
@ -500,7 +500,7 @@ std::unique_ptr<Timer> Timer::CreateManualResetTimer() {
} }
std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() { std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() {
HANDLE handle = CreateWaitableTimer(NULL, false, NULL); HANDLE handle = CreateWaitableTimer(NULL, FALSE, NULL);
if (handle) { if (handle) {
return std::make_unique<Win32Timer>(handle); return std::make_unique<Win32Timer>(handle);
} else { } else {

View File

@ -11,8 +11,11 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <locale>
#include <numeric>
#include <tuple>
#define UTF_CPP_CPLUSPLUS 202002L #define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h" #include "third_party/utfcpp/source/utf8.h"
namespace utfcpp = utf8; namespace utfcpp = utf8;
@ -79,10 +82,10 @@ std::string upper_ascii(const std::string_view view) {
template <bool LOWER> template <bool LOWER>
inline size_t hash_fnv1a(const std::string_view view) { inline size_t hash_fnv1a(const std::string_view view) {
constexpr size_t offset_basis = 0xCBF29CE484222325ull; const size_t offset_basis = 0xCBF29CE484222325ull;
// chrispy: constant capture errors on clang // chrispy: constant capture errors on clang
auto work = [](size_t hash, uint8_t byte_of_data) { auto work = [](size_t hash, uint8_t byte_of_data) {
constexpr size_t prime = 0x00000100000001B3ull; const size_t prime = 0x00000100000001B3ull;
hash ^= byte_of_data; hash ^= byte_of_data;
hash *= prime; hash *= prime;
return hash; return hash;

View File

@ -7,6 +7,7 @@
****************************************************************************** ******************************************************************************
*/ */
#include <cstddef>
#include <ostream> #include <ostream>
#include <string> #include <string>

View File

@ -206,7 +206,7 @@ void SaveConfig() {
line_count = xe::utf8::count(*last); line_count = xe::utf8::count(*last);
} }
constexpr size_t value_alignment = 50; const size_t value_alignment = 50;
const auto& description = config_var->description(); const auto& description = config_var->description();
if (!description.empty()) { if (!description.empty()) {
if (line_count < value_alignment) { if (line_count < value_alignment) {

View File

@ -14,6 +14,8 @@ project("xenia-cpu-backend-x64")
}) })
defines({ defines({
"CAPSTONE_X86_ATT_DISABLE", "CAPSTONE_X86_ATT_DISABLE",
"CAPSTONE_DIET_NO",
"CAPSTONE_X86_REDUCE_NO",
"CAPSTONE_HAS_X86", "CAPSTONE_HAS_X86",
"CAPSTONE_USE_SYS_DYN_MEM", "CAPSTONE_USE_SYS_DYN_MEM",
"XBYAK_NO_OP_NAMES", "XBYAK_NO_OP_NAMES",

View File

@ -9,6 +9,7 @@
#include "xenia/cpu/backend/x64/x64_backend.h" #include "xenia/cpu/backend/x64/x64_backend.h"
#include <algorithm>
#include <cstddef> #include <cstddef>
#include "third_party/capstone/include/capstone/capstone.h" #include "third_party/capstone/include/capstone/capstone.h"
#include "third_party/capstone/include/capstone/x86.h" #include "third_party/capstone/include/capstone/x86.h"
@ -635,7 +636,7 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
_code_offsets code_offsets = {}; _code_offsets code_offsets = {};
constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
code_offsets.prolog = getSize(); code_offsets.prolog = getSize();
@ -680,10 +681,13 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
size_t tail; size_t tail;
} code_offsets = {}; } code_offsets = {};
constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
code_offsets.prolog = getSize(); code_offsets.prolog = getSize();
// rsp + 0 = return address // rsp + 0 = return address
mov(qword[rsp + 8 * 3], rdx);
mov(qword[rsp + 8 * 2], rsi);
mov(qword[rsp + 8 * 1], rdi);
sub(rsp, stack_size); sub(rsp, stack_size);
code_offsets.prolog_stack_alloc = getSize(); code_offsets.prolog_stack_alloc = getSize();
@ -703,6 +707,9 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
code_offsets.epilog = getSize(); code_offsets.epilog = getSize();
add(rsp, stack_size); add(rsp, stack_size);
mov(rdi, qword[rsp + 8 * 1]);
mov(rsi, qword[rsp + 8 * 2]);
mov(rdx, qword[rsp + 8 * 3]);
ret(); ret();
#else #else
assert_always("Unknown platform ABI in host to guest thunk!"); assert_always("Unknown platform ABI in host to guest thunk!");
@ -734,7 +741,7 @@ GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
_code_offsets code_offsets = {}; _code_offsets code_offsets = {};
constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
code_offsets.prolog = getSize(); code_offsets.prolog = getSize();
@ -780,7 +787,7 @@ GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
size_t tail; size_t tail;
} code_offsets = {}; } code_offsets = {};
constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
code_offsets.prolog = getSize(); code_offsets.prolog = getSize();
@ -837,7 +844,7 @@ ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk() {
_code_offsets code_offsets = {}; _code_offsets code_offsets = {};
constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
code_offsets.prolog = getSize(); code_offsets.prolog = getSize();
@ -877,7 +884,7 @@ ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk() {
size_t epilog; size_t epilog;
size_t tail; size_t tail;
} code_offsets = {}; } code_offsets = {};
constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
code_offsets.prolog = getSize(); code_offsets.prolog = getSize();
@ -1736,7 +1743,7 @@ void X64Backend::PrepareForReentry(void* ctx) {
bctx->current_stackpoint_depth = 0; bctx->current_stackpoint_depth = 0;
} }
constexpr uint32_t mxcsr_table[8] = { const uint32_t mxcsr_table[8] = {
0x1F80, 0x7F80, 0x5F80, 0x3F80, 0x9F80, 0xFF80, 0xDF80, 0xBF80, 0x1F80, 0x7F80, 0x5F80, 0x3F80, 0x9F80, 0xFF80, 0xDF80, 0xBF80,
}; };

View File

@ -120,7 +120,7 @@ constexpr unsigned int DEFAULT_FPU_MXCSR = 0x1F80;
extern const uint32_t mxcsr_table[8]; extern const uint32_t mxcsr_table[8];
class X64Backend : public Backend { class X64Backend : public Backend {
public: public:
static constexpr uint32_t kForceReturnAddress = 0x9FFF0000u; static const uint32_t kForceReturnAddress = 0x9FFF0000u;
explicit X64Backend(); explicit X64Backend();
~X64Backend() override; ~X64Backend() override;

View File

@ -79,13 +79,13 @@ class X64CodeCache : public CodeCache {
protected: protected:
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can // All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
// only map enough for lookups within that range. // only map enough for lookups within that range.
static constexpr size_t kIndirectionTableSize = 0x1FFFFFFF; static const size_t kIndirectionTableSize = 0x1FFFFFFF;
static constexpr uintptr_t kIndirectionTableBase = 0x80000000; static const uintptr_t kIndirectionTableBase = 0x80000000;
// The code range is 512MB, but we know the total code games will have is // The code range is 512MB, but we know the total code games will have is
// pretty small (dozens of mb at most) and our expansion is reasonablish // pretty small (dozens of mb at most) and our expansion is reasonablish
// so 256MB should be more than enough. // so 256MB should be more than enough.
static constexpr size_t kGeneratedCodeSize = 0x0FFFFFFF; static const size_t kGeneratedCodeSize = 0x0FFFFFFF;
static constexpr uintptr_t kGeneratedCodeExecuteBase = 0xA0000000; static const uintptr_t kGeneratedCodeExecuteBase = 0xA0000000;
// Used for writing when PageAccess::kExecuteReadWrite is not supported. // Used for writing when PageAccess::kExecuteReadWrite is not supported.
static const uintptr_t kGeneratedCodeWriteBase = static const uintptr_t kGeneratedCodeWriteBase =
kGeneratedCodeExecuteBase + kGeneratedCodeSize + 1; kGeneratedCodeExecuteBase + kGeneratedCodeSize + 1;
@ -95,7 +95,7 @@ class X64CodeCache : public CodeCache {
// in analysis triggering. // in analysis triggering.
// chrispy: raised this, some games that were compiled with low optimization // chrispy: raised this, some games that were compiled with low optimization
// levels can exceed this // levels can exceed this
static constexpr size_t kMaximumFunctionCount = 1000000; static const size_t kMaximumFunctionCount = 1000000;
struct UnwindReservation { struct UnwindReservation {
size_t data_size = 0; size_t data_size = 0;

View File

@ -92,7 +92,7 @@ typedef struct _UNWIND_INFO {
// Size of unwind info per function. // Size of unwind info per function.
// TODO(benvanik): move this to emitter. // TODO(benvanik): move this to emitter.
static constexpr uint32_t kUnwindInfoSize = static const uint32_t kUnwindInfoSize =
sizeof(UNWIND_INFO) + (sizeof(UNWIND_CODE) * (6 - 1)); sizeof(UNWIND_INFO) + (sizeof(UNWIND_CODE) * (6 - 1));
class Win32X64CodeCache : public X64CodeCache { class Win32X64CodeCache : public X64CodeCache {

View File

@ -72,17 +72,17 @@ using xe::cpu::hir::HIRBuilder;
using xe::cpu::hir::Instr; using xe::cpu::hir::Instr;
using namespace xe::literals; using namespace xe::literals;
static constexpr size_t kMaxCodeSize = 1_MiB; static const size_t kMaxCodeSize = 1_MiB;
// static const size_t kStashOffsetHigh = 32 + 32; // static const size_t kStashOffsetHigh = 32 + 32;
constexpr uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = { const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
Xbyak::Operand::RBX, Xbyak::Operand::R10, Xbyak::Operand::R11, Xbyak::Operand::RBX, Xbyak::Operand::R10, Xbyak::Operand::R11,
Xbyak::Operand::R12, Xbyak::Operand::R13, Xbyak::Operand::R14, Xbyak::Operand::R12, Xbyak::Operand::R13, Xbyak::Operand::R14,
Xbyak::Operand::R15, Xbyak::Operand::R15,
}; };
constexpr uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = { const uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = {
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}; };
@ -1242,11 +1242,11 @@ void* X64Emitter::FindQwordConstantOffset(uint64_t qwordvalue) {
return nullptr; return nullptr;
} }
// First location to try and place constants. // First location to try and place constants.
static constexpr uintptr_t kConstDataLocation = 0x20000000; static const uintptr_t kConstDataLocation = 0x20000000;
static constexpr uintptr_t kConstDataSize = sizeof(xmm_consts); static const uintptr_t kConstDataSize = sizeof(xmm_consts);
// Increment the location by this amount for every allocation failure. // Increment the location by this amount for every allocation failure.
static constexpr uintptr_t kConstDataIncrement = 0x00001000; static const uintptr_t kConstDataIncrement = 0x00001000;
// This function places constant data that is used by the emitter later on. // This function places constant data that is used by the emitter later on.
// Only called once and used by multiple instances of the emitter. // Only called once and used by multiple instances of the emitter.

View File

@ -227,8 +227,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
// xmm0-2 // xmm0-2
// Available: rbx, r10-r15 // Available: rbx, r10-r15
// xmm4-xmm15 (save to get xmm3) // xmm4-xmm15 (save to get xmm3)
static constexpr int GPR_COUNT = 7; static const int GPR_COUNT = 7;
static constexpr int XMM_COUNT = 12; static const int XMM_COUNT = 12;
static constexpr size_t kStashOffset = 32; static constexpr size_t kStashOffset = 32;
static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) { static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) {
auto idx = gpr_reg_map_[v->reg.index]; auto idx = gpr_reg_map_[v->reg.index];

View File

@ -123,7 +123,7 @@ struct OpBase {};
template <typename T, KeyType KEY_TYPE> template <typename T, KeyType KEY_TYPE>
struct Op : OpBase { struct Op : OpBase {
static constexpr KeyType key_type = KEY_TYPE; static const KeyType key_type = KEY_TYPE;
}; };
struct VoidOp : Op<VoidOp, KEY_TYPE_X> { struct VoidOp : Op<VoidOp, KEY_TYPE_X> {
@ -298,7 +298,7 @@ struct I;
template <hir::Opcode OPCODE, typename DEST> template <hir::Opcode OPCODE, typename DEST>
struct I<OPCODE, DEST> : DestField<DEST> { struct I<OPCODE, DEST> : DestField<DEST> {
typedef DestField<DEST> BASE; typedef DestField<DEST> BASE;
static constexpr hir::Opcode opcode = OPCODE; static const hir::Opcode opcode = OPCODE;
static const uint32_t key = static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type>::value; InstrKey::Construct<OPCODE, DEST::key_type>::value;
static const KeyType dest_type = DEST::key_type; static const KeyType dest_type = DEST::key_type;
@ -318,7 +318,7 @@ struct I<OPCODE, DEST> : DestField<DEST> {
template <hir::Opcode OPCODE, typename DEST, typename SRC1> template <hir::Opcode OPCODE, typename DEST, typename SRC1>
struct I<OPCODE, DEST, SRC1> : DestField<DEST> { struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
typedef DestField<DEST> BASE; typedef DestField<DEST> BASE;
static constexpr hir::Opcode opcode = OPCODE; static const hir::Opcode opcode = OPCODE;
static const uint32_t key = static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value; InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
static const KeyType dest_type = DEST::key_type; static const KeyType dest_type = DEST::key_type;
@ -341,7 +341,7 @@ struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2> template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> { struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
typedef DestField<DEST> BASE; typedef DestField<DEST> BASE;
static constexpr hir::Opcode opcode = OPCODE; static const hir::Opcode opcode = OPCODE;
static const uint32_t key = static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type,
SRC2::key_type>::value; SRC2::key_type>::value;
@ -369,7 +369,7 @@ template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2,
typename SRC3> typename SRC3>
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> { struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
typedef DestField<DEST> BASE; typedef DestField<DEST> BASE;
static constexpr hir::Opcode opcode = OPCODE; static const hir::Opcode opcode = OPCODE;
static const uint32_t key = static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type,
SRC2::key_type, SRC3::key_type>::value; SRC2::key_type, SRC3::key_type>::value;

View File

@ -9,6 +9,7 @@
#include "xenia/cpu/backend/x64/x64_sequences.h" #include "xenia/cpu/backend/x64/x64_sequences.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"

View File

@ -24,7 +24,9 @@
#include "xenia/cpu/backend/x64/x64_sequences.h" #include "xenia/cpu/backend/x64/x64_sequences.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include <unordered_map>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/clock.h" #include "xenia/base/clock.h"

View File

@ -95,7 +95,7 @@ class StackLayout {
}); });
static_assert(sizeof(Thunk) % 16 == 0, static_assert(sizeof(Thunk) % 16 == 0,
"sizeof(Thunk) must be a multiple of 16!"); "sizeof(Thunk) must be a multiple of 16!");
static constexpr size_t THUNK_STACK_SIZE = sizeof(Thunk) + 8; static const size_t THUNK_STACK_SIZE = sizeof(Thunk) + 8;
/** /**
* *
@ -121,16 +121,16 @@ class StackLayout {
* +------------------+ * +------------------+
* *
*/ */
static constexpr size_t GUEST_STACK_SIZE = 104; static const size_t GUEST_STACK_SIZE = 104;
// was GUEST_CTX_HOME, can't remove because that'd throw stack alignment off. // was GUEST_CTX_HOME, can't remove because that'd throw stack alignment off.
// instead, can be used as a temporary in sequences // instead, can be used as a temporary in sequences
static constexpr size_t GUEST_SCRATCH = 0; static const size_t GUEST_SCRATCH = 0;
// when profiling is on, this stores the nanosecond time at the start of the // when profiling is on, this stores the nanosecond time at the start of the
// function // function
static constexpr size_t GUEST_PROFILER_START = 80; static const size_t GUEST_PROFILER_START = 80;
static constexpr size_t GUEST_RET_ADDR = 88; static const size_t GUEST_RET_ADDR = 88;
static constexpr size_t GUEST_CALL_RET_ADDR = 96; static const size_t GUEST_CALL_RET_ADDR = 96;
}; };
} // namespace x64 } // namespace x64

View File

@ -9,6 +9,8 @@
#include "xenia/cpu/backend/x64/x64_tracers.h" #include "xenia/cpu/backend/x64/x64_tracers.h"
#include <cinttypes>
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/vec128.h" #include "xenia/base/vec128.h"
#include "xenia/cpu/backend/x64/x64_emitter.h" #include "xenia/cpu/backend/x64/x64_emitter.h"

View File

@ -48,7 +48,7 @@ bool ControlFlowSimplificationPass::Run(HIRBuilder* builder) {
block = builder->last_block(); block = builder->last_block();
while (block) { while (block) {
auto prev_block = block->prev; auto prev_block = block->prev;
constexpr uint32_t expected = Edge::DOMINATES | Edge::UNCONDITIONAL; const uint32_t expected = Edge::DOMINATES | Edge::UNCONDITIONAL;
if (block->incoming_edge_head && if (block->incoming_edge_head &&
(block->incoming_edge_head->flags & expected) == expected) { (block->incoming_edge_head->flags & expected) == expected) {
// Dominated by the incoming block. // Dominated by the incoming block.

View File

@ -9,6 +9,7 @@
#include "xenia/cpu/compiler/passes/register_allocation_pass.h" #include "xenia/cpu/compiler/passes/register_allocation_pass.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"

View File

@ -33,15 +33,8 @@ DEFINE_bool(trace_function_data, false,
DEFINE_bool(validate_hir, false, DEFINE_bool(validate_hir, false,
"Perform validation checks on the HIR during compilation.", "CPU"); "Perform validation checks on the HIR during compilation.", "CPU");
// https://github.com/bitsh1ft3r/Xenon/blob/091e8cd4dc4a7c697b4979eb200be7c9dee3590b/Xenon/Core/XCPU/PPU/PowerPC.h#L370
DEFINE_uint64( DEFINE_uint64(
pvr, 0x710700, pvr, 0x710700,
"Known PVR's.\n"
" 0x710200 = Used by Zephyr \n"
" 0x710300 = Used by Zephyr\n"
" 0x710500 = Used by Jasper\n"
" 0x710700 = Default\n"
" 0x710800 = Used by Corona V1 & V2\n"
"Processor version and revision number.\nBits 0 to 15 are the version " "Processor version and revision number.\nBits 0 to 15 are the version "
"number.\nBits 16 to 31 are the revision number.\nNote: Some XEXs (such as " "number.\nBits 16 to 31 are the revision number.\nNote: Some XEXs (such as "
"mfgbootlauncher.xex) may check for a value that's less than 0x710700.", "mfgbootlauncher.xex) may check for a value that's less than 0x710700.",

View File

@ -9,6 +9,9 @@
#include "xenia/cpu/elf_module.h" #include "xenia/cpu/elf_module.h"
#include <algorithm>
#include <memory>
#include "xenia/base/byte_order.h" #include "xenia/base/byte_order.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"

View File

@ -9,6 +9,7 @@
#include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/hir/hir_builder.h"
#include <cinttypes>
#include <cstdarg> #include <cstdarg>
#include <cstring> #include <cstring>
@ -2099,7 +2100,7 @@ Value* HIRBuilder::CountLeadingZeros(Value* value) {
ASSERT_INTEGER_TYPE(value); ASSERT_INTEGER_TYPE(value);
if (value->IsConstantZero()) { if (value->IsConstantZero()) {
static constexpr uint8_t zeros[] = { static const uint8_t zeros[] = {
8, 8,
16, 16,
32, 32,

View File

@ -14,7 +14,7 @@ namespace cpu {
namespace hir { namespace hir {
#define DEFINE_OPCODE(num, name, sig, flags) \ #define DEFINE_OPCODE(num, name, sig, flags) \
constexpr OpcodeInfo num##_info = { \ const OpcodeInfo num##_info = { \
num, \ num, \
flags, \ flags, \
sig, \ sig, \

View File

@ -9,7 +9,9 @@
#include "xenia/cpu/mmio_handler.h" #include "xenia/cpu/mmio_handler.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include <utility>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/byte_order.h" #include "xenia/base/byte_order.h"

View File

@ -9,6 +9,11 @@
#include "xenia/cpu/module.h" #include "xenia/cpu/module.h"
#include <algorithm>
#include <fstream>
#include <sstream> // NOLINT(readability/streams): should be replaced.
#include <string>
#include "xenia/base/profiling.h" #include "xenia/base/profiling.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"

View File

@ -9,6 +9,7 @@
#include "xenia/cpu/ppc/ppc_context.h" #include "xenia/cpu/ppc/ppc_context.h"
#include <cinttypes>
#include <cstdlib> #include <cstdlib>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"

Some files were not shown because too many files have changed in this diff Show More