Compare commits
61 Commits
238bda1a87
...
c9a70cab65
Author | SHA1 | Date |
---|---|---|
Xphalnos | c9a70cab65 | |
Marco Rodolfi | 55bbb28a80 | |
The-Little-Wolf | 4d7b30e844 | |
Adrian | ae23222ba8 | |
Adrian | d99d053408 | |
Gliniak | 1688ea5d69 | |
Gliniak | e80d4effa1 | |
Gliniak | 57da74814b | |
The-Little-Wolf | a77213dcdb | |
Adrian | e134bbccd1 | |
Marco Rodolfi | 1822bca890 | |
Gliniak | 09be7e874a | |
Gliniak | cdd3f161fa | |
Gliniak | ccf4d6d5f8 | |
Gliniak | 2253cb978d | |
Gliniak | b50e32ab45 | |
Gliniak | 4620fa93d8 | |
Gliniak | 7e51efeec5 | |
marko1616 | 34da168284 | |
Margen67 | 762145687b | |
Margen67 | 6f1cb9e253 | |
Gliniak | 7437c020d6 | |
The-Little-Wolf | 57eeed86b7 | |
Margen67 | 3b49054d6b | |
Margen67 | 72df2129a0 | |
Margen67 | fff79eb41d | |
Margen67 | affb2fb7af | |
Margen67 | 26bf95d50f | |
The-Little-Wolf | b5d319834e | |
Xphalnos | bb20ada9bf | |
Gliniak | 13badbb4c0 | |
Gliniak | d660a82997 | |
Gliniak | ad323dd9d8 | |
Gliniak | 4584794e24 | |
The-Little-Wolf | d6ed8af4e7 | |
The-Little-Wolf | 95df198d8a | |
Gliniak | b757601f01 | |
Gliniak | 3f0a7f171f | |
Adrian | ceb94e019a | |
Gliniak | fe85be8817 | |
Gliniak | c3301d9281 | |
Gliniak | 1ba30c519c | |
Adrian | 3dac88113f | |
The-Little-Wolf | 160d80d5cc | |
Gliniak | 580b1f4345 | |
Gliniak | 9dfb0d0b68 | |
Gliniak | bcc3c3172d | |
Gliniak | c3586bc165 | |
Gliniak | a6e3d77504 | |
Gliniak | 2e521383c2 | |
Gliniak | 11f14e8488 | |
Adrian | 3d79874828 | |
The-Little-Wolf | 919f7403e2 | |
Gliniak | 263c722a40 | |
Gliniak | b98ff2d278 | |
Gliniak | ef8619b0a8 | |
Gliniak | f6ae651cc2 | |
Margen67 | 2596aef111 | |
Gliniak | b187ffeef7 | |
Xphalnos | c8c62fc07c | |
Gliniak | cfd965342a |
|
@ -0,0 +1,77 @@
|
||||||
|
name: Linux build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- '.clang-format'
|
||||||
|
- '.drone.star'
|
||||||
|
- '.gitattributes'
|
||||||
|
- '.gitignore'
|
||||||
|
- '.gdbinit'
|
||||||
|
- '.github/*'
|
||||||
|
- '.github/workflows/Windows_build.yml'
|
||||||
|
- '.github/*_TEMPLATE/**'
|
||||||
|
- '*.md'
|
||||||
|
- '*.yml'
|
||||||
|
- '*.txt'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'src/**/*_windows.*'
|
||||||
|
- 'src/**/*_android.*'
|
||||||
|
- 'src/**/*_mac.*'
|
||||||
|
- 'LICENSE'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '.clang-format'
|
||||||
|
- '.drone.star'
|
||||||
|
- '.gitattributes'
|
||||||
|
- '.gitignore'
|
||||||
|
- '.gdbinit'
|
||||||
|
- '.github/*'
|
||||||
|
- '.github/workflows/Windows_build.yml'
|
||||||
|
- '.github/*_TEMPLATE/**'
|
||||||
|
- '*.md'
|
||||||
|
- '*.yml'
|
||||||
|
- '*.txt'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'src/**/*_windows.*'
|
||||||
|
- 'src/**/*_android.*'
|
||||||
|
- 'src/**/*_mac.*'
|
||||||
|
- 'LICENSE'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# lint:
|
||||||
|
# name: Lint
|
||||||
|
# runs-on: ubuntu-24.04
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@main
|
||||||
|
# - name: Check Clang-Format Version
|
||||||
|
# run: clang-format --version
|
||||||
|
# - name: Lint
|
||||||
|
# run: ./xb lint --all
|
||||||
|
|
||||||
|
build-linux:
|
||||||
|
name: Build (Linux) # runner.os can't be used here
|
||||||
|
# needs: lint
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@main
|
||||||
|
with:
|
||||||
|
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
|
||||||
|
run: |
|
||||||
|
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 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
|
||||||
|
./xb setup
|
||||||
|
- name: Build
|
||||||
|
run: ./xb build --config=Release
|
|
@ -9,6 +9,7 @@ on:
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
- '.gdbinit'
|
- '.gdbinit'
|
||||||
- '.github/*'
|
- '.github/*'
|
||||||
|
- '.github/workflows/Linux_build.yml'
|
||||||
- '.github/*_TEMPLATE/**'
|
- '.github/*_TEMPLATE/**'
|
||||||
- '*.md'
|
- '*.md'
|
||||||
- '*.yml'
|
- '*.yml'
|
||||||
|
@ -30,6 +31,7 @@ on:
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
- '.gdbinit'
|
- '.gdbinit'
|
||||||
- '.github/*'
|
- '.github/*'
|
||||||
|
- '.github/workflows/Linux_build.yml'
|
||||||
- '.github/*_TEMPLATE/**'
|
- '.github/*_TEMPLATE/**'
|
||||||
- '*.md'
|
- '*.md'
|
||||||
- '*.yml'
|
- '*.yml'
|
||||||
|
@ -50,7 +52,7 @@ jobs:
|
||||||
name: Lint
|
name: Lint
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@main
|
||||||
- name: Check Clang-Format Version
|
- name: Check Clang-Format Version
|
||||||
run: clang-format --version
|
run: clang-format --version
|
||||||
- name: Lint
|
- name: Lint
|
||||||
|
@ -67,7 +69,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@main
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup
|
- name: Setup
|
||||||
|
@ -75,12 +77,15 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
run: .\xb build --config=Release --target=src\xenia-app
|
run: .\xb build --config=Release --target=src\xenia-app
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
|
id: prepare_artifacts
|
||||||
run: |
|
run: |
|
||||||
robocopy . build\bin\${{ runner.os }}\Release LICENSE /r:0 /w:0
|
robocopy . build\bin\Windows\Release LICENSE /r:0 /w:0
|
||||||
robocopy build\bin\${{ runner.os }}\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
|
||||||
uses: actions/upload-artifact@v4
|
if: steps.prepare_artifacts.outcome == 'success'
|
||||||
|
id: upload_artifacts
|
||||||
|
uses: actions/upload-artifact@main
|
||||||
with:
|
with:
|
||||||
name: xenia_canary_vs${{ matrix.vsver }}
|
name: xenia_canary_vs${{ matrix.vsver }}
|
||||||
path: artifacts\xenia_canary
|
path: artifacts\xenia_canary
|
||||||
|
@ -89,7 +94,8 @@ jobs:
|
||||||
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' &&
|
||||||
|
steps.upload_artifacts.outcome == 'success'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
url = https://github.com/catchorg/Catch2.git
|
url = https://github.com/catchorg/Catch2.git
|
||||||
[submodule "third_party/premake-core"]
|
[submodule "third_party/premake-core"]
|
||||||
path = third_party/premake-core
|
path = third_party/premake-core
|
||||||
url = https://github.com/xenia-project/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/xenia-project/snappy.git
|
url = https://github.com/xenia-project/snappy.git
|
||||||
|
|
|
@ -21,7 +21,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/latest) ◦ [All](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 | Curently unsupported
|
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)
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
@ -36,7 +36,7 @@ See the [frequently asked questions](https://github.com/xenia-canary/xenia-canar
|
||||||
|
|
||||||
See the [Game compatibility list](https://github.com/xenia-canary/game-compatibility/issues)
|
See the [Game compatibility list](https://github.com/xenia-canary/game-compatibility/issues)
|
||||||
for currently tracked games, and feel free to contribute your own updates,
|
for currently tracked games, and feel free to contribute your own updates,
|
||||||
screenshots, and information there following the [existing conventions](https://github.com/xenia-canary/game-compatibility/blob/master/README.md).
|
screenshots, and information there following the [existing conventions](https://github.com/xenia-canary/game-compatibility/blob/canary/README.md).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
16
premake5.lua
16
premake5.lua
|
@ -26,7 +26,7 @@ defines({
|
||||||
"UNICODE",
|
"UNICODE",
|
||||||
})
|
})
|
||||||
|
|
||||||
cppdialect("C++17")
|
cppdialect("C++20")
|
||||||
exceptionhandling("On")
|
exceptionhandling("On")
|
||||||
rtti("On")
|
rtti("On")
|
||||||
symbols("On")
|
symbols("On")
|
||||||
|
@ -113,12 +113,18 @@ 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({"platforms:Linux", "language:C++", "toolset:gcc"})
|
filter({"platforms:Linux", "language:C++", "toolset:gcc"})
|
||||||
disablewarnings({
|
disablewarnings({
|
||||||
"unused-result"
|
"unused-result",
|
||||||
|
"deprecated-volatile",
|
||||||
|
"switch",
|
||||||
|
"deprecated-enum-enum-conversion",
|
||||||
})
|
})
|
||||||
|
|
||||||
filter({"platforms:Linux", "toolset:gcc"})
|
filter({"platforms:Linux", "toolset:gcc"})
|
||||||
|
@ -135,11 +141,15 @@ filter({"platforms:Linux", "toolset:gcc"})
|
||||||
|
|
||||||
filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
||||||
disablewarnings({
|
disablewarnings({
|
||||||
"deprecated-register"
|
"deprecated-register",
|
||||||
|
"deprecated-volatile",
|
||||||
|
"switch",
|
||||||
|
"deprecated-enum-enum-conversion",
|
||||||
})
|
})
|
||||||
filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"})
|
filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"})
|
||||||
buildoptions({
|
buildoptions({
|
||||||
"-stdlib=libstdc++",
|
"-stdlib=libstdc++",
|
||||||
|
"-std=c++20", -- clang doesn't respect cppdialect(?)
|
||||||
})
|
})
|
||||||
|
|
||||||
filter("platforms:Android-*")
|
filter("platforms:Android-*")
|
||||||
|
|
|
@ -1405,15 +1405,13 @@ void EmulatorWindow::ToggleDisplayConfigDialog() {
|
||||||
void EmulatorWindow::ToggleProfilesConfigDialog() {
|
void EmulatorWindow::ToggleProfilesConfigDialog() {
|
||||||
if (!profile_config_dialog_) {
|
if (!profile_config_dialog_) {
|
||||||
disable_hotkeys_ = true;
|
disable_hotkeys_ = true;
|
||||||
emulator_->kernel_state()->BroadcastNotification(kXNotificationIDSystemUI,
|
emulator_->kernel_state()->BroadcastNotification(kXNotificationSystemUI, 1);
|
||||||
1);
|
|
||||||
profile_config_dialog_ =
|
profile_config_dialog_ =
|
||||||
std::make_unique<ProfileConfigDialog>(imgui_drawer_.get(), this);
|
std::make_unique<ProfileConfigDialog>(imgui_drawer_.get(), this);
|
||||||
kernel::xam::xam_dialogs_shown_++;
|
kernel::xam::xam_dialogs_shown_++;
|
||||||
} else {
|
} else {
|
||||||
disable_hotkeys_ = false;
|
disable_hotkeys_ = false;
|
||||||
emulator_->kernel_state()->BroadcastNotification(kXNotificationIDSystemUI,
|
emulator_->kernel_state()->BroadcastNotification(kXNotificationSystemUI, 0);
|
||||||
0);
|
|
||||||
profile_config_dialog_.reset();
|
profile_config_dialog_.reset();
|
||||||
kernel::xam::xam_dialogs_shown_--;
|
kernel::xam::xam_dialogs_shown_--;
|
||||||
}
|
}
|
||||||
|
@ -1462,17 +1460,17 @@ void EmulatorWindow::UpdateTitle() {
|
||||||
|
|
||||||
// Title information, if available
|
// Title information, if available
|
||||||
if (emulator()->is_title_open()) {
|
if (emulator()->is_title_open()) {
|
||||||
sb.AppendFormat(u8" | [{:08X}", emulator()->title_id());
|
sb.AppendFormat(" | [{:08X}", emulator()->title_id());
|
||||||
auto title_version = emulator()->title_version();
|
auto title_version = emulator()->title_version();
|
||||||
if (!title_version.empty()) {
|
if (!title_version.empty()) {
|
||||||
sb.Append(u8" v");
|
sb.Append(" v");
|
||||||
sb.Append(title_version);
|
sb.Append(title_version);
|
||||||
}
|
}
|
||||||
sb.Append(u8"]");
|
sb.Append("]");
|
||||||
|
|
||||||
auto title_name = emulator()->title_name();
|
auto title_name = emulator()->title_name();
|
||||||
if (!title_name.empty()) {
|
if (!title_name.empty()) {
|
||||||
sb.Append(u8" ");
|
sb.Append(" ");
|
||||||
sb.Append(title_name);
|
sb.Append(title_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1482,28 +1480,28 @@ void EmulatorWindow::UpdateTitle() {
|
||||||
if (graphics_system) {
|
if (graphics_system) {
|
||||||
auto graphics_name = graphics_system->name();
|
auto graphics_name = graphics_system->name();
|
||||||
if (!graphics_name.empty()) {
|
if (!graphics_name.empty()) {
|
||||||
sb.Append(u8" <");
|
sb.Append(" <");
|
||||||
sb.Append(graphics_name);
|
sb.Append(graphics_name);
|
||||||
sb.Append(u8">");
|
sb.Append(">");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Clock::guest_time_scalar() != 1.0) {
|
if (Clock::guest_time_scalar() != 1.0) {
|
||||||
sb.AppendFormat(u8" (@{:.2f}x)", Clock::guest_time_scalar());
|
sb.AppendFormat(" (@{:.2f}x)", Clock::guest_time_scalar());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initializing_shader_storage_) {
|
if (initializing_shader_storage_) {
|
||||||
sb.Append(u8" (Preloading shaders\u2026)");
|
sb.Append(" (Preloading shaders\u2026)");
|
||||||
}
|
}
|
||||||
|
|
||||||
patcher::Patcher* patcher = emulator()->patcher();
|
patcher::Patcher* patcher = emulator()->patcher();
|
||||||
if (patcher && patcher->IsAnyPatchApplied()) {
|
if (patcher && patcher->IsAnyPatchApplied()) {
|
||||||
sb.Append(u8" [Patches Applied]");
|
sb.Append(" [Patches Applied]");
|
||||||
}
|
}
|
||||||
|
|
||||||
patcher::PluginLoader* pluginloader = emulator()->plugin_loader();
|
patcher::PluginLoader* pluginloader = emulator()->plugin_loader();
|
||||||
if (pluginloader && pluginloader->IsAnyPluginLoaded()) {
|
if (pluginloader && pluginloader->IsAnyPluginLoaded()) {
|
||||||
sb.Append(u8" [Plugins Loaded]");
|
sb.Append(" [Plugins Loaded]");
|
||||||
}
|
}
|
||||||
|
|
||||||
window_->SetTitle(sb.to_string_view());
|
window_->SetTitle(sb.to_string_view());
|
||||||
|
@ -1664,11 +1662,16 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
xe::threading::Sleep(delay);
|
xe::threading::Sleep(delay);
|
||||||
break;
|
break;
|
||||||
case ButtonFunctions::RunTitle: {
|
case ButtonFunctions::RunTitle: {
|
||||||
if (selected_title_index == -1) selected_title_index++;
|
if (selected_title_index == -1) {
|
||||||
|
selected_title_index++;
|
||||||
|
}
|
||||||
|
|
||||||
app_context().CallInUIThread([this]() {
|
if (selected_title_index < recently_launched_titles_.size()) {
|
||||||
RunTitle(recently_launched_titles_[selected_title_index].path_to_file);
|
app_context().CallInUIThread([this]() {
|
||||||
});
|
RunTitle(
|
||||||
|
recently_launched_titles_[selected_title_index].path_to_file);
|
||||||
|
});
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case ButtonFunctions::ClearMemoryPageState:
|
case ButtonFunctions::ClearMemoryPageState:
|
||||||
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
||||||
|
@ -1761,8 +1764,9 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
if ((button_combination.function == ButtonFunctions::IncTitleSelect ||
|
if ((button_combination.function == ButtonFunctions::IncTitleSelect ||
|
||||||
button_combination.function == ButtonFunctions::DecTitleSelect) &&
|
button_combination.function == ButtonFunctions::DecTitleSelect) &&
|
||||||
recently_launched_titles_.size() > 0) {
|
recently_launched_titles_.size() > 0) {
|
||||||
selected_title_index = std::clamp(
|
selected_title_index =
|
||||||
selected_title_index, 0, (int)recently_launched_titles_.size() - 1);
|
std::clamp(selected_title_index, 0,
|
||||||
|
static_cast<int32_t>(recently_launched_titles_.size() - 1));
|
||||||
|
|
||||||
// Must clear dialogs to prevent stacking
|
// Must clear dialogs to prevent stacking
|
||||||
ClearDialogs();
|
ClearDialogs();
|
||||||
|
@ -1835,7 +1839,8 @@ void EmulatorWindow::GamepadHotKeys() {
|
||||||
|
|
||||||
for (uint32_t user_index = 0; user_index < XUserMaxUserCount;
|
for (uint32_t user_index = 0; user_index < XUserMaxUserCount;
|
||||||
++user_index) {
|
++user_index) {
|
||||||
X_RESULT result = input_sys->GetState(user_index, &state);
|
X_RESULT result = input_sys->GetState(
|
||||||
|
user_index, X_INPUT_FLAG::X_INPUT_FLAG_GAMEPAD, &state);
|
||||||
|
|
||||||
// Release the lock before processing the hotkey
|
// Release the lock before processing the hotkey
|
||||||
input_lock.mutex()->unlock();
|
input_lock.mutex()->unlock();
|
||||||
|
@ -1870,24 +1875,6 @@ void EmulatorWindow::ToggleGPUSetting(gpu_cvar value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the Xbox Gamebar is enabled via the Windows registry
|
|
||||||
bool EmulatorWindow::IsUseNexusForGameBarEnabled() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
const LPWSTR reg_path = L"SOFTWARE\\Microsoft\\GameBar";
|
|
||||||
const LPWSTR key = L"UseNexusForGameBarEnabled";
|
|
||||||
|
|
||||||
DWORD value = 0;
|
|
||||||
DWORD dataSize = sizeof(value);
|
|
||||||
|
|
||||||
RegGetValue(HKEY_CURRENT_USER, reg_path, key, RRF_RT_DWORD, nullptr, &value,
|
|
||||||
&dataSize);
|
|
||||||
|
|
||||||
return (bool)value;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmulatorWindow::DisplayHotKeysConfig() {
|
void EmulatorWindow::DisplayHotKeysConfig() {
|
||||||
std::string msg = "";
|
std::string msg = "";
|
||||||
std::string msg_passthru = "";
|
std::string msg_passthru = "";
|
||||||
|
|
|
@ -236,7 +236,6 @@ class EmulatorWindow {
|
||||||
bool vibrate = true);
|
bool vibrate = true);
|
||||||
void GamepadHotKeys();
|
void GamepadHotKeys();
|
||||||
void ToggleGPUSetting(gpu_cvar index);
|
void ToggleGPUSetting(gpu_cvar index);
|
||||||
bool IsUseNexusForGameBarEnabled();
|
|
||||||
void DisplayHotKeysConfig();
|
void DisplayHotKeysConfig();
|
||||||
|
|
||||||
static std::string CanonicalizeFileExtension(
|
static std::string CanonicalizeFileExtension(
|
||||||
|
|
|
@ -176,23 +176,49 @@ class EmulatorApp final : public xe::ui::WindowedApp {
|
||||||
std::vector<std::unique_ptr<T>> CreateAll(const std::string_view name,
|
std::vector<std::unique_ptr<T>> CreateAll(const std::string_view name,
|
||||||
Args... args) {
|
Args... args) {
|
||||||
std::vector<std::unique_ptr<T>> instances;
|
std::vector<std::unique_ptr<T>> instances;
|
||||||
if (!name.empty() && name != "any") {
|
|
||||||
|
// "Any" path
|
||||||
|
if (name.empty() || name == "any") {
|
||||||
|
for (const auto& creator : creators_) {
|
||||||
|
if (!creator.is_available()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip xinput for "any" and use SDL
|
||||||
|
if (creator.name.compare("xinput") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto instance = creator.instantiate(std::forward<Args>(args)...);
|
||||||
|
if (instance) {
|
||||||
|
instances.emplace_back(std::move(instance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Specified" path. Winkey is always added on windows.
|
||||||
|
if (name != "winkey") {
|
||||||
auto it = std::find_if(
|
auto it = std::find_if(
|
||||||
creators_.cbegin(), creators_.cend(),
|
creators_.cbegin(), creators_.cend(),
|
||||||
[&name](const auto& f) { return name.compare(f.name) == 0; });
|
[&name](const auto& f) { return name.compare(f.name) == 0; });
|
||||||
|
|
||||||
if (it != creators_.cend() && (*it).is_available()) {
|
if (it != creators_.cend() && (*it).is_available()) {
|
||||||
auto instance = (*it).instantiate(std::forward<Args>(args)...);
|
auto instance = (*it).instantiate(std::forward<Args>(args)...);
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instances.emplace_back(std::move(instance));
|
instances.emplace_back(std::move(instance));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
for (const auto& creator : creators_) {
|
|
||||||
if (!creator.is_available()) continue;
|
// Always add winkey for passthrough.
|
||||||
auto instance = creator.instantiate(std::forward<Args>(args)...);
|
auto it = std::find_if(
|
||||||
if (instance) {
|
creators_.cbegin(), creators_.cend(),
|
||||||
instances.emplace_back(std::move(instance));
|
[&name](const auto& f) { return f.name.compare("winkey") == 0; });
|
||||||
}
|
if (it != creators_.cend() && (*it).is_available()) {
|
||||||
|
auto instance = (*it).instantiate(std::forward<Args>(args)...);
|
||||||
|
if (instance) {
|
||||||
|
instances.emplace_back(std::move(instance));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return instances;
|
return instances;
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include "xenia/apu/xma_context.h"
|
#include "xenia/apu/xma_context.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
|
||||||
#include <XAudio2.h>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#if XE_COMPILER_MSVC
|
#if XE_COMPILER_MSVC
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
|
@ -451,7 +449,7 @@ void AudioMediaPlayer::RemovePlaylist(uint32_t handle) {
|
||||||
X_STATUS AudioMediaPlayer::SetVolume(float volume) {
|
X_STATUS AudioMediaPlayer::SetVolume(float volume) {
|
||||||
volume_ = std::min(volume, 1.0f);
|
volume_ = std::min(volume, 1.0f);
|
||||||
|
|
||||||
std::unique_lock<xe::xe_fast_mutex> guard(driver_mutex_);
|
std::unique_lock<xe_mutex> guard(driver_mutex_);
|
||||||
if (!driver_) {
|
if (!driver_) {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
@ -483,7 +481,7 @@ void AudioMediaPlayer::SetCaptureCallback(uint32_t callback, uint32_t context,
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMediaPlayer::OnStateChanged() {
|
void AudioMediaPlayer::OnStateChanged() {
|
||||||
kernel_state_->BroadcastNotification(kNotificationXmpStateChanged,
|
kernel_state_->BroadcastNotification(kXNotificationXmpStateChanged,
|
||||||
static_cast<uint32_t>(state_));
|
static_cast<uint32_t>(state_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,7 +515,7 @@ void AudioMediaPlayer::ProcessAudioBuffer(std::vector<float>* buffer) {
|
||||||
bool AudioMediaPlayer::SetupDriver(uint32_t sample_rate, uint32_t channels) {
|
bool AudioMediaPlayer::SetupDriver(uint32_t sample_rate, uint32_t channels) {
|
||||||
DeleteDriver();
|
DeleteDriver();
|
||||||
|
|
||||||
std::unique_lock<xe::xe_fast_mutex> guard(driver_mutex_);
|
std::unique_lock<xe_mutex> guard(driver_mutex_);
|
||||||
driver_semaphore_ = xe::threading::Semaphore::Create(
|
driver_semaphore_ = xe::threading::Semaphore::Create(
|
||||||
AudioSystem::kMaximumQueuedFrames, AudioSystem::kMaximumQueuedFrames);
|
AudioSystem::kMaximumQueuedFrames, AudioSystem::kMaximumQueuedFrames);
|
||||||
|
|
||||||
|
@ -543,7 +541,7 @@ bool AudioMediaPlayer::SetupDriver(uint32_t sample_rate, uint32_t channels) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMediaPlayer::DeleteDriver() {
|
void AudioMediaPlayer::DeleteDriver() {
|
||||||
std::unique_lock<xe::xe_fast_mutex> guard(driver_mutex_);
|
std::unique_lock<xe_mutex> guard(driver_mutex_);
|
||||||
if (driver_) {
|
if (driver_) {
|
||||||
if (driver_semaphore_) {
|
if (driver_semaphore_) {
|
||||||
driver_semaphore_.reset();
|
driver_semaphore_.reset();
|
||||||
|
|
|
@ -130,7 +130,7 @@ class AudioMediaPlayer {
|
||||||
// really compatible with it.
|
// really compatible with it.
|
||||||
std::unique_ptr<AudioDriver> driver_ = nullptr;
|
std::unique_ptr<AudioDriver> driver_ = nullptr;
|
||||||
std::unique_ptr<xe::threading::Semaphore> driver_semaphore_ = {};
|
std::unique_ptr<xe::threading::Semaphore> driver_semaphore_ = {};
|
||||||
xe::xe_fast_mutex driver_mutex_ = {};
|
xe_mutex driver_mutex_ = {};
|
||||||
|
|
||||||
bool SetupDriver(uint32_t sample_rate, uint32_t channels);
|
bool SetupDriver(uint32_t sample_rate, uint32_t channels);
|
||||||
void DeleteDriver();
|
void DeleteDriver();
|
||||||
|
|
|
@ -35,7 +35,7 @@ static void _generic_sequential_6_BE_to_interleaved_6_LE(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if XE_COMPILER_CLANG_CL != 1
|
#if XE_COMPILER_CLANG_CL != 1 && !XE_PLATFORM_LINUX
|
||||||
// load_be_u32 unavailable on clang-cl
|
// load_be_u32 unavailable on clang-cl
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
static void _movbe_sequential_6_BE_to_interleaved_6_LE(
|
static void _movbe_sequential_6_BE_to_interleaved_6_LE(
|
||||||
|
|
|
@ -74,6 +74,15 @@ inline int32_t atomic_inc(volatile int32_t* value) {
|
||||||
inline int32_t atomic_dec(volatile int32_t* value) {
|
inline int32_t atomic_dec(volatile int32_t* value) {
|
||||||
return __sync_sub_and_fetch(value, 1);
|
return __sync_sub_and_fetch(value, 1);
|
||||||
}
|
}
|
||||||
|
inline int32_t atomic_or(volatile int32_t* value, int32_t nv) {
|
||||||
|
return __sync_or_and_fetch(value, nv);
|
||||||
|
}
|
||||||
|
inline int32_t atomic_and(volatile int32_t* value, int32_t nv) {
|
||||||
|
return __sync_and_and_fetch(value, nv);
|
||||||
|
}
|
||||||
|
inline int32_t atomic_xor(volatile int32_t* value, int32_t nv) {
|
||||||
|
return __sync_xor_and_fetch(value, nv);
|
||||||
|
}
|
||||||
|
|
||||||
inline int32_t atomic_exchange(int32_t new_value, volatile int32_t* value) {
|
inline int32_t atomic_exchange(int32_t new_value, volatile int32_t* value) {
|
||||||
return __sync_val_compare_and_swap(value, *value, new_value);
|
return __sync_val_compare_and_swap(value, *value, new_value);
|
||||||
|
|
|
@ -91,6 +91,14 @@ struct NtSystemClock {
|
||||||
return sys_time{cdp.time_since_epoch()};
|
return sys_time{cdp.time_since_epoch()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <Domain domain_fresh_ = domain_>
|
||||||
|
static constexpr std::enable_if_t<
|
||||||
|
domain_fresh_ == Domain::Host,
|
||||||
|
std::chrono::local_time<std::chrono::system_clock::duration>>
|
||||||
|
to_local(const time_point& tp) {
|
||||||
|
return std::chrono::current_zone()->to_local(to_sys(tp));
|
||||||
|
}
|
||||||
|
|
||||||
template <Domain domain_fresh_ = domain_>
|
template <Domain domain_fresh_ = domain_>
|
||||||
static constexpr std::enable_if_t<domain_fresh_ == Domain::Host, time_point>
|
static constexpr std::enable_if_t<domain_fresh_ == Domain::Host, time_point>
|
||||||
from_sys(const std::chrono::system_clock::time_point& tp) {
|
from_sys(const std::chrono::system_clock::time_point& tp) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ extern "C" int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize logging. Needs parsed cvars.
|
// Initialize logging. Needs parsed cvars.
|
||||||
xe::InitializeLogging(entry_info.name);
|
// xe::InitializeLogging(entry_info.name);
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
for (int n = 0; n < argc; n++) {
|
for (int n = 0; n < argc; n++) {
|
||||||
|
@ -32,7 +32,7 @@ extern "C" int main(int argc, char** argv) {
|
||||||
|
|
||||||
int result = entry_info.entry_point(args);
|
int result = entry_info.entry_point(args);
|
||||||
|
|
||||||
xe::ShutdownLogging();
|
// xe::ShutdownLogging();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,24 +110,24 @@ std::string EscapeBasicString(const std::string_view view) {
|
||||||
for (auto it = begin; it != end; ++it) {
|
for (auto it = begin; it != end; ++it) {
|
||||||
auto c = *it;
|
auto c = *it;
|
||||||
if (c == '\b') {
|
if (c == '\b') {
|
||||||
result += u8"\\b";
|
result += "\\b";
|
||||||
} else if (c == '\t') {
|
} else if (c == '\t') {
|
||||||
result += u8"\\t";
|
result += "\\t";
|
||||||
} else if (c == '\n') {
|
} else if (c == '\n') {
|
||||||
result += u8"\\n";
|
result += "\\n";
|
||||||
} else if (c == '\f') {
|
} else if (c == '\f') {
|
||||||
result += u8"\\f";
|
result += "\\f";
|
||||||
} else if (c == '\r') {
|
} else if (c == '\r') {
|
||||||
result += u8"\\r";
|
result += "\\r";
|
||||||
} else if (c == '"') {
|
} else if (c == '"') {
|
||||||
result += u8"\\\"";
|
result += "\\\"";
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
result += u8"\\\\";
|
result += "\\\\";
|
||||||
} else if (c < 0x20 || c == 0x7F) {
|
} else if (c < 0x20 || c == 0x7F) {
|
||||||
if (c <= 0xFFFF) {
|
if (c <= 0xFFFF) {
|
||||||
result += fmt::format(u8"\\u{:04X}", c);
|
result += fmt::format("\\u{:04X}", c);
|
||||||
} else {
|
} else {
|
||||||
result += fmt::format(u8"\\u{:08X}", 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);
|
||||||
|
@ -150,30 +150,30 @@ std::string EscapeMultilineBasicString(const std::string_view view) {
|
||||||
}
|
}
|
||||||
for (int i = 0; i < quote_run; ++i) {
|
for (int i = 0; i < quote_run; ++i) {
|
||||||
if ((i % 3) == 2) {
|
if ((i % 3) == 2) {
|
||||||
result += u8"\\";
|
result += "\\";
|
||||||
}
|
}
|
||||||
result += u8"\"";
|
result += "\"";
|
||||||
}
|
}
|
||||||
quote_run = 0;
|
quote_run = 0;
|
||||||
}
|
}
|
||||||
if (c == '\b') {
|
if (c == '\b') {
|
||||||
result += u8"\\b";
|
result += "\\b";
|
||||||
} else if (c == '\t' || c == '\n') {
|
} else if (c == '\t' || c == '\n') {
|
||||||
result += c;
|
result += c;
|
||||||
} else if (c == '\f') {
|
} else if (c == '\f') {
|
||||||
result += u8"\\f";
|
result += "\\f";
|
||||||
} else if (c == '\r') {
|
} else if (c == '\r') {
|
||||||
// Silently drop \r.
|
// Silently drop \r.
|
||||||
// result += c;
|
// result += c;
|
||||||
} else if (c == '"') {
|
} else if (c == '"') {
|
||||||
quote_run = 1;
|
quote_run = 1;
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
result += u8"\\\\";
|
result += "\\\\";
|
||||||
} else if (c < 0x20 || c == 0x7F) {
|
} else if (c < 0x20 || c == 0x7F) {
|
||||||
if (c <= 0xFFFF) {
|
if (c <= 0xFFFF) {
|
||||||
result += fmt::format(u8"\\u{:04X}", c);
|
result += fmt::format("\\u{:04X}", c);
|
||||||
} else {
|
} else {
|
||||||
result += fmt::format(u8"\\u{:08X}", 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);
|
||||||
|
@ -181,9 +181,9 @@ std::string EscapeMultilineBasicString(const std::string_view view) {
|
||||||
}
|
}
|
||||||
for (int i = 0; i < quote_run; ++i) {
|
for (int i = 0; i < quote_run; ++i) {
|
||||||
if ((i % 3) == 2) {
|
if ((i % 3) == 2) {
|
||||||
result += u8"\\";
|
result += "\\";
|
||||||
}
|
}
|
||||||
result += u8"\"";
|
result += "\"";
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -207,11 +207,11 @@ std::string EscapeString(const std::string_view view) {
|
||||||
} else {
|
} else {
|
||||||
// multi line
|
// multi line
|
||||||
if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos &&
|
if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos &&
|
||||||
xe::utf8::find_first_of(view, u8"'''") == std::string_view::npos) {
|
xe::utf8::find_first_of(view, "'''") == std::string_view::npos) {
|
||||||
return "'''\n" + std::string(view) + "'''";
|
return "'''\n" + std::string(view) + "'''";
|
||||||
} else {
|
} else {
|
||||||
return u8"\"\"\"\n" + toml_internal::EscapeMultilineBasicString(view) +
|
return "\"\"\"\n" + toml_internal::EscapeMultilineBasicString(view) +
|
||||||
u8"\"\"\"";
|
"\"\"\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,10 +99,11 @@ void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written);
|
||||||
// might as well be noalias
|
// might as well be noalias
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
XE_NOALIAS XE_NOINLINE XE_COLD static void AppendLogLineFormat_Impl(
|
XE_NOALIAS XE_NOINLINE XE_COLD static void AppendLogLineFormat_Impl(
|
||||||
LogLevel log_level, const char prefix_char, const char* format,
|
LogLevel log_level, const char prefix_char, std::string_view format,
|
||||||
const Args&... args) {
|
const Args&... args) noexcept {
|
||||||
auto target = internal::GetThreadBuffer();
|
auto target = internal::GetThreadBuffer();
|
||||||
auto result = fmt::format_to_n(target.first, target.second, format, args...);
|
auto result = fmt::format_to_n(target.first, target.second,
|
||||||
|
fmt::runtime(format), args...);
|
||||||
internal::AppendLogLine(log_level, prefix_char, result.size);
|
internal::AppendLogLine(log_level, prefix_char, result.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,8 +114,8 @@ template <typename... Args>
|
||||||
XE_FORCEINLINE static void AppendLogLineFormat(uint32_t log_src_mask,
|
XE_FORCEINLINE static void AppendLogLineFormat(uint32_t log_src_mask,
|
||||||
LogLevel log_level,
|
LogLevel log_level,
|
||||||
const char prefix_char,
|
const char prefix_char,
|
||||||
const char* format,
|
std::string_view format,
|
||||||
const Args&... args) {
|
const Args&... args) noexcept {
|
||||||
if (!internal::ShouldLog(log_level, log_src_mask)) {
|
if (!internal::ShouldLog(log_level, log_src_mask)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +144,8 @@ struct LoggerBatch {
|
||||||
LoggerBatch() { reset(); }
|
LoggerBatch() { reset(); }
|
||||||
template <size_t fmtlen, typename... Ts>
|
template <size_t fmtlen, typename... Ts>
|
||||||
void operator()(const char (&fmt)[fmtlen], Ts&&... args) {
|
void operator()(const char (&fmt)[fmtlen], Ts&&... args) {
|
||||||
auto tmpres = fmt::format_to_n(thrd_buf, thrd_buf_rem, fmt, args...);
|
auto tmpres =
|
||||||
|
fmt::format_to_n(thrd_buf, thrd_buf_rem, fmt::runtime(fmt), args...);
|
||||||
thrd_buf_rem -= tmpres.size;
|
thrd_buf_rem -= tmpres.size;
|
||||||
thrd_buf = tmpres.out;
|
thrd_buf = tmpres.out;
|
||||||
total_size += tmpres.size;
|
total_size += tmpres.size;
|
||||||
|
@ -164,55 +166,55 @@ struct LoggerBatch {
|
||||||
#if XE_OPTION_ENABLE_LOGGING
|
#if XE_OPTION_ENABLE_LOGGING
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
XE_COLD void XELOGE(const char* format, const Args&... args) {
|
XE_COLD void XELOGE(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
||||||
xe::LogLevel::Error, '!', format, args...);
|
xe::LogLevel::Error, '!', format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
XE_COLD void XELOGW(const char* format, const Args&... args) {
|
XE_COLD void XELOGW(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
||||||
xe::LogLevel::Warning, 'w', format, args...);
|
xe::LogLevel::Warning, 'w', format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGI(const char* format, const Args&... args) {
|
void XELOGI(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
||||||
xe::LogLevel::Info, 'i', format, args...);
|
xe::LogLevel::Info, 'i', format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGD(const char* format, const Args&... args) {
|
void XELOGD(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
||||||
xe::LogLevel::Debug, 'd', format, args...);
|
xe::LogLevel::Debug, 'd', format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGCPU(const char* format, const Args&... args) {
|
void XELOGCPU(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Cpu, xe::LogLevel::Info, 'C',
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Cpu, xe::LogLevel::Info, 'C',
|
||||||
format, args...);
|
format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGAPU(const char* format, const Args&... args) {
|
void XELOGAPU(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Apu, xe::LogLevel::Debug, 'A',
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Apu, xe::LogLevel::Debug, 'A',
|
||||||
format, args...);
|
format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGGPU(const char* format, const Args&... args) {
|
void XELOGGPU(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
||||||
xe::LogLevel::Debug, 'G', format, args...);
|
xe::LogLevel::Debug, 'G', format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGKERNEL(const char* format, const Args&... args) {
|
void XELOGKERNEL(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Kernel, xe::LogLevel::Info, 'K',
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Kernel, xe::LogLevel::Info, 'K',
|
||||||
format, args...);
|
format, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void XELOGFS(const char* format, const Args&... args) {
|
void XELOGFS(std::string_view format, const Args&... args) {
|
||||||
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
xe::logging::AppendLogLineFormat(xe::LogSrc::Uncategorized,
|
||||||
xe::LogLevel::Info, 'F', format, args...);
|
xe::LogLevel::Info, 'F', format, args...);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#define XENIA_BASE_MATH_H_
|
#define XENIA_BASE_MATH_H_
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <climits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -330,7 +331,7 @@ inline T rotate_right(T v, uint8_t sh) {
|
||||||
constexpr unsigned char SHIFT_MASK = (CHAR_BIT * sizeof(T)) - 1;
|
constexpr unsigned char SHIFT_MASK = (CHAR_BIT * sizeof(T)) - 1;
|
||||||
uint8_t rshr = sh & SHIFT_MASK;
|
uint8_t rshr = sh & SHIFT_MASK;
|
||||||
uint8_t lshl = static_cast<uint8_t>(-static_cast<int8_t>(sh)) & SHIFT_MASK;
|
uint8_t lshl = static_cast<uint8_t>(-static_cast<int8_t>(sh)) & SHIFT_MASK;
|
||||||
return (n >> rshr) | (n << lshl);
|
return (v >> rshr) | (v << lshl);
|
||||||
}
|
}
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_WIN32
|
||||||
template <>
|
template <>
|
||||||
|
|
|
@ -193,7 +193,7 @@ std::unique_ptr<Socket> Socket::Connect(std::string hostname, uint16_t port) {
|
||||||
InitializeWinsock();
|
InitializeWinsock();
|
||||||
|
|
||||||
auto socket = std::make_unique<Win32Socket>();
|
auto socket = std::make_unique<Win32Socket>();
|
||||||
if (!socket->Connect(std::move(hostname), port)) {
|
if (!socket->Connect(hostname, port)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::unique_ptr<Socket>(socket.release());
|
return std::unique_ptr<Socket>(socket.release());
|
||||||
|
|
|
@ -35,7 +35,7 @@ class StringBuffer {
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void AppendFormat(const char* format, const Args&... args) {
|
void AppendFormat(const char* format, const Args&... args) {
|
||||||
auto s = fmt::format(format, args...);
|
auto s = fmt::format(fmt::runtime(format), args...);
|
||||||
Append(s.c_str());
|
Append(s.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@ void LaunchFileExplorer(const std::filesystem::path& path);
|
||||||
|
|
||||||
bool SetProcessPriorityClass(const uint32_t priority_class);
|
bool SetProcessPriorityClass(const uint32_t priority_class);
|
||||||
|
|
||||||
|
// Determine if the Xbox Gamebar is enabled via the Windows registry
|
||||||
|
bool IsUseNexusForGameBarEnabled();
|
||||||
|
|
||||||
enum class SimpleMessageBoxType {
|
enum class SimpleMessageBoxType {
|
||||||
Help,
|
Help,
|
||||||
Warning,
|
Warning,
|
||||||
|
|
|
@ -296,4 +296,5 @@ void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
|
||||||
|
|
||||||
bool SetProcessPriorityClass(const uint32_t priority_class) { return true; }
|
bool SetProcessPriorityClass(const uint32_t priority_class) { return true; }
|
||||||
|
|
||||||
|
bool IsUseNexusForGameBarEnabled() { return false; }
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -69,4 +69,5 @@ void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
|
||||||
|
|
||||||
bool SetProcessPriorityClass(const uint32_t priority_class) { return true; }
|
bool SetProcessPriorityClass(const uint32_t priority_class) { return true; }
|
||||||
|
|
||||||
|
bool IsUseNexusForGameBarEnabled() { return false; }
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -64,4 +64,17 @@ bool SetProcessPriorityClass(const uint32_t priority_class) {
|
||||||
xeniaToWindowsPriorityClassMapping[priority_class]);
|
xeniaToWindowsPriorityClassMapping[priority_class]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsUseNexusForGameBarEnabled() {
|
||||||
|
const LPCWSTR reg_path = L"SOFTWARE\\Microsoft\\GameBar";
|
||||||
|
const LPCWSTR key = L"UseNexusForGameBarEnabled";
|
||||||
|
|
||||||
|
DWORD value = 0;
|
||||||
|
DWORD dataSize = sizeof(value);
|
||||||
|
|
||||||
|
RegGetValue(HKEY_CURRENT_USER, reg_path, key, RRF_RT_DWORD, nullptr, &value,
|
||||||
|
&dataSize);
|
||||||
|
|
||||||
|
return static_cast<bool>(value);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -407,7 +407,7 @@ TEST_CASE("Wait on Multiple Events", "[event]") {
|
||||||
std::array<char, 8> order = {0};
|
std::array<char, 8> order = {0};
|
||||||
std::atomic_uint index(0);
|
std::atomic_uint index(0);
|
||||||
auto sign_in = [&order, &index](uint32_t id) {
|
auto sign_in = [&order, &index](uint32_t id) {
|
||||||
auto i = index.fetch_add(1, std::memory_order::memory_order_relaxed);
|
auto i = index.fetch_add(1, std::memory_order_relaxed);
|
||||||
order[i] = static_cast<char>('0' + id);
|
order[i] = static_cast<char>('0' + id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1071,8 +1071,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
||||||
int is_modified;
|
int is_modified;
|
||||||
int has_finished;
|
int has_finished;
|
||||||
auto callback = [&is_modified, &order] {
|
auto callback = [&is_modified, &order] {
|
||||||
is_modified = std::atomic_fetch_add_explicit(
|
is_modified =
|
||||||
&order, 1, std::memory_order::memory_order_relaxed);
|
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Without alertable
|
// Without alertable
|
||||||
|
@ -1084,8 +1084,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
||||||
order++; // 1
|
order++; // 1
|
||||||
Sleep(90ms);
|
Sleep(90ms);
|
||||||
order++; // 2
|
order++; // 2
|
||||||
has_finished = std::atomic_fetch_add_explicit(
|
has_finished =
|
||||||
&order, 1, std::memory_order::memory_order_relaxed);
|
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||||
});
|
});
|
||||||
REQUIRE(!spin_wait_for(50ms, [&] { return order == 2; }));
|
REQUIRE(!spin_wait_for(50ms, [&] { return order == 2; }));
|
||||||
REQUIRE(is_modified == -1);
|
REQUIRE(is_modified == -1);
|
||||||
|
@ -1104,8 +1104,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
||||||
order++; // 1
|
order++; // 1
|
||||||
AlertableSleep(90ms);
|
AlertableSleep(90ms);
|
||||||
order++; // 3
|
order++; // 3
|
||||||
has_finished = std::atomic_fetch_add_explicit(
|
has_finished =
|
||||||
&order, 1, std::memory_order::memory_order_relaxed);
|
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||||
});
|
});
|
||||||
REQUIRE(!spin_wait_for(50ms, [&] { return order == 2; }));
|
REQUIRE(!spin_wait_for(50ms, [&] { return order == 2; }));
|
||||||
REQUIRE(is_modified == -1);
|
REQUIRE(is_modified == -1);
|
||||||
|
@ -1120,8 +1120,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
||||||
is_modified = -1;
|
is_modified = -1;
|
||||||
has_finished = -1;
|
has_finished = -1;
|
||||||
thread = Thread::Create(params, [&is_modified, &has_finished, &order] {
|
thread = Thread::Create(params, [&is_modified, &has_finished, &order] {
|
||||||
is_modified = std::atomic_fetch_add_explicit(
|
is_modified =
|
||||||
&order, 1, std::memory_order::memory_order_relaxed);
|
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||||
// Using Alertable so callback is registered
|
// Using Alertable so callback is registered
|
||||||
order++; // 2
|
order++; // 2
|
||||||
AlertableSleep(1s);
|
AlertableSleep(1s);
|
||||||
|
|
|
@ -37,121 +37,121 @@ namespace examples {
|
||||||
|
|
||||||
const size_t kDanishCount = 1;
|
const size_t kDanishCount = 1;
|
||||||
const char* kDanishValues[kDanishCount] = {
|
const char* kDanishValues[kDanishCount] = {
|
||||||
u8"Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther "
|
"Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther "
|
||||||
u8"spillede på xylofon.",
|
"spillede på xylofon.",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Danish(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Danish(func, results) \
|
||||||
TEST_EXAMPLES_1(func, Danish, results)
|
TEST_EXAMPLES_1(func, Danish, results)
|
||||||
|
|
||||||
const size_t kGermanCount = 3;
|
const size_t kGermanCount = 3;
|
||||||
const char* kGermanValues[kGermanCount] = {
|
const char* kGermanValues[kGermanCount] = {
|
||||||
u8"Falsches Üben von Xylophonmusik quält jeden größeren Zwerg",
|
"Falsches Üben von Xylophonmusik quält jeden größeren Zwerg",
|
||||||
u8"Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich",
|
"Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich",
|
||||||
u8"Heizölrückstoßabdämpfung",
|
"Heizölrückstoßabdämpfung",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_German(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_German(func, results) \
|
||||||
TEST_EXAMPLES_2(func, German, results)
|
TEST_EXAMPLES_2(func, German, results)
|
||||||
|
|
||||||
const size_t kGreekCount = 2;
|
const size_t kGreekCount = 2;
|
||||||
const char* kGreekValues[kGreekCount] = {
|
const char* kGreekValues[kGreekCount] = {
|
||||||
u8"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο",
|
"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο",
|
||||||
u8"Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία",
|
"Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Greek(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Greek(func, results) \
|
||||||
TEST_EXAMPLES_2(func, Greek, results)
|
TEST_EXAMPLES_2(func, Greek, results)
|
||||||
|
|
||||||
const size_t kEnglishCount = 1;
|
const size_t kEnglishCount = 1;
|
||||||
const char* kEnglishValues[kEnglishCount] = {
|
const char* kEnglishValues[kEnglishCount] = {
|
||||||
u8"The quick brown fox jumps over the lazy dog",
|
"The quick brown fox jumps over the lazy dog",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_English(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_English(func, results) \
|
||||||
TEST_EXAMPLES_1(func, English, results)
|
TEST_EXAMPLES_1(func, English, results)
|
||||||
|
|
||||||
const size_t kSpanishCount = 1;
|
const size_t kSpanishCount = 1;
|
||||||
const char* kSpanishValues[kSpanishCount] = {
|
const char* kSpanishValues[kSpanishCount] = {
|
||||||
u8"El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, "
|
"El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, "
|
||||||
u8"añoraba a su querido cachorro.",
|
"añoraba a su querido cachorro.",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Spanish(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Spanish(func, results) \
|
||||||
TEST_EXAMPLES_1(func, Spanish, results)
|
TEST_EXAMPLES_1(func, Spanish, results)
|
||||||
|
|
||||||
const size_t kFrenchCount = 3;
|
const size_t kFrenchCount = 3;
|
||||||
const char* kFrenchValues[kFrenchCount] = {
|
const char* kFrenchValues[kFrenchCount] = {
|
||||||
u8"Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à "
|
"Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à "
|
||||||
u8"côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce qui "
|
"côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce qui "
|
||||||
u8"lui permet de penser à la cænogenèse de l'être dont il est question "
|
"lui permet de penser à la cænogenèse de l'être dont il est question "
|
||||||
u8"dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui, "
|
"dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui, "
|
||||||
u8"pense-t-il, diminue çà et là la qualité de son œuvre.",
|
"pense-t-il, diminue çà et là la qualité de son œuvre.",
|
||||||
u8"l'île exiguë\n"
|
"l'île exiguë\n"
|
||||||
u8"Où l'obèse jury mûr\n"
|
"Où l'obèse jury mûr\n"
|
||||||
u8"Fête l'haï volapük,\n"
|
"Fête l'haï volapük,\n"
|
||||||
u8"Âne ex aéquo au whist,\n"
|
"Âne ex aéquo au whist,\n"
|
||||||
u8"Ôtez ce vœu déçu.",
|
"Ôtez ce vœu déçu.",
|
||||||
u8"Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë "
|
"Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë "
|
||||||
u8"au delà des îles, près du mälström où brûlent les novæ.",
|
"au delà des îles, près du mälström où brûlent les novæ.",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_French(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_French(func, results) \
|
||||||
TEST_EXAMPLES_3(func, French, results)
|
TEST_EXAMPLES_3(func, French, results)
|
||||||
|
|
||||||
const size_t kIrishGaelicCount = 1;
|
const size_t kIrishGaelicCount = 1;
|
||||||
const char* kIrishGaelicValues[kIrishGaelicCount] = {
|
const char* kIrishGaelicValues[kIrishGaelicCount] = {
|
||||||
u8"D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh",
|
"D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_IrishGaelic(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_IrishGaelic(func, results) \
|
||||||
TEST_EXAMPLES_1(func, IrishGaelic, results)
|
TEST_EXAMPLES_1(func, IrishGaelic, results)
|
||||||
|
|
||||||
const size_t kHungarianCount = 1;
|
const size_t kHungarianCount = 1;
|
||||||
const char* kHungarianValues[kHungarianCount] = {
|
const char* kHungarianValues[kHungarianCount] = {
|
||||||
u8"Árvíztűrő tükörfúrógép",
|
"Árvíztűrő tükörfúrógép",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Hungarian(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Hungarian(func, results) \
|
||||||
TEST_EXAMPLES_1(func, Hungarian, results)
|
TEST_EXAMPLES_1(func, Hungarian, results)
|
||||||
|
|
||||||
const size_t kIcelandicCount = 2;
|
const size_t kIcelandicCount = 2;
|
||||||
const char* kIcelandicValues[kIcelandicCount] = {
|
const char* kIcelandicValues[kIcelandicCount] = {
|
||||||
u8"Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa",
|
"Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa",
|
||||||
u8"Sævör grét áðan því úlpan var ónýt",
|
"Sævör grét áðan því úlpan var ónýt",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Icelandic(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Icelandic(func, results) \
|
||||||
TEST_EXAMPLES_2(func, Icelandic, results)
|
TEST_EXAMPLES_2(func, Icelandic, results)
|
||||||
|
|
||||||
const size_t kJapaneseCount = 2;
|
const size_t kJapaneseCount = 2;
|
||||||
const char* kJapaneseValues[kJapaneseCount] = {
|
const char* kJapaneseValues[kJapaneseCount] = {
|
||||||
u8"いろはにほへとちりぬるを\n"
|
"いろはにほへとちりぬるを\n"
|
||||||
u8"わかよたれそつねならむ\n"
|
"わかよたれそつねならむ\n"
|
||||||
u8"うゐのおくやまけふこえて\n"
|
"うゐのおくやまけふこえて\n"
|
||||||
u8"あさきゆめみしゑひもせす\n",
|
"あさきゆめみしゑひもせす\n",
|
||||||
u8"イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム\n"
|
"イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム\n"
|
||||||
u8"ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン",
|
"ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Japanese(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Japanese(func, results) \
|
||||||
TEST_EXAMPLES_2(func, Japanese, results)
|
TEST_EXAMPLES_2(func, Japanese, results)
|
||||||
|
|
||||||
const size_t kHebrewCount = 1;
|
const size_t kHebrewCount = 1;
|
||||||
const char* kHebrewValues[kHebrewCount] = {
|
const char* kHebrewValues[kHebrewCount] = {
|
||||||
u8"? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה",
|
"? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Hebrew(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Hebrew(func, results) \
|
||||||
TEST_EXAMPLES_1(func, Hebrew, results)
|
TEST_EXAMPLES_1(func, Hebrew, results)
|
||||||
|
|
||||||
const size_t kPolishCount = 1;
|
const size_t kPolishCount = 1;
|
||||||
const char* kPolishValues[kPolishCount] = {
|
const char* kPolishValues[kPolishCount] = {
|
||||||
u8"Pchnąć w tę łódź jeża lub ośm skrzyń fig",
|
"Pchnąć w tę łódź jeża lub ośm skrzyń fig",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Polish(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Polish(func, results) \
|
||||||
TEST_EXAMPLES_1(func, Polish, results)
|
TEST_EXAMPLES_1(func, Polish, results)
|
||||||
|
|
||||||
const size_t kRussianCount = 2;
|
const size_t kRussianCount = 2;
|
||||||
const char* kRussianValues[kRussianCount] = {
|
const char* kRussianValues[kRussianCount] = {
|
||||||
u8"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
|
"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
|
||||||
u8"Съешь же ещё этих мягких французских булок да выпей чаю",
|
"Съешь же ещё этих мягких французских булок да выпей чаю",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Russian(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Russian(func, results) \
|
||||||
TEST_EXAMPLES_2(func, Russian, results)
|
TEST_EXAMPLES_2(func, Russian, results)
|
||||||
|
|
||||||
const size_t kTurkishCount = 1;
|
const size_t kTurkishCount = 1;
|
||||||
const char* kTurkishValues[kTurkishCount] = {
|
const char* kTurkishValues[kTurkishCount] = {
|
||||||
u8"Pijamalı hasta, yağız şoföre çabucak güvendi.",
|
"Pijamalı hasta, yağız şoföre çabucak güvendi.",
|
||||||
};
|
};
|
||||||
#define TEST_LANGUAGE_EXAMPLES_Turkish(func, results) \
|
#define TEST_LANGUAGE_EXAMPLES_Turkish(func, results) \
|
||||||
TEST_EXAMPLES_1(func, Turkish, results)
|
TEST_EXAMPLES_1(func, Turkish, results)
|
||||||
|
@ -229,54 +229,54 @@ TEST_CASE("UTF-8 Split", "[utf8]") {
|
||||||
std::vector<std::string_view> parts;
|
std::vector<std::string_view> parts;
|
||||||
|
|
||||||
// Danish
|
// Danish
|
||||||
parts = utf8::split(examples::kDanishValues[0], u8"æcå");
|
parts = utf8::split(examples::kDanishValues[0], "æcå");
|
||||||
REQUIRE(parts.size() == 4);
|
REQUIRE(parts.size() == 4);
|
||||||
REQUIRE(parts[0] == u8"Quizdeltagerne spiste jordb");
|
REQUIRE(parts[0] == "Quizdeltagerne spiste jordb");
|
||||||
REQUIRE(parts[1] == u8"r med fløde, mens ");
|
REQUIRE(parts[1] == "r med fløde, mens ");
|
||||||
REQUIRE(parts[2] == u8"irkusklovnen Wolther spillede p");
|
REQUIRE(parts[2] == "irkusklovnen Wolther spillede p");
|
||||||
REQUIRE(parts[3] == u8" xylofon.");
|
REQUIRE(parts[3] == " xylofon.");
|
||||||
|
|
||||||
// German
|
// German
|
||||||
parts = utf8::split(examples::kGermanValues[0], u8"ßS");
|
parts = utf8::split(examples::kGermanValues[0], "ßS");
|
||||||
REQUIRE(parts.size() == 2);
|
REQUIRE(parts.size() == 2);
|
||||||
REQUIRE(parts[0] == u8"Falsches Üben von Xylophonmusik quält jeden grö");
|
REQUIRE(parts[0] == "Falsches Üben von Xylophonmusik quält jeden grö");
|
||||||
REQUIRE(parts[1] == u8"eren Zwerg");
|
REQUIRE(parts[1] == "eren Zwerg");
|
||||||
parts = utf8::split(examples::kGermanValues[1], u8"ßS");
|
parts = utf8::split(examples::kGermanValues[1], "ßS");
|
||||||
REQUIRE(parts.size() == 2);
|
REQUIRE(parts.size() == 2);
|
||||||
REQUIRE(parts[0] == u8"Zwölf Boxkämpfer jagten Eva quer über den ");
|
REQUIRE(parts[0] == "Zwölf Boxkämpfer jagten Eva quer über den ");
|
||||||
REQUIRE(parts[1] == u8"ylter Deich");
|
REQUIRE(parts[1] == "ylter Deich");
|
||||||
parts = utf8::split(examples::kGermanValues[2], u8"ßS");
|
parts = utf8::split(examples::kGermanValues[2], "ßS");
|
||||||
REQUIRE(parts.size() == 2);
|
REQUIRE(parts.size() == 2);
|
||||||
REQUIRE(parts[0] == u8"Heizölrücksto");
|
REQUIRE(parts[0] == "Heizölrücksto");
|
||||||
REQUIRE(parts[1] == u8"abdämpfung");
|
REQUIRE(parts[1] == "abdämpfung");
|
||||||
|
|
||||||
// Greek
|
// Greek
|
||||||
parts = utf8::split(examples::kGreekValues[0], u8"πφ");
|
parts = utf8::split(examples::kGreekValues[0], "πφ");
|
||||||
REQUIRE(parts.size() == 4);
|
REQUIRE(parts.size() == 4);
|
||||||
REQUIRE(parts[0] == u8"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ ");
|
REQUIRE(parts[0] == "Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ ");
|
||||||
REQUIRE(parts[1] == u8"ιὰ στὸ χρυσα");
|
REQUIRE(parts[1] == "ιὰ στὸ χρυσα");
|
||||||
REQUIRE(parts[2] == u8"ὶ ξέ");
|
REQUIRE(parts[2] == "ὶ ξέ");
|
||||||
REQUIRE(parts[3] == u8"ωτο");
|
REQUIRE(parts[3] == "ωτο");
|
||||||
parts = utf8::split(examples::kGreekValues[1], u8"πφ");
|
parts = utf8::split(examples::kGreekValues[1], "πφ");
|
||||||
REQUIRE(parts.size() == 3);
|
REQUIRE(parts.size() == 3);
|
||||||
REQUIRE(parts[0] == u8"Ξεσκε");
|
REQUIRE(parts[0] == "Ξεσκε");
|
||||||
REQUIRE(parts[1] == u8"άζω τὴν ψυχο");
|
REQUIRE(parts[1] == "άζω τὴν ψυχο");
|
||||||
REQUIRE(parts[2] == u8"θόρα βδελυγμία");
|
REQUIRE(parts[2] == "θόρα βδελυγμία");
|
||||||
|
|
||||||
// English
|
// English
|
||||||
parts = utf8::split(examples::kEnglishValues[0], "xy");
|
parts = utf8::split(examples::kEnglishValues[0], "xy");
|
||||||
REQUIRE(parts.size() == 3);
|
REQUIRE(parts.size() == 3);
|
||||||
REQUIRE(parts[0] == u8"The quick brown fo");
|
REQUIRE(parts[0] == "The quick brown fo");
|
||||||
REQUIRE(parts[1] == u8" jumps over the laz");
|
REQUIRE(parts[1] == " jumps over the laz");
|
||||||
REQUIRE(parts[2] == u8" dog");
|
REQUIRE(parts[2] == " dog");
|
||||||
|
|
||||||
// Spanish
|
// Spanish
|
||||||
parts = utf8::split(examples::kSpanishValues[0], u8"ójd");
|
parts = utf8::split(examples::kSpanishValues[0], "ójd");
|
||||||
REQUIRE(parts.size() == 4);
|
REQUIRE(parts.size() == 4);
|
||||||
REQUIRE(parts[0] == u8"El pingüino Wenceslao hizo kil");
|
REQUIRE(parts[0] == "El pingüino Wenceslao hizo kil");
|
||||||
REQUIRE(parts[1] == u8"metros ba");
|
REQUIRE(parts[1] == "metros ba");
|
||||||
REQUIRE(parts[2] == u8"o exhaustiva lluvia y frío, añoraba a su queri");
|
REQUIRE(parts[2] == "o exhaustiva lluvia y frío, añoraba a su queri");
|
||||||
REQUIRE(parts[3] == u8"o cachorro.");
|
REQUIRE(parts[3] == "o cachorro.");
|
||||||
|
|
||||||
// TODO(gibbed): French
|
// TODO(gibbed): French
|
||||||
// TODO(gibbed): Irish Gaelic
|
// TODO(gibbed): Irish Gaelic
|
||||||
|
@ -291,18 +291,18 @@ TEST_CASE("UTF-8 Split", "[utf8]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("UTF-8 Equal Z", "[utf8]") {
|
TEST_CASE("UTF-8 Equal Z", "[utf8]") {
|
||||||
REQUIRE(utf8::equal_z(u8"foo", u8"foo\0"));
|
REQUIRE(utf8::equal_z("foo", "foo\0"));
|
||||||
REQUIRE_FALSE(utf8::equal_z(u8"bar", u8"baz\0"));
|
REQUIRE_FALSE(utf8::equal_z("bar", "baz\0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("UTF-8 Equal Case", "[utf8]") {
|
TEST_CASE("UTF-8 Equal Case", "[utf8]") {
|
||||||
REQUIRE(utf8::equal_case(u8"foo", u8"foo\0"));
|
REQUIRE(utf8::equal_case("foo", "foo\0"));
|
||||||
REQUIRE_FALSE(utf8::equal_case(u8"bar", u8"baz\0"));
|
REQUIRE_FALSE(utf8::equal_case("bar", "baz\0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("UTF-8 Equal Case Z", "[utf8]") {
|
TEST_CASE("UTF-8 Equal Case Z", "[utf8]") {
|
||||||
REQUIRE(utf8::equal_case_z(u8"foo", u8"foo\0"));
|
REQUIRE(utf8::equal_case_z("foo", "foo\0"));
|
||||||
REQUIRE_FALSE(utf8::equal_case_z(u8"bar", u8"baz\0"));
|
REQUIRE_FALSE(utf8::equal_case_z("bar", "baz\0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gibbed): find_any_of
|
// TODO(gibbed): find_any_of
|
||||||
|
@ -346,11 +346,11 @@ TEST_CASE("UTF-8 Equal Case Z", "[utf8]") {
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
TEST_CASE("UTF-8 Join Paths", "[utf8]") {
|
TEST_CASE("UTF-8 Join Paths", "[utf8]") {
|
||||||
TEST_PATHS(utf8::join_paths, u8"");
|
TEST_PATHS(utf8::join_paths, "");
|
||||||
TEST_PATHS(utf8::join_paths, u8"foo", u8"foo");
|
TEST_PATHS(utf8::join_paths, "foo", "foo");
|
||||||
TEST_PATHS(utf8::join_paths, u8"foo/bar", u8"foo", u8"bar");
|
TEST_PATHS(utf8::join_paths, "foo/bar", "foo", "bar");
|
||||||
TEST_PATHS(utf8::join_paths, "X:/foo/bar/baz/qux", u8"X:", u8"foo", u8"bar",
|
TEST_PATHS(utf8::join_paths, "X:/foo/bar/baz/qux", "X:", "foo", "bar", "baz",
|
||||||
u8"baz", u8"qux");
|
"qux");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gibbed): join_guest_paths
|
// TODO(gibbed): join_guest_paths
|
||||||
|
|
|
@ -402,6 +402,7 @@ class Timer : public WaitHandle {
|
||||||
virtual bool Cancel() = 0;
|
virtual bool Cancel() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if XE_PLATFORM_WINDOWS
|
||||||
struct ThreadPriority {
|
struct ThreadPriority {
|
||||||
static const int32_t kLowest = -2;
|
static const int32_t kLowest = -2;
|
||||||
static const int32_t kBelowNormal = -1;
|
static const int32_t kBelowNormal = -1;
|
||||||
|
@ -409,6 +410,15 @@ struct ThreadPriority {
|
||||||
static const int32_t kAboveNormal = 1;
|
static const int32_t kAboveNormal = 1;
|
||||||
static const int32_t kHighest = 2;
|
static const int32_t kHighest = 2;
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
struct ThreadPriority {
|
||||||
|
static const int32_t kLowest = 1;
|
||||||
|
static const int32_t kBelowNormal = 8;
|
||||||
|
static const int32_t kNormal = 16;
|
||||||
|
static const int32_t kAboveNormal = 24;
|
||||||
|
static const int32_t kHighest = 32;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// Models a Win32-like thread object.
|
// Models a Win32-like thread object.
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_ANDROID
|
#if XE_PLATFORM_ANDROID
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
@ -660,8 +662,18 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
||||||
WaitStarted();
|
WaitStarted();
|
||||||
sched_param param{};
|
sched_param param{};
|
||||||
param.sched_priority = new_priority;
|
param.sched_priority = new_priority;
|
||||||
if (pthread_setschedparam(thread_, SCHED_FIFO, ¶m) != 0)
|
int res = pthread_setschedparam(thread_, SCHED_FIFO, ¶m);
|
||||||
assert_always();
|
if (res != 0) {
|
||||||
|
switch (res) {
|
||||||
|
case EPERM:
|
||||||
|
XELOGW("Permission denied while setting priority");
|
||||||
|
break;
|
||||||
|
case EINVAL:
|
||||||
|
assert_always();
|
||||||
|
default:
|
||||||
|
XELOGW("Unknown error while setting priority");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueueUserCallback(std::function<void()> callback) {
|
void QueueUserCallback(std::function<void()> callback) {
|
||||||
|
@ -905,7 +917,7 @@ class PosixEvent : public PosixConditionHandle<Event> {
|
||||||
~PosixEvent() override = default;
|
~PosixEvent() override = default;
|
||||||
void Set() override { handle_.Signal(); }
|
void Set() override { handle_.Signal(); }
|
||||||
void Reset() override { handle_.Reset(); }
|
void Reset() override { handle_.Reset(); }
|
||||||
EventInfo Query() {
|
EventInfo Query() override {
|
||||||
EventInfo result{};
|
EventInfo result{};
|
||||||
assert_always();
|
assert_always();
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -38,7 +38,12 @@ using WaitItem = TimerQueueWaitItem;
|
||||||
condition_variable::wait_until) but now builds
|
condition_variable::wait_until) but now builds
|
||||||
|
|
||||||
*/
|
*/
|
||||||
using WaitStrat = dp::blocking_wait_strategy;
|
|
||||||
|
/*
|
||||||
|
edit2: (30.12.2024) After uplifting version of MSVC compiler Xenia cannot be
|
||||||
|
correctly initialized if you're using proton.
|
||||||
|
*/
|
||||||
|
using WaitStrat = dp::spin_wait_strategy;
|
||||||
|
|
||||||
class TimerQueue {
|
class TimerQueue {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -469,7 +469,7 @@ bool ends_with_case(const std::string_view haystack,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string_view> split_path(const std::string_view path) {
|
std::vector<std::string_view> split_path(const std::string_view path) {
|
||||||
return split(path, u8"\\/", true);
|
return split(path, "\\/", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string join_paths(const std::string_view left_path,
|
std::string join_paths(const std::string_view left_path,
|
||||||
|
|
|
@ -99,7 +99,8 @@ class HIRBuilder {
|
||||||
void CommentFormat(const std::string_view format, const Args&... args) {
|
void CommentFormat(const std::string_view format, const Args&... args) {
|
||||||
static const uint32_t kMaxCommentSize = 1024;
|
static const uint32_t kMaxCommentSize = 1024;
|
||||||
char* p = reinterpret_cast<char*>(arena_->Alloc(kMaxCommentSize, 1));
|
char* p = reinterpret_cast<char*>(arena_->Alloc(kMaxCommentSize, 1));
|
||||||
auto result = fmt::format_to_n(p, kMaxCommentSize - 1, format, args...);
|
auto result =
|
||||||
|
fmt::format_to_n(p, kMaxCommentSize - 1, fmt::runtime(format), args...);
|
||||||
p[result.size] = '\0';
|
p[result.size] = '\0';
|
||||||
size_t rewind = kMaxCommentSize - 1 - result.size;
|
size_t rewind = kMaxCommentSize - 1 - result.size;
|
||||||
arena_->Rewind(rewind);
|
arena_->Rewind(rewind);
|
||||||
|
|
|
@ -433,17 +433,14 @@ typedef struct alignas(64) PPCContext_s {
|
||||||
template <typename T = uint8_t*>
|
template <typename T = uint8_t*>
|
||||||
inline T TranslateVirtual(uint32_t guest_address) XE_RESTRICT const {
|
inline T TranslateVirtual(uint32_t guest_address) XE_RESTRICT const {
|
||||||
static_assert(std::is_pointer_v<T>);
|
static_assert(std::is_pointer_v<T>);
|
||||||
#if XE_PLATFORM_WIN32 == 1
|
|
||||||
uint8_t* host_address = virtual_membase + guest_address;
|
uint8_t* host_address = virtual_membase + guest_address;
|
||||||
|
#if XE_PLATFORM_WIN32 == 1
|
||||||
if (guest_address >=
|
if (guest_address >=
|
||||||
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this))) {
|
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this))) {
|
||||||
host_address += 0x1000;
|
host_address += 0x1000;
|
||||||
}
|
}
|
||||||
return reinterpret_cast<T>(host_address);
|
|
||||||
#else
|
|
||||||
return processor->memory()->TranslateVirtual<T>(guest_address);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
return reinterpret_cast<T>(host_address);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline xe::be<T>* TranslateVirtualBE(uint32_t guest_address)
|
inline xe::be<T>* TranslateVirtualBE(uint32_t guest_address)
|
||||||
|
@ -464,17 +461,14 @@ typedef struct alignas(64) PPCContext_s {
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline uint32_t HostToGuestVirtual(T* host_ptr) XE_RESTRICT const {
|
inline uint32_t HostToGuestVirtual(T* host_ptr) XE_RESTRICT const {
|
||||||
#if XE_PLATFORM_WIN32 == 1
|
|
||||||
uint32_t guest_tmp = static_cast<uint32_t>(
|
uint32_t guest_tmp = static_cast<uint32_t>(
|
||||||
reinterpret_cast<const uint8_t*>(host_ptr) - virtual_membase);
|
reinterpret_cast<const uint8_t*>(host_ptr) - virtual_membase);
|
||||||
|
#if XE_PLATFORM_WIN32 == 1
|
||||||
if (guest_tmp >= static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this))) {
|
if (guest_tmp >= static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this))) {
|
||||||
guest_tmp -= 0x1000;
|
guest_tmp -= 0x1000;
|
||||||
}
|
}
|
||||||
return guest_tmp;
|
|
||||||
#else
|
|
||||||
return processor->memory()->HostToGuestVirtual(
|
|
||||||
reinterpret_cast<void*>(host_ptr));
|
|
||||||
#endif
|
#endif
|
||||||
|
return guest_tmp;
|
||||||
}
|
}
|
||||||
static std::string GetRegisterName(PPCRegister reg);
|
static std::string GetRegisterName(PPCRegister reg);
|
||||||
std::string GetStringFromValue(PPCRegister reg) const;
|
std::string GetStringFromValue(PPCRegister reg) const;
|
||||||
|
|
|
@ -57,15 +57,15 @@ Memory* PPCFrontend::memory() const { return processor_->memory(); }
|
||||||
// Checks the state of the global lock and sets scratch to the current MSR
|
// Checks the state of the global lock and sets scratch to the current MSR
|
||||||
// value.
|
// value.
|
||||||
void CheckGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
void CheckGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
||||||
auto global_mutex = reinterpret_cast<xe_global_mutex*>(arg0);
|
auto global_mutex = reinterpret_cast<global_mutex_type*>(arg0);
|
||||||
auto global_lock_count = reinterpret_cast<int32_t*>(arg1);
|
auto global_lock_count = reinterpret_cast<int32_t*>(arg1);
|
||||||
std::lock_guard<xe_global_mutex> lock(*global_mutex);
|
std::lock_guard<global_mutex_type> lock(*global_mutex);
|
||||||
ppc_context->scratch = *global_lock_count ? 0 : 0x8000;
|
ppc_context->scratch = *global_lock_count ? 0 : 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enters the global lock. Safe to recursion.
|
// Enters the global lock. Safe to recursion.
|
||||||
void EnterGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
void EnterGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
||||||
auto global_mutex = reinterpret_cast<xe_global_mutex*>(arg0);
|
auto global_mutex = reinterpret_cast<global_mutex_type*>(arg0);
|
||||||
auto global_lock_count = reinterpret_cast<int32_t*>(arg1);
|
auto global_lock_count = reinterpret_cast<int32_t*>(arg1);
|
||||||
global_mutex->lock();
|
global_mutex->lock();
|
||||||
xe::atomic_inc(global_lock_count);
|
xe::atomic_inc(global_lock_count);
|
||||||
|
@ -73,7 +73,7 @@ void EnterGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
||||||
|
|
||||||
// Leaves the global lock. Safe to recursion.
|
// Leaves the global lock. Safe to recursion.
|
||||||
void LeaveGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
void LeaveGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
|
||||||
auto global_mutex = reinterpret_cast<xe_global_mutex*>(arg0);
|
auto global_mutex = reinterpret_cast<global_mutex_type*>(arg0);
|
||||||
auto global_lock_count = reinterpret_cast<int32_t*>(arg1);
|
auto global_lock_count = reinterpret_cast<int32_t*>(arg1);
|
||||||
auto new_lock_count = xe::atomic_dec(global_lock_count);
|
auto new_lock_count = xe::atomic_dec(global_lock_count);
|
||||||
assert_true(new_lock_count >= 0);
|
assert_true(new_lock_count >= 0);
|
||||||
|
|
|
@ -151,11 +151,15 @@ void PPCTranslator::DumpHIR(GuestFunction* function, PPCHIRBuilder* builder) {
|
||||||
|
|
||||||
{
|
{
|
||||||
wchar_t tmpbuf[64];
|
wchar_t tmpbuf[64];
|
||||||
|
#ifdef XE_PLATFORM_WIN32
|
||||||
_snwprintf(tmpbuf, 64, L"%X", function->address());
|
_snwprintf(tmpbuf, 64, L"%X", function->address());
|
||||||
|
#else
|
||||||
|
swprintf(tmpbuf, 64, L"%X", function->address());
|
||||||
|
#endif
|
||||||
folder_path.append(&tmpbuf[0]);
|
folder_path.append(&tmpbuf[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* f = fopen(folder_path.generic_u8string().c_str(), "w");
|
FILE* f = fopen(folder_path.string().c_str(), "w");
|
||||||
if (f) {
|
if (f) {
|
||||||
fputs(buffer.buffer(), f);
|
fputs(buffer.buffer(), f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
|
@ -447,7 +447,7 @@ bool Processor::Restore(ByteStream* stream) {
|
||||||
std::vector<uint32_t> to_delete;
|
std::vector<uint32_t> to_delete;
|
||||||
for (auto& it : thread_debug_infos_) {
|
for (auto& it : thread_debug_infos_) {
|
||||||
if (it.second->state == ThreadDebugInfo::State::kZombie) {
|
if (it.second->state == ThreadDebugInfo::State::kZombie) {
|
||||||
it.second->thread_handle = NULL;
|
it.second->thread_handle = 0;
|
||||||
to_delete.push_back(it.first);
|
to_delete.push_back(it.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ void Processor::OnThreadDestroyed(uint32_t thread_id) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
auto it = thread_debug_infos_.find(thread_id);
|
auto it = thread_debug_infos_.find(thread_id);
|
||||||
assert_true(it != thread_debug_infos_.end());
|
assert_true(it != thread_debug_infos_.end());
|
||||||
it->second->thread_handle = NULL;
|
it->second->thread_handle = 0;
|
||||||
thread_debug_infos_.erase(it);
|
thread_debug_infos_.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1136,10 +1136,14 @@ void XexModule::Precompile() {
|
||||||
high_code);
|
high_code);
|
||||||
final_image_sha_.finalize(image_sha_bytes_);
|
final_image_sha_.finalize(image_sha_bytes_);
|
||||||
|
|
||||||
char fmtbuf[16];
|
char fmtbuf[20];
|
||||||
|
|
||||||
for (unsigned i = 0; i < 20; ++i) {
|
for (unsigned i = 0; i < 20; ++i) {
|
||||||
|
#ifdef XE_PLATFORM_WIN32
|
||||||
sprintf_s(fmtbuf, "%X", image_sha_bytes_[i]);
|
sprintf_s(fmtbuf, "%X", image_sha_bytes_[i]);
|
||||||
|
#else
|
||||||
|
snprintf(fmtbuf, sizeof(fmtbuf), "%X", image_sha_bytes_[i]);
|
||||||
|
#endif
|
||||||
image_sha_str_ += &fmtbuf[0];
|
image_sha_str_ += &fmtbuf[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1397,6 +1401,10 @@ void XexInfoCache::Init(XexModule* xexmod) {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool did_exist = try_open();
|
bool did_exist = try_open();
|
||||||
|
if (!GetHeader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!did_exist) {
|
if (!did_exist) {
|
||||||
GetHeader()->version = CURRENT_INFOCACHE_VERSION;
|
GetHeader()->version = CURRENT_INFOCACHE_VERSION;
|
||||||
|
|
||||||
|
|
|
@ -309,7 +309,7 @@ void DebugWindow::DrawToolbar() {
|
||||||
case cpu::ThreadDebugInfo::State::kAlive:
|
case cpu::ThreadDebugInfo::State::kAlive:
|
||||||
case cpu::ThreadDebugInfo::State::kExited:
|
case cpu::ThreadDebugInfo::State::kExited:
|
||||||
case cpu::ThreadDebugInfo::State::kWaiting:
|
case cpu::ThreadDebugInfo::State::kWaiting:
|
||||||
if (thread_info->thread_handle == NULL || thread_info->thread == NULL) {
|
if (!thread_info->thread_handle || thread_info->thread == nullptr) {
|
||||||
thread_combo.Append("(invalid)");
|
thread_combo.Append("(invalid)");
|
||||||
} else {
|
} else {
|
||||||
thread_combo.Append(thread_info->thread->thread_name());
|
thread_combo.Append(thread_info->thread->thread_name());
|
||||||
|
|
|
@ -451,7 +451,7 @@ Emulator::FileSignatureType Emulator::GetFileSignature(
|
||||||
}
|
}
|
||||||
|
|
||||||
char file_magic[header_size];
|
char file_magic[header_size];
|
||||||
fread_s(file_magic, sizeof(file_magic), 1, header_size, file);
|
fread(file_magic, sizeof(file_magic), 1, file);
|
||||||
|
|
||||||
fourcc_t magic_value =
|
fourcc_t magic_value =
|
||||||
make_fourcc(file_magic[0], file_magic[1], file_magic[2], file_magic[3]);
|
make_fourcc(file_magic[0], file_magic[1], file_magic[2], file_magic[3]);
|
||||||
|
@ -485,7 +485,7 @@ Emulator::FileSignatureType Emulator::GetFileSignature(
|
||||||
|
|
||||||
file = xe::filesystem::OpenFile(path, "rb");
|
file = xe::filesystem::OpenFile(path, "rb");
|
||||||
xe::filesystem::Seek(file, -header_size, SEEK_END);
|
xe::filesystem::Seek(file, -header_size, SEEK_END);
|
||||||
fread_s(file_magic, sizeof(file_magic), 1, header_size, file);
|
fread(file_magic, 1, header_size, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
magic_value =
|
magic_value =
|
||||||
|
@ -505,6 +505,7 @@ Emulator::FileSignatureType Emulator::GetFileSignature(
|
||||||
return FileSignatureType::XISO;
|
return FileSignatureType::XISO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XELOGE("{}: {} ({:08X})", __func__, path.extension(), magic_value);
|
||||||
return FileSignatureType::Unknown;
|
return FileSignatureType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +719,7 @@ X_STATUS Emulator::DataMigration(const uint64_t xuid) {
|
||||||
const auto old_profile_data =
|
const auto old_profile_data =
|
||||||
xe::filesystem::ListDirectories(title.path / title.name / "profile");
|
xe::filesystem::ListDirectories(title.path / title.name / "profile");
|
||||||
|
|
||||||
xe::filesystem::FileInfo& entry_to_copy = xe::filesystem::FileInfo();
|
xe::filesystem::FileInfo entry_to_copy = xe::filesystem::FileInfo();
|
||||||
if (old_profile_data.size() != 1) {
|
if (old_profile_data.size() != 1) {
|
||||||
for (const auto& entry : old_profile_data) {
|
for (const auto& entry : old_profile_data) {
|
||||||
if (entry.name == "User") {
|
if (entry.name == "User") {
|
||||||
|
@ -835,8 +836,7 @@ X_STATUS Emulator::InstallContentPackage(
|
||||||
return error_code;
|
return error_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDLiveContentInstalled,
|
kernel_state()->BroadcastNotification(kXNotificationLiveContentInstalled, 0);
|
||||||
0);
|
|
||||||
|
|
||||||
return error_code;
|
return error_code;
|
||||||
}
|
}
|
||||||
|
@ -938,8 +938,7 @@ X_STATUS Emulator::CreateZarchivePackage(
|
||||||
uint64_t total_bytes_read = 0;
|
uint64_t total_bytes_read = 0;
|
||||||
|
|
||||||
while (total_bytes_read < file_size) {
|
while (total_bytes_read < file_size) {
|
||||||
uint64_t bytes_read =
|
uint64_t bytes_read = fread(buffer.data(), 1, buffer.size(), file);
|
||||||
fread_s(buffer.data(), buffer.size(), 1, buffer.size(), file);
|
|
||||||
|
|
||||||
total_bytes_read += bytes_read;
|
total_bytes_read += bytes_read;
|
||||||
|
|
||||||
|
@ -1373,6 +1372,12 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
|
||||||
return X_STATUS_NOT_FOUND;
|
return X_STATUS_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!module->is_executable()) {
|
||||||
|
kernel_state_->UnloadUserModule(module, false);
|
||||||
|
XELOGE("Failed to load user module {}", path);
|
||||||
|
return X_STATUS_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
X_RESULT result = kernel_state_->ApplyTitleUpdate(module);
|
X_RESULT result = kernel_state_->ApplyTitleUpdate(module);
|
||||||
if (XFAILED(result)) {
|
if (XFAILED(result)) {
|
||||||
XELOGE("Failed to apply title update! Cannot run module {}", path);
|
XELOGE("Failed to apply title update! Cannot run module {}", path);
|
||||||
|
|
|
@ -245,9 +245,8 @@ class Emulator {
|
||||||
ContentInstallationInfo& installation_info);
|
ContentInstallationInfo& installation_info);
|
||||||
|
|
||||||
// Extract content of zar package to desired directory.
|
// Extract content of zar package to desired directory.
|
||||||
X_STATUS Emulator::ExtractZarchivePackage(
|
X_STATUS ExtractZarchivePackage(const std::filesystem::path& path,
|
||||||
const std::filesystem::path& path,
|
const std::filesystem::path& extract_dir);
|
||||||
const std::filesystem::path& extract_dir);
|
|
||||||
|
|
||||||
// Pack contents of a folder into a zar package.
|
// Pack contents of a folder into a zar package.
|
||||||
X_STATUS CreateZarchivePackage(const std::filesystem::path& inputDirectory,
|
X_STATUS CreateZarchivePackage(const std::filesystem::path& inputDirectory,
|
||||||
|
|
|
@ -580,10 +580,17 @@ static inline void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
||||||
__m128i pa_sc_scissor = _mm_setr_epi32(
|
__m128i pa_sc_scissor = _mm_setr_epi32(
|
||||||
pa_sc_screen_scissor_tl_tl_x, pa_sc_screen_scissor_tl_tl_y,
|
pa_sc_screen_scissor_tl_tl_x, pa_sc_screen_scissor_tl_tl_y,
|
||||||
pa_sc_screen_scissor_br_br_x, pa_sc_screen_scissor_br_br_y);
|
pa_sc_screen_scissor_br_br_x, pa_sc_screen_scissor_br_br_y);
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
__m128i xyoffsetadd = _mm_cvtsi64x_si128(
|
__m128i xyoffsetadd = _mm_cvtsi64x_si128(
|
||||||
static_cast<unsigned long long>(pa_sc_window_offset_window_x_offset) |
|
static_cast<unsigned long long>(pa_sc_window_offset_window_x_offset) |
|
||||||
(static_cast<unsigned long long>(pa_sc_window_offset_window_y_offset)
|
(static_cast<unsigned long long>(pa_sc_window_offset_window_y_offset)
|
||||||
<< 32));
|
<< 32));
|
||||||
|
#else
|
||||||
|
__m128i xyoffsetadd = _mm_cvtsi64_si128(
|
||||||
|
static_cast<unsigned long long>(pa_sc_window_offset_window_x_offset) |
|
||||||
|
(static_cast<unsigned long long>(pa_sc_window_offset_window_y_offset)
|
||||||
|
<< 32));
|
||||||
|
#endif
|
||||||
xyoffsetadd = _mm_unpacklo_epi64(xyoffsetadd, xyoffsetadd);
|
xyoffsetadd = _mm_unpacklo_epi64(xyoffsetadd, xyoffsetadd);
|
||||||
// chrispy: put this here to make it clear that the shift by 31 is extracting
|
// chrispy: put this here to make it clear that the shift by 31 is extracting
|
||||||
// this field
|
// this field
|
||||||
|
|
|
@ -100,7 +100,7 @@ constexpr bool IsPrimitivePolygonal(bool vgt_output_path_is_tessellation_enable,
|
||||||
return (primitive_polygonal_table & (1U << static_cast<uint32_t>(type))) != 0;
|
return (primitive_polygonal_table & (1U << static_cast<uint32_t>(type))) != 0;
|
||||||
}
|
}
|
||||||
XE_FORCEINLINE
|
XE_FORCEINLINE
|
||||||
bool IsPrimitivePolygonal(const RegisterFile& regs) {
|
static bool IsPrimitivePolygonal(const RegisterFile& regs) {
|
||||||
return IsPrimitivePolygonal(
|
return IsPrimitivePolygonal(
|
||||||
regs.Get<reg::VGT_OUTPUT_PATH_CNTL>().path_select ==
|
regs.Get<reg::VGT_OUTPUT_PATH_CNTL>().path_select ==
|
||||||
xenos::VGTOutputPath::kTessellationEnable,
|
xenos::VGTOutputPath::kTessellationEnable,
|
||||||
|
@ -383,7 +383,7 @@ struct GetViewportInfoArgs {
|
||||||
depth_format = regs.Get<reg::RB_DEPTH_INFO>().depth_format;
|
depth_format = regs.Get<reg::RB_DEPTH_INFO>().depth_format;
|
||||||
}
|
}
|
||||||
XE_FORCEINLINE
|
XE_FORCEINLINE
|
||||||
bool operator==(const GetViewportInfoArgs& prev) {
|
bool operator==(const GetViewportInfoArgs& prev) const {
|
||||||
#if XE_ARCH_AMD64 == 0
|
#if XE_ARCH_AMD64 == 0
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ DEFINE_uint32(internal_display_resolution, 8,
|
||||||
"Allow games that support different resolutions to render "
|
"Allow games that support different resolutions to render "
|
||||||
"in a specific resolution.\n"
|
"in a specific resolution.\n"
|
||||||
"This is not guaranteed to work with all games or improve "
|
"This is not guaranteed to work with all games or improve "
|
||||||
"performance."
|
"performance.\n"
|
||||||
" 0=640x480\n"
|
" 0=640x480\n"
|
||||||
" 1=640x576\n"
|
" 1=640x576\n"
|
||||||
" 2=720x480\n"
|
" 2=720x480\n"
|
||||||
|
|
|
@ -482,7 +482,7 @@ void SharedMemory::TryFindUploadRange(const uint32_t& block_first,
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool UploadRange_DoBestScanForward(uint64_t v, uint32_t* out) {
|
static bool UploadRange_DoBestScanForward(uint64_t v, uint32_t* out) {
|
||||||
#if XE_ARCH_AMD64 == 1
|
#if XE_ARCH_AMD64 == 1 && XE_PLATFORM_WIN32
|
||||||
if (!v) {
|
if (!v) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,9 +94,9 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length) {
|
||||||
|
|
||||||
static int dump_counter = 0;
|
static int dump_counter = 0;
|
||||||
std::filesystem::path path = "texture_dumps";
|
std::filesystem::path path = "texture_dumps";
|
||||||
path /= fmt::format("{:05d}_{:08X}_{:08X}_{:08X}.dds", dump_counter++,
|
path /= fmt::format(fmt::runtime("{:05d}_{:08X}_{:08X}_{:08X}.dds"),
|
||||||
src.memory.base_address, src.memory.mip_address,
|
dump_counter++, src.memory.base_address,
|
||||||
src.format_name());
|
src.memory.mip_address, src.format_name());
|
||||||
|
|
||||||
FILE* handle = filesystem::OpenFile(path, "wb");
|
FILE* handle = filesystem::OpenFile(path, "wb");
|
||||||
if (handle) {
|
if (handle) {
|
||||||
|
|
|
@ -89,7 +89,7 @@ void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode,
|
TracePlaybackMode playback_mode,
|
||||||
bool clear_caches) {
|
bool clear_caches) {
|
||||||
playing_trace_ = true;
|
playing_trace_ = true;
|
||||||
graphics_system_->command_processor()->CallInThread([=]() {
|
graphics_system_->command_processor()->CallInThread([=, this]() {
|
||||||
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
|
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ TraceViewer::TraceViewer(xe::ui::WindowedAppContext& app_context,
|
||||||
TraceViewer::~TraceViewer() = default;
|
TraceViewer::~TraceViewer() = default;
|
||||||
|
|
||||||
bool TraceViewer::OnInitialize() {
|
bool TraceViewer::OnInitialize() {
|
||||||
std::string path = cvars::target_trace_file.u8string();
|
std::string path = cvars::target_trace_file.string();
|
||||||
|
|
||||||
// If no path passed, ask the user.
|
// If no path passed, ask the user.
|
||||||
// On Android, however, there's no synchronous file picker, and the trace file
|
// On Android, however, there's no synchronous file picker, and the trace file
|
||||||
|
@ -899,9 +899,11 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
} break;
|
} break;
|
||||||
case xenos::VertexFormat::k_16_16_FLOAT: {
|
case xenos::VertexFormat::k_16_16_FLOAT: {
|
||||||
auto e0 = LOADEL(uint32_t, 0);
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
ImGui::Text("%.2f", half_float::detail::uint16((e0 >> 16) & 0xFFFF));
|
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||||
|
(e0 >> 16) & 0xFFFF)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", half_float::detail::uint16((e0 >> 0) & 0xFFFF));
|
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||||
|
(e0 >> 0) & 0xFFFF)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
} break;
|
} break;
|
||||||
case xenos::VertexFormat::k_32_32:
|
case xenos::VertexFormat::k_32_32:
|
||||||
|
@ -972,13 +974,17 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
case xenos::VertexFormat::k_16_16_16_16_FLOAT: {
|
case xenos::VertexFormat::k_16_16_16_16_FLOAT: {
|
||||||
auto e0 = LOADEL(uint32_t, 0);
|
auto e0 = LOADEL(uint32_t, 0);
|
||||||
auto e1 = LOADEL(uint32_t, 1);
|
auto e1 = LOADEL(uint32_t, 1);
|
||||||
ImGui::Text("%.2f", half_float::detail::uint16((e0 >> 16) & 0xFFFF));
|
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||||
|
(e0 >> 16) & 0xFFFF)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", half_float::detail::uint16((e0 >> 0) & 0xFFFF));
|
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||||
|
(e0 >> 0) & 0xFFFF)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", half_float::detail::uint16((e1 >> 16) & 0xFFFF));
|
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||||
|
(e1 >> 16) & 0xFFFF)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("%.2f", half_float::detail::uint16((e1 >> 0) & 0xFFFF));
|
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||||
|
(e1 >> 0) & 0xFFFF)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
} break;
|
} break;
|
||||||
case xenos::VertexFormat::k_32_32_32_32_FLOAT:
|
case xenos::VertexFormat::k_32_32_32_32_FLOAT:
|
||||||
|
|
|
@ -26,6 +26,22 @@ enum X_INPUT_CAPS {
|
||||||
|
|
||||||
enum X_INPUT_FLAG {
|
enum X_INPUT_FLAG {
|
||||||
X_INPUT_FLAG_GAMEPAD = 0x00000001,
|
X_INPUT_FLAG_GAMEPAD = 0x00000001,
|
||||||
|
X_INPUT_FLAG_KEYBOARD = 0x00000002,
|
||||||
|
X_INPUT_FLAG_UNKNOWN = 0x00000004,
|
||||||
|
X_INPUT_FLAG_UNKNOWN2 = 0x00000008,
|
||||||
|
X_INPUT_FLAG_MIC = 0x00000020,
|
||||||
|
X_INPUT_FLAG_ANYDEVICE = 0x000000FF,
|
||||||
|
X_INPUT_FLAG_ANY_USER = 1 << 30
|
||||||
|
};
|
||||||
|
|
||||||
|
enum X_INPUT_DEVTYPE {
|
||||||
|
XINPUT_DEVTYPE_GAMEPAD = 0x00000001,
|
||||||
|
XINPUT_DEVTYPE_KEYBOARD = 0x00000002,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum X_INPUT_DEVSUBTYPE {
|
||||||
|
XINPUT_DEVSUBTYPE_USB_KEYBOARD = 0x00000000,
|
||||||
|
XINPUT_DEVSUBTYPE_GAMEPAD = 0x00000001,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum X_INPUT_GAMEPAD_BUTTON {
|
enum X_INPUT_GAMEPAD_BUTTON {
|
||||||
|
|
|
@ -28,6 +28,8 @@ namespace hid {
|
||||||
|
|
||||||
class InputSystem;
|
class InputSystem;
|
||||||
|
|
||||||
|
enum InputType { None, Controller = 1, Keyboard = 2, Other = 4 };
|
||||||
|
|
||||||
class InputDriver {
|
class InputDriver {
|
||||||
public:
|
public:
|
||||||
virtual ~InputDriver() = default;
|
virtual ~InputDriver() = default;
|
||||||
|
@ -42,6 +44,8 @@ class InputDriver {
|
||||||
virtual X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
virtual X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) = 0;
|
X_INPUT_KEYSTROKE* out_keystroke) = 0;
|
||||||
|
|
||||||
|
virtual InputType GetInputType() const = 0;
|
||||||
|
|
||||||
void set_is_active_callback(std::function<bool()> is_active_callback) {
|
void set_is_active_callback(std::function<bool()> is_active_callback) {
|
||||||
is_active_callback_ = is_active_callback;
|
is_active_callback_ = is_active_callback;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,13 @@ void InputSystem::AddDriver(std::unique_ptr<InputDriver> driver) {
|
||||||
void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot,
|
void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot,
|
||||||
bool connected) {
|
bool connected) {
|
||||||
if (slot == XUserIndexAny) {
|
if (slot == XUserIndexAny) {
|
||||||
slot = 0;
|
XELOGW("{} received requrest for slot any! Unsupported", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not report passthrough as a controller.
|
||||||
|
if (driver && driver->GetInputType() == InputType::Keyboard) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connected_slots.test(slot) == connected) {
|
if (connected_slots.test(slot) == connected) {
|
||||||
|
@ -53,7 +59,7 @@ void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot,
|
||||||
connected_slots.flip(slot);
|
connected_slots.flip(slot);
|
||||||
if (kernel::kernel_state()) {
|
if (kernel::kernel_state()) {
|
||||||
kernel::kernel_state()->BroadcastNotification(
|
kernel::kernel_state()->BroadcastNotification(
|
||||||
kXNotificationIDSystemInputDevicesChanged, 0);
|
kXNotificationSystemInputDevicesChanged, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver) {
|
if (driver) {
|
||||||
|
@ -69,36 +75,45 @@ void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<InputDriver*> InputSystem::FilterDrivers(uint32_t flags) {
|
||||||
|
std::vector<InputDriver*> filtered_drivers;
|
||||||
|
for (auto& driver : drivers_) {
|
||||||
|
if (driver->GetInputType() == InputType::None) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & driver->GetInputType()) != 0) {
|
||||||
|
filtered_drivers.push_back(driver.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered_drivers;
|
||||||
|
}
|
||||||
|
|
||||||
X_RESULT InputSystem::GetCapabilities(uint32_t user_index, uint32_t flags,
|
X_RESULT InputSystem::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_CAPABILITIES* out_caps) {
|
X_INPUT_CAPABILITIES* out_caps) {
|
||||||
SCOPE_profile_cpu_f("hid");
|
SCOPE_profile_cpu_f("hid");
|
||||||
|
|
||||||
bool any_connected = false;
|
std::vector<InputDriver*> filtered_drivers = FilterDrivers(flags);
|
||||||
for (auto& driver : drivers_) {
|
|
||||||
|
for (auto& driver : filtered_drivers) {
|
||||||
X_RESULT result = driver->GetCapabilities(user_index, flags, out_caps);
|
X_RESULT result = driver->GetCapabilities(user_index, flags, out_caps);
|
||||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
|
||||||
any_connected = true;
|
|
||||||
}
|
|
||||||
if (result == X_ERROR_SUCCESS) {
|
if (result == X_ERROR_SUCCESS) {
|
||||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT InputSystem::GetState(uint32_t user_index, X_INPUT_STATE* out_state) {
|
X_RESULT InputSystem::GetState(uint32_t user_index, uint32_t flags,
|
||||||
|
X_INPUT_STATE* out_state) {
|
||||||
SCOPE_profile_cpu_f("hid");
|
SCOPE_profile_cpu_f("hid");
|
||||||
|
|
||||||
bool any_connected = false;
|
std::vector<InputDriver*> filtered_drivers = FilterDrivers(flags);
|
||||||
for (auto& driver : drivers_) {
|
|
||||||
|
for (auto& driver : filtered_drivers) {
|
||||||
X_RESULT result = driver->GetState(user_index, out_state);
|
X_RESULT result = driver->GetState(user_index, out_state);
|
||||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
|
||||||
any_connected = true;
|
|
||||||
}
|
|
||||||
if (result == X_ERROR_SUCCESS) {
|
if (result == X_ERROR_SUCCESS) {
|
||||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
UpdateUsedSlot(driver, user_index, true);
|
||||||
AdjustDeadzoneLevels(user_index, &out_state->gamepad);
|
AdjustDeadzoneLevels(user_index, &out_state->gamepad);
|
||||||
|
|
||||||
if (out_state->gamepad.buttons != 0) {
|
if (out_state->gamepad.buttons != 0) {
|
||||||
|
@ -107,49 +122,49 @@ X_RESULT InputSystem::GetState(uint32_t user_index, X_INPUT_STATE* out_state) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
UpdateUsedSlot(nullptr, user_index, false);
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT InputSystem::SetState(uint32_t user_index,
|
X_RESULT InputSystem::SetState(uint32_t user_index,
|
||||||
X_INPUT_VIBRATION* vibration) {
|
X_INPUT_VIBRATION* vibration) {
|
||||||
SCOPE_profile_cpu_f("hid");
|
SCOPE_profile_cpu_f("hid");
|
||||||
X_INPUT_VIBRATION modified_vibration = ModifyVibrationLevel(vibration);
|
X_INPUT_VIBRATION modified_vibration = ModifyVibrationLevel(vibration);
|
||||||
bool any_connected = false;
|
|
||||||
for (auto& driver : drivers_) {
|
for (auto& driver : drivers_) {
|
||||||
X_RESULT result = driver->SetState(user_index, &modified_vibration);
|
X_RESULT result = driver->SetState(user_index, &modified_vibration);
|
||||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
|
||||||
any_connected = true;
|
|
||||||
}
|
|
||||||
if (result == X_ERROR_SUCCESS) {
|
if (result == X_ERROR_SUCCESS) {
|
||||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) {
|
X_INPUT_KEYSTROKE* out_keystroke) {
|
||||||
SCOPE_profile_cpu_f("hid");
|
SCOPE_profile_cpu_f("hid");
|
||||||
|
|
||||||
bool any_connected = false;
|
std::vector<InputDriver*> filtered_drivers = FilterDrivers(flags);
|
||||||
for (auto& driver : drivers_) {
|
|
||||||
X_RESULT result = driver->GetKeystroke(user_index, flags, out_keystroke);
|
|
||||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
|
||||||
any_connected = true;
|
|
||||||
}
|
|
||||||
if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) {
|
|
||||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
|
||||||
|
|
||||||
if (result == X_ERROR_SUCCESS) {
|
bool any_connected = false;
|
||||||
last_used_slot = user_index;
|
for (auto& driver : filtered_drivers) {
|
||||||
}
|
// connected_slots
|
||||||
|
X_RESULT result = driver->GetKeystroke(user_index, flags, out_keystroke);
|
||||||
|
if (result == X_ERROR_INVALID_PARAMETER ||
|
||||||
|
result == X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
any_connected = true;
|
||||||
|
|
||||||
|
if (result == X_ERROR_SUCCESS) {
|
||||||
|
last_used_slot = user_index;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result == X_ERROR_EMPTY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,13 +40,13 @@ class InputSystem {
|
||||||
|
|
||||||
X_RESULT GetCapabilities(uint32_t user_index, uint32_t flags,
|
X_RESULT GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_CAPABILITIES* out_caps);
|
X_INPUT_CAPABILITIES* out_caps);
|
||||||
X_RESULT GetState(uint32_t user_index, X_INPUT_STATE* out_state);
|
X_RESULT GetState(uint32_t user_index, uint32_t flags,
|
||||||
|
X_INPUT_STATE* out_state);
|
||||||
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration);
|
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration);
|
||||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke);
|
X_INPUT_KEYSTROKE* out_keystroke);
|
||||||
|
|
||||||
bool GetVibrationCvar();
|
bool GetVibrationCvar();
|
||||||
|
|
||||||
void ToggleVibration();
|
void ToggleVibration();
|
||||||
|
|
||||||
const std::bitset<XUserMaxUserCount> GetConnectedSlots() const {
|
const std::bitset<XUserMaxUserCount> GetConnectedSlots() const {
|
||||||
|
@ -68,6 +68,8 @@ class InputSystem {
|
||||||
void AdjustDeadzoneLevels(const uint8_t slot, X_INPUT_GAMEPAD* gamepad);
|
void AdjustDeadzoneLevels(const uint8_t slot, X_INPUT_GAMEPAD* gamepad);
|
||||||
X_INPUT_VIBRATION ModifyVibrationLevel(X_INPUT_VIBRATION* vibration);
|
X_INPUT_VIBRATION ModifyVibrationLevel(X_INPUT_VIBRATION* vibration);
|
||||||
|
|
||||||
|
std::vector<InputDriver*> FilterDrivers(uint32_t flags);
|
||||||
|
|
||||||
xe::ui::Window* window_ = nullptr;
|
xe::ui::Window* window_ = nullptr;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<InputDriver>> drivers_;
|
std::vector<std::unique_ptr<InputDriver>> drivers_;
|
||||||
|
|
|
@ -45,6 +45,8 @@ X_RESULT NopInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputType NopInputDriver::GetInputType() const { return InputType::Other; }
|
||||||
|
|
||||||
} // namespace nop
|
} // namespace nop
|
||||||
} // namespace hid
|
} // namespace hid
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -29,6 +29,7 @@ class NopInputDriver final : public InputDriver {
|
||||||
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
||||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||||
|
virtual InputType GetInputType() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nop
|
} // namespace nop
|
||||||
|
|
|
@ -155,7 +155,7 @@ void SDLInputDriver::LoadGameControllerDB() {
|
||||||
std::string guid = row[0];
|
std::string guid = row[0];
|
||||||
std::string controller_name = row[1];
|
std::string controller_name = row[1];
|
||||||
|
|
||||||
auto format = [](std::string& ss, std::string& s) {
|
auto format = [](std::string ss, const std::string& s) {
|
||||||
return ss.empty() ? s : ss + "," + s;
|
return ss.empty() ? s : ss + "," + s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -429,6 +429,8 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags,
|
||||||
return X_ERROR_EMPTY;
|
return X_ERROR_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputType SDLInputDriver::GetInputType() const { return InputType::Controller; }
|
||||||
|
|
||||||
void SDLInputDriver::HandleEvent(const SDL_Event& event) {
|
void SDLInputDriver::HandleEvent(const SDL_Event& event) {
|
||||||
// This callback will likely run on the thread that posts the event, which
|
// This callback will likely run on the thread that posts the event, which
|
||||||
// may be a dedicated thread SDL has created for the joystick subsystem.
|
// may be a dedicated thread SDL has created for the joystick subsystem.
|
||||||
|
|
|
@ -44,6 +44,7 @@ class SDLInputDriver final : public InputDriver {
|
||||||
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
||||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||||
|
virtual InputType GetInputType() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ControllerState {
|
struct ControllerState {
|
||||||
|
|
|
@ -25,13 +25,36 @@
|
||||||
#include "winkey_binding_table.inc"
|
#include "winkey_binding_table.inc"
|
||||||
#undef XE_HID_WINKEY_BINDING
|
#undef XE_HID_WINKEY_BINDING
|
||||||
|
|
||||||
DEFINE_int32(keyboard_user_index, 0, "Controller port that keyboard emulates",
|
DEFINE_int32(keyboard_mode, 0,
|
||||||
"HID.WinKey");
|
"Allows user do specify keyboard working mode. Possible values: 0 "
|
||||||
|
"- Disabled, 1 - Enabled, 2 - Passthrough. Passthrough requires "
|
||||||
|
"controller being connected!",
|
||||||
|
"HID");
|
||||||
|
|
||||||
|
DEFINE_int32(
|
||||||
|
keyboard_user_index, 0,
|
||||||
|
"Controller port that keyboard emulates. [0, 3] - Keyboard is assigned to "
|
||||||
|
"selected slot. Passthrough does not require assigning slot.",
|
||||||
|
"HID");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace hid {
|
namespace hid {
|
||||||
namespace winkey {
|
namespace winkey {
|
||||||
|
|
||||||
|
bool static IsPassthroughEnabled() {
|
||||||
|
return static_cast<KeyboardMode>(cvars::keyboard_mode) ==
|
||||||
|
KeyboardMode::Passthrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool static IsKeyboardForUserEnabled(uint32_t user_index) {
|
||||||
|
if (static_cast<KeyboardMode>(cvars::keyboard_mode) !=
|
||||||
|
KeyboardMode::Enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cvars::keyboard_user_index == user_index;
|
||||||
|
}
|
||||||
|
|
||||||
bool __inline IsKeyToggled(uint8_t key) {
|
bool __inline IsKeyToggled(uint8_t key) {
|
||||||
return (GetKeyState(key) & 0x1) == 0x1;
|
return (GetKeyState(key) & 0x1) == 0x1;
|
||||||
}
|
}
|
||||||
|
@ -105,13 +128,18 @@ X_STATUS WinKeyInputDriver::Setup() { return X_STATUS_SUCCESS; }
|
||||||
|
|
||||||
X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
|
X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_CAPABILITIES* out_caps) {
|
X_INPUT_CAPABILITIES* out_caps) {
|
||||||
if (user_index != cvars::keyboard_user_index) {
|
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): confirm with a real XInput controller.
|
if (IsPassthroughEnabled()) {
|
||||||
out_caps->type = 0x01; // XINPUT_DEVTYPE_GAMEPAD
|
out_caps->type = X_INPUT_DEVTYPE::XINPUT_DEVTYPE_KEYBOARD;
|
||||||
out_caps->sub_type = 0x01; // XINPUT_DEVSUBTYPE_GAMEPAD
|
out_caps->sub_type = X_INPUT_DEVSUBTYPE::XINPUT_DEVSUBTYPE_USB_KEYBOARD;
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_caps->type = X_INPUT_DEVTYPE::XINPUT_DEVTYPE_GAMEPAD;
|
||||||
|
out_caps->sub_type = X_INPUT_DEVSUBTYPE::XINPUT_DEVSUBTYPE_GAMEPAD;
|
||||||
out_caps->flags = 0;
|
out_caps->flags = 0;
|
||||||
out_caps->gamepad.buttons = 0xFFFF;
|
out_caps->gamepad.buttons = 0xFFFF;
|
||||||
out_caps->gamepad.left_trigger = 0xFF;
|
out_caps->gamepad.left_trigger = 0xFF;
|
||||||
|
@ -127,7 +155,7 @@ X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
|
|
||||||
X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
|
X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
|
||||||
X_INPUT_STATE* out_state) {
|
X_INPUT_STATE* out_state) {
|
||||||
if (user_index != cvars::keyboard_user_index) {
|
if (!IsKeyboardForUserEnabled(user_index)) {
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,12 +265,16 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
|
||||||
out_state->gamepad.thumb_rx = thumb_rx;
|
out_state->gamepad.thumb_rx = thumb_rx;
|
||||||
out_state->gamepad.thumb_ry = thumb_ry;
|
out_state->gamepad.thumb_ry = thumb_ry;
|
||||||
|
|
||||||
|
if (IsPassthroughEnabled()) {
|
||||||
|
memset(out_state, 0, sizeof(out_state));
|
||||||
|
}
|
||||||
|
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
|
X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
|
||||||
X_INPUT_VIBRATION* vibration) {
|
X_INPUT_VIBRATION* vibration) {
|
||||||
if (user_index != cvars::keyboard_user_index) {
|
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,21 +283,13 @@ X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
|
||||||
|
|
||||||
X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) {
|
X_INPUT_KEYSTROKE* out_keystroke) {
|
||||||
if (user_index != cvars::keyboard_user_index) {
|
if (!is_active()) {
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_active()) {
|
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||||
return X_ERROR_EMPTY;
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT result = X_ERROR_EMPTY;
|
|
||||||
|
|
||||||
ui::VirtualKey xinput_virtual_key = ui::VirtualKey::kNone;
|
|
||||||
uint16_t unicode = 0;
|
|
||||||
uint16_t keystroke_flags = 0;
|
|
||||||
uint8_t hid_code = 0;
|
|
||||||
|
|
||||||
// Pop from the queue.
|
// Pop from the queue.
|
||||||
KeyEvent evt;
|
KeyEvent evt;
|
||||||
{
|
{
|
||||||
|
@ -278,24 +302,60 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
key_events_.pop();
|
key_events_.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X_RESULT result = X_ERROR_EMPTY;
|
||||||
|
|
||||||
|
ui::VirtualKey xinput_virtual_key = ui::VirtualKey::kNone;
|
||||||
|
uint16_t unicode = 0;
|
||||||
|
uint16_t keystroke_flags = 0;
|
||||||
|
uint8_t hid_code = 0;
|
||||||
|
|
||||||
bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT);
|
bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT);
|
||||||
for (const KeyBinding& b : key_bindings_) {
|
|
||||||
if (b.input_key == evt.virtual_key &&
|
if (!IsPassthroughEnabled()) {
|
||||||
((b.lowercase == b.uppercase) || (b.lowercase && !capital) ||
|
if (IsKeyboardForUserEnabled(user_index)) {
|
||||||
(b.uppercase && capital))) {
|
for (const KeyBinding& b : key_bindings_) {
|
||||||
xinput_virtual_key = b.output_key;
|
if (b.input_key == evt.virtual_key &&
|
||||||
|
((b.lowercase == b.uppercase) || (b.lowercase && !capital) ||
|
||||||
|
(b.uppercase && capital))) {
|
||||||
|
xinput_virtual_key = b.output_key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xinput_virtual_key = evt.virtual_key;
|
||||||
|
|
||||||
|
if (capital) {
|
||||||
|
keystroke_flags |= 0x0008; // XINPUT_KEYSTROKE_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyToggled(VK_CONTROL)) {
|
||||||
|
keystroke_flags |= 0x0010; // XINPUT_KEYSTROKE_CTRL
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyToggled(VK_MENU)) {
|
||||||
|
keystroke_flags |= 0x0020; // XINPUT_KEYSTROKE_ALT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xinput_virtual_key != ui::VirtualKey::kNone) {
|
if (xinput_virtual_key != ui::VirtualKey::kNone) {
|
||||||
if (evt.transition == true) {
|
if (evt.transition == true) {
|
||||||
keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN
|
keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN
|
||||||
|
if (evt.prev_state == evt.transition) {
|
||||||
|
keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT
|
||||||
|
}
|
||||||
} else if (evt.transition == false) {
|
} else if (evt.transition == false) {
|
||||||
keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP
|
keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.prev_state == evt.transition) {
|
if (IsPassthroughEnabled()) {
|
||||||
keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT
|
if (GetKeyboardState(key_map_)) {
|
||||||
|
WCHAR buf;
|
||||||
|
if (ToUnicode(uint8_t(xinput_virtual_key), 0, key_map_, &buf, 1, 0) ==
|
||||||
|
1) {
|
||||||
|
keystroke_flags |= 0x1000; // XINPUT_KEYSTROKE_VALIDUNICODE
|
||||||
|
unicode = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = X_ERROR_SUCCESS;
|
result = X_ERROR_SUCCESS;
|
||||||
|
@ -304,7 +364,7 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
out_keystroke->virtual_key = uint16_t(xinput_virtual_key);
|
out_keystroke->virtual_key = uint16_t(xinput_virtual_key);
|
||||||
out_keystroke->unicode = unicode;
|
out_keystroke->unicode = unicode;
|
||||||
out_keystroke->flags = keystroke_flags;
|
out_keystroke->flags = keystroke_flags;
|
||||||
out_keystroke->user_index = 0;
|
out_keystroke->user_index = user_index;
|
||||||
out_keystroke->hid_code = hid_code;
|
out_keystroke->hid_code = hid_code;
|
||||||
|
|
||||||
// X_ERROR_EMPTY if no new keys
|
// X_ERROR_EMPTY if no new keys
|
||||||
|
@ -322,7 +382,8 @@ void WinKeyInputDriver::WinKeyWindowInputListener::OnKeyUp(ui::KeyEvent& e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) {
|
void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) {
|
||||||
if (!is_active()) {
|
if (!is_active() || static_cast<KeyboardMode>(cvars::keyboard_mode) ==
|
||||||
|
KeyboardMode::Disabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +397,20 @@ void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) {
|
||||||
key_events_.push(key);
|
key_events_.push(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputType WinKeyInputDriver::GetInputType() const {
|
||||||
|
switch (static_cast<KeyboardMode>(cvars::keyboard_mode)) {
|
||||||
|
case KeyboardMode::Disabled:
|
||||||
|
return InputType::None;
|
||||||
|
case KeyboardMode::Enabled:
|
||||||
|
return InputType::Controller;
|
||||||
|
case KeyboardMode::Passthrough:
|
||||||
|
return InputType::Keyboard;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return InputType::Controller;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace winkey
|
} // namespace winkey
|
||||||
} // namespace hid
|
} // namespace hid
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace xe {
|
||||||
namespace hid {
|
namespace hid {
|
||||||
namespace winkey {
|
namespace winkey {
|
||||||
|
|
||||||
|
enum class KeyboardMode { Disabled, Enabled, Passthrough };
|
||||||
|
|
||||||
class WinKeyInputDriver final : public InputDriver {
|
class WinKeyInputDriver final : public InputDriver {
|
||||||
public:
|
public:
|
||||||
explicit WinKeyInputDriver(xe::ui::Window* window, size_t window_z_order);
|
explicit WinKeyInputDriver(xe::ui::Window* window, size_t window_z_order);
|
||||||
|
@ -33,6 +35,7 @@ class WinKeyInputDriver final : public InputDriver {
|
||||||
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
||||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||||
|
virtual InputType GetInputType() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct KeyEvent {
|
struct KeyEvent {
|
||||||
|
@ -72,7 +75,7 @@ class WinKeyInputDriver final : public InputDriver {
|
||||||
xe::global_critical_region global_critical_region_;
|
xe::global_critical_region global_critical_region_;
|
||||||
std::queue<KeyEvent> key_events_;
|
std::queue<KeyEvent> key_events_;
|
||||||
std::vector<KeyBinding> key_bindings_;
|
std::vector<KeyBinding> key_bindings_;
|
||||||
|
uint8_t key_map_[256];
|
||||||
uint32_t packet_number_ = 1;
|
uint32_t packet_number_ = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,9 @@ X_RESULT XInputInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
}
|
}
|
||||||
XINPUT_CAPABILITIES native_caps;
|
XINPUT_CAPABILITIES native_caps;
|
||||||
auto xigc = (decltype(&XInputGetCapabilities))XInputGetCapabilities_;
|
auto xigc = (decltype(&XInputGetCapabilities))XInputGetCapabilities_;
|
||||||
DWORD result = xigc(user_index, flags, &native_caps);
|
DWORD result =
|
||||||
|
xigc(user_index, flags & ~X_INPUT_DEVTYPE::XINPUT_DEVTYPE_KEYBOARD,
|
||||||
|
&native_caps);
|
||||||
if (result) {
|
if (result) {
|
||||||
if (result == ERROR_DEVICE_NOT_CONNECTED) {
|
if (result == ERROR_DEVICE_NOT_CONNECTED) {
|
||||||
set_skip(user_index);
|
set_skip(user_index);
|
||||||
|
@ -239,6 +241,10 @@ X_RESULT XInputInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputType XInputInputDriver::GetInputType() const {
|
||||||
|
return InputType::Controller;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xinput
|
} // namespace xinput
|
||||||
} // namespace hid
|
} // namespace hid
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -29,6 +29,7 @@ class XInputInputDriver final : public InputDriver {
|
||||||
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
||||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||||
|
virtual InputType GetInputType() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void* module_;
|
void* module_;
|
||||||
|
|
|
@ -867,11 +867,11 @@ void KernelState::RegisterNotifyListener(XNotifyListener* listener) {
|
||||||
if (!has_notified_startup_ && listener->mask() & kXNotifySystem) {
|
if (!has_notified_startup_ && listener->mask() & kXNotifySystem) {
|
||||||
has_notified_startup_ = true;
|
has_notified_startup_ = true;
|
||||||
// XN_SYS_UI (on, off)
|
// XN_SYS_UI (on, off)
|
||||||
listener->EnqueueNotification(kXNotificationIDSystemUI, 1);
|
listener->EnqueueNotification(kXNotificationSystemUI, 1);
|
||||||
listener->EnqueueNotification(kXNotificationIDSystemUI, 0);
|
listener->EnqueueNotification(kXNotificationSystemUI, 0);
|
||||||
// XN_SYS_SIGNINCHANGED x2
|
// XN_SYS_SIGNINCHANGED x2
|
||||||
listener->EnqueueNotification(kXNotificationIDSystemSignInChanged, 1);
|
listener->EnqueueNotification(kXNotificationSystemSignInChanged, 1);
|
||||||
listener->EnqueueNotification(kXNotificationIDSystemSignInChanged, 1);
|
listener->EnqueueNotification(kXNotificationSystemSignInChanged, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ class NativeList {
|
||||||
};
|
};
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
static X_LIST_ENTRY* XeHostList(uint32_t ptr, VirtualTranslator context) {
|
static X_LIST_ENTRY* XeHostList(uint32_t ptr, VirtualTranslator context) {
|
||||||
return context->TranslateVirtual<X_LIST_ENTRY*>(ptr);
|
return context->template TranslateVirtual<X_LIST_ENTRY*>(ptr);
|
||||||
}
|
}
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
static uint32_t XeGuestList(X_LIST_ENTRY* ptr, VirtualTranslator context) {
|
static uint32_t XeGuestList(X_LIST_ENTRY* ptr, VirtualTranslator context) {
|
||||||
|
@ -193,7 +193,8 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
||||||
|
|
||||||
inline ForwardIterator& operator++() {
|
inline ForwardIterator& operator++() {
|
||||||
current_entry =
|
current_entry =
|
||||||
vt->TranslateVirtual<X_LIST_ENTRY*>(current_entry)->flink_ptr;
|
vt->template TranslateVirtual<X_LIST_ENTRY*>(current_entry)
|
||||||
|
->flink_ptr;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
inline bool operator!=(uint32_t other_ptr) const {
|
inline bool operator!=(uint32_t other_ptr) const {
|
||||||
|
@ -202,7 +203,7 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
||||||
|
|
||||||
inline TObject& operator*() {
|
inline TObject& operator*() {
|
||||||
return *ListEntryObject(
|
return *ListEntryObject(
|
||||||
vt->TranslateVirtual<X_LIST_ENTRY*>(current_entry));
|
vt->template TranslateVirtual<X_LIST_ENTRY*>(current_entry));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
|
@ -236,15 +237,17 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
||||||
}
|
}
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
bool empty(VirtualTranslator vt) const {
|
bool empty(VirtualTranslator vt) const {
|
||||||
return vt->TranslateVirtual<X_LIST_ENTRY*>(flink_ptr) == this;
|
return vt->template TranslateVirtual<X_LIST_ENTRY*>(flink_ptr) == this;
|
||||||
}
|
}
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
TObject* HeadObject(VirtualTranslator vt) {
|
TObject* HeadObject(VirtualTranslator vt) {
|
||||||
return ListEntryObject(vt->TranslateVirtual<X_LIST_ENTRY*>(flink_ptr));
|
return ListEntryObject(
|
||||||
|
vt->template TranslateVirtual<X_LIST_ENTRY*>(flink_ptr));
|
||||||
}
|
}
|
||||||
template <typename VirtualTranslator>
|
template <typename VirtualTranslator>
|
||||||
TObject* TailObject(VirtualTranslator vt) {
|
TObject* TailObject(VirtualTranslator vt) {
|
||||||
return ListEntryObject(vt->TranslateVirtual<X_LIST_ENTRY*>(blink_ptr));
|
return ListEntryObject(
|
||||||
|
vt->template TranslateVirtual<X_LIST_ENTRY*>(blink_ptr));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
|
@ -77,4 +77,4 @@ class AttributeStringFormatter {
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
#endif XENIA_KERNEL_UTIL_PRESENCE_STRING_BUILDER_H_
|
#endif // XENIA_KERNEL_UTIL_PRESENCE_STRING_BUILDER_H_
|
|
@ -93,6 +93,12 @@ inline std::string_view TranslateAnsiString(const Memory* memory,
|
||||||
ansi_string->length);
|
ansi_string->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string TranslateAnsiPath(const Memory* memory,
|
||||||
|
const X_ANSI_STRING* ansi_string) {
|
||||||
|
return string_util::trim(
|
||||||
|
std::string(TranslateAnsiString(memory, ansi_string)));
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string_view TranslateAnsiStringAddress(const Memory* memory,
|
inline std::string_view TranslateAnsiStringAddress(const Memory* memory,
|
||||||
uint32_t guest_address) {
|
uint32_t guest_address) {
|
||||||
if (!guest_address) {
|
if (!guest_address) {
|
||||||
|
|
|
@ -43,8 +43,7 @@ void GpdAchievementBackend::EarnAchievement(const uint64_t xuid,
|
||||||
achievement->flags = achievement->flags |
|
achievement->flags = achievement->flags |
|
||||||
static_cast<uint32_t>(AchievementFlags::kAchieved) |
|
static_cast<uint32_t>(AchievementFlags::kAchieved) |
|
||||||
static_cast<uint32_t>(AchievementFlags::kAchievedOnline);
|
static_cast<uint32_t>(AchievementFlags::kAchievedOnline);
|
||||||
achievement->unlock_time.high_part = static_cast<uint32_t>(unlock_time >> 32);
|
achievement->unlock_time = unlock_time;
|
||||||
achievement->unlock_time.low_part = static_cast<uint32_t>(unlock_time);
|
|
||||||
|
|
||||||
SaveAchievementData(xuid, title_id, achievement_id);
|
SaveAchievementData(xuid, title_id, achievement_id);
|
||||||
}
|
}
|
||||||
|
@ -72,6 +71,10 @@ bool GpdAchievementBackend::IsAchievementUnlocked(
|
||||||
const auto achievement =
|
const auto achievement =
|
||||||
GetAchievementInfoInternal(xuid, title_id, achievement_id);
|
GetAchievementInfoInternal(xuid, title_id, achievement_id);
|
||||||
|
|
||||||
|
if (!achievement) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return (achievement->flags &
|
return (achievement->flags &
|
||||||
static_cast<uint32_t>(AchievementFlags::kAchieved)) != 0;
|
static_cast<uint32_t>(AchievementFlags::kAchieved)) != 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/base/chrono.h"
|
||||||
#include "xenia/kernel/util/xdbf_utils.h"
|
#include "xenia/kernel/util/xdbf_utils.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
|
@ -53,6 +54,32 @@ enum class AchievementFlags : uint32_t {
|
||||||
struct X_ACHIEVEMENT_UNLOCK_TIME {
|
struct X_ACHIEVEMENT_UNLOCK_TIME {
|
||||||
xe::be<uint32_t> high_part;
|
xe::be<uint32_t> high_part;
|
||||||
xe::be<uint32_t> low_part;
|
xe::be<uint32_t> low_part;
|
||||||
|
|
||||||
|
X_ACHIEVEMENT_UNLOCK_TIME() {
|
||||||
|
high_part = 0;
|
||||||
|
low_part = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_ACHIEVEMENT_UNLOCK_TIME(uint64_t filetime) {
|
||||||
|
high_part = static_cast<uint32_t>(filetime >> 32);
|
||||||
|
low_part = static_cast<uint32_t>(filetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
X_ACHIEVEMENT_UNLOCK_TIME(std::time_t time) {
|
||||||
|
const auto file_time =
|
||||||
|
chrono::WinSystemClock::to_file_time(chrono::WinSystemClock::from_sys(
|
||||||
|
std::chrono::system_clock::from_time_t(time)));
|
||||||
|
|
||||||
|
high_part = static_cast<uint32_t>(file_time >> 32);
|
||||||
|
low_part = static_cast<uint32_t>(file_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
chrono::WinSystemClock::time_point to_time_point() const {
|
||||||
|
const uint64_t filetime =
|
||||||
|
(static_cast<uint64_t>(high_part) << 32) | low_part;
|
||||||
|
|
||||||
|
return chrono::WinSystemClock::from_file_time(filetime);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct X_ACHIEVEMENT_DETAILS {
|
struct X_ACHIEVEMENT_DETAILS {
|
||||||
|
|
|
@ -108,7 +108,7 @@ X_HRESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
|
||||||
XELOGD("XMPPlayTitlePlaylist({:08X}, {:08X})", playlist_handle, song_handle);
|
XELOGD("XMPPlayTitlePlaylist({:08X}, {:08X})", playlist_handle, song_handle);
|
||||||
kernel_state_->emulator()->audio_media_player()->Play(playlist_handle,
|
kernel_state_->emulator()->audio_media_player()->Play(playlist_handle,
|
||||||
song_handle, false);
|
song_handle, false);
|
||||||
kernel_state_->BroadcastNotification(kNotificationXmpPlaybackBehaviorChanged,
|
kernel_state_->BroadcastNotification(kXNotificationXmpPlaybackBehaviorChanged,
|
||||||
1);
|
1);
|
||||||
return X_E_SUCCESS;
|
return X_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
static_cast<PlaybackFlags>(uint32_t(args->flags)));
|
static_cast<PlaybackFlags>(uint32_t(args->flags)));
|
||||||
|
|
||||||
kernel_state_->BroadcastNotification(
|
kernel_state_->BroadcastNotification(
|
||||||
kNotificationXmpPlaybackBehaviorChanged, 0);
|
kXNotificationXmpPlaybackBehaviorChanged, 0);
|
||||||
return X_E_SUCCESS;
|
return X_E_SUCCESS;
|
||||||
}
|
}
|
||||||
case 0x00070009: {
|
case 0x00070009: {
|
||||||
|
@ -359,7 +359,7 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
PlaybackClient(uint32_t(args->playback_client)));
|
PlaybackClient(uint32_t(args->playback_client)));
|
||||||
|
|
||||||
kernel_state_->BroadcastNotification(
|
kernel_state_->BroadcastNotification(
|
||||||
kNotificationXmpPlaybackControllerChanged,
|
kXNotificationXmpPlaybackControllerChanged,
|
||||||
kernel_state_->emulator()
|
kernel_state_->emulator()
|
||||||
->audio_media_player()
|
->audio_media_player()
|
||||||
->IsTitleInPlaybackControl());
|
->IsTitleInPlaybackControl());
|
||||||
|
|
|
@ -103,7 +103,14 @@ std::filesystem::path ContentManager::ResolvePackagePath(
|
||||||
// Content path:
|
// Content path:
|
||||||
// content_root/title_id/content_type/data_file_name/
|
// content_root/title_id/content_type/data_file_name/
|
||||||
auto get_package_path = [&, data, disc_number](const uint32_t title_id) {
|
auto get_package_path = [&, data, disc_number](const uint32_t title_id) {
|
||||||
uint64_t used_xuid = (data.xuid != -1 && data.xuid != 0) ? data.xuid : xuid;
|
uint64_t used_xuid =
|
||||||
|
(data.xuid != -1 && data.xuid != 0) ? data.xuid.get() : xuid;
|
||||||
|
|
||||||
|
// All DLCs are stored in common directory, so we need to override xuid for
|
||||||
|
// them and probably some other types.
|
||||||
|
if (data.content_type == XContentType::kMarketplaceContent) {
|
||||||
|
used_xuid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
auto package_root =
|
auto package_root =
|
||||||
ResolvePackageRoot(used_xuid, title_id, data.content_type);
|
ResolvePackageRoot(used_xuid, title_id, data.content_type);
|
||||||
|
@ -263,7 +270,8 @@ X_RESULT ContentManager::WriteContentHeaderFile(const uint64_t xuid,
|
||||||
if (data.xuid == -1) {
|
if (data.xuid == -1) {
|
||||||
data.xuid = xuid;
|
data.xuid = xuid;
|
||||||
}
|
}
|
||||||
uint64_t used_xuid = (data.xuid != -1 && data.xuid != 0) ? data.xuid : xuid;
|
uint64_t used_xuid =
|
||||||
|
(data.xuid != -1 && data.xuid != 0) ? data.xuid.get() : xuid;
|
||||||
|
|
||||||
auto header_path = ResolvePackageHeaderPath(data.file_name(), used_xuid,
|
auto header_path = ResolvePackageHeaderPath(data.file_name(), used_xuid,
|
||||||
data.title_id, data.content_type);
|
data.title_id, data.content_type);
|
||||||
|
|
|
@ -253,9 +253,12 @@ void ProfileManager::ModifyGamertag(const uint64_t xuid, std::string gamertag) {
|
||||||
DismountProfile(xuid);
|
DismountProfile(xuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfileManager::MountProfile(const uint64_t xuid) {
|
bool ProfileManager::MountProfile(const uint64_t xuid, std::string mount_path) {
|
||||||
std::filesystem::path profile_path = GetProfilePath(xuid);
|
std::filesystem::path profile_path = GetProfilePath(xuid);
|
||||||
std::string mount_path = fmt::format("{:016X}", xuid) + ':';
|
if (mount_path.empty()) {
|
||||||
|
mount_path = fmt::format("{:016X}", xuid);
|
||||||
|
}
|
||||||
|
mount_path += ':';
|
||||||
|
|
||||||
auto device =
|
auto device =
|
||||||
std::make_unique<vfs::HostPathDevice>(mount_path, profile_path, false);
|
std::make_unique<vfs::HostPathDevice>(mount_path, profile_path, false);
|
||||||
|
@ -314,8 +317,17 @@ void ProfileManager::Login(const uint64_t xuid, const uint8_t user_index,
|
||||||
|
|
||||||
logged_profiles_[assigned_user_slot] =
|
logged_profiles_[assigned_user_slot] =
|
||||||
std::make_unique<UserProfile>(xuid, &profile);
|
std::make_unique<UserProfile>(xuid, &profile);
|
||||||
|
|
||||||
|
if (kernel_state_->emulator()->is_title_open()) {
|
||||||
|
const kernel::util::XdbfGameData db = kernel_state_->title_xdbf();
|
||||||
|
if (db.is_valid()) {
|
||||||
|
kernel_state_->xam_state()->achievement_manager()->LoadTitleAchievements(
|
||||||
|
xuid, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
kernel_state_->BroadcastNotification(kXNotificationIDSystemSignInChanged,
|
kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged,
|
||||||
GetUsedUserSlots().to_ulong());
|
GetUsedUserSlots().to_ulong());
|
||||||
}
|
}
|
||||||
UpdateConfig(xuid, assigned_user_slot);
|
UpdateConfig(xuid, assigned_user_slot);
|
||||||
|
@ -329,7 +341,7 @@ void ProfileManager::Logout(const uint8_t user_index, bool notify) {
|
||||||
DismountProfile(profile->second->xuid());
|
DismountProfile(profile->second->xuid());
|
||||||
logged_profiles_.erase(profile);
|
logged_profiles_.erase(profile);
|
||||||
if (notify) {
|
if (notify) {
|
||||||
kernel_state_->BroadcastNotification(kXNotificationIDSystemSignInChanged,
|
kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged,
|
||||||
GetUsedUserSlots().to_ulong());
|
GetUsedUserSlots().to_ulong());
|
||||||
}
|
}
|
||||||
UpdateConfig(0, user_index);
|
UpdateConfig(0, user_index);
|
||||||
|
@ -343,7 +355,7 @@ void ProfileManager::LoginMultiple(
|
||||||
slots_mask |= (1 << slot);
|
slots_mask |= (1 << slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel_state_->BroadcastNotification(kXNotificationIDSystemSignInChanged,
|
kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged,
|
||||||
slots_mask);
|
slots_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class ProfileManager {
|
||||||
|
|
||||||
void ModifyGamertag(const uint64_t xuid, std::string gamertag);
|
void ModifyGamertag(const uint64_t xuid, std::string gamertag);
|
||||||
|
|
||||||
bool MountProfile(const uint64_t xuid);
|
bool MountProfile(const uint64_t xuid, std::string mount_path = "");
|
||||||
bool DismountProfile(const uint64_t xuid);
|
bool DismountProfile(const uint64_t xuid);
|
||||||
|
|
||||||
void Login(const uint64_t xuid, const uint8_t user_index = XUserIndexAny,
|
void Login(const uint64_t xuid, const uint8_t user_index = XUserIndexAny,
|
||||||
|
|
|
@ -52,9 +52,9 @@ UserProfile::UserProfile(uint64_t xuid, X_XAMACCOUNTINFO* account_info)
|
||||||
// XPROFILE_GAMER_CONTROL_SENSITIVITY
|
// XPROFILE_GAMER_CONTROL_SENSITIVITY
|
||||||
AddSetting(std::make_unique<UserSetting>(0x10040018, 0));
|
AddSetting(std::make_unique<UserSetting>(0x10040018, 0));
|
||||||
// Preferred color 1
|
// Preferred color 1
|
||||||
AddSetting(std::make_unique<UserSetting>(0x1004001D, 0xFFFF0000u));
|
AddSetting(std::make_unique<UserSetting>(0x1004001D, PREFERRED_COLOR_NONE));
|
||||||
// Preferred color 2
|
// Preferred color 2
|
||||||
AddSetting(std::make_unique<UserSetting>(0x1004001E, 0xFF00FF00u));
|
AddSetting(std::make_unique<UserSetting>(0x1004001E, PREFERRED_COLOR_NONE));
|
||||||
// XPROFILE_GAMER_ACTION_AUTO_AIM
|
// XPROFILE_GAMER_ACTION_AUTO_AIM
|
||||||
AddSetting(std::make_unique<UserSetting>(0x10040022, 1));
|
AddSetting(std::make_unique<UserSetting>(0x10040022, 1));
|
||||||
// XPROFILE_GAMER_ACTION_AUTO_CENTER
|
// XPROFILE_GAMER_ACTION_AUTO_CENTER
|
||||||
|
@ -170,7 +170,8 @@ void UserProfile::LoadSetting(UserSetting* setting) {
|
||||||
} else {
|
} else {
|
||||||
// Unsupported for now. Other settings aren't per-game and need to be
|
// Unsupported for now. Other settings aren't per-game and need to be
|
||||||
// stored some other way.
|
// stored some other way.
|
||||||
XELOGW("Attempting to load unsupported profile setting from disk");
|
XELOGW("Attempting to load unsupported profile setting 0x{:08X} from disk",
|
||||||
|
setting->GetSettingId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +205,8 @@ void UserProfile::SaveSetting(UserSetting* setting) {
|
||||||
} else {
|
} else {
|
||||||
// Unsupported for now. Other settings aren't per-game and need to be
|
// Unsupported for now. Other settings aren't per-game and need to be
|
||||||
// stored some other way.
|
// stored some other way.
|
||||||
XELOGW("Attempting to save unsupported profile setting to disk");
|
XELOGW("Attempting to save unsupported profile setting 0x{:08X} from disk",
|
||||||
|
setting->GetSettingId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,21 @@ enum class X_USER_PROFILE_SETTING_SOURCE : uint32_t {
|
||||||
UNKNOWN = 3,
|
UNKNOWN = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PREFERRED_COLOR_OPTIONS : uint32_t {
|
||||||
|
PREFERRED_COLOR_NONE,
|
||||||
|
PREFERRED_COLOR_BLACK,
|
||||||
|
PREFERRED_COLOR_WHITE,
|
||||||
|
PREFERRED_COLOR_YELLOW,
|
||||||
|
PREFERRED_COLOR_ORANGE,
|
||||||
|
PREFERRED_COLOR_PINK,
|
||||||
|
PREFERRED_COLOR_RED,
|
||||||
|
PREFERRED_COLOR_PURPLE,
|
||||||
|
PREFERRED_COLOR_BLUE,
|
||||||
|
PREFERRED_COLOR_GREEN,
|
||||||
|
PREFERRED_COLOR_BROWN,
|
||||||
|
PREFERRED_COLOR_SILVER
|
||||||
|
};
|
||||||
|
|
||||||
// Each setting contains 0x18 bytes long header
|
// Each setting contains 0x18 bytes long header
|
||||||
struct X_USER_PROFILE_SETTING_HEADER {
|
struct X_USER_PROFILE_SETTING_HEADER {
|
||||||
xe::be<uint32_t> setting_id;
|
xe::be<uint32_t> setting_id;
|
||||||
|
@ -165,6 +180,10 @@ class UserProfile {
|
||||||
uint32_t GetSubscriptionTier() const {
|
uint32_t GetSubscriptionTier() const {
|
||||||
return account_info_.GetSubscriptionTier();
|
return account_info_.GetSubscriptionTier();
|
||||||
}
|
}
|
||||||
|
void GetPasscode(uint16_t* passcode) const {
|
||||||
|
std::memcpy(passcode, account_info_.passcode,
|
||||||
|
sizeof(account_info_.passcode));
|
||||||
|
};
|
||||||
|
|
||||||
void AddSetting(std::unique_ptr<UserSetting> setting);
|
void AddSetting(std::unique_ptr<UserSetting> setting);
|
||||||
UserSetting* GetSetting(uint32_t setting_id);
|
UserSetting* GetSetting(uint32_t setting_id);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
* Copyright 2024 Ben Vanik. All rights reserved. *
|
||||||
* 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. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -17,24 +17,248 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace xam {
|
namespace xam {
|
||||||
|
|
||||||
|
// Start/End
|
||||||
dword_result_t XamAvatarInitialize_entry(
|
dword_result_t XamAvatarInitialize_entry(
|
||||||
dword_t unk1, // 1, 4, etc
|
dword_t unk1, // 1, 2, 4, etc
|
||||||
dword_t unk2, // 0 or 1
|
dword_t unk2, // 0 or 1
|
||||||
dword_t processor_number, // for thread creation?
|
dword_t processor_number, // for thread creation?
|
||||||
lpdword_t function_ptrs, // 20b, 5 pointers
|
lpdword_t function_ptrs, // 20b, 5 pointers
|
||||||
lpunknown_t unk5, // ptr in data segment
|
lpunknown_t unk5, // ptr in data segment
|
||||||
dword_t unk6 // flags - 0x00300000, 0x30, etc
|
dword_t unk6 // flags - 0x00300000, 0x30, etc
|
||||||
) {
|
) {
|
||||||
|
// This should only work for avatar editor to test avatar rendering
|
||||||
|
auto title_id = kernel_state()->title_id();
|
||||||
|
if (title_id == 0x584D07D1) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
// Negative to fail. Game should immediately call XamAvatarShutdown.
|
// Negative to fail. Game should immediately call XamAvatarShutdown.
|
||||||
return ~0u;
|
return ~0u;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamAvatarInitialize, kAvatars, kStub);
|
DECLARE_XAM_EXPORT1(XamAvatarInitialize, kAvatars, kStub);
|
||||||
|
|
||||||
void XamAvatarShutdown_entry() {
|
void XamAvatarShutdown_entry() {
|
||||||
// No-op.
|
// Calls XMsgStartIORequestEx(0xf3,0x600002,0,0,0,0).
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamAvatarShutdown, kAvatars, kStub);
|
DECLARE_XAM_EXPORT1(XamAvatarShutdown, kAvatars, kStub);
|
||||||
|
|
||||||
|
// Get & Set
|
||||||
|
dword_result_t XamAvatarGetManifestLocalUser_entry(dword_t user_index,
|
||||||
|
lpdword_t avatar_metadata,
|
||||||
|
qword_t unk3) {
|
||||||
|
// XMsgStartIORequestEx(0xf3, 0x600003, unk3, stack1, 8, 0)
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetManifestLocalUser, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetManifestsByXuid_entry(dword_t unk1, qword_t unk2,
|
||||||
|
qword_t unk3, dword_t unk4,
|
||||||
|
dword_t unk5, qword_t unk6) {
|
||||||
|
// Function_818D1738(0x600004,param_6,&uStack_d0,0x90,0)
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetManifestsByXuid, kAvatars, kStub)
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetAssetsResultSize_entry(qword_t unk1, qword_t unk2,
|
||||||
|
dword_t unk3) {
|
||||||
|
// return XMsgInProcessCall(0xf3,0x600005,local_20,0);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetAssetsResultSize, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetAssets_entry(qword_t unk1, word_t unk2, dword_t unk3,
|
||||||
|
dword_t unk4, dword_t unk5,
|
||||||
|
qword_t unk6) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetAssets, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarSetCustomAsset_entry(qword_t unk1, qword_t unk2,
|
||||||
|
dword_t unk3, dword_t unk4,
|
||||||
|
dword_t unk5, dword_t unk6) {
|
||||||
|
// XMsgInProcessCall(0xf3,0x60000a,local_30,0);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarSetCustomAsset, kAvatars, kStub)
|
||||||
|
|
||||||
|
dword_result_t XamAvatarSetManifest_entry(qword_t unk1, qword_t unk2,
|
||||||
|
qword_t unk3) {
|
||||||
|
// Function_818D1738(0x600009,param_3,&local_410,0x3ec,0);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarSetManifest, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetMetadataRandom_entry(word_t body_type, word_t unk2,
|
||||||
|
lpdword_t avatar_metadata,
|
||||||
|
qword_t unk4) {
|
||||||
|
// some_function(0x600010,param_4,local_28,0xc,local_30)
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetMetadataRandom, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetMetadataSignedOutProfileCount_entry(dword_t unk1,
|
||||||
|
qword_t unk2) {
|
||||||
|
// Function_818D1738(0x600013,param_2,local_10,4,0);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetMetadataSignedOutProfileCount, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetMetadataSignedOutProfile_entry(dword_t unk1,
|
||||||
|
dword_t unk2,
|
||||||
|
qword_t unk3) {
|
||||||
|
// Function_818D1738(0x600014,param_3,local_10,8,0);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetMetadataSignedOutProfile, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarManifestGetBodyType_entry(qword_t body_type) {
|
||||||
|
/* Notes:
|
||||||
|
- Calls XMsgStartIORequestEx(0xf3, 0x60000F, unk4, stack1, 0x18,
|
||||||
|
0x10000000)
|
||||||
|
- return either char of 1 - male, 2 - female, else - unknown
|
||||||
|
*/
|
||||||
|
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarManifestGetBodyType, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetInstrumentation_entry(qword_t unk1, lpdword_t unk2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetInstrumentation, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetAssetIcon_entry(lpqword_t unk1, dword_t unk2,
|
||||||
|
dword_t unk3, dword_t unk4,
|
||||||
|
qword_t unk5) {
|
||||||
|
// XMsgStartIORequestEx(0xf3, 0x60000B, unk5, stack1, 0x1C, 0x10000000)
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetAssetIcon, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetAssetBinary_entry(lpqword_t unk1, dword_t unk2,
|
||||||
|
dword_t unk3, dword_t unk4,
|
||||||
|
qword_t unk5) {
|
||||||
|
// Function_818D1738(0x600008,param_5,&uStack_60,0x1c,auStack_70);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetAssetBinary, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarGetInstalledAssetPackageDescription_entry(
|
||||||
|
qword_t unk1, qword_t unk2) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGetInstalledAssetPackageDescription, kAvatars,
|
||||||
|
kStub);
|
||||||
|
|
||||||
|
void XamAvatarSetMocks_entry() {
|
||||||
|
// No-op.
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarSetMocks, kAvatars, kStub);
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
|
||||||
|
const static std::map<uint64_t, std::string> XAnimationTypeMap = {
|
||||||
|
// Animation Generic Stand
|
||||||
|
{0x0040000000030003, "Animation Generic Stand 0"},
|
||||||
|
{0x0040000000040003, "Animation Generic Stand 1"},
|
||||||
|
{0x0040000000050003, "Animation Generic Stand 2"},
|
||||||
|
{0x0040000000270003, "Animation Generic Stand 3"},
|
||||||
|
{0x0040000000280003, "Animation Generic Stand 4"},
|
||||||
|
{0x0040000000290003, "Animation Generic Stand 5"},
|
||||||
|
{0x00400000002A0003, "Animation Generic Stand 6"},
|
||||||
|
{0x00400000002B0003, "Animation Generic Stand 7"},
|
||||||
|
// Animation Idle
|
||||||
|
{0x0040000000130001, "Animation Male Idle Looks Around"},
|
||||||
|
{0x0040000000140001, "Animation Male Idle Stretch"},
|
||||||
|
{0x0040000000150001, "Animation Male Idle Shifts Weight"},
|
||||||
|
{0x0040000000260001, "Animation Male Idle Checks Hand"},
|
||||||
|
{0x0040000000090002, "Animation Female Idle Check Nails"},
|
||||||
|
{0x00400000000A0002, "Animation Female Idle Looks Around"},
|
||||||
|
{0x00400000000B0002, "Animation Female Idle Shifts Weight"},
|
||||||
|
{0x00400000000C0002, "Animation Female Idle Fixes Shoe"},
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://github.com/xenia-canary/xenia-canary/commit/212c99eee2724de15f471148d10197d89794ff32
|
||||||
|
dword_result_t XamAvatarLoadAnimation_entry(lpqword_t asset_id_ptr,
|
||||||
|
dword_t flags, dword_t output,
|
||||||
|
dword_t unk1) {
|
||||||
|
/* Notes:
|
||||||
|
- Calls XMsgStartIORequestEx(0xf3, 0x60000F, unk4, stack1, 0x18,
|
||||||
|
0x10000000)
|
||||||
|
- I cannot find an error code for when asset_id_ptr returns nullptr. It
|
||||||
|
could be due to decompiling issues
|
||||||
|
*/
|
||||||
|
assert_true(asset_id_ptr);
|
||||||
|
std::string summary = "Request to load avatar animation: ";
|
||||||
|
|
||||||
|
if (XAnimationTypeMap.find(*asset_id_ptr) != XAnimationTypeMap.cend()) {
|
||||||
|
summary += XAnimationTypeMap.at(*asset_id_ptr);
|
||||||
|
} else {
|
||||||
|
summary += fmt::format("Unknown animation {}",
|
||||||
|
static_cast<uint64_t>(*asset_id_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
XELOGD("{}", summary);
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarLoadAnimation, kAvatars, kStub);
|
||||||
|
|
||||||
|
// Enum
|
||||||
|
dword_result_t XamAvatarBeginEnumAssets_entry(qword_t unk1, word_t unk2,
|
||||||
|
dword_t unk3, word_t unk4,
|
||||||
|
word_t unk5, qword_t unk6) {
|
||||||
|
// XMsgStartIORequestEx(0xf3, 0x60000c, unkn6, stack1, 0x14, stack2)
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarBeginEnumAssets, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarEnumAssets_entry(lpqword_t unk1, dword_t unk2,
|
||||||
|
qword_t unk3) {
|
||||||
|
// XMsgStartIORequestEx(0xf3, 0x60000d, unk3, stack1, 8, stack2)
|
||||||
|
|
||||||
|
return X_E_NO_MORE_FILES; // Stop it from calling endlessly
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarEnumAssets, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarEndEnumAssets_entry(qword_t unk1) {
|
||||||
|
// some_function(0x60000e,param_1,0,0,local_10)
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarEndEnumAssets, kAvatars, kStub);
|
||||||
|
|
||||||
|
// Other
|
||||||
|
dword_result_t XamAvatarGenerateMipMaps_entry(qword_t unk1, qword_t unk2,
|
||||||
|
dword_t unk3, dword_t unk4,
|
||||||
|
qword_t unk5) {
|
||||||
|
// Function_818D1738(0x600007,param_5,local_30,0x10,local_40);
|
||||||
|
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarGenerateMipMaps, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarWearNow_entry(qword_t unk1, lpdword_t unk2,
|
||||||
|
qword_t unk3) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarWearNow, kAvatars, kStub);
|
||||||
|
|
||||||
|
dword_result_t XamAvatarReinstallAwardedAsset_entry(qword_t unk1, qword_t unk2,
|
||||||
|
qword_t unk3) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamAvatarReinstallAwardedAsset, kAvatars, kStub);
|
||||||
|
|
||||||
} // namespace xam
|
} // namespace xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -74,9 +74,9 @@ DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency);
|
||||||
|
|
||||||
dword_result_t XamContentResolve_entry(dword_t user_index,
|
dword_result_t XamContentResolve_entry(dword_t user_index,
|
||||||
lpvoid_t content_data_ptr,
|
lpvoid_t content_data_ptr,
|
||||||
lpunknown_t buffer_ptr,
|
lpvoid_t buffer_ptr, dword_t buffer_size,
|
||||||
dword_t buffer_size, dword_t unk1,
|
dword_t unk1, dword_t unk2,
|
||||||
dword_t unk2, dword_t unk3) {
|
dword_t unk3) {
|
||||||
auto content_data = content_data_ptr.as<XCONTENT_DATA*>();
|
auto content_data = content_data_ptr.as<XCONTENT_DATA*>();
|
||||||
|
|
||||||
// Result of buffer_ptr is sent to RtlInitAnsiString.
|
// Result of buffer_ptr is sent to RtlInitAnsiString.
|
||||||
|
@ -351,7 +351,7 @@ dword_result_t XamContentOpenFile_entry(dword_t user_index,
|
||||||
DECLARE_XAM_EXPORT1(XamContentOpenFile, kContent, kStub);
|
DECLARE_XAM_EXPORT1(XamContentOpenFile, kContent, kStub);
|
||||||
|
|
||||||
dword_result_t XamContentFlush_entry(lpstring_t root_name,
|
dword_result_t XamContentFlush_entry(lpstring_t root_name,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
X_RESULT result = X_ERROR_SUCCESS;
|
X_RESULT result = X_ERROR_SUCCESS;
|
||||||
if (overlapped_ptr) {
|
if (overlapped_ptr) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result);
|
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result);
|
||||||
|
@ -363,7 +363,7 @@ dword_result_t XamContentFlush_entry(lpstring_t root_name,
|
||||||
DECLARE_XAM_EXPORT1(XamContentFlush, kContent, kStub);
|
DECLARE_XAM_EXPORT1(XamContentFlush, kContent, kStub);
|
||||||
|
|
||||||
dword_result_t XamContentClose_entry(lpstring_t root_name,
|
dword_result_t XamContentClose_entry(lpstring_t root_name,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
// Closes a previously opened root from XamContentCreate*.
|
// Closes a previously opened root from XamContentCreate*.
|
||||||
auto result =
|
auto result =
|
||||||
kernel_state()->content_manager()->CloseContent(root_name.value());
|
kernel_state()->content_manager()->CloseContent(root_name.value());
|
||||||
|
@ -381,7 +381,7 @@ dword_result_t XamContentGetCreator_entry(dword_t user_index,
|
||||||
lpvoid_t content_data_ptr,
|
lpvoid_t content_data_ptr,
|
||||||
lpdword_t is_creator_ptr,
|
lpdword_t is_creator_ptr,
|
||||||
lpqword_t creator_xuid_ptr,
|
lpqword_t creator_xuid_ptr,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
if (!is_creator_ptr) {
|
if (!is_creator_ptr) {
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
@ -449,7 +449,7 @@ dword_result_t XamContentGetThumbnail_entry(dword_t user_index,
|
||||||
lpvoid_t content_data_ptr,
|
lpvoid_t content_data_ptr,
|
||||||
lpvoid_t buffer_ptr,
|
lpvoid_t buffer_ptr,
|
||||||
lpdword_t buffer_size_ptr,
|
lpdword_t buffer_size_ptr,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
@ -494,7 +494,7 @@ dword_result_t XamContentSetThumbnail_entry(dword_t user_index,
|
||||||
lpvoid_t content_data_ptr,
|
lpvoid_t content_data_ptr,
|
||||||
lpvoid_t buffer_ptr,
|
lpvoid_t buffer_ptr,
|
||||||
dword_t buffer_size,
|
dword_t buffer_size,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
@ -518,10 +518,15 @@ dword_result_t XamContentSetThumbnail_entry(dword_t user_index,
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented);
|
DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented);
|
||||||
|
|
||||||
dword_result_t XamContentDelete_entry(dword_t user_index,
|
dword_result_t xeXamContentDelete(dword_t user_index, lpvoid_t content_data_ptr,
|
||||||
lpvoid_t content_data_ptr,
|
dword_t content_data_size,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
uint64_t xuid = 0;
|
uint64_t xuid = 0;
|
||||||
|
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
|
||||||
|
if (content_data_size == sizeof(XCONTENT_AGGREGATE_DATA)) {
|
||||||
|
content_data = *content_data_ptr.as<XCONTENT_AGGREGATE_DATA*>();
|
||||||
|
}
|
||||||
|
|
||||||
if (user_index != XUserIndexNone) {
|
if (user_index != XUserIndexNone) {
|
||||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
|
|
||||||
|
@ -532,8 +537,6 @@ dword_result_t XamContentDelete_entry(dword_t user_index,
|
||||||
xuid = user->xuid();
|
xuid = user->xuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
|
|
||||||
|
|
||||||
auto result =
|
auto result =
|
||||||
kernel_state()->content_manager()->DeleteContent(xuid, content_data);
|
kernel_state()->content_manager()->DeleteContent(xuid, content_data);
|
||||||
|
|
||||||
|
@ -544,14 +547,22 @@ dword_result_t XamContentDelete_entry(dword_t user_index,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dword_result_t XamContentDelete_entry(dword_t user_index,
|
||||||
|
lpvoid_t content_data_ptr,
|
||||||
|
lpvoid_t overlapped_ptr) {
|
||||||
|
return xeXamContentDelete(user_index, content_data_ptr, sizeof(XCONTENT_DATA),
|
||||||
|
overlapped_ptr);
|
||||||
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamContentDelete, kContent, kImplemented);
|
DECLARE_XAM_EXPORT1(XamContentDelete, kContent, kImplemented);
|
||||||
|
|
||||||
dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr,
|
dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr,
|
||||||
lpunknown_t overlapped_ptr) {
|
lpvoid_t overlapped_ptr) {
|
||||||
// INFO: Analysis of xam.xex shows that "internal" functions are wrappers with
|
// INFO: Analysis of xam.xex shows that "internal" functions are wrappers with
|
||||||
// 0xFE as user_index
|
// 0xFE as user_index.
|
||||||
return XamContentDelete_entry(XUserIndexNone, content_data_ptr,
|
// In XAM content size is set to 0x200.
|
||||||
overlapped_ptr);
|
return xeXamContentDelete(XUserIndexNone, content_data_ptr,
|
||||||
|
sizeof(XCONTENT_AGGREGATE_DATA), overlapped_ptr);
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented);
|
DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented);
|
||||||
|
|
||||||
|
@ -621,8 +632,8 @@ DECLARE_XAM_EXPORT1(XamLoaderGetMediaInfoEx, kContent, kStub);
|
||||||
|
|
||||||
dword_result_t XamContentLaunchImageFromFileInternal_entry(
|
dword_result_t XamContentLaunchImageFromFileInternal_entry(
|
||||||
lpstring_t image_location, lpstring_t xex_name, dword_t unk) {
|
lpstring_t image_location, lpstring_t xex_name, dword_t unk) {
|
||||||
const std::string image_path = image_location;
|
const std::string image_path = static_cast<std::string>(image_location);
|
||||||
const std::string xex_name_ = xex_name;
|
const std::string xex_name_ = static_cast<std::string>(xex_name);
|
||||||
|
|
||||||
vfs::Entry* entry = kernel_state()->file_system()->ResolvePath(image_path);
|
vfs::Entry* entry = kernel_state()->file_system()->ResolvePath(image_path);
|
||||||
|
|
||||||
|
@ -696,7 +707,7 @@ dword_result_t XamContentLaunchImageInternal_entry(lpvoid_t content_data_ptr,
|
||||||
|
|
||||||
auto& loader_data = xam->loader_data();
|
auto& loader_data = xam->loader_data();
|
||||||
loader_data.host_path = xe::path_to_utf8(host_path);
|
loader_data.host_path = xe::path_to_utf8(host_path);
|
||||||
loader_data.launch_path = xex_path;
|
loader_data.launch_path = xex_path.value();
|
||||||
|
|
||||||
xam->SaveLoaderData();
|
xam->SaveLoaderData();
|
||||||
|
|
||||||
|
|
|
@ -115,8 +115,9 @@ dword_result_t XamContentAggregateCreateEnumerator_entry(qword_t xuid,
|
||||||
for (auto& title_id : title_ids) {
|
for (auto& title_id : title_ids) {
|
||||||
// Get all content data.
|
// Get all content data.
|
||||||
auto content_datas = kernel_state()->content_manager()->ListContent(
|
auto content_datas = kernel_state()->content_manager()->ListContent(
|
||||||
static_cast<uint32_t>(DummyDeviceId::HDD), xuid == -1 ? 0 : xuid,
|
static_cast<uint32_t>(DummyDeviceId::HDD),
|
||||||
title_id, content_type_enum);
|
xuid == -1 ? 0 : static_cast<uint64_t>(xuid), title_id,
|
||||||
|
content_type_enum);
|
||||||
for (const auto& content_data : content_datas) {
|
for (const auto& content_data : content_datas) {
|
||||||
auto item = e->AppendItem();
|
auto item = e->AppendItem();
|
||||||
assert_not_null(item);
|
assert_not_null(item);
|
||||||
|
|
|
@ -167,6 +167,18 @@ dword_result_t XamProfileEnumerate_entry(dword_t handle, dword_t flags,
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamProfileEnumerate, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(XamProfileEnumerate, kNone, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t EnumerateMediaObjects_entry() { return X_E_NOT_IMPLEMENTED; }
|
||||||
|
DECLARE_XAM_EXPORT1(EnumerateMediaObjects, kNone, kStub);
|
||||||
|
|
||||||
|
dword_result_t EnumerateMediaObjects__entry() { return X_E_NOT_IMPLEMENTED; }
|
||||||
|
DECLARE_XAM_EXPORT1(EnumerateMediaObjects_, kNone, kStub);
|
||||||
|
|
||||||
|
dword_result_t EnumerateMediaObjects_0_entry() { return X_E_NOT_IMPLEMENTED; }
|
||||||
|
DECLARE_XAM_EXPORT1(EnumerateMediaObjects_0, kNone, kStub);
|
||||||
|
|
||||||
|
dword_result_t EnumerateMediaObjects_1_entry() { return X_E_NOT_IMPLEMENTED; }
|
||||||
|
DECLARE_XAM_EXPORT1(EnumerateMediaObjects_1, kNone, kStub);
|
||||||
|
|
||||||
} // namespace xam
|
} // namespace xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -27,10 +27,6 @@
|
||||||
#include "xenia/ui/windowed_app_context.h"
|
#include "xenia/ui/windowed_app_context.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32
|
|
||||||
#include "xenia/base/platform_win.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
#include "third_party/fmt/include/fmt/xchar.h"
|
#include "third_party/fmt/include/fmt/xchar.h"
|
||||||
|
|
||||||
|
@ -91,51 +87,37 @@ dword_result_t XamGetOnlineSchema_entry() {
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamGetOnlineSchema, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(XamGetOnlineSchema, kNone, kImplemented);
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32
|
void XamFormatDateString_entry(dword_t locale_format, qword_t filetime,
|
||||||
static SYSTEMTIME xeGetLocalSystemTime(uint64_t filetime) {
|
|
||||||
FILETIME t;
|
|
||||||
t.dwHighDateTime = filetime >> 32;
|
|
||||||
t.dwLowDateTime = (uint32_t)filetime;
|
|
||||||
|
|
||||||
SYSTEMTIME st;
|
|
||||||
SYSTEMTIME local_st;
|
|
||||||
FileTimeToSystemTime(&t, &st);
|
|
||||||
SystemTimeToTzSpecificLocalTime(NULL, &st, &local_st);
|
|
||||||
return local_st;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void XamFormatDateString_entry(dword_t unk, qword_t filetime,
|
|
||||||
lpvoid_t output_buffer, dword_t output_count) {
|
lpvoid_t output_buffer, dword_t output_count) {
|
||||||
std::memset(output_buffer, 0, output_count * sizeof(char16_t));
|
output_buffer.Zero(output_count * sizeof(char16_t));
|
||||||
|
|
||||||
// TODO: implement this for other platforms
|
auto tp = xe::chrono::WinSystemClock::to_sys(
|
||||||
#if XE_PLATFORM_WIN32
|
xe::chrono::WinSystemClock::from_file_time(filetime));
|
||||||
auto st = xeGetLocalSystemTime(filetime);
|
auto dp = date::floor<date::days>(tp);
|
||||||
// TODO: format this depending on users locale?
|
auto year_month_day = date::year_month_day{dp};
|
||||||
auto str = fmt::format(u"{:02d}/{:02d}/{}", st.wMonth, st.wDay, st.wYear);
|
|
||||||
|
auto str = fmt::format(u"{:02d}/{:02d}/{}",
|
||||||
|
static_cast<unsigned>(year_month_day.month()),
|
||||||
|
static_cast<unsigned>(year_month_day.day()),
|
||||||
|
static_cast<int>(year_month_day.year()));
|
||||||
xe::string_util::copy_and_swap_truncating(output_buffer.as<char16_t*>(), str,
|
xe::string_util::copy_and_swap_truncating(output_buffer.as<char16_t*>(), str,
|
||||||
output_count);
|
output_count);
|
||||||
#else
|
|
||||||
assert_always();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamFormatDateString, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(XamFormatDateString, kNone, kImplemented);
|
||||||
|
|
||||||
void XamFormatTimeString_entry(dword_t unk, qword_t filetime,
|
void XamFormatTimeString_entry(dword_t unk, qword_t filetime,
|
||||||
lpvoid_t output_buffer, dword_t output_count) {
|
lpvoid_t output_buffer, dword_t output_count) {
|
||||||
std::memset(output_buffer, 0, output_count * sizeof(char16_t));
|
output_buffer.Zero(output_count * sizeof(char16_t));
|
||||||
|
|
||||||
// TODO: implement this for other platforms
|
auto tp = xe::chrono::WinSystemClock::to_sys(
|
||||||
#if XE_PLATFORM_WIN32
|
xe::chrono::WinSystemClock::from_file_time(filetime));
|
||||||
auto st = xeGetLocalSystemTime(filetime);
|
auto dp = date::floor<date::days>(tp);
|
||||||
// TODO: format this depending on users locale?
|
auto time = date::hh_mm_ss{date::floor<std::chrono::milliseconds>(tp - dp)};
|
||||||
auto str = fmt::format(u"{:02d}:{:02d}", st.wHour, st.wMinute);
|
|
||||||
|
auto str = fmt::format(u"{:02d}:{:02d}", time.hours().count(),
|
||||||
|
time.minutes().count());
|
||||||
xe::string_util::copy_and_swap_truncating(output_buffer.as<char16_t*>(), str,
|
xe::string_util::copy_and_swap_truncating(output_buffer.as<char16_t*>(), str,
|
||||||
output_count);
|
output_count);
|
||||||
#else
|
|
||||||
assert_always();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamFormatTimeString, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(XamFormatTimeString, kNone, kImplemented);
|
||||||
|
|
||||||
|
@ -477,20 +459,19 @@ DECLARE_XAM_EXPORT1(XamQueryLiveHiveW, kNone, kStub);
|
||||||
// http://www.noxa.org/blog/2011/02/28/building-an-xbox-360-emulator-part-3-feasibilityos/
|
// http://www.noxa.org/blog/2011/02/28/building-an-xbox-360-emulator-part-3-feasibilityos/
|
||||||
// http://www.noxa.org/blog/2011/08/13/building-an-xbox-360-emulator-part-5-xex-files/
|
// http://www.noxa.org/blog/2011/08/13/building-an-xbox-360-emulator-part-5-xex-files/
|
||||||
dword_result_t RtlSleep_entry(dword_t dwMilliseconds, dword_t bAlertable) {
|
dword_result_t RtlSleep_entry(dword_t dwMilliseconds, dword_t bAlertable) {
|
||||||
LARGE_INTEGER delay{};
|
uint64_t delay{};
|
||||||
|
|
||||||
// Convert the delay time to 100-nanosecond intervals
|
// Convert the delay time to 100-nanosecond intervals
|
||||||
delay.QuadPart = dwMilliseconds == -1
|
delay = dwMilliseconds == -1 ? LLONG_MAX
|
||||||
? LLONG_MAX
|
: static_cast<int64_t>(-10000) * dwMilliseconds;
|
||||||
: static_cast<LONGLONG>(-10000) * dwMilliseconds;
|
|
||||||
|
|
||||||
X_STATUS result = xboxkrnl::KeDelayExecutionThread(
|
X_STATUS result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
|
||||||
MODE::UserMode, bAlertable, (uint64_t*)&delay, nullptr);
|
&delay, nullptr);
|
||||||
|
|
||||||
// If the delay was interrupted by an APC, keep delaying the thread
|
// If the delay was interrupted by an APC, keep delaying the thread
|
||||||
while (bAlertable && result == X_STATUS_ALERTED) {
|
while (bAlertable && result == X_STATUS_ALERTED) {
|
||||||
result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
|
result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
|
||||||
(uint64_t*)&delay, nullptr);
|
&delay, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result == X_STATUS_SUCCESS ? X_STATUS_SUCCESS : X_STATUS_USER_APC;
|
return result == X_STATUS_SUCCESS ? X_STATUS_SUCCESS : X_STATUS_USER_APC;
|
||||||
|
@ -504,7 +485,7 @@ DECLARE_XAM_EXPORT1(SleepEx, kNone, kImplemented);
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
|
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
|
||||||
void Sleep_entry(dword_t dwMilliseconds) {
|
void Sleep_entry(dword_t dwMilliseconds) {
|
||||||
RtlSleep_entry(dwMilliseconds, FALSE);
|
RtlSleep_entry(dwMilliseconds, false);
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(Sleep, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(Sleep, kNone, kImplemented);
|
||||||
|
|
||||||
|
@ -619,28 +600,29 @@ DECLARE_XAM_EXPORT1(GetCurrentThreadId, kNone, kImplemented);
|
||||||
|
|
||||||
qword_result_t XapiFormatTimeOut_entry(lpqword_t result,
|
qword_result_t XapiFormatTimeOut_entry(lpqword_t result,
|
||||||
dword_t dwMilliseconds) {
|
dword_t dwMilliseconds) {
|
||||||
LARGE_INTEGER delay{};
|
if (dwMilliseconds == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert the delay time to 100-nanosecond intervals
|
*result = static_cast<int64_t>(-10000) * dwMilliseconds;
|
||||||
delay.QuadPart =
|
return result.host_address();
|
||||||
dwMilliseconds == -1 ? 0 : static_cast<LONGLONG>(-10000) * dwMilliseconds;
|
|
||||||
|
|
||||||
return (uint64_t)&delay;
|
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XapiFormatTimeOut, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(XapiFormatTimeOut, kNone, kImplemented);
|
||||||
|
|
||||||
dword_result_t WaitForSingleObjectEx_entry(dword_t hHandle,
|
dword_result_t WaitForSingleObjectEx_entry(dword_t hHandle,
|
||||||
dword_t dwMilliseconds,
|
dword_t dwMilliseconds,
|
||||||
dword_t bAlertable) {
|
dword_t bAlertable) {
|
||||||
uint64_t* timeout = nullptr;
|
// TODO(Gliniak): Figure it out to be less janky.
|
||||||
uint64_t timeout_ptr = XapiFormatTimeOut_entry(timeout, dwMilliseconds);
|
uint64_t timeout;
|
||||||
|
uint64_t* timeout_ptr = reinterpret_cast<uint64_t*>(
|
||||||
|
static_cast<uint64_t>(XapiFormatTimeOut_entry(&timeout, dwMilliseconds)));
|
||||||
|
|
||||||
X_STATUS result = xe::kernel::xboxkrnl::NtWaitForSingleObjectEx(
|
X_STATUS result =
|
||||||
hHandle, 1, bAlertable, &timeout_ptr);
|
xboxkrnl::NtWaitForSingleObjectEx(hHandle, 1, bAlertable, timeout_ptr);
|
||||||
|
|
||||||
while (bAlertable && result == X_STATUS_ALERTED) {
|
while (bAlertable && result == X_STATUS_ALERTED) {
|
||||||
result = xe::kernel::xboxkrnl::NtWaitForSingleObjectEx(
|
result =
|
||||||
hHandle, 1, bAlertable, &timeout_ptr);
|
xboxkrnl::NtWaitForSingleObjectEx(hHandle, 1, bAlertable, timeout_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
RtlSetLastNTError_entry(result);
|
RtlSetLastNTError_entry(result);
|
||||||
|
@ -716,6 +698,9 @@ dword_result_t RtlRandom_entry(lpdword_t seed_out) {
|
||||||
|
|
||||||
DECLARE_XAM_EXPORT1(RtlRandom, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(RtlRandom, kNone, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t Refresh_entry() { return X_ERROR_SUCCESS; }
|
||||||
|
DECLARE_XAM_EXPORT1(Refresh, kNone, kStub);
|
||||||
|
|
||||||
} // namespace xam
|
} // namespace xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -21,16 +21,11 @@ namespace kernel {
|
||||||
namespace xam {
|
namespace xam {
|
||||||
|
|
||||||
using xe::hid::X_INPUT_CAPABILITIES;
|
using xe::hid::X_INPUT_CAPABILITIES;
|
||||||
|
using xe::hid::X_INPUT_FLAG;
|
||||||
using xe::hid::X_INPUT_KEYSTROKE;
|
using xe::hid::X_INPUT_KEYSTROKE;
|
||||||
using xe::hid::X_INPUT_STATE;
|
using xe::hid::X_INPUT_STATE;
|
||||||
using xe::hid::X_INPUT_VIBRATION;
|
using xe::hid::X_INPUT_VIBRATION;
|
||||||
|
|
||||||
constexpr uint32_t XINPUT_FLAG_GAMEPAD = 0x01;
|
|
||||||
constexpr uint32_t XINPUT_FLAG_KEYBOARD = 0x02;
|
|
||||||
constexpr uint32_t XINPUT_FLAG_MIC = 0x20; // Based on "karaoke" titles
|
|
||||||
constexpr uint32_t XINPUT_FLAG_ANYDEVICE = 0xFF;
|
|
||||||
constexpr uint32_t XINPUT_FLAG_ANY_USER = 1 << 30;
|
|
||||||
|
|
||||||
dword_result_t XAutomationpUnbindController_entry(dword_t user_index) {
|
dword_result_t XAutomationpUnbindController_entry(dword_t user_index) {
|
||||||
if (user_index >= XUserMaxUserCount) {
|
if (user_index >= XUserMaxUserCount) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -65,7 +60,7 @@ dword_result_t XamInputGetCapabilitiesEx_entry(
|
||||||
|
|
||||||
caps.Zero();
|
caps.Zero();
|
||||||
|
|
||||||
if ((flags & XINPUT_FLAG_ANY_USER) != 0) {
|
if ((flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER) != 0) {
|
||||||
// should trap
|
// should trap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,21 +68,22 @@ dword_result_t XamInputGetCapabilitiesEx_entry(
|
||||||
// should trap
|
// should trap
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
||||||
// Ignore any query for other types of devices.
|
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t actual_user_index = user_index;
|
uint32_t actual_user_index = user_index;
|
||||||
if ((actual_user_index & XUserIndexAny) == XUserIndexAny ||
|
if ((actual_user_index & XUserIndexAny) == XUserIndexAny ||
|
||||||
(flags & XINPUT_FLAG_ANY_USER)) {
|
(flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER)) {
|
||||||
// Always pin user to 0.
|
// Always pin user to 0.
|
||||||
actual_user_index = 0;
|
actual_user_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t actual_flags = flags;
|
||||||
|
if (!flags) {
|
||||||
|
actual_flags = X_INPUT_FLAG::X_INPUT_FLAG_GAMEPAD |
|
||||||
|
X_INPUT_FLAG::X_INPUT_FLAG_KEYBOARD;
|
||||||
|
}
|
||||||
|
|
||||||
auto input_system = kernel_state()->emulator()->input_system();
|
auto input_system = kernel_state()->emulator()->input_system();
|
||||||
auto lock = input_system->lock();
|
auto lock = input_system->lock();
|
||||||
return input_system->GetCapabilities(actual_user_index, flags, caps);
|
return input_system->GetCapabilities(actual_user_index, actual_flags, caps);
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamInputGetCapabilitiesEx, kInput, kSketchy);
|
DECLARE_XAM_EXPORT1(XamInputGetCapabilitiesEx, kInput, kSketchy);
|
||||||
|
|
||||||
|
@ -112,22 +108,19 @@ dword_result_t XamInputGetState_entry(dword_t user_index, dword_t flags,
|
||||||
|
|
||||||
// Games call this with a NULL state ptr, probably as a query.
|
// Games call this with a NULL state ptr, probably as a query.
|
||||||
|
|
||||||
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
||||||
// Ignore any query for other types of devices.
|
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t actual_user_index = user_index;
|
uint32_t actual_user_index = user_index;
|
||||||
// chrispy: change this, logic is not right
|
// chrispy: change this, logic is not right
|
||||||
if ((actual_user_index & XUserIndexAny) == XUserIndexAny ||
|
if ((actual_user_index & XUserIndexAny) == XUserIndexAny ||
|
||||||
(flags & XINPUT_FLAG_ANY_USER)) {
|
(flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER)) {
|
||||||
// Always pin user to 0.
|
// Always pin user to 0.
|
||||||
actual_user_index = 0;
|
actual_user_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto input_system = kernel_state()->emulator()->input_system();
|
auto input_system = kernel_state()->emulator()->input_system();
|
||||||
auto lock = input_system->lock();
|
auto lock = input_system->lock();
|
||||||
return input_system->GetState(user_index, input_state);
|
return input_system->GetState(
|
||||||
|
user_index, !flags ? X_INPUT_FLAG::X_INPUT_FLAG_GAMEPAD : flags,
|
||||||
|
input_state);
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT2(XamInputGetState, kInput, kImplemented, kHighFrequency);
|
DECLARE_XAM_EXPORT2(XamInputGetState, kInput, kImplemented, kHighFrequency);
|
||||||
|
|
||||||
|
@ -160,14 +153,9 @@ dword_result_t XamInputGetKeystroke_entry(
|
||||||
return X_ERROR_BAD_ARGUMENTS;
|
return X_ERROR_BAD_ARGUMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
||||||
// Ignore any query for other types of devices.
|
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t actual_user_index = user_index;
|
uint32_t actual_user_index = user_index;
|
||||||
if ((actual_user_index & XUserIndexAny) == XUserIndexAny ||
|
if ((actual_user_index & XUserIndexAny) == XUserIndexAny ||
|
||||||
(flags & XINPUT_FLAG_ANY_USER)) {
|
(flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER)) {
|
||||||
// Always pin user to 0.
|
// Always pin user to 0.
|
||||||
actual_user_index = 0;
|
actual_user_index = 0;
|
||||||
}
|
}
|
||||||
|
@ -186,11 +174,6 @@ dword_result_t XamInputGetKeystrokeEx_entry(
|
||||||
return X_ERROR_BAD_ARGUMENTS;
|
return X_ERROR_BAD_ARGUMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
||||||
// Ignore any query for other types of devices.
|
|
||||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t user_index = *user_index_ptr;
|
uint32_t user_index = *user_index_ptr;
|
||||||
auto input_system = kernel_state()->emulator()->input_system();
|
auto input_system = kernel_state()->emulator()->input_system();
|
||||||
auto lock = input_system->lock();
|
auto lock = input_system->lock();
|
||||||
|
@ -199,7 +182,7 @@ dword_result_t XamInputGetKeystrokeEx_entry(
|
||||||
user_index = 0;
|
user_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & XINPUT_FLAG_ANY_USER) {
|
if (flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER) {
|
||||||
// That flag means we should iterate over every connected controller and
|
// That flag means we should iterate over every connected controller and
|
||||||
// check which one have pending request.
|
// check which one have pending request.
|
||||||
auto result = X_ERROR_DEVICE_NOT_CONNECTED;
|
auto result = X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
|
@ -224,9 +207,10 @@ dword_result_t XamInputGetKeystrokeEx_entry(
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamInputGetKeystrokeEx, kInput, kImplemented);
|
DECLARE_XAM_EXPORT1(XamInputGetKeystrokeEx, kInput, kImplemented);
|
||||||
|
|
||||||
X_HRESULT_result_t XamUserGetDeviceContext_entry(dword_t user_index,
|
X_HRESULT_result_t XamUserGetDeviceContext_entry(
|
||||||
dword_t unk,
|
dword_t user_index,
|
||||||
lpdword_t out_ptr) {
|
dword_t unk, // It's set to 3 for a big button
|
||||||
|
lpdword_t out_ptr) {
|
||||||
// Games check the result - usually with some masking.
|
// Games check the result - usually with some masking.
|
||||||
// If this function fails they assume zero, so let's fail AND
|
// If this function fails they assume zero, so let's fail AND
|
||||||
// set zero just to be safe.
|
// set zero just to be safe.
|
||||||
|
|
|
@ -111,14 +111,14 @@ void XamModule::SaveLoaderData() {
|
||||||
std::filesystem::path host_path = loader_data_.host_path;
|
std::filesystem::path host_path = loader_data_.host_path;
|
||||||
std::string launch_path = loader_data_.launch_path;
|
std::string launch_path = loader_data_.launch_path;
|
||||||
|
|
||||||
auto remove_prefix = [&launch_path](std::string& prefix) {
|
auto remove_prefix = [&launch_path](std::string_view prefix) {
|
||||||
if (launch_path.compare(0, prefix.length(), prefix) == 0) {
|
if (launch_path.compare(0, prefix.length(), prefix) == 0) {
|
||||||
launch_path = launch_path.substr(prefix.length());
|
launch_path = launch_path.substr(prefix.length());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
remove_prefix(std::string("game:\\"));
|
remove_prefix("game:\\");
|
||||||
remove_prefix(std::string("d:\\"));
|
remove_prefix("d:\\");
|
||||||
|
|
||||||
if (host_path.extension() == ".xex") {
|
if (host_path.extension() == ".xex") {
|
||||||
host_path.remove_filename();
|
host_path.remove_filename();
|
||||||
|
|
|
@ -24,6 +24,7 @@ XE_MODULE_EXPORT_GROUP(xam, Net)
|
||||||
XE_MODULE_EXPORT_GROUP(xam, Notify)
|
XE_MODULE_EXPORT_GROUP(xam, Notify)
|
||||||
XE_MODULE_EXPORT_GROUP(xam, NUI)
|
XE_MODULE_EXPORT_GROUP(xam, NUI)
|
||||||
XE_MODULE_EXPORT_GROUP(xam, Party)
|
XE_MODULE_EXPORT_GROUP(xam, Party)
|
||||||
|
XE_MODULE_EXPORT_GROUP(xam, Profile)
|
||||||
XE_MODULE_EXPORT_GROUP(xam, Task)
|
XE_MODULE_EXPORT_GROUP(xam, Task)
|
||||||
XE_MODULE_EXPORT_GROUP(xam, UI)
|
XE_MODULE_EXPORT_GROUP(xam, UI)
|
||||||
XE_MODULE_EXPORT_GROUP(xam, User)
|
XE_MODULE_EXPORT_GROUP(xam, User)
|
||||||
|
|
|
@ -722,7 +722,7 @@ dword_result_t NetDll_getsockopt_entry(dword_t caller, dword_t socket_handle,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int native_len = *optlen;
|
uint32_t native_len = *optlen;
|
||||||
X_STATUS status = socket->GetOption(level, optname, optval_ptr, &native_len);
|
X_STATUS status = socket->GetOption(level, optname, optval_ptr, &native_len);
|
||||||
return XSUCCEEDED(status) ? 0 : -1;
|
return XSUCCEEDED(status) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
@ -1083,8 +1083,8 @@ dword_result_t NetDll_XNetRegisterKey_entry(dword_t caller, lpdword_t key_id,
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(NetDll_XNetRegisterKey, kNetworking, kStub);
|
DECLARE_XAM_EXPORT1(NetDll_XNetRegisterKey, kNetworking, kStub);
|
||||||
|
|
||||||
dword_result_t NetDll_XNetUnregisterKey_entry(dword_t caller, lpdword_t key_id,
|
dword_result_t NetDll_XNetUnregisterKey_entry(dword_t caller,
|
||||||
lpdword_t exchange_key) {
|
lpdword_t key_id) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(NetDll_XNetUnregisterKey, kNetworking, kStub);
|
DECLARE_XAM_EXPORT1(NetDll_XNetUnregisterKey, kNetworking, kStub);
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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/kernel/kernel_state.h"
|
||||||
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
|
#include "xenia/kernel/xam/xam_private.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
namespace xam {
|
||||||
|
|
||||||
|
dword_result_t XamProfileFindAccount_entry(
|
||||||
|
qword_t offline_xuid, pointer_t<X_XAMACCOUNTINFO> account_ptr,
|
||||||
|
lpdword_t device_id) {
|
||||||
|
if (!account_ptr) {
|
||||||
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
account_ptr.Zero();
|
||||||
|
|
||||||
|
const auto& account =
|
||||||
|
kernel_state()->xam_state()->profile_manager()->GetAccount(offline_xuid);
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return X_ERROR_NO_SUCH_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(account_ptr, &account, sizeof(X_XAMACCOUNTINFO));
|
||||||
|
|
||||||
|
xe::string_util::copy_and_swap_truncating(
|
||||||
|
account_ptr->gamertag, account->gamertag, sizeof(account->gamertag));
|
||||||
|
|
||||||
|
if (device_id) {
|
||||||
|
*device_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamProfileFindAccount, kUserProfiles, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t XamProfileOpen_entry(qword_t xuid, lpstring_t mount_path,
|
||||||
|
dword_t flags, lpvoid_t content_data) {
|
||||||
|
/* Notes:
|
||||||
|
- If xuid is not local then returns X_ERROR_INVALID_PARAMETER
|
||||||
|
*/
|
||||||
|
const bool result =
|
||||||
|
kernel_state()->xam_state()->profile_manager()->MountProfile(
|
||||||
|
xuid, mount_path.value());
|
||||||
|
|
||||||
|
return result ? X_ERROR_SUCCESS : X_ERROR_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamProfileOpen, kNone, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t XamProfileCreate_entry(dword_t flags, lpdword_t device_id,
|
||||||
|
qword_t xuid,
|
||||||
|
pointer_t<X_XAMACCOUNTINFO> account,
|
||||||
|
dword_t unk1, dword_t unk2, dword_t unk3,
|
||||||
|
dword_t unk4) {
|
||||||
|
// **unk4
|
||||||
|
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamProfileCreate, kNone, kStub);
|
||||||
|
|
||||||
|
} // namespace xam
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Profile);
|
|
@ -85,7 +85,7 @@ X_RESULT xeXamDispatchDialog(T* dialog,
|
||||||
std::function<X_RESULT(T*)> close_callback,
|
std::function<X_RESULT(T*)> close_callback,
|
||||||
uint32_t overlapped) {
|
uint32_t overlapped) {
|
||||||
auto pre = []() {
|
auto pre = []() {
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||||
};
|
};
|
||||||
auto run = [dialog, close_callback]() -> X_RESULT {
|
auto run = [dialog, close_callback]() -> X_RESULT {
|
||||||
X_RESULT result;
|
X_RESULT result;
|
||||||
|
@ -108,7 +108,7 @@ X_RESULT xeXamDispatchDialog(T* dialog,
|
||||||
};
|
};
|
||||||
auto post = []() {
|
auto post = []() {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||||
};
|
};
|
||||||
if (!overlapped) {
|
if (!overlapped) {
|
||||||
pre();
|
pre();
|
||||||
|
@ -126,7 +126,7 @@ X_RESULT xeXamDispatchDialogEx(
|
||||||
T* dialog, std::function<X_RESULT(T*, uint32_t&, uint32_t&)> close_callback,
|
T* dialog, std::function<X_RESULT(T*, uint32_t&, uint32_t&)> close_callback,
|
||||||
uint32_t overlapped) {
|
uint32_t overlapped) {
|
||||||
auto pre = []() {
|
auto pre = []() {
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||||
};
|
};
|
||||||
auto run = [dialog, close_callback](uint32_t& extended_error,
|
auto run = [dialog, close_callback](uint32_t& extended_error,
|
||||||
uint32_t& length) -> X_RESULT {
|
uint32_t& length) -> X_RESULT {
|
||||||
|
@ -150,7 +150,7 @@ X_RESULT xeXamDispatchDialogEx(
|
||||||
};
|
};
|
||||||
auto post = []() {
|
auto post = []() {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||||
};
|
};
|
||||||
if (!overlapped) {
|
if (!overlapped) {
|
||||||
pre();
|
pre();
|
||||||
|
@ -168,11 +168,11 @@ X_RESULT xeXamDispatchDialogEx(
|
||||||
X_RESULT xeXamDispatchHeadless(std::function<X_RESULT()> run_callback,
|
X_RESULT xeXamDispatchHeadless(std::function<X_RESULT()> run_callback,
|
||||||
uint32_t overlapped) {
|
uint32_t overlapped) {
|
||||||
auto pre = []() {
|
auto pre = []() {
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||||
};
|
};
|
||||||
auto post = []() {
|
auto post = []() {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||||
};
|
};
|
||||||
if (!overlapped) {
|
if (!overlapped) {
|
||||||
pre();
|
pre();
|
||||||
|
@ -190,11 +190,11 @@ X_RESULT xeXamDispatchHeadlessEx(
|
||||||
std::function<X_RESULT(uint32_t&, uint32_t&)> run_callback,
|
std::function<X_RESULT(uint32_t&, uint32_t&)> run_callback,
|
||||||
uint32_t overlapped) {
|
uint32_t overlapped) {
|
||||||
auto pre = []() {
|
auto pre = []() {
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||||
};
|
};
|
||||||
auto post = []() {
|
auto post = []() {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||||
};
|
};
|
||||||
if (!overlapped) {
|
if (!overlapped) {
|
||||||
pre();
|
pre();
|
||||||
|
@ -213,7 +213,7 @@ X_RESULT xeXamDispatchHeadlessEx(
|
||||||
template <typename T>
|
template <typename T>
|
||||||
X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
||||||
std::function<void(T*)> close_callback) {
|
std::function<void(T*)> close_callback) {
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||||
++xam_dialogs_shown_;
|
++xam_dialogs_shown_;
|
||||||
|
|
||||||
// Important to pass captured vars by value here since we return from this
|
// Important to pass captured vars by value here since we return from this
|
||||||
|
@ -226,7 +226,7 @@ X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
||||||
|
|
||||||
auto run = []() -> void {
|
auto run = []() -> void {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread thread(run);
|
std::thread thread(run);
|
||||||
|
@ -237,7 +237,7 @@ X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
|
X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||||
++xam_dialogs_shown_;
|
++xam_dialogs_shown_;
|
||||||
|
|
||||||
auto display_window = kernel_state()->emulator()->display_window();
|
auto display_window = kernel_state()->emulator()->display_window();
|
||||||
|
@ -248,7 +248,7 @@ X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
|
||||||
|
|
||||||
auto run = []() -> void {
|
auto run = []() -> void {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread thread(run);
|
std::thread thread(run);
|
||||||
|
@ -320,6 +320,119 @@ class MessageBoxDialog : public XamDialog {
|
||||||
uint32_t chosen_button_ = 0;
|
uint32_t chosen_button_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ProfilePasscodeDialog : public XamDialog {
|
||||||
|
public:
|
||||||
|
const char* labelled_keys_[11] = {"None", "X", "Y", "RB", "LB", "LT",
|
||||||
|
"RT", "Up", "Down", "Left", "Right"};
|
||||||
|
|
||||||
|
const std::map<std::string, uint16_t> keys_map_ = {
|
||||||
|
{"None", 0},
|
||||||
|
{"X", X_BUTTON_PASSCODE},
|
||||||
|
{"Y", Y_BUTTON_PASSCODE},
|
||||||
|
{"RB", RIGHT_BUMPER_PASSCODE},
|
||||||
|
{"LB", LEFT_BUMPER_PASSCODE},
|
||||||
|
{"LT", LEFT_TRIGGER_PASSCODE},
|
||||||
|
{"RT", RIGHT_TRIGGER_PASSCODE},
|
||||||
|
{"Up", DPAD_UP_PASSCODE},
|
||||||
|
{"Down", DPAD_DOWN_PASSCODE},
|
||||||
|
{"Left", DPAD_LEFT_PASSCODE},
|
||||||
|
{"Right", DPAD_RIGHT_PASSCODE}};
|
||||||
|
|
||||||
|
ProfilePasscodeDialog(xe::ui::ImGuiDrawer* imgui_drawer, std::string& title,
|
||||||
|
std::string& description, MESSAGEBOX_RESULT* result_ptr)
|
||||||
|
: XamDialog(imgui_drawer),
|
||||||
|
title_(title),
|
||||||
|
description_(description),
|
||||||
|
result_ptr_(result_ptr) {
|
||||||
|
std::memset(result_ptr, 0, sizeof(MESSAGEBOX_RESULT));
|
||||||
|
|
||||||
|
if (title_.empty()) {
|
||||||
|
title_ = "Enter Pass Code";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description_.empty()) {
|
||||||
|
description_ = "Enter your Xbox LIVE pass code.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawPasscodeField(uint8_t key_id) {
|
||||||
|
const std::string label = fmt::format("##Key {}", key_id);
|
||||||
|
|
||||||
|
if (ImGui::BeginCombo(label.c_str(),
|
||||||
|
labelled_keys_[key_indexes_[key_id]])) {
|
||||||
|
for (uint8_t key_index = 0; key_index < keys_map_.size(); key_index++) {
|
||||||
|
bool is_selected = key_id == key_index;
|
||||||
|
|
||||||
|
if (ImGui::Selectable(labelled_keys_[key_index], is_selected)) {
|
||||||
|
key_indexes_[key_id] = key_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDraw(ImGuiIO& io) override {
|
||||||
|
if (!has_opened_) {
|
||||||
|
ImGui::OpenPopup(title_.c_str());
|
||||||
|
has_opened_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupModal(title_.c_str(), nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
if (description_.size()) {
|
||||||
|
ImGui::Text("%s", description_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < passcode_length; i++) {
|
||||||
|
DrawPasscodeField(i);
|
||||||
|
// result_ptr_->Passcode[i] =
|
||||||
|
// keys_map_.at(labelled_keys_[key_indexes_[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
// We write each key on close to prevent simultaneous dialogs.
|
||||||
|
if (ImGui::Button("Sign In")) {
|
||||||
|
for (uint8_t i = 0; i < passcode_length; i++) {
|
||||||
|
result_ptr_->Passcode[i] =
|
||||||
|
keys_map_.at(labelled_keys_[key_indexes_[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
selected_signed_in_ = true;
|
||||||
|
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button("Cancel")) {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ProfilePasscodeDialog() {}
|
||||||
|
|
||||||
|
bool SelectedSignedIn() const { return selected_signed_in_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool has_opened_ = false;
|
||||||
|
bool selected_signed_in_ = false;
|
||||||
|
std::string title_;
|
||||||
|
std::string description_;
|
||||||
|
|
||||||
|
static const uint8_t passcode_length = sizeof(X_XAMACCOUNTINFO::passcode);
|
||||||
|
int key_indexes_[passcode_length] = {0, 0, 0, 0};
|
||||||
|
MESSAGEBOX_RESULT* result_ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
class GamertagModifyDialog final : public ui::ImGuiDialog {
|
class GamertagModifyDialog final : public ui::ImGuiDialog {
|
||||||
public:
|
public:
|
||||||
GamertagModifyDialog(ui::ImGuiDrawer* imgui_drawer,
|
GamertagModifyDialog(ui::ImGuiDrawer* imgui_drawer,
|
||||||
|
@ -394,7 +507,7 @@ struct AchievementInfo {
|
||||||
uint32_t gamerscore;
|
uint32_t gamerscore;
|
||||||
uint32_t image_id;
|
uint32_t image_id;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
std::chrono::system_clock::time_point unlock_time;
|
std::chrono::local_time<std::chrono::system_clock::duration> unlock_time;
|
||||||
|
|
||||||
bool IsUnlocked() const {
|
bool IsUnlocked() const {
|
||||||
return (flags & static_cast<uint32_t>(AchievementFlags::kAchieved)) ||
|
return (flags & static_cast<uint32_t>(AchievementFlags::kAchieved)) ||
|
||||||
|
@ -461,12 +574,8 @@ class GameAchievementsDialog final : public XamDialog {
|
||||||
info.unlock_time = {};
|
info.unlock_time = {};
|
||||||
|
|
||||||
if (entry.IsUnlocked()) {
|
if (entry.IsUnlocked()) {
|
||||||
const uint64_t filetime_time =
|
info.unlock_time =
|
||||||
(static_cast<uint64_t>(entry.unlock_time.high_part) << 32) |
|
chrono::WinSystemClock::to_local(entry.unlock_time.to_time_point());
|
||||||
entry.unlock_time.low_part;
|
|
||||||
|
|
||||||
info.unlock_time = chrono::WinSystemClock::to_sys(
|
|
||||||
chrono::WinSystemClock::from_file_time(filetime_time));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
achievements_info_.insert({info.id, info});
|
achievements_info_.insert({info.id, info});
|
||||||
|
@ -533,14 +642,12 @@ class GameAchievementsDialog final : public XamDialog {
|
||||||
|
|
||||||
ImGui::PushFont(imgui_drawer()->GetTitleFont());
|
ImGui::PushFont(imgui_drawer()->GetTitleFont());
|
||||||
const auto primary_line_height = ImGui::GetTextLineHeight();
|
const auto primary_line_height = ImGui::GetTextLineHeight();
|
||||||
ImGui::TextUnformatted(
|
ImGui::Text("%s", GetAchievementTitle(achievement_entry).c_str());
|
||||||
fmt::format("{}", GetAchievementTitle(achievement_entry)).c_str());
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
ImGui::PushTextWrapPos(ImGui::GetMainViewport()->Size.x * 0.5f);
|
ImGui::PushTextWrapPos(ImGui::GetMainViewport()->Size.x * 0.5f);
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped("%s",
|
||||||
fmt::format("{}", GetAchievementDescription(achievement_entry))
|
GetAchievementDescription(achievement_entry).c_str());
|
||||||
.c_str());
|
|
||||||
ImGui::PopTextWrapPos();
|
ImGui::PopTextWrapPos();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(start_drawing_pos.y + default_image_icon_size.x -
|
ImGui::SetCursorPosY(start_drawing_pos.y + default_image_icon_size.x -
|
||||||
|
@ -763,7 +870,7 @@ class GamesInfoDialog final : public ui::ImGuiDialog {
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + textOffsetX);
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + textOffsetX);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text(no_entries_message.c_str());
|
ImGui::Text("%s", no_entries_message.c_str());
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,13 +894,10 @@ class GamesInfoDialog final : public ui::ImGuiDialog {
|
||||||
static dword_result_t XamShowMessageBoxUi(
|
static dword_result_t XamShowMessageBoxUi(
|
||||||
dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr,
|
dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr,
|
||||||
dword_t button_count, lpdword_t button_ptrs, dword_t active_button,
|
dword_t button_count, lpdword_t button_ptrs, dword_t active_button,
|
||||||
dword_t flags, lpdword_t result_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
|
dword_t flags, pointer_t<MESSAGEBOX_RESULT> result_ptr,
|
||||||
std::string title;
|
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||||
if (title_ptr) {
|
std::string title = title_ptr ? xe::to_utf8(title_ptr.value()) : "";
|
||||||
title = xe::to_utf8(title_ptr.value());
|
std::string text = text_ptr ? xe::to_utf8(text_ptr.value()) : "";
|
||||||
} else {
|
|
||||||
title = ""; // TODO(gibbed): default title based on flags?
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> buttons;
|
std::vector<std::string> buttons;
|
||||||
for (uint32_t i = 0; i < button_count; ++i) {
|
for (uint32_t i = 0; i < button_count; ++i) {
|
||||||
|
@ -807,37 +911,53 @@ static dword_result_t XamShowMessageBoxUi(
|
||||||
if (cvars::headless) {
|
if (cvars::headless) {
|
||||||
// Auto-pick the focused button.
|
// Auto-pick the focused button.
|
||||||
auto run = [result_ptr, active_button]() -> X_RESULT {
|
auto run = [result_ptr, active_button]() -> X_RESULT {
|
||||||
*result_ptr = static_cast<uint32_t>(active_button);
|
result_ptr->ButtonPressed = static_cast<uint32_t>(active_button);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
};
|
};
|
||||||
|
|
||||||
result = xeXamDispatchHeadless(run, overlapped);
|
result = xeXamDispatchHeadless(run, overlapped);
|
||||||
} else {
|
} else {
|
||||||
// TODO(benvanik): setup icon states.
|
|
||||||
switch (flags & 0xF) {
|
switch (flags & 0xF) {
|
||||||
case 0:
|
case XMBox_NOICON: {
|
||||||
// config.pszMainIcon = nullptr;
|
} break;
|
||||||
break;
|
case XMBox_ERRORICON: {
|
||||||
case 1:
|
} break;
|
||||||
// config.pszMainIcon = TD_ERROR_ICON;
|
case XMBox_WARNINGICON: {
|
||||||
break;
|
} break;
|
||||||
case 2:
|
case XMBox_ALERTICON: {
|
||||||
// config.pszMainIcon = TD_WARNING_ICON;
|
} break;
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// config.pszMainIcon = TD_INFORMATION_ICON;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
auto close = [result_ptr](MessageBoxDialog* dialog) -> X_RESULT {
|
|
||||||
*result_ptr = dialog->chosen_button();
|
|
||||||
return X_ERROR_SUCCESS;
|
|
||||||
};
|
|
||||||
const Emulator* emulator = kernel_state()->emulator();
|
const Emulator* emulator = kernel_state()->emulator();
|
||||||
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
||||||
result = xeXamDispatchDialog<MessageBoxDialog>(
|
|
||||||
new MessageBoxDialog(imgui_drawer, title, xe::to_utf8(text_ptr.value()),
|
if (flags & XMBox_PASSCODEMODE || flags & XMBox_VERIFYPASSCODEMODE) {
|
||||||
buttons, active_button),
|
auto close = [result_ptr,
|
||||||
close, overlapped);
|
active_button](ProfilePasscodeDialog* dialog) -> X_RESULT {
|
||||||
|
if (dialog->SelectedSignedIn()) {
|
||||||
|
// Logged in
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return X_ERROR_FUNCTION_FAILED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
result = xeXamDispatchDialog<ProfilePasscodeDialog>(
|
||||||
|
new ProfilePasscodeDialog(imgui_drawer, title, text, result_ptr),
|
||||||
|
close, overlapped);
|
||||||
|
} else {
|
||||||
|
auto close = [result_ptr](MessageBoxDialog* dialog) -> X_RESULT {
|
||||||
|
result_ptr->ButtonPressed = dialog->chosen_button();
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
result = xeXamDispatchDialog<MessageBoxDialog>(
|
||||||
|
new MessageBoxDialog(imgui_drawer, title, text, buttons,
|
||||||
|
static_cast<uint32_t>(active_button)),
|
||||||
|
close, overlapped);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,7 +965,8 @@ static dword_result_t XamShowMessageBoxUi(
|
||||||
dword_result_t XamShowMessageBoxUI_entry(
|
dword_result_t XamShowMessageBoxUI_entry(
|
||||||
dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr,
|
dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr,
|
||||||
dword_t button_count, lpdword_t button_ptrs, dword_t active_button,
|
dword_t button_count, lpdword_t button_ptrs, dword_t active_button,
|
||||||
dword_t flags, lpdword_t result_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
|
dword_t flags, pointer_t<MESSAGEBOX_RESULT> result_ptr,
|
||||||
|
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||||
return XamShowMessageBoxUi(user_index, title_ptr, text_ptr, button_count,
|
return XamShowMessageBoxUi(user_index, title_ptr, text_ptr, button_count,
|
||||||
button_ptrs, active_button, flags, result_ptr,
|
button_ptrs, active_button, flags, result_ptr,
|
||||||
overlapped);
|
overlapped);
|
||||||
|
@ -855,7 +976,8 @@ DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented);
|
||||||
dword_result_t XamShowMessageBoxUIEx_entry(
|
dword_result_t XamShowMessageBoxUIEx_entry(
|
||||||
dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr,
|
dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr,
|
||||||
dword_t button_count, lpdword_t button_ptrs, dword_t active_button,
|
dword_t button_count, lpdword_t button_ptrs, dword_t active_button,
|
||||||
dword_t flags, dword_t unknown_unused, lpdword_t result_ptr,
|
dword_t flags, dword_t unknown_unused,
|
||||||
|
pointer_t<MESSAGEBOX_RESULT> result_ptr,
|
||||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||||
return XamShowMessageBoxUi(user_index, title_ptr, text_ptr, button_count,
|
return XamShowMessageBoxUi(user_index, title_ptr, text_ptr, button_count,
|
||||||
button_ptrs, active_button, flags, result_ptr,
|
button_ptrs, active_button, flags, result_ptr,
|
||||||
|
@ -1016,12 +1138,15 @@ dword_result_t XamShowKeyboardUI_entry(
|
||||||
};
|
};
|
||||||
const Emulator* emulator = kernel_state()->emulator();
|
const Emulator* emulator = kernel_state()->emulator();
|
||||||
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
||||||
|
|
||||||
|
std::string title_str = title ? xe::to_utf8(title.value()) : "";
|
||||||
|
std::string desc_str = description ? xe::to_utf8(description.value()) : "";
|
||||||
|
std::string def_text_str =
|
||||||
|
default_text ? xe::to_utf8(default_text.value()) : "";
|
||||||
|
|
||||||
result = xeXamDispatchDialogEx<KeyboardInputDialog>(
|
result = xeXamDispatchDialogEx<KeyboardInputDialog>(
|
||||||
new KeyboardInputDialog(
|
new KeyboardInputDialog(imgui_drawer, title_str, desc_str, def_text_str,
|
||||||
imgui_drawer, title ? xe::to_utf8(title.value()) : "",
|
buffer_length),
|
||||||
description ? xe::to_utf8(description.value()) : "",
|
|
||||||
default_text ? xe::to_utf8(default_text.value()) : "",
|
|
||||||
buffer_length),
|
|
||||||
close, overlapped);
|
close, overlapped);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1128,6 +1253,8 @@ DECLARE_XAM_EXPORT1(XamShowCommunitySessionsUI, kNone, kStub);
|
||||||
dword_result_t XamSetDashContext_entry(dword_t value,
|
dword_result_t XamSetDashContext_entry(dword_t value,
|
||||||
const ppc_context_t& ctx) {
|
const ppc_context_t& ctx) {
|
||||||
ctx->kernel_state->dash_context_ = value;
|
ctx->kernel_state->dash_context_ = value;
|
||||||
|
kernel_state()->BroadcastNotification(
|
||||||
|
kXNotificationDvdDriveUnknownDashContext, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1139,9 +1266,11 @@ dword_result_t XamGetDashContext_entry(const ppc_context_t& ctx) {
|
||||||
|
|
||||||
DECLARE_XAM_EXPORT1(XamGetDashContext, kNone, kImplemented);
|
DECLARE_XAM_EXPORT1(XamGetDashContext, kNone, kImplemented);
|
||||||
|
|
||||||
dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
// https://gitlab.com/GlitchyScripts/xlivelessness/-/blob/master/xlivelessness/xlive/xdefs.hpp?ref_type=heads#L1235
|
||||||
qword_t offer_id,
|
X_HRESULT xeXShowMarketplaceUIEx(dword_t user_index, dword_t ui_type,
|
||||||
dword_t content_types) {
|
qword_t offer_id, dword_t content_types,
|
||||||
|
unknown_t unk5, unknown_t unk6, unknown_t unk7,
|
||||||
|
unknown_t unk8) {
|
||||||
// ui_type:
|
// ui_type:
|
||||||
// 0 - view all content for the current title
|
// 0 - view all content for the current title
|
||||||
// 1 - view content specified by offer id
|
// 1 - view content specified by offer id
|
||||||
|
@ -1166,7 +1295,7 @@ dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||||
cvars::license_mask = 1;
|
cvars::license_mask = 1;
|
||||||
|
|
||||||
kernel_state()->BroadcastNotification(
|
kernel_state()->BroadcastNotification(
|
||||||
kXNotificationIDLiveContentInstalled, 0);
|
kXNotificationLiveContentInstalled, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1176,15 +1305,59 @@ dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||||
cxxopts::OptionNames buttons;
|
cxxopts::OptionNames buttons;
|
||||||
|
|
||||||
switch (ui_type) {
|
switch (ui_type) {
|
||||||
case 0:
|
case X_MARKETPLACE_ENTRYPOINT::ContentList:
|
||||||
desc =
|
desc =
|
||||||
"Game requested to open marketplace page with all content for the "
|
"Game requested to open marketplace page with all content for the "
|
||||||
"current title ID.";
|
"current title ID.";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case X_MARKETPLACE_ENTRYPOINT::ContentItem:
|
||||||
desc = fmt::format(
|
desc = fmt::format(
|
||||||
"Game requested to open marketplace page for offer ID 0x{:016X}.",
|
"Game requested to open marketplace page for offer ID 0x{:016X}.",
|
||||||
static_cast<uint32_t>(offer_id));
|
static_cast<uint64_t>(offer_id));
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::MembershipList:
|
||||||
|
desc = fmt::format(
|
||||||
|
"Game requested to open marketplace page with all xbox live "
|
||||||
|
"memberships 0x{:016X}.",
|
||||||
|
static_cast<uint64_t>(offer_id));
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::MembershipItem:
|
||||||
|
desc = fmt::format(
|
||||||
|
"Game requested to open marketplace page for an xbox live "
|
||||||
|
"memberships 0x{:016X}.",
|
||||||
|
static_cast<uint64_t>(offer_id));
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::ContentList_Background:
|
||||||
|
// Used when accessing microsoft points
|
||||||
|
desc = fmt::format(
|
||||||
|
"Xbox Marketplace requested access to Microsoft Points offer page "
|
||||||
|
"0x{:016X}.",
|
||||||
|
static_cast<uint64_t>(offer_id));
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::ContentItem_Background:
|
||||||
|
// Used when accessing credit card information and calls
|
||||||
|
// XamShowCreditCardUI
|
||||||
|
desc = fmt::format(
|
||||||
|
"Xbox Marketplace requested access to credit card information page "
|
||||||
|
"0x{:016X}.",
|
||||||
|
static_cast<uint64_t>(offer_id));
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::ForcedNameChangeV1:
|
||||||
|
// Used by XamShowForcedNameChangeUI v1888
|
||||||
|
desc = fmt::format("Changing gamertag currently not implemented");
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::ForcedNameChangeV2:
|
||||||
|
// Used by XamShowForcedNameChangeUI NXE and up
|
||||||
|
desc = fmt::format("Changing gamertag currently not implemented");
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::ProfileNameChange:
|
||||||
|
// Used by dashboard when selecting change gamertag in profile menu
|
||||||
|
desc = fmt::format("Changing gamertag currently not implemented");
|
||||||
|
break;
|
||||||
|
case X_MARKETPLACE_ENTRYPOINT::ActiveDownloads:
|
||||||
|
// Used in profile tabs when clicking active downloads
|
||||||
|
desc = fmt::format(
|
||||||
|
"There are no current plans to download files from xbox servers");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
desc = fmt::format("Unknown marketplace op {:d}",
|
desc = fmt::format("Unknown marketplace op {:d}",
|
||||||
|
@ -1197,11 +1370,11 @@ dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||||
"installed manually using File -> Install Content.";
|
"installed manually using File -> Install Content.";
|
||||||
|
|
||||||
switch (ui_type) {
|
switch (ui_type) {
|
||||||
case 0:
|
case X_MARKETPLACE_ENTRYPOINT::ContentList:
|
||||||
default:
|
default:
|
||||||
buttons.push_back("OK");
|
buttons.push_back("OK");
|
||||||
break;
|
break;
|
||||||
case 1:
|
case X_MARKETPLACE_ENTRYPOINT::ContentItem:
|
||||||
desc +=
|
desc +=
|
||||||
"\n\nTo start trial games in full mode, set license_mask to 1 in "
|
"\n\nTo start trial games in full mode, set license_mask to 1 in "
|
||||||
"Xenia config file.\n\nDo you wish to change license_mask to 1 for "
|
"Xenia config file.\n\nDo you wish to change license_mask to 1 for "
|
||||||
|
@ -1216,8 +1389,26 @@ dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||||
return xeXamDispatchDialogAsync<MessageBoxDialog>(
|
return xeXamDispatchDialogAsync<MessageBoxDialog>(
|
||||||
new MessageBoxDialog(imgui_drawer, title, desc, buttons, 0), close);
|
new MessageBoxDialog(imgui_drawer, title, desc, buttons, 0), close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||||
|
qword_t offer_id,
|
||||||
|
dword_t content_types, unknown_t unk5,
|
||||||
|
unknown_t unk6) {
|
||||||
|
return xeXShowMarketplaceUIEx(user_index, ui_type, offer_id, content_types,
|
||||||
|
unk5, 0, 0, unk6);
|
||||||
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamShowMarketplaceUI, kUI, kSketchy);
|
DECLARE_XAM_EXPORT1(XamShowMarketplaceUI, kUI, kSketchy);
|
||||||
|
|
||||||
|
dword_result_t XamShowMarketplaceUIEx_entry(dword_t user_index, dword_t ui_type,
|
||||||
|
qword_t offer_id,
|
||||||
|
dword_t content_types,
|
||||||
|
unknown_t unk5, unknown_t unk6,
|
||||||
|
unknown_t unk7, unknown_t unk8) {
|
||||||
|
return xeXShowMarketplaceUIEx(user_index, ui_type, offer_id, content_types,
|
||||||
|
unk5, unk6, unk7, unk8);
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamShowMarketplaceUIEx, kUI, kSketchy);
|
||||||
|
|
||||||
dword_result_t XamShowMarketplaceDownloadItemsUI_entry(
|
dword_result_t XamShowMarketplaceDownloadItemsUI_entry(
|
||||||
dword_t user_index, dword_t ui_type, lpqword_t offers, dword_t num_offers,
|
dword_t user_index, dword_t ui_type, lpqword_t offers, dword_t num_offers,
|
||||||
lpdword_t hresult_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
|
lpdword_t hresult_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||||
|
@ -1291,6 +1482,12 @@ dword_result_t XamShowMarketplaceDownloadItemsUI_entry(
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamShowMarketplaceDownloadItemsUI, kUI, kSketchy);
|
DECLARE_XAM_EXPORT1(XamShowMarketplaceDownloadItemsUI, kUI, kSketchy);
|
||||||
|
|
||||||
|
dword_result_t XamShowForcedNameChangeUI_entry(dword_t user_index) {
|
||||||
|
// Changes from 6 to 8 past NXE
|
||||||
|
return xeXShowMarketplaceUIEx(user_index, 6, 0, 0xffffffff, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamShowForcedNameChangeUI, kUI, kImplemented);
|
||||||
|
|
||||||
bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid,
|
bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid,
|
||||||
const uint8_t user_index,
|
const uint8_t user_index,
|
||||||
const X_XAMACCOUNTINFO* account,
|
const X_XAMACCOUNTINFO* account,
|
||||||
|
@ -1376,10 +1573,13 @@ bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid,
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
const bool is_signedin = profile_manager->GetProfile(xuid) != nullptr;
|
||||||
|
ImGui::BeginDisabled(!is_signedin);
|
||||||
if (ImGui::MenuItem("Show Achievements")) {
|
if (ImGui::MenuItem("Show Achievements")) {
|
||||||
new GamesInfoDialog(imgui_drawer, next_window_position,
|
new GamesInfoDialog(imgui_drawer, next_window_position,
|
||||||
profile_manager->GetProfile(user_index));
|
profile_manager->GetProfile(user_index));
|
||||||
}
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
if (ImGui::MenuItem("Show Content Directory")) {
|
if (ImGui::MenuItem("Show Content Directory")) {
|
||||||
const auto path = profile_manager->GetProfileContentPath(
|
const auto path = profile_manager->GetProfileContentPath(
|
||||||
|
|
|
@ -139,28 +139,36 @@ X_HRESULT_result_t XamUserGetSigninInfo_entry(
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserGetSigninInfo, kUserProfiles, kImplemented);
|
DECLARE_XAM_EXPORT1(XamUserGetSigninInfo, kUserProfiles, kImplemented);
|
||||||
|
|
||||||
dword_result_t XamUserGetName_entry(dword_t user_index, lpstring_t buffer,
|
dword_result_t XamUserGetName_entry(dword_t user_index, dword_t buffer,
|
||||||
dword_t buffer_len) {
|
dword_t buffer_len) {
|
||||||
if (user_index >= XUserMaxUserCount) {
|
if (user_index >= XUserMaxUserCount) {
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile =
|
// Based on XAM only first byte is cleared in case of lack of user.
|
||||||
kernel_state()->xam_state()->GetUserProfile(user_index);
|
kernel_memory()->Zero(buffer, 1);
|
||||||
const auto& user_name = user_profile->name();
|
|
||||||
xe::string_util::copy_truncating(
|
|
||||||
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
|
||||||
} else {
|
|
||||||
*buffer = 0;
|
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
|
|
||||||
|
// Because name is always limited to 15 characters we can assume length will
|
||||||
|
// never exceed that limit.
|
||||||
|
const auto& user_name = user_profile->name();
|
||||||
|
|
||||||
|
// buffer_len includes null-terminator. user_name does not.
|
||||||
|
const uint32_t bytes_to_copy = std::min(
|
||||||
|
buffer_len.value(), static_cast<uint32_t>(user_name.length()) + 1);
|
||||||
|
|
||||||
|
char* str_buffer = kernel_memory()->TranslateVirtual<char*>(buffer);
|
||||||
|
xe::string_util::copy_truncating(str_buffer, user_name, bytes_to_copy);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserGetName, kUserProfiles, kImplemented);
|
DECLARE_XAM_EXPORT1(XamUserGetName, kUserProfiles, kImplemented);
|
||||||
|
|
||||||
dword_result_t XamUserGetGamerTag_entry(dword_t user_index,
|
dword_result_t XamUserGetGamerTag_entry(dword_t user_index, dword_t buffer,
|
||||||
lpu16string_t buffer,
|
|
||||||
dword_t buffer_len) {
|
dword_t buffer_len) {
|
||||||
if (!buffer || buffer_len < 16) {
|
if (!buffer || buffer_len < 16) {
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
|
@ -177,8 +185,11 @@ dword_result_t XamUserGetGamerTag_entry(dword_t user_index,
|
||||||
const auto& user_profile =
|
const auto& user_profile =
|
||||||
kernel_state()->xam_state()->GetUserProfile(user_index);
|
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
auto user_name = xe::to_utf16(user_profile->name());
|
auto user_name = xe::to_utf16(user_profile->name());
|
||||||
|
|
||||||
|
char16_t* str_buffer = kernel_memory()->TranslateVirtual<char16_t*>(buffer);
|
||||||
|
|
||||||
xe::string_util::copy_and_swap_truncating(
|
xe::string_util::copy_and_swap_truncating(
|
||||||
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
str_buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
||||||
return X_E_SUCCESS;
|
return X_E_SUCCESS;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserGetGamerTag, kUserProfiles, kImplemented);
|
DECLARE_XAM_EXPORT1(XamUserGetGamerTag, kUserProfiles, kImplemented);
|
||||||
|
@ -620,7 +631,8 @@ dword_result_t XamUserCreateAchievementEnumerator_entry(
|
||||||
}
|
}
|
||||||
|
|
||||||
const util::XdbfGameData db = kernel_state()->title_xdbf();
|
const util::XdbfGameData db = kernel_state()->title_xdbf();
|
||||||
uint32_t title_id_ = title_id ? title_id : kernel_state()->title_id();
|
uint32_t title_id_ =
|
||||||
|
title_id ? static_cast<uint32_t>(title_id) : kernel_state()->title_id();
|
||||||
|
|
||||||
const auto user_title_achievements =
|
const auto user_title_achievements =
|
||||||
kernel_state()->achievement_manager()->GetTitleAchievements(
|
kernel_state()->achievement_manager()->GetTitleAchievements(
|
||||||
|
@ -787,6 +799,18 @@ dword_result_t XamUserGetUserFlagsFromXUID_entry(qword_t xuid) {
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserGetUserFlagsFromXUID, kUserProfiles, kImplemented);
|
DECLARE_XAM_EXPORT1(XamUserGetUserFlagsFromXUID, kUserProfiles, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t XamUserGetOnlineLanguageFromXUID_entry(qword_t xuid) {
|
||||||
|
/* Notes:
|
||||||
|
- Calls XamUserGetUserFlagsFromXUID and returns (ulonglong)(cached_flag <<
|
||||||
|
0x20) >> 0x39 & 0x1f;
|
||||||
|
- XamUserGetMembershipTierFromXUID and XamUserGetOnlineCountryFromXUID also
|
||||||
|
call it
|
||||||
|
- Removed in metro
|
||||||
|
*/
|
||||||
|
return cvars::user_language;
|
||||||
|
}
|
||||||
|
DECLARE_XAM_EXPORT1(XamUserGetOnlineLanguageFromXUID, kUserProfiles, kStub);
|
||||||
|
|
||||||
constexpr uint8_t kStatsMaxAmount = 64;
|
constexpr uint8_t kStatsMaxAmount = 64;
|
||||||
|
|
||||||
struct X_STATS_DETAILS {
|
struct X_STATS_DETAILS {
|
||||||
|
@ -832,35 +856,6 @@ dword_result_t XamUserCreateStatsEnumerator_entry(
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserCreateStatsEnumerator, kUserProfiles, kSketchy);
|
DECLARE_XAM_EXPORT1(XamUserCreateStatsEnumerator, kUserProfiles, kSketchy);
|
||||||
|
|
||||||
dword_result_t XamProfileFindAccount_entry(
|
|
||||||
qword_t offline_xuid, pointer_t<X_XAMACCOUNTINFO> account_ptr,
|
|
||||||
lpdword_t device_id) {
|
|
||||||
if (!account_ptr) {
|
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
account_ptr.Zero();
|
|
||||||
|
|
||||||
const auto& account =
|
|
||||||
kernel_state()->xam_state()->profile_manager()->GetAccount(offline_xuid);
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
return X_ERROR_NO_SUCH_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(account_ptr, &account, sizeof(X_XAMACCOUNTINFO));
|
|
||||||
|
|
||||||
xe::string_util::copy_and_swap_truncating(
|
|
||||||
account_ptr->gamertag, account->gamertag, sizeof(account->gamertag));
|
|
||||||
|
|
||||||
if (device_id) {
|
|
||||||
*device_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return X_ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
DECLARE_XAM_EXPORT1(XamProfileFindAccount, kUserProfiles, kImplemented);
|
|
||||||
|
|
||||||
} // namespace xam
|
} // namespace xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -171,6 +171,88 @@ dword_result_t DmGetSystemInfo_entry(pointer_t<XBDM_SYSTEM_INFO> info) {
|
||||||
}
|
}
|
||||||
DECLARE_XBDM_EXPORT1(DmGetSystemInfo, kDebug, kStub);
|
DECLARE_XBDM_EXPORT1(DmGetSystemInfo, kDebug, kStub);
|
||||||
|
|
||||||
|
dword_result_t DmSetMemory_entry(lpvoid_t dest_ptr, dword_t buf_size,
|
||||||
|
lpvoid_t src_ptr, lpdword_t bytes_written) {
|
||||||
|
if (!dest_ptr || !src_ptr || !buf_size) {
|
||||||
|
return XBDM_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_written) {
|
||||||
|
*bytes_written = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t dest_guest_address = dest_ptr.guest_address();
|
||||||
|
const uint32_t dest_guest_high_address = dest_guest_address + buf_size;
|
||||||
|
|
||||||
|
memory::PageAccess access = memory::PageAccess::kNoAccess;
|
||||||
|
BaseHeap* dest_heap_ptr =
|
||||||
|
kernel_state()->memory()->LookupHeap(dest_guest_address);
|
||||||
|
|
||||||
|
if (!dest_heap_ptr) {
|
||||||
|
return XBDM_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
access = dest_heap_ptr->QueryRangeAccess(dest_guest_address,
|
||||||
|
dest_guest_high_address);
|
||||||
|
|
||||||
|
if (access == memory::PageAccess::kReadWrite) {
|
||||||
|
memcpy(dest_ptr, src_ptr, buf_size);
|
||||||
|
|
||||||
|
if (bytes_written) {
|
||||||
|
*bytes_written = static_cast<uint32_t>(buf_size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XELOGE("DmSetMemory failed with page access {}",
|
||||||
|
static_cast<uint32_t>(access));
|
||||||
|
|
||||||
|
return XBDM_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XBDM_SUCCESSFUL;
|
||||||
|
}
|
||||||
|
DECLARE_XBDM_EXPORT1(DmSetMemory, kDebug, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t DmGetMemory_entry(lpvoid_t src_ptr, dword_t buf_size,
|
||||||
|
lpvoid_t dest_ptr, lpdword_t bytes_written) {
|
||||||
|
if (!dest_ptr || !src_ptr || !buf_size) {
|
||||||
|
return XBDM_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_written) {
|
||||||
|
*bytes_written = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t dest_guest_address = dest_ptr.guest_address();
|
||||||
|
const uint32_t dest_guest_high_address = dest_guest_address + buf_size;
|
||||||
|
|
||||||
|
memory::PageAccess access = memory::PageAccess::kNoAccess;
|
||||||
|
BaseHeap* dest_heap_ptr =
|
||||||
|
kernel_state()->memory()->LookupHeap(dest_guest_address);
|
||||||
|
|
||||||
|
if (!dest_heap_ptr) {
|
||||||
|
return XBDM_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
access = dest_heap_ptr->QueryRangeAccess(dest_guest_address,
|
||||||
|
dest_guest_high_address);
|
||||||
|
|
||||||
|
if (access == memory::PageAccess::kReadWrite) {
|
||||||
|
memcpy(dest_ptr, src_ptr, buf_size);
|
||||||
|
|
||||||
|
if (bytes_written) {
|
||||||
|
*bytes_written = static_cast<uint32_t>(buf_size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XELOGE("DmGetMemory failed with page access {}",
|
||||||
|
static_cast<uint32_t>(access));
|
||||||
|
|
||||||
|
return XBDM_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XBDM_SUCCESSFUL;
|
||||||
|
}
|
||||||
|
DECLARE_XBDM_EXPORT1(DmGetMemory, kDebug, kImplemented);
|
||||||
|
|
||||||
dword_result_t DmIsFastCAPEnabled_entry() { return XBDM_UNSUCCESSFUL; }
|
dword_result_t DmIsFastCAPEnabled_entry() { return XBDM_UNSUCCESSFUL; }
|
||||||
DECLARE_XBDM_EXPORT1(DmIsFastCAPEnabled, kDebug, kStub);
|
DECLARE_XBDM_EXPORT1(DmIsFastCAPEnabled, kDebug, kStub);
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ dword_result_t NtCreateFile_entry(lpdword_t handle_out, dword_t desired_access,
|
||||||
vfs::Entry* root_entry = nullptr;
|
vfs::Entry* root_entry = nullptr;
|
||||||
|
|
||||||
// Compute path, possibly attrs relative.
|
// Compute path, possibly attrs relative.
|
||||||
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);
|
auto target_path = util::TranslateAnsiPath(kernel_memory(), object_name);
|
||||||
|
|
||||||
// Enforce that the path is ASCII.
|
// Enforce that the path is ASCII.
|
||||||
if (!IsValidPath(target_path, false)) {
|
if (!IsValidPath(target_path, false)) {
|
||||||
|
@ -462,7 +462,7 @@ dword_result_t NtQueryFullAttributesFile_entry(
|
||||||
assert_always();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);
|
auto target_path = util::TranslateAnsiPath(kernel_memory(), object_name);
|
||||||
|
|
||||||
// Enforce that the path is ASCII.
|
// Enforce that the path is ASCII.
|
||||||
if (!IsValidPath(target_path, false)) {
|
if (!IsValidPath(target_path, false)) {
|
||||||
|
@ -501,7 +501,7 @@ dword_result_t NtQueryDirectoryFile_entry(
|
||||||
uint32_t info = 0;
|
uint32_t info = 0;
|
||||||
|
|
||||||
auto file = kernel_state()->object_table()->LookupObject<XFile>(file_handle);
|
auto file = kernel_state()->object_table()->LookupObject<XFile>(file_handle);
|
||||||
auto name = util::TranslateAnsiString(kernel_memory(), file_name);
|
auto name = util::TranslateAnsiPath(kernel_memory(), file_name);
|
||||||
|
|
||||||
// Enforce that the path is ASCII.
|
// Enforce that the path is ASCII.
|
||||||
if (!IsValidPath(name, true)) {
|
if (!IsValidPath(name, true)) {
|
||||||
|
@ -558,7 +558,7 @@ dword_result_t NtOpenSymbolicLinkObject_entry(
|
||||||
auto object_name =
|
auto object_name =
|
||||||
kernel_memory()->TranslateVirtual<X_ANSI_STRING*>(object_attrs->name_ptr);
|
kernel_memory()->TranslateVirtual<X_ANSI_STRING*>(object_attrs->name_ptr);
|
||||||
|
|
||||||
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);
|
auto target_path = util::TranslateAnsiPath(kernel_memory(), object_name);
|
||||||
|
|
||||||
// Enforce that the path is ASCII.
|
// Enforce that the path is ASCII.
|
||||||
if (!IsValidPath(target_path, false)) {
|
if (!IsValidPath(target_path, false)) {
|
||||||
|
|
|
@ -234,7 +234,7 @@ dword_result_t NtSetInformationFile_entry(
|
||||||
auto info = info_ptr.as<X_FILE_RENAME_INFORMATION*>();
|
auto info = info_ptr.as<X_FILE_RENAME_INFORMATION*>();
|
||||||
// Compute path, possibly attrs relative.
|
// Compute path, possibly attrs relative.
|
||||||
std::filesystem::path target_path =
|
std::filesystem::path target_path =
|
||||||
util::TranslateAnsiString(kernel_memory(), &info->ansi_string);
|
util::TranslateAnsiPath(kernel_memory(), &info->ansi_string);
|
||||||
|
|
||||||
// Place IsValidPath in path from where it can be accessed everywhere
|
// Place IsValidPath in path from where it can be accessed everywhere
|
||||||
if (!IsValidPath(target_path.string(), false)) {
|
if (!IsValidPath(target_path.string(), false)) {
|
||||||
|
|
|
@ -263,7 +263,7 @@ dword_result_t XexLoadImageHeaders_entry(pointer_t<X_ANSI_STRING> path,
|
||||||
return X_STATUS_BUFFER_TOO_SMALL;
|
return X_STATUS_BUFFER_TOO_SMALL;
|
||||||
}
|
}
|
||||||
auto current_kernel = ctx->kernel_state;
|
auto current_kernel = ctx->kernel_state;
|
||||||
auto target_path = util::TranslateAnsiString(current_kernel->memory(), path);
|
auto target_path = util::TranslateAnsiPath(current_kernel->memory(), path);
|
||||||
|
|
||||||
vfs::File* vfs_file = nullptr;
|
vfs::File* vfs_file = nullptr;
|
||||||
vfs::FileAction file_action;
|
vfs::FileAction file_action;
|
||||||
|
|
|
@ -350,11 +350,11 @@ DECLARE_XBOXKRNL_EXPORT1(ObReferenceObject, kNone, kImplemented);
|
||||||
dword_result_t ObCreateSymbolicLink_entry(pointer_t<X_ANSI_STRING> path_ptr,
|
dword_result_t ObCreateSymbolicLink_entry(pointer_t<X_ANSI_STRING> path_ptr,
|
||||||
pointer_t<X_ANSI_STRING> target_ptr) {
|
pointer_t<X_ANSI_STRING> target_ptr) {
|
||||||
auto path = xe::utf8::canonicalize_guest_path(
|
auto path = xe::utf8::canonicalize_guest_path(
|
||||||
util::TranslateAnsiString(kernel_memory(), path_ptr));
|
util::TranslateAnsiPath(kernel_memory(), path_ptr));
|
||||||
auto target = xe::utf8::canonicalize_guest_path(
|
auto target = xe::utf8::canonicalize_guest_path(
|
||||||
util::TranslateAnsiString(kernel_memory(), target_ptr));
|
util::TranslateAnsiPath(kernel_memory(), target_ptr));
|
||||||
|
|
||||||
if (xe::utf8::starts_with(path, u8"\\??\\")) {
|
if (xe::utf8::starts_with(path, "\\??\\")) {
|
||||||
path = path.substr(4); // Strip the full qualifier
|
path = path.substr(4); // Strip the full qualifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +367,7 @@ dword_result_t ObCreateSymbolicLink_entry(pointer_t<X_ANSI_STRING> path_ptr,
|
||||||
DECLARE_XBOXKRNL_EXPORT1(ObCreateSymbolicLink, kNone, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(ObCreateSymbolicLink, kNone, kImplemented);
|
||||||
|
|
||||||
dword_result_t ObDeleteSymbolicLink_entry(pointer_t<X_ANSI_STRING> path_ptr) {
|
dword_result_t ObDeleteSymbolicLink_entry(pointer_t<X_ANSI_STRING> path_ptr) {
|
||||||
auto path = util::TranslateAnsiString(kernel_memory(), path_ptr);
|
auto path = util::TranslateAnsiPath(kernel_memory(), path_ptr);
|
||||||
if (!kernel_state()->file_system()->UnregisterSymbolicLink(path)) {
|
if (!kernel_state()->file_system()->UnregisterSymbolicLink(path)) {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ DEFINE_double(kernel_display_gamma_power, 2.22222233,
|
||||||
"Display gamma to use with kernel_display_gamma_type 3.",
|
"Display gamma to use with kernel_display_gamma_type 3.",
|
||||||
"Kernel");
|
"Kernel");
|
||||||
|
|
||||||
inline constexpr static uint32_t GetVideoStandard() {
|
inline const static uint32_t GetVideoStandard() {
|
||||||
if (cvars::video_standard < 1 || cvars::video_standard > 3) {
|
if (cvars::video_standard < 1 || cvars::video_standard > 3) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,11 @@ inline constexpr static uint32_t GetVideoStandard() {
|
||||||
return cvars::video_standard;
|
return cvars::video_standard;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr static float GetVideoRefreshRate() {
|
inline const static float GetVideoRefreshRate() {
|
||||||
return cvars::use_50Hz_mode ? 50.0f : 60.0f;
|
return cvars::use_50Hz_mode ? 50.0f : 60.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr static std::pair<uint16_t, uint16_t> GetDisplayAspectRatio() {
|
inline const static std::pair<uint16_t, uint16_t> GetDisplayAspectRatio() {
|
||||||
if (cvars::widescreen) {
|
if (cvars::widescreen) {
|
||||||
return {16, 9};
|
return {16, 9};
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,10 @@ DEFINE_int32(user_country, 103,
|
||||||
" 102=UA 103=US 104=UY 105=UZ 106=VE 107=VN 108=YE 109=ZA\n",
|
" 102=UA 103=US 104=UY 105=UZ 106=VE 107=VN 108=YE 109=ZA\n",
|
||||||
"XConfig");
|
"XConfig");
|
||||||
|
|
||||||
|
DECLARE_bool(widescreen);
|
||||||
|
DECLARE_bool(use_50Hz_mode);
|
||||||
|
DECLARE_int32(video_standard);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace xboxkrnl {
|
namespace xboxkrnl {
|
||||||
|
@ -56,9 +60,26 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting,
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case 0x0002: // XCONFIG_SECURED_AV_REGION
|
case 0x0002: // XCONFIG_SECURED_AV_REGION
|
||||||
setting_size = 4;
|
setting_size = 4;
|
||||||
xe::store_and_swap<uint32_t>(value, 0x00001000); // USA/Canada
|
switch (cvars::video_standard) {
|
||||||
|
case 1: // NTSCM
|
||||||
|
xe::store_and_swap<uint32_t>(value, 0x00400100);
|
||||||
|
break;
|
||||||
|
case 2: // NTSCJ
|
||||||
|
xe::store_and_swap<uint32_t>(value, 0x00400200);
|
||||||
|
break;
|
||||||
|
case 3: // PAL
|
||||||
|
xe::store_and_swap<uint32_t>(
|
||||||
|
value, cvars::use_50Hz_mode ? 0x00800300 : 0x00400400);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xe::store_and_swap<uint32_t>(value, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
XELOGW(
|
||||||
|
"An unimplemented setting 0x{:04X} in XCONFIG SECURED CATEGORY",
|
||||||
|
static_cast<uint16_t>(setting));
|
||||||
assert_unhandled_case(setting);
|
assert_unhandled_case(setting);
|
||||||
return X_STATUS_INVALID_PARAMETER_2;
|
return X_STATUS_INVALID_PARAMETER_2;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +104,10 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting,
|
||||||
break;
|
break;
|
||||||
case 0x000A: // XCONFIG_USER_VIDEO_FLAGS
|
case 0x000A: // XCONFIG_USER_VIDEO_FLAGS
|
||||||
setting_size = 4;
|
setting_size = 4;
|
||||||
xe::store_and_swap<uint32_t>(value, 0x00040000);
|
// 0x00040000 normal
|
||||||
|
// 0x00050000 widescreen
|
||||||
|
xe::store_and_swap<uint32_t>(
|
||||||
|
value, cvars::widescreen ? 0x00050000 : 0x00040000);
|
||||||
break;
|
break;
|
||||||
case 0x000C: // XCONFIG_USER_RETAIL_FLAGS
|
case 0x000C: // XCONFIG_USER_RETAIL_FLAGS
|
||||||
setting_size = 4;
|
setting_size = 4;
|
||||||
|
@ -95,11 +119,15 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting,
|
||||||
value[0] = static_cast<uint8_t>(cvars::user_country);
|
value[0] = static_cast<uint8_t>(cvars::user_country);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
XELOGW("An unimplemented setting 0x{:04X} in XCONFIG USER CATEGORY",
|
||||||
|
static_cast<uint16_t>(setting));
|
||||||
assert_unhandled_case(setting);
|
assert_unhandled_case(setting);
|
||||||
return X_STATUS_INVALID_PARAMETER_2;
|
return X_STATUS_INVALID_PARAMETER_2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
XELOGW("An unimplemented category 0x{:04X}",
|
||||||
|
static_cast<uint16_t>(category));
|
||||||
assert_unhandled_case(category);
|
assert_unhandled_case(category);
|
||||||
return X_STATUS_INVALID_PARAMETER_1;
|
return X_STATUS_INVALID_PARAMETER_1;
|
||||||
}
|
}
|
||||||
|
@ -138,6 +166,36 @@ dword_result_t ExGetXConfigSetting_entry(word_t category, word_t setting,
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(ExGetXConfigSetting, kModules, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(ExGetXConfigSetting, kModules, kImplemented);
|
||||||
|
|
||||||
|
dword_result_t ExSetXConfigSetting_entry(word_t category, word_t setting,
|
||||||
|
lpvoid_t buffer_ptr,
|
||||||
|
dword_t buffer_size) {
|
||||||
|
/* Notes:
|
||||||
|
Handles settings the only have a single flag/value like
|
||||||
|
XCONFIG_USER_VIDEO_FLAGS to swap
|
||||||
|
*/
|
||||||
|
XELOGI("ExSetXConfigSetting: category: 0X{:04x}, setting: 0X{:04x}",
|
||||||
|
static_cast<uint16_t>(category), static_cast<uint16_t>(setting));
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XBOXKRNL_EXPORT1(ExSetXConfigSetting, kModules, kStub);
|
||||||
|
|
||||||
|
dword_result_t ExReadModifyWriteXConfigSettingUlong_entry(word_t category,
|
||||||
|
word_t setting,
|
||||||
|
dword_t bit_affected,
|
||||||
|
dword_t flag) {
|
||||||
|
/* Notes:
|
||||||
|
Handles settings with multiple flags like XCONFIG_USER_RETAIL_FLAGS and
|
||||||
|
XCONFIG_CONSOLE_RETAIL_EX_FLAGS
|
||||||
|
*/
|
||||||
|
XELOGI(
|
||||||
|
"ExReadModifyWriteXConfigSettingUlong: category: 0x{:04x}, setting: "
|
||||||
|
"{:04x}, changed bits: 0X{:08x}, setting flag 0X{:08x}",
|
||||||
|
static_cast<uint16_t>(category), static_cast<uint16_t>(setting),
|
||||||
|
static_cast<uint32_t>(bit_affected), static_cast<uint32_t>(flag));
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
DECLARE_XBOXKRNL_EXPORT1(ExReadModifyWriteXConfigSettingUlong, kModules, kStub);
|
||||||
|
|
||||||
} // namespace xboxkrnl
|
} // namespace xboxkrnl
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -98,9 +98,9 @@ static bool IsValidPath(const std::string_view s, bool is_pattern) {
|
||||||
case '+':
|
case '+':
|
||||||
case ',':
|
case ',':
|
||||||
// case ':':
|
// case ':':
|
||||||
case ';':
|
// case ';':
|
||||||
case '<':
|
case '<':
|
||||||
case '=':
|
// case '=':
|
||||||
case '>':
|
case '>':
|
||||||
// case '?':
|
// case '?':
|
||||||
case '|': {
|
case '|': {
|
||||||
|
|
|
@ -135,6 +135,8 @@ class XObject {
|
||||||
case Type::Thread:
|
case Type::Thread:
|
||||||
case Type::Timer:
|
case Type::Timer:
|
||||||
return true;
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -349,7 +351,9 @@ class object_ref {
|
||||||
|
|
||||||
void reset(T* value) noexcept { object_ref(value).swap(*this); }
|
void reset(T* value) noexcept { object_ref(value).swap(*this); }
|
||||||
|
|
||||||
inline bool operator==(const T* right) noexcept { return value_ == right; }
|
inline bool operator==(const T* right) const noexcept {
|
||||||
|
return value_ == right;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* value_ = nullptr;
|
T* value_ = nullptr;
|
||||||
|
|
|
@ -74,9 +74,10 @@ X_STATUS XSocket::Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS XSocket::GetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
X_STATUS XSocket::GetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
||||||
int* optlen) {
|
uint32_t* optlen) {
|
||||||
int ret =
|
int ret =
|
||||||
getsockopt(native_handle_, level, optname, (char*)optval_ptr, optlen);
|
getsockopt(native_handle_, level, optname, static_cast<char*>(optval_ptr),
|
||||||
|
reinterpret_cast<socklen_t*>(optlen));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
// TODO: WSAGetLastError()
|
// TODO: WSAGetLastError()
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
|
|
@ -108,7 +108,7 @@ class XSocket : public XObject {
|
||||||
X_STATUS Close();
|
X_STATUS Close();
|
||||||
|
|
||||||
X_STATUS GetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
X_STATUS GetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
||||||
int* optlen);
|
uint32_t* optlen);
|
||||||
X_STATUS SetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
X_STATUS SetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
||||||
uint32_t optlen);
|
uint32_t optlen);
|
||||||
X_STATUS IOControl(uint32_t cmd, uint8_t* arg_ptr);
|
X_STATUS IOControl(uint32_t cmd, uint8_t* arg_ptr);
|
||||||
|
|
|
@ -48,8 +48,8 @@ void PluginLoader::LoadConfigs() {
|
||||||
xe::filesystem::FilterByName(dir_files, std::regex("[A-Fa-f0-9]{8}"));
|
xe::filesystem::FilterByName(dir_files, std::regex("[A-Fa-f0-9]{8}"));
|
||||||
|
|
||||||
for (const auto& entry : dir_files) {
|
for (const auto& entry : dir_files) {
|
||||||
const uint32_t title_id =
|
const uint32_t title_id = string_util::from_string<uint32_t>(
|
||||||
std::stoi(entry.name.filename().string(), nullptr, 16);
|
entry.name.filename().string(), true);
|
||||||
|
|
||||||
LoadTitleConfig(title_id);
|
LoadTitleConfig(title_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,14 +95,13 @@ void ImGuiGuestNotification::UpdateNotificationState() {
|
||||||
|
|
||||||
const ImVec2 ImGuiGuestNotification::CalculateNotificationSize(ImVec2 text_size,
|
const ImVec2 ImGuiGuestNotification::CalculateNotificationSize(ImVec2 text_size,
|
||||||
float scale) {
|
float scale) {
|
||||||
const ImVec2 result =
|
const ImVec2 result = ImVec2(floorf((default_notification_icon_size.x +
|
||||||
ImVec2(std::floorf((default_notification_icon_size.x +
|
default_notification_margin_size.x) *
|
||||||
default_notification_margin_size.x) *
|
scale) +
|
||||||
scale) +
|
text_size.x,
|
||||||
text_size.x,
|
floorf((default_notification_icon_size.y +
|
||||||
std::floorf((default_notification_icon_size.y +
|
default_notification_margin_size.y) *
|
||||||
default_notification_margin_size.y) *
|
scale));
|
||||||
scale));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -134,13 +133,14 @@ void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
|
||||||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||||
screen_size, final_notification_size, GetPositionId());
|
screen_size, final_notification_size, GetPositionId());
|
||||||
|
|
||||||
if (isnan(notification_position.x) || isnan(notification_position.y)) {
|
if (std::isnan(notification_position.x) ||
|
||||||
|
std::isnan(notification_position.y)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 current_notification_size = final_notification_size;
|
ImVec2 current_notification_size = final_notification_size;
|
||||||
current_notification_size.x *= notification_draw_progress_;
|
current_notification_size.x *= notification_draw_progress_;
|
||||||
current_notification_size.x = std::floorf(current_notification_size.x);
|
current_notification_size.x = floorf(current_notification_size.x);
|
||||||
|
|
||||||
// Initialize position and window size
|
// Initialize position and window size
|
||||||
ImGui::SetNextWindowSize(current_notification_size);
|
ImGui::SetNextWindowSize(current_notification_size);
|
||||||
|
@ -170,7 +170,7 @@ void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (notification_draw_progress_ > 0.5f) {
|
if (notification_draw_progress_ > 0.5f) {
|
||||||
ImGui::TextColored(white_color, GetNotificationText().c_str());
|
ImGui::TextColored(white_color, "%s", GetNotificationText().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Restore previous style
|
// Restore previous style
|
||||||
|
@ -207,13 +207,14 @@ void XNotifyWindow::OnDraw(ImGuiIO& io) {
|
||||||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||||
screen_size, final_notification_size, GetPositionId());
|
screen_size, final_notification_size, GetPositionId());
|
||||||
|
|
||||||
if (isnan(notification_position.x) || isnan(notification_position.y)) {
|
if (std::isnan(notification_position.x) ||
|
||||||
|
std::isnan(notification_position.y)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 current_notification_size = final_notification_size;
|
ImVec2 current_notification_size = final_notification_size;
|
||||||
current_notification_size.x *= notification_draw_progress_;
|
current_notification_size.x *= notification_draw_progress_;
|
||||||
current_notification_size.x = std::floorf(current_notification_size.x);
|
current_notification_size.x = floorf(current_notification_size.x);
|
||||||
|
|
||||||
// Initialize position and window size
|
// Initialize position and window size
|
||||||
ImGui::SetNextWindowSize(current_notification_size);
|
ImGui::SetNextWindowSize(current_notification_size);
|
||||||
|
@ -247,7 +248,7 @@ void XNotifyWindow::OnDraw(ImGuiIO& io) {
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (notification_draw_progress_ > 0.5f) {
|
if (notification_draw_progress_ > 0.5f) {
|
||||||
ImGui::TextColored(white_color, GetDescription().c_str());
|
ImGui::TextColored(white_color, "%s", GetDescription().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Restore previous style
|
// Restore previous style
|
||||||
|
|
|
@ -57,7 +57,7 @@ class ImGuiGuestNotification : public ImGuiNotification {
|
||||||
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||||
float scale) override;
|
float scale) override;
|
||||||
|
|
||||||
virtual void OnDraw(ImGuiIO& io) {}
|
virtual void OnDraw(ImGuiIO& io) override {}
|
||||||
|
|
||||||
float notification_draw_progress_;
|
float notification_draw_progress_;
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ void HostNotificationWindow::OnDraw(ImGuiIO& io) {
|
||||||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||||
screen_size, notification_size, GetPositionId());
|
screen_size, notification_size, GetPositionId());
|
||||||
|
|
||||||
if (isnan(notification_position.x) || isnan(notification_position.y)) {
|
if (std::isnan(notification_position.x) ||
|
||||||
|
std::isnan(notification_position.y)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +79,9 @@ void HostNotificationWindow::OnDraw(ImGuiIO& io) {
|
||||||
{
|
{
|
||||||
ImGui::SetWindowFontScale(window_scale);
|
ImGui::SetWindowFontScale(window_scale);
|
||||||
|
|
||||||
ImGui::Text(GetTitle().c_str());
|
ImGui::Text("%s", GetTitle().c_str());
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text(GetDescription().c_str());
|
ImGui::Text("%s", GetDescription().c_str());
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class ImGuiHostNotification : public ImGuiNotification {
|
||||||
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||||
float scale) override;
|
float scale) override;
|
||||||
|
|
||||||
virtual void OnDraw(ImGuiIO& io) {}
|
virtual void OnDraw(ImGuiIO& io) override {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class HostNotificationWindow final : ImGuiHostNotification {
|
class HostNotificationWindow final : ImGuiHostNotification {
|
||||||
|
|
|
@ -468,10 +468,12 @@ struct XContentMetadata {
|
||||||
};
|
};
|
||||||
static_assert_size(XContentMetadata, 0x93D6);
|
static_assert_size(XContentMetadata, 0x93D6);
|
||||||
|
|
||||||
|
static constexpr uint8_t license_count = 0x10;
|
||||||
|
|
||||||
struct XContentHeader {
|
struct XContentHeader {
|
||||||
be<XContentPackageType> magic;
|
be<XContentPackageType> magic;
|
||||||
uint8_t signature[0x228];
|
uint8_t signature[0x228];
|
||||||
XContentLicense licenses[0x10];
|
XContentLicense licenses[license_count];
|
||||||
uint8_t content_id[0x14];
|
uint8_t content_id[0x14];
|
||||||
be<uint32_t> header_size;
|
be<uint32_t> header_size;
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ class XContentContainerDevice : public Device {
|
||||||
|
|
||||||
~XContentContainerDevice() override;
|
~XContentContainerDevice() override;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize() override;
|
||||||
|
|
||||||
const std::string& name() const override { return name_; }
|
const std::string& name() const override { return name_; }
|
||||||
uint32_t attributes() const override { return 0; }
|
uint32_t attributes() const override { return 0; }
|
||||||
|
@ -65,7 +65,13 @@ class XContentContainerDevice : public Device {
|
||||||
|
|
||||||
kernel::xam::XCONTENT_AGGREGATE_DATA content_header() const;
|
kernel::xam::XCONTENT_AGGREGATE_DATA content_header() const;
|
||||||
uint32_t license_mask() const {
|
uint32_t license_mask() const {
|
||||||
return header_->content_header.licenses[0].license_bits;
|
uint32_t final_license = 0;
|
||||||
|
for (uint8_t i = 0; i < license_count; i++) {
|
||||||
|
if (header_->content_header.licenses[i].license_flags) {
|
||||||
|
final_license |= header_->content_header.licenses[i].license_bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return final_license;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -87,9 +93,9 @@ class XContentContainerDevice : public Device {
|
||||||
// Initialize any container specific fields.
|
// Initialize any container specific fields.
|
||||||
virtual void SetupContainer() {};
|
virtual void SetupContainer() {};
|
||||||
|
|
||||||
Entry* ResolvePath(const std::string_view path);
|
Entry* ResolvePath(const std::string_view path) override;
|
||||||
void CloseFiles();
|
void CloseFiles();
|
||||||
void Dump(StringBuffer* string_buffer);
|
void Dump(StringBuffer* string_buffer) override;
|
||||||
Result ReadHeaderAndVerify(FILE* header_file);
|
Result ReadHeaderAndVerify(FILE* header_file);
|
||||||
|
|
||||||
void SetName(std::string name) { name_ = name; }
|
void SetName(std::string name) { name_ = name; }
|
||||||
|
|
145
src/xenia/xbox.h
145
src/xenia/xbox.h
|
@ -121,6 +121,8 @@ typedef uint32_t X_HRESULT;
|
||||||
|
|
||||||
#define X_E_FALSE static_cast<X_HRESULT>(0x80000000L)
|
#define X_E_FALSE static_cast<X_HRESULT>(0x80000000L)
|
||||||
#define X_E_SUCCESS X_HRESULT_FROM_WIN32(X_ERROR_SUCCESS)
|
#define X_E_SUCCESS X_HRESULT_FROM_WIN32(X_ERROR_SUCCESS)
|
||||||
|
#define X_E_ACCESS_DENIED X_HRESULT_FROM_WIN32(X_ERROR_ACCESS_DENIED)
|
||||||
|
#define X_E_NOT_IMPLEMENTED static_cast<X_HRESULT>(0x80004001L)
|
||||||
#define X_E_FAIL static_cast<X_HRESULT>(0x80004005L)
|
#define X_E_FAIL static_cast<X_HRESULT>(0x80004005L)
|
||||||
#define X_E_NO_MORE_FILES X_HRESULT_FROM_WIN32(X_ERROR_NO_MORE_FILES)
|
#define X_E_NO_MORE_FILES X_HRESULT_FROM_WIN32(X_ERROR_NO_MORE_FILES)
|
||||||
#define X_E_INVALIDARG X_HRESULT_FROM_WIN32(X_ERROR_INVALID_PARAMETER)
|
#define X_E_INVALIDARG X_HRESULT_FROM_WIN32(X_ERROR_INVALID_PARAMETER)
|
||||||
|
@ -275,55 +277,70 @@ enum : XNotificationID {
|
||||||
kXNotifyLive = 0x00000002,
|
kXNotifyLive = 0x00000002,
|
||||||
kXNotifyFriends = 0x00000004,
|
kXNotifyFriends = 0x00000004,
|
||||||
kXNotifyCustom = 0x00000008,
|
kXNotifyCustom = 0x00000008,
|
||||||
|
kXNotifyDvdDrive = 0x00000010, // ?
|
||||||
kXNotifyXmp = 0x00000020,
|
kXNotifyXmp = 0x00000020,
|
||||||
kXNotifyMsgr = 0x00000040,
|
kXNotifyMsgr = 0x00000040,
|
||||||
kXNotifyParty = 0x00000080,
|
kXNotifyParty = 0x00000080,
|
||||||
kXNotifyAll = 0x000000EF,
|
kXNotifyAll = 0x000000EF,
|
||||||
|
|
||||||
// XNotification System
|
// XNotification System (35 total)
|
||||||
kXNotificationIDSystemUI = 0x00000009,
|
kXNotificationSystemUI = 0x00000009,
|
||||||
kXNotificationIDSystemSignInChanged = 0x0000000A,
|
kXNotificationSystemSignInChanged = 0x0000000A,
|
||||||
kXNotificationIDSystemStorageDevicesChanged = 0x0000000B,
|
kXNotificationSystemStorageDevicesChanged = 0x0000000B,
|
||||||
kXNotificationIDSystemProfileSettingChanged = 0x0000000E,
|
kXNotificationSystemProfileSettingChanged = 0x0000000E,
|
||||||
kXNotificationIDSystemMuteListChanged = 0x00000011,
|
kXNotificationSystemMuteListChanged = 0x00000011,
|
||||||
kXNotificationIDSystemInputDevicesChanged = 0x00000012,
|
kXNotificationSystemInputDevicesChanged = 0x00000012,
|
||||||
kXNotificationIDSystemInputDeviceConfigChanged = 0x00000013,
|
kXNotificationSystemInputDeviceConfigChanged = 0x00000013,
|
||||||
kXNotificationIDSystemPlayerTimerNotice = 0x00000015,
|
kXNotificationSystemPlayerTimerNotice = 0x00000015,
|
||||||
kXNotificationIDSystemAvatarChanged = 0x00000017,
|
kXNotificationSystemPXLiveSystemUpdate = 0x00000016,
|
||||||
kXNotificationIDSystemNUIHardwareStatusChanged = 0x00000019,
|
kXNotificationSystemAvatarChanged = 0x00000017,
|
||||||
kXNotificationIDSystemNUIPause = 0x0000001A,
|
kXNotificationSystemUnknown = 0x00000018,
|
||||||
kXNotificationIDSystemNUIUIApproach = 0x0000001B,
|
kXNotificationSystemNUIHardwareStatusChanged = 0x00000019,
|
||||||
kXNotificationIDSystemDeviceRemap = 0x0000001C,
|
kXNotificationSystemNUIPause = 0x0000001A,
|
||||||
kXNotificationIDSystemNUIBindingChanged = 0x0000001D,
|
kXNotificationSystemNUIUIApproach = 0x0000001B,
|
||||||
kXNotificationIDSystemAudioLatencyChanged = 0x0000001E,
|
kXNotificationSystemDeviceRemap = 0x0000001C,
|
||||||
kXNotificationIDSystemNUIChatBindingChanged = 0x0000001F,
|
kXNotificationSystemNUIBindingChanged = 0x0000001D,
|
||||||
kXNotificationIDSystemInputActivityChanged = 0x00000020,
|
kXNotificationSystemAudioLatencyChanged = 0x0000001E,
|
||||||
|
kXNotificationSystemNUIChatBindingChanged = 0x0000001F,
|
||||||
|
kXNotificationSystemInputActivityChanged = 0x00000020,
|
||||||
|
|
||||||
// XNotification Live
|
// XNotification Live (20 total)
|
||||||
kXNotificationIDLiveConnectionChanged = 0x02000001,
|
kXNotificationLiveConnectionChanged = 0x02000001,
|
||||||
kXNotificationIDLiveInviteAccepted = 0x02000002,
|
kXNotificationLiveInviteAccepted = 0x02000002,
|
||||||
kXNotificationIDLiveLinkStateChanged = 0x02000003,
|
kXNotificationLiveLinkStateChanged = 0x02000003,
|
||||||
kXNotificationIDLiveContentInstalled = 0x02000007,
|
kXNotificationLiveContentInstalled = 0x02000007,
|
||||||
kXNotificationIDLiveMembershipPurchased = 0x02000008,
|
kXNotificationLiveMembershipPurchased = 0x02000008,
|
||||||
kXNotificationIDLiveVoicechatAway = 0x02000009,
|
kXNotificationLiveVoicechatAway = 0x02000009,
|
||||||
kXNotificationIDLivePresenceChanged = 0x0200000A,
|
kXNotificationLivePresenceChanged = 0x0200000A,
|
||||||
|
kXNotificationLiveUnknown = 0x02000012,
|
||||||
|
|
||||||
// XNotification Friends
|
// XNotification Friends (9 total)
|
||||||
kXNotificationIDFriendsPresenceChanged = 0x04000001,
|
kXNotificationFriendsPresenceChanged = 0x04000001,
|
||||||
kXNotificationIDFriendsFriendAdded = 0x04000002,
|
kXNotificationFriendsFriendAdded = 0x04000002,
|
||||||
kXNotificationIDFriendsFriendRemoved = 0x04000003,
|
kXNotificationFriendsFriendRemoved = 0x04000003,
|
||||||
|
kXNotificationFriendsUnknown = 0x04000008,
|
||||||
|
|
||||||
// XNotification Custom
|
// XNotification Custom (5 total)
|
||||||
kXNotificationIDCustomActionPressed = 0x06000003,
|
kXNotificationCustomActionPressed = 0x06000003,
|
||||||
kXNotificationIDCustomGamercard = 0x06000004,
|
kXNotificationCustomGamercard = 0x06000004,
|
||||||
|
|
||||||
// XNotification XMP
|
// XNotification Dvd ?
|
||||||
kNotificationXmpStateChanged = 0x0A000001,
|
kXNotificationDvdDriveUnknown = 0x80000003,
|
||||||
kNotificationXmpPlaybackBehaviorChanged = 0x0A000002,
|
kXNotificationDvdDriveUnknownDashContext = 0x8000000C,
|
||||||
kNotificationXmpPlaybackControllerChanged = 0x0A000003,
|
kXNotificationDvdDriveTrayStateChanged = 0x8000000D,
|
||||||
|
|
||||||
// XNotification Party
|
// XNotification XMP (13 total)
|
||||||
kXNotificationIDPartyMembersChanged = 0x0E000002,
|
kXNotificationXmpStateChanged = 0x0A000001,
|
||||||
|
kXNotificationXmpPlaybackBehaviorChanged = 0x0A000002,
|
||||||
|
kXNotificationXmpPlaybackControllerChanged = 0x0A000003,
|
||||||
|
kXNotificationXmpUnknown = 0x0A00000C,
|
||||||
|
|
||||||
|
// XNotification Party (6 total)
|
||||||
|
kXNotificationPartyMembersChanged = 0x0E000002,
|
||||||
|
kXNotificationFriendUnknown = 0x0E000005,
|
||||||
|
|
||||||
|
// XNotification Msgr
|
||||||
|
kXNotificationMsgrUnknown = 0x0C00000E,
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/CodeAsm/ffplay360/blob/master/Common/XTLOnPC.h
|
// https://github.com/CodeAsm/ffplay360/blob/master/Common/XTLOnPC.h
|
||||||
|
@ -540,6 +557,19 @@ const static std::map<XContentType, std::string> XContentTypeMap = {
|
||||||
{XContentType::kCommunityGame, "Community Game"},
|
{XContentType::kCommunityGame, "Community Game"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum X_MARKETPLACE_ENTRYPOINT : uint32_t {
|
||||||
|
ContentList = 0,
|
||||||
|
ContentItem = 1,
|
||||||
|
MembershipList = 2,
|
||||||
|
MembershipItem = 3,
|
||||||
|
ContentList_Background = 4,
|
||||||
|
ContentItem_Background = 5,
|
||||||
|
ForcedNameChangeV1 = 6,
|
||||||
|
ForcedNameChangeV2 = 8,
|
||||||
|
ProfileNameChange = 9,
|
||||||
|
ActiveDownloads = 12
|
||||||
|
};
|
||||||
|
|
||||||
enum class XDeploymentType : uint32_t {
|
enum class XDeploymentType : uint32_t {
|
||||||
kOpticalDisc = 0,
|
kOpticalDisc = 0,
|
||||||
kHardDrive = 1, // Like extracted?
|
kHardDrive = 1, // Like extracted?
|
||||||
|
@ -650,8 +680,43 @@ struct X_PROFILEENUMRESULT {
|
||||||
};
|
};
|
||||||
static_assert_size(X_PROFILEENUMRESULT, 0x188);
|
static_assert_size(X_PROFILEENUMRESULT, 0x188);
|
||||||
|
|
||||||
} // namespace xe
|
struct MESSAGEBOX_RESULT {
|
||||||
|
union {
|
||||||
|
xe::be<uint32_t> ButtonPressed;
|
||||||
|
xe::be<uint16_t> Passcode[4];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#define XMBox_NOICON 0x00000000
|
||||||
|
#define XMBox_ERRORICON 0x00000001
|
||||||
|
#define XMBox_WARNINGICON 0x00000002
|
||||||
|
#define XMBox_ALERTICON 0x00000003
|
||||||
|
|
||||||
|
#define XMBox_PASSCODEMODE 0x00010000
|
||||||
|
#define XMBox_VERIFYPASSCODEMODE 0x00020000
|
||||||
|
|
||||||
|
#define XMBox_WAITANIMATION 0x00001000
|
||||||
|
#define XMBox_LIVEPASSCODEMODE 0x00030000
|
||||||
|
#define XMBox_MODEMASK 0x00030000
|
||||||
|
|
||||||
|
#define XMBox_OK 1
|
||||||
|
#define XMBox_CANCEL 2
|
||||||
|
|
||||||
|
#define X_BUTTON_PASSCODE 0x00005802
|
||||||
|
#define Y_BUTTON_PASSCODE 0x00005803
|
||||||
|
#define RIGHT_BUMPER_PASSCODE 0x00005804
|
||||||
|
#define LEFT_BUMPER_PASSCODE 0x00005805
|
||||||
|
#define LEFT_TRIGGER_PASSCODE 0x00005806
|
||||||
|
#define RIGHT_TRIGGER_PASSCODE 0x00005807
|
||||||
|
#define DPAD_UP_PASSCODE 0x00005810
|
||||||
|
#define DPAD_DOWN_PASSCODE 0x00005811
|
||||||
|
#define DPAD_LEFT_PASSCODE 0x00005812
|
||||||
|
#define DPAD_RIGHT_PASSCODE 0x00005813
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
#endif // XENIA_XBOX_H_
|
#endif // XENIA_XBOX_H_
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue