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'
|
||||
- '.gdbinit'
|
||||
- '.github/*'
|
||||
- '.github/workflows/Linux_build.yml'
|
||||
- '.github/*_TEMPLATE/**'
|
||||
- '*.md'
|
||||
- '*.yml'
|
||||
|
@ -30,6 +31,7 @@ on:
|
|||
- '.gitignore'
|
||||
- '.gdbinit'
|
||||
- '.github/*'
|
||||
- '.github/workflows/Linux_build.yml'
|
||||
- '.github/*_TEMPLATE/**'
|
||||
- '*.md'
|
||||
- '*.yml'
|
||||
|
@ -50,7 +52,7 @@ jobs:
|
|||
name: Lint
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- name: Check Clang-Format Version
|
||||
run: clang-format --version
|
||||
- name: Lint
|
||||
|
@ -67,7 +69,7 @@ jobs:
|
|||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup
|
||||
|
@ -75,12 +77,15 @@ jobs:
|
|||
- name: Build
|
||||
run: .\xb build --config=Release --target=src\xenia-app
|
||||
- name: Prepare artifacts
|
||||
id: prepare_artifacts
|
||||
run: |
|
||||
robocopy . build\bin\${{ runner.os }}\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 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 }
|
||||
- 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:
|
||||
name: xenia_canary_vs${{ matrix.vsver }}
|
||||
path: artifacts\xenia_canary
|
||||
|
@ -89,7 +94,8 @@ jobs:
|
|||
if: |
|
||||
github.repository == 'xenia-canary/xenia-canary' &&
|
||||
github.event.action != 'pull_request' &&
|
||||
github.ref == 'refs/heads/canary_experimental'
|
||||
github.ref == 'refs/heads/canary_experimental' &&
|
||||
steps.upload_artifacts.outcome == 'success'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
url = https://github.com/catchorg/Catch2.git
|
||||
[submodule "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"]
|
||||
path = third_party/snappy
|
||||
url = https://github.com/xenia-project/snappy.git
|
||||
|
|
|
@ -21,7 +21,7 @@ Discussing illegal activities will get you banned.
|
|||
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)
|
||||
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)
|
||||
|
||||
## 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)
|
||||
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
|
||||
|
||||
|
|
16
premake5.lua
16
premake5.lua
|
@ -26,7 +26,7 @@ defines({
|
|||
"UNICODE",
|
||||
})
|
||||
|
||||
cppdialect("C++17")
|
||||
cppdialect("C++20")
|
||||
exceptionhandling("On")
|
||||
rtti("On")
|
||||
symbols("On")
|
||||
|
@ -113,12 +113,18 @@ filter("platforms:Linux")
|
|||
"rt",
|
||||
})
|
||||
|
||||
filter({"platforms:Linux"})
|
||||
vectorextensions("AVX2")
|
||||
|
||||
filter({"platforms:Linux", "kind:*App"})
|
||||
linkgroups("On")
|
||||
|
||||
filter({"platforms:Linux", "language:C++", "toolset:gcc"})
|
||||
disablewarnings({
|
||||
"unused-result"
|
||||
"unused-result",
|
||||
"deprecated-volatile",
|
||||
"switch",
|
||||
"deprecated-enum-enum-conversion",
|
||||
})
|
||||
|
||||
filter({"platforms:Linux", "toolset:gcc"})
|
||||
|
@ -135,11 +141,15 @@ filter({"platforms:Linux", "toolset:gcc"})
|
|||
|
||||
filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
||||
disablewarnings({
|
||||
"deprecated-register"
|
||||
"deprecated-register",
|
||||
"deprecated-volatile",
|
||||
"switch",
|
||||
"deprecated-enum-enum-conversion",
|
||||
})
|
||||
filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"})
|
||||
buildoptions({
|
||||
"-stdlib=libstdc++",
|
||||
"-std=c++20", -- clang doesn't respect cppdialect(?)
|
||||
})
|
||||
|
||||
filter("platforms:Android-*")
|
||||
|
|
|
@ -1405,15 +1405,13 @@ void EmulatorWindow::ToggleDisplayConfigDialog() {
|
|||
void EmulatorWindow::ToggleProfilesConfigDialog() {
|
||||
if (!profile_config_dialog_) {
|
||||
disable_hotkeys_ = true;
|
||||
emulator_->kernel_state()->BroadcastNotification(kXNotificationIDSystemUI,
|
||||
1);
|
||||
emulator_->kernel_state()->BroadcastNotification(kXNotificationSystemUI, 1);
|
||||
profile_config_dialog_ =
|
||||
std::make_unique<ProfileConfigDialog>(imgui_drawer_.get(), this);
|
||||
kernel::xam::xam_dialogs_shown_++;
|
||||
} else {
|
||||
disable_hotkeys_ = false;
|
||||
emulator_->kernel_state()->BroadcastNotification(kXNotificationIDSystemUI,
|
||||
0);
|
||||
emulator_->kernel_state()->BroadcastNotification(kXNotificationSystemUI, 0);
|
||||
profile_config_dialog_.reset();
|
||||
kernel::xam::xam_dialogs_shown_--;
|
||||
}
|
||||
|
@ -1462,17 +1460,17 @@ void EmulatorWindow::UpdateTitle() {
|
|||
|
||||
// Title information, if available
|
||||
if (emulator()->is_title_open()) {
|
||||
sb.AppendFormat(u8" | [{:08X}", emulator()->title_id());
|
||||
sb.AppendFormat(" | [{:08X}", emulator()->title_id());
|
||||
auto title_version = emulator()->title_version();
|
||||
if (!title_version.empty()) {
|
||||
sb.Append(u8" v");
|
||||
sb.Append(" v");
|
||||
sb.Append(title_version);
|
||||
}
|
||||
sb.Append(u8"]");
|
||||
sb.Append("]");
|
||||
|
||||
auto title_name = emulator()->title_name();
|
||||
if (!title_name.empty()) {
|
||||
sb.Append(u8" ");
|
||||
sb.Append(" ");
|
||||
sb.Append(title_name);
|
||||
}
|
||||
}
|
||||
|
@ -1482,28 +1480,28 @@ void EmulatorWindow::UpdateTitle() {
|
|||
if (graphics_system) {
|
||||
auto graphics_name = graphics_system->name();
|
||||
if (!graphics_name.empty()) {
|
||||
sb.Append(u8" <");
|
||||
sb.Append(" <");
|
||||
sb.Append(graphics_name);
|
||||
sb.Append(u8">");
|
||||
sb.Append(">");
|
||||
}
|
||||
}
|
||||
|
||||
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_) {
|
||||
sb.Append(u8" (Preloading shaders\u2026)");
|
||||
sb.Append(" (Preloading shaders\u2026)");
|
||||
}
|
||||
|
||||
patcher::Patcher* patcher = emulator()->patcher();
|
||||
if (patcher && patcher->IsAnyPatchApplied()) {
|
||||
sb.Append(u8" [Patches Applied]");
|
||||
sb.Append(" [Patches Applied]");
|
||||
}
|
||||
|
||||
patcher::PluginLoader* pluginloader = emulator()->plugin_loader();
|
||||
if (pluginloader && pluginloader->IsAnyPluginLoaded()) {
|
||||
sb.Append(u8" [Plugins Loaded]");
|
||||
sb.Append(" [Plugins Loaded]");
|
||||
}
|
||||
|
||||
window_->SetTitle(sb.to_string_view());
|
||||
|
@ -1664,11 +1662,16 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
|||
xe::threading::Sleep(delay);
|
||||
break;
|
||||
case ButtonFunctions::RunTitle: {
|
||||
if (selected_title_index == -1) selected_title_index++;
|
||||
if (selected_title_index == -1) {
|
||||
selected_title_index++;
|
||||
}
|
||||
|
||||
if (selected_title_index < recently_launched_titles_.size()) {
|
||||
app_context().CallInUIThread([this]() {
|
||||
RunTitle(recently_launched_titles_[selected_title_index].path_to_file);
|
||||
RunTitle(
|
||||
recently_launched_titles_[selected_title_index].path_to_file);
|
||||
});
|
||||
}
|
||||
} break;
|
||||
case ButtonFunctions::ClearMemoryPageState:
|
||||
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
||||
|
@ -1761,8 +1764,9 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
|||
if ((button_combination.function == ButtonFunctions::IncTitleSelect ||
|
||||
button_combination.function == ButtonFunctions::DecTitleSelect) &&
|
||||
recently_launched_titles_.size() > 0) {
|
||||
selected_title_index = std::clamp(
|
||||
selected_title_index, 0, (int)recently_launched_titles_.size() - 1);
|
||||
selected_title_index =
|
||||
std::clamp(selected_title_index, 0,
|
||||
static_cast<int32_t>(recently_launched_titles_.size() - 1));
|
||||
|
||||
// Must clear dialogs to prevent stacking
|
||||
ClearDialogs();
|
||||
|
@ -1835,7 +1839,8 @@ void EmulatorWindow::GamepadHotKeys() {
|
|||
|
||||
for (uint32_t user_index = 0; user_index < XUserMaxUserCount;
|
||||
++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
|
||||
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() {
|
||||
std::string msg = "";
|
||||
std::string msg_passthru = "";
|
||||
|
|
|
@ -236,7 +236,6 @@ class EmulatorWindow {
|
|||
bool vibrate = true);
|
||||
void GamepadHotKeys();
|
||||
void ToggleGPUSetting(gpu_cvar index);
|
||||
bool IsUseNexusForGameBarEnabled();
|
||||
void DisplayHotKeysConfig();
|
||||
|
||||
static std::string CanonicalizeFileExtension(
|
||||
|
|
|
@ -176,25 +176,51 @@ class EmulatorApp final : public xe::ui::WindowedApp {
|
|||
std::vector<std::unique_ptr<T>> CreateAll(const std::string_view name,
|
||||
Args... args) {
|
||||
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(
|
||||
creators_.cbegin(), creators_.cend(),
|
||||
[&name](const auto& f) { return name.compare(f.name) == 0; });
|
||||
|
||||
if (it != creators_.cend() && (*it).is_available()) {
|
||||
auto instance = (*it).instantiate(std::forward<Args>(args)...);
|
||||
if (instance) {
|
||||
instances.emplace_back(std::move(instance));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& creator : creators_) {
|
||||
if (!creator.is_available()) continue;
|
||||
auto instance = creator.instantiate(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Always add winkey for passthrough.
|
||||
auto it = std::find_if(
|
||||
creators_.cbegin(), creators_.cend(),
|
||||
[&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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "xenia/apu/xma_context.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
#include <XAudio2.h>
|
||||
|
||||
extern "C" {
|
||||
#if XE_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
|
@ -451,7 +449,7 @@ void AudioMediaPlayer::RemovePlaylist(uint32_t handle) {
|
|||
X_STATUS AudioMediaPlayer::SetVolume(float volume) {
|
||||
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_) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
@ -483,7 +481,7 @@ void AudioMediaPlayer::SetCaptureCallback(uint32_t callback, uint32_t context,
|
|||
}
|
||||
|
||||
void AudioMediaPlayer::OnStateChanged() {
|
||||
kernel_state_->BroadcastNotification(kNotificationXmpStateChanged,
|
||||
kernel_state_->BroadcastNotification(kXNotificationXmpStateChanged,
|
||||
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) {
|
||||
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(
|
||||
AudioSystem::kMaximumQueuedFrames, AudioSystem::kMaximumQueuedFrames);
|
||||
|
||||
|
@ -543,7 +541,7 @@ bool AudioMediaPlayer::SetupDriver(uint32_t sample_rate, uint32_t channels) {
|
|||
}
|
||||
|
||||
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_semaphore_) {
|
||||
driver_semaphore_.reset();
|
||||
|
|
|
@ -130,7 +130,7 @@ class AudioMediaPlayer {
|
|||
// really compatible with it.
|
||||
std::unique_ptr<AudioDriver> driver_ = nullptr;
|
||||
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);
|
||||
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
|
||||
XE_NOINLINE
|
||||
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) {
|
||||
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) {
|
||||
return __sync_val_compare_and_swap(value, *value, new_value);
|
||||
|
|
|
@ -91,6 +91,14 @@ struct NtSystemClock {
|
|||
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_>
|
||||
static constexpr std::enable_if_t<domain_fresh_ == Domain::Host, time_point>
|
||||
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.
|
||||
xe::InitializeLogging(entry_info.name);
|
||||
// xe::InitializeLogging(entry_info.name);
|
||||
|
||||
std::vector<std::string> args;
|
||||
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);
|
||||
|
||||
xe::ShutdownLogging();
|
||||
// xe::ShutdownLogging();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -110,24 +110,24 @@ std::string EscapeBasicString(const std::string_view view) {
|
|||
for (auto it = begin; it != end; ++it) {
|
||||
auto c = *it;
|
||||
if (c == '\b') {
|
||||
result += u8"\\b";
|
||||
result += "\\b";
|
||||
} else if (c == '\t') {
|
||||
result += u8"\\t";
|
||||
result += "\\t";
|
||||
} else if (c == '\n') {
|
||||
result += u8"\\n";
|
||||
result += "\\n";
|
||||
} else if (c == '\f') {
|
||||
result += u8"\\f";
|
||||
result += "\\f";
|
||||
} else if (c == '\r') {
|
||||
result += u8"\\r";
|
||||
result += "\\r";
|
||||
} else if (c == '"') {
|
||||
result += u8"\\\"";
|
||||
result += "\\\"";
|
||||
} else if (c == '\\') {
|
||||
result += u8"\\\\";
|
||||
result += "\\\\";
|
||||
} else if (c < 0x20 || c == 0x7F) {
|
||||
if (c <= 0xFFFF) {
|
||||
result += fmt::format(u8"\\u{:04X}", c);
|
||||
result += fmt::format("\\u{:04X}", c);
|
||||
} else {
|
||||
result += fmt::format(u8"\\u{:08X}", c);
|
||||
result += fmt::format("\\u{:08X}", c);
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
if ((i % 3) == 2) {
|
||||
result += u8"\\";
|
||||
result += "\\";
|
||||
}
|
||||
result += u8"\"";
|
||||
result += "\"";
|
||||
}
|
||||
quote_run = 0;
|
||||
}
|
||||
if (c == '\b') {
|
||||
result += u8"\\b";
|
||||
result += "\\b";
|
||||
} else if (c == '\t' || c == '\n') {
|
||||
result += c;
|
||||
} else if (c == '\f') {
|
||||
result += u8"\\f";
|
||||
result += "\\f";
|
||||
} else if (c == '\r') {
|
||||
// Silently drop \r.
|
||||
// result += c;
|
||||
} else if (c == '"') {
|
||||
quote_run = 1;
|
||||
} else if (c == '\\') {
|
||||
result += u8"\\\\";
|
||||
result += "\\\\";
|
||||
} else if (c < 0x20 || c == 0x7F) {
|
||||
if (c <= 0xFFFF) {
|
||||
result += fmt::format(u8"\\u{:04X}", c);
|
||||
result += fmt::format("\\u{:04X}", c);
|
||||
} else {
|
||||
result += fmt::format(u8"\\u{:08X}", c);
|
||||
result += fmt::format("\\u{:08X}", c);
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
if ((i % 3) == 2) {
|
||||
result += u8"\\";
|
||||
result += "\\";
|
||||
}
|
||||
result += u8"\"";
|
||||
result += "\"";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -207,11 +207,11 @@ std::string EscapeString(const std::string_view view) {
|
|||
} else {
|
||||
// multi line
|
||||
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) + "'''";
|
||||
} else {
|
||||
return u8"\"\"\"\n" + toml_internal::EscapeMultilineBasicString(view) +
|
||||
u8"\"\"\"";
|
||||
return "\"\"\"\n" + toml_internal::EscapeMultilineBasicString(view) +
|
||||
"\"\"\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,10 +99,11 @@ void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written);
|
|||
// might as well be noalias
|
||||
template <typename... Args>
|
||||
XE_NOALIAS XE_NOINLINE XE_COLD static void AppendLogLineFormat_Impl(
|
||||
LogLevel log_level, const char prefix_char, const char* format,
|
||||
const Args&... args) {
|
||||
LogLevel log_level, const char prefix_char, std::string_view format,
|
||||
const Args&... args) noexcept {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -113,8 +114,8 @@ template <typename... Args>
|
|||
XE_FORCEINLINE static void AppendLogLineFormat(uint32_t log_src_mask,
|
||||
LogLevel log_level,
|
||||
const char prefix_char,
|
||||
const char* format,
|
||||
const Args&... args) {
|
||||
std::string_view format,
|
||||
const Args&... args) noexcept {
|
||||
if (!internal::ShouldLog(log_level, log_src_mask)) {
|
||||
return;
|
||||
}
|
||||
|
@ -143,7 +144,8 @@ struct LoggerBatch {
|
|||
LoggerBatch() { reset(); }
|
||||
template <size_t fmtlen, typename... Ts>
|
||||
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 = tmpres.out;
|
||||
total_size += tmpres.size;
|
||||
|
@ -164,55 +166,55 @@ struct LoggerBatch {
|
|||
#if XE_OPTION_ENABLE_LOGGING
|
||||
|
||||
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::LogLevel::Error, '!', format, 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::LogLevel::Warning, 'w', format, 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::LogLevel::Info, 'i', format, 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::LogLevel::Debug, 'd', format, 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',
|
||||
format, 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',
|
||||
format, 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::LogLevel::Debug, 'G', format, 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',
|
||||
format, 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::LogLevel::Info, 'F', format, args...);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define XENIA_BASE_MATH_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#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;
|
||||
uint8_t rshr = 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
|
||||
template <>
|
||||
|
|
|
@ -193,7 +193,7 @@ std::unique_ptr<Socket> Socket::Connect(std::string hostname, uint16_t port) {
|
|||
InitializeWinsock();
|
||||
|
||||
auto socket = std::make_unique<Win32Socket>();
|
||||
if (!socket->Connect(std::move(hostname), port)) {
|
||||
if (!socket->Connect(hostname, port)) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<Socket>(socket.release());
|
||||
|
|
|
@ -35,7 +35,7 @@ class StringBuffer {
|
|||
|
||||
template <typename... 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());
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ void LaunchFileExplorer(const std::filesystem::path& path);
|
|||
|
||||
bool SetProcessPriorityClass(const uint32_t priority_class);
|
||||
|
||||
// Determine if the Xbox Gamebar is enabled via the Windows registry
|
||||
bool IsUseNexusForGameBarEnabled();
|
||||
|
||||
enum class SimpleMessageBoxType {
|
||||
Help,
|
||||
Warning,
|
||||
|
|
|
@ -296,4 +296,5 @@ void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
|
|||
|
||||
bool SetProcessPriorityClass(const uint32_t priority_class) { return true; }
|
||||
|
||||
bool IsUseNexusForGameBarEnabled() { return false; }
|
||||
} // namespace xe
|
||||
|
|
|
@ -69,4 +69,5 @@ void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
|
|||
|
||||
bool SetProcessPriorityClass(const uint32_t priority_class) { return true; }
|
||||
|
||||
bool IsUseNexusForGameBarEnabled() { return false; }
|
||||
} // namespace xe
|
||||
|
|
|
@ -64,4 +64,17 @@ bool SetProcessPriorityClass(const uint32_t 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
|
||||
|
|
|
@ -407,7 +407,7 @@ TEST_CASE("Wait on Multiple Events", "[event]") {
|
|||
std::array<char, 8> order = {0};
|
||||
std::atomic_uint index(0);
|
||||
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);
|
||||
};
|
||||
|
||||
|
@ -1071,8 +1071,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
|||
int is_modified;
|
||||
int has_finished;
|
||||
auto callback = [&is_modified, &order] {
|
||||
is_modified = std::atomic_fetch_add_explicit(
|
||||
&order, 1, std::memory_order::memory_order_relaxed);
|
||||
is_modified =
|
||||
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
// Without alertable
|
||||
|
@ -1084,8 +1084,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
|||
order++; // 1
|
||||
Sleep(90ms);
|
||||
order++; // 2
|
||||
has_finished = std::atomic_fetch_add_explicit(
|
||||
&order, 1, std::memory_order::memory_order_relaxed);
|
||||
has_finished =
|
||||
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||
});
|
||||
REQUIRE(!spin_wait_for(50ms, [&] { return order == 2; }));
|
||||
REQUIRE(is_modified == -1);
|
||||
|
@ -1104,8 +1104,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
|||
order++; // 1
|
||||
AlertableSleep(90ms);
|
||||
order++; // 3
|
||||
has_finished = std::atomic_fetch_add_explicit(
|
||||
&order, 1, std::memory_order::memory_order_relaxed);
|
||||
has_finished =
|
||||
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||
});
|
||||
REQUIRE(!spin_wait_for(50ms, [&] { return order == 2; }));
|
||||
REQUIRE(is_modified == -1);
|
||||
|
@ -1120,8 +1120,8 @@ TEST_CASE("Test Thread QueueUserCallback", "[thread]") {
|
|||
is_modified = -1;
|
||||
has_finished = -1;
|
||||
thread = Thread::Create(params, [&is_modified, &has_finished, &order] {
|
||||
is_modified = std::atomic_fetch_add_explicit(
|
||||
&order, 1, std::memory_order::memory_order_relaxed);
|
||||
is_modified =
|
||||
std::atomic_fetch_add_explicit(&order, 1, std::memory_order_relaxed);
|
||||
// Using Alertable so callback is registered
|
||||
order++; // 2
|
||||
AlertableSleep(1s);
|
||||
|
|
|
@ -37,121 +37,121 @@ namespace examples {
|
|||
|
||||
const size_t kDanishCount = 1;
|
||||
const char* kDanishValues[kDanishCount] = {
|
||||
u8"Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther "
|
||||
u8"spillede på xylofon.",
|
||||
"Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther "
|
||||
"spillede på xylofon.",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Danish(func, results) \
|
||||
TEST_EXAMPLES_1(func, Danish, results)
|
||||
|
||||
const size_t kGermanCount = 3;
|
||||
const char* kGermanValues[kGermanCount] = {
|
||||
u8"Falsches Üben von Xylophonmusik quält jeden größeren Zwerg",
|
||||
u8"Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich",
|
||||
u8"Heizölrückstoßabdämpfung",
|
||||
"Falsches Üben von Xylophonmusik quält jeden größeren Zwerg",
|
||||
"Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich",
|
||||
"Heizölrückstoßabdämpfung",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_German(func, results) \
|
||||
TEST_EXAMPLES_2(func, German, results)
|
||||
|
||||
const size_t kGreekCount = 2;
|
||||
const char* kGreekValues[kGreekCount] = {
|
||||
u8"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο",
|
||||
u8"Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία",
|
||||
"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο",
|
||||
"Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Greek(func, results) \
|
||||
TEST_EXAMPLES_2(func, Greek, results)
|
||||
|
||||
const size_t kEnglishCount = 1;
|
||||
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) \
|
||||
TEST_EXAMPLES_1(func, English, results)
|
||||
|
||||
const size_t kSpanishCount = 1;
|
||||
const char* kSpanishValues[kSpanishCount] = {
|
||||
u8"El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, "
|
||||
u8"añoraba a su querido cachorro.",
|
||||
"El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, "
|
||||
"añoraba a su querido cachorro.",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Spanish(func, results) \
|
||||
TEST_EXAMPLES_1(func, Spanish, results)
|
||||
|
||||
const size_t kFrenchCount = 3;
|
||||
const char* kFrenchValues[kFrenchCount] = {
|
||||
u8"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 "
|
||||
u8"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, "
|
||||
u8"pense-t-il, diminue çà et là la qualité de son œuvre.",
|
||||
u8"l'île exiguë\n"
|
||||
u8"Où l'obèse jury mûr\n"
|
||||
u8"Fête l'haï volapük,\n"
|
||||
u8"Âne ex aéquo au whist,\n"
|
||||
u8"Ô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ë "
|
||||
u8"au delà des îles, près du mälström où brûlent les novæ.",
|
||||
"Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à "
|
||||
"côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce qui "
|
||||
"lui permet de penser à la cænogenèse de l'être dont il est question "
|
||||
"dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui, "
|
||||
"pense-t-il, diminue çà et là la qualité de son œuvre.",
|
||||
"l'île exiguë\n"
|
||||
"Où l'obèse jury mûr\n"
|
||||
"Fête l'haï volapük,\n"
|
||||
"Âne ex aéquo au whist,\n"
|
||||
"Ôtez ce vœu déçu.",
|
||||
"Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë "
|
||||
"au delà des îles, près du mälström où brûlent les novæ.",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_French(func, results) \
|
||||
TEST_EXAMPLES_3(func, French, results)
|
||||
|
||||
const size_t kIrishGaelicCount = 1;
|
||||
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) \
|
||||
TEST_EXAMPLES_1(func, IrishGaelic, results)
|
||||
|
||||
const size_t kHungarianCount = 1;
|
||||
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) \
|
||||
TEST_EXAMPLES_1(func, Hungarian, results)
|
||||
|
||||
const size_t kIcelandicCount = 2;
|
||||
const char* kIcelandicValues[kIcelandicCount] = {
|
||||
u8"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",
|
||||
"Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa",
|
||||
"Sævör grét áðan því úlpan var ónýt",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Icelandic(func, results) \
|
||||
TEST_EXAMPLES_2(func, Icelandic, results)
|
||||
|
||||
const size_t kJapaneseCount = 2;
|
||||
const char* kJapaneseValues[kJapaneseCount] = {
|
||||
u8"いろはにほへとちりぬるを\n"
|
||||
u8"わかよたれそつねならむ\n"
|
||||
u8"うゐのおくやまけふこえて\n"
|
||||
u8"あさきゆめみしゑひもせす\n",
|
||||
u8"イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム\n"
|
||||
u8"ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン",
|
||||
"いろはにほへとちりぬるを\n"
|
||||
"わかよたれそつねならむ\n"
|
||||
"うゐのおくやまけふこえて\n"
|
||||
"あさきゆめみしゑひもせす\n",
|
||||
"イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム\n"
|
||||
"ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Japanese(func, results) \
|
||||
TEST_EXAMPLES_2(func, Japanese, results)
|
||||
|
||||
const size_t kHebrewCount = 1;
|
||||
const char* kHebrewValues[kHebrewCount] = {
|
||||
u8"? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה",
|
||||
"? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Hebrew(func, results) \
|
||||
TEST_EXAMPLES_1(func, Hebrew, results)
|
||||
|
||||
const size_t kPolishCount = 1;
|
||||
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) \
|
||||
TEST_EXAMPLES_1(func, Polish, results)
|
||||
|
||||
const size_t kRussianCount = 2;
|
||||
const char* kRussianValues[kRussianCount] = {
|
||||
u8"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
|
||||
u8"Съешь же ещё этих мягких французских булок да выпей чаю",
|
||||
"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
|
||||
"Съешь же ещё этих мягких французских булок да выпей чаю",
|
||||
};
|
||||
#define TEST_LANGUAGE_EXAMPLES_Russian(func, results) \
|
||||
TEST_EXAMPLES_2(func, Russian, results)
|
||||
|
||||
const size_t kTurkishCount = 1;
|
||||
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) \
|
||||
TEST_EXAMPLES_1(func, Turkish, results)
|
||||
|
@ -229,54 +229,54 @@ TEST_CASE("UTF-8 Split", "[utf8]") {
|
|||
std::vector<std::string_view> parts;
|
||||
|
||||
// Danish
|
||||
parts = utf8::split(examples::kDanishValues[0], u8"æcå");
|
||||
parts = utf8::split(examples::kDanishValues[0], "æcå");
|
||||
REQUIRE(parts.size() == 4);
|
||||
REQUIRE(parts[0] == u8"Quizdeltagerne spiste jordb");
|
||||
REQUIRE(parts[1] == u8"r med fløde, mens ");
|
||||
REQUIRE(parts[2] == u8"irkusklovnen Wolther spillede p");
|
||||
REQUIRE(parts[3] == u8" xylofon.");
|
||||
REQUIRE(parts[0] == "Quizdeltagerne spiste jordb");
|
||||
REQUIRE(parts[1] == "r med fløde, mens ");
|
||||
REQUIRE(parts[2] == "irkusklovnen Wolther spillede p");
|
||||
REQUIRE(parts[3] == " xylofon.");
|
||||
|
||||
// German
|
||||
parts = utf8::split(examples::kGermanValues[0], u8"ßS");
|
||||
parts = utf8::split(examples::kGermanValues[0], "ßS");
|
||||
REQUIRE(parts.size() == 2);
|
||||
REQUIRE(parts[0] == u8"Falsches Üben von Xylophonmusik quält jeden grö");
|
||||
REQUIRE(parts[1] == u8"eren Zwerg");
|
||||
parts = utf8::split(examples::kGermanValues[1], u8"ßS");
|
||||
REQUIRE(parts[0] == "Falsches Üben von Xylophonmusik quält jeden grö");
|
||||
REQUIRE(parts[1] == "eren Zwerg");
|
||||
parts = utf8::split(examples::kGermanValues[1], "ßS");
|
||||
REQUIRE(parts.size() == 2);
|
||||
REQUIRE(parts[0] == u8"Zwölf Boxkämpfer jagten Eva quer über den ");
|
||||
REQUIRE(parts[1] == u8"ylter Deich");
|
||||
parts = utf8::split(examples::kGermanValues[2], u8"ßS");
|
||||
REQUIRE(parts[0] == "Zwölf Boxkämpfer jagten Eva quer über den ");
|
||||
REQUIRE(parts[1] == "ylter Deich");
|
||||
parts = utf8::split(examples::kGermanValues[2], "ßS");
|
||||
REQUIRE(parts.size() == 2);
|
||||
REQUIRE(parts[0] == u8"Heizölrücksto");
|
||||
REQUIRE(parts[1] == u8"abdämpfung");
|
||||
REQUIRE(parts[0] == "Heizölrücksto");
|
||||
REQUIRE(parts[1] == "abdämpfung");
|
||||
|
||||
// Greek
|
||||
parts = utf8::split(examples::kGreekValues[0], u8"πφ");
|
||||
parts = utf8::split(examples::kGreekValues[0], "πφ");
|
||||
REQUIRE(parts.size() == 4);
|
||||
REQUIRE(parts[0] == u8"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ ");
|
||||
REQUIRE(parts[1] == u8"ιὰ στὸ χρυσα");
|
||||
REQUIRE(parts[2] == u8"ὶ ξέ");
|
||||
REQUIRE(parts[3] == u8"ωτο");
|
||||
parts = utf8::split(examples::kGreekValues[1], u8"πφ");
|
||||
REQUIRE(parts[0] == "Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ ");
|
||||
REQUIRE(parts[1] == "ιὰ στὸ χρυσα");
|
||||
REQUIRE(parts[2] == "ὶ ξέ");
|
||||
REQUIRE(parts[3] == "ωτο");
|
||||
parts = utf8::split(examples::kGreekValues[1], "πφ");
|
||||
REQUIRE(parts.size() == 3);
|
||||
REQUIRE(parts[0] == u8"Ξεσκε");
|
||||
REQUIRE(parts[1] == u8"άζω τὴν ψυχο");
|
||||
REQUIRE(parts[2] == u8"θόρα βδελυγμία");
|
||||
REQUIRE(parts[0] == "Ξεσκε");
|
||||
REQUIRE(parts[1] == "άζω τὴν ψυχο");
|
||||
REQUIRE(parts[2] == "θόρα βδελυγμία");
|
||||
|
||||
// English
|
||||
parts = utf8::split(examples::kEnglishValues[0], "xy");
|
||||
REQUIRE(parts.size() == 3);
|
||||
REQUIRE(parts[0] == u8"The quick brown fo");
|
||||
REQUIRE(parts[1] == u8" jumps over the laz");
|
||||
REQUIRE(parts[2] == u8" dog");
|
||||
REQUIRE(parts[0] == "The quick brown fo");
|
||||
REQUIRE(parts[1] == " jumps over the laz");
|
||||
REQUIRE(parts[2] == " dog");
|
||||
|
||||
// Spanish
|
||||
parts = utf8::split(examples::kSpanishValues[0], u8"ójd");
|
||||
parts = utf8::split(examples::kSpanishValues[0], "ójd");
|
||||
REQUIRE(parts.size() == 4);
|
||||
REQUIRE(parts[0] == u8"El pingüino Wenceslao hizo kil");
|
||||
REQUIRE(parts[1] == u8"metros ba");
|
||||
REQUIRE(parts[2] == u8"o exhaustiva lluvia y frío, añoraba a su queri");
|
||||
REQUIRE(parts[3] == u8"o cachorro.");
|
||||
REQUIRE(parts[0] == "El pingüino Wenceslao hizo kil");
|
||||
REQUIRE(parts[1] == "metros ba");
|
||||
REQUIRE(parts[2] == "o exhaustiva lluvia y frío, añoraba a su queri");
|
||||
REQUIRE(parts[3] == "o cachorro.");
|
||||
|
||||
// TODO(gibbed): French
|
||||
// TODO(gibbed): Irish Gaelic
|
||||
|
@ -291,18 +291,18 @@ TEST_CASE("UTF-8 Split", "[utf8]") {
|
|||
}
|
||||
|
||||
TEST_CASE("UTF-8 Equal Z", "[utf8]") {
|
||||
REQUIRE(utf8::equal_z(u8"foo", u8"foo\0"));
|
||||
REQUIRE_FALSE(utf8::equal_z(u8"bar", u8"baz\0"));
|
||||
REQUIRE(utf8::equal_z("foo", "foo\0"));
|
||||
REQUIRE_FALSE(utf8::equal_z("bar", "baz\0"));
|
||||
}
|
||||
|
||||
TEST_CASE("UTF-8 Equal Case", "[utf8]") {
|
||||
REQUIRE(utf8::equal_case(u8"foo", u8"foo\0"));
|
||||
REQUIRE_FALSE(utf8::equal_case(u8"bar", u8"baz\0"));
|
||||
REQUIRE(utf8::equal_case("foo", "foo\0"));
|
||||
REQUIRE_FALSE(utf8::equal_case("bar", "baz\0"));
|
||||
}
|
||||
|
||||
TEST_CASE("UTF-8 Equal Case Z", "[utf8]") {
|
||||
REQUIRE(utf8::equal_case_z(u8"foo", u8"foo\0"));
|
||||
REQUIRE_FALSE(utf8::equal_case_z(u8"bar", u8"baz\0"));
|
||||
REQUIRE(utf8::equal_case_z("foo", "foo\0"));
|
||||
REQUIRE_FALSE(utf8::equal_case_z("bar", "baz\0"));
|
||||
}
|
||||
|
||||
// TODO(gibbed): find_any_of
|
||||
|
@ -346,11 +346,11 @@ TEST_CASE("UTF-8 Equal Case Z", "[utf8]") {
|
|||
} while (0)
|
||||
|
||||
TEST_CASE("UTF-8 Join Paths", "[utf8]") {
|
||||
TEST_PATHS(utf8::join_paths, u8"");
|
||||
TEST_PATHS(utf8::join_paths, u8"foo", u8"foo");
|
||||
TEST_PATHS(utf8::join_paths, u8"foo/bar", u8"foo", u8"bar");
|
||||
TEST_PATHS(utf8::join_paths, "X:/foo/bar/baz/qux", u8"X:", u8"foo", u8"bar",
|
||||
u8"baz", u8"qux");
|
||||
TEST_PATHS(utf8::join_paths, "");
|
||||
TEST_PATHS(utf8::join_paths, "foo", "foo");
|
||||
TEST_PATHS(utf8::join_paths, "foo/bar", "foo", "bar");
|
||||
TEST_PATHS(utf8::join_paths, "X:/foo/bar/baz/qux", "X:", "foo", "bar", "baz",
|
||||
"qux");
|
||||
}
|
||||
|
||||
// TODO(gibbed): join_guest_paths
|
||||
|
|
|
@ -402,6 +402,7 @@ class Timer : public WaitHandle {
|
|||
virtual bool Cancel() = 0;
|
||||
};
|
||||
|
||||
#if XE_PLATFORM_WINDOWS
|
||||
struct ThreadPriority {
|
||||
static const int32_t kLowest = -2;
|
||||
static const int32_t kBelowNormal = -1;
|
||||
|
@ -409,6 +410,15 @@ struct ThreadPriority {
|
|||
static const int32_t kAboveNormal = 1;
|
||||
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.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#if XE_PLATFORM_ANDROID
|
||||
#include <dlfcn.h>
|
||||
|
||||
|
@ -660,8 +662,18 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
|||
WaitStarted();
|
||||
sched_param param{};
|
||||
param.sched_priority = new_priority;
|
||||
if (pthread_setschedparam(thread_, SCHED_FIFO, ¶m) != 0)
|
||||
int res = pthread_setschedparam(thread_, SCHED_FIFO, ¶m);
|
||||
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) {
|
||||
|
@ -905,7 +917,7 @@ class PosixEvent : public PosixConditionHandle<Event> {
|
|||
~PosixEvent() override = default;
|
||||
void Set() override { handle_.Signal(); }
|
||||
void Reset() override { handle_.Reset(); }
|
||||
EventInfo Query() {
|
||||
EventInfo Query() override {
|
||||
EventInfo result{};
|
||||
assert_always();
|
||||
return result;
|
||||
|
|
|
@ -38,7 +38,12 @@ using WaitItem = TimerQueueWaitItem;
|
|||
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 {
|
||||
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) {
|
||||
return split(path, u8"\\/", true);
|
||||
return split(path, "\\/", true);
|
||||
}
|
||||
|
||||
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) {
|
||||
static const uint32_t kMaxCommentSize = 1024;
|
||||
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';
|
||||
size_t rewind = kMaxCommentSize - 1 - result.size;
|
||||
arena_->Rewind(rewind);
|
||||
|
|
|
@ -433,17 +433,14 @@ typedef struct alignas(64) PPCContext_s {
|
|||
template <typename T = uint8_t*>
|
||||
inline T TranslateVirtual(uint32_t guest_address) XE_RESTRICT const {
|
||||
static_assert(std::is_pointer_v<T>);
|
||||
#if XE_PLATFORM_WIN32 == 1
|
||||
uint8_t* host_address = virtual_membase + guest_address;
|
||||
#if XE_PLATFORM_WIN32 == 1
|
||||
if (guest_address >=
|
||||
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this))) {
|
||||
host_address += 0x1000;
|
||||
}
|
||||
return reinterpret_cast<T>(host_address);
|
||||
#else
|
||||
return processor->memory()->TranslateVirtual<T>(guest_address);
|
||||
|
||||
#endif
|
||||
return reinterpret_cast<T>(host_address);
|
||||
}
|
||||
template <typename T>
|
||||
inline xe::be<T>* TranslateVirtualBE(uint32_t guest_address)
|
||||
|
@ -464,17 +461,14 @@ typedef struct alignas(64) PPCContext_s {
|
|||
}
|
||||
template <typename T>
|
||||
inline uint32_t HostToGuestVirtual(T* host_ptr) XE_RESTRICT const {
|
||||
#if XE_PLATFORM_WIN32 == 1
|
||||
uint32_t guest_tmp = static_cast<uint32_t>(
|
||||
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))) {
|
||||
guest_tmp -= 0x1000;
|
||||
}
|
||||
return guest_tmp;
|
||||
#else
|
||||
return processor->memory()->HostToGuestVirtual(
|
||||
reinterpret_cast<void*>(host_ptr));
|
||||
#endif
|
||||
return guest_tmp;
|
||||
}
|
||||
static std::string GetRegisterName(PPCRegister reg);
|
||||
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
|
||||
// value.
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
// Enters the global lock. Safe to recursion.
|
||||
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);
|
||||
global_mutex->lock();
|
||||
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.
|
||||
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 new_lock_count = xe::atomic_dec(global_lock_count);
|
||||
assert_true(new_lock_count >= 0);
|
||||
|
|
|
@ -151,11 +151,15 @@ void PPCTranslator::DumpHIR(GuestFunction* function, PPCHIRBuilder* builder) {
|
|||
|
||||
{
|
||||
wchar_t tmpbuf[64];
|
||||
#ifdef XE_PLATFORM_WIN32
|
||||
_snwprintf(tmpbuf, 64, L"%X", function->address());
|
||||
#else
|
||||
swprintf(tmpbuf, 64, L"%X", function->address());
|
||||
#endif
|
||||
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) {
|
||||
fputs(buffer.buffer(), f);
|
||||
fclose(f);
|
||||
|
|
|
@ -447,7 +447,7 @@ bool Processor::Restore(ByteStream* stream) {
|
|||
std::vector<uint32_t> to_delete;
|
||||
for (auto& it : thread_debug_infos_) {
|
||||
if (it.second->state == ThreadDebugInfo::State::kZombie) {
|
||||
it.second->thread_handle = NULL;
|
||||
it.second->thread_handle = 0;
|
||||
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 it = thread_debug_infos_.find(thread_id);
|
||||
assert_true(it != thread_debug_infos_.end());
|
||||
it->second->thread_handle = NULL;
|
||||
it->second->thread_handle = 0;
|
||||
thread_debug_infos_.erase(it);
|
||||
}
|
||||
|
||||
|
|
|
@ -1136,10 +1136,14 @@ void XexModule::Precompile() {
|
|||
high_code);
|
||||
final_image_sha_.finalize(image_sha_bytes_);
|
||||
|
||||
char fmtbuf[16];
|
||||
char fmtbuf[20];
|
||||
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
#ifdef XE_PLATFORM_WIN32
|
||||
sprintf_s(fmtbuf, "%X", image_sha_bytes_[i]);
|
||||
#else
|
||||
snprintf(fmtbuf, sizeof(fmtbuf), "%X", image_sha_bytes_[i]);
|
||||
#endif
|
||||
image_sha_str_ += &fmtbuf[0];
|
||||
}
|
||||
|
||||
|
@ -1397,6 +1401,10 @@ void XexInfoCache::Init(XexModule* xexmod) {
|
|||
};
|
||||
|
||||
bool did_exist = try_open();
|
||||
if (!GetHeader()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!did_exist) {
|
||||
GetHeader()->version = CURRENT_INFOCACHE_VERSION;
|
||||
|
||||
|
|
|
@ -309,7 +309,7 @@ void DebugWindow::DrawToolbar() {
|
|||
case cpu::ThreadDebugInfo::State::kAlive:
|
||||
case cpu::ThreadDebugInfo::State::kExited:
|
||||
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)");
|
||||
} else {
|
||||
thread_combo.Append(thread_info->thread->thread_name());
|
||||
|
|
|
@ -451,7 +451,7 @@ Emulator::FileSignatureType Emulator::GetFileSignature(
|
|||
}
|
||||
|
||||
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 =
|
||||
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");
|
||||
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);
|
||||
|
||||
magic_value =
|
||||
|
@ -505,6 +505,7 @@ Emulator::FileSignatureType Emulator::GetFileSignature(
|
|||
return FileSignatureType::XISO;
|
||||
}
|
||||
|
||||
XELOGE("{}: {} ({:08X})", __func__, path.extension(), magic_value);
|
||||
return FileSignatureType::Unknown;
|
||||
}
|
||||
|
||||
|
@ -718,7 +719,7 @@ X_STATUS Emulator::DataMigration(const uint64_t xuid) {
|
|||
const auto old_profile_data =
|
||||
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) {
|
||||
for (const auto& entry : old_profile_data) {
|
||||
if (entry.name == "User") {
|
||||
|
@ -835,8 +836,7 @@ X_STATUS Emulator::InstallContentPackage(
|
|||
return error_code;
|
||||
}
|
||||
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDLiveContentInstalled,
|
||||
0);
|
||||
kernel_state()->BroadcastNotification(kXNotificationLiveContentInstalled, 0);
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
@ -938,8 +938,7 @@ X_STATUS Emulator::CreateZarchivePackage(
|
|||
uint64_t total_bytes_read = 0;
|
||||
|
||||
while (total_bytes_read < file_size) {
|
||||
uint64_t bytes_read =
|
||||
fread_s(buffer.data(), buffer.size(), 1, buffer.size(), file);
|
||||
uint64_t bytes_read = fread(buffer.data(), 1, buffer.size(), file);
|
||||
|
||||
total_bytes_read += bytes_read;
|
||||
|
||||
|
@ -1373,6 +1372,12 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
|
|||
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);
|
||||
if (XFAILED(result)) {
|
||||
XELOGE("Failed to apply title update! Cannot run module {}", path);
|
||||
|
|
|
@ -245,8 +245,7 @@ class Emulator {
|
|||
ContentInstallationInfo& installation_info);
|
||||
|
||||
// Extract content of zar package to desired directory.
|
||||
X_STATUS Emulator::ExtractZarchivePackage(
|
||||
const std::filesystem::path& path,
|
||||
X_STATUS ExtractZarchivePackage(const std::filesystem::path& path,
|
||||
const std::filesystem::path& extract_dir);
|
||||
|
||||
// Pack contents of a folder into a zar package.
|
||||
|
|
|
@ -580,10 +580,17 @@ static inline void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
|||
__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_br_br_x, pa_sc_screen_scissor_br_br_y);
|
||||
#if XE_PLATFORM_WIN32
|
||||
__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_y_offset)
|
||||
<< 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);
|
||||
// chrispy: put this here to make it clear that the shift by 31 is extracting
|
||||
// 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;
|
||||
}
|
||||
XE_FORCEINLINE
|
||||
bool IsPrimitivePolygonal(const RegisterFile& regs) {
|
||||
static bool IsPrimitivePolygonal(const RegisterFile& regs) {
|
||||
return IsPrimitivePolygonal(
|
||||
regs.Get<reg::VGT_OUTPUT_PATH_CNTL>().path_select ==
|
||||
xenos::VGTOutputPath::kTessellationEnable,
|
||||
|
@ -383,7 +383,7 @@ struct GetViewportInfoArgs {
|
|||
depth_format = regs.Get<reg::RB_DEPTH_INFO>().depth_format;
|
||||
}
|
||||
XE_FORCEINLINE
|
||||
bool operator==(const GetViewportInfoArgs& prev) {
|
||||
bool operator==(const GetViewportInfoArgs& prev) const {
|
||||
#if XE_ARCH_AMD64 == 0
|
||||
bool result = true;
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ DEFINE_uint32(internal_display_resolution, 8,
|
|||
"Allow games that support different resolutions to render "
|
||||
"in a specific resolution.\n"
|
||||
"This is not guaranteed to work with all games or improve "
|
||||
"performance."
|
||||
"performance.\n"
|
||||
" 0=640x480\n"
|
||||
" 1=640x576\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) {
|
||||
#if XE_ARCH_AMD64 == 1
|
||||
#if XE_ARCH_AMD64 == 1 && XE_PLATFORM_WIN32
|
||||
if (!v) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -94,9 +94,9 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length) {
|
|||
|
||||
static int dump_counter = 0;
|
||||
std::filesystem::path path = "texture_dumps";
|
||||
path /= fmt::format("{:05d}_{:08X}_{:08X}_{:08X}.dds", dump_counter++,
|
||||
src.memory.base_address, src.memory.mip_address,
|
||||
src.format_name());
|
||||
path /= fmt::format(fmt::runtime("{:05d}_{:08X}_{:08X}_{:08X}.dds"),
|
||||
dump_counter++, src.memory.base_address,
|
||||
src.memory.mip_address, src.format_name());
|
||||
|
||||
FILE* handle = filesystem::OpenFile(path, "wb");
|
||||
if (handle) {
|
||||
|
|
|
@ -89,7 +89,7 @@ void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
|||
TracePlaybackMode playback_mode,
|
||||
bool clear_caches) {
|
||||
playing_trace_ = true;
|
||||
graphics_system_->command_processor()->CallInThread([=]() {
|
||||
graphics_system_->command_processor()->CallInThread([=, this]() {
|
||||
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ TraceViewer::TraceViewer(xe::ui::WindowedAppContext& app_context,
|
|||
TraceViewer::~TraceViewer() = default;
|
||||
|
||||
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.
|
||||
// On Android, however, there's no synchronous file picker, and the trace file
|
||||
|
@ -899,9 +899,11 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
|||
} break;
|
||||
case xenos::VertexFormat::k_16_16_FLOAT: {
|
||||
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::Text("%.2f", half_float::detail::uint16((e0 >> 0) & 0xFFFF));
|
||||
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||
(e0 >> 0) & 0xFFFF)));
|
||||
ImGui::NextColumn();
|
||||
} break;
|
||||
case xenos::VertexFormat::k_32_32:
|
||||
|
@ -972,13 +974,17 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
|||
case xenos::VertexFormat::k_16_16_16_16_FLOAT: {
|
||||
auto e0 = LOADEL(uint32_t, 0);
|
||||
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::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::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::Text("%.2f", half_float::detail::uint16((e1 >> 0) & 0xFFFF));
|
||||
ImGui::Text("%.2f", static_cast<float>(half_float::detail::uint16(
|
||||
(e1 >> 0) & 0xFFFF)));
|
||||
ImGui::NextColumn();
|
||||
} break;
|
||||
case xenos::VertexFormat::k_32_32_32_32_FLOAT:
|
||||
|
|
|
@ -26,6 +26,22 @@ enum X_INPUT_CAPS {
|
|||
|
||||
enum X_INPUT_FLAG {
|
||||
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 {
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace hid {
|
|||
|
||||
class InputSystem;
|
||||
|
||||
enum InputType { None, Controller = 1, Keyboard = 2, Other = 4 };
|
||||
|
||||
class InputDriver {
|
||||
public:
|
||||
virtual ~InputDriver() = default;
|
||||
|
@ -42,6 +44,8 @@ class InputDriver {
|
|||
virtual X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) = 0;
|
||||
|
||||
virtual InputType GetInputType() const = 0;
|
||||
|
||||
void set_is_active_callback(std::function<bool()> 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,
|
||||
bool connected) {
|
||||
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) {
|
||||
|
@ -53,7 +59,7 @@ void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot,
|
|||
connected_slots.flip(slot);
|
||||
if (kernel::kernel_state()) {
|
||||
kernel::kernel_state()->BroadcastNotification(
|
||||
kXNotificationIDSystemInputDevicesChanged, 0);
|
||||
kXNotificationSystemInputDevicesChanged, 0);
|
||||
}
|
||||
|
||||
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_INPUT_CAPABILITIES* out_caps) {
|
||||
SCOPE_profile_cpu_f("hid");
|
||||
|
||||
bool any_connected = false;
|
||||
for (auto& driver : drivers_) {
|
||||
std::vector<InputDriver*> filtered_drivers = FilterDrivers(flags);
|
||||
|
||||
for (auto& driver : filtered_drivers) {
|
||||
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) {
|
||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
return 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");
|
||||
|
||||
bool any_connected = false;
|
||||
for (auto& driver : drivers_) {
|
||||
std::vector<InputDriver*> filtered_drivers = FilterDrivers(flags);
|
||||
|
||||
for (auto& driver : filtered_drivers) {
|
||||
X_RESULT result = driver->GetState(user_index, out_state);
|
||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||
any_connected = true;
|
||||
}
|
||||
if (result == X_ERROR_SUCCESS) {
|
||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
||||
UpdateUsedSlot(driver, user_index, true);
|
||||
AdjustDeadzoneLevels(user_index, &out_state->gamepad);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
UpdateUsedSlot(nullptr, user_index, false);
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
X_RESULT InputSystem::SetState(uint32_t user_index,
|
||||
X_INPUT_VIBRATION* vibration) {
|
||||
SCOPE_profile_cpu_f("hid");
|
||||
X_INPUT_VIBRATION modified_vibration = ModifyVibrationLevel(vibration);
|
||||
bool any_connected = false;
|
||||
for (auto& driver : drivers_) {
|
||||
X_RESULT result = driver->SetState(user_index, &modified_vibration);
|
||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||
any_connected = true;
|
||||
}
|
||||
if (result == X_ERROR_SUCCESS) {
|
||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) {
|
||||
SCOPE_profile_cpu_f("hid");
|
||||
|
||||
std::vector<InputDriver*> filtered_drivers = FilterDrivers(flags);
|
||||
|
||||
bool any_connected = false;
|
||||
for (auto& driver : drivers_) {
|
||||
for (auto& driver : filtered_drivers) {
|
||||
// connected_slots
|
||||
X_RESULT result = driver->GetKeystroke(user_index, flags, out_keystroke);
|
||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||
any_connected = true;
|
||||
if (result == X_ERROR_INVALID_PARAMETER ||
|
||||
result == X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||
continue;
|
||||
}
|
||||
if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) {
|
||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
||||
|
||||
any_connected = true;
|
||||
|
||||
if (result == X_ERROR_SUCCESS) {
|
||||
last_used_slot = user_index;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,13 +40,13 @@ class InputSystem {
|
|||
|
||||
X_RESULT GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||
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 GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke);
|
||||
|
||||
bool GetVibrationCvar();
|
||||
|
||||
void ToggleVibration();
|
||||
|
||||
const std::bitset<XUserMaxUserCount> GetConnectedSlots() const {
|
||||
|
@ -68,6 +68,8 @@ class InputSystem {
|
|||
void AdjustDeadzoneLevels(const uint8_t slot, X_INPUT_GAMEPAD* gamepad);
|
||||
X_INPUT_VIBRATION ModifyVibrationLevel(X_INPUT_VIBRATION* vibration);
|
||||
|
||||
std::vector<InputDriver*> FilterDrivers(uint32_t flags);
|
||||
|
||||
xe::ui::Window* window_ = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
InputType NopInputDriver::GetInputType() const { return InputType::Other; }
|
||||
|
||||
} // namespace nop
|
||||
} // namespace hid
|
||||
} // 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 GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||
virtual InputType GetInputType() const override;
|
||||
};
|
||||
|
||||
} // namespace nop
|
||||
|
|
|
@ -155,7 +155,7 @@ void SDLInputDriver::LoadGameControllerDB() {
|
|||
std::string guid = row[0];
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -429,6 +429,8 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags,
|
|||
return X_ERROR_EMPTY;
|
||||
}
|
||||
|
||||
InputType SDLInputDriver::GetInputType() const { return InputType::Controller; }
|
||||
|
||||
void SDLInputDriver::HandleEvent(const SDL_Event& event) {
|
||||
// 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.
|
||||
|
|
|
@ -44,6 +44,7 @@ class SDLInputDriver final : public InputDriver {
|
|||
X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override;
|
||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||
virtual InputType GetInputType() const override;
|
||||
|
||||
private:
|
||||
struct ControllerState {
|
||||
|
|
|
@ -25,13 +25,36 @@
|
|||
#include "winkey_binding_table.inc"
|
||||
#undef XE_HID_WINKEY_BINDING
|
||||
|
||||
DEFINE_int32(keyboard_user_index, 0, "Controller port that keyboard emulates",
|
||||
"HID.WinKey");
|
||||
DEFINE_int32(keyboard_mode, 0,
|
||||
"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 hid {
|
||||
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) {
|
||||
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_INPUT_CAPABILITIES* out_caps) {
|
||||
if (user_index != cvars::keyboard_user_index) {
|
||||
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
// TODO(benvanik): confirm with a real XInput controller.
|
||||
out_caps->type = 0x01; // XINPUT_DEVTYPE_GAMEPAD
|
||||
out_caps->sub_type = 0x01; // XINPUT_DEVSUBTYPE_GAMEPAD
|
||||
if (IsPassthroughEnabled()) {
|
||||
out_caps->type = X_INPUT_DEVTYPE::XINPUT_DEVTYPE_KEYBOARD;
|
||||
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->gamepad.buttons = 0xFFFF;
|
||||
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_INPUT_STATE* out_state) {
|
||||
if (user_index != cvars::keyboard_user_index) {
|
||||
if (!IsKeyboardForUserEnabled(user_index)) {
|
||||
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_ry = thumb_ry;
|
||||
|
||||
if (IsPassthroughEnabled()) {
|
||||
memset(out_state, 0, sizeof(out_state));
|
||||
}
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
|
||||
X_INPUT_VIBRATION* vibration) {
|
||||
if (user_index != cvars::keyboard_user_index) {
|
||||
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||
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_INPUT_KEYSTROKE* out_keystroke) {
|
||||
if (user_index != cvars::keyboard_user_index) {
|
||||
if (!is_active()) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (!is_active()) {
|
||||
return X_ERROR_EMPTY;
|
||||
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||
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.
|
||||
KeyEvent evt;
|
||||
{
|
||||
|
@ -278,7 +302,17 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
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);
|
||||
|
||||
if (!IsPassthroughEnabled()) {
|
||||
if (IsKeyboardForUserEnabled(user_index)) {
|
||||
for (const KeyBinding& b : key_bindings_) {
|
||||
if (b.input_key == evt.virtual_key &&
|
||||
((b.lowercase == b.uppercase) || (b.lowercase && !capital) ||
|
||||
|
@ -286,16 +320,42 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
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 (evt.transition == true) {
|
||||
keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN
|
||||
if (evt.prev_state == evt.transition) {
|
||||
keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT
|
||||
}
|
||||
} else if (evt.transition == false) {
|
||||
keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP
|
||||
}
|
||||
|
||||
if (evt.prev_state == evt.transition) {
|
||||
keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT
|
||||
if (IsPassthroughEnabled()) {
|
||||
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;
|
||||
|
@ -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->unicode = unicode;
|
||||
out_keystroke->flags = keystroke_flags;
|
||||
out_keystroke->user_index = 0;
|
||||
out_keystroke->user_index = user_index;
|
||||
out_keystroke->hid_code = hid_code;
|
||||
|
||||
// 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) {
|
||||
if (!is_active()) {
|
||||
if (!is_active() || static_cast<KeyboardMode>(cvars::keyboard_mode) ==
|
||||
KeyboardMode::Disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -336,6 +397,20 @@ void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) {
|
|||
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 hid
|
||||
} // namespace xe
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace xe {
|
|||
namespace hid {
|
||||
namespace winkey {
|
||||
|
||||
enum class KeyboardMode { Disabled, Enabled, Passthrough };
|
||||
|
||||
class WinKeyInputDriver final : public InputDriver {
|
||||
public:
|
||||
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 GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||
virtual InputType GetInputType() const override;
|
||||
|
||||
protected:
|
||||
struct KeyEvent {
|
||||
|
@ -72,7 +75,7 @@ class WinKeyInputDriver final : public InputDriver {
|
|||
xe::global_critical_region global_critical_region_;
|
||||
std::queue<KeyEvent> key_events_;
|
||||
std::vector<KeyBinding> key_bindings_;
|
||||
|
||||
uint8_t key_map_[256];
|
||||
uint32_t packet_number_ = 1;
|
||||
};
|
||||
|
||||
|
|
|
@ -110,7 +110,9 @@ X_RESULT XInputInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
|
|||
}
|
||||
XINPUT_CAPABILITIES native_caps;
|
||||
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 == ERROR_DEVICE_NOT_CONNECTED) {
|
||||
set_skip(user_index);
|
||||
|
@ -239,6 +241,10 @@ X_RESULT XInputInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
return result;
|
||||
}
|
||||
|
||||
InputType XInputInputDriver::GetInputType() const {
|
||||
return InputType::Controller;
|
||||
}
|
||||
|
||||
} // namespace xinput
|
||||
} // namespace hid
|
||||
} // 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 GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||
virtual InputType GetInputType() const override;
|
||||
|
||||
private:
|
||||
void* module_;
|
||||
|
|
|
@ -867,11 +867,11 @@ void KernelState::RegisterNotifyListener(XNotifyListener* listener) {
|
|||
if (!has_notified_startup_ && listener->mask() & kXNotifySystem) {
|
||||
has_notified_startup_ = true;
|
||||
// XN_SYS_UI (on, off)
|
||||
listener->EnqueueNotification(kXNotificationIDSystemUI, 1);
|
||||
listener->EnqueueNotification(kXNotificationIDSystemUI, 0);
|
||||
listener->EnqueueNotification(kXNotificationSystemUI, 1);
|
||||
listener->EnqueueNotification(kXNotificationSystemUI, 0);
|
||||
// XN_SYS_SIGNINCHANGED x2
|
||||
listener->EnqueueNotification(kXNotificationIDSystemSignInChanged, 1);
|
||||
listener->EnqueueNotification(kXNotificationIDSystemSignInChanged, 1);
|
||||
listener->EnqueueNotification(kXNotificationSystemSignInChanged, 1);
|
||||
listener->EnqueueNotification(kXNotificationSystemSignInChanged, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class NativeList {
|
|||
};
|
||||
template <typename VirtualTranslator>
|
||||
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>
|
||||
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++() {
|
||||
current_entry =
|
||||
vt->TranslateVirtual<X_LIST_ENTRY*>(current_entry)->flink_ptr;
|
||||
vt->template TranslateVirtual<X_LIST_ENTRY*>(current_entry)
|
||||
->flink_ptr;
|
||||
return *this;
|
||||
}
|
||||
inline bool operator!=(uint32_t other_ptr) const {
|
||||
|
@ -202,7 +203,7 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
|||
|
||||
inline TObject& operator*() {
|
||||
return *ListEntryObject(
|
||||
vt->TranslateVirtual<X_LIST_ENTRY*>(current_entry));
|
||||
vt->template TranslateVirtual<X_LIST_ENTRY*>(current_entry));
|
||||
}
|
||||
};
|
||||
template <typename VirtualTranslator>
|
||||
|
@ -236,15 +237,17 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
|||
}
|
||||
template <typename VirtualTranslator>
|
||||
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>
|
||||
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>
|
||||
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
|
||||
|
|
|
@ -77,4 +77,4 @@ class AttributeStringFormatter {
|
|||
} // namespace kernel
|
||||
} // 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);
|
||||
}
|
||||
|
||||
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,
|
||||
uint32_t guest_address) {
|
||||
if (!guest_address) {
|
||||
|
|
|
@ -43,8 +43,7 @@ void GpdAchievementBackend::EarnAchievement(const uint64_t xuid,
|
|||
achievement->flags = achievement->flags |
|
||||
static_cast<uint32_t>(AchievementFlags::kAchieved) |
|
||||
static_cast<uint32_t>(AchievementFlags::kAchievedOnline);
|
||||
achievement->unlock_time.high_part = static_cast<uint32_t>(unlock_time >> 32);
|
||||
achievement->unlock_time.low_part = static_cast<uint32_t>(unlock_time);
|
||||
achievement->unlock_time = unlock_time;
|
||||
|
||||
SaveAchievementData(xuid, title_id, achievement_id);
|
||||
}
|
||||
|
@ -72,6 +71,10 @@ bool GpdAchievementBackend::IsAchievementUnlocked(
|
|||
const auto achievement =
|
||||
GetAchievementInfoInternal(xuid, title_id, achievement_id);
|
||||
|
||||
if (!achievement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (achievement->flags &
|
||||
static_cast<uint32_t>(AchievementFlags::kAchieved)) != 0;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/chrono.h"
|
||||
#include "xenia/kernel/util/xdbf_utils.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -53,6 +54,32 @@ enum class AchievementFlags : uint32_t {
|
|||
struct X_ACHIEVEMENT_UNLOCK_TIME {
|
||||
xe::be<uint32_t> high_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 {
|
||||
|
|
|
@ -108,7 +108,7 @@ X_HRESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
|
|||
XELOGD("XMPPlayTitlePlaylist({:08X}, {:08X})", playlist_handle, song_handle);
|
||||
kernel_state_->emulator()->audio_media_player()->Play(playlist_handle,
|
||||
song_handle, false);
|
||||
kernel_state_->BroadcastNotification(kNotificationXmpPlaybackBehaviorChanged,
|
||||
kernel_state_->BroadcastNotification(kXNotificationXmpPlaybackBehaviorChanged,
|
||||
1);
|
||||
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)));
|
||||
|
||||
kernel_state_->BroadcastNotification(
|
||||
kNotificationXmpPlaybackBehaviorChanged, 0);
|
||||
kXNotificationXmpPlaybackBehaviorChanged, 0);
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
case 0x00070009: {
|
||||
|
@ -359,7 +359,7 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
|||
PlaybackClient(uint32_t(args->playback_client)));
|
||||
|
||||
kernel_state_->BroadcastNotification(
|
||||
kNotificationXmpPlaybackControllerChanged,
|
||||
kXNotificationXmpPlaybackControllerChanged,
|
||||
kernel_state_->emulator()
|
||||
->audio_media_player()
|
||||
->IsTitleInPlaybackControl());
|
||||
|
|
|
@ -103,7 +103,14 @@ std::filesystem::path ContentManager::ResolvePackagePath(
|
|||
// Content path:
|
||||
// content_root/title_id/content_type/data_file_name/
|
||||
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 =
|
||||
ResolvePackageRoot(used_xuid, title_id, data.content_type);
|
||||
|
@ -263,7 +270,8 @@ X_RESULT ContentManager::WriteContentHeaderFile(const uint64_t xuid,
|
|||
if (data.xuid == -1) {
|
||||
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,
|
||||
data.title_id, data.content_type);
|
||||
|
|
|
@ -253,9 +253,12 @@ void ProfileManager::ModifyGamertag(const uint64_t xuid, std::string gamertag) {
|
|||
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::string mount_path = fmt::format("{:016X}", xuid) + ':';
|
||||
if (mount_path.empty()) {
|
||||
mount_path = fmt::format("{:016X}", xuid);
|
||||
}
|
||||
mount_path += ':';
|
||||
|
||||
auto device =
|
||||
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] =
|
||||
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) {
|
||||
kernel_state_->BroadcastNotification(kXNotificationIDSystemSignInChanged,
|
||||
kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged,
|
||||
GetUsedUserSlots().to_ulong());
|
||||
}
|
||||
UpdateConfig(xuid, assigned_user_slot);
|
||||
|
@ -329,7 +341,7 @@ void ProfileManager::Logout(const uint8_t user_index, bool notify) {
|
|||
DismountProfile(profile->second->xuid());
|
||||
logged_profiles_.erase(profile);
|
||||
if (notify) {
|
||||
kernel_state_->BroadcastNotification(kXNotificationIDSystemSignInChanged,
|
||||
kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged,
|
||||
GetUsedUserSlots().to_ulong());
|
||||
}
|
||||
UpdateConfig(0, user_index);
|
||||
|
@ -343,7 +355,7 @@ void ProfileManager::LoginMultiple(
|
|||
slots_mask |= (1 << slot);
|
||||
}
|
||||
|
||||
kernel_state_->BroadcastNotification(kXNotificationIDSystemSignInChanged,
|
||||
kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged,
|
||||
slots_mask);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ class ProfileManager {
|
|||
|
||||
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);
|
||||
|
||||
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
|
||||
AddSetting(std::make_unique<UserSetting>(0x10040018, 0));
|
||||
// Preferred color 1
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001D, 0xFFFF0000u));
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001D, PREFERRED_COLOR_NONE));
|
||||
// Preferred color 2
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001E, 0xFF00FF00u));
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001E, PREFERRED_COLOR_NONE));
|
||||
// XPROFILE_GAMER_ACTION_AUTO_AIM
|
||||
AddSetting(std::make_unique<UserSetting>(0x10040022, 1));
|
||||
// XPROFILE_GAMER_ACTION_AUTO_CENTER
|
||||
|
@ -170,7 +170,8 @@ void UserProfile::LoadSetting(UserSetting* setting) {
|
|||
} else {
|
||||
// Unsupported for now. Other settings aren't per-game and need to be
|
||||
// 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 {
|
||||
// Unsupported for now. Other settings aren't per-game and need to be
|
||||
// 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,
|
||||
};
|
||||
|
||||
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
|
||||
struct X_USER_PROFILE_SETTING_HEADER {
|
||||
xe::be<uint32_t> setting_id;
|
||||
|
@ -165,6 +180,10 @@ class UserProfile {
|
|||
uint32_t GetSubscriptionTier() const {
|
||||
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);
|
||||
UserSetting* GetSetting(uint32_t setting_id);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -17,24 +17,248 @@ namespace xe {
|
|||
namespace kernel {
|
||||
namespace xam {
|
||||
|
||||
// Start/End
|
||||
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 processor_number, // for thread creation?
|
||||
lpdword_t function_ptrs, // 20b, 5 pointers
|
||||
lpunknown_t unk5, // ptr in data segment
|
||||
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.
|
||||
return ~0u;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarInitialize, kAvatars, kStub);
|
||||
|
||||
void XamAvatarShutdown_entry() {
|
||||
// No-op.
|
||||
// Calls XMsgStartIORequestEx(0xf3,0x600002,0,0,0,0).
|
||||
}
|
||||
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 kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -74,9 +74,9 @@ DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency);
|
|||
|
||||
dword_result_t XamContentResolve_entry(dword_t user_index,
|
||||
lpvoid_t content_data_ptr,
|
||||
lpunknown_t buffer_ptr,
|
||||
dword_t buffer_size, dword_t unk1,
|
||||
dword_t unk2, dword_t unk3) {
|
||||
lpvoid_t buffer_ptr, dword_t buffer_size,
|
||||
dword_t unk1, dword_t unk2,
|
||||
dword_t unk3) {
|
||||
auto content_data = content_data_ptr.as<XCONTENT_DATA*>();
|
||||
|
||||
// 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);
|
||||
|
||||
dword_result_t XamContentFlush_entry(lpstring_t root_name,
|
||||
lpunknown_t overlapped_ptr) {
|
||||
lpvoid_t overlapped_ptr) {
|
||||
X_RESULT result = X_ERROR_SUCCESS;
|
||||
if (overlapped_ptr) {
|
||||
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);
|
||||
|
||||
dword_result_t XamContentClose_entry(lpstring_t root_name,
|
||||
lpunknown_t overlapped_ptr) {
|
||||
lpvoid_t overlapped_ptr) {
|
||||
// Closes a previously opened root from XamContentCreate*.
|
||||
auto result =
|
||||
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,
|
||||
lpdword_t is_creator_ptr,
|
||||
lpqword_t creator_xuid_ptr,
|
||||
lpunknown_t overlapped_ptr) {
|
||||
lpvoid_t overlapped_ptr) {
|
||||
if (!is_creator_ptr) {
|
||||
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 buffer_ptr,
|
||||
lpdword_t buffer_size_ptr,
|
||||
lpunknown_t overlapped_ptr) {
|
||||
lpvoid_t overlapped_ptr) {
|
||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
|
||||
if (!user) {
|
||||
|
@ -494,7 +494,7 @@ dword_result_t XamContentSetThumbnail_entry(dword_t user_index,
|
|||
lpvoid_t content_data_ptr,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_size,
|
||||
lpunknown_t overlapped_ptr) {
|
||||
lpvoid_t overlapped_ptr) {
|
||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
|
||||
if (!user) {
|
||||
|
@ -518,10 +518,15 @@ dword_result_t XamContentSetThumbnail_entry(dword_t user_index,
|
|||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented);
|
||||
|
||||
dword_result_t XamContentDelete_entry(dword_t user_index,
|
||||
lpvoid_t content_data_ptr,
|
||||
lpunknown_t overlapped_ptr) {
|
||||
dword_result_t xeXamContentDelete(dword_t user_index, lpvoid_t content_data_ptr,
|
||||
dword_t content_data_size,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
|
||||
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
|
||||
|
||||
auto result =
|
||||
kernel_state()->content_manager()->DeleteContent(xuid, content_data);
|
||||
|
||||
|
@ -544,14 +547,22 @@ dword_result_t XamContentDelete_entry(dword_t user_index,
|
|||
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);
|
||||
|
||||
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
|
||||
// 0xFE as user_index
|
||||
return XamContentDelete_entry(XUserIndexNone, content_data_ptr,
|
||||
overlapped_ptr);
|
||||
// 0xFE as user_index.
|
||||
// In XAM content size is set to 0x200.
|
||||
return xeXamContentDelete(XUserIndexNone, content_data_ptr,
|
||||
sizeof(XCONTENT_AGGREGATE_DATA), overlapped_ptr);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented);
|
||||
|
||||
|
@ -621,8 +632,8 @@ DECLARE_XAM_EXPORT1(XamLoaderGetMediaInfoEx, kContent, kStub);
|
|||
|
||||
dword_result_t XamContentLaunchImageFromFileInternal_entry(
|
||||
lpstring_t image_location, lpstring_t xex_name, dword_t unk) {
|
||||
const std::string image_path = image_location;
|
||||
const std::string xex_name_ = xex_name;
|
||||
const std::string image_path = static_cast<std::string>(image_location);
|
||||
const std::string xex_name_ = static_cast<std::string>(xex_name);
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
|
|
|
@ -115,8 +115,9 @@ dword_result_t XamContentAggregateCreateEnumerator_entry(qword_t xuid,
|
|||
for (auto& title_id : title_ids) {
|
||||
// Get all content data.
|
||||
auto content_datas = kernel_state()->content_manager()->ListContent(
|
||||
static_cast<uint32_t>(DummyDeviceId::HDD), xuid == -1 ? 0 : xuid,
|
||||
title_id, content_type_enum);
|
||||
static_cast<uint32_t>(DummyDeviceId::HDD),
|
||||
xuid == -1 ? 0 : static_cast<uint64_t>(xuid), title_id,
|
||||
content_type_enum);
|
||||
for (const auto& content_data : content_datas) {
|
||||
auto item = e->AppendItem();
|
||||
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);
|
||||
|
||||
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 kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -27,10 +27,6 @@
|
|||
#include "xenia/ui/windowed_app_context.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/xchar.h"
|
||||
|
||||
|
@ -91,51 +87,37 @@ dword_result_t XamGetOnlineSchema_entry() {
|
|||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetOnlineSchema, kNone, kImplemented);
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
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,
|
||||
void XamFormatDateString_entry(dword_t locale_format, qword_t filetime,
|
||||
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
|
||||
#if XE_PLATFORM_WIN32
|
||||
auto st = xeGetLocalSystemTime(filetime);
|
||||
// TODO: format this depending on users locale?
|
||||
auto str = fmt::format(u"{:02d}/{:02d}/{}", st.wMonth, st.wDay, st.wYear);
|
||||
auto tp = xe::chrono::WinSystemClock::to_sys(
|
||||
xe::chrono::WinSystemClock::from_file_time(filetime));
|
||||
auto dp = date::floor<date::days>(tp);
|
||||
auto year_month_day = date::year_month_day{dp};
|
||||
|
||||
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,
|
||||
output_count);
|
||||
#else
|
||||
assert_always();
|
||||
#endif
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamFormatDateString, kNone, kImplemented);
|
||||
|
||||
void XamFormatTimeString_entry(dword_t unk, qword_t filetime,
|
||||
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
|
||||
#if XE_PLATFORM_WIN32
|
||||
auto st = xeGetLocalSystemTime(filetime);
|
||||
// TODO: format this depending on users locale?
|
||||
auto str = fmt::format(u"{:02d}:{:02d}", st.wHour, st.wMinute);
|
||||
auto tp = xe::chrono::WinSystemClock::to_sys(
|
||||
xe::chrono::WinSystemClock::from_file_time(filetime));
|
||||
auto dp = date::floor<date::days>(tp);
|
||||
auto time = date::hh_mm_ss{date::floor<std::chrono::milliseconds>(tp - dp)};
|
||||
|
||||
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,
|
||||
output_count);
|
||||
#else
|
||||
assert_always();
|
||||
#endif
|
||||
}
|
||||
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/08/13/building-an-xbox-360-emulator-part-5-xex-files/
|
||||
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
|
||||
delay.QuadPart = dwMilliseconds == -1
|
||||
? LLONG_MAX
|
||||
: static_cast<LONGLONG>(-10000) * dwMilliseconds;
|
||||
delay = dwMilliseconds == -1 ? LLONG_MAX
|
||||
: static_cast<int64_t>(-10000) * dwMilliseconds;
|
||||
|
||||
X_STATUS result = xboxkrnl::KeDelayExecutionThread(
|
||||
MODE::UserMode, bAlertable, (uint64_t*)&delay, nullptr);
|
||||
X_STATUS result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
|
||||
&delay, nullptr);
|
||||
|
||||
// If the delay was interrupted by an APC, keep delaying the thread
|
||||
while (bAlertable && result == X_STATUS_ALERTED) {
|
||||
result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable,
|
||||
(uint64_t*)&delay, nullptr);
|
||||
&delay, nullptr);
|
||||
}
|
||||
|
||||
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
|
||||
void Sleep_entry(dword_t dwMilliseconds) {
|
||||
RtlSleep_entry(dwMilliseconds, FALSE);
|
||||
RtlSleep_entry(dwMilliseconds, false);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(Sleep, kNone, kImplemented);
|
||||
|
||||
|
@ -619,28 +600,29 @@ DECLARE_XAM_EXPORT1(GetCurrentThreadId, kNone, kImplemented);
|
|||
|
||||
qword_result_t XapiFormatTimeOut_entry(lpqword_t result,
|
||||
dword_t dwMilliseconds) {
|
||||
LARGE_INTEGER delay{};
|
||||
if (dwMilliseconds == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert the delay time to 100-nanosecond intervals
|
||||
delay.QuadPart =
|
||||
dwMilliseconds == -1 ? 0 : static_cast<LONGLONG>(-10000) * dwMilliseconds;
|
||||
|
||||
return (uint64_t)&delay;
|
||||
*result = static_cast<int64_t>(-10000) * dwMilliseconds;
|
||||
return result.host_address();
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XapiFormatTimeOut, kNone, kImplemented);
|
||||
|
||||
dword_result_t WaitForSingleObjectEx_entry(dword_t hHandle,
|
||||
dword_t dwMilliseconds,
|
||||
dword_t bAlertable) {
|
||||
uint64_t* timeout = nullptr;
|
||||
uint64_t timeout_ptr = XapiFormatTimeOut_entry(timeout, dwMilliseconds);
|
||||
// TODO(Gliniak): Figure it out to be less janky.
|
||||
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(
|
||||
hHandle, 1, bAlertable, &timeout_ptr);
|
||||
X_STATUS result =
|
||||
xboxkrnl::NtWaitForSingleObjectEx(hHandle, 1, bAlertable, timeout_ptr);
|
||||
|
||||
while (bAlertable && result == X_STATUS_ALERTED) {
|
||||
result = xe::kernel::xboxkrnl::NtWaitForSingleObjectEx(
|
||||
hHandle, 1, bAlertable, &timeout_ptr);
|
||||
result =
|
||||
xboxkrnl::NtWaitForSingleObjectEx(hHandle, 1, bAlertable, timeout_ptr);
|
||||
}
|
||||
|
||||
RtlSetLastNTError_entry(result);
|
||||
|
@ -716,6 +698,9 @@ dword_result_t RtlRandom_entry(lpdword_t seed_out) {
|
|||
|
||||
DECLARE_XAM_EXPORT1(RtlRandom, kNone, kImplemented);
|
||||
|
||||
dword_result_t Refresh_entry() { return X_ERROR_SUCCESS; }
|
||||
DECLARE_XAM_EXPORT1(Refresh, kNone, kStub);
|
||||
|
||||
} // namespace xam
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -21,16 +21,11 @@ namespace kernel {
|
|||
namespace xam {
|
||||
|
||||
using xe::hid::X_INPUT_CAPABILITIES;
|
||||
using xe::hid::X_INPUT_FLAG;
|
||||
using xe::hid::X_INPUT_KEYSTROKE;
|
||||
using xe::hid::X_INPUT_STATE;
|
||||
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) {
|
||||
if (user_index >= XUserMaxUserCount) {
|
||||
return 0;
|
||||
|
@ -65,7 +60,7 @@ dword_result_t XamInputGetCapabilitiesEx_entry(
|
|||
|
||||
caps.Zero();
|
||||
|
||||
if ((flags & XINPUT_FLAG_ANY_USER) != 0) {
|
||||
if ((flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER) != 0) {
|
||||
// should trap
|
||||
}
|
||||
|
||||
|
@ -73,21 +68,22 @@ dword_result_t XamInputGetCapabilitiesEx_entry(
|
|||
// 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;
|
||||
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.
|
||||
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 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);
|
||||
|
||||
|
@ -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.
|
||||
|
||||
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;
|
||||
// chrispy: change this, logic is not right
|
||||
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.
|
||||
actual_user_index = 0;
|
||||
}
|
||||
|
||||
auto input_system = kernel_state()->emulator()->input_system();
|
||||
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);
|
||||
|
||||
|
@ -160,14 +153,9 @@ dword_result_t XamInputGetKeystroke_entry(
|
|||
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;
|
||||
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.
|
||||
actual_user_index = 0;
|
||||
}
|
||||
|
@ -186,11 +174,6 @@ dword_result_t XamInputGetKeystrokeEx_entry(
|
|||
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;
|
||||
auto input_system = kernel_state()->emulator()->input_system();
|
||||
auto lock = input_system->lock();
|
||||
|
@ -199,7 +182,7 @@ dword_result_t XamInputGetKeystrokeEx_entry(
|
|||
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
|
||||
// check which one have pending request.
|
||||
auto result = X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
|
@ -224,8 +207,9 @@ dword_result_t XamInputGetKeystrokeEx_entry(
|
|||
}
|
||||
DECLARE_XAM_EXPORT1(XamInputGetKeystrokeEx, kInput, kImplemented);
|
||||
|
||||
X_HRESULT_result_t XamUserGetDeviceContext_entry(dword_t user_index,
|
||||
dword_t unk,
|
||||
X_HRESULT_result_t XamUserGetDeviceContext_entry(
|
||||
dword_t user_index,
|
||||
dword_t unk, // It's set to 3 for a big button
|
||||
lpdword_t out_ptr) {
|
||||
// Games check the result - usually with some masking.
|
||||
// If this function fails they assume zero, so let's fail AND
|
||||
|
|
|
@ -111,14 +111,14 @@ void XamModule::SaveLoaderData() {
|
|||
std::filesystem::path host_path = loader_data_.host_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) {
|
||||
launch_path = launch_path.substr(prefix.length());
|
||||
}
|
||||
};
|
||||
|
||||
remove_prefix(std::string("game:\\"));
|
||||
remove_prefix(std::string("d:\\"));
|
||||
remove_prefix("game:\\");
|
||||
remove_prefix("d:\\");
|
||||
|
||||
if (host_path.extension() == ".xex") {
|
||||
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, NUI)
|
||||
XE_MODULE_EXPORT_GROUP(xam, Party)
|
||||
XE_MODULE_EXPORT_GROUP(xam, Profile)
|
||||
XE_MODULE_EXPORT_GROUP(xam, Task)
|
||||
XE_MODULE_EXPORT_GROUP(xam, UI)
|
||||
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;
|
||||
}
|
||||
|
||||
int native_len = *optlen;
|
||||
uint32_t native_len = *optlen;
|
||||
X_STATUS status = socket->GetOption(level, optname, optval_ptr, &native_len);
|
||||
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);
|
||||
|
||||
dword_result_t NetDll_XNetUnregisterKey_entry(dword_t caller, lpdword_t key_id,
|
||||
lpdword_t exchange_key) {
|
||||
dword_result_t NetDll_XNetUnregisterKey_entry(dword_t caller,
|
||||
lpdword_t key_id) {
|
||||
return 0;
|
||||
}
|
||||
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,
|
||||
uint32_t overlapped) {
|
||||
auto pre = []() {
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||
};
|
||||
auto run = [dialog, close_callback]() -> X_RESULT {
|
||||
X_RESULT result;
|
||||
|
@ -108,7 +108,7 @@ X_RESULT xeXamDispatchDialog(T* dialog,
|
|||
};
|
||||
auto post = []() {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||
};
|
||||
if (!overlapped) {
|
||||
pre();
|
||||
|
@ -126,7 +126,7 @@ X_RESULT xeXamDispatchDialogEx(
|
|||
T* dialog, std::function<X_RESULT(T*, uint32_t&, uint32_t&)> close_callback,
|
||||
uint32_t overlapped) {
|
||||
auto pre = []() {
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||
};
|
||||
auto run = [dialog, close_callback](uint32_t& extended_error,
|
||||
uint32_t& length) -> X_RESULT {
|
||||
|
@ -150,7 +150,7 @@ X_RESULT xeXamDispatchDialogEx(
|
|||
};
|
||||
auto post = []() {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||
};
|
||||
if (!overlapped) {
|
||||
pre();
|
||||
|
@ -168,11 +168,11 @@ X_RESULT xeXamDispatchDialogEx(
|
|||
X_RESULT xeXamDispatchHeadless(std::function<X_RESULT()> run_callback,
|
||||
uint32_t overlapped) {
|
||||
auto pre = []() {
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||
};
|
||||
auto post = []() {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||
};
|
||||
if (!overlapped) {
|
||||
pre();
|
||||
|
@ -190,11 +190,11 @@ X_RESULT xeXamDispatchHeadlessEx(
|
|||
std::function<X_RESULT(uint32_t&, uint32_t&)> run_callback,
|
||||
uint32_t overlapped) {
|
||||
auto pre = []() {
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||
};
|
||||
auto post = []() {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||
};
|
||||
if (!overlapped) {
|
||||
pre();
|
||||
|
@ -213,7 +213,7 @@ X_RESULT xeXamDispatchHeadlessEx(
|
|||
template <typename T>
|
||||
X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
||||
std::function<void(T*)> close_callback) {
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||
++xam_dialogs_shown_;
|
||||
|
||||
// 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 {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||
};
|
||||
|
||||
std::thread thread(run);
|
||||
|
@ -237,7 +237,7 @@ X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
|||
}
|
||||
|
||||
X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, true);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, true);
|
||||
++xam_dialogs_shown_;
|
||||
|
||||
auto display_window = kernel_state()->emulator()->display_window();
|
||||
|
@ -248,7 +248,7 @@ X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
|
|||
|
||||
auto run = []() -> void {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, false);
|
||||
kernel_state()->BroadcastNotification(kXNotificationSystemUI, false);
|
||||
};
|
||||
|
||||
std::thread thread(run);
|
||||
|
@ -320,6 +320,119 @@ class MessageBoxDialog : public XamDialog {
|
|||
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 {
|
||||
public:
|
||||
GamertagModifyDialog(ui::ImGuiDrawer* imgui_drawer,
|
||||
|
@ -394,7 +507,7 @@ struct AchievementInfo {
|
|||
uint32_t gamerscore;
|
||||
uint32_t image_id;
|
||||
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 {
|
||||
return (flags & static_cast<uint32_t>(AchievementFlags::kAchieved)) ||
|
||||
|
@ -461,12 +574,8 @@ class GameAchievementsDialog final : public XamDialog {
|
|||
info.unlock_time = {};
|
||||
|
||||
if (entry.IsUnlocked()) {
|
||||
const uint64_t filetime_time =
|
||||
(static_cast<uint64_t>(entry.unlock_time.high_part) << 32) |
|
||||
entry.unlock_time.low_part;
|
||||
|
||||
info.unlock_time = chrono::WinSystemClock::to_sys(
|
||||
chrono::WinSystemClock::from_file_time(filetime_time));
|
||||
info.unlock_time =
|
||||
chrono::WinSystemClock::to_local(entry.unlock_time.to_time_point());
|
||||
}
|
||||
|
||||
achievements_info_.insert({info.id, info});
|
||||
|
@ -533,14 +642,12 @@ class GameAchievementsDialog final : public XamDialog {
|
|||
|
||||
ImGui::PushFont(imgui_drawer()->GetTitleFont());
|
||||
const auto primary_line_height = ImGui::GetTextLineHeight();
|
||||
ImGui::TextUnformatted(
|
||||
fmt::format("{}", GetAchievementTitle(achievement_entry)).c_str());
|
||||
ImGui::Text("%s", GetAchievementTitle(achievement_entry).c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetMainViewport()->Size.x * 0.5f);
|
||||
ImGui::TextWrapped(
|
||||
fmt::format("{}", GetAchievementDescription(achievement_entry))
|
||||
.c_str());
|
||||
ImGui::TextWrapped("%s",
|
||||
GetAchievementDescription(achievement_entry).c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
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::Text(no_entries_message.c_str());
|
||||
ImGui::Text("%s", no_entries_message.c_str());
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
|
@ -787,13 +894,10 @@ class GamesInfoDialog final : public ui::ImGuiDialog {
|
|||
static dword_result_t XamShowMessageBoxUi(
|
||||
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 flags, lpdword_t result_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||
std::string title;
|
||||
if (title_ptr) {
|
||||
title = xe::to_utf8(title_ptr.value());
|
||||
} else {
|
||||
title = ""; // TODO(gibbed): default title based on flags?
|
||||
}
|
||||
dword_t flags, pointer_t<MESSAGEBOX_RESULT> result_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||
std::string title = title_ptr ? xe::to_utf8(title_ptr.value()) : "";
|
||||
std::string text = text_ptr ? xe::to_utf8(text_ptr.value()) : "";
|
||||
|
||||
std::vector<std::string> buttons;
|
||||
for (uint32_t i = 0; i < button_count; ++i) {
|
||||
|
@ -807,37 +911,53 @@ static dword_result_t XamShowMessageBoxUi(
|
|||
if (cvars::headless) {
|
||||
// Auto-pick the focused button.
|
||||
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;
|
||||
};
|
||||
|
||||
result = xeXamDispatchHeadless(run, overlapped);
|
||||
} else {
|
||||
// TODO(benvanik): setup icon states.
|
||||
switch (flags & 0xF) {
|
||||
case 0:
|
||||
// config.pszMainIcon = nullptr;
|
||||
break;
|
||||
case 1:
|
||||
// config.pszMainIcon = TD_ERROR_ICON;
|
||||
break;
|
||||
case 2:
|
||||
// config.pszMainIcon = TD_WARNING_ICON;
|
||||
break;
|
||||
case 3:
|
||||
// config.pszMainIcon = TD_INFORMATION_ICON;
|
||||
break;
|
||||
case XMBox_NOICON: {
|
||||
} break;
|
||||
case XMBox_ERRORICON: {
|
||||
} break;
|
||||
case XMBox_WARNINGICON: {
|
||||
} break;
|
||||
case XMBox_ALERTICON: {
|
||||
} break;
|
||||
}
|
||||
auto close = [result_ptr](MessageBoxDialog* dialog) -> X_RESULT {
|
||||
*result_ptr = dialog->chosen_button();
|
||||
return X_ERROR_SUCCESS;
|
||||
};
|
||||
|
||||
const Emulator* emulator = kernel_state()->emulator();
|
||||
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
||||
|
||||
if (flags & XMBox_PASSCODEMODE || flags & XMBox_VERIFYPASSCODEMODE) {
|
||||
auto close = [result_ptr,
|
||||
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, xe::to_utf8(text_ptr.value()),
|
||||
buttons, active_button),
|
||||
new MessageBoxDialog(imgui_drawer, title, text, buttons,
|
||||
static_cast<uint32_t>(active_button)),
|
||||
close, overlapped);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -845,7 +965,8 @@ static dword_result_t XamShowMessageBoxUi(
|
|||
dword_result_t XamShowMessageBoxUI_entry(
|
||||
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 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,
|
||||
button_ptrs, active_button, flags, result_ptr,
|
||||
overlapped);
|
||||
|
@ -855,7 +976,8 @@ DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented);
|
|||
dword_result_t XamShowMessageBoxUIEx_entry(
|
||||
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 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) {
|
||||
return XamShowMessageBoxUi(user_index, title_ptr, text_ptr, button_count,
|
||||
button_ptrs, active_button, flags, result_ptr,
|
||||
|
@ -1016,11 +1138,14 @@ dword_result_t XamShowKeyboardUI_entry(
|
|||
};
|
||||
const Emulator* emulator = kernel_state()->emulator();
|
||||
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>(
|
||||
new KeyboardInputDialog(
|
||||
imgui_drawer, title ? xe::to_utf8(title.value()) : "",
|
||||
description ? xe::to_utf8(description.value()) : "",
|
||||
default_text ? xe::to_utf8(default_text.value()) : "",
|
||||
new KeyboardInputDialog(imgui_drawer, title_str, desc_str, def_text_str,
|
||||
buffer_length),
|
||||
close, overlapped);
|
||||
}
|
||||
|
@ -1128,6 +1253,8 @@ DECLARE_XAM_EXPORT1(XamShowCommunitySessionsUI, kNone, kStub);
|
|||
dword_result_t XamSetDashContext_entry(dword_t value,
|
||||
const ppc_context_t& ctx) {
|
||||
ctx->kernel_state->dash_context_ = value;
|
||||
kernel_state()->BroadcastNotification(
|
||||
kXNotificationDvdDriveUnknownDashContext, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1139,9 +1266,11 @@ dword_result_t XamGetDashContext_entry(const ppc_context_t& ctx) {
|
|||
|
||||
DECLARE_XAM_EXPORT1(XamGetDashContext, kNone, kImplemented);
|
||||
|
||||
dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||
qword_t offer_id,
|
||||
dword_t content_types) {
|
||||
// https://gitlab.com/GlitchyScripts/xlivelessness/-/blob/master/xlivelessness/xlive/xdefs.hpp?ref_type=heads#L1235
|
||||
X_HRESULT xeXShowMarketplaceUIEx(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) {
|
||||
// ui_type:
|
||||
// 0 - view all content for the current title
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
switch (ui_type) {
|
||||
case 0:
|
||||
case X_MARKETPLACE_ENTRYPOINT::ContentList:
|
||||
desc =
|
||||
"Game requested to open marketplace page with all content for the "
|
||||
"current title ID.";
|
||||
break;
|
||||
case 1:
|
||||
case X_MARKETPLACE_ENTRYPOINT::ContentItem:
|
||||
desc = fmt::format(
|
||||
"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;
|
||||
default:
|
||||
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.";
|
||||
|
||||
switch (ui_type) {
|
||||
case 0:
|
||||
case X_MARKETPLACE_ENTRYPOINT::ContentList:
|
||||
default:
|
||||
buttons.push_back("OK");
|
||||
break;
|
||||
case 1:
|
||||
case X_MARKETPLACE_ENTRYPOINT::ContentItem:
|
||||
desc +=
|
||||
"\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 "
|
||||
|
@ -1216,8 +1389,26 @@ dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
|||
return xeXamDispatchDialogAsync<MessageBoxDialog>(
|
||||
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);
|
||||
|
||||
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_t user_index, dword_t ui_type, lpqword_t offers, dword_t num_offers,
|
||||
lpdword_t hresult_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||
|
@ -1291,6 +1482,12 @@ dword_result_t XamShowMarketplaceDownloadItemsUI_entry(
|
|||
}
|
||||
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,
|
||||
const uint8_t user_index,
|
||||
const X_XAMACCOUNTINFO* account,
|
||||
|
@ -1376,10 +1573,13 @@ bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid,
|
|||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
const bool is_signedin = profile_manager->GetProfile(xuid) != nullptr;
|
||||
ImGui::BeginDisabled(!is_signedin);
|
||||
if (ImGui::MenuItem("Show Achievements")) {
|
||||
new GamesInfoDialog(imgui_drawer, next_window_position,
|
||||
profile_manager->GetProfile(user_index));
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::MenuItem("Show Content Directory")) {
|
||||
const auto path = profile_manager->GetProfileContentPath(
|
||||
|
|
|
@ -139,28 +139,36 @@ X_HRESULT_result_t XamUserGetSigninInfo_entry(
|
|||
}
|
||||
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) {
|
||||
if (user_index >= XUserMaxUserCount) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||
const auto& user_profile =
|
||||
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
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;
|
||||
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||
// Based on XAM only first byte is cleared in case of lack of user.
|
||||
kernel_memory()->Zero(buffer, 1);
|
||||
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;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamUserGetName, kUserProfiles, kImplemented);
|
||||
|
||||
dword_result_t XamUserGetGamerTag_entry(dword_t user_index,
|
||||
lpu16string_t buffer,
|
||||
dword_result_t XamUserGetGamerTag_entry(dword_t user_index, dword_t buffer,
|
||||
dword_t buffer_len) {
|
||||
if (!buffer || buffer_len < 16) {
|
||||
return X_E_INVALIDARG;
|
||||
|
@ -177,8 +185,11 @@ dword_result_t XamUserGetGamerTag_entry(dword_t user_index,
|
|||
const auto& user_profile =
|
||||
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
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(
|
||||
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;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamUserGetGamerTag, kUserProfiles, kImplemented);
|
||||
|
@ -620,7 +631,8 @@ dword_result_t XamUserCreateAchievementEnumerator_entry(
|
|||
}
|
||||
|
||||
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 =
|
||||
kernel_state()->achievement_manager()->GetTitleAchievements(
|
||||
|
@ -787,6 +799,18 @@ dword_result_t XamUserGetUserFlagsFromXUID_entry(qword_t xuid) {
|
|||
}
|
||||
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;
|
||||
|
||||
struct X_STATS_DETAILS {
|
||||
|
@ -832,35 +856,6 @@ dword_result_t XamUserCreateStatsEnumerator_entry(
|
|||
}
|
||||
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 kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -171,6 +171,88 @@ dword_result_t DmGetSystemInfo_entry(pointer_t<XBDM_SYSTEM_INFO> info) {
|
|||
}
|
||||
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; }
|
||||
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;
|
||||
|
||||
// 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.
|
||||
if (!IsValidPath(target_path, false)) {
|
||||
|
@ -462,7 +462,7 @@ dword_result_t NtQueryFullAttributesFile_entry(
|
|||
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.
|
||||
if (!IsValidPath(target_path, false)) {
|
||||
|
@ -501,7 +501,7 @@ dword_result_t NtQueryDirectoryFile_entry(
|
|||
uint32_t info = 0;
|
||||
|
||||
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.
|
||||
if (!IsValidPath(name, true)) {
|
||||
|
@ -558,7 +558,7 @@ dword_result_t NtOpenSymbolicLinkObject_entry(
|
|||
auto object_name =
|
||||
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.
|
||||
if (!IsValidPath(target_path, false)) {
|
||||
|
|
|
@ -234,7 +234,7 @@ dword_result_t NtSetInformationFile_entry(
|
|||
auto info = info_ptr.as<X_FILE_RENAME_INFORMATION*>();
|
||||
// Compute path, possibly attrs relative.
|
||||
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
|
||||
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;
|
||||
}
|
||||
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::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,
|
||||
pointer_t<X_ANSI_STRING> target_ptr) {
|
||||
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(
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -367,7 +367,7 @@ dword_result_t ObCreateSymbolicLink_entry(pointer_t<X_ANSI_STRING> path_ptr,
|
|||
DECLARE_XBOXKRNL_EXPORT1(ObCreateSymbolicLink, kNone, kImplemented);
|
||||
|
||||
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)) {
|
||||
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.",
|
||||
"Kernel");
|
||||
|
||||
inline constexpr static uint32_t GetVideoStandard() {
|
||||
inline const static uint32_t GetVideoStandard() {
|
||||
if (cvars::video_standard < 1 || cvars::video_standard > 3) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -52,11 +52,11 @@ inline constexpr static uint32_t GetVideoStandard() {
|
|||
return cvars::video_standard;
|
||||
}
|
||||
|
||||
inline constexpr static float GetVideoRefreshRate() {
|
||||
inline const static float GetVideoRefreshRate() {
|
||||
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) {
|
||||
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",
|
||||
"XConfig");
|
||||
|
||||
DECLARE_bool(widescreen);
|
||||
DECLARE_bool(use_50Hz_mode);
|
||||
DECLARE_int32(video_standard);
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
|
@ -56,9 +60,26 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting,
|
|||
switch (setting) {
|
||||
case 0x0002: // XCONFIG_SECURED_AV_REGION
|
||||
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;
|
||||
default:
|
||||
XELOGW(
|
||||
"An unimplemented setting 0x{:04X} in XCONFIG SECURED CATEGORY",
|
||||
static_cast<uint16_t>(setting));
|
||||
assert_unhandled_case(setting);
|
||||
return X_STATUS_INVALID_PARAMETER_2;
|
||||
}
|
||||
|
@ -83,7 +104,10 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting,
|
|||
break;
|
||||
case 0x000A: // XCONFIG_USER_VIDEO_FLAGS
|
||||
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;
|
||||
case 0x000C: // XCONFIG_USER_RETAIL_FLAGS
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
XELOGW("An unimplemented setting 0x{:04X} in XCONFIG USER CATEGORY",
|
||||
static_cast<uint16_t>(setting));
|
||||
assert_unhandled_case(setting);
|
||||
return X_STATUS_INVALID_PARAMETER_2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
XELOGW("An unimplemented category 0x{:04X}",
|
||||
static_cast<uint16_t>(category));
|
||||
assert_unhandled_case(category);
|
||||
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);
|
||||
|
||||
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 kernel
|
||||
} // 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 '|': {
|
||||
|
|
|
@ -135,6 +135,8 @@ class XObject {
|
|||
case Type::Thread:
|
||||
case Type::Timer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -349,7 +351,9 @@ class object_ref {
|
|||
|
||||
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:
|
||||
T* value_ = nullptr;
|
||||
|
|
|
@ -74,9 +74,10 @@ X_STATUS XSocket::Close() {
|
|||
}
|
||||
|
||||
X_STATUS XSocket::GetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
||||
int* optlen) {
|
||||
uint32_t* optlen) {
|
||||
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) {
|
||||
// TODO: WSAGetLastError()
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
|
|
|
@ -108,7 +108,7 @@ class XSocket : public XObject {
|
|||
X_STATUS Close();
|
||||
|
||||
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,
|
||||
uint32_t optlen);
|
||||
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}"));
|
||||
|
||||
for (const auto& entry : dir_files) {
|
||||
const uint32_t title_id =
|
||||
std::stoi(entry.name.filename().string(), nullptr, 16);
|
||||
const uint32_t title_id = string_util::from_string<uint32_t>(
|
||||
entry.name.filename().string(), true);
|
||||
|
||||
LoadTitleConfig(title_id);
|
||||
}
|
||||
|
|
|
@ -95,12 +95,11 @@ void ImGuiGuestNotification::UpdateNotificationState() {
|
|||
|
||||
const ImVec2 ImGuiGuestNotification::CalculateNotificationSize(ImVec2 text_size,
|
||||
float scale) {
|
||||
const ImVec2 result =
|
||||
ImVec2(std::floorf((default_notification_icon_size.x +
|
||||
const ImVec2 result = ImVec2(floorf((default_notification_icon_size.x +
|
||||
default_notification_margin_size.x) *
|
||||
scale) +
|
||||
text_size.x,
|
||||
std::floorf((default_notification_icon_size.y +
|
||||
floorf((default_notification_icon_size.y +
|
||||
default_notification_margin_size.y) *
|
||||
scale));
|
||||
|
||||
|
@ -134,13 +133,14 @@ void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
|
|||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||
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;
|
||||
}
|
||||
|
||||
ImVec2 current_notification_size = final_notification_size;
|
||||
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
|
||||
ImGui::SetNextWindowSize(current_notification_size);
|
||||
|
@ -170,7 +170,7 @@ void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
|
|||
|
||||
ImGui::SameLine();
|
||||
if (notification_draw_progress_ > 0.5f) {
|
||||
ImGui::TextColored(white_color, GetNotificationText().c_str());
|
||||
ImGui::TextColored(white_color, "%s", GetNotificationText().c_str());
|
||||
}
|
||||
}
|
||||
// Restore previous style
|
||||
|
@ -207,13 +207,14 @@ void XNotifyWindow::OnDraw(ImGuiIO& io) {
|
|||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||
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;
|
||||
}
|
||||
|
||||
ImVec2 current_notification_size = final_notification_size;
|
||||
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
|
||||
ImGui::SetNextWindowSize(current_notification_size);
|
||||
|
@ -247,7 +248,7 @@ void XNotifyWindow::OnDraw(ImGuiIO& io) {
|
|||
|
||||
ImGui::SameLine();
|
||||
if (notification_draw_progress_ > 0.5f) {
|
||||
ImGui::TextColored(white_color, GetDescription().c_str());
|
||||
ImGui::TextColored(white_color, "%s", GetDescription().c_str());
|
||||
}
|
||||
}
|
||||
// Restore previous style
|
||||
|
|
|
@ -57,7 +57,7 @@ class ImGuiGuestNotification : public ImGuiNotification {
|
|||
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||
float scale) override;
|
||||
|
||||
virtual void OnDraw(ImGuiIO& io) {}
|
||||
virtual void OnDraw(ImGuiIO& io) override {}
|
||||
|
||||
float notification_draw_progress_;
|
||||
|
||||
|
|
|
@ -68,7 +68,8 @@ void HostNotificationWindow::OnDraw(ImGuiIO& io) {
|
|||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -78,9 +79,9 @@ void HostNotificationWindow::OnDraw(ImGuiIO& io) {
|
|||
{
|
||||
ImGui::SetWindowFontScale(window_scale);
|
||||
|
||||
ImGui::Text(GetTitle().c_str());
|
||||
ImGui::Text("%s", GetTitle().c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::Text(GetDescription().c_str());
|
||||
ImGui::Text("%s", GetDescription().c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class ImGuiHostNotification : public ImGuiNotification {
|
|||
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||
float scale) override;
|
||||
|
||||
virtual void OnDraw(ImGuiIO& io) {}
|
||||
virtual void OnDraw(ImGuiIO& io) override {}
|
||||
};
|
||||
|
||||
class HostNotificationWindow final : ImGuiHostNotification {
|
||||
|
|
|
@ -468,10 +468,12 @@ struct XContentMetadata {
|
|||
};
|
||||
static_assert_size(XContentMetadata, 0x93D6);
|
||||
|
||||
static constexpr uint8_t license_count = 0x10;
|
||||
|
||||
struct XContentHeader {
|
||||
be<XContentPackageType> magic;
|
||||
uint8_t signature[0x228];
|
||||
XContentLicense licenses[0x10];
|
||||
XContentLicense licenses[license_count];
|
||||
uint8_t content_id[0x14];
|
||||
be<uint32_t> header_size;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class XContentContainerDevice : public Device {
|
|||
|
||||
~XContentContainerDevice() override;
|
||||
|
||||
bool Initialize();
|
||||
bool Initialize() override;
|
||||
|
||||
const std::string& name() const override { return name_; }
|
||||
uint32_t attributes() const override { return 0; }
|
||||
|
@ -65,7 +65,13 @@ class XContentContainerDevice : public Device {
|
|||
|
||||
kernel::xam::XCONTENT_AGGREGATE_DATA content_header() 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:
|
||||
|
@ -87,9 +93,9 @@ class XContentContainerDevice : public Device {
|
|||
// Initialize any container specific fields.
|
||||
virtual void SetupContainer() {};
|
||||
|
||||
Entry* ResolvePath(const std::string_view path);
|
||||
Entry* ResolvePath(const std::string_view path) override;
|
||||
void CloseFiles();
|
||||
void Dump(StringBuffer* string_buffer);
|
||||
void Dump(StringBuffer* string_buffer) override;
|
||||
Result ReadHeaderAndVerify(FILE* header_file);
|
||||
|
||||
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_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_NO_MORE_FILES X_HRESULT_FROM_WIN32(X_ERROR_NO_MORE_FILES)
|
||||
#define X_E_INVALIDARG X_HRESULT_FROM_WIN32(X_ERROR_INVALID_PARAMETER)
|
||||
|
@ -275,55 +277,70 @@ enum : XNotificationID {
|
|||
kXNotifyLive = 0x00000002,
|
||||
kXNotifyFriends = 0x00000004,
|
||||
kXNotifyCustom = 0x00000008,
|
||||
kXNotifyDvdDrive = 0x00000010, // ?
|
||||
kXNotifyXmp = 0x00000020,
|
||||
kXNotifyMsgr = 0x00000040,
|
||||
kXNotifyParty = 0x00000080,
|
||||
kXNotifyAll = 0x000000EF,
|
||||
|
||||
// XNotification System
|
||||
kXNotificationIDSystemUI = 0x00000009,
|
||||
kXNotificationIDSystemSignInChanged = 0x0000000A,
|
||||
kXNotificationIDSystemStorageDevicesChanged = 0x0000000B,
|
||||
kXNotificationIDSystemProfileSettingChanged = 0x0000000E,
|
||||
kXNotificationIDSystemMuteListChanged = 0x00000011,
|
||||
kXNotificationIDSystemInputDevicesChanged = 0x00000012,
|
||||
kXNotificationIDSystemInputDeviceConfigChanged = 0x00000013,
|
||||
kXNotificationIDSystemPlayerTimerNotice = 0x00000015,
|
||||
kXNotificationIDSystemAvatarChanged = 0x00000017,
|
||||
kXNotificationIDSystemNUIHardwareStatusChanged = 0x00000019,
|
||||
kXNotificationIDSystemNUIPause = 0x0000001A,
|
||||
kXNotificationIDSystemNUIUIApproach = 0x0000001B,
|
||||
kXNotificationIDSystemDeviceRemap = 0x0000001C,
|
||||
kXNotificationIDSystemNUIBindingChanged = 0x0000001D,
|
||||
kXNotificationIDSystemAudioLatencyChanged = 0x0000001E,
|
||||
kXNotificationIDSystemNUIChatBindingChanged = 0x0000001F,
|
||||
kXNotificationIDSystemInputActivityChanged = 0x00000020,
|
||||
// XNotification System (35 total)
|
||||
kXNotificationSystemUI = 0x00000009,
|
||||
kXNotificationSystemSignInChanged = 0x0000000A,
|
||||
kXNotificationSystemStorageDevicesChanged = 0x0000000B,
|
||||
kXNotificationSystemProfileSettingChanged = 0x0000000E,
|
||||
kXNotificationSystemMuteListChanged = 0x00000011,
|
||||
kXNotificationSystemInputDevicesChanged = 0x00000012,
|
||||
kXNotificationSystemInputDeviceConfigChanged = 0x00000013,
|
||||
kXNotificationSystemPlayerTimerNotice = 0x00000015,
|
||||
kXNotificationSystemPXLiveSystemUpdate = 0x00000016,
|
||||
kXNotificationSystemAvatarChanged = 0x00000017,
|
||||
kXNotificationSystemUnknown = 0x00000018,
|
||||
kXNotificationSystemNUIHardwareStatusChanged = 0x00000019,
|
||||
kXNotificationSystemNUIPause = 0x0000001A,
|
||||
kXNotificationSystemNUIUIApproach = 0x0000001B,
|
||||
kXNotificationSystemDeviceRemap = 0x0000001C,
|
||||
kXNotificationSystemNUIBindingChanged = 0x0000001D,
|
||||
kXNotificationSystemAudioLatencyChanged = 0x0000001E,
|
||||
kXNotificationSystemNUIChatBindingChanged = 0x0000001F,
|
||||
kXNotificationSystemInputActivityChanged = 0x00000020,
|
||||
|
||||
// XNotification Live
|
||||
kXNotificationIDLiveConnectionChanged = 0x02000001,
|
||||
kXNotificationIDLiveInviteAccepted = 0x02000002,
|
||||
kXNotificationIDLiveLinkStateChanged = 0x02000003,
|
||||
kXNotificationIDLiveContentInstalled = 0x02000007,
|
||||
kXNotificationIDLiveMembershipPurchased = 0x02000008,
|
||||
kXNotificationIDLiveVoicechatAway = 0x02000009,
|
||||
kXNotificationIDLivePresenceChanged = 0x0200000A,
|
||||
// XNotification Live (20 total)
|
||||
kXNotificationLiveConnectionChanged = 0x02000001,
|
||||
kXNotificationLiveInviteAccepted = 0x02000002,
|
||||
kXNotificationLiveLinkStateChanged = 0x02000003,
|
||||
kXNotificationLiveContentInstalled = 0x02000007,
|
||||
kXNotificationLiveMembershipPurchased = 0x02000008,
|
||||
kXNotificationLiveVoicechatAway = 0x02000009,
|
||||
kXNotificationLivePresenceChanged = 0x0200000A,
|
||||
kXNotificationLiveUnknown = 0x02000012,
|
||||
|
||||
// XNotification Friends
|
||||
kXNotificationIDFriendsPresenceChanged = 0x04000001,
|
||||
kXNotificationIDFriendsFriendAdded = 0x04000002,
|
||||
kXNotificationIDFriendsFriendRemoved = 0x04000003,
|
||||
// XNotification Friends (9 total)
|
||||
kXNotificationFriendsPresenceChanged = 0x04000001,
|
||||
kXNotificationFriendsFriendAdded = 0x04000002,
|
||||
kXNotificationFriendsFriendRemoved = 0x04000003,
|
||||
kXNotificationFriendsUnknown = 0x04000008,
|
||||
|
||||
// XNotification Custom
|
||||
kXNotificationIDCustomActionPressed = 0x06000003,
|
||||
kXNotificationIDCustomGamercard = 0x06000004,
|
||||
// XNotification Custom (5 total)
|
||||
kXNotificationCustomActionPressed = 0x06000003,
|
||||
kXNotificationCustomGamercard = 0x06000004,
|
||||
|
||||
// XNotification XMP
|
||||
kNotificationXmpStateChanged = 0x0A000001,
|
||||
kNotificationXmpPlaybackBehaviorChanged = 0x0A000002,
|
||||
kNotificationXmpPlaybackControllerChanged = 0x0A000003,
|
||||
// XNotification Dvd ?
|
||||
kXNotificationDvdDriveUnknown = 0x80000003,
|
||||
kXNotificationDvdDriveUnknownDashContext = 0x8000000C,
|
||||
kXNotificationDvdDriveTrayStateChanged = 0x8000000D,
|
||||
|
||||
// XNotification Party
|
||||
kXNotificationIDPartyMembersChanged = 0x0E000002,
|
||||
// XNotification XMP (13 total)
|
||||
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
|
||||
|
@ -540,6 +557,19 @@ const static std::map<XContentType, std::string> XContentTypeMap = {
|
|||
{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 {
|
||||
kOpticalDisc = 0,
|
||||
kHardDrive = 1, // Like extracted?
|
||||
|
@ -650,8 +680,43 @@ struct X_PROFILEENUMRESULT {
|
|||
};
|
||||
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
|
||||
|
||||
} // namespace xe
|
||||
|
||||
#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