mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 18:50:14 +00:00
resolve conflicts
This commit is contained in:
commit
8c6ffa17eb
@ -11,12 +11,12 @@ fi
|
||||
|
||||
#if OS is linux or is not set
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
|
||||
export CC=gcc-5
|
||||
export CXX=g++-5
|
||||
export CC=gcc-6
|
||||
export CXX=g++-6
|
||||
export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
|
||||
mkdir build && cd build
|
||||
cmake -DCITRA_FORCE_QT4=ON ..
|
||||
cmake ..
|
||||
make -j4
|
||||
|
||||
ctest -VV -C Release
|
||||
|
@ -5,11 +5,11 @@ set -x
|
||||
|
||||
#if OS is linux or is not set
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
|
||||
export CC=gcc-5
|
||||
export CXX=g++-5
|
||||
export CC=gcc-6
|
||||
export CXX=g++-6
|
||||
mkdir -p $HOME/.local
|
||||
|
||||
curl -L http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-i386.tar.gz \
|
||||
curl -L http://www.cmake.org/files/v3.2/cmake-3.2.0-Linux-i386.tar.gz \
|
||||
| tar -xz -C $HOME/.local --strip-components=1
|
||||
|
||||
(
|
||||
@ -21,6 +21,6 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
brew update > /dev/null # silence the very verbose output
|
||||
brew unlink cmake
|
||||
brew install cmake31 qt5 sdl2 dylibbundler
|
||||
brew install cmake qt5 sdl2 dylibbundler
|
||||
gem install xcpretty
|
||||
fi
|
||||
|
@ -23,8 +23,103 @@ if [ "$TRAVIS_BRANCH" = "master" ]; then
|
||||
|
||||
# move SDL2 libs into folder for deployment
|
||||
dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
|
||||
|
||||
# Make the changes to make the citra-qt app standalone (i.e. not dependent on the current brew installation).
|
||||
# To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks
|
||||
# (in the Contents/Frameworks folder).
|
||||
# The "install_name_tool" is used to do so.
|
||||
|
||||
# Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e:
|
||||
# ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1
|
||||
# grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1
|
||||
brew install coreutils
|
||||
|
||||
REV_NAME_ALT=$REV_NAME/
|
||||
# grealpath is located in coreutils, there is no "realpath" for OS X :(
|
||||
QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)")
|
||||
BREW_PATH=$(brew --prefix)
|
||||
QT_VERSION_NUM=5
|
||||
|
||||
$BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}citra-qt.app" \
|
||||
-executable="${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt"
|
||||
|
||||
# These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them.
|
||||
declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport")
|
||||
|
||||
for macos_lib in "${macos_libs[@]}"
|
||||
do
|
||||
SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib
|
||||
# Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/)
|
||||
cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
|
||||
|
||||
# Replace references within the embedded Framework files with "internal" versions.
|
||||
for macos_lib2 in "${macos_libs[@]}"
|
||||
do
|
||||
# Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated.
|
||||
# /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files.
|
||||
# So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't.
|
||||
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
|
||||
install_name_tool -change \
|
||||
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
|
||||
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
|
||||
"${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
|
||||
install_name_tool -change \
|
||||
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
|
||||
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
|
||||
"${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
|
||||
done
|
||||
done
|
||||
|
||||
# Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"`
|
||||
# Which manifests itself as:
|
||||
# "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY"
|
||||
# There may be more dylibs needed to be fixed...
|
||||
declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib")
|
||||
|
||||
for macos_lib in "${macos_plugins[@]}"
|
||||
do
|
||||
install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
|
||||
for macos_lib2 in "${macos_libs[@]}"
|
||||
do
|
||||
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
|
||||
install_name_tool -change \
|
||||
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
|
||||
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
|
||||
"${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
|
||||
install_name_tool -change \
|
||||
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
|
||||
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
|
||||
"${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
|
||||
done
|
||||
done
|
||||
|
||||
for macos_lib in "${macos_libs[@]}"
|
||||
do
|
||||
# Debugging info for Travis-CI
|
||||
otool -L "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib"
|
||||
done
|
||||
|
||||
# Make the citra-qt.app application launch a debugging terminal.
|
||||
# Store away the actual binary
|
||||
mv ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt-bin
|
||||
|
||||
cat > ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt <<EOL
|
||||
#!/usr/bin/env bash
|
||||
cd "\`dirname "\$0"\`"
|
||||
chmod +x citra-qt-bin
|
||||
open citra-qt-bin --args "\$@"
|
||||
EOL
|
||||
# Content that will serve as the launching script for citra (within the .app folder)
|
||||
|
||||
# Make the launching script executable
|
||||
chmod +x ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt
|
||||
|
||||
fi
|
||||
|
||||
# Copy documentation
|
||||
cp license.txt "$REV_NAME"
|
||||
cp README.md "$REV_NAME"
|
||||
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
tar -cJvf "$ARCHIVE_NAME" "$REV_NAME"
|
||||
lftp -c "open -u citra-builds,$BUILD_PASSWORD sftp://builds.citra-emu.org; set sftp:auto-confirm yes; put -O '$UPLOAD_DIR' '$ARCHIVE_NAME'"
|
||||
|
20
.travis.yml
20
.travis.yml
@ -1,22 +1,26 @@
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
sudo: true
|
||||
dist: trusty
|
||||
- os: osx
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "AXHFIafTmbGDsHD3mUVj5a4I397DQjti/WoqAJGUp2PglxTcc04BwxZ9Z+xLuf5N2Hs5r9ojAJLT8OGxJCLBDXzneQTNSqXbFuYSLbqrEAiIRlA9eRIotWCg+wYcO+5e8MKX+cHVKwiIWasUB21AtCdq6msh6Y3pUshZp212VPg="
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-5
|
||||
- g++-5
|
||||
- gcc-6
|
||||
- g++-6
|
||||
- qt5-default
|
||||
- libqt5opengl5-dev
|
||||
- xorg-dev
|
||||
- lib32stdc++6 # For CMake
|
||||
- lftp # To upload builds
|
||||
|
@ -1,6 +1,5 @@
|
||||
# CMake 3.1 required for Qt5 settings to be applied automatically on
|
||||
# dependent libraries and IMPORTED targets.
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
# CMake 3.2 required for cmake to know the right flags for CXX standard on OSX
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
function(download_bundled_external remote_path lib_name prefix_var)
|
||||
set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}")
|
||||
@ -40,7 +39,6 @@ option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF)
|
||||
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF)
|
||||
option(CITRA_FORCE_QT4 "Use Qt4 even if Qt5 is available." OFF)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
@ -64,14 +62,12 @@ if (NOT DEFINED ARCHITECTURE)
|
||||
endif()
|
||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.1")
|
||||
endif()
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
else()
|
||||
# Silence "deprecation" warnings
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS)
|
||||
@ -185,7 +181,7 @@ ENDIF (APPLE)
|
||||
if (ENABLE_QT)
|
||||
if (CITRA_USE_BUNDLED_QT)
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.5-msvc2015_64)
|
||||
set(QT_VER qt-5.7-msvc2015_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
|
||||
endif()
|
||||
@ -201,16 +197,8 @@ if (ENABLE_QT)
|
||||
set(QT_PREFIX_HINT)
|
||||
endif()
|
||||
|
||||
if (NOT CITRA_FORCE_QT4)
|
||||
find_package(Qt5 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
set(CITRA_QT_LIBS Qt5::Widgets Qt5::OpenGL)
|
||||
endif()
|
||||
|
||||
if (CITRA_FORCE_QT4 OR NOT Qt5_FOUND)
|
||||
# Try to fallback to Qt4
|
||||
find_package(Qt4 REQUIRED COMPONENTS QtGui QtOpenGL ${QT_PREFIX_HINT})
|
||||
set(CITRA_QT_LIBS Qt4::QtGui Qt4::QtOpenGL)
|
||||
endif()
|
||||
find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
set(CITRA_QT_LIBS Qt5::Widgets Qt5::OpenGL)
|
||||
endif()
|
||||
|
||||
# This function should be passed a list of all files in a target. It will automatically generate
|
||||
|
@ -15,6 +15,8 @@ For development discussion, please join us @ #citra on freenode.
|
||||
|
||||
### Development
|
||||
|
||||
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/citra-emu/citra) is hosted.
|
||||
|
||||
If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md), [TODO list](https://docs.google.com/document/d/1SWIop0uBI9IW8VGg97TAtoT_CHNoP42FzYmvG1F4QDA) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You should as well contact any of the developers in the forum in order to know about the current state of the emulator.
|
||||
|
||||
### Building
|
||||
|
16
appveyor.yml
16
appveyor.yml
@ -39,17 +39,15 @@ on_success:
|
||||
# Where are these spaces coming from? Regardless, let's remove them
|
||||
$BUILD_NAME = "citra-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ",""
|
||||
$BUILD_NAME_NOQT = "citra-noqt-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ",""
|
||||
# Zip up the build folder
|
||||
7z a $BUILD_NAME .\build\bin\release\*
|
||||
# Do a second archive with only the binaries
|
||||
7z a $BUILD_NAME_NOQT .\build\bin\release\*.exe
|
||||
# Zip up the build folder and documentation
|
||||
7z a $BUILD_NAME .\build\bin\release\* .\license.txt .\README.md
|
||||
# Do a second archive with only the binaries (excludes dlls) and documentation
|
||||
7z a $BUILD_NAME_NOQT .\build\bin\release\*.exe .\license.txt .\README.md
|
||||
|
||||
# Download winscp
|
||||
Invoke-WebRequest "http://iweb.dl.sourceforge.net/project/winscp/WinSCP/5.7.3/winscp573.zip" -OutFile "winscp573.zip"
|
||||
7z e -y winscp573.zip
|
||||
|
||||
# Upload to server
|
||||
.\WinSCP.com /command `
|
||||
# Download WinSCP and upload to server
|
||||
choco install winscp.portable
|
||||
WinSCP.exe /command `
|
||||
"option batch abort" `
|
||||
"option confirm off" `
|
||||
"open sftp://citra-builds:${env:BUILD_PASSWORD}@builds.citra-emu.org -hostkey=*" `
|
||||
|
9
externals/cmake-modules/FindSDL2.cmake
vendored
9
externals/cmake-modules/FindSDL2.cmake
vendored
@ -3,6 +3,7 @@
|
||||
# SDL2_LIBRARY, the name of the library to link against
|
||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
||||
# SDL2_DLL_DIR, where to find SDL2.dll if it exists
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
@ -149,6 +150,14 @@ FIND_LIBRARY(SDL2_LIBRARY_TEMP
|
||||
)
|
||||
|
||||
IF(SDL2_LIBRARY_TEMP)
|
||||
if(MSVC)
|
||||
get_filename_component(SDL2_DLL_DIR_TEMP ${SDL2_LIBRARY_TEMP} DIRECTORY)
|
||||
if(EXISTS ${SDL2_DLL_DIR_TEMP}/SDL2.dll)
|
||||
set(SDL2_DLL_DIR ${SDL2_DLL_DIR_TEMP})
|
||||
unset(SDL2_DLL_DIR_TEMP)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
|
@ -17,11 +17,16 @@
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/settings.h"
|
||||
#include "core/system.h"
|
||||
@ -55,6 +60,15 @@ int main(int argc, char **argv) {
|
||||
bool use_gdbstub = Settings::values.use_gdbstub;
|
||||
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
|
||||
char *endarg;
|
||||
#ifdef _WIN32
|
||||
int argc_w;
|
||||
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
|
||||
|
||||
if (argv_w == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
std::string boot_filename;
|
||||
|
||||
static struct option long_options[] = {
|
||||
@ -86,11 +100,19 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
boot_filename = Common::UTF16ToUTF8(argv_w[optind]);
|
||||
#else
|
||||
boot_filename = argv[optind];
|
||||
#endif
|
||||
optind++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
LocalFree(argv_w);
|
||||
#endif
|
||||
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
Log::SetFilter(&log_filter);
|
||||
|
||||
@ -114,7 +136,13 @@ int main(int argc, char **argv) {
|
||||
System::Init(emu_window.get());
|
||||
SCOPE_EXIT({ System::Shutdown(); });
|
||||
|
||||
Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(boot_filename);
|
||||
if (!loader) {
|
||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", boot_filename.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
Loader::ResultStatus load_result = loader->Load();
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
|
||||
return -1;
|
||||
|
@ -44,12 +44,16 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||
}
|
||||
|
||||
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
|
||||
// directly mapped keys
|
||||
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
|
||||
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
|
||||
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
|
||||
SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
|
||||
SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
|
||||
|
||||
// indirectly mapped keys
|
||||
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
|
||||
SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L
|
||||
SDL_SCANCODE_D,
|
||||
};
|
||||
|
||||
void Config::ReadValues() {
|
||||
@ -58,12 +62,13 @@ void Config::ReadValues() {
|
||||
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
|
||||
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
|
||||
}
|
||||
Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
|
||||
|
||||
// Core
|
||||
Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
|
||||
|
||||
// Renderer
|
||||
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", false);
|
||||
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
|
||||
Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
|
||||
Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);
|
||||
|
||||
|
@ -23,14 +23,19 @@ pad_l =
|
||||
pad_r =
|
||||
pad_zl =
|
||||
pad_zr =
|
||||
pad_sup =
|
||||
pad_sdown =
|
||||
pad_sleft =
|
||||
pad_sright =
|
||||
pad_cup =
|
||||
pad_cdown =
|
||||
pad_cleft =
|
||||
pad_cright =
|
||||
pad_circle_up =
|
||||
pad_circle_down =
|
||||
pad_circle_left =
|
||||
pad_circle_right =
|
||||
pad_circle_modifier =
|
||||
|
||||
# The applied modifier scale to circle pad.
|
||||
# Must be in range of 0.0-1.0. Defaults to 0.5
|
||||
pad_circle_modifier_scale =
|
||||
|
||||
[Core]
|
||||
# The applied frameskip amount. Must be a power of two.
|
||||
@ -39,11 +44,11 @@ frame_skip =
|
||||
|
||||
[Renderer]
|
||||
# Whether to use software or hardware rendering.
|
||||
# 0 (default): Software, 1: Hardware
|
||||
# 0: Software, 1 (default): Hardware
|
||||
use_hw_renderer =
|
||||
|
||||
# Whether to use the Just-In-Time (JIT) compiler for shader emulation
|
||||
# 0 : Interpreter (slow), 1 (default): JIT (fast)
|
||||
# 0: Interpreter (slow), 1 (default): JIT (fast)
|
||||
use_shader_jit =
|
||||
|
||||
# Whether to use native 3DS screen resolution or to scale rendering resolution to the displayed screen size.
|
||||
@ -66,7 +71,11 @@ output_engine =
|
||||
# 1 (default): Yes, 0: No
|
||||
use_virtual_sd =
|
||||
|
||||
[System Region]
|
||||
[System]
|
||||
# The system model that Citra will try to emulate
|
||||
# 0: Old 3DS (default), 1: New 3DS
|
||||
is_new_3ds =
|
||||
|
||||
# The system region that Citra will use during emulation
|
||||
# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
|
||||
region_value =
|
||||
|
@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
if (state == SDL_PRESSED) {
|
||||
KeyPressed({ key, keyboard_id });
|
||||
KeyMap::PressKey(*this, { key, keyboard_id });
|
||||
} else if (state == SDL_RELEASED) {
|
||||
KeyReleased({ key, keyboard_id });
|
||||
KeyMap::ReleaseKey(*this, { key, keyboard_id });
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,8 +168,9 @@ void EmuWindow_SDL2::DoneCurrent() {
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::ReloadSetKeymaps() {
|
||||
KeyMap::ClearKeyMapping(keyboard_id);
|
||||
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
|
||||
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, Service::HID::pad_mapping[i]);
|
||||
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,6 @@ set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(SRCS
|
||||
config/controller_config.cpp
|
||||
config/controller_config_util.cpp
|
||||
config.cpp
|
||||
debugger/callstack.cpp
|
||||
debugger/disassembler.cpp
|
||||
@ -11,7 +9,7 @@ set(SRCS
|
||||
debugger/graphics_breakpoint_observer.cpp
|
||||
debugger/graphics_breakpoints.cpp
|
||||
debugger/graphics_cmdlists.cpp
|
||||
debugger/graphics_framebuffer.cpp
|
||||
debugger/graphics_surface.cpp
|
||||
debugger/graphics_tracing.cpp
|
||||
debugger/graphics_vertex_shader.cpp
|
||||
debugger/profiler.cpp
|
||||
@ -24,6 +22,8 @@ set(SRCS
|
||||
configure_debug.cpp
|
||||
configure_dialog.cpp
|
||||
configure_general.cpp
|
||||
configure_system.cpp
|
||||
configure_input.cpp
|
||||
game_list.cpp
|
||||
hotkeys.cpp
|
||||
main.cpp
|
||||
@ -33,8 +33,6 @@ set(SRCS
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
config/controller_config.h
|
||||
config/controller_config_util.h
|
||||
config.h
|
||||
debugger/callstack.h
|
||||
debugger/disassembler.h
|
||||
@ -43,7 +41,7 @@ set(HEADERS
|
||||
debugger/graphics_breakpoints.h
|
||||
debugger/graphics_breakpoints_p.h
|
||||
debugger/graphics_cmdlists.h
|
||||
debugger/graphics_framebuffer.h
|
||||
debugger/graphics_surface.h
|
||||
debugger/graphics_tracing.h
|
||||
debugger/graphics_vertex_shader.h
|
||||
debugger/profiler.h
|
||||
@ -56,6 +54,8 @@ set(HEADERS
|
||||
configure_debug.h
|
||||
configure_dialog.h
|
||||
configure_general.h
|
||||
configure_system.h
|
||||
configure_input.h
|
||||
game_list.h
|
||||
game_list_p.h
|
||||
hotkeys.h
|
||||
@ -65,7 +65,6 @@ set(HEADERS
|
||||
)
|
||||
|
||||
set(UIS
|
||||
config/controller_config.ui
|
||||
debugger/callstack.ui
|
||||
debugger/disassembler.ui
|
||||
debugger/profiler.ui
|
||||
@ -74,6 +73,8 @@ set(UIS
|
||||
configure_audio.ui
|
||||
configure_debug.ui
|
||||
configure_general.ui
|
||||
configure_system.ui
|
||||
configure_input.ui
|
||||
hotkeys.ui
|
||||
main.ui
|
||||
)
|
||||
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string></string>
|
||||
<key>CFBundleIconFile</key>
|
||||
|
@ -235,12 +235,12 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||
|
||||
void GRenderWindow::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
this->KeyPressed({event->key(), keyboard_id});
|
||||
KeyMap::PressKey(*this, { event->key(), keyboard_id });
|
||||
}
|
||||
|
||||
void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
|
||||
{
|
||||
this->KeyReleased({event->key(), keyboard_id});
|
||||
KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
|
||||
}
|
||||
|
||||
void GRenderWindow::mousePressEvent(QMouseEvent *event)
|
||||
@ -270,8 +270,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
void GRenderWindow::ReloadSetKeymaps()
|
||||
{
|
||||
KeyMap::ClearKeyMapping(keyboard_id);
|
||||
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
|
||||
KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]);
|
||||
KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "citra_qt/config.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
@ -21,13 +18,17 @@ Config::Config() {
|
||||
Reload();
|
||||
}
|
||||
|
||||
static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = {
|
||||
const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
|
||||
// directly mapped keys
|
||||
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X,
|
||||
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
|
||||
Qt::Key_M, Qt::Key_N, Qt::Key_B,
|
||||
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
|
||||
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
|
||||
|
||||
// indirectly mapped keys
|
||||
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
|
||||
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L
|
||||
Qt::Key_D,
|
||||
};
|
||||
|
||||
void Config::ReadValues() {
|
||||
@ -36,6 +37,7 @@ void Config::ReadValues() {
|
||||
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
|
||||
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt();
|
||||
}
|
||||
Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
@ -43,7 +45,7 @@ void Config::ReadValues() {
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", false).toBool();
|
||||
Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool();
|
||||
Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool();
|
||||
Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool();
|
||||
|
||||
@ -104,7 +106,7 @@ void Config::ReadValues() {
|
||||
UISettings::values.shortcuts.emplace_back(
|
||||
UISettings::Shortcut(group + "/" + hotkey,
|
||||
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
|
||||
qt_config->value("Context").toInt())));
|
||||
qt_config->value("Context").toInt())));
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@ -126,6 +128,7 @@ void Config::SaveValues() {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
|
||||
Settings::values.input_mappings[Settings::NativeInput::All[i]]);
|
||||
}
|
||||
qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
@ -185,7 +188,7 @@ void Config::SaveValues() {
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Shortcuts");
|
||||
for (auto shortcut : UISettings::values.shortcuts ) {
|
||||
for (auto shortcut : UISettings::values.shortcuts) {
|
||||
qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first);
|
||||
qt_config->setValue(shortcut.first + "/Context", shortcut.second.second);
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <QVariant>
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
class QSettings;
|
||||
|
||||
@ -20,4 +23,5 @@ public:
|
||||
|
||||
void Reload();
|
||||
void Save();
|
||||
static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults;
|
||||
};
|
||||
|
@ -1,95 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "controller_config.h"
|
||||
#include "controller_config_util.h"
|
||||
|
||||
/* TODO(bunnei): ImplementMe
|
||||
|
||||
using common::Config;
|
||||
|
||||
GControllerConfig::GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
((QGridLayout*)ui.mainStickTab->layout())->addWidget(new GStickConfig(Config::ANALOG_LEFT, Config::ANALOG_RIGHT, Config::ANALOG_UP, Config::ANALOG_DOWN, this, this), 1, 1);
|
||||
((QGridLayout*)ui.cStickTab->layout())->addWidget(new GStickConfig(Config::C_LEFT, Config::C_RIGHT, Config::C_UP, Config::C_DOWN, this, this), 1, 1);
|
||||
((QGridLayout*)ui.dPadTab->layout())->addWidget(new GStickConfig(Config::DPAD_LEFT, Config::DPAD_RIGHT, Config::DPAD_UP, Config::DPAD_DOWN, this, this), 1, 1);
|
||||
|
||||
// TODO: Arrange these more compactly?
|
||||
QVBoxLayout* layout = (QVBoxLayout*)ui.buttonsTab->layout();
|
||||
layout->addWidget(new GButtonConfigGroup("A Button", Config::BUTTON_A, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("B Button", Config::BUTTON_B, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("X Button", Config::BUTTON_X, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("Y Button", Config::BUTTON_Y, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("Z Button", Config::BUTTON_Z, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("L Trigger", Config::TRIGGER_L, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("R Trigger", Config::TRIGGER_R, this, ui.buttonsTab));
|
||||
layout->addWidget(new GButtonConfigGroup("Start Button", Config::BUTTON_START, this, ui.buttonsTab));
|
||||
|
||||
memcpy(config, initial_config, sizeof(config));
|
||||
|
||||
emit ActivePortChanged(config[0]);
|
||||
}
|
||||
|
||||
void GControllerConfig::OnKeyConfigChanged(common::Config::Control id, int key, const QString& name)
|
||||
{
|
||||
if (InputSourceJoypad())
|
||||
{
|
||||
config[GetActiveController()].pads.key_code[id] = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
config[GetActiveController()].keys.key_code[id] = key;
|
||||
}
|
||||
emit ActivePortChanged(config[GetActiveController()]);
|
||||
}
|
||||
|
||||
int GControllerConfig::GetActiveController()
|
||||
{
|
||||
return ui.activeControllerCB->currentIndex();
|
||||
}
|
||||
|
||||
bool GControllerConfig::InputSourceJoypad()
|
||||
{
|
||||
return ui.inputSourceCB->currentIndex() == 1;
|
||||
}
|
||||
|
||||
GControllerConfigDialog::GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent) : QDialog(parent), config_ptr(controller_ports)
|
||||
{
|
||||
setWindowTitle(tr("Input configuration"));
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
config_widget = new GControllerConfig(controller_ports, this);
|
||||
layout->addWidget(config_widget);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
|
||||
connect(this, SIGNAL(accepted()), this, SLOT(EnableChanges()));
|
||||
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(layout);
|
||||
setModal(true);
|
||||
show();
|
||||
}
|
||||
|
||||
void GControllerConfigDialog::EnableChanges()
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
{
|
||||
memcpy(&config_ptr[i], &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
|
||||
|
||||
if (common::g_config) {
|
||||
// Apply changes if running a game
|
||||
memcpy(&common::g_config->controller_ports(i), &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
@ -1,56 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _CONTROLLER_CONFIG_HXX_
|
||||
#define _CONTROLLER_CONFIG_HXX_
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
//#include "ui_controller_config.h"
|
||||
|
||||
/* TODO(bunnei): ImplementMe
|
||||
|
||||
#include "config.h"
|
||||
|
||||
class GControllerConfig : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent = NULL);
|
||||
|
||||
const common::Config::ControllerPort& GetControllerConfig(int index) const { return config[index]; }
|
||||
|
||||
signals:
|
||||
void ActivePortChanged(const common::Config::ControllerPort&);
|
||||
|
||||
public slots:
|
||||
void OnKeyConfigChanged(common::Config::Control id, int key, const QString& name);
|
||||
|
||||
private:
|
||||
int GetActiveController();
|
||||
bool InputSourceJoypad();
|
||||
|
||||
Ui::ControllerConfig ui;
|
||||
common::Config::ControllerPort config[4];
|
||||
};
|
||||
|
||||
class GControllerConfigDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent = NULL);
|
||||
|
||||
public slots:
|
||||
void EnableChanges();
|
||||
|
||||
private:
|
||||
GControllerConfig* config_widget;
|
||||
common::Config::ControllerPort* config_ptr;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
#endif // _CONTROLLER_CONFIG_HXX_
|
@ -1,308 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ControllerConfig</class>
|
||||
<widget class="QWidget" name="ControllerConfig">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>503</width>
|
||||
<height>293</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Controller Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="activeControllerCB">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Controller 1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Controller 2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Controller 3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Controller 4</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="checkBox">
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="inputSourceCB">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Keyboard</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Joypad</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Active Controller:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Input Source:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="mainStickTab">
|
||||
<attribute name="title">
|
||||
<string>Main Stick</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="2" column="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="cStickTab">
|
||||
<attribute name="title">
|
||||
<string>C-Stick</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="dPadTab">
|
||||
<attribute name="title">
|
||||
<string>D-Pad</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="buttonsTab">
|
||||
<attribute name="title">
|
||||
<string>Buttons</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<slots>
|
||||
<signal>ControlsChanged()</signal>
|
||||
<signal>MainStickCleared()</signal>
|
||||
<signal>CStickCleared()</signal>
|
||||
<signal>DPadCleared()</signal>
|
||||
<signal>ButtonsCleared()</signal>
|
||||
<slot>OnControlsChanged()</slot>
|
||||
<slot>OnMainStickCleared()</slot>
|
||||
<slot>OnCStickCleared()</slot>
|
||||
<slot>OnDPadCleared()</slot>
|
||||
<slot>OnButtonsCleared()</slot>
|
||||
</slots>
|
||||
</ui>
|
@ -1,125 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QStyle>
|
||||
#include <QGridLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "controller_config_util.h"
|
||||
|
||||
/* TODO(bunnei): ImplementMe
|
||||
GStickConfig::GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
left = new GKeyConfigButton(leftid, style()->standardIcon(QStyle::SP_ArrowLeft), QString(), change_receiver, this);
|
||||
right = new GKeyConfigButton(rightid, style()->standardIcon(QStyle::SP_ArrowRight), QString(), change_receiver, this);
|
||||
up = new GKeyConfigButton(upid, style()->standardIcon(QStyle::SP_ArrowUp), QString(), change_receiver, this);
|
||||
down = new GKeyConfigButton(downid, style()->standardIcon(QStyle::SP_ArrowDown), QString(), change_receiver, this);
|
||||
clear = new QPushButton(tr("Clear"), this);
|
||||
|
||||
QGridLayout* layout = new QGridLayout(this);
|
||||
layout->addWidget(left, 1, 0);
|
||||
layout->addWidget(right, 1, 2);
|
||||
layout->addWidget(up, 0, 1);
|
||||
layout->addWidget(down, 2, 1);
|
||||
layout->addWidget(clear, 1, 1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(icon, text, parent), id(id), inputGrabbed(false)
|
||||
{
|
||||
connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
|
||||
connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
|
||||
connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
|
||||
}
|
||||
|
||||
GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(text, parent), id(id), inputGrabbed(false)
|
||||
{
|
||||
connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
|
||||
connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
|
||||
connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
|
||||
}
|
||||
|
||||
void GKeyConfigButton::OnActivePortChanged(const common::Config::ControllerPort& config)
|
||||
{
|
||||
// TODO: Doesn't use joypad struct if that's the input source...
|
||||
QString text = QKeySequence(config.keys.key_code[id]).toString(); // has a nicer format
|
||||
if (config.keys.key_code[id] == Qt::Key_Shift) text = tr("Shift");
|
||||
else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control");
|
||||
else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt");
|
||||
else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta");
|
||||
setText(text);
|
||||
}
|
||||
|
||||
void GKeyConfigButton::OnClicked()
|
||||
{
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
inputGrabbed = true;
|
||||
|
||||
old_text = text();
|
||||
setText(tr("Input..."));
|
||||
}
|
||||
|
||||
void GKeyConfigButton::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (inputGrabbed)
|
||||
{
|
||||
releaseKeyboard();
|
||||
releaseMouse();
|
||||
setText(QString());
|
||||
|
||||
// TODO: Doesn't capture "return" key
|
||||
// TODO: This doesn't quite work well, yet... find a better way
|
||||
QString text = QKeySequence(event->key()).toString(); // has a nicer format than event->text()
|
||||
int key = event->key();
|
||||
if (event->modifiers() == Qt::ShiftModifier) { text = tr("Shift"); key = Qt::Key_Shift; }
|
||||
else if (event->modifiers() == Qt::ControlModifier) { text = tr("Ctrl"); key = Qt::Key_Control; }
|
||||
else if (event->modifiers() == Qt::AltModifier) { text = tr("Alt"); key = Qt::Key_Alt; }
|
||||
else if (event->modifiers() == Qt::MetaModifier) { text = tr("Meta"); key = Qt::Key_Meta; }
|
||||
|
||||
setText(old_text);
|
||||
emit KeyAssigned(id, key, text);
|
||||
|
||||
inputGrabbed = false;
|
||||
|
||||
// TODO: Keys like "return" cause another keyPressEvent to be generated after this one...
|
||||
}
|
||||
|
||||
QPushButton::keyPressEvent(event); // TODO: Necessary?
|
||||
}
|
||||
|
||||
void GKeyConfigButton::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
// Abort key assignment
|
||||
if (inputGrabbed)
|
||||
{
|
||||
releaseKeyboard();
|
||||
releaseMouse();
|
||||
setText(old_text);
|
||||
inputGrabbed = false;
|
||||
}
|
||||
|
||||
QAbstractButton::mousePressEvent(event);
|
||||
}
|
||||
|
||||
GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent) : QWidget(parent), id(id)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
|
||||
QPushButton* clear_button = new QPushButton(tr("Clear"));
|
||||
|
||||
layout->addWidget(new QLabel(name, this));
|
||||
layout->addWidget(config_button = new GKeyConfigButton(id, QString(), change_receiver, this));
|
||||
layout->addWidget(clear_button);
|
||||
|
||||
// TODO: connect config_button, clear_button
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
*/
|
@ -1,82 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _CONTROLLER_CONFIG_UTIL_HXX_
|
||||
#define _CONTROLLER_CONFIG_UTIL_HXX_
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
|
||||
/* TODO(bunnei): ImplementMe
|
||||
|
||||
#include "config.h"
|
||||
|
||||
class GStickConfig : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
|
||||
GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent = NULL);
|
||||
|
||||
signals:
|
||||
void LeftChanged();
|
||||
void RightChanged();
|
||||
void UpChanged();
|
||||
void DownChanged();
|
||||
|
||||
private:
|
||||
QPushButton* left;
|
||||
QPushButton* right;
|
||||
QPushButton* up;
|
||||
QPushButton* down;
|
||||
|
||||
QPushButton* clear;
|
||||
};
|
||||
|
||||
class GKeyConfigButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// TODO: change_receiver also needs to have an ActivePortChanged(const common::Config::ControllerPort&) signal
|
||||
// change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
|
||||
GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent);
|
||||
GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent);
|
||||
|
||||
signals:
|
||||
void KeyAssigned(common::Config::Control id, int key, const QString& text);
|
||||
|
||||
private slots:
|
||||
void OnActivePortChanged(const common::Config::ControllerPort& config);
|
||||
|
||||
void OnClicked();
|
||||
|
||||
void keyPressEvent(QKeyEvent* event); // TODO: bGrabbed?
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
||||
private:
|
||||
common::Config::Control id;
|
||||
bool inputGrabbed;
|
||||
|
||||
QString old_text;
|
||||
};
|
||||
|
||||
class GButtonConfigGroup : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
|
||||
GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent = NULL);
|
||||
|
||||
private:
|
||||
GKeyConfigButton* config_button;
|
||||
|
||||
common::Config::Control id;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
#endif // _CONTROLLER_CONFIG_HXX_
|
@ -24,7 +24,12 @@
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="inputTab">
|
||||
<widget class="ConfigureSystem" name="systemTab">
|
||||
<attribute name="title">
|
||||
<string>System</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureInput" name="inputTab">
|
||||
<attribute name="title">
|
||||
<string>Input</string>
|
||||
</attribute>
|
||||
@ -57,6 +62,12 @@
|
||||
<header>configure_general.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureSystem</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configure_system.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureAudio</class>
|
||||
<extends>QWidget</extends>
|
||||
@ -69,6 +80,12 @@
|
||||
<header>configure_debug.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureInput</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configure_input.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
@ -9,9 +9,10 @@
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
ConfigureDialog::ConfigureDialog(QWidget *parent) :
|
||||
ConfigureDialog::ConfigureDialog(QWidget *parent, bool running) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ConfigureDialog)
|
||||
ui(new Ui::ConfigureDialog),
|
||||
emulation_running(running)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
@ -21,10 +22,15 @@ ConfigureDialog::~ConfigureDialog() {
|
||||
}
|
||||
|
||||
void ConfigureDialog::setConfiguration() {
|
||||
// System tab needs set manually
|
||||
// depending on whether emulation is running
|
||||
ui->systemTab->setConfiguration(emulation_running);
|
||||
}
|
||||
|
||||
void ConfigureDialog::applyConfiguration() {
|
||||
ui->generalTab->applyConfiguration();
|
||||
ui->systemTab->applyConfiguration();
|
||||
ui->inputTab->applyConfiguration();
|
||||
ui->audioTab->applyConfiguration();
|
||||
ui->debugTab->applyConfiguration();
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class ConfigureDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureDialog(QWidget *parent = nullptr);
|
||||
explicit ConfigureDialog(QWidget *parent, bool emulation_running);
|
||||
~ConfigureDialog();
|
||||
|
||||
void applyConfiguration();
|
||||
@ -26,4 +26,5 @@ private:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDialog> ui;
|
||||
bool emulation_running;
|
||||
};
|
||||
|
149
src/citra_qt/configure_input.cpp
Normal file
149
src/citra_qt/configure_input.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <QTimer>
|
||||
|
||||
#include "citra_qt/configure_input.h"
|
||||
|
||||
ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
// Initialize mapping of input enum to UI button.
|
||||
input_mapping = {
|
||||
{ std::make_pair(Settings::NativeInput::Values::A, ui->buttonA) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::B, ui->buttonB) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::X, ui->buttonX) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::L, ui->buttonL) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::R, ui->buttonR) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) },
|
||||
{ std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) },
|
||||
};
|
||||
|
||||
// Attach handle click method to each button click.
|
||||
for (const auto& entry : input_mapping) {
|
||||
connect(entry.second, SIGNAL(released()), this, SLOT(handleClick()));
|
||||
}
|
||||
connect(ui->buttonRestoreDefaults, SIGNAL(released()), this, SLOT(restoreDefaults()));
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
timer = new QTimer(this);
|
||||
timer->setSingleShot(true);
|
||||
connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); });
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
void ConfigureInput::handleClick() {
|
||||
QPushButton* sender = qobject_cast<QPushButton*>(QObject::sender());
|
||||
previous_mapping = sender->text();
|
||||
sender->setText(tr("[waiting]"));
|
||||
sender->setFocus();
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
changing_button = sender;
|
||||
timer->start(5000); //Cancel after 5 seconds
|
||||
}
|
||||
|
||||
void ConfigureInput::applyConfiguration() {
|
||||
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
|
||||
int value = getKeyValue(input_mapping[Settings::NativeInput::Values(i)]->text());
|
||||
Settings::values.input_mappings[Settings::NativeInput::All[i]] = value;
|
||||
}
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureInput::setConfiguration() {
|
||||
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
|
||||
QString keyValue = getKeyName(Settings::values.input_mappings[i]);
|
||||
input_mapping[Settings::NativeInput::Values(i)]->setText(keyValue);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
|
||||
if (!changing_button)
|
||||
return;
|
||||
if (!event || event->key() == Qt::Key_unknown)
|
||||
return;
|
||||
key_pressed = event->key();
|
||||
timer->stop();
|
||||
setKey();
|
||||
}
|
||||
|
||||
void ConfigureInput::setKey() {
|
||||
const QString key_value = getKeyName(key_pressed);
|
||||
if (key_pressed == Qt::Key_Escape)
|
||||
changing_button->setText(previous_mapping);
|
||||
else
|
||||
changing_button->setText(key_value);
|
||||
removeDuplicates(key_value);
|
||||
key_pressed = Qt::Key_unknown;
|
||||
releaseKeyboard();
|
||||
releaseMouse();
|
||||
changing_button = nullptr;
|
||||
previous_mapping = nullptr;
|
||||
}
|
||||
|
||||
QString ConfigureInput::getKeyName(int key_code) const {
|
||||
if (key_code == Qt::Key_Shift)
|
||||
return tr("Shift");
|
||||
if (key_code == Qt::Key_Control)
|
||||
return tr("Ctrl");
|
||||
if (key_code == Qt::Key_Alt)
|
||||
return tr("Alt");
|
||||
if (key_code == Qt::Key_Meta)
|
||||
return "";
|
||||
if (key_code == -1)
|
||||
return "";
|
||||
|
||||
return QKeySequence(key_code).toString();
|
||||
}
|
||||
|
||||
Qt::Key ConfigureInput::getKeyValue(const QString& text) const {
|
||||
if (text == "Shift")
|
||||
return Qt::Key_Shift;
|
||||
if (text == "Ctrl")
|
||||
return Qt::Key_Control;
|
||||
if (text == "Alt")
|
||||
return Qt::Key_Alt;
|
||||
if (text == "Meta")
|
||||
return Qt::Key_unknown;
|
||||
if (text == "")
|
||||
return Qt::Key_unknown;
|
||||
|
||||
return Qt::Key(QKeySequence(text)[0]);
|
||||
}
|
||||
|
||||
void ConfigureInput::removeDuplicates(const QString& newValue) {
|
||||
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
|
||||
if (changing_button != input_mapping[Settings::NativeInput::Values(i)]) {
|
||||
const QString oldValue = input_mapping[Settings::NativeInput::Values(i)]->text();
|
||||
if (newValue == oldValue)
|
||||
input_mapping[Settings::NativeInput::Values(i)]->setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::restoreDefaults() {
|
||||
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
|
||||
const QString keyValue = getKeyName(Config::defaults[i].toInt());
|
||||
input_mapping[Settings::NativeInput::Values(i)]->setText(keyValue);
|
||||
}
|
||||
}
|
63
src/citra_qt/configure_input.h
Normal file
63
src/citra_qt/configure_input.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "citra_qt/config.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_input.h"
|
||||
|
||||
class QPushButton;
|
||||
class QString;
|
||||
class QTimer;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureInput;
|
||||
}
|
||||
|
||||
class ConfigureInput : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureInput(QWidget* parent = nullptr);
|
||||
|
||||
/// Save all button configurations to settings file
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureInput> ui;
|
||||
std::map<Settings::NativeInput::Values, QPushButton*> input_mapping;
|
||||
int key_pressed;
|
||||
QPushButton* changing_button = nullptr; ///< button currently waiting for key press.
|
||||
QString previous_mapping;
|
||||
QTimer* timer;
|
||||
|
||||
/// Load configuration settings into button text
|
||||
void setConfiguration();
|
||||
|
||||
/// Check all inputs for duplicate keys. Clears out any other button with the same value as this button's new value.
|
||||
void removeDuplicates(const QString& newValue);
|
||||
|
||||
/// Handle key press event for input tab when a button is 'waiting'.
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
/// Convert key ASCII value to its' letter/name
|
||||
QString getKeyName(int key_code) const;
|
||||
|
||||
/// Convert letter/name of key to its ASCII value.
|
||||
Qt::Key getKeyValue(const QString& text) const;
|
||||
|
||||
/// Set button text to name of key pressed.
|
||||
void setKey();
|
||||
|
||||
private slots:
|
||||
/// Event handler for all button released() event.
|
||||
void handleClick();
|
||||
|
||||
/// Restore all buttons to their default values.
|
||||
void restoreDefaults();
|
||||
};
|
593
src/citra_qt/configure_input.ui
Normal file
593
src/citra_qt/configure_input.ui
Normal file
@ -0,0 +1,593 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureInput</class>
|
||||
<widget class="QWidget" name="ConfigureInput">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>370</width>
|
||||
<height>534</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>ConfigureInput</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="faceButtons">
|
||||
<property name="title">
|
||||
<string>Face Buttons</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>A:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonA">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>B:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonB">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>X:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonX">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Y:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonY">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="faceButtons_2">
|
||||
<property name="title">
|
||||
<string>Directional Pad</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_34">
|
||||
<property name="text">
|
||||
<string>Up:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonDpadUp">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_35">
|
||||
<property name="text">
|
||||
<string>Down:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonDpadDown">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_32">
|
||||
<property name="text">
|
||||
<string>Left:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonDpadLeft">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_33">
|
||||
<property name="text">
|
||||
<string>Right:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonDpadRight">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="faceButtons_3">
|
||||
<property name="title">
|
||||
<string>Shoulder Buttons</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>L:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonL">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="text">
|
||||
<string>R:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonR">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
<string>ZL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonZL">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>ZR:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonZR">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder></zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="faceButtons_4">
|
||||
<property name="title">
|
||||
<string>Circle Pad</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>Left:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCircleLeft">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_18">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="text">
|
||||
<string>Right:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCircleRight">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Up:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCircleUp">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_20">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="text">
|
||||
<string>Down:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCircleDown">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="faceButtons_5">
|
||||
<property name="title">
|
||||
<string>C-Stick</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_21">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="text">
|
||||
<string>Left:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCStickLeft">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_22">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_27">
|
||||
<property name="text">
|
||||
<string>Right:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCStickRight">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_23">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_28">
|
||||
<property name="text">
|
||||
<string>Up:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCStickUp">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_24">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="text">
|
||||
<string>Down:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCStickDown">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QGroupBox" name="faceButtons_6">
|
||||
<property name="title">
|
||||
<string>Misc.</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_25">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_29">
|
||||
<property name="text">
|
||||
<string>Start:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonStart">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_26">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_30">
|
||||
<property name="text">
|
||||
<string>Select:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSelect">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_27">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_31">
|
||||
<property name="text">
|
||||
<string>Home:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonHome">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_28">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_34">
|
||||
<property name="text">
|
||||
<string>Circle Mod:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCircleMod">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRestoreDefaults">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sizeIncrement">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restore Defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
136
src/citra_qt/configure_system.cpp
Normal file
136
src/citra_qt/configure_system.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "citra_qt/configure_system.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "ui_configure_system.h"
|
||||
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
|
||||
static const std::array<int, 12> days_in_month = {{
|
||||
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
}};
|
||||
|
||||
ConfigureSystem::ConfigureSystem(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::ConfigureSystem) {
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int)));
|
||||
}
|
||||
|
||||
ConfigureSystem::~ConfigureSystem() {
|
||||
}
|
||||
|
||||
void ConfigureSystem::setConfiguration(bool emulation_running) {
|
||||
enabled = !emulation_running;
|
||||
|
||||
if (!enabled) {
|
||||
ReadSystemSettings();
|
||||
ui->group_system_settings->setEnabled(false);
|
||||
} else {
|
||||
// This tab is enabled only when game is not running (i.e. all service are not initialized).
|
||||
// Temporarily register archive types and load the config savegame file to memory.
|
||||
Service::FS::RegisterArchiveTypes();
|
||||
ResultCode result = Service::CFG::LoadConfigNANDSaveFile();
|
||||
Service::FS::UnregisterArchiveTypes();
|
||||
|
||||
if (result.IsError()) {
|
||||
ui->label_disable_info->setText(tr("Failed to load system settings data."));
|
||||
ui->group_system_settings->setEnabled(false);
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ReadSystemSettings();
|
||||
ui->label_disable_info->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureSystem::ReadSystemSettings() {
|
||||
// set username
|
||||
username = Service::CFG::GetUsername();
|
||||
// ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5
|
||||
ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
|
||||
|
||||
// set birthday
|
||||
std::tie(birthmonth, birthday) = Service::CFG::GetBirthday();
|
||||
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
|
||||
ui->combo_birthday->setCurrentIndex(birthday - 1);
|
||||
|
||||
// set system language
|
||||
language_index = Service::CFG::GetSystemLanguage();
|
||||
ui->combo_language->setCurrentIndex(language_index);
|
||||
|
||||
// set sound output mode
|
||||
sound_index = Service::CFG::GetSoundOutputMode();
|
||||
ui->combo_sound->setCurrentIndex(sound_index);
|
||||
}
|
||||
|
||||
void ConfigureSystem::applyConfiguration() {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
bool modified = false;
|
||||
|
||||
// apply username
|
||||
// std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5
|
||||
std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
|
||||
if (new_username != username) {
|
||||
Service::CFG::SetUsername(new_username);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// apply birthday
|
||||
int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
|
||||
int new_birthday = ui->combo_birthday->currentIndex() + 1;
|
||||
if (birthmonth != new_birthmonth || birthday != new_birthday) {
|
||||
Service::CFG::SetBirthday(new_birthmonth, new_birthday);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// apply language
|
||||
int new_language = ui->combo_language->currentIndex();
|
||||
if (language_index != new_language) {
|
||||
Service::CFG::SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language));
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// apply sound
|
||||
int new_sound = ui->combo_sound->currentIndex();
|
||||
if (sound_index != new_sound) {
|
||||
Service::CFG::SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound));
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// update the config savegame if any item is modified.
|
||||
if (modified)
|
||||
Service::CFG::UpdateConfigNANDSavegame();
|
||||
}
|
||||
|
||||
void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
|
||||
if (birthmonth_index < 0 || birthmonth_index >= 12)
|
||||
return;
|
||||
|
||||
// store current day selection
|
||||
int birthday_index = ui->combo_birthday->currentIndex();
|
||||
|
||||
// get number of days in the new selected month
|
||||
int days = days_in_month[birthmonth_index];
|
||||
|
||||
// if the selected day is out of range,
|
||||
// reset it to 1st
|
||||
if (birthday_index < 0 || birthday_index >= days)
|
||||
birthday_index = 0;
|
||||
|
||||
// update the day combo box
|
||||
ui->combo_birthday->clear();
|
||||
for (int i = 1; i <= days; ++i) {
|
||||
ui->combo_birthday->addItem(QString::number(i));
|
||||
}
|
||||
|
||||
// restore the day selection
|
||||
ui->combo_birthday->setCurrentIndex(birthday_index);
|
||||
}
|
38
src/citra_qt/configure_system.h
Normal file
38
src/citra_qt/configure_system.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureSystem;
|
||||
}
|
||||
|
||||
class ConfigureSystem : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureSystem(QWidget *parent = nullptr);
|
||||
~ConfigureSystem();
|
||||
|
||||
void applyConfiguration();
|
||||
void setConfiguration(bool emulation_running);
|
||||
|
||||
public slots:
|
||||
void updateBirthdayComboBox(int birthmonth_index);
|
||||
|
||||
private:
|
||||
void ReadSystemSettings();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||
bool enabled;
|
||||
|
||||
std::u16string username;
|
||||
int birthmonth, birthday;
|
||||
int language_index;
|
||||
int sound_index;
|
||||
};
|
252
src/citra_qt/configure_system.ui
Normal file
252
src/citra_qt/configure_system.ui
Normal file
@ -0,0 +1,252 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureSystem</class>
|
||||
<widget class="QWidget" name="ConfigureSystem">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="group_system_settings">
|
||||
<property name="title">
|
||||
<string>System Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="edit_username">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_birthday">
|
||||
<property name="text">
|
||||
<string>Birthday</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_birthday2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthmonth">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>January</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>February</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>March</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>April</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>May</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>June</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>July</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>August</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>September</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>October</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>November</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>December</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthday"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_language">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="combo_language">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Japanese (日本語)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>English</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>French (français)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>German (Deutsch)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Italian (italiano)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spanish (español)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Simplified Chinese (简体中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Korean (한국어)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dutch (Nederlands)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Portuguese (português)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Russian (Русский)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Traditional Chinese (正體中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_sound">
|
||||
<property name="text">
|
||||
<string>Sound output mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="combo_sound">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mono</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stereo</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Surround</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_disable_info">
|
||||
<property name="text">
|
||||
<string>System settings are available only when game is not running.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -37,10 +37,13 @@ void CallstackWidget::OnDebugModeEntered()
|
||||
int counter = 0;
|
||||
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
|
||||
{
|
||||
if (!Memory::IsValidVirtualAddress(addr))
|
||||
break;
|
||||
|
||||
const u32 ret_addr = Memory::Read32(addr);
|
||||
const u32 call_addr = ret_addr - 4; //get call address???
|
||||
|
||||
if (Memory::GetPointer(call_addr) == nullptr)
|
||||
if (!Memory::IsValidVirtualAddress(call_addr))
|
||||
break;
|
||||
|
||||
/* TODO (mattvail) clean me, move to debugger interface */
|
||||
|
@ -50,123 +50,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent)
|
||||
: QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))),
|
||||
info(info) {
|
||||
|
||||
QWidget* main_widget = new QWidget;
|
||||
|
||||
QLabel* image_widget = new QLabel;
|
||||
|
||||
connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&)));
|
||||
|
||||
CSpinBox* phys_address_spinbox = new CSpinBox;
|
||||
phys_address_spinbox->SetBase(16);
|
||||
phys_address_spinbox->SetRange(0, 0xFFFFFFFF);
|
||||
phys_address_spinbox->SetPrefix("0x");
|
||||
phys_address_spinbox->SetValue(info.physical_address);
|
||||
connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64)));
|
||||
|
||||
QComboBox* format_choice = new QComboBox;
|
||||
format_choice->addItem(tr("RGBA8"));
|
||||
format_choice->addItem(tr("RGB8"));
|
||||
format_choice->addItem(tr("RGB5A1"));
|
||||
format_choice->addItem(tr("RGB565"));
|
||||
format_choice->addItem(tr("RGBA4"));
|
||||
format_choice->addItem(tr("IA8"));
|
||||
format_choice->addItem(tr("RG8"));
|
||||
format_choice->addItem(tr("I8"));
|
||||
format_choice->addItem(tr("A8"));
|
||||
format_choice->addItem(tr("IA4"));
|
||||
format_choice->addItem(tr("I4"));
|
||||
format_choice->addItem(tr("A4"));
|
||||
format_choice->addItem(tr("ETC1"));
|
||||
format_choice->addItem(tr("ETC1A4"));
|
||||
format_choice->setCurrentIndex(static_cast<int>(info.format));
|
||||
connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
|
||||
|
||||
QSpinBox* width_spinbox = new QSpinBox;
|
||||
width_spinbox->setMaximum(65535);
|
||||
width_spinbox->setValue(info.width);
|
||||
connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int)));
|
||||
|
||||
QSpinBox* height_spinbox = new QSpinBox;
|
||||
height_spinbox->setMaximum(65535);
|
||||
height_spinbox->setValue(info.height);
|
||||
connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int)));
|
||||
|
||||
QSpinBox* stride_spinbox = new QSpinBox;
|
||||
stride_spinbox->setMaximum(65535 * 4);
|
||||
stride_spinbox->setValue(info.stride);
|
||||
connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int)));
|
||||
|
||||
QVBoxLayout* main_layout = new QVBoxLayout;
|
||||
main_layout->addWidget(image_widget);
|
||||
|
||||
{
|
||||
QHBoxLayout* sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Source Address:")));
|
||||
sub_layout->addWidget(phys_address_spinbox);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
|
||||
{
|
||||
QHBoxLayout* sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Format")));
|
||||
sub_layout->addWidget(format_choice);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
|
||||
{
|
||||
QHBoxLayout* sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Width:")));
|
||||
sub_layout->addWidget(width_spinbox);
|
||||
sub_layout->addStretch();
|
||||
sub_layout->addWidget(new QLabel(tr("Height:")));
|
||||
sub_layout->addWidget(height_spinbox);
|
||||
sub_layout->addStretch();
|
||||
sub_layout->addWidget(new QLabel(tr("Stride:")));
|
||||
sub_layout->addWidget(stride_spinbox);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
|
||||
main_widget->setLayout(main_layout);
|
||||
|
||||
emit UpdatePixmap(ReloadPixmap());
|
||||
|
||||
setWidget(main_widget);
|
||||
}
|
||||
|
||||
void TextureInfoDockWidget::OnAddressChanged(qint64 value) {
|
||||
info.physical_address = value;
|
||||
emit UpdatePixmap(ReloadPixmap());
|
||||
}
|
||||
|
||||
void TextureInfoDockWidget::OnFormatChanged(int value) {
|
||||
info.format = static_cast<Pica::Regs::TextureFormat>(value);
|
||||
emit UpdatePixmap(ReloadPixmap());
|
||||
}
|
||||
|
||||
void TextureInfoDockWidget::OnWidthChanged(int value) {
|
||||
info.width = value;
|
||||
emit UpdatePixmap(ReloadPixmap());
|
||||
}
|
||||
|
||||
void TextureInfoDockWidget::OnHeightChanged(int value) {
|
||||
info.height = value;
|
||||
emit UpdatePixmap(ReloadPixmap());
|
||||
}
|
||||
|
||||
void TextureInfoDockWidget::OnStrideChanged(int value) {
|
||||
info.stride = value;
|
||||
emit UpdatePixmap(ReloadPixmap());
|
||||
}
|
||||
|
||||
QPixmap TextureInfoDockWidget::ReloadPixmap() const {
|
||||
u8* src = Memory::GetPhysicalPointer(info.physical_address);
|
||||
return QPixmap::fromImage(LoadTexture(src, info));
|
||||
}
|
||||
|
||||
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
|
||||
|
||||
}
|
||||
@ -249,16 +132,16 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
|
||||
index = 0;
|
||||
} else if (COMMAND_IN_RANGE(command_id, texture1)) {
|
||||
index = 1;
|
||||
} else {
|
||||
} else if (COMMAND_IN_RANGE(command_id, texture2)) {
|
||||
index = 2;
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unknown texture command");
|
||||
}
|
||||
auto config = Pica::g_state.regs.GetTextures()[index].config;
|
||||
auto format = Pica::g_state.regs.GetTextures()[index].format;
|
||||
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
|
||||
|
||||
// TODO: Instead, emit a signal here to be caught by the main window widget.
|
||||
auto main_window = static_cast<QMainWindow*>(parent());
|
||||
main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
|
||||
// TODO: Open a surface debugger
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,25 +61,3 @@ private:
|
||||
QWidget* command_info_widget;
|
||||
QPushButton* toggle_tracing;
|
||||
};
|
||||
|
||||
class TextureInfoDockWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void UpdatePixmap(const QPixmap& pixmap);
|
||||
|
||||
private slots:
|
||||
void OnAddressChanged(qint64 value);
|
||||
void OnFormatChanged(int value);
|
||||
void OnWidthChanged(int value);
|
||||
void OnHeightChanged(int value);
|
||||
void OnStrideChanged(int value);
|
||||
|
||||
private:
|
||||
QPixmap ReloadPixmap() const;
|
||||
|
||||
Pica::DebugUtils::TextureInfo info;
|
||||
};
|
||||
|
@ -1,356 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "citra_qt/debugger/graphics_framebuffer.h"
|
||||
#include "citra_qt/util/spinbox.h"
|
||||
|
||||
#include "common/color.h"
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "core/hw/gpu.h"
|
||||
|
||||
#include "video_core/pica.h"
|
||||
#include "video_core/pica_state.h"
|
||||
#include "video_core/utils.h"
|
||||
|
||||
GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
|
||||
QWidget* parent)
|
||||
: BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
|
||||
framebuffer_source(Source::PicaTarget)
|
||||
{
|
||||
setObjectName("PicaFramebuffer");
|
||||
|
||||
framebuffer_source_list = new QComboBox;
|
||||
framebuffer_source_list->addItem(tr("Active Render Target"));
|
||||
framebuffer_source_list->addItem(tr("Active Depth Buffer"));
|
||||
framebuffer_source_list->addItem(tr("Custom"));
|
||||
framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
|
||||
|
||||
framebuffer_address_control = new CSpinBox;
|
||||
framebuffer_address_control->SetBase(16);
|
||||
framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
|
||||
framebuffer_address_control->SetPrefix("0x");
|
||||
|
||||
framebuffer_width_control = new QSpinBox;
|
||||
framebuffer_width_control->setMinimum(1);
|
||||
framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
|
||||
|
||||
framebuffer_height_control = new QSpinBox;
|
||||
framebuffer_height_control->setMinimum(1);
|
||||
framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
|
||||
|
||||
framebuffer_format_control = new QComboBox;
|
||||
framebuffer_format_control->addItem(tr("RGBA8"));
|
||||
framebuffer_format_control->addItem(tr("RGB8"));
|
||||
framebuffer_format_control->addItem(tr("RGB5A1"));
|
||||
framebuffer_format_control->addItem(tr("RGB565"));
|
||||
framebuffer_format_control->addItem(tr("RGBA4"));
|
||||
framebuffer_format_control->addItem(tr("D16"));
|
||||
framebuffer_format_control->addItem(tr("D24"));
|
||||
framebuffer_format_control->addItem(tr("D24X8"));
|
||||
framebuffer_format_control->addItem(tr("X24S8"));
|
||||
framebuffer_format_control->addItem(tr("(unknown)"));
|
||||
|
||||
// TODO: This QLabel should shrink the image to the available space rather than just expanding...
|
||||
framebuffer_picture_label = new QLabel;
|
||||
|
||||
auto enlarge_button = new QPushButton(tr("Enlarge"));
|
||||
|
||||
// Connections
|
||||
connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
|
||||
connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
|
||||
connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
|
||||
connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
|
||||
connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
|
||||
connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
|
||||
|
||||
auto main_widget = new QWidget;
|
||||
auto main_layout = new QVBoxLayout;
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Source:")));
|
||||
sub_layout->addWidget(framebuffer_source_list);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
|
||||
sub_layout->addWidget(framebuffer_address_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Width:")));
|
||||
sub_layout->addWidget(framebuffer_width_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Height:")));
|
||||
sub_layout->addWidget(framebuffer_height_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Format:")));
|
||||
sub_layout->addWidget(framebuffer_format_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
main_layout->addWidget(framebuffer_picture_label);
|
||||
main_layout->addWidget(enlarge_button);
|
||||
main_widget->setLayout(main_layout);
|
||||
setWidget(main_widget);
|
||||
|
||||
// Load current data - TODO: Make sure this works when emulation is not running
|
||||
if (debug_context && debug_context->at_breakpoint)
|
||||
emit Update();
|
||||
widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
|
||||
{
|
||||
emit Update();
|
||||
widget()->setEnabled(true);
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnResumed()
|
||||
{
|
||||
widget()->setEnabled(false);
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
|
||||
{
|
||||
framebuffer_source = static_cast<Source>(new_value);
|
||||
emit Update();
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
|
||||
{
|
||||
if (framebuffer_address != new_value) {
|
||||
framebuffer_address = static_cast<unsigned>(new_value);
|
||||
|
||||
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
|
||||
{
|
||||
if (framebuffer_width != static_cast<unsigned>(new_value)) {
|
||||
framebuffer_width = static_cast<unsigned>(new_value);
|
||||
|
||||
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
|
||||
{
|
||||
if (framebuffer_height != static_cast<unsigned>(new_value)) {
|
||||
framebuffer_height = static_cast<unsigned>(new_value);
|
||||
|
||||
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
|
||||
{
|
||||
if (framebuffer_format != static_cast<Format>(new_value)) {
|
||||
framebuffer_format = static_cast<Format>(new_value);
|
||||
|
||||
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsFramebufferWidget::OnUpdate()
|
||||
{
|
||||
QPixmap pixmap;
|
||||
|
||||
switch (framebuffer_source) {
|
||||
case Source::PicaTarget:
|
||||
{
|
||||
// TODO: Store a reference to the registers in the debug context instead of accessing them directly...
|
||||
|
||||
const auto& framebuffer = Pica::g_state.regs.framebuffer;
|
||||
|
||||
framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
|
||||
framebuffer_width = framebuffer.GetWidth();
|
||||
framebuffer_height = framebuffer.GetHeight();
|
||||
|
||||
switch (framebuffer.color_format) {
|
||||
case Pica::Regs::ColorFormat::RGBA8:
|
||||
framebuffer_format = Format::RGBA8;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGB8:
|
||||
framebuffer_format = Format::RGB8;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGB5A1:
|
||||
framebuffer_format = Format::RGB5A1;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGB565:
|
||||
framebuffer_format = Format::RGB565;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGBA4:
|
||||
framebuffer_format = Format::RGBA4;
|
||||
break;
|
||||
|
||||
default:
|
||||
framebuffer_format = Format::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::DepthBuffer:
|
||||
{
|
||||
const auto& framebuffer = Pica::g_state.regs.framebuffer;
|
||||
|
||||
framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress();
|
||||
framebuffer_width = framebuffer.GetWidth();
|
||||
framebuffer_height = framebuffer.GetHeight();
|
||||
|
||||
switch (framebuffer.depth_format) {
|
||||
case Pica::Regs::DepthFormat::D16:
|
||||
framebuffer_format = Format::D16;
|
||||
break;
|
||||
|
||||
case Pica::Regs::DepthFormat::D24:
|
||||
framebuffer_format = Format::D24;
|
||||
break;
|
||||
|
||||
case Pica::Regs::DepthFormat::D24S8:
|
||||
framebuffer_format = Format::D24X8;
|
||||
break;
|
||||
|
||||
default:
|
||||
framebuffer_format = Format::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::Custom:
|
||||
{
|
||||
// Keep user-specified values
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Implement a good way to visualize alpha components!
|
||||
// TODO: Unify this decoding code with the texture decoder
|
||||
u32 bytes_per_pixel = GraphicsFramebufferWidget::BytesPerPixel(framebuffer_format);
|
||||
|
||||
QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
|
||||
u8* buffer = Memory::GetPhysicalPointer(framebuffer_address);
|
||||
|
||||
for (unsigned int y = 0; y < framebuffer_height; ++y) {
|
||||
for (unsigned int x = 0; x < framebuffer_width; ++x) {
|
||||
const u32 coarse_y = y & ~7;
|
||||
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer_width * bytes_per_pixel;
|
||||
const u8* pixel = buffer + offset;
|
||||
Math::Vec4<u8> color = { 0, 0, 0, 0 };
|
||||
|
||||
switch (framebuffer_format) {
|
||||
case Format::RGBA8:
|
||||
color = Color::DecodeRGBA8(pixel);
|
||||
break;
|
||||
case Format::RGB8:
|
||||
color = Color::DecodeRGB8(pixel);
|
||||
break;
|
||||
case Format::RGB5A1:
|
||||
color = Color::DecodeRGB5A1(pixel);
|
||||
break;
|
||||
case Format::RGB565:
|
||||
color = Color::DecodeRGB565(pixel);
|
||||
break;
|
||||
case Format::RGBA4:
|
||||
color = Color::DecodeRGBA4(pixel);
|
||||
break;
|
||||
case Format::D16:
|
||||
{
|
||||
u32 data = Color::DecodeD16(pixel);
|
||||
color.r() = data & 0xFF;
|
||||
color.g() = (data >> 8) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case Format::D24:
|
||||
{
|
||||
u32 data = Color::DecodeD24(pixel);
|
||||
color.r() = data & 0xFF;
|
||||
color.g() = (data >> 8) & 0xFF;
|
||||
color.b() = (data >> 16) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case Format::D24X8:
|
||||
{
|
||||
Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
|
||||
color.r() = data.x & 0xFF;
|
||||
color.g() = (data.x >> 8) & 0xFF;
|
||||
color.b() = (data.x >> 16) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case Format::X24S8:
|
||||
{
|
||||
Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
|
||||
color.r() = color.g() = color.b() = data.y;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
|
||||
break;
|
||||
}
|
||||
|
||||
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
|
||||
}
|
||||
}
|
||||
pixmap = QPixmap::fromImage(decoded_image);
|
||||
|
||||
framebuffer_address_control->SetValue(framebuffer_address);
|
||||
framebuffer_width_control->setValue(framebuffer_width);
|
||||
framebuffer_height_control->setValue(framebuffer_height);
|
||||
framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
|
||||
framebuffer_picture_label->setPixmap(pixmap);
|
||||
}
|
||||
|
||||
u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) {
|
||||
switch (format) {
|
||||
case Format::RGBA8:
|
||||
case Format::D24X8:
|
||||
case Format::X24S8:
|
||||
return 4;
|
||||
case Format::RGB8:
|
||||
case Format::D24:
|
||||
return 3;
|
||||
case Format::RGB5A1:
|
||||
case Format::RGB565:
|
||||
case Format::RGBA4:
|
||||
case Format::D16:
|
||||
return 2;
|
||||
default:
|
||||
UNREACHABLE_MSG("GraphicsFramebufferWidget::BytesPerPixel: this "
|
||||
"should not be reached as this function should "
|
||||
"be given a format which is in "
|
||||
"GraphicsFramebufferWidget::Format. Instead got %i",
|
||||
static_cast<int>(format));
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
|
||||
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QSpinBox;
|
||||
|
||||
class CSpinBox;
|
||||
|
||||
class GraphicsFramebufferWidget : public BreakPointObserverDock {
|
||||
Q_OBJECT
|
||||
|
||||
using Event = Pica::DebugContext::Event;
|
||||
|
||||
enum class Source {
|
||||
PicaTarget = 0,
|
||||
DepthBuffer = 1,
|
||||
Custom = 2,
|
||||
|
||||
// TODO: Add GPU framebuffer sources!
|
||||
};
|
||||
|
||||
enum class Format {
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
D16 = 5,
|
||||
D24 = 6,
|
||||
D24X8 = 7,
|
||||
X24S8 = 8,
|
||||
Unknown = 9
|
||||
};
|
||||
|
||||
static u32 BytesPerPixel(Format format);
|
||||
|
||||
public:
|
||||
GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void OnFramebufferSourceChanged(int new_value);
|
||||
void OnFramebufferAddressChanged(qint64 new_value);
|
||||
void OnFramebufferWidthChanged(int new_value);
|
||||
void OnFramebufferHeightChanged(int new_value);
|
||||
void OnFramebufferFormatChanged(int new_value);
|
||||
void OnUpdate();
|
||||
|
||||
private slots:
|
||||
void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
|
||||
void OnResumed() override;
|
||||
|
||||
signals:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
|
||||
QComboBox* framebuffer_source_list;
|
||||
CSpinBox* framebuffer_address_control;
|
||||
QSpinBox* framebuffer_width_control;
|
||||
QSpinBox* framebuffer_height_control;
|
||||
QComboBox* framebuffer_format_control;
|
||||
|
||||
QLabel* framebuffer_picture_label;
|
||||
|
||||
Source framebuffer_source;
|
||||
unsigned framebuffer_address;
|
||||
unsigned framebuffer_width;
|
||||
unsigned framebuffer_height;
|
||||
Format framebuffer_format;
|
||||
};
|
736
src/citra_qt/debugger/graphics_surface.cpp
Normal file
736
src/citra_qt/debugger/graphics_surface.cpp
Normal file
@ -0,0 +1,736 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "citra_qt/debugger/graphics_surface.h"
|
||||
#include "citra_qt/util/spinbox.h"
|
||||
|
||||
#include "common/color.h"
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "core/hw/gpu.h"
|
||||
|
||||
#include "video_core/pica.h"
|
||||
#include "video_core/pica_state.h"
|
||||
#include "video_core/utils.h"
|
||||
|
||||
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {}
|
||||
SurfacePicture::~SurfacePicture() {}
|
||||
|
||||
void SurfacePicture::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
// Only do something while the left mouse button is held down
|
||||
if (!(event->buttons() & Qt::LeftButton))
|
||||
return;
|
||||
|
||||
if (pixmap() == nullptr)
|
||||
return;
|
||||
|
||||
if (surface_widget)
|
||||
surface_widget->Pick(event->x() * pixmap()->width() / width(),
|
||||
event->y() * pixmap()->height() / height());
|
||||
}
|
||||
|
||||
void SurfacePicture::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
// We also want to handle the event if the user moves the mouse while holding down the LMB
|
||||
mousePressEvent(event);
|
||||
}
|
||||
|
||||
|
||||
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
|
||||
QWidget* parent)
|
||||
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
|
||||
surface_source(Source::ColorBuffer)
|
||||
{
|
||||
setObjectName("PicaSurface");
|
||||
|
||||
surface_source_list = new QComboBox;
|
||||
surface_source_list->addItem(tr("Color Buffer"));
|
||||
surface_source_list->addItem(tr("Depth Buffer"));
|
||||
surface_source_list->addItem(tr("Stencil Buffer"));
|
||||
surface_source_list->addItem(tr("Texture 0"));
|
||||
surface_source_list->addItem(tr("Texture 1"));
|
||||
surface_source_list->addItem(tr("Texture 2"));
|
||||
surface_source_list->addItem(tr("Custom"));
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
|
||||
|
||||
surface_address_control = new CSpinBox;
|
||||
surface_address_control->SetBase(16);
|
||||
surface_address_control->SetRange(0, 0xFFFFFFFF);
|
||||
surface_address_control->SetPrefix("0x");
|
||||
|
||||
unsigned max_dimension = 16384; // TODO: Find actual maximum
|
||||
|
||||
surface_width_control = new QSpinBox;
|
||||
surface_width_control->setRange(0, max_dimension);
|
||||
|
||||
surface_height_control = new QSpinBox;
|
||||
surface_height_control->setRange(0, max_dimension);
|
||||
|
||||
surface_picker_x_control = new QSpinBox;
|
||||
surface_picker_x_control->setRange(0, max_dimension - 1);
|
||||
|
||||
surface_picker_y_control = new QSpinBox;
|
||||
surface_picker_y_control->setRange(0, max_dimension - 1);
|
||||
|
||||
surface_format_control = new QComboBox;
|
||||
|
||||
// Color formats sorted by Pica texture format index
|
||||
surface_format_control->addItem(tr("RGBA8"));
|
||||
surface_format_control->addItem(tr("RGB8"));
|
||||
surface_format_control->addItem(tr("RGB5A1"));
|
||||
surface_format_control->addItem(tr("RGB565"));
|
||||
surface_format_control->addItem(tr("RGBA4"));
|
||||
surface_format_control->addItem(tr("IA8"));
|
||||
surface_format_control->addItem(tr("RG8"));
|
||||
surface_format_control->addItem(tr("I8"));
|
||||
surface_format_control->addItem(tr("A8"));
|
||||
surface_format_control->addItem(tr("IA4"));
|
||||
surface_format_control->addItem(tr("I4"));
|
||||
surface_format_control->addItem(tr("A4"));
|
||||
surface_format_control->addItem(tr("ETC1"));
|
||||
surface_format_control->addItem(tr("ETC1A4"));
|
||||
surface_format_control->addItem(tr("D16"));
|
||||
surface_format_control->addItem(tr("D24"));
|
||||
surface_format_control->addItem(tr("D24X8"));
|
||||
surface_format_control->addItem(tr("X24S8"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
|
||||
surface_info_label = new QLabel();
|
||||
surface_info_label->setWordWrap(true);
|
||||
|
||||
surface_picture_label = new SurfacePicture(0, this);
|
||||
surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
surface_picture_label->setScaledContents(false);
|
||||
|
||||
auto scroll_area = new QScrollArea();
|
||||
scroll_area->setBackgroundRole(QPalette::Dark);
|
||||
scroll_area->setWidgetResizable(false);
|
||||
scroll_area->setWidget(surface_picture_label);
|
||||
|
||||
save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
|
||||
|
||||
// Connections
|
||||
connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
|
||||
connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int)));
|
||||
connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64)));
|
||||
connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int)));
|
||||
connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int)));
|
||||
connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int)));
|
||||
connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int)));
|
||||
connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int)));
|
||||
connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
|
||||
|
||||
auto main_widget = new QWidget;
|
||||
auto main_layout = new QVBoxLayout;
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Source:")));
|
||||
sub_layout->addWidget(surface_source_list);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Physical Address:")));
|
||||
sub_layout->addWidget(surface_address_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Width:")));
|
||||
sub_layout->addWidget(surface_width_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Height:")));
|
||||
sub_layout->addWidget(surface_height_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Format:")));
|
||||
sub_layout->addWidget(surface_format_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
main_layout->addWidget(scroll_area);
|
||||
|
||||
auto info_layout = new QHBoxLayout;
|
||||
{
|
||||
auto xy_layout = new QVBoxLayout;
|
||||
{
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("X:")));
|
||||
sub_layout->addWidget(surface_picker_x_control);
|
||||
xy_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Y:")));
|
||||
sub_layout->addWidget(surface_picker_y_control);
|
||||
xy_layout->addLayout(sub_layout);
|
||||
}
|
||||
}
|
||||
info_layout->addLayout(xy_layout);
|
||||
surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
info_layout->addWidget(surface_info_label);
|
||||
}
|
||||
main_layout->addLayout(info_layout);
|
||||
|
||||
main_layout->addWidget(save_surface);
|
||||
main_widget->setLayout(main_layout);
|
||||
setWidget(main_widget);
|
||||
|
||||
// Load current data - TODO: Make sure this works when emulation is not running
|
||||
if (debug_context && debug_context->at_breakpoint) {
|
||||
emit Update();
|
||||
widget()->setEnabled(debug_context->at_breakpoint);
|
||||
} else {
|
||||
widget()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
|
||||
{
|
||||
emit Update();
|
||||
widget()->setEnabled(true);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnResumed()
|
||||
{
|
||||
widget()->setEnabled(false);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value)
|
||||
{
|
||||
surface_source = static_cast<Source>(new_value);
|
||||
emit Update();
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
|
||||
{
|
||||
if (surface_address != new_value) {
|
||||
surface_address = static_cast<unsigned>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value)
|
||||
{
|
||||
if (surface_width != static_cast<unsigned>(new_value)) {
|
||||
surface_width = static_cast<unsigned>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value)
|
||||
{
|
||||
if (surface_height != static_cast<unsigned>(new_value)) {
|
||||
surface_height = static_cast<unsigned>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value)
|
||||
{
|
||||
if (surface_format != static_cast<Format>(new_value)) {
|
||||
surface_format = static_cast<Format>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value)
|
||||
{
|
||||
if (surface_picker_x != new_value) {
|
||||
surface_picker_x = new_value;
|
||||
Pick(surface_picker_x, surface_picker_y);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value)
|
||||
{
|
||||
if (surface_picker_y != new_value) {
|
||||
surface_picker_y = new_value;
|
||||
Pick(surface_picker_x, surface_picker_y);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::Pick(int x, int y)
|
||||
{
|
||||
surface_picker_x_control->setValue(x);
|
||||
surface_picker_y_control->setValue(y);
|
||||
|
||||
if (x < 0 || x >= surface_width || y < 0 || y >= surface_height) {
|
||||
surface_info_label->setText(tr("Pixel out of bounds"));
|
||||
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
u8* buffer = Memory::GetPhysicalPointer(surface_address);
|
||||
if (buffer == nullptr) {
|
||||
surface_info_label->setText(tr("(unable to access pixel data)"));
|
||||
surface_info_label->setAlignment(Qt::AlignCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
|
||||
unsigned stride = nibbles_per_pixel * surface_width / 2;
|
||||
|
||||
unsigned bytes_per_pixel;
|
||||
bool nibble_mode = (nibbles_per_pixel == 1);
|
||||
if (nibble_mode) {
|
||||
// As nibbles are contained in a byte we still need to access one byte per nibble
|
||||
bytes_per_pixel = 1;
|
||||
} else {
|
||||
bytes_per_pixel = nibbles_per_pixel / 2;
|
||||
}
|
||||
|
||||
const u32 coarse_y = y & ~7;
|
||||
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
|
||||
const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
|
||||
|
||||
auto GetText = [offset](Format format, const u8* pixel) {
|
||||
switch (format) {
|
||||
case Format::RGBA8:
|
||||
{
|
||||
auto value = Color::DecodeRGBA8(pixel) / 255.0f;
|
||||
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2))
|
||||
.arg(QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::RGB8:
|
||||
{
|
||||
auto value = Color::DecodeRGB8(pixel) / 255.0f;
|
||||
return QString("Red: %1, Green: %2, Blue: %3")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
case Format::RGB5A1:
|
||||
{
|
||||
auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
|
||||
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2))
|
||||
.arg(QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::RGB565:
|
||||
{
|
||||
auto value = Color::DecodeRGB565(pixel) / 255.0f;
|
||||
return QString("Red: %1, Green: %2, Blue: %3")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
case Format::RGBA4:
|
||||
{
|
||||
auto value = Color::DecodeRGBA4(pixel) / 255.0f;
|
||||
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2))
|
||||
.arg(QString::number(value.b(), 'f', 2))
|
||||
.arg(QString::number(value.a(), 'f', 2));
|
||||
}
|
||||
case Format::IA8:
|
||||
return QString("Index: %1, Alpha: %2")
|
||||
.arg(pixel[0])
|
||||
.arg(pixel[1]);
|
||||
case Format::RG8: {
|
||||
auto value = Color::DecodeRG8(pixel) / 255.0f;
|
||||
return QString("Red: %1, Green: %2")
|
||||
.arg(QString::number(value.r(), 'f', 2))
|
||||
.arg(QString::number(value.g(), 'f', 2));
|
||||
}
|
||||
case Format::I8:
|
||||
return QString("Index: %1").arg(*pixel);
|
||||
case Format::A8:
|
||||
return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
|
||||
case Format::IA4:
|
||||
return QString("Index: %1, Alpha: %2")
|
||||
.arg(*pixel & 0xF)
|
||||
.arg((*pixel & 0xF0) >> 4);
|
||||
case Format::I4:
|
||||
{
|
||||
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
|
||||
return QString("Index: %1").arg(i);
|
||||
}
|
||||
case Format::A4:
|
||||
{
|
||||
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
|
||||
return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
|
||||
}
|
||||
case Format::ETC1:
|
||||
case Format::ETC1A4:
|
||||
// TODO: Display block information or channel values?
|
||||
return QString("Compressed data");
|
||||
case Format::D16:
|
||||
{
|
||||
auto value = Color::DecodeD16(pixel);
|
||||
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
|
||||
}
|
||||
case Format::D24:
|
||||
{
|
||||
auto value = Color::DecodeD24(pixel);
|
||||
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
|
||||
}
|
||||
case Format::D24X8:
|
||||
case Format::X24S8:
|
||||
{
|
||||
auto values = Color::DecodeD24S8(pixel);
|
||||
return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]);
|
||||
}
|
||||
case Format::Unknown:
|
||||
return QString("Unknown format");
|
||||
default:
|
||||
return QString("Unhandled format");
|
||||
}
|
||||
return QString("");
|
||||
};
|
||||
|
||||
QString nibbles = "";
|
||||
for (unsigned i = 0; i < nibbles_per_pixel; i++) {
|
||||
unsigned nibble_index = i;
|
||||
if (nibble_mode) {
|
||||
nibble_index += (offset % 2) ? 0 : 1;
|
||||
}
|
||||
u8 byte = pixel[nibble_index / 2];
|
||||
u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
|
||||
nibbles.append(QString::number(nibble, 16).toUpper());
|
||||
}
|
||||
|
||||
surface_info_label->setText(QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
|
||||
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnUpdate()
|
||||
{
|
||||
QPixmap pixmap;
|
||||
|
||||
switch (surface_source) {
|
||||
case Source::ColorBuffer:
|
||||
{
|
||||
// TODO: Store a reference to the registers in the debug context instead of accessing them directly...
|
||||
|
||||
const auto& framebuffer = Pica::g_state.regs.framebuffer;
|
||||
|
||||
surface_address = framebuffer.GetColorBufferPhysicalAddress();
|
||||
surface_width = framebuffer.GetWidth();
|
||||
surface_height = framebuffer.GetHeight();
|
||||
|
||||
switch (framebuffer.color_format) {
|
||||
case Pica::Regs::ColorFormat::RGBA8:
|
||||
surface_format = Format::RGBA8;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGB8:
|
||||
surface_format = Format::RGB8;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGB5A1:
|
||||
surface_format = Format::RGB5A1;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGB565:
|
||||
surface_format = Format::RGB565;
|
||||
break;
|
||||
|
||||
case Pica::Regs::ColorFormat::RGBA4:
|
||||
surface_format = Format::RGBA4;
|
||||
break;
|
||||
|
||||
default:
|
||||
surface_format = Format::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::DepthBuffer:
|
||||
{
|
||||
const auto& framebuffer = Pica::g_state.regs.framebuffer;
|
||||
|
||||
surface_address = framebuffer.GetDepthBufferPhysicalAddress();
|
||||
surface_width = framebuffer.GetWidth();
|
||||
surface_height = framebuffer.GetHeight();
|
||||
|
||||
switch (framebuffer.depth_format) {
|
||||
case Pica::Regs::DepthFormat::D16:
|
||||
surface_format = Format::D16;
|
||||
break;
|
||||
|
||||
case Pica::Regs::DepthFormat::D24:
|
||||
surface_format = Format::D24;
|
||||
break;
|
||||
|
||||
case Pica::Regs::DepthFormat::D24S8:
|
||||
surface_format = Format::D24X8;
|
||||
break;
|
||||
|
||||
default:
|
||||
surface_format = Format::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::StencilBuffer:
|
||||
{
|
||||
const auto& framebuffer = Pica::g_state.regs.framebuffer;
|
||||
|
||||
surface_address = framebuffer.GetDepthBufferPhysicalAddress();
|
||||
surface_width = framebuffer.GetWidth();
|
||||
surface_height = framebuffer.GetHeight();
|
||||
|
||||
switch (framebuffer.depth_format) {
|
||||
case Pica::Regs::DepthFormat::D24S8:
|
||||
surface_format = Format::X24S8;
|
||||
break;
|
||||
|
||||
default:
|
||||
surface_format = Format::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::Texture0:
|
||||
case Source::Texture1:
|
||||
case Source::Texture2:
|
||||
{
|
||||
unsigned texture_index;
|
||||
if (surface_source == Source::Texture0) texture_index = 0;
|
||||
else if (surface_source == Source::Texture1) texture_index = 1;
|
||||
else if (surface_source == Source::Texture2) texture_index = 2;
|
||||
else {
|
||||
qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
|
||||
break;
|
||||
}
|
||||
|
||||
const auto texture = Pica::g_state.regs.GetTextures()[texture_index];
|
||||
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
|
||||
|
||||
surface_address = info.physical_address;
|
||||
surface_width = info.width;
|
||||
surface_height = info.height;
|
||||
surface_format = static_cast<Format>(info.format);
|
||||
|
||||
if (surface_format > Format::MaxTextureFormat) {
|
||||
qDebug() << "Unknown texture format " << static_cast<int>(info.format);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::Custom:
|
||||
{
|
||||
// Keep user-specified values
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
|
||||
break;
|
||||
}
|
||||
|
||||
surface_address_control->SetValue(surface_address);
|
||||
surface_width_control->setValue(surface_width);
|
||||
surface_height_control->setValue(surface_height);
|
||||
surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
|
||||
|
||||
// TODO: Implement a good way to visualize alpha components!
|
||||
|
||||
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
|
||||
u8* buffer = Memory::GetPhysicalPointer(surface_address);
|
||||
|
||||
if (buffer == nullptr) {
|
||||
surface_picture_label->hide();
|
||||
surface_info_label->setText(tr("(invalid surface address)"));
|
||||
surface_info_label->setAlignment(Qt::AlignCenter);
|
||||
surface_picker_x_control->setEnabled(false);
|
||||
surface_picker_y_control->setEnabled(false);
|
||||
save_surface->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface_format == Format::Unknown) {
|
||||
surface_picture_label->hide();
|
||||
surface_info_label->setText(tr("(unknown surface format)"));
|
||||
surface_info_label->setAlignment(Qt::AlignCenter);
|
||||
surface_picker_x_control->setEnabled(false);
|
||||
surface_picker_y_control->setEnabled(false);
|
||||
save_surface->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
surface_picture_label->show();
|
||||
|
||||
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
|
||||
unsigned stride = nibbles_per_pixel * surface_width / 2;
|
||||
|
||||
// We handle depth formats here because DebugUtils only supports TextureFormats
|
||||
if (surface_format <= Format::MaxTextureFormat) {
|
||||
|
||||
// Generate a virtual texture
|
||||
Pica::DebugUtils::TextureInfo info;
|
||||
info.physical_address = surface_address;
|
||||
info.width = surface_width;
|
||||
info.height = surface_height;
|
||||
info.format = static_cast<Pica::Regs::TextureFormat>(surface_format);
|
||||
info.stride = stride;
|
||||
|
||||
for (unsigned int y = 0; y < surface_height; ++y) {
|
||||
for (unsigned int x = 0; x < surface_width; ++x) {
|
||||
Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(buffer, x, y, info, true);
|
||||
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel");
|
||||
unsigned bytes_per_pixel = nibbles_per_pixel / 2;
|
||||
|
||||
for (unsigned int y = 0; y < surface_height; ++y) {
|
||||
for (unsigned int x = 0; x < surface_width; ++x) {
|
||||
const u32 coarse_y = y & ~7;
|
||||
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
|
||||
const u8* pixel = buffer + offset;
|
||||
Math::Vec4<u8> color = { 0, 0, 0, 0 };
|
||||
|
||||
switch(surface_format) {
|
||||
case Format::D16:
|
||||
{
|
||||
u32 data = Color::DecodeD16(pixel);
|
||||
color.r() = data & 0xFF;
|
||||
color.g() = (data >> 8) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case Format::D24:
|
||||
{
|
||||
u32 data = Color::DecodeD24(pixel);
|
||||
color.r() = data & 0xFF;
|
||||
color.g() = (data >> 8) & 0xFF;
|
||||
color.b() = (data >> 16) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case Format::D24X8:
|
||||
{
|
||||
Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
|
||||
color.r() = data.x & 0xFF;
|
||||
color.g() = (data.x >> 8) & 0xFF;
|
||||
color.b() = (data.x >> 16) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case Format::X24S8:
|
||||
{
|
||||
Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
|
||||
color.r() = color.g() = color.b() = data.y;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qDebug() << "Unknown surface format " << static_cast<int>(surface_format);
|
||||
break;
|
||||
}
|
||||
|
||||
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pixmap = QPixmap::fromImage(decoded_image);
|
||||
surface_picture_label->setPixmap(pixmap);
|
||||
surface_picture_label->resize(pixmap.size());
|
||||
|
||||
// Update the info with pixel data
|
||||
surface_picker_x_control->setEnabled(true);
|
||||
surface_picker_y_control->setEnabled(true);
|
||||
Pick(surface_picker_x, surface_picker_y);
|
||||
|
||||
// Enable saving the converted pixmap to file
|
||||
save_surface->setEnabled(true);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::SaveSurface() {
|
||||
QString png_filter = tr("Portable Network Graphic (*.png)");
|
||||
QString bin_filter = tr("Binary data (*.bin)");
|
||||
|
||||
QString selectedFilter;
|
||||
QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
|
||||
QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
// If the user canceled the dialog, don't save anything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedFilter == png_filter) {
|
||||
const QPixmap* pixmap = surface_picture_label->pixmap();
|
||||
ASSERT_MSG(pixmap != nullptr, "No pixmap set");
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
if (pixmap)
|
||||
pixmap->save(&file, "PNG");
|
||||
} else if (selectedFilter == bin_filter) {
|
||||
const u8* buffer = Memory::GetPhysicalPointer(surface_address);
|
||||
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
int size = surface_width * surface_height * NibblesPerPixel(surface_format) / 2;
|
||||
QByteArray data(reinterpret_cast<const char*>(buffer), size);
|
||||
file.write(data);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unhandled filter selected");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Format format) {
|
||||
if (format <= Format::MaxTextureFormat) {
|
||||
return Pica::Regs::NibblesPerPixel(static_cast<Pica::Regs::TextureFormat>(format));
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case Format::D24X8:
|
||||
case Format::X24S8:
|
||||
return 4 * 2;
|
||||
case Format::D24:
|
||||
return 3 * 2;
|
||||
case Format::D16:
|
||||
return 2 * 2;
|
||||
default:
|
||||
UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this "
|
||||
"should not be reached as this function should "
|
||||
"be given a format which is in "
|
||||
"GraphicsSurfaceWidget::Format. Instead got %i",
|
||||
static_cast<int>(format));
|
||||
return 0;
|
||||
}
|
||||
}
|
120
src/citra_qt/debugger/graphics_surface.h
Normal file
120
src/citra_qt/debugger/graphics_surface.h
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "citra_qt/debugger/graphics_breakpoint_observer.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
class QComboBox;
|
||||
class QSpinBox;
|
||||
class CSpinBox;
|
||||
|
||||
class GraphicsSurfaceWidget;
|
||||
|
||||
class SurfacePicture : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SurfacePicture(QWidget* parent = 0, GraphicsSurfaceWidget* surface_widget = nullptr);
|
||||
~SurfacePicture();
|
||||
|
||||
protected slots:
|
||||
virtual void mouseMoveEvent(QMouseEvent* event);
|
||||
virtual void mousePressEvent(QMouseEvent* event);
|
||||
|
||||
private:
|
||||
GraphicsSurfaceWidget* surface_widget;
|
||||
|
||||
};
|
||||
|
||||
class GraphicsSurfaceWidget : public BreakPointObserverDock {
|
||||
Q_OBJECT
|
||||
|
||||
using Event = Pica::DebugContext::Event;
|
||||
|
||||
enum class Source {
|
||||
ColorBuffer = 0,
|
||||
DepthBuffer = 1,
|
||||
StencilBuffer = 2,
|
||||
Texture0 = 3,
|
||||
Texture1 = 4,
|
||||
Texture2 = 5,
|
||||
Custom = 6,
|
||||
};
|
||||
|
||||
enum class Format {
|
||||
// These must match the TextureFormat type!
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
IA8 = 5,
|
||||
RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
|
||||
I8 = 7,
|
||||
A8 = 8,
|
||||
IA4 = 9,
|
||||
I4 = 10,
|
||||
A4 = 11,
|
||||
ETC1 = 12, // compressed
|
||||
ETC1A4 = 13,
|
||||
MaxTextureFormat = 13,
|
||||
D16 = 14,
|
||||
D24 = 15,
|
||||
D24X8 = 16,
|
||||
X24S8 = 17,
|
||||
Unknown = 18,
|
||||
};
|
||||
|
||||
static unsigned int NibblesPerPixel(Format format);
|
||||
|
||||
public:
|
||||
GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
|
||||
void Pick(int x, int y);
|
||||
|
||||
public slots:
|
||||
void OnSurfaceSourceChanged(int new_value);
|
||||
void OnSurfaceAddressChanged(qint64 new_value);
|
||||
void OnSurfaceWidthChanged(int new_value);
|
||||
void OnSurfaceHeightChanged(int new_value);
|
||||
void OnSurfaceFormatChanged(int new_value);
|
||||
void OnSurfacePickerXChanged(int new_value);
|
||||
void OnSurfacePickerYChanged(int new_value);
|
||||
void OnUpdate();
|
||||
|
||||
private slots:
|
||||
void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
|
||||
void OnResumed() override;
|
||||
|
||||
void SaveSurface();
|
||||
|
||||
signals:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
|
||||
QComboBox* surface_source_list;
|
||||
CSpinBox* surface_address_control;
|
||||
QSpinBox* surface_width_control;
|
||||
QSpinBox* surface_height_control;
|
||||
QComboBox* surface_format_control;
|
||||
|
||||
SurfacePicture* surface_picture_label;
|
||||
QSpinBox* surface_picker_x_control;
|
||||
QSpinBox* surface_picker_y_control;
|
||||
QLabel* surface_info_label;
|
||||
QPushButton* save_surface;
|
||||
|
||||
Source surface_source;
|
||||
unsigned surface_address;
|
||||
unsigned surface_width;
|
||||
unsigned surface_height;
|
||||
Format surface_format;
|
||||
int surface_picker_x = 0;
|
||||
int surface_picker_y = 0;
|
||||
};
|
@ -151,8 +151,8 @@ private:
|
||||
/// This timer is used to redraw the widget's contents continuously. To save resources, it only
|
||||
/// runs while the widget is visible.
|
||||
QTimer update_timer;
|
||||
/// Scale the coordinate system appropriately when physical DPI != logical DPI.
|
||||
qreal x_scale, y_scale;
|
||||
/// Scale the coordinate system appropriately when dpi != 96.
|
||||
qreal x_scale = 1.0, y_scale = 1.0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -222,15 +222,14 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
|
||||
MicroProfileInitUI();
|
||||
|
||||
connect(&update_timer, SIGNAL(timeout()), SLOT(update()));
|
||||
|
||||
QPainter painter(this);
|
||||
x_scale = qreal(painter.device()->physicalDpiX()) / qreal(painter.device()->logicalDpiX());
|
||||
y_scale = qreal(painter.device()->physicalDpiY()) / qreal(painter.device()->logicalDpiY());
|
||||
}
|
||||
|
||||
void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
|
||||
QPainter painter(this);
|
||||
|
||||
// The units used by Microprofile for drawing are based in pixels on a 96 dpi display.
|
||||
x_scale = qreal(painter.device()->logicalDpiX()) / 96.0;
|
||||
y_scale = qreal(painter.device()->logicalDpiY()) / 96.0;
|
||||
painter.scale(x_scale, y_scale);
|
||||
|
||||
painter.setBackground(Qt::black);
|
||||
@ -241,7 +240,7 @@ void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
|
||||
painter.setFont(font);
|
||||
|
||||
mp_painter = &painter;
|
||||
MicroProfileDraw(rect().width(), rect().height());
|
||||
MicroProfileDraw(rect().width() / x_scale, rect().height() / y_scale);
|
||||
mp_painter = nullptr;
|
||||
}
|
||||
|
||||
|
@ -118,46 +118,31 @@ void GameList::LoadInterfaceLayout()
|
||||
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
|
||||
}
|
||||
|
||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan)
|
||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion)
|
||||
{
|
||||
const auto callback = [&](unsigned* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
|
||||
const auto callback = [this, recursion](unsigned* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
|
||||
if (stop_processing)
|
||||
return false; // Breaks the callback loop.
|
||||
|
||||
if (deep_scan && FileUtil::IsDirectory(physical_name)) {
|
||||
AddFstEntriesToGameList(physical_name, true);
|
||||
} else {
|
||||
std::string filename_filename, filename_extension;
|
||||
Common::SplitPath(physical_name, nullptr, &filename_filename, &filename_extension);
|
||||
|
||||
Loader::FileType guessed_filetype = Loader::GuessFromExtension(filename_extension);
|
||||
if (guessed_filetype == Loader::FileType::Unknown)
|
||||
if (!FileUtil::IsDirectory(physical_name)) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
if (!loader)
|
||||
return true;
|
||||
Loader::FileType filetype = Loader::IdentifyFile(physical_name);
|
||||
if (filetype == Loader::FileType::Unknown) {
|
||||
LOG_WARNING(Frontend, "File %s is of indeterminate type and is possibly corrupted.", physical_name.c_str());
|
||||
return true;
|
||||
}
|
||||
if (guessed_filetype != filetype) {
|
||||
LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str());
|
||||
}
|
||||
|
||||
std::vector<u8> smdh;
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name);
|
||||
|
||||
if (loader)
|
||||
loader->ReadIcon(smdh);
|
||||
loader->ReadIcon(smdh);
|
||||
|
||||
emit EntryReady({
|
||||
new GameListItemPath(QString::fromStdString(physical_name), smdh),
|
||||
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))),
|
||||
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
});
|
||||
} else if (recursion > 0) {
|
||||
AddFstEntriesToGameList(physical_name, recursion - 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -169,7 +154,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
|
||||
void GameListWorker::run()
|
||||
{
|
||||
stop_processing = false;
|
||||
AddFstEntriesToGameList(dir_path.toStdString(), deep_scan);
|
||||
AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
|
||||
emit Finished();
|
||||
}
|
||||
|
||||
|
@ -15,52 +15,21 @@
|
||||
#include "common/string_util.h"
|
||||
#include "common/color.h"
|
||||
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/loader/smdh.h"
|
||||
|
||||
#include "video_core/utils.h"
|
||||
|
||||
/**
|
||||
* Tests if data is a valid SMDH by its length and magic number.
|
||||
* @param smdh_data data buffer to test
|
||||
* @return bool test result
|
||||
*/
|
||||
static bool IsValidSMDH(const std::vector<u8>& smdh_data) {
|
||||
if (smdh_data.size() < sizeof(Loader::SMDH))
|
||||
return false;
|
||||
|
||||
u32 magic;
|
||||
memcpy(&magic, smdh_data.data(), 4);
|
||||
|
||||
return Loader::MakeMagic('S', 'M', 'D', 'H') == magic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets game icon from SMDH
|
||||
* @param sdmh SMDH data
|
||||
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
|
||||
* @return QPixmap game icon
|
||||
*/
|
||||
static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) {
|
||||
u32 size;
|
||||
const u8* icon_data;
|
||||
|
||||
if (large) {
|
||||
size = 48;
|
||||
icon_data = smdh.large_icon.data();
|
||||
} else {
|
||||
size = 24;
|
||||
icon_data = smdh.small_icon.data();
|
||||
}
|
||||
|
||||
QImage icon(size, size, QImage::Format::Format_RGB888);
|
||||
for (u32 x = 0; x < size; ++x) {
|
||||
for (u32 y = 0; y < size; ++y) {
|
||||
u32 coarse_y = y & ~7;
|
||||
auto v = Color::DecodeRGB565(
|
||||
icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2);
|
||||
icon.setPixel(x, y, qRgb(v.r(), v.g(), v.b()));
|
||||
}
|
||||
}
|
||||
static QPixmap GetQPixmapFromSMDH(const Loader::SMDH& smdh, bool large) {
|
||||
std::vector<u16> icon_data = smdh.GetIcon(large);
|
||||
const uchar* data = reinterpret_cast<const uchar*>(icon_data.data());
|
||||
int size = large ? 48 : 24;
|
||||
QImage icon(data, size, size, QImage::Format::Format_RGB16);
|
||||
return QPixmap::fromImage(icon);
|
||||
}
|
||||
|
||||
@ -82,8 +51,8 @@ static QPixmap GetDefaultIcon(bool large) {
|
||||
* @param language title language
|
||||
* @return QString short title
|
||||
*/
|
||||
static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) {
|
||||
return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data());
|
||||
static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) {
|
||||
return QString::fromUtf16(smdh.GetShortTitle(language).data());
|
||||
}
|
||||
|
||||
class GameListItem : public QStandardItem {
|
||||
@ -112,7 +81,7 @@ public:
|
||||
{
|
||||
setData(game_path, FullPathRole);
|
||||
|
||||
if (!IsValidSMDH(smdh_data)) {
|
||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||
// SMDH is not valid, set a default icon
|
||||
setData(GetDefaultIcon(true), Qt::DecorationRole);
|
||||
return;
|
||||
@ -122,10 +91,10 @@ public:
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
// Get icon from SMDH
|
||||
setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole);
|
||||
setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
|
||||
|
||||
// Get title form SMDH
|
||||
setData(GetShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole);
|
||||
setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole);
|
||||
}
|
||||
|
||||
QVariant data(int role) const override {
|
||||
@ -212,5 +181,5 @@ private:
|
||||
bool deep_scan;
|
||||
std::atomic_bool stop_processing;
|
||||
|
||||
void AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan);
|
||||
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "citra_qt/debugger/graphics.h"
|
||||
#include "citra_qt/debugger/graphics_breakpoints.h"
|
||||
#include "citra_qt/debugger/graphics_cmdlists.h"
|
||||
#include "citra_qt/debugger/graphics_framebuffer.h"
|
||||
#include "citra_qt/debugger/graphics_surface.h"
|
||||
#include "citra_qt/debugger/graphics_tracing.h"
|
||||
#include "citra_qt/debugger/graphics_vertex_shader.h"
|
||||
#include "citra_qt/debugger/profiler.h"
|
||||
@ -101,10 +101,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
|
||||
graphicsBreakpointsWidget->hide();
|
||||
|
||||
auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
|
||||
graphicsFramebufferWidget->hide();
|
||||
|
||||
auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
|
||||
graphicsVertexShaderWidget->hide();
|
||||
@ -113,7 +109,12 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
|
||||
graphicsTracingWidget->hide();
|
||||
|
||||
auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this);
|
||||
connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer()));
|
||||
|
||||
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
|
||||
debug_menu->addAction(graphicsSurfaceViewerAction);
|
||||
debug_menu->addSeparator();
|
||||
debug_menu->addAction(profilerWidget->toggleViewAction());
|
||||
#if MICROPROFILE_ENABLED
|
||||
debug_menu->addAction(microProfileDialog->toggleViewAction());
|
||||
@ -124,7 +125,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
|
||||
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
||||
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
||||
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
|
||||
debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
|
||||
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
|
||||
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
|
||||
|
||||
@ -272,7 +272,15 @@ bool GMainWindow::InitializeSystem() {
|
||||
}
|
||||
|
||||
bool GMainWindow::LoadROM(const std::string& filename) {
|
||||
Loader::ResultStatus result = Loader::LoadFile(filename);
|
||||
std::unique_ptr<Loader::AppLoader> app_loader = Loader::GetLoader(filename);
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str());
|
||||
QMessageBox::critical(this, tr("Error while loading ROM!"),
|
||||
tr("The ROM format is not supported."));
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus result = app_loader->Load();
|
||||
if (Loader::ResultStatus::Success != result) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
System::Shutdown();
|
||||
@ -500,15 +508,23 @@ void GMainWindow::ToggleWindowMode() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
ConfigureDialog configureDialog(this);
|
||||
ConfigureDialog configureDialog(this, emulation_running);
|
||||
auto result = configureDialog.exec();
|
||||
if (result == QDialog::Accepted)
|
||||
{
|
||||
configureDialog.applyConfiguration();
|
||||
render_window->ReloadSetKeymaps();
|
||||
config->Save();
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnCreateGraphicsSurfaceViewer() {
|
||||
auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget);
|
||||
// TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true);
|
||||
graphicsSurfaceViewerWidget->show();
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmClose() {
|
||||
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
||||
return true;
|
||||
|
@ -108,6 +108,7 @@ private slots:
|
||||
void OnConfigure();
|
||||
void OnDisplayTitleBars(bool);
|
||||
void ToggleWindowMode();
|
||||
void OnCreateGraphicsSurfaceViewer();
|
||||
|
||||
private:
|
||||
Ui::MainWindow ui;
|
||||
|
@ -72,18 +72,24 @@ inline u64 _rotr64(u64 x, unsigned int shift){
|
||||
}
|
||||
|
||||
#else // _MSC_VER
|
||||
#if (_MSC_VER < 1900)
|
||||
// Function Cross-Compatibility
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Locale Cross-Compatibility
|
||||
#define locale_t _locale_t
|
||||
#if (_MSC_VER < 1900)
|
||||
// Function Cross-Compatibility
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Locale Cross-Compatibility
|
||||
#define locale_t _locale_t
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
}
|
||||
#define Crash() {DebugBreak();}
|
||||
|
||||
// cstdlib provides these on MSVC
|
||||
#define rotr _rotr
|
||||
#define rotl _rotl
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
}
|
||||
#define Crash() {DebugBreak();}
|
||||
#endif // _MSC_VER ndef
|
||||
|
||||
// Generic function to get last error message.
|
||||
|
@ -11,12 +11,28 @@
|
||||
#include "emu_window.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
|
||||
pad_state.hex |= KeyMap::GetPadKey(key).hex;
|
||||
void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
|
||||
pad_state.hex |= pad.hex;
|
||||
}
|
||||
|
||||
void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
|
||||
pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
|
||||
void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
|
||||
pad_state.hex &= ~pad.hex;
|
||||
}
|
||||
|
||||
void EmuWindow::CirclePadUpdated(float x, float y) {
|
||||
constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
|
||||
|
||||
// Make sure the coordinates are in the unit circle,
|
||||
// otherwise normalize it.
|
||||
float r = x * x + y * y;
|
||||
if (r > 1) {
|
||||
r = std::sqrt(r);
|
||||
x /= r;
|
||||
y /= r;
|
||||
}
|
||||
|
||||
circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS);
|
||||
circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +51,6 @@ static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsi
|
||||
}
|
||||
|
||||
std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) {
|
||||
|
||||
new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
|
||||
new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1);
|
||||
|
||||
@ -76,9 +91,9 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
}
|
||||
|
||||
EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
|
||||
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
// When hiding the widget, the function receives a size of 0
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
|
||||
EmuWindow::FramebufferLayout res = { width, height, {}, {} };
|
||||
|
||||
|
@ -12,10 +12,6 @@
|
||||
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
|
||||
namespace KeyMap {
|
||||
struct HostDeviceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
@ -76,11 +72,27 @@ public:
|
||||
|
||||
virtual void ReloadSetKeymaps() = 0;
|
||||
|
||||
/// Signals a key press action to the HID module
|
||||
void KeyPressed(KeyMap::HostDeviceKey key);
|
||||
/**
|
||||
* Signals a button press action to the HID module.
|
||||
* @param pad_state indicates which button to press
|
||||
* @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
|
||||
*/
|
||||
void ButtonPressed(Service::HID::PadState pad_state);
|
||||
|
||||
/// Signals a key release action to the HID module
|
||||
void KeyReleased(KeyMap::HostDeviceKey key);
|
||||
/**
|
||||
* Signals a button release action to the HID module.
|
||||
* @param pad_state indicates which button to press
|
||||
* @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
|
||||
*/
|
||||
void ButtonReleased(Service::HID::PadState pad_state);
|
||||
|
||||
/**
|
||||
* Signals a circle pad change action to the HID module.
|
||||
* @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0]
|
||||
* @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0]
|
||||
* @note the coordinates will be normalized if the radius is larger than 1
|
||||
*/
|
||||
void CirclePadUpdated(float x, float y);
|
||||
|
||||
/**
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
@ -100,8 +112,9 @@ public:
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
|
||||
/**
|
||||
* Gets the current pad state (which buttons are pressed and the circle pad direction).
|
||||
* Gets the current pad state (which buttons are pressed).
|
||||
* @note This should be called by the core emu thread to get a state set by the window thread.
|
||||
* @note This doesn't include analog input like circle pad direction
|
||||
* @todo Fix this function to be thread-safe.
|
||||
* @return PadState object indicating the current pad state
|
||||
*/
|
||||
@ -109,6 +122,16 @@ public:
|
||||
return pad_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current circle pad state.
|
||||
* @note This should be called by the core emu thread to get a state set by the window thread.
|
||||
* @todo Fix this function to be thread-safe.
|
||||
* @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates
|
||||
*/
|
||||
std::tuple<s16, s16> GetCirclePadState() const {
|
||||
return std::make_tuple(circle_pad_x, circle_pad_y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
|
||||
* @note This should be called by the core emu thread to get a state set by the window thread.
|
||||
@ -200,6 +223,8 @@ protected:
|
||||
pad_state.hex = 0;
|
||||
touch_x = 0;
|
||||
touch_y = 0;
|
||||
circle_pad_x = 0;
|
||||
circle_pad_y = 0;
|
||||
touch_pressed = false;
|
||||
}
|
||||
virtual ~EmuWindow() {}
|
||||
@ -260,6 +285,9 @@ private:
|
||||
u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
|
||||
u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
|
||||
|
||||
s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
|
||||
s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
|
||||
|
||||
/**
|
||||
* Clip the provided coordinates to be inside the touchscreen area.
|
||||
*/
|
||||
|
@ -486,30 +486,33 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
|
||||
closedir(dirp);
|
||||
#endif
|
||||
|
||||
if (!callback_error) {
|
||||
// num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
|
||||
if (num_entries_out != nullptr)
|
||||
*num_entries_out = found_entries;
|
||||
return true;
|
||||
} else {
|
||||
if (callback_error)
|
||||
return false;
|
||||
}
|
||||
|
||||
// num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
|
||||
if (num_entries_out != nullptr)
|
||||
*num_entries_out = found_entries;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
|
||||
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion)
|
||||
{
|
||||
const auto callback = [&parent_entry](unsigned* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
const auto callback = [recursion, &parent_entry](unsigned* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
FSTEntry entry;
|
||||
entry.virtualName = virtual_name;
|
||||
entry.physicalName = directory + DIR_SEP + virtual_name;
|
||||
|
||||
if (IsDirectory(entry.physicalName)) {
|
||||
entry.isDirectory = true;
|
||||
// is a directory, lets go inside
|
||||
entry.size = ScanDirectoryTree(entry.physicalName, entry);
|
||||
*num_entries_out += (int)entry.size;
|
||||
// is a directory, lets go inside if we didn't recurse to often
|
||||
if (recursion > 0) {
|
||||
entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1);
|
||||
*num_entries_out += (int)entry.size;
|
||||
} else {
|
||||
entry.size = 0;
|
||||
}
|
||||
} else { // is a file
|
||||
entry.isDirectory = false;
|
||||
entry.size = GetSize(entry.physicalName);
|
||||
@ -526,15 +529,18 @@ unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
|
||||
}
|
||||
|
||||
|
||||
bool DeleteDirRecursively(const std::string &directory)
|
||||
bool DeleteDirRecursively(const std::string &directory, unsigned int recursion)
|
||||
{
|
||||
const static auto callback = [](unsigned* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
const auto callback = [recursion](unsigned* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
|
||||
if (IsDirectory(new_path))
|
||||
return DeleteDirRecursively(new_path);
|
||||
|
||||
if (IsDirectory(new_path)) {
|
||||
if (recursion == 0)
|
||||
return false;
|
||||
return DeleteDirRecursively(new_path, recursion - 1);
|
||||
}
|
||||
return Delete(new_path);
|
||||
};
|
||||
|
||||
|
@ -125,12 +125,13 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
|
||||
* Scans the directory tree, storing the results.
|
||||
* @param directory the parent directory to start scanning from
|
||||
* @param parent_entry FSTEntry where the filesystem tree results will be stored.
|
||||
* @param recursion Number of children directories to read before giving up.
|
||||
* @return the total number of files/directories found
|
||||
*/
|
||||
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry);
|
||||
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion = 0);
|
||||
|
||||
// deletes the given directory and anything under it. Returns true on success.
|
||||
bool DeleteDirRecursively(const std::string &directory);
|
||||
bool DeleteDirRecursively(const std::string &directory, unsigned int recursion = 256);
|
||||
|
||||
// Returns the current directory
|
||||
std::string GetCurrentDir();
|
||||
|
@ -2,24 +2,138 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "key_map.h"
|
||||
#include <map>
|
||||
|
||||
#include "common/emu_window.h"
|
||||
#include "common/key_map.h"
|
||||
|
||||
namespace KeyMap {
|
||||
|
||||
static std::map<HostDeviceKey, Service::HID::PadState> key_map;
|
||||
// TODO (wwylele): currently we treat c-stick as four direction buttons
|
||||
// and map it directly to EmuWindow::ButtonPressed.
|
||||
// It should go the analog input way like circle pad does.
|
||||
const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{
|
||||
Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y,
|
||||
Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR,
|
||||
Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE,
|
||||
Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT,
|
||||
Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT,
|
||||
|
||||
IndirectTarget::CirclePadUp,
|
||||
IndirectTarget::CirclePadDown,
|
||||
IndirectTarget::CirclePadLeft,
|
||||
IndirectTarget::CirclePadRight,
|
||||
IndirectTarget::CirclePadModifier,
|
||||
}};
|
||||
|
||||
static std::map<HostDeviceKey, KeyTarget> key_map;
|
||||
static int next_device_id = 0;
|
||||
|
||||
static bool circle_pad_up = false;
|
||||
static bool circle_pad_down = false;
|
||||
static bool circle_pad_left = false;
|
||||
static bool circle_pad_right = false;
|
||||
static bool circle_pad_modifier = false;
|
||||
|
||||
static void UpdateCirclePad(EmuWindow& emu_window) {
|
||||
constexpr float SQRT_HALF = 0.707106781;
|
||||
int x = 0, y = 0;
|
||||
|
||||
if (circle_pad_right)
|
||||
++x;
|
||||
if (circle_pad_left)
|
||||
--x;
|
||||
if (circle_pad_up)
|
||||
++y;
|
||||
if (circle_pad_down)
|
||||
--y;
|
||||
|
||||
float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0;
|
||||
emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), y * modifier * (x == 0 ? 1.0 : SQRT_HALF));
|
||||
}
|
||||
|
||||
int NewDeviceId() {
|
||||
return next_device_id++;
|
||||
}
|
||||
|
||||
void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) {
|
||||
key_map[key].hex = padState.hex;
|
||||
void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
|
||||
key_map[key] = target;
|
||||
}
|
||||
|
||||
Service::HID::PadState GetPadKey(HostDeviceKey key) {
|
||||
return key_map[key];
|
||||
void ClearKeyMapping(int device_id) {
|
||||
auto iter = key_map.begin();
|
||||
while (iter != key_map.end()) {
|
||||
if (iter->first.device_id == device_id)
|
||||
key_map.erase(iter++);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void PressKey(EmuWindow& emu_window, HostDeviceKey key) {
|
||||
auto target = key_map.find(key);
|
||||
if (target == key_map.end())
|
||||
return;
|
||||
|
||||
if (target->second.direct) {
|
||||
emu_window.ButtonPressed({{target->second.target.direct_target_hex}});
|
||||
} else {
|
||||
switch (target->second.target.indirect_target) {
|
||||
case IndirectTarget::CirclePadUp:
|
||||
circle_pad_up = true;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadDown:
|
||||
circle_pad_down = true;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadLeft:
|
||||
circle_pad_left = true;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadRight:
|
||||
circle_pad_right = true;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadModifier:
|
||||
circle_pad_modifier = true;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) {
|
||||
auto target = key_map.find(key);
|
||||
if (target == key_map.end())
|
||||
return;
|
||||
|
||||
if (target->second.direct) {
|
||||
emu_window.ButtonReleased({{target->second.target.direct_target_hex}});
|
||||
} else {
|
||||
switch (target->second.target.indirect_target) {
|
||||
case IndirectTarget::CirclePadUp:
|
||||
circle_pad_up = false;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadDown:
|
||||
circle_pad_down = false;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadLeft:
|
||||
circle_pad_left = false;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadRight:
|
||||
circle_pad_right = false;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
case IndirectTarget::CirclePadModifier:
|
||||
circle_pad_modifier = false;
|
||||
UpdateCirclePad(emu_window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,11 +4,50 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
|
||||
class EmuWindow;
|
||||
|
||||
namespace KeyMap {
|
||||
|
||||
/**
|
||||
* Represents key mapping targets that are not real 3DS buttons.
|
||||
* They will be handled by KeyMap and translated to 3DS input.
|
||||
*/
|
||||
enum class IndirectTarget {
|
||||
CirclePadUp,
|
||||
CirclePadDown,
|
||||
CirclePadLeft,
|
||||
CirclePadRight,
|
||||
CirclePadModifier,
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a key mapping target. It can be a PadState that represents real 3DS buttons,
|
||||
* or an IndirectTarget.
|
||||
*/
|
||||
struct KeyTarget {
|
||||
bool direct;
|
||||
union {
|
||||
u32 direct_target_hex;
|
||||
IndirectTarget indirect_target;
|
||||
} target;
|
||||
|
||||
KeyTarget() : direct(true) {
|
||||
target.direct_target_hex = 0;
|
||||
}
|
||||
|
||||
KeyTarget(Service::HID::PadState pad) : direct(true) {
|
||||
target.direct_target_hex = pad.hex;
|
||||
}
|
||||
|
||||
KeyTarget(IndirectTarget i) : direct(false) {
|
||||
target.indirect_target = i;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a key for a specific host device.
|
||||
*/
|
||||
@ -27,19 +66,31 @@ struct HostDeviceKey {
|
||||
}
|
||||
};
|
||||
|
||||
extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets;
|
||||
|
||||
/**
|
||||
* Generates a new device id, which uniquely identifies a host device within KeyMap.
|
||||
*/
|
||||
int NewDeviceId();
|
||||
|
||||
/**
|
||||
* Maps a device-specific key to a PadState.
|
||||
* Maps a device-specific key to a target (a PadState or an IndirectTarget).
|
||||
*/
|
||||
void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState);
|
||||
void SetKeyMapping(HostDeviceKey key, KeyTarget target);
|
||||
|
||||
/**
|
||||
* Gets the PadState that's mapped to the provided device-specific key.
|
||||
* Clears all key mappings belonging to one device.
|
||||
*/
|
||||
Service::HID::PadState GetPadKey(HostDeviceKey key);
|
||||
void ClearKeyMapping(int device_id);
|
||||
|
||||
/**
|
||||
* Maps a key press action and call the corresponding function in EmuWindow
|
||||
*/
|
||||
void PressKey(EmuWindow& emu_window, HostDeviceKey key);
|
||||
|
||||
/**
|
||||
* Maps a key release action and call the corresponding function in EmuWindow
|
||||
*/
|
||||
void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key);
|
||||
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ Entry CreateEntry(Class log_class, Level log_level,
|
||||
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
|
||||
entry.message = std::string(formatting_buffer.data());
|
||||
|
||||
return std::move(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static Filter* filter = nullptr;
|
||||
|
@ -5,6 +5,7 @@ set(SRCS
|
||||
arm/dyncom/arm_dyncom_dec.cpp
|
||||
arm/dyncom/arm_dyncom_interpreter.cpp
|
||||
arm/dyncom/arm_dyncom_thumb.cpp
|
||||
arm/dyncom/arm_dyncom_trans.cpp
|
||||
arm/skyeye_common/armstate.cpp
|
||||
arm/skyeye_common/armsupp.cpp
|
||||
arm/skyeye_common/vfp/vfp.cpp
|
||||
@ -26,9 +27,11 @@ set(SRCS
|
||||
hle/config_mem.cpp
|
||||
hle/hle.cpp
|
||||
hle/applets/applet.cpp
|
||||
hle/applets/erreula.cpp
|
||||
hle/applets/mii_selector.cpp
|
||||
hle/applets/swkbd.cpp
|
||||
hle/kernel/address_arbiter.cpp
|
||||
hle/kernel/client_port.cpp
|
||||
hle/kernel/event.cpp
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/memory.cpp
|
||||
@ -36,6 +39,7 @@ set(SRCS
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/resource_limit.cpp
|
||||
hle/kernel/semaphore.cpp
|
||||
hle/kernel/server_port.cpp
|
||||
hle/kernel/session.cpp
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/thread.cpp
|
||||
@ -70,7 +74,10 @@ set(SRCS
|
||||
hle/service/cfg/cfg_s.cpp
|
||||
hle/service/cfg/cfg_u.cpp
|
||||
hle/service/csnd_snd.cpp
|
||||
hle/service/dlp_srvr.cpp
|
||||
hle/service/dlp/dlp.cpp
|
||||
hle/service/dlp/dlp_clnt.cpp
|
||||
hle/service/dlp/dlp_fkcl.cpp
|
||||
hle/service/dlp/dlp_srvr.cpp
|
||||
hle/service/dsp_dsp.cpp
|
||||
hle/service/err_f.cpp
|
||||
hle/service/frd/frd.cpp
|
||||
@ -121,6 +128,7 @@ set(SRCS
|
||||
loader/elf.cpp
|
||||
loader/loader.cpp
|
||||
loader/ncch.cpp
|
||||
loader/smdh.cpp
|
||||
tracer/recorder.cpp
|
||||
memory.cpp
|
||||
settings.cpp
|
||||
@ -136,6 +144,7 @@ set(HEADERS
|
||||
arm/dyncom/arm_dyncom_interpreter.h
|
||||
arm/dyncom/arm_dyncom_run.h
|
||||
arm/dyncom/arm_dyncom_thumb.h
|
||||
arm/dyncom/arm_dyncom_trans.h
|
||||
arm/skyeye_common/arm_regformat.h
|
||||
arm/skyeye_common/armstate.h
|
||||
arm/skyeye_common/armsupp.h
|
||||
@ -160,9 +169,11 @@ set(HEADERS
|
||||
hle/function_wrappers.h
|
||||
hle/hle.h
|
||||
hle/applets/applet.h
|
||||
hle/applets/erreula.h
|
||||
hle/applets/mii_selector.h
|
||||
hle/applets/swkbd.h
|
||||
hle/kernel/address_arbiter.h
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/event.h
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory.h
|
||||
@ -170,6 +181,7 @@ set(HEADERS
|
||||
hle/kernel/process.h
|
||||
hle/kernel/resource_limit.h
|
||||
hle/kernel/semaphore.h
|
||||
hle/kernel/server_port.h
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/thread.h
|
||||
@ -205,7 +217,10 @@ set(HEADERS
|
||||
hle/service/cfg/cfg_s.h
|
||||
hle/service/cfg/cfg_u.h
|
||||
hle/service/csnd_snd.h
|
||||
hle/service/dlp_srvr.h
|
||||
hle/service/dlp/dlp.h
|
||||
hle/service/dlp/dlp_clnt.h
|
||||
hle/service/dlp/dlp_fkcl.h
|
||||
hle/service/dlp/dlp_srvr.h
|
||||
hle/service/dsp_dsp.h
|
||||
hle/service/err_f.h
|
||||
hle/service/frd/frd.h
|
||||
@ -256,6 +271,7 @@ set(HEADERS
|
||||
loader/elf.h
|
||||
loader/loader.h
|
||||
loader/ncch.h
|
||||
loader/smdh.h
|
||||
tracer/recorder.h
|
||||
tracer/citrace.h
|
||||
memory.h
|
||||
|
@ -422,6 +422,10 @@ ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) {
|
||||
n = arm_instruction[i].attribute_value;
|
||||
base = 0;
|
||||
|
||||
// 3DS has no VFP3 support
|
||||
if (arm_instruction[i].version == ARMVFP3)
|
||||
continue;
|
||||
|
||||
while (n) {
|
||||
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
|
||||
// clrex
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
|
||||
// ARM instruction, and using the existing ARM simulator.
|
||||
|
||||
@ -293,15 +295,22 @@ ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u3
|
||||
| (BIT(tinstr, 4) << 18); // enable bit
|
||||
}
|
||||
} else if ((tinstr & 0x0F00) == 0x0a00) {
|
||||
static const u32 subset[3] = {
|
||||
static const u32 subset[4] = {
|
||||
0xE6BF0F30, // REV
|
||||
0xE6BF0FB0, // REV16
|
||||
0, // undefined
|
||||
0xE6FF0FB0, // REVSH
|
||||
};
|
||||
|
||||
*ainstr = subset[BITS(tinstr, 6, 7)] // base
|
||||
| (BITS(tinstr, 0, 2) << 12) // Rd
|
||||
| BITS(tinstr, 3, 5); // Rm
|
||||
size_t subset_index = BITS(tinstr, 6, 7);
|
||||
|
||||
if (subset_index == 2) {
|
||||
valid = ThumbDecodeStatus::UNDEFINED;
|
||||
} else {
|
||||
*ainstr = subset[subset_index] // base
|
||||
| (BITS(tinstr, 0, 2) << 12) // Rd
|
||||
| BITS(tinstr, 3, 5); // Rm
|
||||
}
|
||||
} else {
|
||||
static const u32 subset[4] = {
|
||||
0xE92D0000, // STMDB sp!,{rlist}
|
||||
|
2178
src/core/arm/dyncom/arm_dyncom_trans.cpp
Normal file
2178
src/core/arm/dyncom/arm_dyncom_trans.cpp
Normal file
File diff suppressed because it is too large
Load Diff
493
src/core/arm/dyncom/arm_dyncom_trans.h
Normal file
493
src/core/arm/dyncom/arm_dyncom_trans.h
Normal file
@ -0,0 +1,493 @@
|
||||
struct ARMul_State;
|
||||
typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper);
|
||||
|
||||
enum class TransExtData {
|
||||
COND = (1 << 0),
|
||||
NON_BRANCH = (1 << 1),
|
||||
DIRECT_BRANCH = (1 << 2),
|
||||
INDIRECT_BRANCH = (1 << 3),
|
||||
CALL = (1 << 4),
|
||||
RET = (1 << 5),
|
||||
END_OF_PAGE = (1 << 6),
|
||||
THUMB = (1 << 7),
|
||||
SINGLE_STEP = (1 << 8)
|
||||
};
|
||||
|
||||
struct arm_inst {
|
||||
unsigned int idx;
|
||||
unsigned int cond;
|
||||
TransExtData br;
|
||||
char component[0];
|
||||
};
|
||||
|
||||
struct generic_arm_inst {
|
||||
u32 Ra;
|
||||
u32 Rm;
|
||||
u32 Rn;
|
||||
u32 Rd;
|
||||
u8 op1;
|
||||
u8 op2;
|
||||
};
|
||||
|
||||
struct adc_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct add_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct orr_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct and_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct eor_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct bbl_inst {
|
||||
unsigned int L;
|
||||
int signed_immed_24;
|
||||
unsigned int next_addr;
|
||||
unsigned int jmp_addr;
|
||||
};
|
||||
|
||||
struct bx_inst {
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct blx_inst {
|
||||
union {
|
||||
s32 signed_immed_24;
|
||||
u32 Rm;
|
||||
} val;
|
||||
unsigned int inst;
|
||||
};
|
||||
|
||||
struct clz_inst {
|
||||
unsigned int Rm;
|
||||
unsigned int Rd;
|
||||
};
|
||||
|
||||
struct cps_inst {
|
||||
unsigned int imod0;
|
||||
unsigned int imod1;
|
||||
unsigned int mmod;
|
||||
unsigned int A, I, F;
|
||||
unsigned int mode;
|
||||
};
|
||||
|
||||
struct clrex_inst {
|
||||
};
|
||||
|
||||
struct cpy_inst {
|
||||
unsigned int Rm;
|
||||
unsigned int Rd;
|
||||
};
|
||||
|
||||
struct bic_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct sub_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct tst_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct cmn_inst {
|
||||
unsigned int I;
|
||||
unsigned int Rn;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct teq_inst {
|
||||
unsigned int I;
|
||||
unsigned int Rn;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct stm_inst {
|
||||
unsigned int inst;
|
||||
};
|
||||
|
||||
struct bkpt_inst {
|
||||
u32 imm;
|
||||
};
|
||||
|
||||
struct stc_inst {
|
||||
};
|
||||
|
||||
struct ldc_inst {
|
||||
};
|
||||
|
||||
struct swi_inst {
|
||||
unsigned int num;
|
||||
};
|
||||
|
||||
struct cmp_inst {
|
||||
unsigned int I;
|
||||
unsigned int Rn;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct mov_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct mvn_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct rev_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rm;
|
||||
unsigned int op1;
|
||||
unsigned int op2;
|
||||
};
|
||||
|
||||
struct rsb_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct rsc_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct sbc_inst {
|
||||
unsigned int I;
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int shifter_operand;
|
||||
shtop_fp_t shtop_func;
|
||||
};
|
||||
|
||||
struct mul_inst {
|
||||
unsigned int S;
|
||||
unsigned int Rd;
|
||||
unsigned int Rs;
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct smul_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rs;
|
||||
unsigned int Rm;
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
};
|
||||
|
||||
struct umull_inst {
|
||||
unsigned int S;
|
||||
unsigned int RdHi;
|
||||
unsigned int RdLo;
|
||||
unsigned int Rs;
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct smlad_inst {
|
||||
unsigned int m;
|
||||
unsigned int Rm;
|
||||
unsigned int Rd;
|
||||
unsigned int Ra;
|
||||
unsigned int Rn;
|
||||
unsigned int op1;
|
||||
unsigned int op2;
|
||||
};
|
||||
|
||||
struct smla_inst {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int Rm;
|
||||
unsigned int Rd;
|
||||
unsigned int Rs;
|
||||
unsigned int Rn;
|
||||
};
|
||||
|
||||
struct smlalxy_inst {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int RdLo;
|
||||
unsigned int RdHi;
|
||||
unsigned int Rm;
|
||||
unsigned int Rn;
|
||||
};
|
||||
|
||||
struct ssat_inst {
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int imm5;
|
||||
unsigned int sat_imm;
|
||||
unsigned int shift_type;
|
||||
};
|
||||
|
||||
struct umaal_inst {
|
||||
unsigned int Rn;
|
||||
unsigned int Rm;
|
||||
unsigned int RdHi;
|
||||
unsigned int RdLo;
|
||||
};
|
||||
|
||||
struct umlal_inst {
|
||||
unsigned int S;
|
||||
unsigned int Rm;
|
||||
unsigned int Rs;
|
||||
unsigned int RdHi;
|
||||
unsigned int RdLo;
|
||||
};
|
||||
|
||||
struct smlal_inst {
|
||||
unsigned int S;
|
||||
unsigned int Rm;
|
||||
unsigned int Rs;
|
||||
unsigned int RdHi;
|
||||
unsigned int RdLo;
|
||||
};
|
||||
|
||||
struct smlald_inst {
|
||||
unsigned int RdLo;
|
||||
unsigned int RdHi;
|
||||
unsigned int Rm;
|
||||
unsigned int Rn;
|
||||
unsigned int swap;
|
||||
unsigned int op1;
|
||||
unsigned int op2;
|
||||
};
|
||||
|
||||
struct mla_inst {
|
||||
unsigned int S;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int Rs;
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct mrc_inst {
|
||||
unsigned int opcode_1;
|
||||
unsigned int opcode_2;
|
||||
unsigned int cp_num;
|
||||
unsigned int crn;
|
||||
unsigned int crm;
|
||||
unsigned int Rd;
|
||||
unsigned int inst;
|
||||
};
|
||||
|
||||
struct mcr_inst {
|
||||
unsigned int opcode_1;
|
||||
unsigned int opcode_2;
|
||||
unsigned int cp_num;
|
||||
unsigned int crn;
|
||||
unsigned int crm;
|
||||
unsigned int Rd;
|
||||
unsigned int inst;
|
||||
};
|
||||
|
||||
struct mcrr_inst {
|
||||
unsigned int opcode_1;
|
||||
unsigned int cp_num;
|
||||
unsigned int crm;
|
||||
unsigned int rt;
|
||||
unsigned int rt2;
|
||||
};
|
||||
|
||||
struct mrs_inst {
|
||||
unsigned int R;
|
||||
unsigned int Rd;
|
||||
};
|
||||
|
||||
struct msr_inst {
|
||||
unsigned int field_mask;
|
||||
unsigned int R;
|
||||
unsigned int inst;
|
||||
};
|
||||
|
||||
struct pld_inst {
|
||||
};
|
||||
|
||||
struct sxtb_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rm;
|
||||
unsigned int rotate;
|
||||
};
|
||||
|
||||
struct sxtab_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rn;
|
||||
unsigned int Rm;
|
||||
unsigned rotate;
|
||||
};
|
||||
|
||||
struct sxtah_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rn;
|
||||
unsigned int Rm;
|
||||
unsigned int rotate;
|
||||
};
|
||||
|
||||
struct sxth_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rm;
|
||||
unsigned int rotate;
|
||||
};
|
||||
|
||||
struct uxtab_inst {
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int rotate;
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct uxtah_inst {
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int rotate;
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct uxth_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rm;
|
||||
unsigned int rotate;
|
||||
};
|
||||
|
||||
struct cdp_inst {
|
||||
unsigned int opcode_1;
|
||||
unsigned int CRn;
|
||||
unsigned int CRd;
|
||||
unsigned int cp_num;
|
||||
unsigned int opcode_2;
|
||||
unsigned int CRm;
|
||||
unsigned int inst;
|
||||
};
|
||||
|
||||
struct uxtb_inst {
|
||||
unsigned int Rd;
|
||||
unsigned int Rm;
|
||||
unsigned int rotate;
|
||||
};
|
||||
|
||||
struct swp_inst {
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned int Rm;
|
||||
};
|
||||
|
||||
struct setend_inst {
|
||||
unsigned int set_bigend;
|
||||
};
|
||||
|
||||
struct b_2_thumb {
|
||||
unsigned int imm;
|
||||
};
|
||||
struct b_cond_thumb {
|
||||
unsigned int imm;
|
||||
unsigned int cond;
|
||||
};
|
||||
|
||||
struct bl_1_thumb {
|
||||
unsigned int imm;
|
||||
};
|
||||
struct bl_2_thumb {
|
||||
unsigned int imm;
|
||||
};
|
||||
struct blx_1_thumb {
|
||||
unsigned int imm;
|
||||
unsigned int instr;
|
||||
};
|
||||
|
||||
struct pkh_inst {
|
||||
unsigned int Rm;
|
||||
unsigned int Rn;
|
||||
unsigned int Rd;
|
||||
unsigned char imm;
|
||||
};
|
||||
|
||||
// Floating point VFPv3 structures
|
||||
#define VFP_INTERPRETER_STRUCT
|
||||
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
|
||||
#undef VFP_INTERPRETER_STRUCT
|
||||
|
||||
typedef void (*get_addr_fp_t)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr);
|
||||
|
||||
struct ldst_inst {
|
||||
unsigned int inst;
|
||||
get_addr_fp_t get_addr;
|
||||
};
|
||||
|
||||
typedef arm_inst* ARM_INST_PTR;
|
||||
typedef ARM_INST_PTR (*transop_fp_t)(unsigned int, int);
|
||||
|
||||
extern const transop_fp_t arm_instruction_trans[];
|
||||
extern const size_t arm_instruction_trans_len;
|
||||
|
||||
#define TRANS_CACHE_SIZE (64 * 1024 * 2000)
|
||||
extern char trans_cache_buf[TRANS_CACHE_SIZE];
|
||||
extern size_t trans_cache_buf_top;
|
@ -271,8 +271,9 @@ inline int vfp_single_type(const vfp_single* s)
|
||||
// Unpack a single-precision float. Note that this returns the magnitude
|
||||
// of the single-precision float mantissa with the 1. if necessary,
|
||||
// aligned to bit 30.
|
||||
inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
|
||||
inline u32 vfp_single_unpack(vfp_single* s, s32 val, u32 fpscr)
|
||||
{
|
||||
u32 exceptions = 0;
|
||||
s->sign = vfp_single_packed_sign(val) >> 16,
|
||||
s->exponent = vfp_single_packed_exponent(val);
|
||||
|
||||
@ -283,12 +284,13 @@ inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
|
||||
|
||||
// If flush-to-zero mode is enabled, turn the denormal into zero.
|
||||
// On a VFPv2 architecture, the sign of the zero is always positive.
|
||||
if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) {
|
||||
if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) {
|
||||
s->sign = 0;
|
||||
s->exponent = 0;
|
||||
s->significand = 0;
|
||||
*fpscr |= FPSCR_IDC;
|
||||
exceptions |= FPSCR_IDC;
|
||||
}
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
// Re-pack a single-precision float. This assumes that the float is
|
||||
@ -302,7 +304,7 @@ inline s32 vfp_single_pack(const vfp_single* s)
|
||||
}
|
||||
|
||||
|
||||
u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, u32 exceptions, const char* func);
|
||||
u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, const char* func);
|
||||
|
||||
// Double-precision
|
||||
struct vfp_double {
|
||||
@ -357,8 +359,9 @@ inline int vfp_double_type(const vfp_double* s)
|
||||
// Unpack a double-precision float. Note that this returns the magnitude
|
||||
// of the double-precision float mantissa with the 1. if necessary,
|
||||
// aligned to bit 62.
|
||||
inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
|
||||
inline u32 vfp_double_unpack(vfp_double* s, s64 val, u32 fpscr)
|
||||
{
|
||||
u32 exceptions = 0;
|
||||
s->sign = vfp_double_packed_sign(val) >> 48;
|
||||
s->exponent = vfp_double_packed_exponent(val);
|
||||
|
||||
@ -369,12 +372,13 @@ inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
|
||||
|
||||
// If flush-to-zero mode is enabled, turn the denormal into zero.
|
||||
// On a VFPv2 architecture, the sign of the zero is always positive.
|
||||
if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) {
|
||||
if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) {
|
||||
s->sign = 0;
|
||||
s->exponent = 0;
|
||||
s->significand = 0;
|
||||
*fpscr |= FPSCR_IDC;
|
||||
exceptions |= FPSCR_IDC;
|
||||
}
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
// Re-pack a double-precision float. This assumes that the float is
|
||||
@ -447,4 +451,4 @@ inline u32 fls(u32 x)
|
||||
|
||||
u32 vfp_double_multiply(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr);
|
||||
u32 vfp_double_add(vfp_double* vdd, vfp_double* vdn, vfp_double *vdm, u32 fpscr);
|
||||
u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, u32 exceptions, const char* func);
|
||||
u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, const char* func);
|
||||
|
@ -85,11 +85,12 @@ static void vfp_double_normalise_denormal(struct vfp_double *vd)
|
||||
vfp_double_dump("normalise_denormal: out", vd);
|
||||
}
|
||||
|
||||
u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func)
|
||||
u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, const char *func)
|
||||
{
|
||||
u64 significand, incr;
|
||||
int exponent, shift, underflow;
|
||||
u32 rmode;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vfp_double_dump("pack: in", vd);
|
||||
|
||||
@ -291,8 +292,9 @@ static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double vdm, vdd, *vdp;
|
||||
int ret, tm;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
|
||||
tm = vfp_double_type(&vdm);
|
||||
if (tm & (VFP_NAN|VFP_INFINITY)) {
|
||||
@ -369,7 +371,8 @@ sqrt_invalid:
|
||||
}
|
||||
vdd.significand = vfp_shiftright64jamming(vdd.significand, 1);
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fsqrt");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsqrt");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -475,7 +478,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
u32 exceptions = 0;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
|
||||
tm = vfp_double_type(&vdm);
|
||||
|
||||
@ -504,7 +507,8 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
else
|
||||
vsd.exponent = vdm.exponent - (1023 - 127);
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fcvts");
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fcvts");
|
||||
return exceptions;
|
||||
|
||||
pack_nan:
|
||||
vfp_put_float(state, vfp_single_pack(&vsd), sd);
|
||||
@ -514,6 +518,7 @@ pack_nan:
|
||||
static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
|
||||
{
|
||||
struct vfp_double vdm;
|
||||
u32 exceptions = 0;
|
||||
u32 m = vfp_get_float(state, dm);
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
@ -521,12 +526,14 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32
|
||||
vdm.exponent = 1023 + 63 - 1;
|
||||
vdm.significand = (u64)m;
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fuito");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fuito");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
|
||||
{
|
||||
struct vfp_double vdm;
|
||||
u32 exceptions = 0;
|
||||
u32 m = vfp_get_float(state, dm);
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
@ -534,7 +541,8 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32
|
||||
vdm.exponent = 1023 + 63 - 1;
|
||||
vdm.significand = vdm.sign ? (~m + 1) : m;
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fsito");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fsito");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
|
||||
@ -545,7 +553,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
int tm;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
|
||||
/*
|
||||
* Do we have a denormalised number?
|
||||
@ -560,7 +568,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
if (vdm.exponent >= 1023 + 32) {
|
||||
d = vdm.sign ? 0 : 0xffffffff;
|
||||
exceptions = FPSCR_IOC;
|
||||
} else if (vdm.exponent >= 1023 - 1) {
|
||||
} else if (vdm.exponent >= 1023) {
|
||||
int shift = 1023 + 63 - vdm.exponent;
|
||||
u64 rem, incr = 0;
|
||||
|
||||
@ -595,12 +603,20 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
} else {
|
||||
d = 0;
|
||||
if (vdm.exponent | vdm.significand) {
|
||||
exceptions |= FPSCR_IXC;
|
||||
if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
|
||||
if (rmode == FPSCR_ROUND_NEAREST) {
|
||||
if (vdm.exponent >= 1022) {
|
||||
d = vdm.sign ? 0 : 1;
|
||||
exceptions |= vdm.sign ? FPSCR_IOC : FPSCR_IXC;
|
||||
} else {
|
||||
exceptions |= FPSCR_IXC;
|
||||
}
|
||||
} else if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) {
|
||||
d = 1;
|
||||
else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
|
||||
d = 0;
|
||||
exceptions |= FPSCR_IOC;
|
||||
exceptions |= FPSCR_IXC;
|
||||
} else if (rmode == FPSCR_ROUND_MINUSINF) {
|
||||
exceptions |= vdm.sign ? FPSCR_IOC : FPSCR_IXC;
|
||||
} else {
|
||||
exceptions |= FPSCR_IXC;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -615,7 +631,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
|
||||
{
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
|
||||
return vfp_double_ftoui(state, sd, unused, dm, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
|
||||
}
|
||||
|
||||
static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
|
||||
@ -626,7 +642,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
int tm;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
vfp_double_dump("VDM", &vdm);
|
||||
|
||||
/*
|
||||
@ -639,12 +655,12 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
if (tm & VFP_NAN) {
|
||||
d = 0;
|
||||
exceptions |= FPSCR_IOC;
|
||||
} else if (vdm.exponent >= 1023 + 32) {
|
||||
} else if (vdm.exponent >= 1023 + 31) {
|
||||
d = 0x7fffffff;
|
||||
if (vdm.sign)
|
||||
d = ~d;
|
||||
exceptions |= FPSCR_IOC;
|
||||
} else if (vdm.exponent >= 1023 - 1) {
|
||||
} else if (vdm.exponent >= 1023) {
|
||||
int shift = 1023 + 63 - vdm.exponent; /* 58 */
|
||||
u64 rem, incr = 0;
|
||||
|
||||
@ -675,10 +691,17 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
d = 0;
|
||||
if (vdm.exponent | vdm.significand) {
|
||||
exceptions |= FPSCR_IXC;
|
||||
if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
|
||||
if (rmode == FPSCR_ROUND_NEAREST) {
|
||||
if (vdm.exponent >= 1022) {
|
||||
d = vdm.sign ? 0xffffffff : 1;
|
||||
} else {
|
||||
d = 0;
|
||||
}
|
||||
} else if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) {
|
||||
d = 1;
|
||||
else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
|
||||
d = -1;
|
||||
} else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
|
||||
d = 0xffffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,7 +715,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
|
||||
static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
|
||||
{
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
|
||||
return vfp_double_ftosi(state, dd, unused, dm, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
|
||||
}
|
||||
|
||||
static struct op fops_ext[] = {
|
||||
@ -892,21 +915,21 @@ static u32
|
||||
vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 fpscr, u32 negate, const char *func)
|
||||
{
|
||||
struct vfp_double vdd, vdp, vdn, vdm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
|
||||
if (vdn.exponent == 0 && vdn.significand)
|
||||
vfp_double_normalise_denormal(&vdn);
|
||||
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
if (vdm.exponent == 0 && vdm.significand)
|
||||
vfp_double_normalise_denormal(&vdm);
|
||||
|
||||
exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
|
||||
exceptions |= vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
|
||||
if (negate & NEG_MULTIPLY)
|
||||
vdp.sign = vfp_sign_negate(vdp.sign);
|
||||
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dd), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dd), fpscr);
|
||||
if (vdn.exponent == 0 && vdn.significand != 0)
|
||||
vfp_double_normalise_denormal(&vdn);
|
||||
|
||||
@ -915,7 +938,8 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f
|
||||
|
||||
exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, func);
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, func);
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -964,19 +988,21 @@ static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
|
||||
static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
|
||||
{
|
||||
struct vfp_double vdd, vdn, vdm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
|
||||
if (vdn.exponent == 0 && vdn.significand)
|
||||
vfp_double_normalise_denormal(&vdn);
|
||||
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
if (vdm.exponent == 0 && vdm.significand)
|
||||
vfp_double_normalise_denormal(&vdm);
|
||||
|
||||
exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fmul");
|
||||
exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
|
||||
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fmul");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -985,21 +1011,22 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
|
||||
static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
|
||||
{
|
||||
struct vfp_double vdd, vdn, vdm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
|
||||
if (vdn.exponent == 0 && vdn.significand)
|
||||
vfp_double_normalise_denormal(&vdn);
|
||||
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
if (vdm.exponent == 0 && vdm.significand)
|
||||
vfp_double_normalise_denormal(&vdm);
|
||||
|
||||
exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
|
||||
exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
|
||||
vdd.sign = vfp_sign_negate(vdd.sign);
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fnmul");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fnmul");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1008,20 +1035,21 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
|
||||
static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
|
||||
{
|
||||
struct vfp_double vdd, vdn, vdm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
|
||||
if (vdn.exponent == 0 && vdn.significand)
|
||||
vfp_double_normalise_denormal(&vdn);
|
||||
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
if (vdm.exponent == 0 && vdm.significand)
|
||||
vfp_double_normalise_denormal(&vdm);
|
||||
|
||||
exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
|
||||
exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr);
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fadd");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fadd");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1030,14 +1058,14 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
|
||||
static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
|
||||
{
|
||||
struct vfp_double vdd, vdn, vdm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
|
||||
if (vdn.exponent == 0 && vdn.significand)
|
||||
vfp_double_normalise_denormal(&vdn);
|
||||
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
if (vdm.exponent == 0 && vdm.significand)
|
||||
vfp_double_normalise_denormal(&vdm);
|
||||
|
||||
@ -1046,9 +1074,10 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
|
||||
*/
|
||||
vdm.sign = vfp_sign_negate(vdm.sign);
|
||||
|
||||
exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
|
||||
exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr);
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fsub");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsub");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1061,8 +1090,8 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
|
||||
int tm, tn;
|
||||
|
||||
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
|
||||
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
|
||||
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr);
|
||||
exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
|
||||
|
||||
vdd.sign = vdn.sign ^ vdm.sign;
|
||||
|
||||
@ -1131,16 +1160,18 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
|
||||
}
|
||||
vdd.significand |= (reml != 0);
|
||||
}
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fdiv");
|
||||
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fdiv");
|
||||
return exceptions;
|
||||
|
||||
vdn_nan:
|
||||
exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
|
||||
exceptions |= vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
|
||||
pack:
|
||||
vfp_put_double(state, vfp_double_pack(&vdd), dd);
|
||||
return exceptions;
|
||||
|
||||
vdm_nan:
|
||||
exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
|
||||
exceptions |= vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
|
||||
goto pack;
|
||||
|
||||
zero:
|
||||
@ -1149,7 +1180,7 @@ zero:
|
||||
goto pack;
|
||||
|
||||
divzero:
|
||||
exceptions = FPSCR_DZC;
|
||||
exceptions |= FPSCR_DZC;
|
||||
infinity:
|
||||
vdd.exponent = 2047;
|
||||
vdd.significand = 0;
|
||||
@ -1157,7 +1188,8 @@ infinity:
|
||||
|
||||
invalid:
|
||||
vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd);
|
||||
return FPSCR_IOC;
|
||||
exceptions |= FPSCR_IOC;
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
static struct op fops[] = {
|
||||
|
@ -26,7 +26,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmla)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -75,7 +75,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmls)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -124,7 +124,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmla)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -174,7 +174,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmls)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -223,7 +223,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmul)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -272,7 +272,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmul)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -321,7 +321,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vadd)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -370,7 +370,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vsub)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -419,7 +419,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vdiv)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -470,7 +470,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovi)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->d = (inst_cream->single ? BITS(inst,12,15)<<1 | BIT(inst,22) : BITS(inst,12,15) | BIT(inst,22)<<4);
|
||||
@ -518,7 +518,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovr)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->d = (inst_cream->single ? BITS(inst,12,15)<<1 | BIT(inst,22) : BITS(inst,12,15) | BIT(inst,22)<<4);
|
||||
@ -560,7 +560,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vabs)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -610,7 +610,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vneg)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -659,7 +659,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vsqrt)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -708,7 +708,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcmp)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -757,7 +757,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcmp2)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -806,7 +806,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbds)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -857,7 +857,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbff)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -906,7 +906,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbfi)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->dp_operation = BIT(inst, 8);
|
||||
inst_cream->instr = inst;
|
||||
@ -962,7 +962,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrs)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->to_arm = BIT(inst, 20) == 1;
|
||||
inst_cream->t = BITS(inst, 12, 15);
|
||||
@ -1006,7 +1006,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->reg = BITS(inst, 16, 19);
|
||||
inst_cream->Rt = BITS(inst, 12, 15);
|
||||
@ -1069,7 +1069,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrc)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4;
|
||||
inst_cream->t = BITS(inst, 12, 15);
|
||||
@ -1115,7 +1115,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmrs)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->reg = BITS(inst, 16, 19);
|
||||
inst_cream->Rt = BITS(inst, 12, 15);
|
||||
@ -1200,7 +1200,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbcr)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4;
|
||||
inst_cream->t = BITS(inst, 12, 15);
|
||||
@ -1253,7 +1253,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrrss)(unsigned int inst, int inde
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->to_arm = BIT(inst, 20) == 1;
|
||||
inst_cream->t = BITS(inst, 12, 15);
|
||||
@ -1301,7 +1301,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrrd)(unsigned int inst, int index
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->to_arm = BIT(inst, 20) == 1;
|
||||
inst_cream->t = BITS(inst, 12, 15);
|
||||
@ -1354,7 +1354,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vstr)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->add = BIT(inst, 23);
|
||||
@ -1420,7 +1420,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vpush)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4);
|
||||
@ -1495,7 +1495,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vstm)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->add = BIT(inst, 23);
|
||||
@ -1580,7 +1580,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vpop)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->d = (inst_cream->single ? (BITS(inst, 12, 15)<<1)|BIT(inst, 22) : BITS(inst, 12, 15)|(BIT(inst, 22)<<4));
|
||||
@ -1653,7 +1653,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vldr)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->add = BIT(inst, 23);
|
||||
@ -1722,7 +1722,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vldm)(unsigned int inst, int index)
|
||||
|
||||
inst_base->cond = BITS(inst, 28, 31);
|
||||
inst_base->idx = index;
|
||||
inst_base->br = NON_BRANCH;
|
||||
inst_base->br = TransExtData::NON_BRANCH;
|
||||
|
||||
inst_cream->single = BIT(inst, 8) == 0;
|
||||
inst_cream->add = BIT(inst, 23);
|
||||
|
@ -89,10 +89,11 @@ static void vfp_single_normalise_denormal(struct vfp_single *vs)
|
||||
}
|
||||
|
||||
|
||||
u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func)
|
||||
u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, const char *func)
|
||||
{
|
||||
u32 significand, incr, rmode;
|
||||
int exponent, shift, underflow;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vfp_single_dump("pack: in", vs);
|
||||
|
||||
@ -334,8 +335,9 @@ static u32 vfp_single_fsqrt(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
{
|
||||
struct vfp_single vsm, vsd, *vsp;
|
||||
int ret, tm;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
tm = vfp_single_type(&vsm);
|
||||
if (tm & (VFP_NAN|VFP_INFINITY)) {
|
||||
vsp = &vsd;
|
||||
@ -408,7 +410,8 @@ sqrt_invalid:
|
||||
}
|
||||
vsd.significand = vfp_shiftright32jamming(vsd.significand, 1);
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fsqrt");
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fsqrt");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -503,7 +506,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f
|
||||
int tm;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
|
||||
tm = vfp_single_type(&vsm);
|
||||
|
||||
@ -511,7 +514,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f
|
||||
* If we have a signalling NaN, signal invalid operation.
|
||||
*/
|
||||
if (tm == VFP_SNAN)
|
||||
exceptions = FPSCR_IOC;
|
||||
exceptions |= FPSCR_IOC;
|
||||
|
||||
if (tm & VFP_DENORMAL)
|
||||
vfp_single_normalise_denormal(&vsm);
|
||||
@ -532,7 +535,8 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f
|
||||
else
|
||||
vdd.exponent = vsm.exponent + (1023 - 127);
|
||||
|
||||
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fcvtd");
|
||||
exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fcvtd");
|
||||
return exceptions;
|
||||
|
||||
pack_nan:
|
||||
vfp_put_double(state, vfp_double_pack(&vdd), dd);
|
||||
@ -542,23 +546,27 @@ pack_nan:
|
||||
static u32 vfp_single_fuito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
|
||||
{
|
||||
struct vfp_single vs;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vs.sign = 0;
|
||||
vs.exponent = 127 + 31 - 1;
|
||||
vs.significand = (u32)m;
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fuito");
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fuito");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
static u32 vfp_single_fsito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
|
||||
{
|
||||
struct vfp_single vs;
|
||||
u32 exceptions = 0;
|
||||
|
||||
vs.sign = (m & 0x80000000) >> 16;
|
||||
vs.exponent = 127 + 31 - 1;
|
||||
vs.significand = vs.sign ? -m : m;
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fsito");
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fsito");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
|
||||
@ -568,7 +576,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
int rmode = fpscr & FPSCR_RMODE_MASK;
|
||||
int tm;
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
vfp_single_dump("VSM", &vsm);
|
||||
|
||||
/*
|
||||
@ -583,7 +591,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
|
||||
if (vsm.exponent >= 127 + 32) {
|
||||
d = vsm.sign ? 0 : 0xffffffff;
|
||||
exceptions = FPSCR_IOC;
|
||||
exceptions |= FPSCR_IOC;
|
||||
} else if (vsm.exponent >= 127) {
|
||||
int shift = 127 + 31 - vsm.exponent;
|
||||
u32 rem, incr = 0;
|
||||
@ -592,7 +600,11 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
* 2^0 <= m < 2^32-2^8
|
||||
*/
|
||||
d = (vsm.significand << 1) >> shift;
|
||||
rem = vsm.significand << (33 - shift);
|
||||
if (shift > 0) {
|
||||
rem = (vsm.significand << 1) << (32 - shift);
|
||||
} else {
|
||||
rem = 0;
|
||||
}
|
||||
|
||||
if (rmode == FPSCR_ROUND_NEAREST) {
|
||||
incr = 0x80000000;
|
||||
@ -619,12 +631,20 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
} else {
|
||||
d = 0;
|
||||
if (vsm.exponent | vsm.significand) {
|
||||
exceptions |= FPSCR_IXC;
|
||||
if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
|
||||
if (rmode == FPSCR_ROUND_NEAREST) {
|
||||
if (vsm.exponent >= 126) {
|
||||
d = vsm.sign ? 0 : 1;
|
||||
exceptions |= vsm.sign ? FPSCR_IOC : FPSCR_IXC;
|
||||
} else {
|
||||
exceptions |= FPSCR_IXC;
|
||||
}
|
||||
} else if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) {
|
||||
d = 1;
|
||||
else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
|
||||
d = 0;
|
||||
exceptions |= FPSCR_IOC;
|
||||
exceptions |= FPSCR_IXC;
|
||||
} else if (rmode == FPSCR_ROUND_MINUSINF) {
|
||||
exceptions |= vsm.sign ? FPSCR_IOC : FPSCR_IXC;
|
||||
} else {
|
||||
exceptions |= FPSCR_IXC;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -638,7 +658,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
|
||||
static u32 vfp_single_ftouiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
|
||||
{
|
||||
return vfp_single_ftoui(state, sd, unused, m, FPSCR_ROUND_TOZERO);
|
||||
return vfp_single_ftoui(state, sd, unused, m, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
|
||||
}
|
||||
|
||||
static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
|
||||
@ -648,7 +668,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
int rmode = fpscr & FPSCR_RMODE_MASK;
|
||||
int tm;
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
vfp_single_dump("VSM", &vsm);
|
||||
|
||||
/*
|
||||
@ -661,7 +681,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
if (tm & VFP_NAN) {
|
||||
d = 0;
|
||||
exceptions |= FPSCR_IOC;
|
||||
} else if (vsm.exponent >= 127 + 32) {
|
||||
} else if (vsm.exponent >= 127 + 31) {
|
||||
/*
|
||||
* m >= 2^31-2^7: invalid
|
||||
*/
|
||||
@ -675,7 +695,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
|
||||
/* 2^0 <= m <= 2^31-2^7 */
|
||||
d = (vsm.significand << 1) >> shift;
|
||||
rem = vsm.significand << (33 - shift);
|
||||
rem = (vsm.significand << 1) << (32 - shift);
|
||||
|
||||
if (rmode == FPSCR_ROUND_NEAREST) {
|
||||
incr = 0x80000000;
|
||||
@ -701,10 +721,14 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
d = 0;
|
||||
if (vsm.exponent | vsm.significand) {
|
||||
exceptions |= FPSCR_IXC;
|
||||
if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
|
||||
if (rmode == FPSCR_ROUND_NEAREST) {
|
||||
if (vsm.exponent >= 126)
|
||||
d = vsm.sign ? 0xffffffff : 1;
|
||||
} else if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) {
|
||||
d = 1;
|
||||
else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
|
||||
d = -1;
|
||||
} else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
|
||||
d = 0xffffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -717,7 +741,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
|
||||
|
||||
static u32 vfp_single_ftosiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
|
||||
{
|
||||
return vfp_single_ftosi(state, sd, unused, m, FPSCR_ROUND_TOZERO);
|
||||
return vfp_single_ftosi(state, sd, unused, m, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
|
||||
}
|
||||
|
||||
static struct op fops_ext[] = {
|
||||
@ -774,7 +798,7 @@ vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
|
||||
/*
|
||||
* different signs -> invalid
|
||||
*/
|
||||
exceptions = FPSCR_IOC;
|
||||
exceptions |= FPSCR_IOC;
|
||||
vsp = &vfp_single_default_qnan;
|
||||
} else {
|
||||
/*
|
||||
@ -921,27 +945,27 @@ static u32
|
||||
vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func)
|
||||
{
|
||||
vfp_single vsd, vsp, vsn, vsm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
s32 v;
|
||||
|
||||
v = vfp_get_float(state, sn);
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, v);
|
||||
vfp_single_unpack(&vsn, v, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsn, v, fpscr);
|
||||
if (vsn.exponent == 0 && vsn.significand)
|
||||
vfp_single_normalise_denormal(&vsn);
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
if (vsm.exponent == 0 && vsm.significand)
|
||||
vfp_single_normalise_denormal(&vsm);
|
||||
|
||||
exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
|
||||
exceptions |= vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
|
||||
|
||||
if (negate & NEG_MULTIPLY)
|
||||
vsp.sign = vfp_sign_negate(vsp.sign);
|
||||
|
||||
v = vfp_get_float(state, sd);
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sd, v);
|
||||
vfp_single_unpack(&vsn, v, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsn, v, fpscr);
|
||||
if (vsn.exponent == 0 && vsn.significand != 0)
|
||||
vfp_single_normalise_denormal(&vsn);
|
||||
|
||||
@ -950,7 +974,8 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
|
||||
|
||||
exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, func);
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -962,8 +987,10 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp
|
||||
*/
|
||||
static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
{
|
||||
u32 exceptions = 0;
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd);
|
||||
return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
|
||||
exceptions |= vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1000,21 +1027,23 @@ static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
|
||||
static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
{
|
||||
struct vfp_single vsd, vsn, vsm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
s32 n = vfp_get_float(state, sn);
|
||||
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
|
||||
|
||||
vfp_single_unpack(&vsn, n, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsn, n, fpscr);
|
||||
if (vsn.exponent == 0 && vsn.significand)
|
||||
vfp_single_normalise_denormal(&vsn);
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
if (vsm.exponent == 0 && vsm.significand)
|
||||
vfp_single_normalise_denormal(&vsm);
|
||||
|
||||
exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fmul");
|
||||
exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
|
||||
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fmul");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1023,22 +1052,24 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
{
|
||||
struct vfp_single vsd, vsn, vsm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
s32 n = vfp_get_float(state, sn);
|
||||
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
|
||||
|
||||
vfp_single_unpack(&vsn, n, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsn, n, fpscr);
|
||||
if (vsn.exponent == 0 && vsn.significand)
|
||||
vfp_single_normalise_denormal(&vsn);
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
if (vsm.exponent == 0 && vsm.significand)
|
||||
vfp_single_normalise_denormal(&vsm);
|
||||
|
||||
exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
|
||||
exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
|
||||
vsd.sign = vfp_sign_negate(vsd.sign);
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fnmul");
|
||||
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fnmul");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1047,7 +1078,7 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr
|
||||
static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
{
|
||||
struct vfp_single vsd, vsn, vsm;
|
||||
u32 exceptions;
|
||||
u32 exceptions = 0;
|
||||
s32 n = vfp_get_float(state, sn);
|
||||
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
|
||||
@ -1055,17 +1086,18 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
/*
|
||||
* Unpack and normalise denormals.
|
||||
*/
|
||||
vfp_single_unpack(&vsn, n, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsn, n, fpscr);
|
||||
if (vsn.exponent == 0 && vsn.significand)
|
||||
vfp_single_normalise_denormal(&vsn);
|
||||
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
if (vsm.exponent == 0 && vsm.significand)
|
||||
vfp_single_normalise_denormal(&vsm);
|
||||
|
||||
exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr);
|
||||
exceptions |= vfp_single_add(&vsd, &vsn, &vsm, fpscr);
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fadd");
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fadd");
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1095,8 +1127,8 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
|
||||
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
|
||||
|
||||
vfp_single_unpack(&vsn, n, &fpscr);
|
||||
vfp_single_unpack(&vsm, m, &fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsn, n, fpscr);
|
||||
exceptions |= vfp_single_unpack(&vsm, m, fpscr);
|
||||
|
||||
vsd.sign = vsn.sign ^ vsm.sign;
|
||||
|
||||
@ -1162,16 +1194,17 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
|
||||
if ((vsd.significand & 0x3f) == 0)
|
||||
vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
|
||||
|
||||
return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fdiv");
|
||||
exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fdiv");
|
||||
return exceptions;
|
||||
|
||||
vsn_nan:
|
||||
exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
|
||||
exceptions |= vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
|
||||
pack:
|
||||
vfp_put_float(state, vfp_single_pack(&vsd), sd);
|
||||
return exceptions;
|
||||
|
||||
vsm_nan:
|
||||
exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
|
||||
exceptions |= vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
|
||||
goto pack;
|
||||
|
||||
zero:
|
||||
@ -1180,7 +1213,7 @@ zero:
|
||||
goto pack;
|
||||
|
||||
divzero:
|
||||
exceptions = FPSCR_DZC;
|
||||
exceptions |= FPSCR_DZC;
|
||||
infinity:
|
||||
vsd.exponent = 255;
|
||||
vsd.significand = 0;
|
||||
@ -1188,7 +1221,8 @@ infinity:
|
||||
|
||||
invalid:
|
||||
vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd);
|
||||
return FPSCR_IOC;
|
||||
exceptions |= FPSCR_IOC;
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
static struct op fops[] = {
|
||||
|
@ -26,7 +26,7 @@
|
||||
extern int g_clock_rate_arm11;
|
||||
|
||||
inline s64 msToCycles(int ms) {
|
||||
return g_clock_rate_arm11 / 1000 * ms;
|
||||
return (s64)g_clock_rate_arm11 / 1000 * ms;
|
||||
}
|
||||
|
||||
inline s64 msToCycles(float ms) {
|
||||
|
@ -19,22 +19,22 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
|
||||
switch (type) {
|
||||
case Binary:
|
||||
{
|
||||
u8* data = Memory::GetPointer(pointer);
|
||||
binary = std::vector<u8>(data, data + size);
|
||||
binary.resize(size);
|
||||
Memory::ReadBlock(pointer, binary.data(), binary.size());
|
||||
break;
|
||||
}
|
||||
|
||||
case Char:
|
||||
{
|
||||
const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
|
||||
string = std::string(data, size - 1); // Data is always null-terminated.
|
||||
string.resize(size - 1); // Data is always null-terminated.
|
||||
Memory::ReadBlock(pointer, &string[0], string.size());
|
||||
break;
|
||||
}
|
||||
|
||||
case Wchar:
|
||||
{
|
||||
const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
|
||||
u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
|
||||
u16str.resize(size / 2 - 1); // Data is always null-terminated.
|
||||
Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -646,7 +646,7 @@ static void ReadMemory() {
|
||||
|
||||
u8* data = Memory::GetPointer(addr);
|
||||
if (!data) {
|
||||
return SendReply("E0");
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
MemToGdbHex(reply, data, len);
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/applets/erreula.h"
|
||||
#include "core/hle/applets/mii_selector.h"
|
||||
#include "core/hle/applets/swkbd.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -52,6 +53,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) {
|
||||
case Service::APT::AppletId::Ed2:
|
||||
applets[id] = std::make_shared<MiiSelector>(id);
|
||||
break;
|
||||
case Service::APT::AppletId::Error:
|
||||
case Service::APT::AppletId::Error2:
|
||||
applets[id] = std::make_shared<ErrEula>(id);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_APT, "Could not create applet %u", id);
|
||||
// TODO(Subv): Find the right error code
|
||||
|
72
src/core/hle/applets/erreula.cpp
Normal file
72
src/core/hle/applets/erreula.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/hle/applets/erreula.h"
|
||||
#include "core/hle/service/apt/apt.h"
|
||||
|
||||
namespace HLE {
|
||||
namespace Applets {
|
||||
|
||||
ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
||||
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
|
||||
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
|
||||
UNIMPLEMENTED();
|
||||
// TODO(Subv): Find the right error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
// The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
|
||||
// Create the SharedMemory that will hold the framebuffer data
|
||||
Service::APT::CaptureBufferInfo capture_info;
|
||||
ASSERT(sizeof(capture_info) == parameter.buffer.size());
|
||||
|
||||
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
|
||||
|
||||
// TODO: allocated memory never released
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
|
||||
MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"ErrEula Memory");
|
||||
|
||||
// Send the response message with the newly created SharedMemory
|
||||
Service::APT::MessageParameter result;
|
||||
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
|
||||
result.buffer.clear();
|
||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
||||
result.sender_id = static_cast<u32>(id);
|
||||
result.object = framebuffer_memory;
|
||||
|
||||
Service::APT::SendParameter(result);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
|
||||
started = true;
|
||||
|
||||
// TODO(Subv): Set the expected fields in the response buffer before resending it to the application.
|
||||
// TODO(Subv): Reverse the parameter format for the ErrEula applet
|
||||
|
||||
// Let the application know that we're closing
|
||||
Service::APT::MessageParameter message;
|
||||
message.buffer.resize(parameter.buffer.size());
|
||||
std::fill(message.buffer.begin(), message.buffer.end(), 0);
|
||||
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
|
||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
||||
message.sender_id = static_cast<u32>(id);
|
||||
Service::APT::SendParameter(message);
|
||||
|
||||
started = false;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void ErrEula::Update() {
|
||||
}
|
||||
|
||||
} // namespace Applets
|
||||
} // namespace HLE
|
31
src/core/hle/applets/erreula.h
Normal file
31
src/core/hle/applets/erreula.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
|
||||
namespace HLE {
|
||||
namespace Applets {
|
||||
|
||||
class ErrEula final : public Applet {
|
||||
public:
|
||||
explicit ErrEula(Service::APT::AppletId id): Applet(id) { }
|
||||
|
||||
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
||||
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
||||
void Update() override;
|
||||
bool IsRunning() const override { return started; }
|
||||
|
||||
/// This SharedMemory will be created when we receive the LibAppJustStarted message.
|
||||
/// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
|
||||
private:
|
||||
/// Whether this applet is currently running instead of the host application or not.
|
||||
bool started = false;
|
||||
};
|
||||
|
||||
} // namespace Applets
|
||||
} // namespace HLE
|
@ -32,9 +32,9 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
|
||||
// The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
|
||||
// Create the SharedMemory that will hold the framebuffer data
|
||||
Service::APT::CaptureBufferInfo capture_info;
|
||||
ASSERT(sizeof(capture_info) == parameter.buffer_size);
|
||||
ASSERT(sizeof(capture_info) == parameter.buffer.size());
|
||||
|
||||
memcpy(&capture_info, parameter.data, sizeof(capture_info));
|
||||
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
|
||||
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
@ -47,8 +47,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
|
||||
// Send the response message with the newly created SharedMemory
|
||||
Service::APT::MessageParameter result;
|
||||
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
|
||||
result.data = nullptr;
|
||||
result.buffer_size = 0;
|
||||
result.buffer.clear();
|
||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
||||
result.sender_id = static_cast<u32>(id);
|
||||
result.object = framebuffer_memory;
|
||||
@ -63,15 +62,17 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
|
||||
// TODO(Subv): Set the expected fields in the response buffer before resending it to the application.
|
||||
// TODO(Subv): Reverse the parameter format for the Mii Selector
|
||||
|
||||
if(parameter.buffer_size >= sizeof(u32)) {
|
||||
// TODO: defaults return no error, but garbage in other unknown fields
|
||||
memset(parameter.data, 0, sizeof(u32));
|
||||
}
|
||||
memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
|
||||
|
||||
// TODO(Subv): Find more about this structure, result code 0 is enough to let most games continue.
|
||||
MiiResult result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
result.result_code = 0;
|
||||
|
||||
// Let the application know that we're closing
|
||||
Service::APT::MessageParameter message;
|
||||
message.buffer_size = parameter.buffer_size;
|
||||
message.data = parameter.data;
|
||||
message.buffer.resize(sizeof(MiiResult));
|
||||
std::memcpy(message.buffer.data(), &result, message.buffer.size());
|
||||
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
|
||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
||||
message.sender_id = static_cast<u32>(id);
|
||||
|
@ -24,7 +24,7 @@ struct MiiConfig {
|
||||
u8 unk_004;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u16 unk_008;
|
||||
INSERT_PADDING_BYTES(0x8C - 0xA);
|
||||
INSERT_PADDING_BYTES(0x82);
|
||||
u8 unk_08C;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u16 unk_090;
|
||||
@ -75,6 +75,8 @@ public:
|
||||
|
||||
/// Whether this applet is currently running instead of the host application or not.
|
||||
bool started;
|
||||
|
||||
MiiConfig config;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -35,9 +35,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
|
||||
// The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
|
||||
// Create the SharedMemory that will hold the framebuffer data
|
||||
Service::APT::CaptureBufferInfo capture_info;
|
||||
ASSERT(sizeof(capture_info) == parameter.buffer_size);
|
||||
ASSERT(sizeof(capture_info) == parameter.buffer.size());
|
||||
|
||||
memcpy(&capture_info, parameter.data, sizeof(capture_info));
|
||||
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
|
||||
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
@ -50,8 +50,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
|
||||
// Send the response message with the newly created SharedMemory
|
||||
Service::APT::MessageParameter result;
|
||||
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
|
||||
result.data = nullptr;
|
||||
result.buffer_size = 0;
|
||||
result.buffer.clear();
|
||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
||||
result.sender_id = static_cast<u32>(id);
|
||||
result.object = framebuffer_memory;
|
||||
@ -61,9 +60,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
|
||||
}
|
||||
|
||||
ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) {
|
||||
ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong");
|
||||
ASSERT_MSG(parameter.buffer.size() == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong");
|
||||
|
||||
memcpy(&config, parameter.data, parameter.buffer_size);
|
||||
memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
|
||||
text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object);
|
||||
|
||||
// TODO(Subv): Verify if this is the correct behavior
|
||||
@ -99,7 +98,7 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
|
||||
auto info = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
|
||||
// TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer
|
||||
memset(Memory::GetPointer(info.address_left), 0, info.stride * 320);
|
||||
Memory::ZeroBlock(info.address_left, info.stride * 320);
|
||||
|
||||
GSP_GPU::SetBufferSwap(1, info);
|
||||
}
|
||||
@ -107,8 +106,8 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
|
||||
void SoftwareKeyboard::Finalize() {
|
||||
// Let the application know that we're closing
|
||||
Service::APT::MessageParameter message;
|
||||
message.buffer_size = sizeof(SoftwareKeyboardConfig);
|
||||
message.data = reinterpret_cast<u8*>(&config);
|
||||
message.buffer.resize(sizeof(SoftwareKeyboardConfig));
|
||||
std::memcpy(message.buffer.data(), &config, message.buffer.size());
|
||||
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
|
||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
||||
message.sender_id = static_cast<u32>(id);
|
||||
|
@ -194,6 +194,16 @@ template<ResultCode func(Handle, u32)> void Wrap() {
|
||||
FuncReturn(func(PARAM(0), PARAM(1)).raw);
|
||||
}
|
||||
|
||||
template<ResultCode func(Handle*, Handle*, const char*, u32)> void Wrap() {
|
||||
Handle param_1 = 0;
|
||||
Handle param_2 = 0;
|
||||
u32 retval = func(¶m_1, ¶m_2, reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)).raw;
|
||||
// The first out parameter is moved into R2 and the second is moved into R1.
|
||||
Core::g_app_core->SetReg(1, param_2);
|
||||
Core::g_app_core->SetReg(2, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function wrappers that return type u32
|
||||
|
||||
|
16
src/core/hle/kernel/client_port.cpp
Normal file
16
src/core/hle/kernel/client_port.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientPort::ClientPort() {}
|
||||
ClientPort::~ClientPort() {}
|
||||
|
||||
} // namespace
|
36
src/core/hle/kernel/client_port.h
Normal file
36
src/core/hle/kernel/client_port.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ServerPort;
|
||||
|
||||
class ClientPort : public Object {
|
||||
public:
|
||||
friend class ServerPort;
|
||||
std::string GetTypeName() const override { return "ClientPort"; }
|
||||
std::string GetName() const override { return name; }
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ClientPort;
|
||||
HandleType GetHandleType() const override { return HANDLE_TYPE; }
|
||||
|
||||
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have
|
||||
u32 active_sessions; ///< Number of currently open sessions to this port
|
||||
std::string name; ///< Name of client port (optional)
|
||||
|
||||
protected:
|
||||
ClientPort();
|
||||
~ClientPort() override;
|
||||
};
|
||||
|
||||
} // namespace
|
@ -35,7 +35,7 @@ enum KernelHandle : Handle {
|
||||
|
||||
enum class HandleType : u32 {
|
||||
Unknown = 0,
|
||||
Port = 1,
|
||||
|
||||
Session = 2,
|
||||
Event = 3,
|
||||
Mutex = 4,
|
||||
@ -48,6 +48,8 @@ enum class HandleType : u32 {
|
||||
Timer = 11,
|
||||
ResourceLimit = 12,
|
||||
CodeSet = 13,
|
||||
ClientPort = 14,
|
||||
ServerPort = 15,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -72,6 +74,7 @@ public:
|
||||
bool IsWaitable() const {
|
||||
switch (GetHandleType()) {
|
||||
case HandleType::Session:
|
||||
case HandleType::ServerPort:
|
||||
case HandleType::Event:
|
||||
case HandleType::Mutex:
|
||||
case HandleType::Thread:
|
||||
@ -80,13 +83,13 @@ public:
|
||||
return true;
|
||||
|
||||
case HandleType::Unknown:
|
||||
case HandleType::Port:
|
||||
case HandleType::SharedMemory:
|
||||
case HandleType::Redirection:
|
||||
case HandleType::Process:
|
||||
case HandleType::AddressArbiter:
|
||||
case HandleType::ResourceLimit:
|
||||
case HandleType::CodeSet:
|
||||
case HandleType::ClientPort:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
41
src/core/hle/kernel/server_port.cpp
Normal file
41
src/core/hle/kernel/server_port.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ServerPort::ServerPort() {}
|
||||
ServerPort::~ServerPort() {}
|
||||
|
||||
bool ServerPort::ShouldWait() {
|
||||
// If there are no pending sessions, we wait until a new one is added.
|
||||
return pending_sessions.size() == 0;
|
||||
}
|
||||
|
||||
void ServerPort::Acquire() {
|
||||
ASSERT_MSG(!ShouldWait(), "object unavailable!");
|
||||
}
|
||||
|
||||
std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(u32 max_sessions, std::string name) {
|
||||
SharedPtr<ServerPort> server_port(new ServerPort);
|
||||
SharedPtr<ClientPort> client_port(new ClientPort);
|
||||
|
||||
server_port->name = name + "_Server";
|
||||
client_port->name = name + "_Client";
|
||||
client_port->server_port = server_port;
|
||||
client_port->max_sessions = max_sessions;
|
||||
client_port->active_sessions = 0;
|
||||
|
||||
return std::make_tuple(std::move(server_port), std::move(client_port));
|
||||
}
|
||||
|
||||
} // namespace
|
46
src/core/hle/kernel/server_port.h
Normal file
46
src/core/hle/kernel/server_port.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ClientPort;
|
||||
|
||||
class ServerPort final : public WaitObject {
|
||||
public:
|
||||
/**
|
||||
* Creates a pair of ServerPort and an associated ClientPort.
|
||||
* @param max_sessions Maximum number of sessions to the port
|
||||
* @param name Optional name of the ports
|
||||
* @return The created port tuple
|
||||
*/
|
||||
static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(u32 max_sessions, std::string name = "UnknownPort");
|
||||
|
||||
std::string GetTypeName() const override { return "ServerPort"; }
|
||||
std::string GetName() const override { return name; }
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ServerPort;
|
||||
HandleType GetHandleType() const override { return HANDLE_TYPE; }
|
||||
|
||||
std::string name; ///< Name of port (optional)
|
||||
|
||||
std::vector<SharedPtr<WaitObject>> pending_sessions; ///< ServerSessions waiting to be accepted by the port
|
||||
|
||||
bool ShouldWait() override;
|
||||
void Acquire() override;
|
||||
|
||||
private:
|
||||
ServerPort();
|
||||
~ServerPort() override;
|
||||
};
|
||||
|
||||
} // namespace
|
@ -32,6 +32,10 @@ constexpr u32 CallingPidDesc() {
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
constexpr u32 TransferHandleDesc() {
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
|
||||
return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10);
|
||||
}
|
||||
|
@ -181,6 +181,48 @@ static void PriorityBoostStarvedThreads() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registers for timeout parameter of the next WaitSynchronization call.
|
||||
* @param thread a pointer to the thread that is ready to call WaitSynchronization
|
||||
* @returns a tuple of two register pointers to low and high part of the timeout parameter
|
||||
*/
|
||||
static std::tuple<u32*, u32*> GetWaitSynchTimeoutParameterRegister(Thread* thread) {
|
||||
bool thumb_mode = (thread->context.cpsr & TBIT) != 0;
|
||||
u16 thumb_inst = Memory::Read16(thread->context.pc & 0xFFFFFFFE);
|
||||
u32 inst = Memory::Read32(thread->context.pc & 0xFFFFFFFC) & 0x0FFFFFFF;
|
||||
|
||||
if ((thumb_mode && thumb_inst == 0xDF24) || (!thumb_mode && inst == 0x0F000024)) {
|
||||
// svc #0x24 (WaitSynchronization1)
|
||||
return std::make_tuple(&thread->context.cpu_registers[2], &thread->context.cpu_registers[3]);
|
||||
} else if ((thumb_mode && thumb_inst == 0xDF25) || (!thumb_mode && inst == 0x0F000025)) {
|
||||
// svc #0x25 (WaitSynchronizationN)
|
||||
return std::make_tuple(&thread->context.cpu_registers[0], &thread->context.cpu_registers[4]);
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the WaitSynchronization timeout paramter according to the difference
|
||||
* between ticks of the last WaitSynchronization call and the incoming one.
|
||||
* @param timeout_low a pointer to the register for the low part of the timeout parameter
|
||||
* @param timeout_high a pointer to the register for the high part of the timeout parameter
|
||||
* @param last_tick tick of the last WaitSynchronization call
|
||||
*/
|
||||
static void UpdateTimeoutParameter(u32* timeout_low, u32* timeout_high, u64 last_tick) {
|
||||
s64 timeout = ((s64)*timeout_high << 32) | *timeout_low;
|
||||
|
||||
if (timeout != -1) {
|
||||
timeout -= cyclesToUs(CoreTiming::GetTicks() - last_tick) * 1000; // in nanoseconds
|
||||
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
|
||||
*timeout_low = timeout & 0xFFFFFFFF;
|
||||
*timeout_high = timeout >> 32;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the CPU's active thread context to that of the specified thread
|
||||
* @param new_thread The thread to switch to
|
||||
@ -219,6 +261,13 @@ static void SwitchContext(Thread* new_thread) {
|
||||
|
||||
// SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
|
||||
new_thread->context.pc -= thumb_mode ? 2 : 4;
|
||||
|
||||
// Get the register for timeout parameter
|
||||
u32* timeout_low, *timeout_high;
|
||||
std::tie(timeout_low, timeout_high) = GetWaitSynchTimeoutParameterRegister(new_thread);
|
||||
|
||||
// Update the timeout parameter
|
||||
UpdateTimeoutParameter(timeout_low, timeout_high, new_thread->last_running_ticks);
|
||||
}
|
||||
|
||||
// Clean up the thread's wait_objects, they'll be restored if needed during
|
||||
@ -403,7 +452,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
priority = new_priority;
|
||||
}
|
||||
|
||||
if (!Memory::GetPointer(entry_point)) {
|
||||
if (!Memory::IsValidVirtualAddress(entry_point)) {
|
||||
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
|
||||
// TODO: Verify error
|
||||
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
|
||||
@ -542,8 +591,12 @@ void Reschedule() {
|
||||
|
||||
HLE::DoneRescheduling();
|
||||
|
||||
// Don't bother switching to the same thread
|
||||
if (next == cur)
|
||||
// Don't bother switching to the same thread.
|
||||
// But if the thread was waiting on objects, we still need to switch it
|
||||
// to perform PC modification, change state to RUNNING, etc.
|
||||
// This occurs in the case when an object the thread is waiting on immediately wakes up
|
||||
// the current thread before Reschedule() is called.
|
||||
if (next == cur && (next == nullptr || next->waitsynch_waited == false))
|
||||
return;
|
||||
|
||||
if (cur && next) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
|
@ -20,12 +20,14 @@ enum class ErrorDescription : u32 {
|
||||
WrongPermission = 46,
|
||||
OS_InvalidBufferDescriptor = 48,
|
||||
WrongAddress = 53,
|
||||
FS_ArchiveNotMounted = 101,
|
||||
FS_NotFound = 120,
|
||||
FS_AlreadyExists = 190,
|
||||
FS_InvalidOpenFlags = 230,
|
||||
FS_NotAFile = 250,
|
||||
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
|
||||
OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage
|
||||
GPU_FirstInitialization = 519,
|
||||
FS_InvalidPath = 702,
|
||||
InvalidSection = 1000,
|
||||
TooLarge = 1001,
|
||||
@ -134,15 +136,28 @@ enum class ErrorModule : u32 {
|
||||
MCU = 72,
|
||||
NS = 73,
|
||||
News = 74,
|
||||
RO_1 = 75,
|
||||
RO = 75,
|
||||
GD = 76,
|
||||
CardSPI = 77,
|
||||
EC = 78,
|
||||
RO_2 = 79,
|
||||
WebBrowser = 80,
|
||||
Test = 81,
|
||||
ENC = 82,
|
||||
PIA = 83,
|
||||
WebBrowser = 79,
|
||||
Test = 80,
|
||||
ENC = 81,
|
||||
PIA = 82,
|
||||
ACT = 83,
|
||||
VCTL = 84,
|
||||
OLV = 85,
|
||||
NEIA = 86,
|
||||
NPNS = 87,
|
||||
|
||||
AVD = 90,
|
||||
L2B = 91,
|
||||
MVD = 92,
|
||||
NFC = 93,
|
||||
UART = 94,
|
||||
SPM = 95,
|
||||
QTM = 96,
|
||||
NFP = 97,
|
||||
|
||||
Application = 254,
|
||||
InvalidResult = 255
|
||||
|
@ -37,6 +37,8 @@ static u32 cpu_percent; ///< CPU time available to the running application
|
||||
// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
|
||||
static u8 unknown_ns_state_field;
|
||||
|
||||
static ScreencapPostPermission screen_capture_post_permission;
|
||||
|
||||
/// Parameter data to be returned in the next call to Glance/ReceiveParameter
|
||||
static MessageParameter next_parameter;
|
||||
|
||||
@ -70,6 +72,13 @@ void Initialize(Service::Interface* self) {
|
||||
void GetSharedFont(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
if (!shared_font_mem) {
|
||||
LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
|
||||
cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
|
||||
cmd_buff[1] = -1; // TODO: Find the right error code
|
||||
return;
|
||||
}
|
||||
|
||||
// The shared font has to be relocated to the new address before being passed to the application.
|
||||
VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address);
|
||||
// The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base,
|
||||
@ -180,12 +189,12 @@ void SendParameter(Service::Interface* self) {
|
||||
}
|
||||
|
||||
MessageParameter param;
|
||||
param.buffer_size = buffer_size;
|
||||
param.destination_id = dst_app_id;
|
||||
param.sender_id = src_app_id;
|
||||
param.object = Kernel::g_handle_table.GetGeneric(handle);
|
||||
param.signal = signal_type;
|
||||
param.data = Memory::GetPointer(buffer);
|
||||
param.buffer.resize(buffer_size);
|
||||
Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
|
||||
|
||||
cmd_buff[1] = dest_applet->ReceiveParameter(param).raw;
|
||||
|
||||
@ -203,16 +212,15 @@ void ReceiveParameter(Service::Interface* self) {
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = next_parameter.sender_id;
|
||||
cmd_buff[3] = next_parameter.signal; // Signal type
|
||||
cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size
|
||||
cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
|
||||
cmd_buff[5] = 0x10;
|
||||
cmd_buff[6] = 0;
|
||||
if (next_parameter.object != nullptr)
|
||||
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
|
||||
cmd_buff[7] = (next_parameter.buffer_size << 14) | 2;
|
||||
cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
|
||||
cmd_buff[8] = buffer;
|
||||
|
||||
if (next_parameter.data)
|
||||
memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
|
||||
Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
|
||||
|
||||
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
|
||||
}
|
||||
@ -226,16 +234,15 @@ void GlanceParameter(Service::Interface* self) {
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = next_parameter.sender_id;
|
||||
cmd_buff[3] = next_parameter.signal; // Signal type
|
||||
cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size
|
||||
cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
|
||||
cmd_buff[5] = 0x10;
|
||||
cmd_buff[6] = 0;
|
||||
if (next_parameter.object != nullptr)
|
||||
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
|
||||
cmd_buff[7] = (next_parameter.buffer_size << 14) | 2;
|
||||
cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
|
||||
cmd_buff[8] = buffer;
|
||||
|
||||
if (next_parameter.data)
|
||||
memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
|
||||
Memory::WriteBlock(buffer, next_parameter.buffer.data(), std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size()));
|
||||
|
||||
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
|
||||
}
|
||||
@ -373,31 +380,34 @@ void StartLibraryApplet(Service::Interface* self) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t buffer_size = cmd_buff[2];
|
||||
VAddr buffer_addr = cmd_buff[6];
|
||||
|
||||
AppletStartupParameter parameter;
|
||||
parameter.buffer_size = cmd_buff[2];
|
||||
parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]);
|
||||
parameter.data = Memory::GetPointer(cmd_buff[6]);
|
||||
parameter.buffer.resize(buffer_size);
|
||||
Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
|
||||
|
||||
cmd_buff[1] = applet->Start(parameter).raw;
|
||||
}
|
||||
|
||||
void SetNSStateField(Service::Interface* self) {
|
||||
void SetScreenCapPostPermission(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
unknown_ns_state_field = cmd_buff[1];
|
||||
screen_capture_post_permission = static_cast<ScreencapPostPermission>(cmd_buff[1] & 0xF);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field);
|
||||
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", screen_capture_post_permission);
|
||||
}
|
||||
|
||||
void GetNSStateField(Service::Interface* self) {
|
||||
void GetScreenCapPostPermission(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[8] = unknown_ns_state_field;
|
||||
LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field);
|
||||
cmd_buff[2] = static_cast<u32>(screen_capture_post_permission);
|
||||
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", screen_capture_post_permission);
|
||||
}
|
||||
|
||||
void GetAppletInfo(Service::Interface* self) {
|
||||
@ -492,6 +502,7 @@ void Init() {
|
||||
|
||||
cpu_percent = 0;
|
||||
unknown_ns_state_field = 0;
|
||||
screen_capture_post_permission = ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
|
||||
|
||||
// TODO(bunnei): Check if these are created in Initialize or on APT process startup.
|
||||
notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification");
|
||||
|
@ -20,16 +20,14 @@ struct MessageParameter {
|
||||
u32 sender_id = 0;
|
||||
u32 destination_id = 0;
|
||||
u32 signal = 0;
|
||||
u32 buffer_size = 0;
|
||||
Kernel::SharedPtr<Kernel::Object> object = nullptr;
|
||||
u8* data = nullptr;
|
||||
std::vector<u8> buffer;
|
||||
};
|
||||
|
||||
/// Holds information about the parameters used in StartLibraryApplet
|
||||
struct AppletStartupParameter {
|
||||
u32 buffer_size = 0;
|
||||
Kernel::SharedPtr<Kernel::Object> object = nullptr;
|
||||
u8* data = nullptr;
|
||||
std::vector<u8> buffer;
|
||||
};
|
||||
|
||||
/// Used by the application to pass information about the current framebuffer to applets.
|
||||
@ -68,6 +66,8 @@ enum class AppletId : u32 {
|
||||
InstructionManual = 0x115,
|
||||
Notifications = 0x116,
|
||||
Miiverse = 0x117,
|
||||
MiiversePost = 0x118,
|
||||
AmiiboSettings = 0x119,
|
||||
SoftwareKeyboard1 = 0x201,
|
||||
Ed1 = 0x202,
|
||||
PnoteApp = 0x204,
|
||||
@ -80,6 +80,12 @@ enum class AppletId : u32 {
|
||||
AnyLibraryApplet = 0x400,
|
||||
SoftwareKeyboard2 = 0x401,
|
||||
Ed2 = 0x402,
|
||||
PnoteApp2 = 0x404,
|
||||
SnoteApp2 = 0x405,
|
||||
Error2 = 0x406,
|
||||
Mint2 = 0x407,
|
||||
Extrapad2 = 0x408,
|
||||
Memolib2 = 0x409,
|
||||
};
|
||||
|
||||
enum class StartupArgumentType : u32 {
|
||||
@ -88,6 +94,13 @@ enum class StartupArgumentType : u32 {
|
||||
OtherMedia = 2,
|
||||
};
|
||||
|
||||
enum class ScreencapPostPermission : u32 {
|
||||
CleanThePermission = 0, //TODO(JamePeng): verify what "zero" means
|
||||
NoExplicitSetting = 1,
|
||||
EnableScreenshotPostingToMiiverse = 2,
|
||||
DisableScreenshotPostingToMiiverse = 3
|
||||
};
|
||||
|
||||
/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
|
||||
void SendParameter(const MessageParameter& parameter);
|
||||
|
||||
@ -377,25 +390,24 @@ void StartLibraryApplet(Service::Interface* self);
|
||||
void GetStartupArgument(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* APT::SetNSStateField service function
|
||||
* APT::SetScreenCapPostPermission service function
|
||||
* Inputs:
|
||||
* 1 : u8 NS state field
|
||||
* 0 : Header Code[0x00550040]
|
||||
* 1 : u8 The screenshot posting permission
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* Note:
|
||||
* This writes the input u8 to a NS state field.
|
||||
*/
|
||||
void SetNSStateField(Service::Interface* self);
|
||||
void SetScreenCapPostPermission(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* APT::GetNSStateField service function
|
||||
* APT::GetScreenCapPostPermission service function
|
||||
* Inputs:
|
||||
* 0 : Header Code[0x00560000]
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 8 : u8 NS state field
|
||||
* Note:
|
||||
* This returns a u8 NS state field(which can be set by cmd 0x00550040), at cmdreply+8.
|
||||
* 2 : u8 The screenshot posting permission
|
||||
*/
|
||||
void GetNSStateField(Service::Interface* self);
|
||||
void GetScreenCapPostPermission(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* APT::CheckNew3DSApp service function
|
||||
|
@ -33,8 +33,8 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
|
||||
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
|
||||
{0x00510080, GetStartupArgument, "GetStartupArgument"},
|
||||
{0x00550040, SetNSStateField, "SetNSStateField?"},
|
||||
{0x00560000, GetNSStateField, "GetNSStateField?"},
|
||||
{0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
|
||||
{0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
|
||||
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
|
||||
{0x01020000, CheckNew3DS, "CheckNew3DS"}
|
||||
};
|
||||
|
@ -92,8 +92,8 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00510080, GetStartupArgument, "GetStartupArgument"},
|
||||
{0x00520104, nullptr, "Wrap1"},
|
||||
{0x00530104, nullptr, "Unwrap1"},
|
||||
{0x00550040, SetNSStateField, "SetNSStateField?" },
|
||||
{0x00560000, GetNSStateField, "GetNSStateField?" },
|
||||
{0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
|
||||
{0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
|
||||
{0x00580002, nullptr, "GetProgramID"},
|
||||
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
|
||||
{0x01020000, CheckNew3DS, "CheckNew3DS"}
|
||||
|
@ -92,8 +92,8 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00510080, GetStartupArgument, "GetStartupArgument"},
|
||||
{0x00520104, nullptr, "Wrap1"},
|
||||
{0x00530104, nullptr, "Unwrap1"},
|
||||
{0x00550040, SetNSStateField, "SetNSStateField?"},
|
||||
{0x00560000, GetNSStateField, "GetNSStateField?"},
|
||||
{0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
|
||||
{0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
|
||||
{0x00580002, nullptr, "GetProgramID"},
|
||||
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
|
||||
{0x01020000, CheckNew3DS, "CheckNew3DS"}
|
||||
|
@ -40,6 +40,20 @@ struct SaveFileConfig {
|
||||
};
|
||||
static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes");
|
||||
|
||||
enum ConfigBlockID {
|
||||
StereoCameraSettingsBlockID = 0x00050005,
|
||||
SoundOutputModeBlockID = 0x00070001,
|
||||
ConsoleUniqueIDBlockID = 0x00090001,
|
||||
UsernameBlockID = 0x000A0000,
|
||||
BirthdayBlockID = 0x000A0001,
|
||||
LanguageBlockID = 0x000A0002,
|
||||
CountryInfoBlockID = 0x000B0000,
|
||||
CountryNameBlockID = 0x000B0001,
|
||||
StateNameBlockID = 0x000B0002,
|
||||
EULAVersionBlockID = 0x000D0000,
|
||||
ConsoleModelBlockID = 0x000F0004,
|
||||
};
|
||||
|
||||
struct UsernameBlock {
|
||||
char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
|
||||
u32 zero;
|
||||
@ -47,6 +61,12 @@ struct UsernameBlock {
|
||||
};
|
||||
static_assert(sizeof(UsernameBlock) == 0x1C, "UsernameBlock must be exactly 0x1C bytes");
|
||||
|
||||
struct BirthdayBlock {
|
||||
u8 month; ///< The month of the birthday
|
||||
u8 day; ///< The day of the birthday
|
||||
};
|
||||
static_assert(sizeof(BirthdayBlock) == 2, "BirthdayBlock must be exactly 2 bytes");
|
||||
|
||||
struct ConsoleModelInfo {
|
||||
u8 model; ///< The console model (3DS, 2DS, etc)
|
||||
u8 unknown[3]; ///< Unknown data
|
||||
@ -65,11 +85,9 @@ static const u64 CFG_SAVE_ID = 0x00010017;
|
||||
static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
|
||||
static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
|
||||
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
|
||||
static const char CONSOLE_USERNAME[0x14] = "CITRA";
|
||||
/// This will be initialized in Init, and will be used when creating the block
|
||||
static UsernameBlock CONSOLE_USERNAME_BLOCK;
|
||||
/// TODO(Subv): Find out what this actually is
|
||||
static const u8 SOUND_OUTPUT_MODE = 2;
|
||||
static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
|
||||
static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
|
||||
static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
|
||||
static const u8 UNITED_STATES_COUNTRY_ID = 49;
|
||||
/// TODO(Subv): Find what the other bytes are
|
||||
static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID };
|
||||
@ -191,28 +209,48 @@ void GetConfigInfoBlk2(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
u32 size = cmd_buff[1];
|
||||
u32 block_id = cmd_buff[2];
|
||||
u8* data_pointer = Memory::GetPointer(cmd_buff[4]);
|
||||
VAddr data_pointer = cmd_buff[4];
|
||||
|
||||
if (data_pointer == nullptr) {
|
||||
if (!Memory::IsValidVirtualAddress(data_pointer)) {
|
||||
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
|
||||
std::vector<u8> data(size);
|
||||
cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data.data()).raw;
|
||||
Memory::WriteBlock(data_pointer, data.data(), data.size());
|
||||
}
|
||||
|
||||
void GetConfigInfoBlk8(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
u32 size = cmd_buff[1];
|
||||
u32 block_id = cmd_buff[2];
|
||||
u8* data_pointer = Memory::GetPointer(cmd_buff[4]);
|
||||
VAddr data_pointer = cmd_buff[4];
|
||||
|
||||
if (data_pointer == nullptr) {
|
||||
if (!Memory::IsValidVirtualAddress(data_pointer)) {
|
||||
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
|
||||
std::vector<u8> data(size);
|
||||
cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data.data()).raw;
|
||||
Memory::WriteBlock(data_pointer, data.data(), data.size());
|
||||
}
|
||||
|
||||
void SetConfigInfoBlk4(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
u32 block_id = cmd_buff[1];
|
||||
u32 size = cmd_buff[2];
|
||||
VAddr data_pointer = cmd_buff[4];
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(data_pointer)) {
|
||||
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> data(size);
|
||||
Memory::ReadBlock(data_pointer, data.data(), data.size());
|
||||
cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw;
|
||||
}
|
||||
|
||||
void UpdateConfigNANDSavegame(Service::Interface* self) {
|
||||
@ -225,13 +263,13 @@ void FormatConfig(Service::Interface* self) {
|
||||
cmd_buff[1] = Service::CFG::FormatConfig().raw;
|
||||
}
|
||||
|
||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
|
||||
static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) {
|
||||
// Read the header
|
||||
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
|
||||
|
||||
auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
|
||||
[&](const SaveConfigBlockEntry& entry) {
|
||||
return entry.block_id == block_id && (entry.flags & flag);
|
||||
return entry.block_id == block_id;
|
||||
});
|
||||
|
||||
if (itr == std::end(config->block_entries)) {
|
||||
@ -239,17 +277,38 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
|
||||
return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
if ((itr->flags & flag) == 0) {
|
||||
LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id, size);
|
||||
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
if (itr->size != size) {
|
||||
LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag);
|
||||
return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
void* pointer;
|
||||
|
||||
// The data is located in the block header itself if the size is less than 4 bytes
|
||||
if (itr->size <= 4)
|
||||
memcpy(output, &itr->offset_or_data, itr->size);
|
||||
pointer = &itr->offset_or_data;
|
||||
else
|
||||
memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size);
|
||||
pointer = &cfg_config_file_buffer[itr->offset_or_data];
|
||||
|
||||
return MakeResult<void*>(pointer);
|
||||
}
|
||||
|
||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
|
||||
void* pointer;
|
||||
CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
|
||||
memcpy(output, pointer, size);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) {
|
||||
void* pointer;
|
||||
CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
|
||||
memcpy(pointer, input, size);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -327,35 +386,25 @@ ResultCode FormatConfig() {
|
||||
res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
|
||||
if (!res.IsSuccess()) return res;
|
||||
res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
|
||||
if (!res.IsSuccess()) return res;
|
||||
res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
|
||||
if (!res.IsSuccess()) return res;
|
||||
res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
|
||||
res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
// 0x000A0000 - Profile username
|
||||
struct {
|
||||
u16_le username[10];
|
||||
u8 unused[4];
|
||||
u32_le wordfilter_version; // Unused by Citra
|
||||
} profile_username = {};
|
||||
|
||||
std::u16string username_string = Common::UTF8ToUTF16("Citra");
|
||||
std::copy(username_string.cbegin(), username_string.cend(), profile_username.username);
|
||||
res = CreateConfigInfoBlk(0x000A0000, sizeof(profile_username), 0xE, &profile_username);
|
||||
res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
// 0x000A0001 - Profile birthday
|
||||
const u8 profile_birthday[2] = {3, 25}; // March 25th, 2014
|
||||
res = CreateConfigInfoBlk(0x000A0001, sizeof(profile_birthday), 0xE, profile_birthday);
|
||||
res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
|
||||
res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
|
||||
if (!res.IsSuccess()) return res;
|
||||
res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
|
||||
|
||||
res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
u16_le country_name_buffer[16][0x40] = {};
|
||||
@ -364,10 +413,10 @@ ResultCode FormatConfig() {
|
||||
std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
|
||||
}
|
||||
// 0x000B0001 - Localized names for the profile Country
|
||||
res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer);
|
||||
res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer);
|
||||
if (!res.IsSuccess()) return res;
|
||||
// 0x000B0002 - Localized names for the profile State/Province
|
||||
res = CreateConfigInfoBlk(0x000B0002, sizeof(country_name_buffer), 0xE, country_name_buffer);
|
||||
res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
// 0x000B0003 - Unknown, related to country/address (zip code?)
|
||||
@ -383,10 +432,10 @@ ResultCode FormatConfig() {
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
// 0x000D0000 - Accepted EULA version
|
||||
res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer);
|
||||
res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
|
||||
res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
|
||||
if (!res.IsSuccess()) return res;
|
||||
|
||||
// 0x00170000 - Unknown
|
||||
@ -400,11 +449,7 @@ ResultCode FormatConfig() {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Init() {
|
||||
AddService(new CFG_I_Interface);
|
||||
AddService(new CFG_S_Interface);
|
||||
AddService(new CFG_U_Interface);
|
||||
|
||||
ResultCode LoadConfigNANDSaveFile() {
|
||||
// Open the SystemSaveData archive 0x00010017
|
||||
FileSys::Path archive_path(cfg_system_savedata_id);
|
||||
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
|
||||
@ -432,25 +477,75 @@ void Init() {
|
||||
if (config_result.Succeeded()) {
|
||||
auto config = config_result.MoveFrom();
|
||||
config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
|
||||
return;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Initialize the Username block
|
||||
// TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
|
||||
memset(&CONSOLE_USERNAME_BLOCK, 0, sizeof(CONSOLE_USERNAME_BLOCK));
|
||||
CONSOLE_USERNAME_BLOCK.ng_word = 0;
|
||||
CONSOLE_USERNAME_BLOCK.zero = 0;
|
||||
return FormatConfig();
|
||||
}
|
||||
|
||||
// Copy string to buffer and pad with zeros at the end
|
||||
auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14);
|
||||
std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size,
|
||||
std::end(CONSOLE_USERNAME_BLOCK.username), 0);
|
||||
void Init() {
|
||||
AddService(new CFG_I_Interface);
|
||||
AddService(new CFG_S_Interface);
|
||||
AddService(new CFG_U_Interface);
|
||||
|
||||
FormatConfig();
|
||||
LoadConfigNANDSaveFile();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
}
|
||||
|
||||
void SetUsername(const std::u16string& name) {
|
||||
ASSERT(name.size() <= 10);
|
||||
UsernameBlock block{};
|
||||
name.copy(block.username, name.size());
|
||||
SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block);
|
||||
}
|
||||
|
||||
std::u16string GetUsername() {
|
||||
UsernameBlock block;
|
||||
GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block);
|
||||
|
||||
// the username string in the block isn't null-terminated,
|
||||
// so we need to find the end manually.
|
||||
std::u16string username(block.username, ARRAY_SIZE(block.username));
|
||||
const size_t pos = username.find(u'\0');
|
||||
if (pos != std::u16string::npos)
|
||||
username.erase(pos);
|
||||
return username;
|
||||
}
|
||||
|
||||
void SetBirthday(u8 month, u8 day) {
|
||||
BirthdayBlock block = { month, day };
|
||||
SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block);
|
||||
}
|
||||
|
||||
std::tuple<u8, u8> GetBirthday() {
|
||||
BirthdayBlock block;
|
||||
GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block);
|
||||
return std::make_tuple(block.month, block.day);
|
||||
}
|
||||
|
||||
void SetSystemLanguage(SystemLanguage language) {
|
||||
u8 block = language;
|
||||
SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block);
|
||||
}
|
||||
|
||||
SystemLanguage GetSystemLanguage() {
|
||||
u8 block;
|
||||
GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block);
|
||||
return static_cast<SystemLanguage>(block);
|
||||
}
|
||||
|
||||
void SetSoundOutputMode(SoundOutputMode mode) {
|
||||
u8 block = mode;
|
||||
SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block);
|
||||
}
|
||||
|
||||
SoundOutputMode GetSoundOutputMode() {
|
||||
u8 block;
|
||||
GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block);
|
||||
return static_cast<SoundOutputMode>(block);
|
||||
}
|
||||
|
||||
} // namespace CFG
|
||||
} // namespace Service
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@ -35,7 +36,14 @@ enum SystemLanguage {
|
||||
LANGUAGE_KO = 7,
|
||||
LANGUAGE_NL = 8,
|
||||
LANGUAGE_PT = 9,
|
||||
LANGUAGE_RU = 10
|
||||
LANGUAGE_RU = 10,
|
||||
LANGUAGE_TW = 11
|
||||
};
|
||||
|
||||
enum SoundOutputMode {
|
||||
SOUND_MONO = 0,
|
||||
SOUND_STEREO = 1,
|
||||
SOUND_SURROUND = 2
|
||||
};
|
||||
|
||||
/// Block header in the config savedata file
|
||||
@ -177,6 +185,22 @@ void GetConfigInfoBlk2(Service::Interface* self);
|
||||
*/
|
||||
void GetConfigInfoBlk8(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* CFG::SetConfigInfoBlk4 service function
|
||||
* Inputs:
|
||||
* 0 : 0x04020082 / 0x08020082
|
||||
* 1 : Block ID
|
||||
* 2 : Size
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* Note:
|
||||
* The parameters order is different from GetConfigInfoBlk2/8's,
|
||||
* where Block ID and Size are switched.
|
||||
*/
|
||||
void SetConfigInfoBlk4(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* CFG::UpdateConfigNANDSavegame service function
|
||||
* Inputs:
|
||||
@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self);
|
||||
* @param output A pointer where we will write the read data
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
|
||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output);
|
||||
|
||||
/**
|
||||
* Reads data from input and writes to a block with the specified id and flag
|
||||
* in the Config savegame buffer.
|
||||
* The input size must match exactly the size of the target block
|
||||
* @param block_id The id of the block we want to write
|
||||
* @param size The size of the block we want to write
|
||||
* @param flag The target block must have this flag set
|
||||
* @param input A pointer where we will read data and write to Config savegame buffer
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input);
|
||||
|
||||
/**
|
||||
* Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory.
|
||||
@ -236,11 +272,70 @@ ResultCode UpdateConfigNANDSavegame();
|
||||
*/
|
||||
ResultCode FormatConfig();
|
||||
|
||||
/**
|
||||
* Open the config savegame file and load it to the memory buffer
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode LoadConfigNANDSaveFile();
|
||||
|
||||
/// Initialize the config service
|
||||
void Init();
|
||||
|
||||
/// Shutdown the config service
|
||||
void Shutdown();
|
||||
|
||||
// Utilities for frontend to set config data.
|
||||
// Note: before calling these functions, LoadConfigNANDSaveFile should be called,
|
||||
// and UpdateConfigNANDSavegame should be called after making changes to config data.
|
||||
|
||||
/**
|
||||
* Sets the username in config savegame.
|
||||
* @param name the username to set. The maximum size is 10 in char16_t.
|
||||
*/
|
||||
void SetUsername(const std::u16string& name);
|
||||
|
||||
/**
|
||||
* Gets the username from config savegame.
|
||||
* @returns the username
|
||||
*/
|
||||
std::u16string GetUsername();
|
||||
|
||||
/**
|
||||
* Sets the profile birthday in config savegame.
|
||||
* @param month the month of birthday.
|
||||
* @param day the day of the birthday.
|
||||
*/
|
||||
void SetBirthday(u8 month, u8 day);
|
||||
|
||||
/**
|
||||
* Gets the profile birthday from the config savegame.
|
||||
* @returns a tuple of (month, day) of birthday
|
||||
*/
|
||||
std::tuple<u8, u8> GetBirthday();
|
||||
|
||||
/**
|
||||
* Sets the system language in config savegame.
|
||||
* @param language the system language to set.
|
||||
*/
|
||||
void SetSystemLanguage(SystemLanguage language);
|
||||
|
||||
/**
|
||||
* Gets the system language from config savegame.
|
||||
* @returns the system language
|
||||
*/
|
||||
SystemLanguage GetSystemLanguage();
|
||||
|
||||
/**
|
||||
* Sets the sound output mode in config savegame.
|
||||
* @param mode the sound output mode to set
|
||||
*/
|
||||
void SetSoundOutputMode(SoundOutputMode mode);
|
||||
|
||||
/**
|
||||
* Gets the sound output mode from config savegame.
|
||||
* @returns the sound output mode
|
||||
*/
|
||||
SoundOutputMode GetSoundOutputMode();
|
||||
|
||||
} // namespace CFG
|
||||
} // namespace Service
|
||||
|
@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
|
||||
// cfg:i
|
||||
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
||||
{0x04020082, nullptr, "SetConfigInfoBlk4"},
|
||||
{0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
|
||||
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
|
||||
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
|
||||
@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x04080042, nullptr, "SecureInfoGetSerialNo"},
|
||||
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
|
||||
{0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
||||
{0x08020082, nullptr, "SetConfigInfoBlk4"},
|
||||
{0x08020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
|
||||
{0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||
{0x080400C2, nullptr, "CreateConfigInfoBlk"},
|
||||
{0x08050000, nullptr, "DeleteConfigNANDSavefile"},
|
||||
|
@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
|
||||
// cfg:s
|
||||
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
||||
{0x04020082, nullptr, "SetConfigInfoBlk4"},
|
||||
{0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
|
||||
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
|
||||
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
|
||||
|
24
src/core/hle/service/dlp/dlp.cpp
Normal file
24
src/core/hle/service/dlp/dlp.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/dlp/dlp.h"
|
||||
#include "core/hle/service/dlp/dlp_clnt.h"
|
||||
#include "core/hle/service/dlp/dlp_fkcl.h"
|
||||
#include "core/hle/service/dlp/dlp_srvr.h"
|
||||
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
void Init() {
|
||||
AddService(new DLP_CLNT_Interface);
|
||||
AddService(new DLP_FKCL_Interface);
|
||||
AddService(new DLP_SRVR_Interface);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
}
|
||||
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
15
src/core/hle/service/dlp/dlp.h
Normal file
15
src/core/hle/service/dlp/dlp.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
/// Initializes the DLP services.
|
||||
void Init();
|
||||
|
||||
/// Shuts down the DLP services.
|
||||
void Shutdown();
|
||||
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
20
src/core/hle/service/dlp/dlp_clnt.cpp
Normal file
20
src/core/hle/service/dlp/dlp_clnt.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/dlp/dlp_clnt.h"
|
||||
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x000100C3, nullptr, "Initialize"},
|
||||
{0x00110000, nullptr, "GetWirelessRebootPassphrase"},
|
||||
};
|
||||
|
||||
DLP_CLNT_Interface::DLP_CLNT_Interface() {
|
||||
Register(FunctionTable);
|
||||
}
|
||||
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
22
src/core/hle/service/dlp/dlp_clnt.h
Normal file
22
src/core/hle/service/dlp/dlp_clnt.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
class DLP_CLNT_Interface final : public Interface {
|
||||
public:
|
||||
DLP_CLNT_Interface();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "dlp:CLNT";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
20
src/core/hle/service/dlp/dlp_fkcl.cpp
Normal file
20
src/core/hle/service/dlp/dlp_fkcl.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/dlp/dlp_fkcl.h"
|
||||
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010083, nullptr, "Initialize"},
|
||||
{0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
|
||||
};
|
||||
|
||||
DLP_FKCL_Interface::DLP_FKCL_Interface() {
|
||||
Register(FunctionTable);
|
||||
}
|
||||
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
22
src/core/hle/service/dlp/dlp_fkcl.h
Normal file
22
src/core/hle/service/dlp/dlp_fkcl.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
class DLP_FKCL_Interface final : public Interface {
|
||||
public:
|
||||
DLP_FKCL_Interface();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "dlp:FKCL";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
@ -2,16 +2,15 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/hle.h"
|
||||
#include "core/hle/service/dlp_srvr.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/dlp/dlp_srvr.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Namespace DLP_SRVR
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
namespace DLP_SRVR {
|
||||
|
||||
static void unk_0x000E0040(Service::Interface* self) {
|
||||
static void unk_0x000E0040(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
@ -23,14 +22,13 @@ static void unk_0x000E0040(Service::Interface* self) {
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010183, nullptr, "Initialize"},
|
||||
{0x00020000, nullptr, "Finalize"},
|
||||
{0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
|
||||
{0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface class
|
||||
|
||||
Interface::Interface() {
|
||||
DLP_SRVR_Interface::DLP_SRVR_Interface() {
|
||||
Register(FunctionTable);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
@ -6,18 +6,17 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Namespace DLP_SRVR
|
||||
namespace Service {
|
||||
namespace DLP {
|
||||
|
||||
namespace DLP_SRVR {
|
||||
|
||||
class Interface : public Service::Interface {
|
||||
class DLP_SRVR_Interface final : public Interface {
|
||||
public:
|
||||
Interface();
|
||||
DLP_SRVR_Interface();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "dlp:SRVR";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace DLP
|
||||
} // namespace Service
|
@ -140,12 +140,15 @@ static void LoadComponent(Service::Interface* self) {
|
||||
|
||||
// TODO(bunnei): Implement real DSP firmware loading
|
||||
|
||||
ASSERT(Memory::GetPointer(buffer) != nullptr);
|
||||
ASSERT(size > 0x37C);
|
||||
ASSERT(Memory::IsValidVirtualAddress(buffer));
|
||||
|
||||
LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer), size));
|
||||
std::vector<u8> component_data(size);
|
||||
Memory::ReadBlock(buffer, component_data.data(), component_data.size());
|
||||
|
||||
LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(component_data.data(), component_data.size()));
|
||||
// Some versions of the firmware have the location of DSP structures listed here.
|
||||
LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer) + 0x340, 60));
|
||||
ASSERT(size > 0x37C);
|
||||
LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(component_data.data() + 0x340, 60));
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
|
||||
size, prog_mask, data_mask, buffer);
|
||||
@ -285,7 +288,7 @@ static void WriteProcessPipe(Service::Interface* self) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
|
||||
ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
|
||||
|
||||
std::vector<u8> message(size);
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
@ -324,7 +327,7 @@ static void ReadPipeIfPossible(Service::Interface* self) {
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
|
||||
ASSERT_MSG(Memory::IsValidVirtualAddress(addr), "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
@ -364,7 +367,7 @@ static void ReadPipe(Service::Interface* self) {
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
|
||||
ASSERT_MSG(Memory::IsValidVirtualAddress(addr), "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
|
||||
|
||||
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
|
||||
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
|
||||
|
@ -58,6 +58,10 @@ namespace FS {
|
||||
const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS,
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||
|
||||
/// Returned when a function is passed an invalid archive handle.
|
||||
const ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS,
|
||||
ErrorSummary::NotFound, ErrorLevel::Status); // 0xC8804465
|
||||
|
||||
// Command to access archive file
|
||||
enum class FileCommand : u32 {
|
||||
Dummy1 = 0x000100C6,
|
||||
@ -108,13 +112,14 @@ ResultVal<bool> File::SyncRequest() {
|
||||
offset, length, backend->GetSize());
|
||||
}
|
||||
|
||||
ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address));
|
||||
std::vector<u8> data(length);
|
||||
ResultVal<size_t> read = backend->Read(offset, data.size(), data.data());
|
||||
if (read.Failed()) {
|
||||
cmd_buff[1] = read.Code().raw;
|
||||
return read.Code();
|
||||
}
|
||||
Memory::WriteBlock(address, data.data(), *read);
|
||||
cmd_buff[2] = static_cast<u32>(*read);
|
||||
Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -128,7 +133,9 @@ ResultVal<bool> File::SyncRequest() {
|
||||
LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
|
||||
GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
|
||||
|
||||
ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address));
|
||||
std::vector<u8> data(length);
|
||||
Memory::ReadBlock(address, data.data(), data.size());
|
||||
ResultVal<size_t> written = backend->Write(offset, data.size(), flush != 0, data.data());
|
||||
if (written.Failed()) {
|
||||
cmd_buff[1] = written.Code().raw;
|
||||
return written.Code();
|
||||
@ -216,12 +223,14 @@ ResultVal<bool> Directory::SyncRequest() {
|
||||
{
|
||||
u32 count = cmd_buff[1];
|
||||
u32 address = cmd_buff[3];
|
||||
auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
|
||||
std::vector<FileSys::Entry> entries(count);
|
||||
LOG_TRACE(Service_FS, "Read %s %s: count=%d",
|
||||
GetTypeName().c_str(), GetName().c_str(), count);
|
||||
|
||||
// Number of entries actually read
|
||||
cmd_buff[2] = backend->Read(count, entries);
|
||||
u32 read = backend->Read(entries.size(), entries.data());
|
||||
cmd_buff[2] = read;
|
||||
Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -250,7 +259,7 @@ using FileSys::ArchiveFactory;
|
||||
|
||||
/**
|
||||
* Map of registered archives, identified by id code. Once an archive is registered here, it is
|
||||
* never removed until the FS service is shut down.
|
||||
* never removed until UnregisterArchiveTypes is called.
|
||||
*/
|
||||
static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map;
|
||||
|
||||
@ -287,7 +296,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
|
||||
|
||||
ResultCode CloseArchive(ArchiveHandle handle) {
|
||||
if (handle_map.erase(handle) == 0)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
else
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@ -309,7 +318,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
|
||||
const FileSys::Path& path, const FileSys::Mode mode) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
auto backend = archive->OpenFile(path, mode);
|
||||
if (backend.Failed())
|
||||
@ -322,7 +331,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
|
||||
ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
return archive->DeleteFile(path);
|
||||
}
|
||||
@ -332,7 +341,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
|
||||
ArchiveBackend* src_archive = GetArchive(src_archive_handle);
|
||||
ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
|
||||
if (src_archive == nullptr || dest_archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
if (src_archive == dest_archive) {
|
||||
if (src_archive->RenameFile(src_path, dest_path))
|
||||
@ -351,7 +360,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
|
||||
ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
if (archive->DeleteDirectory(path))
|
||||
return RESULT_SUCCESS;
|
||||
@ -362,7 +371,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
|
||||
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
return archive->CreateFile(path, file_size);
|
||||
}
|
||||
@ -370,7 +379,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path
|
||||
ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
if (archive->CreateDirectory(path))
|
||||
return RESULT_SUCCESS;
|
||||
@ -383,7 +392,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
|
||||
ArchiveBackend* src_archive = GetArchive(src_archive_handle);
|
||||
ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
|
||||
if (src_archive == nullptr || dest_archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
if (src_archive == dest_archive) {
|
||||
if (src_archive->RenameDirectory(src_path, dest_path))
|
||||
@ -403,7 +412,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
|
||||
const FileSys::Path& path) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
|
||||
std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
|
||||
if (backend == nullptr) {
|
||||
@ -418,7 +427,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
|
||||
ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
|
||||
ArchiveBackend* archive = GetArchive(archive_handle);
|
||||
if (archive == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_INVALID_ARCHIVE_HANDLE;
|
||||
return MakeResult<u64>(archive->GetFreeBytes());
|
||||
}
|
||||
|
||||
@ -456,11 +465,12 @@ ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon
|
||||
if (result.IsError())
|
||||
return result;
|
||||
|
||||
u8* smdh_icon = Memory::GetPointer(icon_buffer);
|
||||
if (!smdh_icon)
|
||||
if (!Memory::IsValidVirtualAddress(icon_buffer))
|
||||
return ResultCode(-1); // TODO(Subv): Find the right error code
|
||||
|
||||
ext_savedata->WriteIcon(path, smdh_icon, icon_size);
|
||||
std::vector<u8> smdh_icon(icon_size);
|
||||
Memory::ReadBlock(icon_buffer, smdh_icon.data(), smdh_icon.size());
|
||||
ext_savedata->WriteIcon(path, smdh_icon.data(), smdh_icon.size());
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -510,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Initialize archives
|
||||
void ArchiveInit() {
|
||||
next_handle = 1;
|
||||
|
||||
AddService(new FS::Interface);
|
||||
|
||||
void RegisterArchiveTypes() {
|
||||
// TODO(Subv): Add the other archive types (see here for the known types:
|
||||
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
|
||||
|
||||
@ -552,10 +557,23 @@ void ArchiveInit() {
|
||||
RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
|
||||
}
|
||||
|
||||
void UnregisterArchiveTypes() {
|
||||
id_code_map.clear();
|
||||
}
|
||||
|
||||
/// Initialize archives
|
||||
void ArchiveInit() {
|
||||
next_handle = 1;
|
||||
|
||||
AddService(new FS::Interface);
|
||||
|
||||
RegisterArchiveTypes();
|
||||
}
|
||||
|
||||
/// Shutdown archives
|
||||
void ArchiveShutdown() {
|
||||
handle_map.clear();
|
||||
id_code_map.clear();
|
||||
UnregisterArchiveTypes();
|
||||
}
|
||||
|
||||
} // namespace FS
|
||||
|
@ -235,5 +235,11 @@ void ArchiveInit();
|
||||
/// Shutdown archives
|
||||
void ArchiveShutdown();
|
||||
|
||||
/// Register all archive types
|
||||
void RegisterArchiveTypes();
|
||||
|
||||
/// Unregister all archive types
|
||||
void UnregisterArchiveTypes();
|
||||
|
||||
} // namespace FS
|
||||
} // namespace Service
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user