Merge branch 'master' into vulkan

This commit is contained in:
Triang3l 2021-07-03 20:59:25 +03:00
commit 9bb104b354
73 changed files with 1611 additions and 873 deletions

View File

@ -8,6 +8,7 @@ skip_tags: true
skip_commits: skip_commits:
files: files:
- .drone.yml
- .github/** - .github/**
- .travis.yml - .travis.yml
- docs/** - docs/**

214
.drone.yml Normal file
View File

@ -0,0 +1,214 @@
---
kind: pipeline
type: docker
name: lint
# Run this in a separate pipeline so that it will build even if this fails
steps:
- name: lint
image: xeniaproject/buildenv:2021-06-21
commands:
- clang-format --version
- ./xenia-build lint --all
---
kind: pipeline
type: docker
name: x86_64-linux
platform:
os: linux
arch: amd64
# Some expressions in this file are duplicates. Scripting support is
# available using jsonnet but increases complexity
# https://docs.drone.io/pipeline/scripting/jsonnet/
# These volumes will be mounted at the build directory, allowing to
# run different premake toolchains from the same source tree
volumes:
- name: build-premake
temp: {}
- name: build-cmake
temp: {}
steps:
#
# Setup the source tree
#
- name: clone-submodules
image: xeniaproject/buildenv:2021-06-21
commands:
- pwd
# May miss recursive submodules (but faster than xb setup)
- git submodule update --init --depth 1 -j $(nproc)
#
# Setup the two build systems
#
# Native premake Makefiles for production
- name: toolchain-premake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- $CXX --version
- $AR --version
- python3 --version
- ./xenia-build premake
depends_on:
- clone-submodules
# Development toolchain
- name: toolchain-cmake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- |
./xenia-build premake --devenv=cmake
cd build
for c in Debug Release
do
mkdir cmake-$c
cd cmake-$c
cmake -DCMAKE_BUILD_TYPE=$c ..
cd ..
done
depends_on:
# Premake itself needs to be build first:
- toolchain-premake
#
# Building
#
- name: build-premake-debug-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./xenia-build build --no_premake -j$(nproc) --config=Debug
depends_on:
- toolchain-premake
- name: build-premake-release-tests
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./xenia-build build --no_premake -j$(nproc) --config=Release --target=xenia-base-tests
depends_on:
- toolchain-premake
- name: build-premake-release-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./xenia-build build --no_premake -j$(nproc) --config=Release
depends_on:
- build-premake-release-tests
- name: build-cmake-debug-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- cd build/cmake-Debug
- cmake --build . -j$(nproc)
depends_on:
- toolchain-cmake
- name: build-cmake-release-tests
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- cd build/cmake-Release
- cmake --build . -j$(nproc) --target xenia-base-tests
depends_on:
- toolchain-cmake
- name: build-cmake-release-all
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- cd build/cmake-Release
- cmake --build . -j$(nproc)
depends_on:
- build-cmake-release-tests
#
# Tests
#
- name: test-premake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /drone/src/build
commands:
- ./build/bin/Linux/Release/xenia-base-tests
depends_on:
- build-premake-release-tests
- name: test-cmake
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-cmake
path: /drone/src/build
commands:
- ./build/bin/Linux/Release/xenia-base-tests
depends_on:
- build-cmake-release-tests
#
# Stat
#
- name: stat
image: xeniaproject/buildenv:2021-06-21
volumes:
- name: build-premake
path: /build-premake
- name: build-cmake
path: /build-cmake
commands:
- |
header() {
SEP='============================================================'
echo
echo $SEP
echo $@
echo $SEP
}
for v in premake cmake
do
for c in Debug Release
do
header $v $c
p=/build-$v/bin/Linux/$c
ls -la $p
sha256sum $p/*
done
done
depends_on:
- build-premake-debug-all
- build-premake-release-all
- build-cmake-debug-all
- build-cmake-release-all

View File

@ -1,73 +0,0 @@
# Make Travis use docker (for faster builds, in theory).
language: cpp
os:
- linux
dist: bionic
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-9
packages:
- clang-9
- clang-format-9
- llvm-9-dev
- g++-8
- python3
- libc++-dev
- libc++abi-dev
- libgtk-3-dev
- libpthread-stubs0-dev
- libsdl2-dev
#- libvulkan1
#- libvulkan-dev
- libx11-dev
- liblz4-dev
jobs:
include:
- env: C_COMPILER=clang-9 CXX_COMPILER=clang++-9 AR_COMPILER=llvm-ar-9 LINT=true
- env: C_COMPILER=clang-9 CXX_COMPILER=clang++-9 AR_COMPILER=llvm-ar-9 BUILD=true CONFIG=Debug
- env: C_COMPILER=clang-9 CXX_COMPILER=clang++-9 AR_COMPILER=llvm-ar-9 BUILD=true CONFIG=Release
git:
# We handle submodules ourselves in xenia-build setup.
submodules: false
before_script:
- export LIBVULKAN_VERSION=1.1.70
- export CXX=$CXX_COMPILER
- export CC=$C_COMPILER
- export AR=$AR_COMPILER
# Dump useful info.
- $CXX --version
- $AR_COMPILER --version
- python3 --version
- clang-format-9 --version
- clang-format-9 -style=file -dump-config
# Add Vulkan dependencies.
- travis_retry wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb
- travis_retry wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb
- if [[ $BUILD == true ]]; then sudo dpkg -i libvulkan1_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb libvulkan-dev_$LIBVULKAN_VERSION+dfsg1-1_amd64.deb; fi
# Prepare environment (pull dependencies, build tools).
- travis_retry ./xenia-build setup
script:
# Run linter.
- if [[ $LINT == true ]]; then ./xenia-build lint --all; fi
# Build and run base tests.
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG --target=xenia-base-tests; fi
- if [[ $BUILD == true ]]; then ./build/bin/Linux/$CONFIG/xenia-base-tests; fi
# Build and run ppc tests.
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG --target=xenia-cpu-ppc-tests; fi
# - if [[ $BUILD == true ]]; then ./build/bin/Linux/$CONFIG/xenia-cpu-ppc-tests --log_file=stdout; fi
# Build all of xenia.
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG; fi
# All tests (without haswell support).
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=false
# All tests (with haswell support).
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=true

View File

@ -24,7 +24,7 @@ Discussing illegal activities will get you banned.
Buildbot | Status Buildbot | Status
-------- | ------ -------- | ------
[Windows](https://ci.appveyor.com/project/benvanik/xenia/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/ftqiy86kdfawyx3a/branch/master?svg=true)](https://ci.appveyor.com/project/benvanik/xenia/branch/master) [Windows](https://ci.appveyor.com/project/benvanik/xenia/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/ftqiy86kdfawyx3a/branch/master?svg=true)](https://ci.appveyor.com/project/benvanik/xenia/branch/master)
[Linux](https://travis-ci.org/xenia-project/xenia) | [![Build status](https://travis-ci.org/xenia-project/xenia.svg?branch=master)](https://travis-ci.org/xenia-project/xenia) [Linux](https://cloud.drone.io/xenia-project/xenia) | [![Build status](https://cloud.drone.io/api/badges/xenia-project/xenia/status.svg)](https://cloud.drone.io/xenia-project/xenia)
Quite a few real games run. Quite a few don't. Quite a few real games run. Quite a few don't.
See the [Game compatibility list](https://github.com/xenia-project/game-compatibility/issues) See the [Game compatibility list](https://github.com/xenia-project/game-compatibility/issues)

View File

@ -24,6 +24,7 @@
#include "xenia/ui/file_picker.h" #include "xenia/ui/file_picker.h"
#include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/virtual_key.h"
// Autogenerated by `xb premake`. // Autogenerated by `xb premake`.
#include "build/version.h" #include "build/version.h"
@ -93,49 +94,49 @@ bool EmulatorWindow::Initialize() {
window_->on_key_down.AddListener([this](KeyEvent* e) { window_->on_key_down.AddListener([this](KeyEvent* e) {
bool handled = true; bool handled = true;
switch (e->key_code()) { switch (e->virtual_key()) {
case 0x4F: { // o case ui::VirtualKey::kO: {
if (e->is_ctrl_pressed()) { if (e->is_ctrl_pressed()) {
FileOpen(); FileOpen();
} }
} break; } break;
case 0x6A: { // numpad * case ui::VirtualKey::kMultiply: {
CpuTimeScalarReset(); CpuTimeScalarReset();
} break; } break;
case 0x6D: { // numpad minus case ui::VirtualKey::kSubtract: {
CpuTimeScalarSetHalf(); CpuTimeScalarSetHalf();
} break; } break;
case 0x6B: { // numpad plus case ui::VirtualKey::kAdd: {
CpuTimeScalarSetDouble(); CpuTimeScalarSetDouble();
} break; } break;
case 0x72: { // F3 case ui::VirtualKey::kF3: {
Profiler::ToggleDisplay(); Profiler::ToggleDisplay();
} break; } break;
case 0x73: { // VK_F4 case ui::VirtualKey::kF4: {
GpuTraceFrame(); GpuTraceFrame();
} break; } break;
case 0x74: { // VK_F5 case ui::VirtualKey::kF5: {
GpuClearCaches(); GpuClearCaches();
} break; } break;
case 0x76: { // VK_F7 case ui::VirtualKey::kF7: {
// Save to file // Save to file
// TODO: Choose path based on user input, or from options // TODO: Choose path based on user input, or from options
// TODO: Spawn a new thread to do this. // TODO: Spawn a new thread to do this.
emulator()->SaveToFile("test.sav"); emulator()->SaveToFile("test.sav");
} break; } break;
case 0x77: { // VK_F8 case ui::VirtualKey::kF8: {
// Restore from file // Restore from file
// TODO: Choose path from user // TODO: Choose path from user
// TODO: Spawn a new thread to do this. // TODO: Spawn a new thread to do this.
emulator()->RestoreFromFile("test.sav"); emulator()->RestoreFromFile("test.sav");
} break; } break;
case 0x7A: { // VK_F11 case ui::VirtualKey::kF11: {
ToggleFullscreen(); ToggleFullscreen();
} break; } break;
case 0x1B: { // VK_ESCAPE case ui::VirtualKey::kEscape: {
// Allow users to escape fullscreen (but not enter it). // Allow users to escape fullscreen (but not enter it).
if (window_->is_fullscreen()) { if (window_->is_fullscreen()) {
window_->ToggleFullscreen(false); window_->ToggleFullscreen(false);
} else { } else {
@ -143,18 +144,18 @@ bool EmulatorWindow::Initialize() {
} }
} break; } break;
case 0x13: { // VK_PAUSE case ui::VirtualKey::kPause: {
CpuBreakIntoDebugger(); CpuBreakIntoDebugger();
} break; } break;
case 0x03: { // VK_CANCEL case ui::VirtualKey::kCancel: {
CpuBreakIntoHostDebugger(); CpuBreakIntoHostDebugger();
} break; } break;
case 0x70: { // VK_F1 case ui::VirtualKey::kF1: {
ShowHelpWebsite(); ShowHelpWebsite();
} break; } break;
case 0x71: { // VK_F2 case ui::VirtualKey::kF2: {
ShowCommitID(); ShowCommitID();
} break; } break;

View File

@ -123,7 +123,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
sizeof(XMA_CONTEXT_DATA) * kContextCount, 256, kSystemHeapPhysical); sizeof(XMA_CONTEXT_DATA) * kContextCount, 256, kSystemHeapPhysical);
context_data_last_ptr_ = context_data_last_ptr_ =
context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1); context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1);
register_file_[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32 = register_file_[XmaRegister::ContextArrayAddress] =
memory()->GetPhysicalAddress(context_data_first_ptr_); memory()->GetPhysicalAddress(context_data_first_ptr_);
// Setup XMA contexts. // Setup XMA contexts.
@ -134,7 +134,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
assert_always(); assert_always();
} }
} }
register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32 = 1; register_file_[XmaRegister::NextContextIndex] = 1;
context_bitmap_.Resize(kContextCount); context_bitmap_.Resize(kContextCount);
worker_running_ = true; worker_running_ = true;
@ -254,27 +254,29 @@ bool XmaDecoder::BlockOnContext(uint32_t guest_ptr, bool poll) {
} }
uint32_t XmaDecoder::ReadRegister(uint32_t addr) { uint32_t XmaDecoder::ReadRegister(uint32_t addr) {
uint32_t r = (addr & 0xFFFF) / 4; auto r = (addr & 0xFFFF) / 4;
assert_true(r < XmaRegisterFile::kRegisterCount); assert_true(r < XmaRegisterFile::kRegisterCount);
switch (r) { switch (r) {
case XE_XMA_REG_CURRENT_CONTEXT_INDEX: { case XmaRegister::ContextArrayAddress:
break;
case XmaRegister::CurrentContextIndex: {
// 0606h (1818h) is rotating context processing # set to hardware ID of // 0606h (1818h) is rotating context processing # set to hardware ID of
// context being processed. // context being processed.
// If bit 200h is set, the locking code will possibly collide on hardware // If bit 200h is set, the locking code will possibly collide on hardware
// IDs and error out, so we should never set it (I think?). // IDs and error out, so we should never set it (I think?).
uint32_t& current_context_index = uint32_t& current_context_index =
register_file_[XE_XMA_REG_CURRENT_CONTEXT_INDEX].u32; register_file_[XmaRegister::CurrentContextIndex];
uint32_t& next_context_index = uint32_t& next_context_index =
register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32; register_file_[XmaRegister::NextContextIndex];
// To prevent games from seeing a stuck XMA context, return a rotating // To prevent games from seeing a stuck XMA context, return a rotating
// number. // number.
current_context_index = next_context_index; current_context_index = next_context_index;
next_context_index = (next_context_index + 1) % kContextCount; next_context_index = (next_context_index + 1) % kContextCount;
break; break;
} }
default: { default:
const auto register_info = register_file_.GetRegisterInfo(r); const auto register_info = register_file_.GetRegisterInfo(r);
if (register_info) { if (register_info) {
XELOGW("XMA: Read from unhandled register ({:04X}, {})", r, XELOGW("XMA: Read from unhandled register ({:04X}, {})", r,
@ -283,10 +285,9 @@ uint32_t XmaDecoder::ReadRegister(uint32_t addr) {
XELOGW("XMA: Read from unknown register ({:04X})", r); XELOGW("XMA: Read from unknown register ({:04X})", r);
} }
break; break;
}
} }
return xe::byte_swap(register_file_.values[r].u32); return xe::byte_swap(register_file_[r]);
} }
void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
@ -296,16 +297,16 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
value = xe::byte_swap(value); value = xe::byte_swap(value);
assert_true(r < XmaRegisterFile::kRegisterCount); assert_true(r < XmaRegisterFile::kRegisterCount);
register_file_.values[r].u32 = value; register_file_[r] = value;
if (r >= XE_XMA_REG_CONTEXT_KICK_0 && r <= XE_XMA_REG_CONTEXT_KICK_9) { if (r >= XmaRegister::Context0Kick && r <= XmaRegister::Context9Kick) {
// Context kick command. // Context kick command.
// This will kick off the given hardware contexts. // This will kick off the given hardware contexts.
// Basically, this kicks the SPU and says "hey, decode that audio!" // Basically, this kicks the SPU and says "hey, decode that audio!"
// XMAEnableContext // XMAEnableContext
// The context ID is a bit in the range of the entire context array. // The context ID is a bit in the range of the entire context array.
uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_KICK_0) * 32; uint32_t base_context_id = (r - XmaRegister::Context0Kick) * 32;
for (int i = 0; value && i < 32; ++i, value >>= 1) { for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) { if (value & 1) {
uint32_t context_id = base_context_id + i; uint32_t context_id = base_context_id + i;
@ -315,11 +316,11 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
} }
// Signal the decoder thread to start processing. // Signal the decoder thread to start processing.
work_event_->Set(); work_event_->Set();
} else if (r >= XE_XMA_REG_CONTEXT_LOCK_0 && r <= XE_XMA_REG_CONTEXT_LOCK_9) { } else if (r >= XmaRegister::Context0Lock && r <= XmaRegister::Context9Lock) {
// Context lock command. // Context lock command.
// This requests a lock by flagging the context. // This requests a lock by flagging the context.
// XMADisableContext // XMADisableContext
uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_LOCK_0) * 32; uint32_t base_context_id = (r - XmaRegister::Context0Lock) * 32;
for (int i = 0; value && i < 32; ++i, value >>= 1) { for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) { if (value & 1) {
uint32_t context_id = base_context_id + i; uint32_t context_id = base_context_id + i;
@ -328,12 +329,12 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
} }
} }
// Signal the decoder thread to start processing. // Signal the decoder thread to start processing.
work_event_->Set(); // work_event_->Set();
} else if (r >= XE_XMA_REG_CONTEXT_CLEAR_0 && } else if (r >= XmaRegister::Context0Clear &&
r <= XE_XMA_REG_CONTEXT_CLEAR_9) { r <= XmaRegister::Context9Clear) {
// Context clear command. // Context clear command.
// This will reset the given hardware contexts. // This will reset the given hardware contexts.
uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_CLEAR_0) * 32; uint32_t base_context_id = (r - XmaRegister::Context0Clear) * 32;
for (int i = 0; value && i < 32; ++i, value >>= 1) { for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) { if (value & 1) {
uint32_t context_id = base_context_id + i; uint32_t context_id = base_context_id + i;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -37,7 +37,7 @@ class XmaDecoder {
void Shutdown(); void Shutdown();
uint32_t context_array_ptr() const { uint32_t context_array_ptr() const {
return register_file_.values[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32; return register_file_[XmaRegister::ContextArrayAddress];
} }
uint32_t AllocateContext(); uint32_t AllocateContext();

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -20,10 +20,9 @@ XmaRegisterFile::XmaRegisterFile() { std::memset(values, 0, sizeof(values)); }
const XmaRegisterInfo* XmaRegisterFile::GetRegisterInfo(uint32_t index) { const XmaRegisterInfo* XmaRegisterFile::GetRegisterInfo(uint32_t index) {
switch (index) { switch (index) {
#define XE_XMA_REGISTER(index, type, name) \ #define XE_XMA_REGISTER(index, name) \
case index: { \ case index: { \
static const XmaRegisterInfo reg_info = { \ static const XmaRegisterInfo reg_info = { \
XmaRegisterInfo::Type::type, \
#name, \ #name, \
}; \ }; \
return &reg_info; \ return &reg_info; \

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -16,18 +16,13 @@
namespace xe { namespace xe {
namespace apu { namespace apu {
enum XmaRegister { struct XmaRegister {
#define XE_XMA_REGISTER(index, type, name) XE_XMA_REG_##name = index, #define XE_XMA_REGISTER(index, name) static const uint32_t name = index;
#include "xenia/apu/xma_register_table.inc" #include "xenia/apu/xma_register_table.inc"
#undef XE_XMA_REGISTER #undef XE_XMA_REGISTER
}; };
struct XmaRegisterInfo { struct XmaRegisterInfo {
enum class Type {
kDword,
kFloat,
};
Type type;
const char* name; const char* name;
}; };
@ -38,14 +33,10 @@ class XmaRegisterFile {
static const XmaRegisterInfo* GetRegisterInfo(uint32_t index); static const XmaRegisterInfo* GetRegisterInfo(uint32_t index);
static const size_t kRegisterCount = (0xFFFF + 1) / 4; static const size_t kRegisterCount = (0xFFFF + 1) / 4;
union RegisterValue { uint32_t values[kRegisterCount];
uint32_t u32;
float f32;
};
RegisterValue values[kRegisterCount];
RegisterValue& operator[](int reg) { return values[reg]; } uint32_t operator[](uint32_t reg) const { return values[reg]; }
RegisterValue& operator[](XmaRegister reg) { return values[reg]; } uint32_t& operator[](uint32_t reg) { return values[reg]; }
}; };
} // namespace apu } // namespace apu

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,42 +10,72 @@
// This is a partial file designed to be included by other files when // This is a partial file designed to be included by other files when
// constructing various tables. // constructing various tables.
//#define XE_XMA_REGISTER(index, type, name) #ifndef XE_XMA_REGISTER
#define XE_XMA_REGISTER(index, name)
#define __XE_XMA_REGISTER_UNSET
#endif
XE_XMA_REGISTER(0x0600, kDword, CONTEXT_ARRAY_ADDRESS) #ifndef XE_XMA_REGISTER_CONTEXT_GROUP
#define XE_XMA_REGISTER_CONTEXT_GROUP(index, suffix) \
XE_XMA_REGISTER(index + 0, Context0##suffix) \
XE_XMA_REGISTER(index + 1, Context1##suffix) \
XE_XMA_REGISTER(index + 2, Context2##suffix) \
XE_XMA_REGISTER(index + 3, Context3##suffix) \
XE_XMA_REGISTER(index + 4, Context4##suffix) \
XE_XMA_REGISTER(index + 5, Context5##suffix) \
XE_XMA_REGISTER(index + 6, Context6##suffix) \
XE_XMA_REGISTER(index + 7, Context7##suffix) \
XE_XMA_REGISTER(index + 8, Context8##suffix) \
XE_XMA_REGISTER(index + 9, Context9##suffix)
#endif
XE_XMA_REGISTER(0x0606, kDword, CURRENT_CONTEXT_INDEX) // 0x0000..0x001F : ???
XE_XMA_REGISTER(0x0607, kDword, NEXT_CONTEXT_INDEX) // 0x0020..0x03FF : all 0xFFs?
// 0x0400..0x043F : ???
// 0x0440..0x047F : all 0xFFs?
// 0x0480..0x048B : ???
// 0x048C..0x04C0 : all 0xFFs?
// 0x04C1..0x04CB : ???
// 0x04CC..0x04FF : all 0xFFs?
// 0x0500..0x051F : ???
// 0x0520..0x057F : all 0xFFs?
// 0x0580..0x058F : ???
// 0x0590..0x05FF : all 0xFFs?
XE_XMA_REGISTER(0x0650, kDword, CONTEXT_KICK_0) // XMA stuff is probably only 0x0600..0x06FF
XE_XMA_REGISTER(0x0651, kDword, CONTEXT_KICK_1) //---------------------------------------------------------------------------//
XE_XMA_REGISTER(0x0652, kDword, CONTEXT_KICK_2)
XE_XMA_REGISTER(0x0653, kDword, CONTEXT_KICK_3)
XE_XMA_REGISTER(0x0654, kDword, CONTEXT_KICK_4)
XE_XMA_REGISTER(0x0655, kDword, CONTEXT_KICK_5)
XE_XMA_REGISTER(0x0656, kDword, CONTEXT_KICK_6)
XE_XMA_REGISTER(0x0657, kDword, CONTEXT_KICK_7)
XE_XMA_REGISTER(0x0658, kDword, CONTEXT_KICK_8)
XE_XMA_REGISTER(0x0659, kDword, CONTEXT_KICK_9)
XE_XMA_REGISTER(0x0690, kDword, CONTEXT_LOCK_0) XE_XMA_REGISTER(0x0600, ContextArrayAddress)
XE_XMA_REGISTER(0x0691, kDword, CONTEXT_LOCK_1) // 0x0601..0x0605 : ???
XE_XMA_REGISTER(0x0692, kDword, CONTEXT_LOCK_2) XE_XMA_REGISTER(0x0606, CurrentContextIndex)
XE_XMA_REGISTER(0x0693, kDword, CONTEXT_LOCK_3) XE_XMA_REGISTER(0x0607, NextContextIndex)
XE_XMA_REGISTER(0x0694, kDword, CONTEXT_LOCK_4) // 0x0608 : ???
XE_XMA_REGISTER(0x0695, kDword, CONTEXT_LOCK_5) // 0x0609..0x060F : zero?
XE_XMA_REGISTER(0x0696, kDword, CONTEXT_LOCK_6) XE_XMA_REGISTER_CONTEXT_GROUP(0x0610, Unknown610)
XE_XMA_REGISTER(0x0697, kDword, CONTEXT_LOCK_7) // 0x061A..0x061F : zero?
XE_XMA_REGISTER(0x0698, kDword, CONTEXT_LOCK_8) XE_XMA_REGISTER_CONTEXT_GROUP(0x0620, Unknown620)
XE_XMA_REGISTER(0x0699, kDword, CONTEXT_LOCK_9) // 0x062A..0x0641 : zero?
// 0x0642..0x0644 : ???
// 0x0645..0x064F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0650, Kick)
// 0x065A..0x065F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0660, Unknown660)
// 0x066A..0x0681 : zero?
// 0x0682..0x0684 : ???
// 0x0685..0x068F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x0690, Lock)
// 0x069A..0x069F : zero?
XE_XMA_REGISTER_CONTEXT_GROUP(0x06A0, Clear)
XE_XMA_REGISTER(0x06A0, kDword, CONTEXT_CLEAR_0) //---------------------------------------------------------------------------//
XE_XMA_REGISTER(0x06A1, kDword, CONTEXT_CLEAR_1)
XE_XMA_REGISTER(0x06A2, kDword, CONTEXT_CLEAR_2) // 0x0700..0x07FF : all 0xFFs?
XE_XMA_REGISTER(0x06A3, kDword, CONTEXT_CLEAR_3) // 0x0800..0x17FF : ???
XE_XMA_REGISTER(0x06A4, kDword, CONTEXT_CLEAR_4) // 0x1800..0x2FFF : all 0xFFs?
XE_XMA_REGISTER(0x06A5, kDword, CONTEXT_CLEAR_5) // 0x3000..0x30FF : ???
XE_XMA_REGISTER(0x06A6, kDword, CONTEXT_CLEAR_6) // 0x3100..0x3FFF : all 0xFFs?
XE_XMA_REGISTER(0x06A7, kDword, CONTEXT_CLEAR_7)
XE_XMA_REGISTER(0x06A8, kDword, CONTEXT_CLEAR_8) #ifdef __XE_XMA_REGISTER_UNSET
XE_XMA_REGISTER(0x06A9, kDword, CONTEXT_CLEAR_9) #undef __XE_XMA_REGISTER_UNSET
#undef XE_XMA_REGISTER
#endif

View File

@ -81,20 +81,23 @@ inline T byte_swap(T value) {
template <typename T, std::endian E> template <typename T, std::endian E>
struct endian_store { struct endian_store {
endian_store() = default; endian_store() = default;
endian_store(const T& src) { endian_store(const T& src) { set(src); }
endian_store(const endian_store& other) { set(other); }
operator T() const { return get(); }
void set(const T& src) {
if constexpr (std::endian::native == E) { if constexpr (std::endian::native == E) {
value = src; value = src;
} else { } else {
value = xe::byte_swap(src); value = xe::byte_swap(src);
} }
} }
endian_store(const endian_store& other) { value = other.value; } void set(const endian_store& other) { value = other.value; }
operator T() const { T get() const {
if constexpr (std::endian::native == E) { if constexpr (std::endian::native == E) {
return value; return value;
} else {
return xe::byte_swap(value);
} }
return xe::byte_swap(value);
} }
endian_store<T, E>& operator+=(int a) { endian_store<T, E>& operator+=(int a) {

View File

@ -20,18 +20,19 @@ ByteStream::ByteStream(uint8_t* data, size_t data_length, size_t offset)
ByteStream::~ByteStream() = default; ByteStream::~ByteStream() = default;
void ByteStream::Advance(size_t num_bytes) { offset_ += num_bytes; } void ByteStream::Advance(size_t num_bytes) {
assert_true(offset_ + num_bytes <= data_length_);
offset_ += num_bytes;
}
void ByteStream::Read(uint8_t* buf, size_t len) { void ByteStream::Read(uint8_t* buf, size_t len) {
assert_true(offset_ < data_length_); assert_true(offset_ + len <= data_length_);
std::memcpy(buf, data_ + offset_, len); std::memcpy(buf, data_ + offset_, len);
Advance(len); Advance(len);
} }
void ByteStream::Write(const uint8_t* buf, size_t len) { void ByteStream::Write(const uint8_t* buf, size_t len) {
assert_true(offset_ < data_length_); assert_true(offset_ + len <= data_length_);
std::memcpy(data_ + offset_, buf, len); std::memcpy(data_ + offset_, buf, len);
Advance(len); Advance(len);
} }
@ -41,7 +42,6 @@ std::string ByteStream::Read() {
std::string str; std::string str;
uint32_t len = Read<uint32_t>(); uint32_t len = Read<uint32_t>();
str.resize(len); str.resize(len);
Read(reinterpret_cast<uint8_t*>(&str[0]), len); Read(reinterpret_cast<uint8_t*>(&str[0]), len);
return str; return str;
} }
@ -49,9 +49,8 @@ std::string ByteStream::Read() {
template <> template <>
std::u16string ByteStream::Read() { std::u16string ByteStream::Read() {
std::u16string str; std::u16string str;
uint32_t len = Read<uint32_t>(); size_t len = Read<uint32_t>();
str.resize(len); str.resize(len);
Read(reinterpret_cast<uint8_t*>(&str[0]), len * 2); Read(reinterpret_cast<uint8_t*>(&str[0]), len * 2);
return str; return str;
} }

View File

@ -14,6 +14,10 @@
#define UTF_CPP_CPLUSPLUS 201703L #define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h" #include "third_party/utfcpp/source/utf8.h"
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/base/system.h"
namespace utfcpp = utf8; namespace utfcpp = utf8;
using u8_citer = utfcpp::iterator<std::string_view::const_iterator>; using u8_citer = utfcpp::iterator<std::string_view::const_iterator>;
@ -61,7 +65,14 @@ void ParseLaunchArguments(int& argc, char**& argv,
auto result = options.parse(argc, argv); auto result = options.parse(argc, argv);
if (result.count("help")) { if (result.count("help")) {
PrintHelpAndExit(); xe::AttachConsole();
if (xe::has_console_attached()) {
PrintHelpAndExit();
} else {
xe::ShowSimpleMessageBox(xe::SimpleMessageBoxType::Help,
options.help({""}));
exit(0);
}
} }
for (auto& it : *CmdVars) { for (auto& it : *CmdVars) {
@ -78,8 +89,16 @@ void ParseLaunchArguments(int& argc, char**& argv,
} }
} }
} catch (const cxxopts::OptionException& e) { } catch (const cxxopts::OptionException& e) {
std::cout << e.what() << std::endl; xe::AttachConsole();
PrintHelpAndExit(); if (xe::has_console_attached()) {
std::cout << e.what() << std::endl;
PrintHelpAndExit();
} else {
std::string m =
"Invalid launch options were given.\n" + options.help({""});
xe::ShowSimpleMessageBox(xe::SimpleMessageBoxType::Error, m);
exit(0);
}
} }
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<!-- Automatically convert endianness for xe::be --> <!-- Automatically convert endianness for xe::endian_store<,1> -->
<Type Name="xe::be&lt;unsigned __int64&gt;"> <Type Name="xe::endian_store&lt;unsigned __int64, 1&gt;">
<DisplayString> <DisplayString>
{(((value &amp; 0xFF00000000000000) &gt;&gt; 56) | {(((value &amp; 0xFF00000000000000) &gt;&gt; 56) |
((value &amp; 0x00FF000000000000) &gt;&gt; 40) | ((value &amp; 0x00FF000000000000) &gt;&gt; 40) |
@ -14,7 +14,7 @@
((value &amp; 0x00000000000000FF) &lt;&lt; 56))} ((value &amp; 0x00000000000000FF) &lt;&lt; 56))}
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;__int64&gt;"> <Type Name="xe::endian_store&lt;__int64, 1&gt;">
<DisplayString> <DisplayString>
{(((value &amp; 0xFF00000000000000) &gt;&gt; 56) | {(((value &amp; 0xFF00000000000000) &gt;&gt; 56) |
((value &amp; 0x00FF000000000000) &gt;&gt; 40) | ((value &amp; 0x00FF000000000000) &gt;&gt; 40) |
@ -27,7 +27,7 @@
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;unsigned int&gt;"> <Type Name="xe::endian_store&lt;unsigned int, 1&gt;">
<DisplayString> <DisplayString>
{(((value &amp; 0xFF000000) &gt;&gt; 24) | {(((value &amp; 0xFF000000) &gt;&gt; 24) |
((value &amp; 0x00FF0000) &gt;&gt; 8) | ((value &amp; 0x00FF0000) &gt;&gt; 8) |
@ -35,7 +35,7 @@
((value &amp; 0x000000FF) &lt;&lt; 24))} ((value &amp; 0x000000FF) &lt;&lt; 24))}
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;int&gt;"> <Type Name="xe::endian_store&lt;int, 1&gt;">
<DisplayString> <DisplayString>
{(((value &amp; 0xFF000000) &gt;&gt; 24) | {(((value &amp; 0xFF000000) &gt;&gt; 24) |
((value &amp; 0x00FF0000) &gt;&gt; 8) | ((value &amp; 0x00FF0000) &gt;&gt; 8) |
@ -44,25 +44,25 @@
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;unsigned short&gt;"> <Type Name="xe::endian_store&lt;unsigned short, 1&gt;">
<DisplayString> <DisplayString>
{(((value &amp; 0xFF00) &gt;&gt; 8) | {(((value &amp; 0xFF00) &gt;&gt; 8) |
((value &amp; 0x00FF) &lt;&lt; 8))} ((value &amp; 0x00FF) &lt;&lt; 8))}
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;short&gt;"> <Type Name="xe::endian_store&lt;short, 1&gt;">
<DisplayString> <DisplayString>
{(((value &amp; 0xFF00) &gt;&gt; 8) | {(((value &amp; 0xFF00) &gt;&gt; 8) |
((value &amp; 0x00FF) &lt;&lt; 8))} ((value &amp; 0x00FF) &lt;&lt; 8))}
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;unsigned char&gt;"> <Type Name="xe::endian_store&lt;unsigned char, 1&gt;">
<DisplayString> <DisplayString>
{value} {value}
</DisplayString> </DisplayString>
</Type> </Type>
<Type Name="xe::be&lt;char&gt;"> <Type Name="xe::endian_store&lt;char, 1&gt;">
<DisplayString> <DisplayString>
{value} {value}
</DisplayString> </DisplayString>

View File

@ -26,6 +26,7 @@
#include "xenia/base/memory.h" #include "xenia/base/memory.h"
#include "xenia/base/ring_buffer.h" #include "xenia/base/ring_buffer.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/base/system.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
// For MessageBox: // For MessageBox:
@ -58,7 +59,7 @@ struct LogLine {
size_t buffer_length; size_t buffer_length;
uint32_t thread_id; uint32_t thread_id;
uint16_t _pad_0; // (2b) padding uint16_t _pad_0; // (2b) padding
uint8_t _pad_1; // (1b) padding bool terminate;
char prefix_char; char prefix_char;
}; };
@ -81,8 +82,7 @@ class Logger {
explicit Logger(const std::string_view app_name) explicit Logger(const std::string_view app_name)
: wait_strategy_(), : wait_strategy_(),
claim_strategy_(kBlockCount, wait_strategy_), claim_strategy_(kBlockCount, wait_strategy_),
consumed_(wait_strategy_), consumed_(wait_strategy_) {
running_(true) {
claim_strategy_.add_claim_barrier(consumed_); claim_strategy_.add_claim_barrier(consumed_);
write_thread_ = write_thread_ =
@ -91,7 +91,7 @@ class Logger {
} }
~Logger() { ~Logger() {
running_ = false; AppendLine(0, '\0', nullptr, 0, true); // append a terminator
xe::threading::Wait(write_thread_.get(), true); xe::threading::Wait(write_thread_.get(), true);
} }
@ -124,7 +124,6 @@ class Logger {
std::vector<std::unique_ptr<LogSink>> sinks_; std::vector<std::unique_ptr<LogSink>> sinks_;
std::atomic<bool> running_;
std::unique_ptr<xe::threading::Thread> write_thread_; std::unique_ptr<xe::threading::Thread> write_thread_;
void Write(const char* buf, size_t size) { void Write(const char* buf, size_t size) {
@ -153,35 +152,30 @@ class Logger {
auto available_sequence = claim_strategy_.wait_until_published( auto available_sequence = claim_strategy_.wait_until_published(
next_range.last(), last_sequence); next_range.last(), last_sequence);
auto available_difference =
dp::difference(available_sequence, next_sequence);
size_t read_count = 0; size_t read_count = 0;
auto available_range = next_range;
auto available_count = available_range.size();
if (available_difference > 0 && rb.set_write_offset(BlockOffset(available_range.end()));
static_cast<size_t>(available_difference) >= desired_count) {
auto available_range = dp::sequence_range(
next_sequence, static_cast<size_t>(available_difference));
auto available_count = available_range.size();
rb.set_write_offset(BlockOffset(available_range.end())); bool terminate = false;
for (size_t i = available_range.first(); i != available_range.end();) {
rb.set_read_offset(BlockOffset(i));
for (size_t i = available_range.first(); i != available_range.end();) { LogLine line;
rb.set_read_offset(BlockOffset(i)); rb.Read(&line, sizeof(line));
LogLine line; auto needed_count = BlockCount(sizeof(LogLine) + line.buffer_length);
rb.Read(&line, sizeof(line)); if (read_count + needed_count > available_count) {
// More blocks are needed for a complete line.
auto needed_count = BlockCount(sizeof(LogLine) + line.buffer_length); desired_count = needed_count;
if (read_count + needed_count > available_count) { break;
// More blocks are needed for a complete line. } else {
desired_count = needed_count; // Enough blocks to read this log line, advance by that many.
break; read_count += needed_count;
} else { i += needed_count;
// Enough blocks to read this log line, advance by that many.
read_count += needed_count;
i += needed_count;
if (line.prefix_char) {
char prefix[] = { char prefix[] = {
line.prefix_char, line.prefix_char,
'>', '>',
@ -200,44 +194,53 @@ class Logger {
fmt::format_to_n(prefix + 3, sizeof(prefix) - 3, "{:08X}", fmt::format_to_n(prefix + 3, sizeof(prefix) - 3, "{:08X}",
line.thread_id); line.thread_id);
Write(prefix, sizeof(prefix) - 1); Write(prefix, sizeof(prefix) - 1);
}
if (line.buffer_length) { if (line.buffer_length) {
// Get access to the line data - which may be split in the ring // Get access to the line data - which may be split in the ring
// buffer - and write it out in parts. // buffer - and write it out in parts.
auto line_range = rb.BeginRead(line.buffer_length); auto line_range = rb.BeginRead(line.buffer_length);
Write(reinterpret_cast<const char*>(line_range.first), Write(reinterpret_cast<const char*>(line_range.first),
line_range.first_length); line_range.first_length);
if (line_range.second_length) { if (line_range.second_length) {
Write(reinterpret_cast<const char*>(line_range.second), Write(reinterpret_cast<const char*>(line_range.second),
line_range.second_length); line_range.second_length);
} }
// Always ensure there is a newline. // Always ensure there is a newline.
char last_char = char last_char =
line_range.second line_range.second
? line_range.second[line_range.second_length - 1] ? line_range.second[line_range.second_length - 1]
: line_range.first[line_range.first_length - 1]; : line_range.first[line_range.first_length - 1];
if (last_char != '\n') { if (last_char != '\n') {
const char suffix[1] = {'\n'};
Write(suffix, 1);
}
rb.EndRead(std::move(line_range));
} else {
// Always ensure there is a newline.
const char suffix[1] = {'\n'}; const char suffix[1] = {'\n'};
Write(suffix, 1); Write(suffix, 1);
} }
rb.EndRead(std::move(line_range));
} else {
// Always ensure there is a newline.
const char suffix[1] = {'\n'};
Write(suffix, 1);
}
if (line.terminate) {
terminate = true;
break;
} }
} }
} }
if (terminate) {
break;
}
if (read_count) { if (read_count) {
// Advance by the number of blocks we read. // Advance by the number of blocks we read.
auto read_range = dp::sequence_range(next_sequence, read_count); auto read_range = dp::sequence_range(next_sequence, read_count);
next_sequence = read_range.end(); next_sequence = read_range.end();
last_sequence = read_range.last(); last_sequence = read_range.last();
consumed_.publish(read_range.last()); consumed_.publish(last_sequence);
desired_count = 1; desired_count = 1;
@ -249,9 +252,6 @@ class Logger {
idle_loops = 0; idle_loops = 0;
} else { } else {
if (!running_) {
break;
}
if (idle_loops >= 1000) { if (idle_loops >= 1000) {
// Introduce a waiting period. // Introduce a waiting period.
xe::threading::Sleep(std::chrono::milliseconds(50)); xe::threading::Sleep(std::chrono::milliseconds(50));
@ -264,7 +264,8 @@ class Logger {
public: public:
void AppendLine(uint32_t thread_id, const char prefix_char, void AppendLine(uint32_t thread_id, const char prefix_char,
const char* buffer_data, size_t buffer_length) { const char* buffer_data, size_t buffer_length,
bool terminate = false) {
size_t count = BlockCount(sizeof(LogLine) + buffer_length); size_t count = BlockCount(sizeof(LogLine) + buffer_length);
auto range = claim_strategy_.claim(count); auto range = claim_strategy_.claim(count);
@ -278,9 +279,12 @@ class Logger {
line.buffer_length = buffer_length; line.buffer_length = buffer_length;
line.thread_id = thread_id; line.thread_id = thread_id;
line.prefix_char = prefix_char; line.prefix_char = prefix_char;
line.terminate = terminate;
rb.Write(&line, sizeof(LogLine)); rb.Write(&line, sizeof(LogLine));
rb.Write(buffer_data, buffer_length); if (buffer_length) {
rb.Write(buffer_data, buffer_length);
}
claim_strategy_.publish(range); claim_strategy_.publish(range);
} }
@ -350,12 +354,10 @@ void logging::AppendLogLine(LogLevel log_level, const char prefix_char,
void FatalError(const std::string_view str) { void FatalError(const std::string_view str) {
logging::AppendLogLine(LogLevel::Error, 'X', str); logging::AppendLogLine(LogLevel::Error, 'X', str);
#if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) { if (!xe::has_console_attached()) {
MessageBoxW(NULL, (LPCWSTR)xe::to_utf16(str).c_str(), L"Xenia Error", ShowSimpleMessageBox(SimpleMessageBoxType::Error, str);
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
} }
#endif // WIN32
ShutdownLogging(); ShutdownLogging();
std::exit(1); std::exit(1);
} }

View File

@ -22,6 +22,8 @@ namespace xe {
// Returns true if there is a user-visible console attached to receive stdout. // Returns true if there is a user-visible console attached to receive stdout.
bool has_console_attached(); bool has_console_attached();
void AttachConsole();
// Extern defined by user code. This must be present for the application to // Extern defined by user code. This must be present for the application to
// launch. // launch.
struct EntryInfo { struct EntryInfo {

View File

@ -7,6 +7,9 @@
****************************************************************************** ******************************************************************************
*/ */
#include <stdio.h>
#include <unistd.h>
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/base/main.h" #include "xenia/base/main.h"
@ -16,7 +19,9 @@
namespace xe { namespace xe {
bool has_console_attached() { return true; } bool has_console_attached() { return isatty(fileno(stdin)) == 1; }
void AttachConsole() {}
} // namespace xe } // namespace xe

View File

@ -38,8 +38,22 @@ bool has_console_attached_ = true;
bool has_console_attached() { return has_console_attached_; } bool has_console_attached() { return has_console_attached_; }
bool has_shell_environment_variable() {
size_t size = 0;
// Check if SHELL exists
// If it doesn't, then we are in a Windows Terminal
auto error = getenv_s(&size, nullptr, 0, "SHELL");
if (error) {
return false;
}
return !!size;
}
void AttachConsole() { void AttachConsole() {
if (!cvars::enable_console) { bool has_console = ::AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
if (!has_console || !has_shell_environment_variable()) {
// We weren't launched from a console, so just return.
has_console_attached_ = false;
return; return;
} }
@ -127,7 +141,9 @@ int Main() {
// Attach a console so we can write output to stdout. If the user hasn't // Attach a console so we can write output to stdout. If the user hasn't
// redirected output themselves it'll pop up a window. // redirected output themselves it'll pop up a window.
xe::AttachConsole(); if (cvars::enable_console) {
xe::AttachConsole();
}
// Setup COM on the main thread. // Setup COM on the main thread.
// NOTE: this may fail if COM has already been initialized - that's OK. // NOTE: this may fail if COM has already been initialized - that's OK.

View File

@ -30,6 +30,7 @@
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/base/profiling.h" #include "xenia/base/profiling.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
#if XE_OPTION_PROFILING #if XE_OPTION_PROFILING
@ -112,31 +113,35 @@ void Profiler::ThreadEnter(const char* name) {
void Profiler::ThreadExit() { MicroProfileOnThreadExit(); } void Profiler::ThreadExit() { MicroProfileOnThreadExit(); }
bool Profiler::OnKeyDown(int key_code) { bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) {
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
switch (key_code) { switch (virtual_key) {
case VK_OEM_3: // ` case ui::VirtualKey::kOem3: // `
MicroProfileTogglePause(); MicroProfileTogglePause();
return true; return true;
#if XE_OPTION_PROFILING_UI #if XE_OPTION_PROFILING_UI
case VK_TAB: case ui::VirtualKey::kTab:
MicroProfileToggleDisplayMode(); MicroProfileToggleDisplayMode();
return true; return true;
case 0x31: // 1 case ui::VirtualKey::k1:
MicroProfileModKey(1); MicroProfileModKey(1);
return true; return true;
#endif // XE_OPTION_PROFILING_UI #endif // XE_OPTION_PROFILING_UI
default:
break;
} }
return false; return false;
} }
bool Profiler::OnKeyUp(int key_code) { bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) {
switch (key_code) { switch (virtual_key) {
#if XE_OPTION_PROFILING_UI #if XE_OPTION_PROFILING_UI
case 0x31: // 1 case ui::VirtualKey::k1:
MicroProfileModKey(0); MicroProfileModKey(0);
return true; return true;
#endif // XE_OPTION_PROFILING_UI #endif // XE_OPTION_PROFILING_UI
default:
break;
} }
return false; return false;
} }
@ -219,14 +224,14 @@ void Profiler::set_window(ui::Window* window) {
// Watch for toggle/mode keys and such. // Watch for toggle/mode keys and such.
window_->on_key_down.AddListener([](ui::KeyEvent* e) { window_->on_key_down.AddListener([](ui::KeyEvent* e) {
if (Profiler::is_visible()) { if (Profiler::is_visible()) {
Profiler::OnKeyDown(e->key_code()); Profiler::OnKeyDown(e->virtual_key());
e->set_handled(true); e->set_handled(true);
window_->Invalidate(); window_->Invalidate();
} }
}); });
window_->on_key_up.AddListener([](ui::KeyEvent* e) { window_->on_key_up.AddListener([](ui::KeyEvent* e) {
if (Profiler::is_visible()) { if (Profiler::is_visible()) {
Profiler::OnKeyUp(e->key_code()); Profiler::OnKeyUp(e->virtual_key());
e->set_handled(true); e->set_handled(true);
window_->Invalidate(); window_->Invalidate();
} }
@ -257,8 +262,8 @@ void Profiler::Shutdown() {}
uint32_t Profiler::GetColor(const char* str) { return 0; } uint32_t Profiler::GetColor(const char* str) { return 0; }
void Profiler::ThreadEnter(const char* name) {} void Profiler::ThreadEnter(const char* name) {}
void Profiler::ThreadExit() {} void Profiler::ThreadExit() {}
bool Profiler::OnKeyDown(int key_code) { return false; } bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) { return false; }
bool Profiler::OnKeyUp(int key_code) { return false; } bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) { return false; }
void Profiler::OnMouseDown(bool left_button, bool right_button) {} void Profiler::OnMouseDown(bool left_button, bool right_button) {}
void Profiler::OnMouseUp() {} void Profiler::OnMouseUp() {}
void Profiler::OnMouseMove(int x, int y) {} void Profiler::OnMouseMove(int x, int y) {}

View File

@ -13,6 +13,7 @@
#include <memory> #include <memory>
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/ui/virtual_key.h"
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
#define XE_OPTION_PROFILING 1 #define XE_OPTION_PROFILING 1
@ -171,8 +172,8 @@ class Profiler {
// Deactivates the calling thread for profiling. // Deactivates the calling thread for profiling.
static void ThreadExit(); static void ThreadExit();
static bool OnKeyDown(int key_code); static bool OnKeyDown(ui::VirtualKey virtual_key);
static bool OnKeyUp(int key_code); static bool OnKeyUp(ui::VirtualKey virtual_key);
static void OnMouseDown(bool left_button, bool right_button); static void OnMouseDown(bool left_button, bool right_button);
static void OnMouseUp(); static void OnMouseUp();
static void OnMouseMove(int x, int y); static void OnMouseMove(int x, int y);

View File

@ -11,15 +11,24 @@
#define XENIA_BASE_SYSTEM_H_ #define XENIA_BASE_SYSTEM_H_
#include <filesystem> #include <filesystem>
#include <string> #include <string_view>
#include "xenia/base/string.h" #include "xenia/base/string.h"
namespace xe { namespace xe {
void LaunchWebBrowser(const std::string& url); void LaunchWebBrowser(const std::string_view url);
void LaunchFileExplorer(const std::filesystem::path& path); void LaunchFileExplorer(const std::filesystem::path& path);
enum class SimpleMessageBoxType {
Help,
Warning,
Error,
};
// This is expected to block the caller until the message box is closed.
void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message);
} // namespace xe } // namespace xe
#endif // XENIA_BASE_SYSTEM_H_ #endif // XENIA_BASE_SYSTEM_H_

View File

@ -7,22 +7,64 @@
****************************************************************************** ******************************************************************************
*/ */
#include <alloca.h>
#include <dlfcn.h>
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <cstring>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/platform_linux.h" #include "xenia/base/logging.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/base/system.h" #include "xenia/base/system.h"
// Use headers in third party to not depend on system sdl headers for building
#include "third_party/SDL2/include/SDL.h"
namespace xe { namespace xe {
void LaunchWebBrowser(const std::string& url) { void LaunchWebBrowser(const std::string_view url) {
auto cmd = "xdg-open " + url; auto cmd = std::string("xdg-open ");
cmd.append(url);
system(cmd.c_str()); system(cmd.c_str());
} }
void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); } void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); }
void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
void* libsdl2 = dlopen("libSDL2.so", RTLD_LAZY | RTLD_LOCAL);
assert_not_null(libsdl2);
if (libsdl2) {
auto* pSDL_ShowSimpleMessageBox =
reinterpret_cast<decltype(SDL_ShowSimpleMessageBox)*>(
dlsym(libsdl2, "SDL_ShowSimpleMessageBox"));
assert_not_null(pSDL_ShowSimpleMessageBox);
if (pSDL_ShowSimpleMessageBox) {
Uint32 flags;
const char* title;
char* message_copy = reinterpret_cast<char*>(alloca(message.size() + 1));
std::memcpy(message_copy, message.data(), message.size());
message_copy[message.size()] = '\0';
switch (type) {
default:
case SimpleMessageBoxType::Help:
title = "Xenia Help";
flags = SDL_MESSAGEBOX_INFORMATION;
break;
case SimpleMessageBoxType::Warning:
title = "Xenia Warning";
flags = SDL_MESSAGEBOX_WARNING;
break;
case SimpleMessageBoxType::Error:
title = "Xenia Error";
flags = SDL_MESSAGEBOX_ERROR;
break;
}
pSDL_ShowSimpleMessageBox(flags, title, message_copy, NULL);
}
dlclose(libsdl2);
}
}
} // namespace xe } // namespace xe

View File

@ -13,9 +13,9 @@
namespace xe { namespace xe {
void LaunchWebBrowser(const std::string& url) { void LaunchWebBrowser(const std::string_view url) {
auto temp = xe::to_utf16(url); auto wide_url = xe::to_utf16(url);
ShellExecuteW(nullptr, L"open", reinterpret_cast<LPCWSTR>(temp.c_str()), ShellExecuteW(nullptr, L"open", reinterpret_cast<LPCWSTR>(wide_url.c_str()),
nullptr, nullptr, SW_SHOWNORMAL); nullptr, nullptr, SW_SHOWNORMAL);
} }
@ -24,4 +24,28 @@ void LaunchFileExplorer(const std::filesystem::path& url) {
SW_SHOWNORMAL); SW_SHOWNORMAL);
} }
void ShowSimpleMessageBox(SimpleMessageBoxType type,
const std::string_view message) {
const wchar_t* title;
std::u16string wide_message = xe::to_utf16(message);
DWORD type_flags = MB_OK | MB_APPLMODAL | MB_SETFOREGROUND;
switch (type) {
default:
case SimpleMessageBoxType::Help:
title = L"Xenia Help";
type_flags |= MB_ICONINFORMATION;
break;
case SimpleMessageBoxType::Warning:
title = L"Xenia Warning";
type_flags |= MB_ICONWARNING;
break;
case SimpleMessageBoxType::Error:
title = L"Xenia Error";
type_flags |= MB_ICONERROR;
break;
}
MessageBoxW(nullptr, reinterpret_cast<LPCWSTR>(wide_message.c_str()), title,
type_flags);
}
} // namespace xe } // namespace xe

View File

@ -25,6 +25,14 @@ std::shared_ptr<cpptoml::table> ParseFile(
throw cpptoml::parse_exception(xe::path_to_utf8(filename) + throw cpptoml::parse_exception(xe::path_to_utf8(filename) +
" could not be opened for parsing"); " could not be opened for parsing");
} }
// since cpptoml can't parse files with a UTF-8 BOM we need to skip them
char bom[3];
file.read(bom, sizeof(bom));
if (file.fail() || bom[0] != '\xEF' || bom[1] != '\xBB' || bom[2] != '\xBF') {
file.clear();
file.seekg(0);
}
cpptoml::parser p(file); cpptoml::parser p(file);
return p.parse(); return p.parse();
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -245,65 +245,34 @@ void Value::Convert(TypeName target_type, RoundMode round_mode) {
} }
} }
template <typename T>
T __inline RoundValue(RoundMode round_mode, T value) {
switch (round_mode) {
case ROUND_TO_ZERO:
return std::trunc(value);
case ROUND_TO_NEAREST:
return std::round(value);
case ROUND_TO_MINUS_INFINITY:
return std::floor(value);
case ROUND_TO_POSITIVE_INFINITY:
return std::ceil(value);
default:
assert_unhandled_case(round_mode);
return value;
}
}
void Value::Round(RoundMode round_mode) { void Value::Round(RoundMode round_mode) {
switch (type) { switch (type) {
case FLOAT32_TYPE: case FLOAT32_TYPE:
switch (round_mode) { constant.f32 = RoundValue(round_mode, constant.f32);
case ROUND_TO_ZERO:
constant.f32 = std::trunc(constant.f32);
break;
case ROUND_TO_NEAREST:
constant.f32 = std::round(constant.f32);
return;
case ROUND_TO_MINUS_INFINITY:
constant.f32 = std::floor(constant.f32);
break;
case ROUND_TO_POSITIVE_INFINITY:
constant.f32 = std::ceil(constant.f32);
break;
default:
assert_unhandled_case(round_mode);
return;
}
return; return;
case FLOAT64_TYPE: case FLOAT64_TYPE:
switch (round_mode) { constant.f64 = RoundValue(round_mode, constant.f64);
case ROUND_TO_ZERO:
constant.f64 = std::trunc(constant.f64);
break;
case ROUND_TO_NEAREST:
constant.f64 = std::round(constant.f64);
return;
case ROUND_TO_MINUS_INFINITY:
constant.f64 = std::floor(constant.f64);
break;
case ROUND_TO_POSITIVE_INFINITY:
constant.f64 = std::ceil(constant.f64);
break;
default:
assert_unhandled_case(round_mode);
return;
}
return; return;
case VEC128_TYPE: case VEC128_TYPE:
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
switch (round_mode) { constant.v128.f32[i] = RoundValue(round_mode, constant.v128.f32[i]);
case ROUND_TO_ZERO:
constant.v128.f32[i] = std::trunc(constant.v128.f32[i]);
break;
case ROUND_TO_NEAREST:
constant.v128.f32[i] = std::round(constant.v128.f32[i]);
break;
case ROUND_TO_MINUS_INFINITY:
constant.v128.f32[i] = std::floor(constant.v128.f32[i]);
break;
case ROUND_TO_POSITIVE_INFINITY:
constant.v128.f32[i] = std::ceil(constant.v128.f32[i]);
break;
default:
assert_unhandled_case(round_mode);
return;
}
} }
return; return;
default: default:
@ -426,8 +395,9 @@ void Value::MulHi(Value* other, bool is_unsigned) {
(uint32_t)other->constant.i32) >> (uint32_t)other->constant.i32) >>
32); 32);
} else { } else {
constant.i32 = (int32_t)( constant.i32 =
((int64_t)constant.i32 * (int64_t)other->constant.i32) >> 32); (int32_t)(((int64_t)constant.i32 * (int64_t)other->constant.i32) >>
32);
} }
break; break;
case INT64_TYPE: case INT64_TYPE:

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -426,6 +426,10 @@ int InstrEmit_sc(PPCHIRBuilder& f, const InstrData& i) {
// Game code should only ever use LEV=0. // Game code should only ever use LEV=0.
// LEV=2 is to signify 'call import' from Xenia. // LEV=2 is to signify 'call import' from Xenia.
// TODO(gibbed): syscalls! // TODO(gibbed): syscalls!
if (i.SC.LEV == 0) {
f.CallExtern(f.builtins()->syscall_handler);
return 0;
}
if (i.SC.LEV == 2) { if (i.SC.LEV == 2) {
f.CallExtern(f.function()); f.CallExtern(f.function());
return 0; return 0;
@ -806,6 +810,6 @@ void RegisterEmitCategoryControl() {
XEREGISTERINSTR(mtmsrd); XEREGISTERINSTR(mtmsrd);
} }
} // namespace ppc
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
} // namespace xe

View File

@ -10,6 +10,7 @@
#include "xenia/cpu/ppc/ppc_frontend.h" #include "xenia/cpu/ppc/ppc_frontend.h"
#include "xenia/base/atomic.h" #include "xenia/base/atomic.h"
#include "xenia/base/logging.h"
#include "xenia/cpu/ppc/ppc_context.h" #include "xenia/cpu/ppc/ppc_context.h"
#include "xenia/cpu/ppc/ppc_emit.h" #include "xenia/cpu/ppc/ppc_emit.h"
#include "xenia/cpu/ppc/ppc_opcode_info.h" #include "xenia/cpu/ppc/ppc_opcode_info.h"
@ -78,6 +79,17 @@ void LeaveGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) {
global_mutex->unlock(); global_mutex->unlock();
} }
void SyscallHandler(PPCContext* ppc_context, void* arg0, void* arg1) {
uint64_t syscall_number = ppc_context->r[0];
switch (syscall_number) {
default:
assert_unhandled_case(syscall_number);
XELOGE("Unhandled syscall {}!", syscall_number);
break;
#pragma warning(suppress : 4065)
}
}
bool PPCFrontend::Initialize() { bool PPCFrontend::Initialize() {
void* arg0 = reinterpret_cast<void*>(&xe::global_critical_region::mutex()); void* arg0 = reinterpret_cast<void*>(&xe::global_critical_region::mutex());
void* arg1 = reinterpret_cast<void*>(&builtins_.global_lock_count); void* arg1 = reinterpret_cast<void*>(&builtins_.global_lock_count);
@ -87,6 +99,8 @@ bool PPCFrontend::Initialize() {
processor_->DefineBuiltin("EnterGlobalLock", EnterGlobalLock, arg0, arg1); processor_->DefineBuiltin("EnterGlobalLock", EnterGlobalLock, arg0, arg1);
builtins_.leave_global_lock = builtins_.leave_global_lock =
processor_->DefineBuiltin("LeaveGlobalLock", LeaveGlobalLock, arg0, arg1); processor_->DefineBuiltin("LeaveGlobalLock", LeaveGlobalLock, arg0, arg1);
builtins_.syscall_handler = processor_->DefineBuiltin(
"SyscallHandler", SyscallHandler, nullptr, nullptr);
return true; return true;
} }

View File

@ -33,6 +33,7 @@ struct PPCBuiltins {
Function* check_global_lock; Function* check_global_lock;
Function* enter_global_lock; Function* enter_global_lock;
Function* leave_global_lock; Function* leave_global_lock;
Function* syscall_handler;
}; };
class PPCFrontend { class PPCFrontend {

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -832,9 +832,10 @@ int XexModule::ReadPEHeaders() {
// offsetof seems to be unable to find OptionalHeader. // offsetof seems to be unable to find OptionalHeader.
#define offsetof1(type, member) ((std::size_t) & (((type*)0)->member)) #define offsetof1(type, member) ((std::size_t) & (((type*)0)->member))
#define IMAGE_FIRST_SECTION1(ntheader) \ #define IMAGE_FIRST_SECTION1(ntheader) \
((PIMAGE_SECTION_HEADER)( \ ((PIMAGE_SECTION_HEADER)((uint8_t*)ntheader + \
(uint8_t*)ntheader + offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \ offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \
((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader)) ((PIMAGE_NT_HEADERS)(ntheader)) \
->FileHeader.SizeOfOptionalHeader))
// Quick scan to determine bounds of sections. // Quick scan to determine bounds of sections.
size_t upper_address = 0; size_t upper_address = 0;

View File

@ -517,6 +517,10 @@ bool CommandProcessor::ExecutePacket(RingBuffer* reader) {
return true; return true;
} }
if (packet == 0xCDCDCDCD) {
XELOGW("GPU packet is CDCDCDCD - probably read uninitialized memory!");
}
switch (packet_type) { switch (packet_type) {
case 0x00: case 0x00:
return ExecutePacketType0(reader, packet); return ExecutePacketType0(reader, packet);

View File

@ -507,10 +507,15 @@ union alignas(uint32_t) PA_SC_WINDOW_SCISSOR_BR {
static_assert_size(PA_SC_WINDOW_SCISSOR_BR, sizeof(uint32_t)); static_assert_size(PA_SC_WINDOW_SCISSOR_BR, sizeof(uint32_t));
/******************************************************************************* /*******************************************************************************
___ ___ ___ ___ _ _ ___ ___ ___
| _ \ _ ) | _ \ __| \| | \| __| _ \
| / _ \ | / _|| .` | |) | _|| /
|_|_\___/ |_|_\___|_|\_|___/|___|_|_\
___ _ ___ _ _____ _ _ ___
| _ ) /_\ / __| |/ / __| \| | \
| _ \/ _ \ (__| ' <| _|| .` | |) |
|___/_/ \_\___|_|\_\___|_|\_|___/
*******************************************************************************/ *******************************************************************************/

View File

@ -28,6 +28,7 @@
#include "xenia/memory.h" #include "xenia/memory.h"
#include "xenia/ui/file_picker.h" #include "xenia/ui/file_picker.h"
#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
@ -135,7 +136,7 @@ bool TraceViewer::Setup() {
window_->set_imgui_input_enabled(true); window_->set_imgui_input_enabled(true);
window_->on_key_char.AddListener([&](xe::ui::KeyEvent* e) { window_->on_key_char.AddListener([&](xe::ui::KeyEvent* e) {
if (e->key_code() == 0x74 /* VK_F5 */) { if (e->virtual_key() == xe::ui::VirtualKey::kF5) {
graphics_system_->ClearCaches(); graphics_system_->ClearCaches();
e->set_handled(true); e->set_handled(true);
} }

View File

@ -194,10 +194,10 @@ enum class SurfaceNumFormat : uint32_t {
kFloat = 7, kFloat = 7,
}; };
// The EDRAM is an opaque block of memory accessible by the RB pipeline stage of // The EDRAM is an opaque block of memory accessible by the RB (render backend)
// the GPU, which performs output-merger functionality (color render target // pipeline stage of the GPU, which performs output-merger functionality (color
// writing and blending, depth and stencil testing) and resolve (copy) // render target writing and blending, depth and stencil testing) and resolve
// operations. // (copy) operations.
// //
// Data in the 10 MiB of EDRAM is laid out as 2048 tiles on 80x16 32bpp MSAA // Data in the 10 MiB of EDRAM is laid out as 2048 tiles on 80x16 32bpp MSAA
// samples. With 2x MSAA, one pixel consists of 1x2 samples, and with 4x, it // samples. With 2x MSAA, one pixel consists of 1x2 samples, and with 4x, it

View File

@ -10,8 +10,8 @@
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <forward_list> #include <forward_list>
#include <map>
#include <tuple> #include <tuple>
#include <unordered_map>
#include "third_party/fmt/include/fmt/format.h" #include "third_party/fmt/include/fmt/format.h"
#include "third_party/imgui/imgui.h" #include "third_party/imgui/imgui.h"
@ -23,6 +23,7 @@
#include "xenia/hid/hid_flags.h" #include "xenia/hid/hid_flags.h"
#include "xenia/hid/input_system.h" #include "xenia/hid/input_system.h"
#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/vulkan/vulkan_provider.h" #include "xenia/ui/vulkan/vulkan_provider.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
@ -281,44 +282,42 @@ void DrawInputGetState() {
ImGui::EndChild(); ImGui::EndChild();
} }
static const std::map<std::underlying_type<X_INPUT_GAMEPAD_VK>::type, static const std::unordered_map<ui::VirtualKey, const std::string> kVkPretty = {
const std::string> {ui::VirtualKey::kXInputPadA, "A"},
vk_pretty = { {ui::VirtualKey::kXInputPadB, "B"},
{X_INPUT_GAMEPAD_VK_A, "A"}, {ui::VirtualKey::kXInputPadX, "X"},
{X_INPUT_GAMEPAD_VK_B, "B"}, {ui::VirtualKey::kXInputPadY, "Y"},
{X_INPUT_GAMEPAD_VK_X, "X"}, {ui::VirtualKey::kXInputPadRShoulder, "R Shoulder"},
{X_INPUT_GAMEPAD_VK_Y, "Y"}, {ui::VirtualKey::kXInputPadLShoulder, "L Shoulder"},
{X_INPUT_GAMEPAD_VK_RSHOULDER, "R Shoulder"}, {ui::VirtualKey::kXInputPadLTrigger, "L Trigger"},
{X_INPUT_GAMEPAD_VK_LSHOULDER, "L Shoulder"}, {ui::VirtualKey::kXInputPadRTrigger, "R Trigger"},
{X_INPUT_GAMEPAD_VK_LTRIGGER, "L Trigger"},
{X_INPUT_GAMEPAD_VK_RTRIGGER, "R Trigger"},
{X_INPUT_GAMEPAD_VK_DPAD_UP, "DPad up"}, {ui::VirtualKey::kXInputPadDpadUp, "DPad up"},
{X_INPUT_GAMEPAD_VK_DPAD_DOWN, "DPad down"}, {ui::VirtualKey::kXInputPadDpadDown, "DPad down"},
{X_INPUT_GAMEPAD_VK_DPAD_LEFT, "DPad left"}, {ui::VirtualKey::kXInputPadDpadLeft, "DPad left"},
{X_INPUT_GAMEPAD_VK_DPAD_RIGHT, "DPad right"}, {ui::VirtualKey::kXInputPadDpadRight, "DPad right"},
{X_INPUT_GAMEPAD_VK_START, "Start"}, {ui::VirtualKey::kXInputPadStart, "Start"},
{X_INPUT_GAMEPAD_VK_BACK, "Back"}, {ui::VirtualKey::kXInputPadBack, "Back"},
{X_INPUT_GAMEPAD_VK_LTHUMB_PRESS, "L Thumb press"}, {ui::VirtualKey::kXInputPadLThumbPress, "L Thumb press"},
{X_INPUT_GAMEPAD_VK_RTHUMB_PRESS, "R Thumb press"}, {ui::VirtualKey::kXInputPadRThumbPress, "R Thumb press"},
{X_INPUT_GAMEPAD_VK_LTHUMB_UP, "L Thumb up"}, {ui::VirtualKey::kXInputPadLThumbUp, "L Thumb up"},
{X_INPUT_GAMEPAD_VK_LTHUMB_DOWN, "L Thumb down"}, {ui::VirtualKey::kXInputPadLThumbDown, "L Thumb down"},
{X_INPUT_GAMEPAD_VK_LTHUMB_RIGHT, "L Thumb right"}, {ui::VirtualKey::kXInputPadLThumbRight, "L Thumb right"},
{X_INPUT_GAMEPAD_VK_LTHUMB_LEFT, "L Thumb left"}, {ui::VirtualKey::kXInputPadLThumbLeft, "L Thumb left"},
{X_INPUT_GAMEPAD_VK_LTHUMB_UPLEFT, "L Thumb up & left"}, {ui::VirtualKey::kXInputPadLThumbUpLeft, "L Thumb up & left"},
{X_INPUT_GAMEPAD_VK_LTHUMB_UPRIGHT, "L Thumb up & right"}, {ui::VirtualKey::kXInputPadLThumbUpRight, "L Thumb up & right"},
{X_INPUT_GAMEPAD_VK_LTHUMB_DOWNRIGHT, "L Thumb down & right"}, {ui::VirtualKey::kXInputPadLThumbDownRight, "L Thumb down & right"},
{X_INPUT_GAMEPAD_VK_LTHUMB_DOWNLEFT, "L Thumb down & left"}, {ui::VirtualKey::kXInputPadLThumbDownLeft, "L Thumb down & left"},
{X_INPUT_GAMEPAD_VK_RTHUMB_UP, "R Thumb up"}, {ui::VirtualKey::kXInputPadRThumbUp, "R Thumb up"},
{X_INPUT_GAMEPAD_VK_RTHUMB_DOWN, "R Thumb down"}, {ui::VirtualKey::kXInputPadRThumbDown, "R Thumb down"},
{X_INPUT_GAMEPAD_VK_RTHUMB_RIGHT, "R Thumb right"}, {ui::VirtualKey::kXInputPadRThumbRight, "R Thumb right"},
{X_INPUT_GAMEPAD_VK_RTHUMB_LEFT, "R Thumb left"}, {ui::VirtualKey::kXInputPadRThumbLeft, "R Thumb left"},
{X_INPUT_GAMEPAD_VK_RTHUMB_UPLEFT, "R Thumb up & left"}, {ui::VirtualKey::kXInputPadRThumbUpLeft, "R Thumb up & left"},
{X_INPUT_GAMEPAD_VK_RTHUMB_UPRIGHT, "R Thumb up & right"}, {ui::VirtualKey::kXInputPadRThumbUpRight, "R Thumb up & right"},
{X_INPUT_GAMEPAD_VK_RTHUMB_DOWNRIGHT, "R Thumb down & right"}, {ui::VirtualKey::kXInputPadRThumbDownRight, "R Thumb down & right"},
{X_INPUT_GAMEPAD_VK_RTHUMB_DOWNLEFT, "R Thumb down & left"}, {ui::VirtualKey::kXInputPadRThumbDownLeft, "R Thumb down & left"},
}; };
void DrawUserInputGetKeystroke(uint32_t user_index, bool poll, void DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
@ -354,10 +353,12 @@ void DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
break; break;
} }
const auto key_search = vk_pretty.find(stroke.virtual_key); ui::VirtualKey virtual_key = ui::VirtualKey(stroke.virtual_key.get());
const auto key = key_search != vk_pretty.end() const auto key_search = kVkPretty.find(virtual_key);
? key_search->second std::string key =
: fmt::format("0x{:04x}", stroke.virtual_key); key_search != kVkPretty.cend()
? key_search->second
: fmt::format("0x{:04x}", uint16_t(virtual_key));
event_log.emplace_front(fmt::format( event_log.emplace_front(fmt::format(
"{:>6} {:>9}ms {:<20} {} {} {}", ImGui::GetFrameCount(), "{:>6} {:>9}ms {:<20} {} {} {}", ImGui::GetFrameCount(),
dur, key, dur, key,

View File

@ -46,43 +46,7 @@ enum X_INPUT_GAMEPAD_BUTTON {
X_INPUT_GAMEPAD_Y = 0x8000, X_INPUT_GAMEPAD_Y = 0x8000,
}; };
enum X_INPUT_GAMEPAD_VK { // For VK_PAD, use ui::VirtualKey.
X_INPUT_GAMEPAD_VK_A = 0x5800,
X_INPUT_GAMEPAD_VK_B = 0x5801,
X_INPUT_GAMEPAD_VK_X = 0x5802,
X_INPUT_GAMEPAD_VK_Y = 0x5803,
X_INPUT_GAMEPAD_VK_RSHOULDER = 0x5804,
X_INPUT_GAMEPAD_VK_LSHOULDER = 0x5805,
X_INPUT_GAMEPAD_VK_LTRIGGER = 0x5806,
X_INPUT_GAMEPAD_VK_RTRIGGER = 0x5807,
X_INPUT_GAMEPAD_VK_DPAD_UP = 0x5810,
X_INPUT_GAMEPAD_VK_DPAD_DOWN = 0x5811,
X_INPUT_GAMEPAD_VK_DPAD_LEFT = 0x5812,
X_INPUT_GAMEPAD_VK_DPAD_RIGHT = 0x5813,
X_INPUT_GAMEPAD_VK_START = 0x5814,
X_INPUT_GAMEPAD_VK_BACK = 0x5815,
X_INPUT_GAMEPAD_VK_LTHUMB_PRESS = 0x5816,
X_INPUT_GAMEPAD_VK_RTHUMB_PRESS = 0x5817,
X_INPUT_GAMEPAD_VK_LTHUMB_UP = 0x5820,
X_INPUT_GAMEPAD_VK_LTHUMB_DOWN = 0x5821,
X_INPUT_GAMEPAD_VK_LTHUMB_RIGHT = 0x5822,
X_INPUT_GAMEPAD_VK_LTHUMB_LEFT = 0x5823,
X_INPUT_GAMEPAD_VK_LTHUMB_UPLEFT = 0x5824,
X_INPUT_GAMEPAD_VK_LTHUMB_UPRIGHT = 0x5825,
X_INPUT_GAMEPAD_VK_LTHUMB_DOWNRIGHT = 0x5826,
X_INPUT_GAMEPAD_VK_LTHUMB_DOWNLEFT = 0x5827,
X_INPUT_GAMEPAD_VK_RTHUMB_UP = 0x5830,
X_INPUT_GAMEPAD_VK_RTHUMB_DOWN = 0x5831,
X_INPUT_GAMEPAD_VK_RTHUMB_RIGHT = 0x5832,
X_INPUT_GAMEPAD_VK_RTHUMB_LEFT = 0x5833,
X_INPUT_GAMEPAD_VK_RTHUMB_UPLEFT = 0x5834,
X_INPUT_GAMEPAD_VK_RTHUMB_UPRIGHT = 0x5835,
X_INPUT_GAMEPAD_VK_RTHUMB_DOWNRIGHT = 0x5836,
X_INPUT_GAMEPAD_VK_RTHUMB_DOWNLEFT = 0x5837,
};
enum X_INPUT_KEYSTROKE_FLAGS { enum X_INPUT_KEYSTROKE_FLAGS {
X_INPUT_KEYSTROKE_KEYDOWN = 0x0001, X_INPUT_KEYSTROKE_KEYDOWN = 0x0001,

View File

@ -20,6 +20,7 @@
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/helper/sdl/sdl_helper.h" #include "xenia/helper/sdl/sdl_helper.h"
#include "xenia/hid/hid_flags.h" #include "xenia/hid/hid_flags.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
// TODO(joellinn) make this path relative to the config folder. // TODO(joellinn) make this path relative to the config folder.
@ -238,48 +239,46 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags,
// The order of this list is also the order in which events are send if // The order of this list is also the order in which events are send if
// multiple buttons change at once. // multiple buttons change at once.
static_assert(sizeof(X_INPUT_GAMEPAD::buttons) == 2); static_assert(sizeof(X_INPUT_GAMEPAD::buttons) == 2);
static constexpr std::array<std::underlying_type<X_INPUT_GAMEPAD_VK>::type, static constexpr std::array<ui::VirtualKey, 34> kVkLookup = {
34> // 00 - True buttons from xinput button field
vk_lookup = { ui::VirtualKey::kXInputPadDpadUp,
// 00 - True buttons from xinput button field ui::VirtualKey::kXInputPadDpadDown,
X_INPUT_GAMEPAD_VK_DPAD_UP, ui::VirtualKey::kXInputPadDpadLeft,
X_INPUT_GAMEPAD_VK_DPAD_DOWN, ui::VirtualKey::kXInputPadDpadRight,
X_INPUT_GAMEPAD_VK_DPAD_LEFT, ui::VirtualKey::kXInputPadStart,
X_INPUT_GAMEPAD_VK_DPAD_RIGHT, ui::VirtualKey::kXInputPadBack,
X_INPUT_GAMEPAD_VK_START, ui::VirtualKey::kXInputPadLThumbPress,
X_INPUT_GAMEPAD_VK_BACK, ui::VirtualKey::kXInputPadRThumbPress,
X_INPUT_GAMEPAD_VK_LTHUMB_PRESS, ui::VirtualKey::kXInputPadLShoulder,
X_INPUT_GAMEPAD_VK_RTHUMB_PRESS, ui::VirtualKey::kXInputPadRShoulder,
X_INPUT_GAMEPAD_VK_LSHOULDER, ui::VirtualKey::kNone, /* Guide has no VK */
X_INPUT_GAMEPAD_VK_RSHOULDER, ui::VirtualKey::kNone, /* Unknown */
0, /* Guide has no VK */ ui::VirtualKey::kXInputPadA,
0, /* Unknown */ ui::VirtualKey::kXInputPadB,
X_INPUT_GAMEPAD_VK_A, ui::VirtualKey::kXInputPadX,
X_INPUT_GAMEPAD_VK_B, ui::VirtualKey::kXInputPadY,
X_INPUT_GAMEPAD_VK_X, // 16 - Fake buttons generated from analog inputs
X_INPUT_GAMEPAD_VK_Y, ui::VirtualKey::kXInputPadLTrigger,
// 16 - Fake buttons generated from analog inputs ui::VirtualKey::kXInputPadRTrigger,
X_INPUT_GAMEPAD_VK_LTRIGGER, // 18
X_INPUT_GAMEPAD_VK_RTRIGGER, ui::VirtualKey::kXInputPadLThumbUp,
// 18 ui::VirtualKey::kXInputPadLThumbDown,
X_INPUT_GAMEPAD_VK_LTHUMB_UP, ui::VirtualKey::kXInputPadLThumbRight,
X_INPUT_GAMEPAD_VK_LTHUMB_DOWN, ui::VirtualKey::kXInputPadLThumbLeft,
X_INPUT_GAMEPAD_VK_LTHUMB_RIGHT, ui::VirtualKey::kXInputPadLThumbUpLeft,
X_INPUT_GAMEPAD_VK_LTHUMB_LEFT, ui::VirtualKey::kXInputPadLThumbUpRight,
X_INPUT_GAMEPAD_VK_LTHUMB_UPLEFT, ui::VirtualKey::kXInputPadLThumbDownRight,
X_INPUT_GAMEPAD_VK_LTHUMB_UPRIGHT, ui::VirtualKey::kXInputPadLThumbDownLeft,
X_INPUT_GAMEPAD_VK_LTHUMB_DOWNRIGHT, // 26
X_INPUT_GAMEPAD_VK_LTHUMB_DOWNLEFT, ui::VirtualKey::kXInputPadRThumbUp,
// 26 ui::VirtualKey::kXInputPadRThumbDown,
X_INPUT_GAMEPAD_VK_RTHUMB_UP, ui::VirtualKey::kXInputPadRThumbRight,
X_INPUT_GAMEPAD_VK_RTHUMB_DOWN, ui::VirtualKey::kXInputPadRThumbLeft,
X_INPUT_GAMEPAD_VK_RTHUMB_RIGHT, ui::VirtualKey::kXInputPadRThumbUpLeft,
X_INPUT_GAMEPAD_VK_RTHUMB_LEFT, ui::VirtualKey::kXInputPadRThumbUpRight,
X_INPUT_GAMEPAD_VK_RTHUMB_UPLEFT, ui::VirtualKey::kXInputPadRThumbDownRight,
X_INPUT_GAMEPAD_VK_RTHUMB_UPRIGHT, ui::VirtualKey::kXInputPadRThumbDownLeft,
X_INPUT_GAMEPAD_VK_RTHUMB_DOWNRIGHT, };
X_INPUT_GAMEPAD_VK_RTHUMB_DOWNLEFT,
};
auto is_active = this->is_active(); auto is_active = this->is_active();
@ -319,9 +318,9 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags,
if (last.repeat_state == RepeatState::Repeating && if (last.repeat_state == RepeatState::Repeating &&
(last.repeat_time + HID_SDL_REPEAT_RATE < guest_now)) { (last.repeat_time + HID_SDL_REPEAT_RATE < guest_now)) {
last.repeat_time = guest_now; last.repeat_time = guest_now;
auto vk = vk_lookup.at(last.repeat_butt_idx); ui::VirtualKey vk = kVkLookup.at(last.repeat_butt_idx);
assert_not_zero(vk); assert_true(vk != ui::VirtualKey::kNone);
out_keystroke->virtual_key = vk; out_keystroke->virtual_key = uint16_t(vk);
out_keystroke->unicode = 0; out_keystroke->unicode = 0;
out_keystroke->user_index = user_index; out_keystroke->user_index = user_index;
out_keystroke->hid_code = 0; out_keystroke->hid_code = 0;
@ -340,17 +339,17 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags,
// up before THUMB_LEFT is down. // up before THUMB_LEFT is down.
for (auto [clear_pass, i] = std::tuple{true, 0}; i < 2; for (auto [clear_pass, i] = std::tuple{true, 0}; i < 2;
clear_pass = false, i++) { clear_pass = false, i++) {
for (uint8_t i = 0; i < std::size(vk_lookup); i++) { for (uint8_t i = 0; i < uint8_t(std::size(kVkLookup)); i++) {
auto fbutton = uint64_t(1) << i; auto fbutton = uint64_t(1) << i;
if (!(butts_changed & fbutton)) { if (!(butts_changed & fbutton)) {
continue; continue;
} }
auto vk = vk_lookup.at(i); ui::VirtualKey vk = kVkLookup.at(last.repeat_butt_idx);
if (!vk) { if (vk == ui::VirtualKey::kNone) {
continue; continue;
} }
out_keystroke->virtual_key = vk; out_keystroke->virtual_key = uint16_t(vk);
out_keystroke->unicode = 0; out_keystroke->unicode = 0;
out_keystroke->user_index = user_index; out_keystroke->user_index = user_index;
out_keystroke->hid_code = 0; out_keystroke->hid_code = 0;

View File

@ -12,6 +12,7 @@
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include "xenia/hid/hid_flags.h" #include "xenia/hid/hid_flags.h"
#include "xenia/hid/input_system.h" #include "xenia/hid/input_system.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
namespace xe { namespace xe {
@ -29,7 +30,7 @@ WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
auto global_lock = global_critical_region_.Acquire(); auto global_lock = global_critical_region_.Acquire();
KeyEvent key; KeyEvent key;
key.vkey = evt->key_code(); key.virtual_key = evt->virtual_key();
key.transition = true; key.transition = true;
key.prev_state = evt->prev_state(); key.prev_state = evt->prev_state();
key.repeat_count = evt->repeat_count(); key.repeat_count = evt->repeat_count();
@ -43,7 +44,7 @@ WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
auto global_lock = global_critical_region_.Acquire(); auto global_lock = global_critical_region_.Acquire();
KeyEvent key; KeyEvent key;
key.vkey = evt->key_code(); key.virtual_key = evt->virtual_key();
key.transition = false; key.transition = false;
key.prev_state = evt->prev_state(); key.prev_state = evt->prev_state();
key.repeat_count = evt->repeat_count(); key.repeat_count = evt->repeat_count();
@ -241,7 +242,7 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
X_RESULT result = X_ERROR_EMPTY; X_RESULT result = X_ERROR_EMPTY;
uint16_t virtual_key = 0; ui::VirtualKey xinput_virtual_key = ui::VirtualKey::kNone;
uint16_t unicode = 0; uint16_t unicode = 0;
uint16_t keystroke_flags = 0; uint16_t keystroke_flags = 0;
uint8_t hid_code = 0; uint8_t hid_code = 0;
@ -258,100 +259,93 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
key_events_.pop(); key_events_.pop();
} }
// TODO(DrChat): Some other way to toggle this... switch (evt.virtual_key) {
if (IS_KEY_TOGGLED(VK_CAPITAL)) { case ui::VirtualKey::kOem1: // ;
// dpad toggled xinput_virtual_key = ui::VirtualKey::kXInputPadA;
if (evt.vkey == (0x41)) { break;
// A case ui::VirtualKey::kOem7: // '
virtual_key = 0x5812; // VK_PAD_DPAD_LEFT xinput_virtual_key = ui::VirtualKey::kXInputPadB;
} else if (evt.vkey == (0x44)) { break;
// D case ui::VirtualKey::kL:
virtual_key = 0x5813; // VK_PAD_DPAD_RIGHT xinput_virtual_key = ui::VirtualKey::kXInputPadX;
} else if (evt.vkey == (0x53)) { break;
// S case ui::VirtualKey::kP:
virtual_key = 0x5811; // VK_PAD_DPAD_DOWN xinput_virtual_key = ui::VirtualKey::kXInputPadY;
} else if (evt.vkey == (0x57)) { break;
// W case ui::VirtualKey::k3:
virtual_key = 0x5810; // VK_PAD_DPAD_UP xinput_virtual_key = ui::VirtualKey::kXInputPadRShoulder;
} break;
} else { case ui::VirtualKey::k1:
// left stick xinput_virtual_key = ui::VirtualKey::kXInputPadLShoulder;
if (evt.vkey == (0x57)) { break;
// W case ui::VirtualKey::kQ:
virtual_key = 0x5820; // VK_PAD_LTHUMB_UP case ui::VirtualKey::kI:
} xinput_virtual_key = ui::VirtualKey::kXInputPadLTrigger;
if (evt.vkey == (0x53)) { break;
// S case ui::VirtualKey::kE:
virtual_key = 0x5821; // VK_PAD_LTHUMB_DOWN case ui::VirtualKey::kO:
} xinput_virtual_key = ui::VirtualKey::kXInputPadRTrigger;
if (evt.vkey == (0x44)) { break;
// D case ui::VirtualKey::kX:
virtual_key = 0x5822; // VK_PAD_LTHUMB_RIGHT xinput_virtual_key = ui::VirtualKey::kXInputPadStart;
} break;
if (evt.vkey == (0x41)) { case ui::VirtualKey::kZ:
// A xinput_virtual_key = ui::VirtualKey::kXInputPadBack;
virtual_key = 0x5823; // VK_PAD_LTHUMB_LEFT break;
} case ui::VirtualKey::kUp:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbUp;
break;
case ui::VirtualKey::kDown:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbDown;
break;
case ui::VirtualKey::kRight:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbRight;
break;
case ui::VirtualKey::kLeft:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbLeft;
break;
default:
// TODO(DrChat): Some other way to toggle this...
if (IS_KEY_TOGGLED(VK_CAPITAL) || IS_KEY_DOWN(VK_SHIFT)) {
// D-pad toggled.
switch (evt.virtual_key) {
case ui::VirtualKey::kW:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadUp;
break;
case ui::VirtualKey::kS:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadDown;
break;
case ui::VirtualKey::kA:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadLeft;
break;
case ui::VirtualKey::kD:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadRight;
break;
default:
break;
}
} else {
// Left thumbstick.
switch (evt.virtual_key) {
case ui::VirtualKey::kW:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbUp;
break;
case ui::VirtualKey::kS:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbDown;
break;
case ui::VirtualKey::kA:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbLeft;
break;
case ui::VirtualKey::kD:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbRight;
break;
default:
break;
}
}
} }
// Right stick if (xinput_virtual_key != ui::VirtualKey::kNone) {
if (evt.vkey == (0x26)) {
// Up
virtual_key = 0x5830;
}
if (evt.vkey == (0x28)) {
// Down
virtual_key = 0x5831;
}
if (evt.vkey == (0x27)) {
// Right
virtual_key = 0x5832;
}
if (evt.vkey == (0x25)) {
// Left
virtual_key = 0x5833;
}
if (evt.vkey == (0x4C)) {
// L
virtual_key = 0x5802; // VK_PAD_X
} else if (evt.vkey == (VK_OEM_7)) {
// '
virtual_key = 0x5801; // VK_PAD_B
} else if (evt.vkey == (VK_OEM_1)) {
// ;
virtual_key = 0x5800; // VK_PAD_A
} else if (evt.vkey == (0x50)) {
// P
virtual_key = 0x5803; // VK_PAD_Y
}
if (evt.vkey == (0x58)) {
// X
virtual_key = 0x5814; // VK_PAD_START
}
if (evt.vkey == (0x5A)) {
// Z
virtual_key = 0x5815; // VK_PAD_BACK
}
if (evt.vkey == (0x51) || evt.vkey == (0x49)) {
// Q / I
virtual_key = 0x5806; // VK_PAD_LTRIGGER
}
if (evt.vkey == (0x45) || evt.vkey == (0x4F)) {
// E / O
virtual_key = 0x5807; // VK_PAD_RTRIGGER
}
if (evt.vkey == (0x31)) {
// 1
virtual_key = 0x5805; // VK_PAD_LSHOULDER
}
if (evt.vkey == (0x33)) {
// 3
virtual_key = 0x5804; // VK_PAD_RSHOULDER
}
if (virtual_key != 0) {
if (evt.transition == true) { if (evt.transition == true) {
keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN
} else if (evt.transition == false) { } else if (evt.transition == false) {
@ -365,7 +359,7 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
result = X_ERROR_SUCCESS; result = X_ERROR_SUCCESS;
} }
out_keystroke->virtual_key = virtual_key; out_keystroke->virtual_key = uint16_t(xinput_virtual_key);
out_keystroke->unicode = unicode; out_keystroke->unicode = unicode;
out_keystroke->flags = keystroke_flags; out_keystroke->flags = keystroke_flags;
out_keystroke->user_index = 0; out_keystroke->user_index = 0;

View File

@ -14,6 +14,7 @@
#include "xenia/base/mutex.h" #include "xenia/base/mutex.h"
#include "xenia/hid/input_driver.h" #include "xenia/hid/input_driver.h"
#include "xenia/ui/virtual_key.h"
namespace xe { namespace xe {
namespace hid { namespace hid {
@ -35,7 +36,7 @@ class WinKeyInputDriver : public InputDriver {
protected: protected:
struct KeyEvent { struct KeyEvent {
int vkey = 0; ui::VirtualKey virtual_key = ui::VirtualKey::kNone;
int repeat_count = 0; int repeat_count = 0;
bool transition = false; // going up(false) or going down(true) bool transition = false; // going up(false) or going down(true)
bool prev_state = false; // down(true) or up(false) bool prev_state = false; // down(true) or up(false)

View File

@ -2,13 +2,12 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
// Post-include file for an export table. // Post-include file for an export table.
#undef FLAG #undef FLAG
#undef XE_EXPORT #undef XE_EXPORT

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -22,8 +22,7 @@
* my_module_export_table, xe::countof(my_module_export_table)); * my_module_export_table, xe::countof(my_module_export_table));
*/ */
#define XE_EXPORT(module, ordinal, name, type) \ #define XE_EXPORT(module, ordinal, name, type) \
xe::cpu::Export(ordinal, xe::cpu::Export::Type::type, #name) xe::cpu::Export(ordinal, xe::cpu::Export::Type::type, #name)
#define FLAG(t) kXEKernelExportFlag##t #define FLAG(t) kXEKernelExportFlag##t

View File

@ -2,12 +2,11 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
// Post-include file for an ordinal table. // Post-include file for an ordinal table.
#undef XE_EXPORT #undef XE_EXPORT

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -21,7 +21,4 @@
* #include "xenia/kernel/util/ordinal_table_post.inc" * #include "xenia/kernel/util/ordinal_table_post.inc"
*/ */
#define XE_EXPORT(module, ordinal, name, type) name = ordinal
#define XE_EXPORT(module, ordinal, name, type) \
name = ordinal

View File

@ -57,8 +57,7 @@ X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
std::memset(buffer, 0, data->buffer_size); std::memset(buffer, 0, data->buffer_size);
} }
uint32_t item_count = 0; uint32_t item_count = 0;
auto result = e->WriteItems(data->buffer_ptr, buffer, data->buffer_size, auto result = e->WriteItems(data->buffer_ptr, buffer, &item_count);
&item_count);
if (result == X_ERROR_SUCCESS && item_count >= 1) { if (result == X_ERROR_SUCCESS && item_count >= 1) {
if (data->length_ptr) { if (data->length_ptr) {

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -80,8 +80,10 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
return X_E_SUCCESS; return X_E_SUCCESS;
} }
case 0x000B0011: { case 0x000B0011: {
// TODO(DrChat): Figure out what this is again // TODO(PermaNull): reverse buffer contents.
} break; XELOGD("XGISessionDelete");
return X_STATUS_SUCCESS;
}
case 0x000B0012: { case 0x000B0012: {
assert_true(buffer_length == 0x14); assert_true(buffer_length == 0x14);
uint32_t session_ptr = xe::load_and_swap<uint32_t>(buffer + 0x0); uint32_t session_ptr = xe::load_and_swap<uint32_t>(buffer + 0x0);
@ -95,6 +97,17 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
user_count, unk_0, user_index_array, private_slots_array); user_count, unk_0, user_index_array, private_slots_array);
return X_E_SUCCESS; return X_E_SUCCESS;
} }
case 0x000B0014: {
// Gets Jetpac XBLA in game
// get high score table?
XELOGD("XGI_unknown");
return X_STATUS_SUCCESS;
}
case 0x000B0015: {
// send high scores?
XELOGD("XGI_unknown");
return X_STATUS_SUCCESS;
}
case 0x000B0041: { case 0x000B0041: {
assert_true(!buffer_length || buffer_length == 32); assert_true(!buffer_length || buffer_length == 32);
// 00000000 2789fecc 00000000 00000000 200491e0 00000000 200491f0 20049340 // 00000000 2789fecc 00000000 00000000 200491e0 00000000 200491f0 20049340

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -41,18 +41,28 @@ X_HRESULT XLiveBaseApp::DispatchMessageSync(uint32_t message,
xe::store_and_swap<uint32_t>(buffer + 0, 1); // XONLINE_NAT_OPEN xe::store_and_swap<uint32_t>(buffer + 0, 1); // XONLINE_NAT_OPEN
return X_E_SUCCESS; return X_E_SUCCESS;
} }
case 0x00058007: {
// Occurs if title calls XOnlineGetServiceInfo, expects dwServiceId
// and pServiceInfo. pServiceInfo should contain pointer to
// XONLINE_SERVICE_INFO structure.
XELOGD("CXLiveLogon::GetServiceInfo({:08X}, {:08X})", buffer_ptr,
buffer_length);
return 1229; // ERROR_CONNECTION_INVALID
}
case 0x00058020: { case 0x00058020: {
// 0x00058004 is called right before this. // 0x00058004 is called right before this.
// We should create a XamEnumerate-able empty list here, but I'm not // We should create a XamEnumerate-able empty list here, but I'm not
// sure of the format. // sure of the format.
// buffer_length seems to be the same ptr sent to 0x00058004. // buffer_length seems to be the same ptr sent to 0x00058004.
XELOGD("XLiveBaseFriendsCreateEnumerator({:08X}, {:08X}) unimplemented", XELOGD("CXLiveFriends::Enumerate({:08X}, {:08X}) unimplemented",
buffer_ptr, buffer_length); buffer_ptr, buffer_length);
return X_E_FAIL; return X_E_FAIL;
} }
case 0x00058023: { case 0x00058023: {
XELOGD("XliveBaseUnk58023({:08X}, {:08X}) unimplemented", buffer_ptr, XELOGD(
buffer_length); "CXLiveMessaging::XMessageGameInviteGetAcceptedInfo({:08X}, {:08X}) "
"unimplemented",
buffer_ptr, buffer_length);
return X_E_FAIL; return X_E_FAIL;
} }
case 0x00058046: { case 0x00058046: {

View File

@ -86,8 +86,8 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id,
*buffer_size_ptr = sizeof(XCONTENT_DATA) * items_per_enumerate; *buffer_size_ptr = sizeof(XCONTENT_DATA) * items_per_enumerate;
} }
auto e = object_ref<XStaticEnumerator>(new XStaticEnumerator( auto e = make_object<XStaticEnumerator<XCONTENT_DATA>>(kernel_state(),
kernel_state(), items_per_enumerate, sizeof(XCONTENT_DATA))); items_per_enumerate);
auto result = e->Initialize(0xFF, 0xFE, 0x20005, 0x20007, 0); auto result = e->Initialize(0xFF, 0xFE, 0x20005, 0x20007, 0);
if (XFAILED(result)) { if (XFAILED(result)) {
return result; return result;
@ -99,8 +99,7 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id,
static_cast<uint32_t>(DummyDeviceId::HDD), static_cast<uint32_t>(DummyDeviceId::HDD),
XContentType(uint32_t(content_type))); XContentType(uint32_t(content_type)));
for (const auto& content_data : content_datas) { for (const auto& content_data : content_datas) {
auto item = reinterpret_cast<XCONTENT_DATA*>(e->AppendItem()); auto item = e->AppendItem();
assert_not_null(item);
*item = content_data; *item = content_data;
} }
} }
@ -124,7 +123,6 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
lpdword_t license_mask_ptr, lpdword_t license_mask_ptr,
dword_t cache_size, qword_t content_size, dword_t cache_size, qword_t content_size,
lpvoid_t overlapped_ptr) { lpvoid_t overlapped_ptr) {
X_RESULT result = X_ERROR_INVALID_PARAMETER;
XCONTENT_AGGREGATE_DATA content_data; XCONTENT_AGGREGATE_DATA content_data;
if (content_data_size == sizeof(XCONTENT_DATA)) { if (content_data_size == sizeof(XCONTENT_DATA)) {
content_data = *content_data_ptr.as<XCONTENT_DATA*>(); content_data = *content_data_ptr.as<XCONTENT_DATA*>();
@ -132,91 +130,99 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
content_data = *content_data_ptr.as<XCONTENT_AGGREGATE_DATA*>(); content_data = *content_data_ptr.as<XCONTENT_AGGREGATE_DATA*>();
} else { } else {
assert_always(); assert_always();
return result; return X_ERROR_INVALID_PARAMETER;
} }
auto content_manager = kernel_state()->content_manager(); auto content_manager = kernel_state()->content_manager();
bool create = false;
bool open = false; if (overlapped_ptr && disposition_ptr) {
switch (flags & 0xF) { *disposition_ptr = 0;
case 1: // CREATE_NEW
// Fail if exists.
if (content_manager->ContentExists(content_data)) {
result = X_ERROR_ALREADY_EXISTS;
} else {
create = true;
}
break;
case 2: // CREATE_ALWAYS
// Overwrite existing, if any.
if (content_manager->ContentExists(content_data)) {
content_manager->DeleteContent(content_data);
create = true;
} else {
create = true;
}
break;
case 3: // OPEN_EXISTING
// Open only if exists.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_PATH_NOT_FOUND;
} else {
open = true;
}
break;
case 4: // OPEN_ALWAYS
// Create if needed.
if (!content_manager->ContentExists(content_data)) {
create = true;
} else {
open = true;
}
break;
case 5: // TRUNCATE_EXISTING
// Fail if doesn't exist, if does exist delete and recreate.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_PATH_NOT_FOUND;
} else {
content_manager->DeleteContent(content_data);
create = true;
}
break;
default:
assert_unhandled_case(flags & 0xF);
break;
} }
// creation result auto run = [content_manager, root_name, flags, content_data, disposition_ptr,
// 0 = ? license_mask_ptr](uint32_t& extended_error,
// 1 = created uint32_t& length) -> X_RESULT {
// 2 = opened X_RESULT result = X_ERROR_INVALID_PARAMETER;
uint32_t disposition = create ? 1 : 2; bool create = false;
if (disposition_ptr) { bool open = false;
// In case when overlapped_ptr exist we should clear disposition_ptr first switch (flags & 0xF) {
// however we're executing it immediately, so it's not required case 1: // CREATE_NEW
*disposition_ptr = disposition; // Fail if exists.
} if (content_manager->ContentExists(content_data)) {
result = X_ERROR_ALREADY_EXISTS;
if (create) { } else {
result = content_manager->CreateContent(root_name.value(), content_data); create = true;
} else if (open) { }
result = content_manager->OpenContent(root_name.value(), content_data); break;
} case 2: // CREATE_ALWAYS
// Overwrite existing, if any.
if (license_mask_ptr && XSUCCEEDED(result)) { if (content_manager->ContentExists(content_data)) {
*license_mask_ptr = 0; // Stub! content_manager->DeleteContent(content_data);
} create = true;
} else {
if (overlapped_ptr) { create = true;
X_RESULT extended_error = X_HRESULT_FROM_WIN32(result); }
if (int32_t(extended_error) < 0) { break;
result = X_ERROR_FUNCTION_FAILED; case 3: // OPEN_EXISTING
// Open only if exists.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_PATH_NOT_FOUND;
} else {
open = true;
}
break;
case 4: // OPEN_ALWAYS
// Create if needed.
if (!content_manager->ContentExists(content_data)) {
create = true;
} else {
open = true;
}
break;
case 5: // TRUNCATE_EXISTING
// Fail if doesn't exist, if does exist delete and recreate.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_PATH_NOT_FOUND;
} else {
content_manager->DeleteContent(content_data);
create = true;
}
break;
default:
assert_unhandled_case(flags & 0xF);
break;
} }
kernel_state()->CompleteOverlappedImmediateEx(overlapped_ptr, result,
extended_error, disposition); // creation result
return X_ERROR_IO_PENDING; // 0 = ?
} else { // 1 = created
// 2 = opened
uint32_t disposition = create ? 1 : 2;
if (disposition_ptr) {
*disposition_ptr = disposition;
}
if (create) {
result = content_manager->CreateContent(root_name.value(), content_data);
} else if (open) {
result = content_manager->OpenContent(root_name.value(), content_data);
}
if (license_mask_ptr && XSUCCEEDED(result)) {
*license_mask_ptr = 0; // Stub!
}
extended_error = X_HRESULT_FROM_WIN32(result);
length = disposition;
return result; return result;
};
if (!overlapped_ptr) {
uint32_t extended_error, length;
return run(extended_error, length);
} else {
kernel_state()->CompleteOverlappedDeferredEx(run, overlapped_ptr);
return X_ERROR_IO_PENDING;
} }
} }

View File

@ -23,7 +23,7 @@ namespace xe {
namespace kernel { namespace kernel {
namespace xam { namespace xam {
void AddODDContentTest(object_ref<XStaticEnumerator> e, void AddODDContentTest(object_ref<XStaticEnumerator<XCONTENT_AGGREGATE_DATA>> e,
XContentType content_type) { XContentType content_type) {
auto root_entry = kernel_state()->file_system()->ResolvePath( auto root_entry = kernel_state()->file_system()->ResolvePath(
"game:\\Content\\0000000000000000"); "game:\\Content\\0000000000000000");
@ -62,13 +62,15 @@ void AddODDContentTest(object_ref<XStaticEnumerator> e,
break; break;
} }
auto item = reinterpret_cast<XCONTENT_AGGREGATE_DATA*>(e->AppendItem()); auto item = e->AppendItem();
assert_not_null(item); assert_not_null(item);
item->device_id = static_cast<uint32_t>(DummyDeviceId::ODD); if (item) {
item->content_type = content_type; item->device_id = static_cast<uint32_t>(DummyDeviceId::ODD);
item->set_display_name(to_utf16(content_entry->name())); item->content_type = content_type;
item->set_file_name(content_entry->name()); item->set_display_name(to_utf16(content_entry->name()));
item->title_id = title_id; item->set_file_name(content_entry->name());
item->title_id = title_id;
}
} }
} }
} }
@ -86,8 +88,8 @@ dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid,
return X_E_INVALIDARG; return X_E_INVALIDARG;
} }
auto e = object_ref<XStaticEnumerator>(new XStaticEnumerator( auto e = make_object<XStaticEnumerator<XCONTENT_AGGREGATE_DATA>>(
kernel_state(), 1, sizeof(XCONTENT_AGGREGATE_DATA))); kernel_state(), 1);
X_KENUMERATOR_CONTENT_AGGREGATE* extra; X_KENUMERATOR_CONTENT_AGGREGATE* extra;
auto result = e->Initialize(0xFF, 0xFE, 0x2000E, 0x20010, 0, &extra); auto result = e->Initialize(0xFF, 0xFE, 0x2000E, 0x20010, 0, &extra);
if (XFAILED(result)) { if (XFAILED(result)) {
@ -116,9 +118,11 @@ dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid,
static_cast<uint32_t>(DummyDeviceId::HDD), content_type_enum, static_cast<uint32_t>(DummyDeviceId::HDD), content_type_enum,
title_id); title_id);
for (const auto& content_data : content_datas) { for (const auto& content_data : content_datas) {
auto item = reinterpret_cast<XCONTENT_AGGREGATE_DATA*>(e->AppendItem()); auto item = e->AppendItem();
assert_not_null(item); assert_not_null(item);
*item = content_data; if (item) {
*item = content_data;
}
} }
} }
} }

View File

@ -139,8 +139,8 @@ dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type,
*buffer_size_ptr = sizeof(X_CONTENT_DEVICE_DATA) * max_count; *buffer_size_ptr = sizeof(X_CONTENT_DEVICE_DATA) * max_count;
} }
auto e = object_ref<XStaticEnumerator>(new XStaticEnumerator( auto e = make_object<XStaticEnumerator<X_CONTENT_DEVICE_DATA>>(kernel_state(),
kernel_state(), max_count, sizeof(X_CONTENT_DEVICE_DATA))); max_count);
auto result = e->Initialize(0xFE, 0xFE, 0x2000A, 0x20009, 0); auto result = e->Initialize(0xFE, 0xFE, 0x2000A, 0x20009, 0);
if (XFAILED(result)) { if (XFAILED(result)) {
return result; return result;
@ -148,7 +148,8 @@ dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type,
for (const auto& device_info : dummy_device_infos_) { for (const auto& device_info : dummy_device_infos_) {
// Copy our dummy device into the enumerator // Copy our dummy device into the enumerator
auto device_data = (X_CONTENT_DEVICE_DATA*)e->AppendItem(); auto device_data = e->AppendItem();
assert_not_null(device_data);
if (device_data) { if (device_data) {
device_data->device_id = static_cast<uint32_t>(device_info->device_id); device_data->device_id = static_cast<uint32_t>(device_info->device_id);
device_data->device_type = device_data->device_type =

View File

@ -32,49 +32,37 @@ uint32_t xeXamEnumerate(uint32_t handle, uint32_t flags, lpvoid_t buffer_ptr,
uint32_t overlapped_ptr) { uint32_t overlapped_ptr) {
assert_true(flags == 0); assert_true(flags == 0);
X_RESULT result;
uint32_t item_count = 0;
auto e = kernel_state()->object_table()->LookupObject<XEnumerator>(handle); auto e = kernel_state()->object_table()->LookupObject<XEnumerator>(handle);
if (!e) { if (!e) {
result = X_ERROR_INVALID_HANDLE; return X_ERROR_INVALID_HANDLE;
} else if (!buffer_ptr) {
result = X_ERROR_INVALID_PARAMETER;
} else {
size_t needed_buffer_size = e->item_size() * e->items_per_enumerate();
uint32_t actual_buffer_size = buffer_size;
if (buffer_size == e->items_per_enumerate()) {
actual_buffer_size = static_cast<uint32_t>(needed_buffer_size);
// Known culprits:
// Final Fight: Double Impact (saves)
XELOGW(
"Broken usage of XamEnumerate! buffer size={:X} vs actual "
"size={:X} (item size={:X}, items per enumerate={})",
buffer_size, actual_buffer_size, e->item_size(),
e->items_per_enumerate());
}
result = X_ERROR_INSUFFICIENT_BUFFER;
if (actual_buffer_size >= needed_buffer_size) {
buffer_ptr.Zero(actual_buffer_size);
result =
e->WriteItems(buffer_ptr.guest_address(), buffer_ptr.as<uint8_t*>(),
actual_buffer_size, &item_count);
}
} }
auto run = [e, buffer_ptr](uint32_t& extended_error,
uint32_t& length) -> X_RESULT {
X_RESULT result;
uint32_t item_count;
if (!buffer_ptr) {
result = X_ERROR_INVALID_PARAMETER;
item_count = 0;
} else {
result = e->WriteItems(buffer_ptr.guest_address(),
buffer_ptr.as<uint8_t*>(), &item_count);
}
extended_error = X_HRESULT_FROM_WIN32(result);
length = item_count;
return result;
};
if (items_returned) { if (items_returned) {
assert_true(!overlapped_ptr); assert_true(!overlapped_ptr);
uint32_t extended_error;
uint32_t item_count;
X_RESULT result = run(extended_error, item_count);
*items_returned = result == X_ERROR_SUCCESS ? item_count : 0; *items_returned = result == X_ERROR_SUCCESS ? item_count : 0;
return result; return result;
} else if (overlapped_ptr) { } else if (overlapped_ptr) {
assert_true(!items_returned); assert_true(!items_returned);
kernel_state()->CompleteOverlappedImmediateEx( kernel_state()->CompleteOverlappedDeferredEx(run, overlapped_ptr);
overlapped_ptr,
result == X_ERROR_SUCCESS ? X_ERROR_SUCCESS : X_ERROR_FUNCTION_FAILED,
X_HRESULT_FROM_WIN32(result),
result == X_ERROR_SUCCESS ? item_count : 0);
return X_ERROR_IO_PENDING; return X_ERROR_IO_PENDING;
} else { } else {
assert_always(); assert_always();

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -306,9 +306,11 @@ dword_result_t NetDll_WSARecvFrom(dword_t caller, dword_t socket,
//} //}
} }
return 0; // we're not going to be receiving packets any time soon
// return error so we don't wait on that - Cancerous
return -1;
} }
DECLARE_XAM_EXPORT1(NetDll_WSARecvFrom, kNetworking, kStub); DECLARE_XAM_EXPORT2(NetDll_WSARecvFrom, kNetworking, kStub, kHighFrequency);
// If the socket is a VDP socket, buffer 0 is the game data length, and buffer 1 // If the socket is a VDP socket, buffer 0 is the game data length, and buffer 1
// is the unencrypted game data. // is the unencrypted game data.

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
// This is a partial file designed to be included by other files when // This is a partial file designed to be included by other files when
// constructing various tables. // constructing various tables.
// clang-format off
XE_EXPORT(xam, 0x00000001, NetDll_WSAStartup, kFunction), XE_EXPORT(xam, 0x00000001, NetDll_WSAStartup, kFunction),
XE_EXPORT(xam, 0x00000002, NetDll_WSACleanup, kFunction), XE_EXPORT(xam, 0x00000002, NetDll_WSACleanup, kFunction),

View File

@ -277,8 +277,9 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
auto setting = user_profile->GetSetting(setting_id); auto setting = user_profile->GetSetting(setting_id);
std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING)); std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING));
out_setting->from = out_setting->from = !setting || !setting->is_set ? 0
!setting || !setting->is_set ? 0 : setting->is_title_specific() ? 2 : 1; : setting->is_title_specific() ? 2
: 1;
out_setting->user_index = static_cast<uint32_t>(user_index); out_setting->user_index = static_cast<uint32_t>(user_index);
out_setting->setting_id = setting_id; out_setting->setting_id = setting_id;
@ -547,7 +548,8 @@ DECLARE_XAM_EXPORT1(XamUserAreUsersFriends, kUserProfiles, kStub);
dword_result_t XamShowSigninUI(dword_t unk, dword_t unk_mask) { dword_result_t XamShowSigninUI(dword_t unk, dword_t unk_mask) {
// Mask values vary. Probably matching user types? Local/remote? // Mask values vary. Probably matching user types? Local/remote?
// To fix game modes that display a 4 profile signin UI (even if playing alone): // To fix game modes that display a 4 profile signin UI (even if playing
// alone):
// XN_SYS_SIGNINCHANGED // XN_SYS_SIGNINCHANGED
kernel_state()->BroadcastNotification(0x0000000A, 1); kernel_state()->BroadcastNotification(0x0000000A, 1);
// Games seem to sit and loop until we trigger this notification: // Games seem to sit and loop until we trigger this notification:
@ -606,7 +608,7 @@ class XStaticAchievementEnumerator : public XEnumerator {
} }
uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data,
uint32_t buffer_size, uint32_t* written_count) override { uint32_t* written_count) override {
size_t count = size_t count =
std::min(items_.size() - current_item_, items_per_enumerate()); std::min(items_.size() - current_item_, items_per_enumerate());
if (!count) { if (!count) {
@ -614,9 +616,6 @@ class XStaticAchievementEnumerator : public XEnumerator {
} }
size_t size = count * item_size(); size_t size = count * item_size();
if (size > buffer_size) {
return X_ERROR_INSUFFICIENT_BUFFER;
}
auto details = reinterpret_cast<X_ACHIEVEMENT_DETAILS*>(buffer_data); auto details = reinterpret_cast<X_ACHIEVEMENT_DETAILS*>(buffer_data);
size_t string_offset = size_t string_offset =
@ -778,8 +777,10 @@ DECLARE_XAM_EXPORT1(XamSessionCreateHandle, kUserProfiles, kStub);
dword_result_t XamSessionRefObjByHandle(dword_t handle, lpdword_t obj_ptr) { dword_result_t XamSessionRefObjByHandle(dword_t handle, lpdword_t obj_ptr) {
assert_true(handle == 0xCAFEDEAD); assert_true(handle == 0xCAFEDEAD);
*obj_ptr = 0; // TODO(PermaNull): Implement this properly,
return X_ERROR_FUNCTION_FAILED; // For the time being returning 0xDEADF00D will prevent crashing.
*obj_ptr = 0xDEADF00D;
return X_ERROR_SUCCESS;
} }
DECLARE_XAM_EXPORT1(XamSessionRefObjByHandle, kUserProfiles, kStub); DECLARE_XAM_EXPORT1(XamSessionRefObjByHandle, kUserProfiles, kStub);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
// This is a partial file designed to be included by other files when // This is a partial file designed to be included by other files when
// constructing various tables. // constructing various tables.
// clang-format off
XE_EXPORT(xbdm, 0x00000001, DmAllocatePool, kFunction), XE_EXPORT(xbdm, 0x00000001, DmAllocatePool, kFunction),
XE_EXPORT(xbdm, 0x00000002, DmAllocatePoolWithTag, kFunction), XE_EXPORT(xbdm, 0x00000002, DmAllocatePoolWithTag, kFunction),

View File

@ -997,7 +997,7 @@ uint32_t xeRtlNtStatusToDosError(uint32_t source_status) {
if (!result) { if (!result) {
break; break;
} }
XELOGI("RtlNtStatusToDosError {:X} => {:X}", status, result); XELOGI("xeRtlNtStatusToDosError {:X} => {:X}", status, result);
return result; return result;
} }
++error_table; ++error_table;
@ -1007,7 +1007,7 @@ uint32_t xeRtlNtStatusToDosError(uint32_t source_status) {
return status & 0xFFFF; return status & 0xFFFF;
} }
XELOGE("RtlNtStatusToDosError lookup NOT IMPLEMENTED"); XELOGE("xeRtlNtStatusToDosError lookup NOT IMPLEMENTED");
return 317; // ERROR_MR_MID_NOT_FOUND return 317; // ERROR_MR_MID_NOT_FOUND
} }

View File

@ -357,11 +357,7 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle,
pointer_t<X_IO_STATUS_BLOCK> io_status_block, pointer_t<X_IO_STATUS_BLOCK> io_status_block,
lpvoid_t buffer, dword_t buffer_length, lpvoid_t buffer, dword_t buffer_length,
lpqword_t byte_offset_ptr) { lpqword_t byte_offset_ptr) {
// Async not supported yet.
assert_zero(apc_routine);
X_STATUS result = X_STATUS_SUCCESS; X_STATUS result = X_STATUS_SUCCESS;
uint32_t info = 0;
// Grab event to signal. // Grab event to signal.
bool signal_event = false; bool signal_event = false;
@ -386,30 +382,37 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle,
buffer.guest_address(), buffer_length, buffer.guest_address(), buffer_length,
byte_offset_ptr ? static_cast<uint64_t>(*byte_offset_ptr) : -1, byte_offset_ptr ? static_cast<uint64_t>(*byte_offset_ptr) : -1,
&bytes_written, apc_context); &bytes_written, apc_context);
if (XSUCCEEDED(result)) {
info = bytes_written; if (io_status_block) {
io_status_block->status = result;
io_status_block->information = static_cast<uint32_t>(bytes_written);
}
// Queue the APC callback. It must be delivered via the APC mechanism even
// though were are completing immediately.
// Low bit probably means do not queue to IO ports.
if ((uint32_t)apc_routine & ~1) {
if (apc_context) {
auto thread = XThread::GetCurrentThread();
thread->EnqueueApc(static_cast<uint32_t>(apc_routine) & ~1u,
apc_context, io_status_block, 0);
}
} }
if (!file->is_synchronous()) { if (!file->is_synchronous()) {
result = X_STATUS_PENDING; result = X_STATUS_PENDING;
} }
if (io_status_block) {
io_status_block->status = X_STATUS_SUCCESS;
io_status_block->information = info;
}
// Mark that we should signal the event now. We do this after // Mark that we should signal the event now. We do this after
// we have written the info out. // we have written the info out.
signal_event = true; signal_event = true;
} else { } else {
// X_STATUS_PENDING if not returning immediately. // X_STATUS_PENDING if not returning immediately.
result = X_STATUS_PENDING; result = X_STATUS_PENDING;
info = 0;
if (io_status_block) { if (io_status_block) {
io_status_block->status = X_STATUS_SUCCESS; io_status_block->status = X_STATUS_PENDING;
io_status_block->information = info; io_status_block->information = 0;
} }
} }
} }
@ -458,7 +461,8 @@ dword_result_t NtSetIoCompletion(dword_t handle, dword_t key_context,
port->QueueNotification(notification); port->QueueNotification(notification);
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;
} }
DECLARE_XBOXKRNL_EXPORT1(NtSetIoCompletion, kFileSystem, kImplemented); DECLARE_XBOXKRNL_EXPORT2(NtSetIoCompletion, kFileSystem, kImplemented,
kHighFrequency);
// Dequeues a packet from the completion port. // Dequeues a packet from the completion port.
dword_result_t NtRemoveIoCompletion( dword_result_t NtRemoveIoCompletion(
@ -473,7 +477,9 @@ dword_result_t NtRemoveIoCompletion(
status = X_STATUS_INVALID_HANDLE; status = X_STATUS_INVALID_HANDLE;
} }
uint64_t timeout_ticks = timeout ? static_cast<uint32_t>(*timeout) : 0u; uint64_t timeout_ticks =
timeout ? static_cast<uint32_t>(*timeout)
: static_cast<uint64_t>(std::numeric_limits<int64_t>::min());
XIOCompletion::IONotification notification; XIOCompletion::IONotification notification;
if (port->WaitForNotification(timeout_ticks, &notification)) { if (port->WaitForNotification(timeout_ticks, &notification)) {
if (key_context) { if (key_context) {
@ -493,7 +499,8 @@ dword_result_t NtRemoveIoCompletion(
return status; return status;
} }
DECLARE_XBOXKRNL_EXPORT1(NtRemoveIoCompletion, kFileSystem, kImplemented); DECLARE_XBOXKRNL_EXPORT2(NtRemoveIoCompletion, kFileSystem, kImplemented,
kHighFrequency);
dword_result_t NtQueryFullAttributesFile( dword_result_t NtQueryFullAttributesFile(
pointer_t<X_OBJECT_ATTRIBUTES> obj_attribs, pointer_t<X_OBJECT_ATTRIBUTES> obj_attribs,

View File

@ -19,6 +19,9 @@
#include "xenia/kernel/xthread.h" #include "xenia/kernel/xthread.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
DEFINE_bool(log_string_format_kernel_calls, false,
"Log kernel calls with the kHighFrequency tag.", "Logging");
namespace xe { namespace xe {
namespace kernel { namespace kernel {
namespace xboxkrnl { namespace xboxkrnl {
@ -830,7 +833,14 @@ SHIM_CALL DbgPrint_shim(PPCContext* ppc_context, KernelState* kernel_state) {
return; return;
} }
XELOGD("(DbgPrint) {}", data.str()); // trim whitespace from end of message
auto str = data.str();
str.erase(std::find_if(str.rbegin(), str.rend(),
[](uint8_t c) { return !std::isspace(c); })
.base(),
str.end());
XELOGI("(DbgPrint) {}", str);
SHIM_SET_RETURN_32(X_STATUS_SUCCESS); SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
} }
@ -841,8 +851,11 @@ SHIM_CALL _snprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
int32_t buffer_count = SHIM_GET_ARG_32(1); int32_t buffer_count = SHIM_GET_ARG_32(1);
uint32_t format_ptr = SHIM_GET_ARG_32(2); uint32_t format_ptr = SHIM_GET_ARG_32(2);
XELOGD("_snprintf({:08X}, {}, {:08X}, ...)", buffer_ptr, buffer_count, if (cvars::log_high_frequency_kernel_calls) {
format_ptr); XELOGD("_snprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr, buffer_count,
format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)));
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -877,7 +890,10 @@ SHIM_CALL sprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
uint32_t format_ptr = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(1);
XELOGD("sprintf({:08X}, {:08X}, ...)", buffer_ptr, format_ptr); if (cvars::log_high_frequency_kernel_calls) {
XELOGD("sprintf({:08X}, {:08X}({}), ...)", buffer_ptr, format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)));
}
if (buffer_ptr == 0 || format_ptr == 0) { if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -906,8 +922,12 @@ SHIM_CALL _snwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
int32_t buffer_count = SHIM_GET_ARG_32(1); int32_t buffer_count = SHIM_GET_ARG_32(1);
uint32_t format_ptr = SHIM_GET_ARG_32(2); uint32_t format_ptr = SHIM_GET_ARG_32(2);
XELOGD("_snwprintf({:08X}, {}, {:08X}, ...)", buffer_ptr, buffer_count, if (cvars::log_high_frequency_kernel_calls) {
format_ptr); XELOGD("_snwprintf({:08X}, {}, {:08X}({}), ...)", buffer_ptr, buffer_count,
format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))));
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -942,7 +962,11 @@ SHIM_CALL swprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0); uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
uint32_t format_ptr = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(1);
XELOGD("swprintf({:08X}, {:08X}, ...)", buffer_ptr, format_ptr); if (cvars::log_high_frequency_kernel_calls) {
XELOGD("swprintf({:08X}, {:08X}({}), ...)", buffer_ptr, format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))));
}
if (buffer_ptr == 0 || format_ptr == 0) { if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -972,8 +996,11 @@ SHIM_CALL _vsnprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(2); uint32_t format_ptr = SHIM_GET_ARG_32(2);
uint32_t arg_ptr = SHIM_GET_ARG_32(3); uint32_t arg_ptr = SHIM_GET_ARG_32(3);
XELOGD("_vsnprintf({:08X}, {}, {:08X}, {:08X})", buffer_ptr, buffer_count, if (cvars::log_high_frequency_kernel_calls) {
format_ptr, arg_ptr); XELOGD("_vsnprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr,
buffer_count, format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)), arg_ptr);
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -1012,8 +1039,13 @@ SHIM_CALL _vsnwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(2); uint32_t format_ptr = SHIM_GET_ARG_32(2);
uint32_t arg_ptr = SHIM_GET_ARG_32(3); uint32_t arg_ptr = SHIM_GET_ARG_32(3);
XELOGD("_vsnwprintf({:08X}, {}, {:08X}, {:08X})", buffer_ptr, buffer_count, if (cvars::log_high_frequency_kernel_calls) {
format_ptr, arg_ptr); XELOGD("_vsnwprintf({:08X}, {}, {:08X}({}), {:08X})", buffer_ptr,
buffer_count, format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
}
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) { if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -1051,7 +1083,10 @@ SHIM_CALL vsprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(1);
uint32_t arg_ptr = SHIM_GET_ARG_32(2); uint32_t arg_ptr = SHIM_GET_ARG_32(2);
XELOGD("vsprintf({:08X}, {:08X}, {:08X})", buffer_ptr, format_ptr, arg_ptr); if (cvars::log_high_frequency_kernel_calls) {
XELOGD("vsprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr, format_ptr,
xe::load_and_swap<std::string>(SHIM_MEM_ADDR(format_ptr)), arg_ptr);
}
if (buffer_ptr == 0 || format_ptr == 0) { if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -1079,7 +1114,12 @@ SHIM_CALL _vscwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(0); uint32_t format_ptr = SHIM_GET_ARG_32(0);
uint32_t arg_ptr = SHIM_GET_ARG_32(1); uint32_t arg_ptr = SHIM_GET_ARG_32(1);
XELOGD("_vscwprintf({:08X}, {:08X})", format_ptr, arg_ptr); if (cvars::log_high_frequency_kernel_calls) {
XELOGD("_vscwprintf({:08X}({}), {:08X})", format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
}
if (format_ptr == 0) { if (format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);
@ -1102,7 +1142,12 @@ SHIM_CALL vswprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t format_ptr = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(1);
uint32_t arg_ptr = SHIM_GET_ARG_32(2); uint32_t arg_ptr = SHIM_GET_ARG_32(2);
XELOGD("vswprintf({:08X}, {:08X}, {:08X})", buffer_ptr, format_ptr, arg_ptr); if (cvars::log_high_frequency_kernel_calls) {
XELOGD("vswprintf({:08X}, {:08X}({}), {:08X})", buffer_ptr, format_ptr,
xe::to_utf8(
xe::load_and_swap<std::u16string>(SHIM_MEM_ADDR(format_ptr))),
arg_ptr);
}
if (buffer_ptr == 0 || format_ptr == 0) { if (buffer_ptr == 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1); SHIM_SET_RETURN_32(-1);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
// This is a partial file designed to be included by other files when // This is a partial file designed to be included by other files when
// constructing various tables. // constructing various tables.
// clang-format off
XE_EXPORT(xboxkrnl, 0x00000001, DbgBreakPoint, kFunction), XE_EXPORT(xboxkrnl, 0x00000001, DbgBreakPoint, kFunction),
XE_EXPORT(xboxkrnl, 0x00000002, DbgBreakPointWithStatus, kFunction), XE_EXPORT(xboxkrnl, 0x00000002, DbgBreakPointWithStatus, kFunction),

View File

@ -21,7 +21,7 @@ XEnumerator::XEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
XEnumerator::~XEnumerator() = default; XEnumerator::~XEnumerator() = default;
X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id, X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id,
uint32_t message, uint32_t message2, uint32_t open_message, uint32_t close_message,
uint32_t flags, uint32_t extra_size, uint32_t flags, uint32_t extra_size,
void** extra_buffer) { void** extra_buffer) {
auto native_object = CreateNative(sizeof(X_KENUMERATOR) + extra_size); auto native_object = CreateNative(sizeof(X_KENUMERATOR) + extra_size);
@ -30,8 +30,8 @@ X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id,
} }
auto guest_object = reinterpret_cast<X_KENUMERATOR*>(native_object); auto guest_object = reinterpret_cast<X_KENUMERATOR*>(native_object);
guest_object->app_id = app_id; guest_object->app_id = app_id;
guest_object->message = message; guest_object->open_message = open_message;
guest_object->message2 = message2; guest_object->close_message = close_message;
guest_object->user_index = user_index; guest_object->user_index = user_index;
guest_object->items_per_enumerate = guest_object->items_per_enumerate =
static_cast<uint32_t>(items_per_enumerate_); static_cast<uint32_t>(items_per_enumerate_);
@ -44,32 +44,28 @@ X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id,
} }
X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id, X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id,
uint32_t message, uint32_t message2, uint32_t open_message, uint32_t close_message,
uint32_t flags) { uint32_t flags) {
return Initialize(user_index, app_id, message, message2, flags, 0, nullptr); return Initialize(user_index, app_id, open_message, close_message, flags, 0,
nullptr);
} }
uint8_t* XStaticEnumerator::AppendItem() { uint8_t* XStaticUntypedEnumerator::AppendItem() {
buffer_.resize(++item_count_ * item_size()); size_t offset = buffer_.size();
auto ptr = buffer_.resize(offset + item_size());
const_cast<uint8_t*>(buffer_.data() + (item_count_ - 1) * item_size()); item_count_++;
return ptr; return const_cast<uint8_t*>(&buffer_.data()[offset]);
} }
uint32_t XStaticEnumerator::WriteItems(uint32_t buffer_ptr, uint32_t XStaticUntypedEnumerator::WriteItems(uint32_t buffer_ptr,
uint8_t* buffer_data, uint8_t* buffer_data,
uint32_t buffer_size, uint32_t* written_count) {
uint32_t* written_count) {
size_t count = std::min(item_count_ - current_item_, items_per_enumerate()); size_t count = std::min(item_count_ - current_item_, items_per_enumerate());
if (!count) { if (!count) {
return X_ERROR_NO_MORE_FILES; return X_ERROR_NO_MORE_FILES;
} }
size_t size = count * item_size(); size_t size = count * item_size();
if (size > buffer_size) {
return X_ERROR_INSUFFICIENT_BUFFER;
}
size_t offset = current_item_ * item_size(); size_t offset = current_item_ * item_size();
std::memcpy(buffer_data, buffer_.data() + offset, size); std::memcpy(buffer_data, buffer_.data() + offset, size);

View File

@ -22,8 +22,8 @@ namespace kernel {
struct X_KENUMERATOR { struct X_KENUMERATOR {
be<uint32_t> app_id; be<uint32_t> app_id;
be<uint32_t> message; be<uint32_t> open_message;
be<uint32_t> message2; be<uint32_t> close_message;
be<uint32_t> user_index; be<uint32_t> user_index;
be<uint32_t> items_per_enumerate; be<uint32_t> items_per_enumerate;
be<uint32_t> flags; be<uint32_t> flags;
@ -43,19 +43,21 @@ class XEnumerator : public XObject {
size_t item_size); size_t item_size);
virtual ~XEnumerator(); virtual ~XEnumerator();
X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message, X_STATUS Initialize(uint32_t user_index, uint32_t app_id,
uint32_t message2, uint32_t flags, uint32_t extra_size, uint32_t open_message, uint32_t close_message,
void** extra_buffer); uint32_t flags, uint32_t extra_size, void** extra_buffer);
X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message, X_STATUS Initialize(uint32_t user_index, uint32_t app_id,
uint32_t message2, uint32_t flags); uint32_t open_message, uint32_t close_message,
uint32_t flags);
template <typename T> template <typename T>
X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message, X_STATUS Initialize(uint32_t user_index, uint32_t app_id,
uint32_t message2, uint32_t flags, T** extra) { uint32_t open_message, uint32_t close_message,
uint32_t flags, T** extra) {
void* dummy; void* dummy;
auto result = Initialize(user_index, app_id, message, message2, flags, auto result = Initialize(user_index, app_id, open_message, close_message,
static_cast<uint32_t>(sizeof(T)), &dummy); flags, static_cast<uint32_t>(sizeof(T)), &dummy);
if (extra) { if (extra) {
*extra = XFAILED(result) ? nullptr : static_cast<T*>(dummy); *extra = XFAILED(result) ? nullptr : static_cast<T*>(dummy);
} }
@ -63,7 +65,6 @@ class XEnumerator : public XObject {
} }
virtual uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, virtual uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data,
uint32_t buffer_size,
uint32_t* written_count) = 0; uint32_t* written_count) = 0;
size_t item_size() const { return item_size_; } size_t item_size() const { return item_size_; }
@ -74,10 +75,10 @@ class XEnumerator : public XObject {
size_t item_size_; size_t item_size_;
}; };
class XStaticEnumerator : public XEnumerator { class XStaticUntypedEnumerator : public XEnumerator {
public: public:
XStaticEnumerator(KernelState* kernel_state, size_t items_per_enumerate, XStaticUntypedEnumerator(KernelState* kernel_state,
size_t item_size) size_t items_per_enumerate, size_t item_size)
: XEnumerator(kernel_state, items_per_enumerate, item_size), : XEnumerator(kernel_state, items_per_enumerate, item_size),
item_count_(0), item_count_(0),
current_item_(0) {} current_item_(0) {}
@ -87,7 +88,7 @@ class XStaticEnumerator : public XEnumerator {
uint8_t* AppendItem(); uint8_t* AppendItem();
uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data,
uint32_t buffer_size, uint32_t* written_count) override; uint32_t* written_count) override;
private: private:
size_t item_count_; size_t item_count_;
@ -95,6 +96,23 @@ class XStaticEnumerator : public XEnumerator {
std::vector<uint8_t> buffer_; std::vector<uint8_t> buffer_;
}; };
template <typename T>
class XStaticEnumerator : public XStaticUntypedEnumerator {
public:
XStaticEnumerator(KernelState* kernel_state, size_t items_per_enumerate)
: XStaticUntypedEnumerator(kernel_state, items_per_enumerate, sizeof(T)) {
}
T* AppendItem() {
return reinterpret_cast<T*>(XStaticUntypedEnumerator::AppendItem());
}
void AppendItem(const T& item) {
auto ptr = AppendItem();
item.Write(ptr);
}
};
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe

View File

@ -30,7 +30,7 @@ bool XIOCompletion::WaitForNotification(uint64_t wait_ticks,
IONotification* notify) { IONotification* notify) {
auto ms = std::chrono::milliseconds(TimeoutTicksToMs(wait_ticks)); auto ms = std::chrono::milliseconds(TimeoutTicksToMs(wait_ticks));
auto res = threading::Wait(notification_semaphore_.get(), false, ms); auto res = threading::Wait(notification_semaphore_.get(), false, ms);
if (res == threading::WaitResult::kSuccess || !wait_ticks) { if (res == threading::WaitResult::kSuccess) {
std::unique_lock<std::mutex> lock(notification_lock_); std::unique_lock<std::mutex> lock(notification_lock_);
assert_false(notifications_.empty()); assert_false(notifications_.empty());

View File

@ -348,6 +348,12 @@ bool operator!=(std::nullptr_t _Left, const object_ref<_Ty>& _Right) noexcept {
return (!(_Left == _Right)); return (!(_Left == _Right));
} }
template <class T, class... Args>
std::enable_if_t<!std::is_array<T>::value, object_ref<T>> make_object(
Args&&... args) {
return object_ref<T>(new T(std::forward<Args>(args)...));
}
template <typename T> template <typename T>
object_ref<T> retain_object(T* ptr) { object_ref<T> retain_object(T* ptr) {
if (ptr) ptr->Retain(); if (ptr) ptr->Retain();

View File

@ -12,6 +12,7 @@
#include "third_party/imgui/imgui.h" #include "third_party/imgui/imgui.h"
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
namespace xe { namespace xe {
@ -107,23 +108,23 @@ void ImGuiDrawer::Initialize() {
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 1.00f, 0.00f, 0.21f); style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 1.00f, 0.00f, 0.21f);
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
io.KeyMap[ImGuiKey_Tab] = 0x09; // VK_TAB; io.KeyMap[ImGuiKey_Tab] = int(ui::VirtualKey::kTab);
io.KeyMap[ImGuiKey_LeftArrow] = 0x25; io.KeyMap[ImGuiKey_LeftArrow] = int(ui::VirtualKey::kLeft);
io.KeyMap[ImGuiKey_RightArrow] = 0x27; io.KeyMap[ImGuiKey_RightArrow] = int(ui::VirtualKey::kRight);
io.KeyMap[ImGuiKey_UpArrow] = 0x26; io.KeyMap[ImGuiKey_UpArrow] = int(ui::VirtualKey::kUp);
io.KeyMap[ImGuiKey_DownArrow] = 0x28; io.KeyMap[ImGuiKey_DownArrow] = int(ui::VirtualKey::kDown);
io.KeyMap[ImGuiKey_Home] = 0x24; io.KeyMap[ImGuiKey_Home] = int(ui::VirtualKey::kHome);
io.KeyMap[ImGuiKey_End] = 0x23; io.KeyMap[ImGuiKey_End] = int(ui::VirtualKey::kEnd);
io.KeyMap[ImGuiKey_Delete] = 0x2E; io.KeyMap[ImGuiKey_Delete] = int(ui::VirtualKey::kDelete);
io.KeyMap[ImGuiKey_Backspace] = 0x08; io.KeyMap[ImGuiKey_Backspace] = int(ui::VirtualKey::kBack);
io.KeyMap[ImGuiKey_Enter] = 0x0D; io.KeyMap[ImGuiKey_Enter] = int(ui::VirtualKey::kReturn);
io.KeyMap[ImGuiKey_Escape] = 0x1B; io.KeyMap[ImGuiKey_Escape] = int(ui::VirtualKey::kEscape);
io.KeyMap[ImGuiKey_A] = 'A'; io.KeyMap[ImGuiKey_A] = int(ui::VirtualKey::kA);
io.KeyMap[ImGuiKey_C] = 'C'; io.KeyMap[ImGuiKey_C] = int(ui::VirtualKey::kC);
io.KeyMap[ImGuiKey_V] = 'V'; io.KeyMap[ImGuiKey_V] = int(ui::VirtualKey::kV);
io.KeyMap[ImGuiKey_X] = 'X'; io.KeyMap[ImGuiKey_X] = int(ui::VirtualKey::kX);
io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Y] = int(ui::VirtualKey::kY);
io.KeyMap[ImGuiKey_Z] = 'Z'; io.KeyMap[ImGuiKey_Z] = int(ui::VirtualKey::kZ);
} }
void ImGuiDrawer::SetupFont() { void ImGuiDrawer::SetupFont() {
@ -228,36 +229,16 @@ void ImGuiDrawer::RenderDrawLists() {
} }
} }
void ImGuiDrawer::OnKeyDown(KeyEvent* e) { void ImGuiDrawer::OnKeyDown(KeyEvent* e) { OnKey(e, true); }
auto& io = GetIO();
io.KeysDown[e->key_code()] = true;
switch (e->key_code()) {
case 16: {
io.KeyShift = true;
} break;
case 17: {
io.KeyCtrl = true;
} break;
}
}
void ImGuiDrawer::OnKeyUp(KeyEvent* e) { void ImGuiDrawer::OnKeyUp(KeyEvent* e) { OnKey(e, false); }
auto& io = GetIO();
io.KeysDown[e->key_code()] = false;
switch (e->key_code()) {
case 16: {
io.KeyShift = false;
} break;
case 17: {
io.KeyCtrl = false;
} break;
}
}
void ImGuiDrawer::OnKeyChar(KeyEvent* e) { void ImGuiDrawer::OnKeyChar(KeyEvent* e) {
auto& io = GetIO(); auto& io = GetIO();
if (e->key_code() > 0 && e->key_code() < 0x10000) { // TODO(Triang3l): Accept the Unicode character.
io.AddInputCharacter(e->key_code()); unsigned int character = static_cast<unsigned int>(e->virtual_key());
if (character > 0 && character < 0x10000) {
io.AddInputCharacter(character);
e->set_handled(true); e->set_handled(true);
} }
} }
@ -327,5 +308,30 @@ void ImGuiDrawer::OnMouseWheel(MouseEvent* e) {
io.MouseWheel += float(e->dy() / 120.0f); io.MouseWheel += float(e->dy() / 120.0f);
} }
void ImGuiDrawer::OnKey(KeyEvent* e, bool is_down) {
auto& io = GetIO();
VirtualKey virtual_key = e->virtual_key();
if (size_t(virtual_key) < xe::countof(io.KeysDown)) {
io.KeysDown[size_t(virtual_key)] = is_down;
}
switch (virtual_key) {
case VirtualKey::kShift:
io.KeyShift = is_down;
break;
case VirtualKey::kControl:
io.KeyCtrl = is_down;
break;
case VirtualKey::kMenu:
// FIXME(Triang3l): Doesn't work in xenia-ui-window-demo.
io.KeyAlt = is_down;
break;
case VirtualKey::kLWin:
io.KeySuper = is_down;
break;
default:
break;
}
}
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe

View File

@ -57,6 +57,9 @@ class ImGuiDrawer : public WindowListener {
ImGuiContext* internal_state_ = nullptr; ImGuiContext* internal_state_ = nullptr;
std::unique_ptr<ImmediateTexture> font_texture_; std::unique_ptr<ImmediateTexture> font_texture_;
private:
void OnKey(KeyEvent* e, bool is_down);
}; };
} // namespace ui } // namespace ui

View File

@ -12,6 +12,8 @@
#include <filesystem> #include <filesystem>
#include "xenia/ui/virtual_key.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
@ -42,11 +44,12 @@ class FileDropEvent : public UIEvent {
class KeyEvent : public UIEvent { class KeyEvent : public UIEvent {
public: public:
KeyEvent(Window* target, int key_code, int repeat_count, bool prev_state, KeyEvent(Window* target, VirtualKey virtual_key, int repeat_count,
bool modifier_shift_pressed, bool modifier_ctrl_pressed, bool prev_state, bool modifier_shift_pressed,
bool modifier_alt_pressed, bool modifier_super_pressed) bool modifier_ctrl_pressed, bool modifier_alt_pressed,
bool modifier_super_pressed)
: UIEvent(target), : UIEvent(target),
key_code_(key_code), virtual_key_(virtual_key),
repeat_count_(repeat_count), repeat_count_(repeat_count),
prev_state_(prev_state), prev_state_(prev_state),
modifier_shift_pressed_(modifier_shift_pressed), modifier_shift_pressed_(modifier_shift_pressed),
@ -58,7 +61,7 @@ class KeyEvent : public UIEvent {
bool is_handled() const { return handled_; } bool is_handled() const { return handled_; }
void set_handled(bool value) { handled_ = value; } void set_handled(bool value) { handled_ = value; }
int key_code() const { return key_code_; } VirtualKey virtual_key() const { return virtual_key_; }
int repeat_count() const { return repeat_count_; } int repeat_count() const { return repeat_count_; }
bool prev_state() const { return prev_state_; } bool prev_state() const { return prev_state_; }
@ -70,7 +73,7 @@ class KeyEvent : public UIEvent {
private: private:
bool handled_ = false; bool handled_ = false;
int key_code_ = 0; VirtualKey virtual_key_ = VirtualKey::kNone;
int repeat_count_ = 0; int repeat_count_ = 0;
bool prev_state_ = false; // Key previously down(true) or up(false) bool prev_state_ = false; // Key previously down(true) or up(false)

362
src/xenia/ui/virtual_key.h Normal file
View File

@ -0,0 +1,362 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VIRTUAL_KEY_H_
#define XENIA_UI_VIRTUAL_KEY_H_
#include <cstdint>
namespace xe {
namespace ui {
// Windows and Xbox 360 / XInput virtual key enumeration.
// This is what platform-specific keys should be translated to, for both HID
// keystroke emulation and Xenia-internal UI events. On Windows, the translation
// is a simple cast.
// This is uint16_t as it's WPARAM (which was 16-bit back in Win16 days, where
// virtual key codes were added), and XINPUT_KEYSTROKE stores the virtual key as
// a WORD. In some cases (see kPacket), bits above 16 may be used as well, but
// VK_ on Windows are defined up to 0xFF (0xFE not counting the reserved 0xFF)
// as of Windows SDK 10.0.19041.0, and XInput virtual key codes are 16-bit.
// Base virtual key codes as of _WIN32_WINNT 0x0500 (Windows 2000, which the
// Xbox 360's kernel is based on), virtual key codes added later are marked
// explicitly as such.
enum class VirtualKey : uint16_t {
// Not a valid key - MapVirtualKey returns zero when there is no translation.
kNone,
kLButton = 0x01,
kRButton = 0x02,
kCancel = 0x03, // Control-break.
kMButton = 0x04, // Not contiguous with kLButton and kRButton.
kXButton1 = 0x05, // Not contiguous with kLButton and kRButton.
kXButton2 = 0x06, // Not contiguous with kLButton and kRButton.
kBack = 0x08, // Backspace.
kTab = 0x09,
kClear = 0x0C,
kReturn = 0x0D, // Enter.
kShift = 0x10,
kControl = 0x11, // Ctrl.
kMenu = 0x12, // Alt.
kPause = 0x13,
kCapital = 0x14, // Caps Lock.
kKana = 0x15,
kHangeul = 0x15, // Old name.
kHangul = 0x15,
kImeOn = 0x16,
kJunja = 0x17,
kFinal = 0x18,
kHanja = 0x19,
kKanji = 0x19,
kImeOff = 0x1A,
kEscape = 0x1B,
kConvert = 0x1C,
kNonConvert = 0x1D,
kAccept = 0x1E,
kModeChange = 0x1F,
kSpace = 0x20,
kPrior = 0x21, // Page Up.
kNext = 0x22, // Page Down.
kEnd = 0x23,
kHome = 0x24,
kLeft = 0x25,
kUp = 0x26,
kRight = 0x27,
kDown = 0x28,
kSelect = 0x29,
kPrint = 0x2A,
kExecute = 0x2B,
kSnapshot = 0x2C,
kInsert = 0x2D,
kDelete = 0x2E,
kHelp = 0x2F,
// Same as ASCII '0' - '9'.
k0 = 0x30,
k1 = 0x31,
k2 = 0x32,
k3 = 0x33,
k4 = 0x34,
k5 = 0x35,
k6 = 0x36,
k7 = 0x37,
k8 = 0x38,
k9 = 0x39,
// Same as ASCII 'A' - 'Z'.
kA = 0x41,
kB = 0x42,
kC = 0x43,
kD = 0x44,
kE = 0x45,
kF = 0x46,
kG = 0x47,
kH = 0x48,
kI = 0x49,
kJ = 0x4A,
kK = 0x4B,
kL = 0x4C,
kM = 0x4D,
kN = 0x4E,
kO = 0x4F,
kP = 0x50,
kQ = 0x51,
kR = 0x52,
kS = 0x53,
kT = 0x54,
kU = 0x55,
kV = 0x56,
kW = 0x57,
kX = 0x58,
kY = 0x59,
kZ = 0x5A,
kLWin = 0x5B,
kRWin = 0x5C,
kApps = 0x5D,
kSleep = 0x5F,
kNumpad0 = 0x60,
kNumpad1 = 0x61,
kNumpad2 = 0x62,
kNumpad3 = 0x63,
kNumpad4 = 0x64,
kNumpad5 = 0x65,
kNumpad6 = 0x66,
kNumpad7 = 0x67,
kNumpad8 = 0x68,
kNumpad9 = 0x69,
kMultiply = 0x6A,
kAdd = 0x6B,
kSeparator = 0x6C,
kSubtract = 0x6D,
kDecimal = 0x6E,
kDivide = 0x6F,
kF1 = 0x70,
kF2 = 0x71,
kF3 = 0x72,
kF4 = 0x73,
kF5 = 0x74,
kF6 = 0x75,
kF7 = 0x76,
kF8 = 0x77,
kF9 = 0x78,
kF10 = 0x79,
kF11 = 0x7A,
kF12 = 0x7B,
kF13 = 0x7C,
kF14 = 0x7D,
kF15 = 0x7E,
kF16 = 0x7F,
kF17 = 0x80,
kF18 = 0x81,
kF19 = 0x82,
kF20 = 0x83,
kF21 = 0x84,
kF22 = 0x85,
kF23 = 0x86,
kF24 = 0x87,
// VK_NAVIGATION_* added in _WIN32_WINNT 0x0604, but marked as reserved in
// WinUser.h and not documented on MSDN.
kNavigationView = 0x88,
kNavigationMenu = 0x89,
kNavigationUp = 0x8A,
kNavigationDown = 0x8B,
kNavigationLeft = 0x8C,
kNavigationRight = 0x8D,
kNavigationAccept = 0x8E,
kNavigationCancel = 0x8F,
kNumLock = 0x90,
kScroll = 0x91,
// NEC PC-9800 keyboard.
kOemNecEqual = 0x92, // '=' key on the numpad.
// Fujitsu/OASYS keyboard.
kOemFjJisho = 0x92, // 'Dictionary' key.
kOemFjMasshou = 0x93, // 'Unregister word' key.
kOemFjTouroku = 0x94, // 'Register word' key.
kOemFjLOya = 0x95, // 'Left OYAYUBI' key.
kOemFjROya = 0x96, // 'Right OYAYUBI' key.
// Left and right Alt, Ctrl and Shift virtual keys.
// On Windows (from WinUser.h):
// "Used only as parameters to GetAsyncKeyState() and GetKeyState().
// No other API or message will distinguish left and right keys in this way."
kLShift = 0xA0,
kRShift = 0xA1,
kLControl = 0xA2,
kRControl = 0xA3,
kLMenu = 0xA4,
kRMenu = 0xA5,
kBrowserBack = 0xA6,
kBrowserForward = 0xA7,
kBrowserRefresh = 0xA8,
kBrowserStop = 0xA9,
kBrowserSearch = 0xAA,
kBrowserFavorites = 0xAB,
kBrowserHome = 0xAC,
kVolumeMute = 0xAD,
kVolumeDown = 0xAE,
kVolumeUp = 0xAF,
kMediaNextTrack = 0xB0,
kMediaPrevTrack = 0xB1,
kMediaStop = 0xB2,
kMediaPlayPause = 0xB3,
kLaunchMail = 0xB4,
kLaunchMediaSelect = 0xB5,
kLaunchApp1 = 0xB6,
kLaunchApp2 = 0xB7,
kOem1 = 0xBA, // ';:' for the US.
kOemPlus = 0xBB, // '+' for any country.
kOemComma = 0xBC, // ',' for any country.
kOemMinus = 0xBD, // '-' for any country.
kOemPeriod = 0xBE, // '.' for any country.
kOem2 = 0xBF, // '/?' for the US.
kOem3 = 0xC0, // '`~' for the US.
// VK_GAMEPAD_* (since _WIN32_WINNT 0x0604) virtual key codes are marked as
// reserved in WinUser.h and are mostly not documented on MSDN (with the
// exception of the Xbox Device Portal Remote Input REST API in the "UWP on
// Xbox One" section).
// Xenia uses VK_PAD_* (kXInputPad*) for HID emulation internally instead
// because XInput is the API used for the Xbox 360 controller.
// To avoid confusion between VK_GAMEPAD_* and VK_PAD_*, here they are
// prefixed with kXboxOne and kXInput respectively.
kXboxOneGamepadA = 0xC3,
kXboxOneGamepadB = 0xC4,
kXboxOneGamepadX = 0xC5,
kXboxOneGamepadY = 0xC6,
kXboxOneGamepadRightShoulder = 0xC7,
kXboxOneGamepadLeftShoulder = 0xC8,
kXboxOneGamepadLeftTrigger = 0xC9,
kXboxOneGamepadRightTrigger = 0xCA,
kXboxOneGamepadDpadUp = 0xCB,
kXboxOneGamepadDpadDown = 0xCC,
kXboxOneGamepadDpadLeft = 0xCD,
kXboxOneGamepadDpadRight = 0xCE,
kXboxOneGamepadMenu = 0xCF,
kXboxOneGamepadView = 0xD0,
kXboxOneGamepadLeftThumbstickButton = 0xD1,
kXboxOneGamepadRightThumbstickButton = 0xD2,
kXboxOneGamepadLeftThumbstickUp = 0xD3,
kXboxOneGamepadLeftThumbstickDown = 0xD4,
kXboxOneGamepadLeftThumbstickRight = 0xD5,
kXboxOneGamepadLeftThumbstickLeft = 0xD6,
kXboxOneGamepadRightThumbstickUp = 0xD7,
kXboxOneGamepadRightThumbstickDown = 0xD8,
kXboxOneGamepadRightThumbstickRight = 0xD9,
kXboxOneGamepadRightThumbstickLeft = 0xDA,
kOem4 = 0xDB, // '[{' for the US.
kOem5 = 0xDC, // '\|' for the US.
kOem6 = 0xDD, // ']}' for the US.
kOem7 = 0xDE, // ''"' for the US.
kOem8 = 0xDF,
kOemAx = 0xE1, // 'AX' key on the Japanese AX keyboard.
kOem102 = 0xE2, // "<>" or "\|" on the RT 102-key keyboard.
kIcoHelp = 0xE3, // Help key on the Olivetti keyboard (ICO).
kIco00 = 0xE4, // 00 key on the ICO.
kProcessKey = 0xE5,
kIcoClear = 0xE6,
// From MSDN:
// "Used to pass Unicode characters as if they were keystrokes. The VK_PACKET
// key is the low word of a 32-bit Virtual Key value used for non-keyboard
// input methods."
kPacket = 0xE7,
// Nokia/Ericsson.
kOemReset = 0xE9,
kOemJump = 0xEA,
kOemPa1 = 0xEB,
kOemPa2 = 0xEC,
kOemPa3 = 0xED,
kOemWsCtrl = 0xEE,
kOemCuSel = 0xEF,
kOemAttn = 0xF0,
kOemFinish = 0xF1,
kOemCopy = 0xF2,
kOemAuto = 0xF3,
kOemEnlW = 0xF4,
kOemBackTab = 0xF5,
kAttn = 0xF6,
kCrSel = 0xF7,
kExSel = 0xF8,
kErEof = 0xF9,
kPlay = 0xFA,
kZoom = 0xFB,
kNoName = 0xFC,
kPa1 = 0xFD,
kOemClear = 0xFE,
// VK_PAD_* from XInput.h for XInputGetKeystroke. kXInput prefix added to
// distinguish from VK_GAMEPAD_*, added much later for the Xbox One
// controller.
kXInputPadA = 0x5800,
kXInputPadB = 0x5801,
kXInputPadX = 0x5802,
kXInputPadY = 0x5803,
// RShoulder before LShoulder, not a typo.
kXInputPadRShoulder = 0x5804,
kXInputPadLShoulder = 0x5805,
kXInputPadLTrigger = 0x5806,
kXInputPadRTrigger = 0x5807,
kXInputPadDpadUp = 0x5810,
kXInputPadDpadDown = 0x5811,
kXInputPadDpadLeft = 0x5812,
kXInputPadDpadRight = 0x5813,
kXInputPadStart = 0x5814,
kXInputPadBack = 0x5815,
kXInputPadLThumbPress = 0x5816,
kXInputPadRThumbPress = 0x5817,
kXInputPadLThumbUp = 0x5820,
kXInputPadLThumbDown = 0x5821,
kXInputPadLThumbRight = 0x5822,
kXInputPadLThumbLeft = 0x5823,
kXInputPadLThumbUpLeft = 0x5824,
kXInputPadLThumbUpRight = 0x5825,
kXInputPadLThumbDownRight = 0x5826,
kXInputPadLThumbDownLeft = 0x5827,
kXInputPadRThumbUp = 0x5830,
kXInputPadRThumbDown = 0x5831,
kXInputPadRThumbRight = 0x5832,
kXInputPadRThumbLeft = 0x5833,
kXInputPadRThumbUpLeft = 0x5834,
kXInputPadRThumbUpRight = 0x5835,
kXInputPadRThumbDownRight = 0x5836,
kXInputPadRThumbDownLeft = 0x5837,
};
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VIRTUAL_KEY_H_

View File

@ -259,20 +259,21 @@ void Window::OnLostFocus(UIEvent* e) {
void Window::OnKeyPress(KeyEvent* e, bool is_down, bool is_char) { void Window::OnKeyPress(KeyEvent* e, bool is_down, bool is_char) {
if (!is_char) { if (!is_char) {
switch (e->key_code()) { switch (e->virtual_key()) {
case 16: case VirtualKey::kShift:
modifier_shift_pressed_ = is_down; modifier_shift_pressed_ = is_down;
break; break;
case 17: case VirtualKey::kControl:
modifier_cntrl_pressed_ = is_down; modifier_cntrl_pressed_ = is_down;
break; break;
// case xx: case VirtualKey::kMenu:
// // alt ?? modifier_alt_pressed_ = is_down;
// modifier_alt_pressed_ = is_down; break;
// break; case VirtualKey::kLWin:
case 91:
modifier_super_pressed_ = is_down; modifier_super_pressed_ = is_down;
break; break;
default:
break;
} }
} }
} }

View File

@ -18,6 +18,7 @@
#include "xenia/ui/graphics_provider.h" #include "xenia/ui/graphics_provider.h"
#include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
namespace xe { namespace xe {
@ -92,10 +93,12 @@ int window_demo_main(const std::vector<std::string>& args) {
loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); }); loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); });
window->on_key_down.AddListener([](xe::ui::KeyEvent* e) { window->on_key_down.AddListener([](xe::ui::KeyEvent* e) {
switch (e->key_code()) { switch (e->virtual_key()) {
case 0x72: { // F3 case VirtualKey::kF3:
Profiler::ToggleDisplay(); Profiler::ToggleDisplay();
} break; break;
default:
break;
} }
}); });

View File

@ -12,6 +12,7 @@
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/platform_linux.h" #include "xenia/base/platform_linux.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window_gtk.h" #include "xenia/ui/window_gtk.h"
namespace xe { namespace xe {
@ -350,9 +351,10 @@ bool GTKWindow::HandleKeyboard(GdkEventKey* event) {
bool ctrl_pressed = modifiers & GDK_CONTROL_MASK; bool ctrl_pressed = modifiers & GDK_CONTROL_MASK;
bool alt_pressed = modifiers & GDK_META_MASK; bool alt_pressed = modifiers & GDK_META_MASK;
bool super_pressed = modifiers & GDK_SUPER_MASK; bool super_pressed = modifiers & GDK_SUPER_MASK;
auto e = // TODO(Triang3l): event->hardware_keycode to VirtualKey translation.
KeyEvent(this, event->hardware_keycode, 1, event->type == GDK_KEY_RELEASE, auto e = KeyEvent(this, VirtualKey(event->hardware_keycode), 1,
shift_pressed, ctrl_pressed, alt_pressed, super_pressed); event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed,
alt_pressed, super_pressed);
switch (event->type) { switch (event->type) {
case GDK_KEY_PRESS: case GDK_KEY_PRESS:
OnKeyDown(&e); OnKeyDown(&e);

View File

@ -17,6 +17,7 @@
#include "xenia/base/filesystem.h" #include "xenia/base/filesystem.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include "xenia/ui/virtual_key.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
@ -706,7 +707,7 @@ bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) {
bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) { bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) {
auto e = KeyEvent( auto e = KeyEvent(
this, static_cast<int>(wParam), lParam & 0xFFFF0000, !!(lParam & 0x2), this, VirtualKey(wParam), lParam & 0xFFFF0000, !!(lParam & 0x2),
!!(GetKeyState(VK_SHIFT) & 0x80), !!(GetKeyState(VK_CONTROL) & 0x80), !!(GetKeyState(VK_SHIFT) & 0x80), !!(GetKeyState(VK_CONTROL) & 0x80),
!!(GetKeyState(VK_MENU) & 0x80), !!(GetKeyState(VK_LWIN) & 0x80)); !!(GetKeyState(VK_MENU) & 0x80), !!(GetKeyState(VK_LWIN) & 0x80));
switch (message) { switch (message) {

View File

@ -25,7 +25,8 @@ void HostPathFile::Destroy() { delete this; }
X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length, X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_read) { size_t byte_offset, size_t* out_bytes_read) {
if (!(file_access_ & FileAccess::kFileReadData)) { if (!(file_access_ &
(FileAccess::kGenericRead | FileAccess::kFileReadData))) {
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
} }
@ -39,8 +40,8 @@ X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length, X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length,
size_t byte_offset, size_t byte_offset,
size_t* out_bytes_written) { size_t* out_bytes_written) {
if (!(file_access_ & if (!(file_access_ & (FileAccess::kGenericWrite | FileAccess::kFileWriteData |
(FileAccess::kFileWriteData | FileAccess::kFileAppendData))) { FileAccess::kFileAppendData))) {
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
} }
@ -53,7 +54,8 @@ X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length,
} }
X_STATUS HostPathFile::SetLength(size_t length) { X_STATUS HostPathFile::SetLength(size_t length) {
if (!(file_access_ & FileAccess::kFileWriteData)) { if (!(file_access_ &
(FileAccess::kGenericWrite | FileAccess::kFileWriteData))) {
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
} }

View File

@ -117,7 +117,11 @@ Entry* VirtualFileSystem::ResolvePath(const std::string_view path) {
return xe::utf8::starts_with(normalized_path, d->mount_path()); return xe::utf8::starts_with(normalized_path, d->mount_path());
}); });
if (it == devices_.cend()) { if (it == devices_.cend()) {
XELOGE("ResolvePath({}) failed - device not found", path); // Supress logging the error for ShaderDumpxe:\CompareBackEnds as this is
// not an actual problem nor something we care about.
if (path != "ShaderDumpxe:\\CompareBackEnds") {
XELOGE("ResolvePath({}) failed - device not found", path);
}
return nullptr; return nullptr;
} }

View File

@ -1321,7 +1321,7 @@ def find_xenia_source_files():
return [os.path.join(root, name) return [os.path.join(root, name)
for root, dirs, files in os.walk('src') for root, dirs, files in os.walk('src')
for name in files for name in files
if name.endswith(('.cc', '.c', '.h', '.inl'))] if name.endswith(('.cc', '.c', '.h', '.inl', '.inc'))]
def find_all_source_files(): def find_all_source_files():