resolve conflicts

This commit is contained in:
mailwl 2016-08-20 09:55:52 +03:00
commit 8c6ffa17eb
140 changed files with 8003 additions and 4933 deletions

View File

@ -11,12 +11,12 @@ fi
#if OS is linux or is not set #if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
export CC=gcc-5 export CC=gcc-6
export CXX=g++-5 export CXX=g++-6
export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH
mkdir build && cd build mkdir build && cd build
cmake -DCITRA_FORCE_QT4=ON .. cmake ..
make -j4 make -j4
ctest -VV -C Release ctest -VV -C Release

View File

@ -5,11 +5,11 @@ set -x
#if OS is linux or is not set #if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
export CC=gcc-5 export CC=gcc-6
export CXX=g++-5 export CXX=g++-6
mkdir -p $HOME/.local 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 | 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 elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update > /dev/null # silence the very verbose output brew update > /dev/null # silence the very verbose output
brew unlink cmake brew unlink cmake
brew install cmake31 qt5 sdl2 dylibbundler brew install cmake qt5 sdl2 dylibbundler
gem install xcpretty gem install xcpretty
fi fi

View File

@ -23,8 +23,103 @@ if [ "$TRAVIS_BRANCH" = "master" ]; then
# move SDL2 libs into folder for deployment # move SDL2 libs into folder for deployment
dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/" 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 fi
# Copy documentation
cp license.txt "$REV_NAME"
cp README.md "$REV_NAME"
ARCHIVE_NAME="${REV_NAME}.tar.xz" ARCHIVE_NAME="${REV_NAME}.tar.xz"
tar -cJvf "$ARCHIVE_NAME" "$REV_NAME" 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'" lftp -c "open -u citra-builds,$BUILD_PASSWORD sftp://builds.citra-emu.org; set sftp:auto-confirm yes; put -O '$UPLOAD_DIR' '$ARCHIVE_NAME'"

View File

@ -1,22 +1,26 @@
os:
- linux
- osx
language: cpp language: cpp
matrix:
include:
- os: linux
sudo: true
dist: trusty
- os: osx
sudo: false
env: env:
global: global:
- secure: "AXHFIafTmbGDsHD3mUVj5a4I397DQjti/WoqAJGUp2PglxTcc04BwxZ9Z+xLuf5N2Hs5r9ojAJLT8OGxJCLBDXzneQTNSqXbFuYSLbqrEAiIRlA9eRIotWCg+wYcO+5e8MKX+cHVKwiIWasUB21AtCdq6msh6Y3pUshZp212VPg=" - secure: "AXHFIafTmbGDsHD3mUVj5a4I397DQjti/WoqAJGUp2PglxTcc04BwxZ9Z+xLuf5N2Hs5r9ojAJLT8OGxJCLBDXzneQTNSqXbFuYSLbqrEAiIRlA9eRIotWCg+wYcO+5e8MKX+cHVKwiIWasUB21AtCdq6msh6Y3pUshZp212VPg="
sudo: false
addons: addons:
apt: apt:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
- gcc-5 - gcc-6
- g++-5 - g++-6
- qt5-default
- libqt5opengl5-dev
- xorg-dev - xorg-dev
- lib32stdc++6 # For CMake - lib32stdc++6 # For CMake
- lftp # To upload builds - lftp # To upload builds

View File

@ -1,6 +1,5 @@
# CMake 3.1 required for Qt5 settings to be applied automatically on # CMake 3.2 required for cmake to know the right flags for CXX standard on OSX
# dependent libraries and IMPORTED targets. cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.1)
function(download_bundled_external remote_path lib_name prefix_var) function(download_bundled_external remote_path lib_name prefix_var)
set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") 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(ENABLE_QT "Enable the Qt frontend" ON)
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) 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) if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook") message(STATUS "Copying pre-commit hook")
@ -64,14 +62,12 @@ if (NOT DEFINED ARCHITECTURE)
endif() endif()
message(STATUS "Target architecture: ${ARCHITECTURE}") message(STATUS "Target architecture: ${ARCHITECTURE}")
if (NOT MSVC) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes") set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
if (ARCHITECTURE_x86_64) if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
endif()
else() else()
# Silence "deprecation" warnings # Silence "deprecation" warnings
add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_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 (ENABLE_QT)
if (CITRA_USE_BUNDLED_QT) if (CITRA_USE_BUNDLED_QT)
if (MSVC14 AND ARCHITECTURE_x86_64) if (MSVC14 AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.5-msvc2015_64) set(QT_VER qt-5.7-msvc2015_64)
else() else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.") message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
endif() endif()
@ -201,16 +197,8 @@ if (ENABLE_QT)
set(QT_PREFIX_HINT) set(QT_PREFIX_HINT)
endif() endif()
if (NOT CITRA_FORCE_QT4) find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
find_package(Qt5 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) set(CITRA_QT_LIBS Qt5::Widgets Qt5::OpenGL)
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()
endif() endif()
# This function should be passed a list of all files in a target. It will automatically generate # This function should be passed a list of all files in a target. It will automatically generate

View File

@ -15,6 +15,8 @@ For development discussion, please join us @ #citra on freenode.
### Development ### 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. 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 ### Building

View File

@ -39,17 +39,15 @@ on_success:
# Where are these spaces coming from? Regardless, let's remove them # Where are these spaces coming from? Regardless, let's remove them
$BUILD_NAME = "citra-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" $BUILD_NAME = "citra-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ",""
$BUILD_NAME_NOQT = "citra-noqt-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" $BUILD_NAME_NOQT = "citra-noqt-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ",""
# Zip up the build folder # Zip up the build folder and documentation
7z a $BUILD_NAME .\build\bin\release\* 7z a $BUILD_NAME .\build\bin\release\* .\license.txt .\README.md
# Do a second archive with only the binaries # Do a second archive with only the binaries (excludes dlls) and documentation
7z a $BUILD_NAME_NOQT .\build\bin\release\*.exe 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 # Download WinSCP and upload to server
.\WinSCP.com /command ` choco install winscp.portable
WinSCP.exe /command `
"option batch abort" ` "option batch abort" `
"option confirm off" ` "option confirm off" `
"open sftp://citra-builds:${env:BUILD_PASSWORD}@builds.citra-emu.org -hostkey=*" ` "open sftp://citra-builds:${env:BUILD_PASSWORD}@builds.citra-emu.org -hostkey=*" `

View File

@ -3,6 +3,7 @@
# SDL2_LIBRARY, the name of the library to link against # SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2 # SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h # 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: # This module responds to the the flag:
# SDL2_BUILDING_LIBRARY # SDL2_BUILDING_LIBRARY
@ -149,6 +150,14 @@ FIND_LIBRARY(SDL2_LIBRARY_TEMP
) )
IF(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 FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS HINTS
$ENV{SDL2DIR} $ENV{SDL2DIR}

View File

@ -17,11 +17,16 @@
#include <getopt.h> #include <getopt.h>
#endif #endif
#ifdef _WIN32
#include <Windows.h>
#endif
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
@ -55,6 +60,15 @@ int main(int argc, char **argv) {
bool use_gdbstub = Settings::values.use_gdbstub; bool use_gdbstub = Settings::values.use_gdbstub;
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
char *endarg; 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; std::string boot_filename;
static struct option long_options[] = { static struct option long_options[] = {
@ -86,11 +100,19 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
} else { } else {
#ifdef _WIN32
boot_filename = Common::UTF16ToUTF8(argv_w[optind]);
#else
boot_filename = argv[optind]; boot_filename = argv[optind];
#endif
optind++; optind++;
} }
} }
#ifdef _WIN32
LocalFree(argv_w);
#endif
Log::Filter log_filter(Log::Level::Debug); Log::Filter log_filter(Log::Level::Debug);
Log::SetFilter(&log_filter); Log::SetFilter(&log_filter);
@ -114,7 +136,13 @@ int main(int argc, char **argv) {
System::Init(emu_window.get()); System::Init(emu_window.get());
SCOPE_EXIT({ System::Shutdown(); }); 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) { if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
return -1; return -1;

View File

@ -44,12 +44,16 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
} }
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { 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_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, 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_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() { void Config::ReadValues() {
@ -58,12 +62,13 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[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 // Core
Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0); Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
// Renderer // 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_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);

View File

@ -23,14 +23,19 @@ pad_l =
pad_r = pad_r =
pad_zl = pad_zl =
pad_zr = pad_zr =
pad_sup =
pad_sdown =
pad_sleft =
pad_sright =
pad_cup = pad_cup =
pad_cdown = pad_cdown =
pad_cleft = pad_cleft =
pad_cright = 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] [Core]
# The applied frameskip amount. Must be a power of two. # The applied frameskip amount. Must be a power of two.
@ -39,11 +44,11 @@ frame_skip =
[Renderer] [Renderer]
# Whether to use software or hardware rendering. # Whether to use software or hardware rendering.
# 0 (default): Software, 1: Hardware # 0: Software, 1 (default): Hardware
use_hw_renderer = use_hw_renderer =
# Whether to use the Just-In-Time (JIT) compiler for shader emulation # 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 = use_shader_jit =
# Whether to use native 3DS screen resolution or to scale rendering resolution to the displayed screen size. # 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 # 1 (default): Yes, 0: No
use_virtual_sd = 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 # The system region that Citra will use during emulation
# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan # 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_value = region_value =

View File

@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
KeyPressed({ key, keyboard_id }); KeyMap::PressKey(*this, { key, keyboard_id });
} else if (state == SDL_RELEASED) { } 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() { void EmuWindow_SDL2::ReloadSetKeymaps() {
KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 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]);
} }
} }

View File

@ -2,8 +2,6 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(SRCS set(SRCS
config/controller_config.cpp
config/controller_config_util.cpp
config.cpp config.cpp
debugger/callstack.cpp debugger/callstack.cpp
debugger/disassembler.cpp debugger/disassembler.cpp
@ -11,7 +9,7 @@ set(SRCS
debugger/graphics_breakpoint_observer.cpp debugger/graphics_breakpoint_observer.cpp
debugger/graphics_breakpoints.cpp debugger/graphics_breakpoints.cpp
debugger/graphics_cmdlists.cpp debugger/graphics_cmdlists.cpp
debugger/graphics_framebuffer.cpp debugger/graphics_surface.cpp
debugger/graphics_tracing.cpp debugger/graphics_tracing.cpp
debugger/graphics_vertex_shader.cpp debugger/graphics_vertex_shader.cpp
debugger/profiler.cpp debugger/profiler.cpp
@ -24,6 +22,8 @@ set(SRCS
configure_debug.cpp configure_debug.cpp
configure_dialog.cpp configure_dialog.cpp
configure_general.cpp configure_general.cpp
configure_system.cpp
configure_input.cpp
game_list.cpp game_list.cpp
hotkeys.cpp hotkeys.cpp
main.cpp main.cpp
@ -33,8 +33,6 @@ set(SRCS
) )
set(HEADERS set(HEADERS
config/controller_config.h
config/controller_config_util.h
config.h config.h
debugger/callstack.h debugger/callstack.h
debugger/disassembler.h debugger/disassembler.h
@ -43,7 +41,7 @@ set(HEADERS
debugger/graphics_breakpoints.h debugger/graphics_breakpoints.h
debugger/graphics_breakpoints_p.h debugger/graphics_breakpoints_p.h
debugger/graphics_cmdlists.h debugger/graphics_cmdlists.h
debugger/graphics_framebuffer.h debugger/graphics_surface.h
debugger/graphics_tracing.h debugger/graphics_tracing.h
debugger/graphics_vertex_shader.h debugger/graphics_vertex_shader.h
debugger/profiler.h debugger/profiler.h
@ -56,6 +54,8 @@ set(HEADERS
configure_debug.h configure_debug.h
configure_dialog.h configure_dialog.h
configure_general.h configure_general.h
configure_system.h
configure_input.h
game_list.h game_list.h
game_list_p.h game_list_p.h
hotkeys.h hotkeys.h
@ -65,7 +65,6 @@ set(HEADERS
) )
set(UIS set(UIS
config/controller_config.ui
debugger/callstack.ui debugger/callstack.ui
debugger/disassembler.ui debugger/disassembler.ui
debugger/profiler.ui debugger/profiler.ui
@ -74,6 +73,8 @@ set(UIS
configure_audio.ui configure_audio.ui
configure_debug.ui configure_debug.ui
configure_general.ui configure_general.ui
configure_system.ui
configure_input.ui
hotkeys.ui hotkeys.ui
main.ui main.ui
) )

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>English</string> <string>English</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string></string> <string></string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>

View File

@ -235,12 +235,12 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
void GRenderWindow::keyPressEvent(QKeyEvent* event) void GRenderWindow::keyPressEvent(QKeyEvent* event)
{ {
this->KeyPressed({event->key(), keyboard_id}); KeyMap::PressKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
{ {
this->KeyReleased({event->key(), keyboard_id}); KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
} }
void GRenderWindow::mousePressEvent(QMouseEvent *event) void GRenderWindow::mousePressEvent(QMouseEvent *event)
@ -270,8 +270,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
void GRenderWindow::ReloadSetKeymaps() void GRenderWindow::ReloadSetKeymaps()
{ {
KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 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]);
} }
} }

View File

@ -3,14 +3,11 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QSettings> #include <QSettings>
#include <QString>
#include <QStringList>
#include "citra_qt/config.h" #include "citra_qt/config.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "core/settings.h"
Config::Config() { Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files. // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@ -21,13 +18,17 @@ Config::Config() {
Reload(); 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_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X,
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_M, Qt::Key_N, Qt::Key_B,
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, 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_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() { void Config::ReadValues() {
@ -36,6 +37,7 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] = Settings::values.input_mappings[Settings::NativeInput::All[i]] =
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); 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->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -43,7 +45,7 @@ void Config::ReadValues() {
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Renderer"); 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_shader_jit = qt_config->value("use_shader_jit", true).toBool();
Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).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::values.shortcuts.emplace_back(
UISettings::Shortcut(group + "/" + hotkey, UISettings::Shortcut(group + "/" + hotkey,
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
qt_config->value("Context").toInt()))); qt_config->value("Context").toInt())));
qt_config->endGroup(); qt_config->endGroup();
} }
@ -126,6 +128,7 @@ void Config::SaveValues() {
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
Settings::values.input_mappings[Settings::NativeInput::All[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->endGroup();
qt_config->beginGroup("Core"); qt_config->beginGroup("Core");
@ -185,7 +188,7 @@ void Config::SaveValues() {
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Shortcuts"); 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 + "/KeySeq", shortcut.second.first);
qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); qt_config->setValue(shortcut.first + "/Context", shortcut.second.second);
} }

View File

@ -1,10 +1,13 @@
// Copyright 2014 Citra Emulator Project // Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include <string> #include <string>
#include <QVariant>
#include "core/settings.h"
class QSettings; class QSettings;
@ -20,4 +23,5 @@ public:
void Reload(); void Reload();
void Save(); void Save();
static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults;
}; };

View File

@ -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));
}
}
}
*/

View File

@ -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_

View File

@ -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>

View File

@ -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);
}
*/

View File

@ -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_

View File

@ -24,7 +24,12 @@
<string>General</string> <string>General</string>
</attribute> </attribute>
</widget> </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"> <attribute name="title">
<string>Input</string> <string>Input</string>
</attribute> </attribute>
@ -57,6 +62,12 @@
<header>configure_general.h</header> <header>configure_general.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>ConfigureSystem</class>
<extends>QWidget</extends>
<header>configure_system.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>ConfigureAudio</class> <class>ConfigureAudio</class>
<extends>QWidget</extends> <extends>QWidget</extends>
@ -69,6 +80,12 @@
<header>configure_debug.h</header> <header>configure_debug.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>ConfigureInput</class>
<extends>QWidget</extends>
<header>configure_input.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections> <connections>

View File

@ -9,9 +9,10 @@
#include "core/settings.h" #include "core/settings.h"
ConfigureDialog::ConfigureDialog(QWidget *parent) : ConfigureDialog::ConfigureDialog(QWidget *parent, bool running) :
QDialog(parent), QDialog(parent),
ui(new Ui::ConfigureDialog) ui(new Ui::ConfigureDialog),
emulation_running(running)
{ {
ui->setupUi(this); ui->setupUi(this);
this->setConfiguration(); this->setConfiguration();
@ -21,10 +22,15 @@ ConfigureDialog::~ConfigureDialog() {
} }
void ConfigureDialog::setConfiguration() { void ConfigureDialog::setConfiguration() {
// System tab needs set manually
// depending on whether emulation is running
ui->systemTab->setConfiguration(emulation_running);
} }
void ConfigureDialog::applyConfiguration() { void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration(); ui->generalTab->applyConfiguration();
ui->systemTab->applyConfiguration();
ui->inputTab->applyConfiguration();
ui->audioTab->applyConfiguration(); ui->audioTab->applyConfiguration();
ui->debugTab->applyConfiguration(); ui->debugTab->applyConfiguration();
} }

View File

@ -16,7 +16,7 @@ class ConfigureDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDialog(QWidget *parent = nullptr); explicit ConfigureDialog(QWidget *parent, bool emulation_running);
~ConfigureDialog(); ~ConfigureDialog();
void applyConfiguration(); void applyConfiguration();
@ -26,4 +26,5 @@ private:
private: private:
std::unique_ptr<Ui::ConfigureDialog> ui; std::unique_ptr<Ui::ConfigureDialog> ui;
bool emulation_running;
}; };

View 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);
}
}

View 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();
};

View 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>

View 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);
}

View 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;
};

View 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>

View File

@ -37,10 +37,13 @@ void CallstackWidget::OnDebugModeEntered()
int counter = 0; int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4) for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
{ {
if (!Memory::IsValidVirtualAddress(addr))
break;
const u32 ret_addr = Memory::Read32(addr); const u32 ret_addr = Memory::Read32(addr);
const u32 call_addr = ret_addr - 4; //get call address??? const u32 call_addr = ret_addr - 4; //get call address???
if (Memory::GetPointer(call_addr) == nullptr) if (!Memory::IsValidVirtualAddress(call_addr))
break; break;
/* TODO (mattvail) clean me, move to debugger interface */ /* TODO (mattvail) clean me, move to debugger interface */

View File

@ -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) { GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
} }
@ -249,16 +132,16 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
index = 0; index = 0;
} else if (COMMAND_IN_RANGE(command_id, texture1)) { } else if (COMMAND_IN_RANGE(command_id, texture1)) {
index = 1; index = 1;
} else { } else if (COMMAND_IN_RANGE(command_id, texture2)) {
index = 2; index = 2;
} else {
UNREACHABLE_MSG("Unknown texture command");
} }
auto config = Pica::g_state.regs.GetTextures()[index].config; auto config = Pica::g_state.regs.GetTextures()[index].config;
auto format = Pica::g_state.regs.GetTextures()[index].format; auto format = Pica::g_state.regs.GetTextures()[index].format;
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
// TODO: Instead, emit a signal here to be caught by the main window widget. // TODO: Open a surface debugger
auto main_window = static_cast<QMainWindow*>(parent());
main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
} }
} }

View File

@ -61,25 +61,3 @@ private:
QWidget* command_info_widget; QWidget* command_info_widget;
QPushButton* toggle_tracing; 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;
};

View File

@ -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));
}
}

View File

@ -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;
};

View 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;
}
}

View 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;
};

View File

@ -151,8 +151,8 @@ private:
/// This timer is used to redraw the widget's contents continuously. To save resources, it only /// This timer is used to redraw the widget's contents continuously. To save resources, it only
/// runs while the widget is visible. /// runs while the widget is visible.
QTimer update_timer; QTimer update_timer;
/// Scale the coordinate system appropriately when physical DPI != logical DPI. /// Scale the coordinate system appropriately when dpi != 96.
qreal x_scale, y_scale; qreal x_scale = 1.0, y_scale = 1.0;
}; };
#endif #endif
@ -222,15 +222,14 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
MicroProfileInitUI(); MicroProfileInitUI();
connect(&update_timer, SIGNAL(timeout()), SLOT(update())); 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) { void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
QPainter painter(this); 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.scale(x_scale, y_scale);
painter.setBackground(Qt::black); painter.setBackground(Qt::black);
@ -241,7 +240,7 @@ void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
painter.setFont(font); painter.setFont(font);
mp_painter = &painter; mp_painter = &painter;
MicroProfileDraw(rect().width(), rect().height()); MicroProfileDraw(rect().width() / x_scale, rect().height() / y_scale);
mp_painter = nullptr; mp_painter = nullptr;
} }

View File

@ -118,46 +118,31 @@ void GameList::LoadInterfaceLayout()
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 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 auto callback = [this, recursion](unsigned* num_entries_out,
const std::string& directory, const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name; std::string physical_name = directory + DIR_SEP + virtual_name;
if (stop_processing) if (stop_processing)
return false; // Breaks the callback loop. return false; // Breaks the callback loop.
if (deep_scan && FileUtil::IsDirectory(physical_name)) { if (!FileUtil::IsDirectory(physical_name)) {
AddFstEntriesToGameList(physical_name, true); std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
} else { if (!loader)
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)
return true; 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::vector<u8> smdh;
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name); loader->ReadIcon(smdh);
if (loader)
loader->ReadIcon(smdh);
emit EntryReady({ emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh), 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)), new GameListItemSize(FileUtil::GetSize(physical_name)),
}); });
} else if (recursion > 0) {
AddFstEntriesToGameList(physical_name, recursion - 1);
} }
return true; return true;
@ -169,7 +154,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
void GameListWorker::run() void GameListWorker::run()
{ {
stop_processing = false; stop_processing = false;
AddFstEntriesToGameList(dir_path.toStdString(), deep_scan); AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
emit Finished(); emit Finished();
} }

View File

@ -15,52 +15,21 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "common/color.h" #include "common/color.h"
#include "core/loader/loader.h" #include "core/loader/smdh.h"
#include "video_core/utils.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 * Gets game icon from SMDH
* @param sdmh SMDH data * @param sdmh SMDH data
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
* @return QPixmap game icon * @return QPixmap game icon
*/ */
static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) { static QPixmap GetQPixmapFromSMDH(const Loader::SMDH& smdh, bool large) {
u32 size; std::vector<u16> icon_data = smdh.GetIcon(large);
const u8* icon_data; const uchar* data = reinterpret_cast<const uchar*>(icon_data.data());
int size = large ? 48 : 24;
if (large) { QImage icon(data, size, size, QImage::Format::Format_RGB16);
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()));
}
}
return QPixmap::fromImage(icon); return QPixmap::fromImage(icon);
} }
@ -82,8 +51,8 @@ static QPixmap GetDefaultIcon(bool large) {
* @param language title language * @param language title language
* @return QString short title * @return QString short title
*/ */
static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) {
return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data()); return QString::fromUtf16(smdh.GetShortTitle(language).data());
} }
class GameListItem : public QStandardItem { class GameListItem : public QStandardItem {
@ -112,7 +81,7 @@ public:
{ {
setData(game_path, FullPathRole); setData(game_path, FullPathRole);
if (!IsValidSMDH(smdh_data)) { if (!Loader::IsValidSMDH(smdh_data)) {
// SMDH is not valid, set a default icon // SMDH is not valid, set a default icon
setData(GetDefaultIcon(true), Qt::DecorationRole); setData(GetDefaultIcon(true), Qt::DecorationRole);
return; return;
@ -122,10 +91,10 @@ public:
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
// Get icon from SMDH // Get icon from SMDH
setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole); setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole);
// Get title form SMDH // 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 { QVariant data(int role) const override {
@ -212,5 +181,5 @@ private:
bool deep_scan; bool deep_scan;
std::atomic_bool stop_processing; 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);
}; };

View File

@ -29,7 +29,7 @@
#include "citra_qt/debugger/graphics.h" #include "citra_qt/debugger/graphics.h"
#include "citra_qt/debugger/graphics_breakpoints.h" #include "citra_qt/debugger/graphics_breakpoints.h"
#include "citra_qt/debugger/graphics_cmdlists.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_tracing.h"
#include "citra_qt/debugger/graphics_vertex_shader.h" #include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/profiler.h"
@ -101,10 +101,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide(); 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); auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
graphicsVertexShaderWidget->hide(); graphicsVertexShaderWidget->hide();
@ -113,7 +109,12 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
graphicsTracingWidget->hide(); 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")); QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(graphicsSurfaceViewerAction);
debug_menu->addSeparator();
debug_menu->addAction(profilerWidget->toggleViewAction()); debug_menu->addAction(profilerWidget->toggleViewAction());
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED
debug_menu->addAction(microProfileDialog->toggleViewAction()); 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(graphicsWidget->toggleViewAction());
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
@ -272,7 +272,15 @@ bool GMainWindow::InitializeSystem() {
} }
bool GMainWindow::LoadROM(const std::string& filename) { 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) { if (Loader::ResultStatus::Success != result) {
LOG_CRITICAL(Frontend, "Failed to load ROM!"); LOG_CRITICAL(Frontend, "Failed to load ROM!");
System::Shutdown(); System::Shutdown();
@ -500,15 +508,23 @@ void GMainWindow::ToggleWindowMode() {
} }
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this); ConfigureDialog configureDialog(this, emulation_running);
auto result = configureDialog.exec(); auto result = configureDialog.exec();
if (result == QDialog::Accepted) if (result == QDialog::Accepted)
{ {
configureDialog.applyConfiguration(); configureDialog.applyConfiguration();
render_window->ReloadSetKeymaps();
config->Save(); 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() { bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;

View File

@ -108,6 +108,7 @@ private slots:
void OnConfigure(); void OnConfigure();
void OnDisplayTitleBars(bool); void OnDisplayTitleBars(bool);
void ToggleWindowMode(); void ToggleWindowMode();
void OnCreateGraphicsSurfaceViewer();
private: private:
Ui::MainWindow ui; Ui::MainWindow ui;

View File

@ -72,18 +72,24 @@ inline u64 _rotr64(u64 x, unsigned int shift){
} }
#else // _MSC_VER #else // _MSC_VER
#if (_MSC_VER < 1900)
// Function Cross-Compatibility
#define snprintf _snprintf
#endif
// Locale Cross-Compatibility #if (_MSC_VER < 1900)
#define locale_t _locale_t // 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 #endif // _MSC_VER ndef
// Generic function to get last error message. // Generic function to get last error message.

View File

@ -11,12 +11,28 @@
#include "emu_window.h" #include "emu_window.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
pad_state.hex |= KeyMap::GetPadKey(key).hex; pad_state.hex |= pad.hex;
} }
void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
pad_state.hex &= ~KeyMap::GetPadKey(key).hex; 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) { 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::max(new_x, framebuffer_layout.bottom_screen.left);
new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1); 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) { EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
// When hiding the widget, the function receives a size of 0
ASSERT(width > 0); if (width == 0) width = 1;
ASSERT(height > 0); if (height == 0) height = 1;
EmuWindow::FramebufferLayout res = { width, height, {}, {} }; EmuWindow::FramebufferLayout res = { width, height, {}, {} };

View File

@ -12,10 +12,6 @@
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
namespace KeyMap {
struct HostDeviceKey;
}
/** /**
* Abstraction class used to provide an interface between emulation code and the frontend * Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...). * (e.g. SDL, QGLWidget, GLFW, etc...).
@ -76,11 +72,27 @@ public:
virtual void ReloadSetKeymaps() = 0; 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) * 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); 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 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. * @todo Fix this function to be thread-safe.
* @return PadState object indicating the current pad state * @return PadState object indicating the current pad state
*/ */
@ -109,6 +122,16 @@ public:
return pad_state; 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). * 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. * @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; pad_state.hex = 0;
touch_x = 0; touch_x = 0;
touch_y = 0; touch_y = 0;
circle_pad_x = 0;
circle_pad_y = 0;
touch_pressed = false; touch_pressed = false;
} }
virtual ~EmuWindow() {} virtual ~EmuWindow() {}
@ -260,6 +285,9 @@ private:
u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 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) 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. * Clip the provided coordinates to be inside the touchscreen area.
*/ */

View File

@ -486,30 +486,33 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
closedir(dirp); closedir(dirp);
#endif #endif
if (!callback_error) { 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 {
return false; 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 auto callback = [recursion, &parent_entry](unsigned* num_entries_out,
const std::string& directory, const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
FSTEntry entry; FSTEntry entry;
entry.virtualName = virtual_name; entry.virtualName = virtual_name;
entry.physicalName = directory + DIR_SEP + virtual_name; entry.physicalName = directory + DIR_SEP + virtual_name;
if (IsDirectory(entry.physicalName)) { if (IsDirectory(entry.physicalName)) {
entry.isDirectory = true; entry.isDirectory = true;
// is a directory, lets go inside // is a directory, lets go inside if we didn't recurse to often
entry.size = ScanDirectoryTree(entry.physicalName, entry); if (recursion > 0) {
*num_entries_out += (int)entry.size; entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1);
*num_entries_out += (int)entry.size;
} else {
entry.size = 0;
}
} else { // is a file } else { // is a file
entry.isDirectory = false; entry.isDirectory = false;
entry.size = GetSize(entry.physicalName); 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 auto callback = [recursion](unsigned* num_entries_out,
const std::string& directory, const std::string& directory,
const std::string& virtual_name) -> bool { const std::string& virtual_name) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name; 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); return Delete(new_path);
}; };

View File

@ -125,12 +125,13 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
* Scans the directory tree, storing the results. * Scans the directory tree, storing the results.
* @param directory the parent directory to start scanning from * @param directory the parent directory to start scanning from
* @param parent_entry FSTEntry where the filesystem tree results will be stored. * @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 * @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. // 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 // Returns the current directory
std::string GetCurrentDir(); std::string GetCurrentDir();

View File

@ -2,24 +2,138 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "key_map.h"
#include <map> #include <map>
#include "common/emu_window.h"
#include "common/key_map.h"
namespace KeyMap { 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 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() { int NewDeviceId() {
return next_device_id++; return next_device_id++;
} }
void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) { void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
key_map[key].hex = padState.hex; key_map[key] = target;
} }
Service::HID::PadState GetPadKey(HostDeviceKey key) { void ClearKeyMapping(int device_id) {
return key_map[key]; 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;
}
}
} }
} }

View File

@ -4,11 +4,50 @@
#pragma once #pragma once
#include <array>
#include <tuple> #include <tuple>
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid.h"
class EmuWindow;
namespace KeyMap { 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. * 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. * Generates a new device id, which uniquely identifies a host device within KeyMap.
*/ */
int NewDeviceId(); 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);
} }

View File

@ -118,7 +118,7 @@ Entry CreateEntry(Class log_class, Level log_level,
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
entry.message = std::string(formatting_buffer.data()); entry.message = std::string(formatting_buffer.data());
return std::move(entry); return entry;
} }
static Filter* filter = nullptr; static Filter* filter = nullptr;

View File

@ -5,6 +5,7 @@ set(SRCS
arm/dyncom/arm_dyncom_dec.cpp arm/dyncom/arm_dyncom_dec.cpp
arm/dyncom/arm_dyncom_interpreter.cpp arm/dyncom/arm_dyncom_interpreter.cpp
arm/dyncom/arm_dyncom_thumb.cpp arm/dyncom/arm_dyncom_thumb.cpp
arm/dyncom/arm_dyncom_trans.cpp
arm/skyeye_common/armstate.cpp arm/skyeye_common/armstate.cpp
arm/skyeye_common/armsupp.cpp arm/skyeye_common/armsupp.cpp
arm/skyeye_common/vfp/vfp.cpp arm/skyeye_common/vfp/vfp.cpp
@ -26,9 +27,11 @@ set(SRCS
hle/config_mem.cpp hle/config_mem.cpp
hle/hle.cpp hle/hle.cpp
hle/applets/applet.cpp hle/applets/applet.cpp
hle/applets/erreula.cpp
hle/applets/mii_selector.cpp hle/applets/mii_selector.cpp
hle/applets/swkbd.cpp hle/applets/swkbd.cpp
hle/kernel/address_arbiter.cpp hle/kernel/address_arbiter.cpp
hle/kernel/client_port.cpp
hle/kernel/event.cpp hle/kernel/event.cpp
hle/kernel/kernel.cpp hle/kernel/kernel.cpp
hle/kernel/memory.cpp hle/kernel/memory.cpp
@ -36,6 +39,7 @@ set(SRCS
hle/kernel/process.cpp hle/kernel/process.cpp
hle/kernel/resource_limit.cpp hle/kernel/resource_limit.cpp
hle/kernel/semaphore.cpp hle/kernel/semaphore.cpp
hle/kernel/server_port.cpp
hle/kernel/session.cpp hle/kernel/session.cpp
hle/kernel/shared_memory.cpp hle/kernel/shared_memory.cpp
hle/kernel/thread.cpp hle/kernel/thread.cpp
@ -70,7 +74,10 @@ set(SRCS
hle/service/cfg/cfg_s.cpp hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.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/dsp_dsp.cpp
hle/service/err_f.cpp hle/service/err_f.cpp
hle/service/frd/frd.cpp hle/service/frd/frd.cpp
@ -121,6 +128,7 @@ set(SRCS
loader/elf.cpp loader/elf.cpp
loader/loader.cpp loader/loader.cpp
loader/ncch.cpp loader/ncch.cpp
loader/smdh.cpp
tracer/recorder.cpp tracer/recorder.cpp
memory.cpp memory.cpp
settings.cpp settings.cpp
@ -136,6 +144,7 @@ set(HEADERS
arm/dyncom/arm_dyncom_interpreter.h arm/dyncom/arm_dyncom_interpreter.h
arm/dyncom/arm_dyncom_run.h arm/dyncom/arm_dyncom_run.h
arm/dyncom/arm_dyncom_thumb.h arm/dyncom/arm_dyncom_thumb.h
arm/dyncom/arm_dyncom_trans.h
arm/skyeye_common/arm_regformat.h arm/skyeye_common/arm_regformat.h
arm/skyeye_common/armstate.h arm/skyeye_common/armstate.h
arm/skyeye_common/armsupp.h arm/skyeye_common/armsupp.h
@ -160,9 +169,11 @@ set(HEADERS
hle/function_wrappers.h hle/function_wrappers.h
hle/hle.h hle/hle.h
hle/applets/applet.h hle/applets/applet.h
hle/applets/erreula.h
hle/applets/mii_selector.h hle/applets/mii_selector.h
hle/applets/swkbd.h hle/applets/swkbd.h
hle/kernel/address_arbiter.h hle/kernel/address_arbiter.h
hle/kernel/client_port.h
hle/kernel/event.h hle/kernel/event.h
hle/kernel/kernel.h hle/kernel/kernel.h
hle/kernel/memory.h hle/kernel/memory.h
@ -170,6 +181,7 @@ set(HEADERS
hle/kernel/process.h hle/kernel/process.h
hle/kernel/resource_limit.h hle/kernel/resource_limit.h
hle/kernel/semaphore.h hle/kernel/semaphore.h
hle/kernel/server_port.h
hle/kernel/session.h hle/kernel/session.h
hle/kernel/shared_memory.h hle/kernel/shared_memory.h
hle/kernel/thread.h hle/kernel/thread.h
@ -205,7 +217,10 @@ set(HEADERS
hle/service/cfg/cfg_s.h hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h hle/service/cfg/cfg_u.h
hle/service/csnd_snd.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/dsp_dsp.h
hle/service/err_f.h hle/service/err_f.h
hle/service/frd/frd.h hle/service/frd/frd.h
@ -256,6 +271,7 @@ set(HEADERS
loader/elf.h loader/elf.h
loader/loader.h loader/loader.h
loader/ncch.h loader/ncch.h
loader/smdh.h
tracer/recorder.h tracer/recorder.h
tracer/citrace.h tracer/citrace.h
memory.h memory.h

View File

@ -422,6 +422,10 @@ ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) {
n = arm_instruction[i].attribute_value; n = arm_instruction[i].attribute_value;
base = 0; base = 0;
// 3DS has no VFP3 support
if (arm_instruction[i].version == ARMVFP3)
continue;
while (n) { while (n) {
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) { if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
// clrex // clrex

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cstddef>
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding // We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
// ARM instruction, and using the existing ARM simulator. // 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 | (BIT(tinstr, 4) << 18); // enable bit
} }
} else if ((tinstr & 0x0F00) == 0x0a00) { } else if ((tinstr & 0x0F00) == 0x0a00) {
static const u32 subset[3] = { static const u32 subset[4] = {
0xE6BF0F30, // REV 0xE6BF0F30, // REV
0xE6BF0FB0, // REV16 0xE6BF0FB0, // REV16
0, // undefined
0xE6FF0FB0, // REVSH 0xE6FF0FB0, // REVSH
}; };
*ainstr = subset[BITS(tinstr, 6, 7)] // base size_t subset_index = BITS(tinstr, 6, 7);
| (BITS(tinstr, 0, 2) << 12) // Rd
| BITS(tinstr, 3, 5); // Rm 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 { } else {
static const u32 subset[4] = { static const u32 subset[4] = {
0xE92D0000, // STMDB sp!,{rlist} 0xE92D0000, // STMDB sp!,{rlist}

File diff suppressed because it is too large Load Diff

View 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;

View File

@ -271,8 +271,9 @@ inline int vfp_single_type(const vfp_single* s)
// Unpack a single-precision float. Note that this returns the magnitude // Unpack a single-precision float. Note that this returns the magnitude
// of the single-precision float mantissa with the 1. if necessary, // of the single-precision float mantissa with the 1. if necessary,
// aligned to bit 30. // 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->sign = vfp_single_packed_sign(val) >> 16,
s->exponent = vfp_single_packed_exponent(val); 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. // If flush-to-zero mode is enabled, turn the denormal into zero.
// On a VFPv2 architecture, the sign of the zero is always positive. // 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->sign = 0;
s->exponent = 0; s->exponent = 0;
s->significand = 0; s->significand = 0;
*fpscr |= FPSCR_IDC; exceptions |= FPSCR_IDC;
} }
return exceptions;
} }
// Re-pack a single-precision float. This assumes that the float is // 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 // Double-precision
struct vfp_double { 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 // Unpack a double-precision float. Note that this returns the magnitude
// of the double-precision float mantissa with the 1. if necessary, // of the double-precision float mantissa with the 1. if necessary,
// aligned to bit 62. // 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->sign = vfp_double_packed_sign(val) >> 48;
s->exponent = vfp_double_packed_exponent(val); 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. // If flush-to-zero mode is enabled, turn the denormal into zero.
// On a VFPv2 architecture, the sign of the zero is always positive. // 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->sign = 0;
s->exponent = 0; s->exponent = 0;
s->significand = 0; s->significand = 0;
*fpscr |= FPSCR_IDC; exceptions |= FPSCR_IDC;
} }
return exceptions;
} }
// Re-pack a double-precision float. This assumes that the float is // 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_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_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);

View File

@ -85,11 +85,12 @@ static void vfp_double_normalise_denormal(struct vfp_double *vd)
vfp_double_dump("normalise_denormal: out", 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; u64 significand, incr;
int exponent, shift, underflow; int exponent, shift, underflow;
u32 rmode; u32 rmode;
u32 exceptions = 0;
vfp_double_dump("pack: in", vd); 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__); LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double vdm, vdd, *vdp; vfp_double vdm, vdd, *vdp;
int ret, tm; 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); tm = vfp_double_type(&vdm);
if (tm & (VFP_NAN|VFP_INFINITY)) { if (tm & (VFP_NAN|VFP_INFINITY)) {
@ -369,7 +371,8 @@ sqrt_invalid:
} }
vdd.significand = vfp_shiftright64jamming(vdd.significand, 1); 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; u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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); 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 else
vsd.exponent = vdm.exponent - (1023 - 127); 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: pack_nan:
vfp_put_float(state, vfp_single_pack(&vsd), sd); 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) static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{ {
struct vfp_double vdm; struct vfp_double vdm;
u32 exceptions = 0;
u32 m = vfp_get_float(state, dm); u32 m = vfp_get_float(state, dm);
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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.exponent = 1023 + 63 - 1;
vdm.significand = (u64)m; 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) static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{ {
struct vfp_double vdm; struct vfp_double vdm;
u32 exceptions = 0;
u32 m = vfp_get_float(state, dm); u32 m = vfp_get_float(state, dm);
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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.exponent = 1023 + 63 - 1;
vdm.significand = vdm.sign ? (~m + 1) : m; 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) 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; int tm;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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? * 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) { if (vdm.exponent >= 1023 + 32) {
d = vdm.sign ? 0 : 0xffffffff; d = vdm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC; exceptions = FPSCR_IOC;
} else if (vdm.exponent >= 1023 - 1) { } else if (vdm.exponent >= 1023) {
int shift = 1023 + 63 - vdm.exponent; int shift = 1023 + 63 - vdm.exponent;
u64 rem, incr = 0; u64 rem, incr = 0;
@ -595,12 +603,20 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
} else { } else {
d = 0; d = 0;
if (vdm.exponent | vdm.significand) { if (vdm.exponent | vdm.significand) {
exceptions |= FPSCR_IXC; if (rmode == FPSCR_ROUND_NEAREST) {
if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) 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; d = 1;
else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) { exceptions |= FPSCR_IXC;
d = 0; } else if (rmode == FPSCR_ROUND_MINUSINF) {
exceptions |= FPSCR_IOC; 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) static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
{ {
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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) 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; int tm;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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); 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) { if (tm & VFP_NAN) {
d = 0; d = 0;
exceptions |= FPSCR_IOC; exceptions |= FPSCR_IOC;
} else if (vdm.exponent >= 1023 + 32) { } else if (vdm.exponent >= 1023 + 31) {
d = 0x7fffffff; d = 0x7fffffff;
if (vdm.sign) if (vdm.sign)
d = ~d; d = ~d;
exceptions |= FPSCR_IOC; exceptions |= FPSCR_IOC;
} else if (vdm.exponent >= 1023 - 1) { } else if (vdm.exponent >= 1023) {
int shift = 1023 + 63 - vdm.exponent; /* 58 */ int shift = 1023 + 63 - vdm.exponent; /* 58 */
u64 rem, incr = 0; 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; d = 0;
if (vdm.exponent | vdm.significand) { if (vdm.exponent | vdm.significand) {
exceptions |= FPSCR_IXC; 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; d = 1;
else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) } else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
d = -1; 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) static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{ {
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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[] = { 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) 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; 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) if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn); 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) if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm); 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) if (negate & NEG_MULTIPLY)
vdp.sign = vfp_sign_negate(vdp.sign); 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) if (vdn.exponent == 0 && vdn.significand != 0)
vfp_double_normalise_denormal(&vdn); 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); 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) static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{ {
struct vfp_double vdd, vdn, vdm; struct vfp_double vdd, vdn, vdm;
u32 exceptions; u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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) if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn); 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) if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm); vfp_double_normalise_denormal(&vdm);
exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fmul");
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) static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{ {
struct vfp_double vdd, vdn, vdm; struct vfp_double vdd, vdn, vdm;
u32 exceptions; u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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) if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn); 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) if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm); 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); 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) static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{ {
struct vfp_double vdd, vdn, vdm; struct vfp_double vdd, vdn, vdm;
u32 exceptions; u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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) if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn); 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) if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm); 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) static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{ {
struct vfp_double vdd, vdn, vdm; struct vfp_double vdd, vdn, vdm;
u32 exceptions; u32 exceptions = 0;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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) if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn); 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) if (vdm.exponent == 0 && vdm.significand)
vfp_double_normalise_denormal(&vdm); 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); 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; int tm, tn;
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); 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);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr);
vdd.sign = vdn.sign ^ vdm.sign; 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); 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: vdn_nan:
exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); exceptions |= vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
pack: pack:
vfp_put_double(state, vfp_double_pack(&vdd), dd); vfp_put_double(state, vfp_double_pack(&vdd), dd);
return exceptions; return exceptions;
vdm_nan: vdm_nan:
exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr); exceptions |= vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
goto pack; goto pack;
zero: zero:
@ -1149,7 +1180,7 @@ zero:
goto pack; goto pack;
divzero: divzero:
exceptions = FPSCR_DZC; exceptions |= FPSCR_DZC;
infinity: infinity:
vdd.exponent = 2047; vdd.exponent = 2047;
vdd.significand = 0; vdd.significand = 0;
@ -1157,7 +1188,8 @@ infinity:
invalid: invalid:
vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd); vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd);
return FPSCR_IOC; exceptions |= FPSCR_IOC;
return exceptions;
} }
static struct op fops[] = { static struct op fops[] = {

View File

@ -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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; 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); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; 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); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->dp_operation = BIT(inst, 8); inst_cream->dp_operation = BIT(inst, 8);
inst_cream->instr = inst; 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; 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->to_arm = BIT(inst, 20) == 1;
inst_cream->t = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->reg = BITS(inst, 16, 19); inst_cream->reg = BITS(inst, 16, 19);
inst_cream->Rt = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; 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->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4;
inst_cream->t = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->reg = BITS(inst, 16, 19); inst_cream->reg = BITS(inst, 16, 19);
inst_cream->Rt = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; 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->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4;
inst_cream->t = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; 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->to_arm = BIT(inst, 20) == 1;
inst_cream->t = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; 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->to_arm = BIT(inst, 20) == 1;
inst_cream->t = BITS(inst, 12, 15); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; inst_cream->single = BIT(inst, 8) == 0;
inst_cream->add = BIT(inst, 23); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; 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); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; inst_cream->single = BIT(inst, 8) == 0;
inst_cream->add = BIT(inst, 23); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; 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)); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; inst_cream->single = BIT(inst, 8) == 0;
inst_cream->add = BIT(inst, 23); 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->cond = BITS(inst, 28, 31);
inst_base->idx = index; inst_base->idx = index;
inst_base->br = NON_BRANCH; inst_base->br = TransExtData::NON_BRANCH;
inst_cream->single = BIT(inst, 8) == 0; inst_cream->single = BIT(inst, 8) == 0;
inst_cream->add = BIT(inst, 23); inst_cream->add = BIT(inst, 23);

View File

@ -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; u32 significand, incr, rmode;
int exponent, shift, underflow; int exponent, shift, underflow;
u32 exceptions = 0;
vfp_single_dump("pack: in", vs); 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; struct vfp_single vsm, vsd, *vsp;
int ret, tm; int ret, tm;
u32 exceptions = 0;
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
tm = vfp_single_type(&vsm); tm = vfp_single_type(&vsm);
if (tm & (VFP_NAN|VFP_INFINITY)) { if (tm & (VFP_NAN|VFP_INFINITY)) {
vsp = &vsd; vsp = &vsd;
@ -408,7 +410,8 @@ sqrt_invalid:
} }
vsd.significand = vfp_shiftright32jamming(vsd.significand, 1); 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; int tm;
u32 exceptions = 0; u32 exceptions = 0;
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
tm = vfp_single_type(&vsm); 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 we have a signalling NaN, signal invalid operation.
*/ */
if (tm == VFP_SNAN) if (tm == VFP_SNAN)
exceptions = FPSCR_IOC; exceptions |= FPSCR_IOC;
if (tm & VFP_DENORMAL) if (tm & VFP_DENORMAL)
vfp_single_normalise_denormal(&vsm); 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 else
vdd.exponent = vsm.exponent + (1023 - 127); 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: pack_nan:
vfp_put_double(state, vfp_double_pack(&vdd), dd); 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) static u32 vfp_single_fuito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{ {
struct vfp_single vs; struct vfp_single vs;
u32 exceptions = 0;
vs.sign = 0; vs.sign = 0;
vs.exponent = 127 + 31 - 1; vs.exponent = 127 + 31 - 1;
vs.significand = (u32)m; 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) static u32 vfp_single_fsito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{ {
struct vfp_single vs; struct vfp_single vs;
u32 exceptions = 0;
vs.sign = (m & 0x80000000) >> 16; vs.sign = (m & 0x80000000) >> 16;
vs.exponent = 127 + 31 - 1; vs.exponent = 127 + 31 - 1;
vs.significand = vs.sign ? -m : m; 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) 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 rmode = fpscr & FPSCR_RMODE_MASK;
int tm; int tm;
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
vfp_single_dump("VSM", &vsm); 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) { if (vsm.exponent >= 127 + 32) {
d = vsm.sign ? 0 : 0xffffffff; d = vsm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC; exceptions |= FPSCR_IOC;
} else if (vsm.exponent >= 127) { } else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent; int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0; 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 * 2^0 <= m < 2^32-2^8
*/ */
d = (vsm.significand << 1) >> shift; 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) { if (rmode == FPSCR_ROUND_NEAREST) {
incr = 0x80000000; incr = 0x80000000;
@ -619,12 +631,20 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
} else { } else {
d = 0; d = 0;
if (vsm.exponent | vsm.significand) { if (vsm.exponent | vsm.significand) {
exceptions |= FPSCR_IXC; if (rmode == FPSCR_ROUND_NEAREST) {
if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) 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; d = 1;
else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) { exceptions |= FPSCR_IXC;
d = 0; } else if (rmode == FPSCR_ROUND_MINUSINF) {
exceptions |= FPSCR_IOC; 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) 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) 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 rmode = fpscr & FPSCR_RMODE_MASK;
int tm; int tm;
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
vfp_single_dump("VSM", &vsm); 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) { if (tm & VFP_NAN) {
d = 0; d = 0;
exceptions |= FPSCR_IOC; exceptions |= FPSCR_IOC;
} else if (vsm.exponent >= 127 + 32) { } else if (vsm.exponent >= 127 + 31) {
/* /*
* m >= 2^31-2^7: invalid * 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 */ /* 2^0 <= m <= 2^31-2^7 */
d = (vsm.significand << 1) >> shift; d = (vsm.significand << 1) >> shift;
rem = vsm.significand << (33 - shift); rem = (vsm.significand << 1) << (32 - shift);
if (rmode == FPSCR_ROUND_NEAREST) { if (rmode == FPSCR_ROUND_NEAREST) {
incr = 0x80000000; incr = 0x80000000;
@ -701,10 +721,14 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
d = 0; d = 0;
if (vsm.exponent | vsm.significand) { if (vsm.exponent | vsm.significand) {
exceptions |= FPSCR_IXC; 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; d = 1;
else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) } else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
d = -1; 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) 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[] = { static struct op fops_ext[] = {
@ -774,7 +798,7 @@ vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
/* /*
* different signs -> invalid * different signs -> invalid
*/ */
exceptions = FPSCR_IOC; exceptions |= FPSCR_IOC;
vsp = &vfp_single_default_qnan; vsp = &vfp_single_default_qnan;
} else { } 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_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func)
{ {
vfp_single vsd, vsp, vsn, vsm; vfp_single vsd, vsp, vsn, vsm;
u32 exceptions; u32 exceptions = 0;
s32 v; s32 v;
v = vfp_get_float(state, sn); v = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, v); 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) if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn); vfp_single_normalise_denormal(&vsn);
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand) if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm); 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) if (negate & NEG_MULTIPLY)
vsp.sign = vfp_sign_negate(vsp.sign); vsp.sign = vfp_sign_negate(vsp.sign);
v = vfp_get_float(state, sd); v = vfp_get_float(state, sd);
LOG_TRACE(Core_ARM11, "s%u = %08x", sd, v); 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) if (vsn.exponent == 0 && vsn.significand != 0)
vfp_single_normalise_denormal(&vsn); 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); 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) 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); 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) static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{ {
struct vfp_single vsd, vsn, vsm; struct vfp_single vsd, vsn, vsm;
u32 exceptions; u32 exceptions = 0;
s32 n = vfp_get_float(state, sn); s32 n = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); 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) if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn); vfp_single_normalise_denormal(&vsn);
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand) if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm); vfp_single_normalise_denormal(&vsm);
exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fmul");
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) static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{ {
struct vfp_single vsd, vsn, vsm; struct vfp_single vsd, vsn, vsm;
u32 exceptions; u32 exceptions = 0;
s32 n = vfp_get_float(state, sn); s32 n = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); 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) if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn); vfp_single_normalise_denormal(&vsn);
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand) if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm); 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); 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) static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
{ {
struct vfp_single vsd, vsn, vsm; struct vfp_single vsd, vsn, vsm;
u32 exceptions; u32 exceptions = 0;
s32 n = vfp_get_float(state, sn); s32 n = vfp_get_float(state, sn);
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); 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. * Unpack and normalise denormals.
*/ */
vfp_single_unpack(&vsn, n, &fpscr); exceptions |= vfp_single_unpack(&vsn, n, fpscr);
if (vsn.exponent == 0 && vsn.significand) if (vsn.exponent == 0 && vsn.significand)
vfp_single_normalise_denormal(&vsn); vfp_single_normalise_denormal(&vsn);
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
if (vsm.exponent == 0 && vsm.significand) if (vsm.exponent == 0 && vsm.significand)
vfp_single_normalise_denormal(&vsm); 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); LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n);
vfp_single_unpack(&vsn, n, &fpscr); exceptions |= vfp_single_unpack(&vsn, n, fpscr);
vfp_single_unpack(&vsm, m, &fpscr); exceptions |= vfp_single_unpack(&vsm, m, fpscr);
vsd.sign = vsn.sign ^ vsm.sign; 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) if ((vsd.significand & 0x3f) == 0)
vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32); 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: vsn_nan:
exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr); exceptions |= vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
pack: pack:
vfp_put_float(state, vfp_single_pack(&vsd), sd); vfp_put_float(state, vfp_single_pack(&vsd), sd);
return exceptions; return exceptions;
vsm_nan: vsm_nan:
exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr); exceptions |= vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
goto pack; goto pack;
zero: zero:
@ -1180,7 +1213,7 @@ zero:
goto pack; goto pack;
divzero: divzero:
exceptions = FPSCR_DZC; exceptions |= FPSCR_DZC;
infinity: infinity:
vsd.exponent = 255; vsd.exponent = 255;
vsd.significand = 0; vsd.significand = 0;
@ -1188,7 +1221,8 @@ infinity:
invalid: invalid:
vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd); vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd);
return FPSCR_IOC; exceptions |= FPSCR_IOC;
return exceptions;
} }
static struct op fops[] = { static struct op fops[] = {

View File

@ -26,7 +26,7 @@
extern int g_clock_rate_arm11; extern int g_clock_rate_arm11;
inline s64 msToCycles(int ms) { 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) { inline s64 msToCycles(float ms) {

View File

@ -19,22 +19,22 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
switch (type) { switch (type) {
case Binary: case Binary:
{ {
u8* data = Memory::GetPointer(pointer); binary.resize(size);
binary = std::vector<u8>(data, data + size); Memory::ReadBlock(pointer, binary.data(), binary.size());
break; break;
} }
case Char: case Char:
{ {
const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); string.resize(size - 1); // Data is always null-terminated.
string = std::string(data, size - 1); // Data is always null-terminated. Memory::ReadBlock(pointer, &string[0], string.size());
break; break;
} }
case Wchar: case Wchar:
{ {
const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer)); u16str.resize(size / 2 - 1); // Data is always null-terminated.
u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated. Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t));
break; break;
} }

View File

@ -646,7 +646,7 @@ static void ReadMemory() {
u8* data = Memory::GetPointer(addr); u8* data = Memory::GetPointer(addr);
if (!data) { if (!data) {
return SendReply("E0"); return SendReply("E00");
} }
MemToGdbHex(reply, data, len); MemToGdbHex(reply, data, len);

View File

@ -12,6 +12,7 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/applets/applet.h" #include "core/hle/applets/applet.h"
#include "core/hle/applets/erreula.h"
#include "core/hle/applets/mii_selector.h" #include "core/hle/applets/mii_selector.h"
#include "core/hle/applets/swkbd.h" #include "core/hle/applets/swkbd.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -52,6 +53,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) {
case Service::APT::AppletId::Ed2: case Service::APT::AppletId::Ed2:
applets[id] = std::make_shared<MiiSelector>(id); applets[id] = std::make_shared<MiiSelector>(id);
break; break;
case Service::APT::AppletId::Error:
case Service::APT::AppletId::Error2:
applets[id] = std::make_shared<ErrEula>(id);
break;
default: default:
LOG_ERROR(Service_APT, "Could not create applet %u", id); LOG_ERROR(Service_APT, "Could not create applet %u", id);
// TODO(Subv): Find the right error code // TODO(Subv): Find the right error code

View 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

View 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

View File

@ -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. // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
// Create the SharedMemory that will hold the framebuffer data // Create the SharedMemory that will hold the framebuffer data
Service::APT::CaptureBufferInfo capture_info; 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; using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet. // 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 // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result; Service::APT::MessageParameter result;
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
result.data = nullptr; result.buffer.clear();
result.buffer_size = 0;
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id); result.sender_id = static_cast<u32>(id);
result.object = framebuffer_memory; 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): Set the expected fields in the response buffer before resending it to the application.
// TODO(Subv): Reverse the parameter format for the Mii Selector // TODO(Subv): Reverse the parameter format for the Mii Selector
if(parameter.buffer_size >= sizeof(u32)) { memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
// TODO: defaults return no error, but garbage in other unknown fields
memset(parameter.data, 0, sizeof(u32)); // 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 // Let the application know that we're closing
Service::APT::MessageParameter message; Service::APT::MessageParameter message;
message.buffer_size = parameter.buffer_size; message.buffer.resize(sizeof(MiiResult));
message.data = parameter.data; std::memcpy(message.buffer.data(), &result, message.buffer.size());
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id); message.sender_id = static_cast<u32>(id);

View File

@ -24,7 +24,7 @@ struct MiiConfig {
u8 unk_004; u8 unk_004;
INSERT_PADDING_BYTES(3); INSERT_PADDING_BYTES(3);
u16 unk_008; u16 unk_008;
INSERT_PADDING_BYTES(0x8C - 0xA); INSERT_PADDING_BYTES(0x82);
u8 unk_08C; u8 unk_08C;
INSERT_PADDING_BYTES(3); INSERT_PADDING_BYTES(3);
u16 unk_090; u16 unk_090;
@ -75,6 +75,8 @@ public:
/// Whether this applet is currently running instead of the host application or not. /// Whether this applet is currently running instead of the host application or not.
bool started; bool started;
MiiConfig config;
}; };
} }

View File

@ -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. // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
// Create the SharedMemory that will hold the framebuffer data // Create the SharedMemory that will hold the framebuffer data
Service::APT::CaptureBufferInfo capture_info; 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; using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet. // 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 // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result; Service::APT::MessageParameter result;
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
result.data = nullptr; result.buffer.clear();
result.buffer_size = 0;
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id); result.sender_id = static_cast<u32>(id);
result.object = framebuffer_memory; result.object = framebuffer_memory;
@ -61,9 +60,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
} }
ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) { 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); text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object);
// TODO(Subv): Verify if this is the correct behavior // 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]; auto info = bottom_screen->framebuffer_info[bottom_screen->index];
// TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer // 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); GSP_GPU::SetBufferSwap(1, info);
} }
@ -107,8 +106,8 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
void SoftwareKeyboard::Finalize() { void SoftwareKeyboard::Finalize() {
// Let the application know that we're closing // Let the application know that we're closing
Service::APT::MessageParameter message; Service::APT::MessageParameter message;
message.buffer_size = sizeof(SoftwareKeyboardConfig); message.buffer.resize(sizeof(SoftwareKeyboardConfig));
message.data = reinterpret_cast<u8*>(&config); std::memcpy(message.buffer.data(), &config, message.buffer.size());
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id); message.sender_id = static_cast<u32>(id);

View File

@ -194,6 +194,16 @@ template<ResultCode func(Handle, u32)> void Wrap() {
FuncReturn(func(PARAM(0), PARAM(1)).raw); 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(&param_1, &param_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 // Function wrappers that return type u32

View 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

View 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

View File

@ -35,7 +35,7 @@ enum KernelHandle : Handle {
enum class HandleType : u32 { enum class HandleType : u32 {
Unknown = 0, Unknown = 0,
Port = 1,
Session = 2, Session = 2,
Event = 3, Event = 3,
Mutex = 4, Mutex = 4,
@ -48,6 +48,8 @@ enum class HandleType : u32 {
Timer = 11, Timer = 11,
ResourceLimit = 12, ResourceLimit = 12,
CodeSet = 13, CodeSet = 13,
ClientPort = 14,
ServerPort = 15,
}; };
enum { enum {
@ -72,6 +74,7 @@ public:
bool IsWaitable() const { bool IsWaitable() const {
switch (GetHandleType()) { switch (GetHandleType()) {
case HandleType::Session: case HandleType::Session:
case HandleType::ServerPort:
case HandleType::Event: case HandleType::Event:
case HandleType::Mutex: case HandleType::Mutex:
case HandleType::Thread: case HandleType::Thread:
@ -80,13 +83,13 @@ public:
return true; return true;
case HandleType::Unknown: case HandleType::Unknown:
case HandleType::Port:
case HandleType::SharedMemory: case HandleType::SharedMemory:
case HandleType::Redirection: case HandleType::Redirection:
case HandleType::Process: case HandleType::Process:
case HandleType::AddressArbiter: case HandleType::AddressArbiter:
case HandleType::ResourceLimit: case HandleType::ResourceLimit:
case HandleType::CodeSet: case HandleType::CodeSet:
case HandleType::ClientPort:
return false; return false;
} }
} }

View 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

View 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

View File

@ -32,6 +32,10 @@ constexpr u32 CallingPidDesc() {
return 0x20; return 0x20;
} }
constexpr u32 TransferHandleDesc() {
return 0x20;
}
constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) { constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) {
return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10); return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10);
} }

View File

@ -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 * Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to * @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 // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
new_thread->context.pc -= thumb_mode ? 2 : 4; 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 // 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; 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); LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
// TODO: Verify error // TODO: Verify error
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@ -542,8 +591,12 @@ void Reschedule() {
HLE::DoneRescheduling(); HLE::DoneRescheduling();
// Don't bother switching to the same thread // Don't bother switching to the same thread.
if (next == cur) // 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; return;
if (cur && next) { if (cur && next) {

View File

@ -7,6 +7,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/memory_setup.h" #include "core/memory_setup.h"
#include "core/mmio.h" #include "core/mmio.h"

View File

@ -20,12 +20,14 @@ enum class ErrorDescription : u32 {
WrongPermission = 46, WrongPermission = 46,
OS_InvalidBufferDescriptor = 48, OS_InvalidBufferDescriptor = 48,
WrongAddress = 53, WrongAddress = 53,
FS_ArchiveNotMounted = 101,
FS_NotFound = 120, FS_NotFound = 120,
FS_AlreadyExists = 190, FS_AlreadyExists = 190,
FS_InvalidOpenFlags = 230, FS_InvalidOpenFlags = 230,
FS_NotAFile = 250, FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive 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 OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage
GPU_FirstInitialization = 519,
FS_InvalidPath = 702, FS_InvalidPath = 702,
InvalidSection = 1000, InvalidSection = 1000,
TooLarge = 1001, TooLarge = 1001,
@ -134,15 +136,28 @@ enum class ErrorModule : u32 {
MCU = 72, MCU = 72,
NS = 73, NS = 73,
News = 74, News = 74,
RO_1 = 75, RO = 75,
GD = 76, GD = 76,
CardSPI = 77, CardSPI = 77,
EC = 78, EC = 78,
RO_2 = 79, WebBrowser = 79,
WebBrowser = 80, Test = 80,
Test = 81, ENC = 81,
ENC = 82, PIA = 82,
PIA = 83, 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, Application = 254,
InvalidResult = 255 InvalidResult = 255

View File

@ -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 // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
static u8 unknown_ns_state_field; static u8 unknown_ns_state_field;
static ScreencapPostPermission screen_capture_post_permission;
/// Parameter data to be returned in the next call to Glance/ReceiveParameter /// Parameter data to be returned in the next call to Glance/ReceiveParameter
static MessageParameter next_parameter; static MessageParameter next_parameter;
@ -70,6 +72,13 @@ void Initialize(Service::Interface* self) {
void GetSharedFont(Service::Interface* self) { void GetSharedFont(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); 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. // 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); 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, // 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; MessageParameter param;
param.buffer_size = buffer_size;
param.destination_id = dst_app_id; param.destination_id = dst_app_id;
param.sender_id = src_app_id; param.sender_id = src_app_id;
param.object = Kernel::g_handle_table.GetGeneric(handle); param.object = Kernel::g_handle_table.GetGeneric(handle);
param.signal = signal_type; 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; 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[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = next_parameter.sender_id; cmd_buff[2] = next_parameter.sender_id;
cmd_buff[3] = next_parameter.signal; // Signal type 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[5] = 0x10;
cmd_buff[6] = 0; cmd_buff[6] = 0;
if (next_parameter.object != nullptr) if (next_parameter.object != nullptr)
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); 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; cmd_buff[8] = buffer;
if (next_parameter.data) Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, 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[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = next_parameter.sender_id; cmd_buff[2] = next_parameter.sender_id;
cmd_buff[3] = next_parameter.signal; // Signal type 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[5] = 0x10;
cmd_buff[6] = 0; cmd_buff[6] = 0;
if (next_parameter.object != nullptr) if (next_parameter.object != nullptr)
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); 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; cmd_buff[8] = buffer;
if (next_parameter.data) Memory::WriteBlock(buffer, next_parameter.buffer.data(), std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size()));
memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, 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; return;
} }
size_t buffer_size = cmd_buff[2];
VAddr buffer_addr = cmd_buff[6];
AppletStartupParameter parameter; AppletStartupParameter parameter;
parameter.buffer_size = cmd_buff[2];
parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); 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; cmd_buff[1] = applet->Start(parameter).raw;
} }
void SetNSStateField(Service::Interface* self) { void SetScreenCapPostPermission(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); 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[0] = IPC::MakeHeader(0x55, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; 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(); u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0); cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[8] = unknown_ns_state_field; cmd_buff[2] = static_cast<u32>(screen_capture_post_permission);
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 GetAppletInfo(Service::Interface* self) { void GetAppletInfo(Service::Interface* self) {
@ -492,6 +502,7 @@ void Init() {
cpu_percent = 0; cpu_percent = 0;
unknown_ns_state_field = 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. // 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"); notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification");

View File

@ -20,16 +20,14 @@ struct MessageParameter {
u32 sender_id = 0; u32 sender_id = 0;
u32 destination_id = 0; u32 destination_id = 0;
u32 signal = 0; u32 signal = 0;
u32 buffer_size = 0;
Kernel::SharedPtr<Kernel::Object> object = nullptr; Kernel::SharedPtr<Kernel::Object> object = nullptr;
u8* data = nullptr; std::vector<u8> buffer;
}; };
/// Holds information about the parameters used in StartLibraryApplet /// Holds information about the parameters used in StartLibraryApplet
struct AppletStartupParameter { struct AppletStartupParameter {
u32 buffer_size = 0;
Kernel::SharedPtr<Kernel::Object> object = nullptr; 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. /// Used by the application to pass information about the current framebuffer to applets.
@ -68,6 +66,8 @@ enum class AppletId : u32 {
InstructionManual = 0x115, InstructionManual = 0x115,
Notifications = 0x116, Notifications = 0x116,
Miiverse = 0x117, Miiverse = 0x117,
MiiversePost = 0x118,
AmiiboSettings = 0x119,
SoftwareKeyboard1 = 0x201, SoftwareKeyboard1 = 0x201,
Ed1 = 0x202, Ed1 = 0x202,
PnoteApp = 0x204, PnoteApp = 0x204,
@ -80,6 +80,12 @@ enum class AppletId : u32 {
AnyLibraryApplet = 0x400, AnyLibraryApplet = 0x400,
SoftwareKeyboard2 = 0x401, SoftwareKeyboard2 = 0x401,
Ed2 = 0x402, Ed2 = 0x402,
PnoteApp2 = 0x404,
SnoteApp2 = 0x405,
Error2 = 0x406,
Mint2 = 0x407,
Extrapad2 = 0x408,
Memolib2 = 0x409,
}; };
enum class StartupArgumentType : u32 { enum class StartupArgumentType : u32 {
@ -88,6 +94,13 @@ enum class StartupArgumentType : u32 {
OtherMedia = 2, 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 /// Send a parameter to the currently-running application, which will read it via ReceiveParameter
void SendParameter(const MessageParameter& parameter); void SendParameter(const MessageParameter& parameter);
@ -377,25 +390,24 @@ void StartLibraryApplet(Service::Interface* self);
void GetStartupArgument(Service::Interface* self); void GetStartupArgument(Service::Interface* self);
/** /**
* APT::SetNSStateField service function * APT::SetScreenCapPostPermission service function
* Inputs: * Inputs:
* 1 : u8 NS state field * 0 : Header Code[0x00550040]
* 1 : u8 The screenshot posting permission
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 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: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
* 8 : u8 NS state field * 2 : u8 The screenshot posting permission
* Note:
* This returns a u8 NS state field(which can be set by cmd 0x00550040), at cmdreply+8.
*/ */
void GetNSStateField(Service::Interface* self); void GetScreenCapPostPermission(Service::Interface* self);
/** /**
* APT::CheckNew3DSApp service function * APT::CheckNew3DSApp service function

View File

@ -33,8 +33,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
{0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00510080, GetStartupArgument, "GetStartupArgument"},
{0x00550040, SetNSStateField, "SetNSStateField?"}, {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
{0x00560000, GetNSStateField, "GetNSStateField?"}, {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
{0x01020000, CheckNew3DS, "CheckNew3DS"} {0x01020000, CheckNew3DS, "CheckNew3DS"}
}; };

View File

@ -92,8 +92,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00510080, GetStartupArgument, "GetStartupArgument"},
{0x00520104, nullptr, "Wrap1"}, {0x00520104, nullptr, "Wrap1"},
{0x00530104, nullptr, "Unwrap1"}, {0x00530104, nullptr, "Unwrap1"},
{0x00550040, SetNSStateField, "SetNSStateField?" }, {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
{0x00560000, GetNSStateField, "GetNSStateField?" }, {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
{0x00580002, nullptr, "GetProgramID"}, {0x00580002, nullptr, "GetProgramID"},
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
{0x01020000, CheckNew3DS, "CheckNew3DS"} {0x01020000, CheckNew3DS, "CheckNew3DS"}

View File

@ -92,8 +92,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00510080, GetStartupArgument, "GetStartupArgument"},
{0x00520104, nullptr, "Wrap1"}, {0x00520104, nullptr, "Wrap1"},
{0x00530104, nullptr, "Unwrap1"}, {0x00530104, nullptr, "Unwrap1"},
{0x00550040, SetNSStateField, "SetNSStateField?"}, {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
{0x00560000, GetNSStateField, "GetNSStateField?"}, {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
{0x00580002, nullptr, "GetProgramID"}, {0x00580002, nullptr, "GetProgramID"},
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
{0x01020000, CheckNew3DS, "CheckNew3DS"} {0x01020000, CheckNew3DS, "CheckNew3DS"}

View File

@ -40,6 +40,20 @@ struct SaveFileConfig {
}; };
static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); 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 { struct UsernameBlock {
char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
u32 zero; u32 zero;
@ -47,6 +61,12 @@ struct UsernameBlock {
}; };
static_assert(sizeof(UsernameBlock) == 0x1C, "UsernameBlock must be exactly 0x1C bytes"); 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 { struct ConsoleModelInfo {
u8 model; ///< The console model (3DS, 2DS, etc) u8 model; ///< The console model (3DS, 2DS, etc)
u8 unknown[3]; ///< Unknown data 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 u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } }; static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
static const char CONSOLE_USERNAME[0x14] = "CITRA"; static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
/// This will be initialized in Init, and will be used when creating the block static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
static UsernameBlock CONSOLE_USERNAME_BLOCK; static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
/// TODO(Subv): Find out what this actually is
static const u8 SOUND_OUTPUT_MODE = 2;
static const u8 UNITED_STATES_COUNTRY_ID = 49; static const u8 UNITED_STATES_COUNTRY_ID = 49;
/// TODO(Subv): Find what the other bytes are /// TODO(Subv): Find what the other bytes are
static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; 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* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1]; u32 size = cmd_buff[1];
u32 block_id = cmd_buff[2]; 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 cmd_buff[1] = -1; // TODO(Subv): Find the right error code
return; 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) { void GetConfigInfoBlk8(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1]; u32 size = cmd_buff[1];
u32 block_id = cmd_buff[2]; 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 cmd_buff[1] = -1; // TODO(Subv): Find the right error code
return; 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) { void UpdateConfigNANDSavegame(Service::Interface* self) {
@ -225,13 +263,13 @@ void FormatConfig(Service::Interface* self) {
cmd_buff[1] = Service::CFG::FormatConfig().raw; 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 // Read the header
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); 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), auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
[&](const SaveConfigBlockEntry& entry) { [&](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)) { 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); 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) { if (itr->size != size) {
LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); 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); 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 // The data is located in the block header itself if the size is less than 4 bytes
if (itr->size <= 4) if (itr->size <= 4)
memcpy(output, &itr->offset_or_data, itr->size); pointer = &itr->offset_or_data;
else 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; return RESULT_SUCCESS;
} }
@ -327,35 +386,25 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer); res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
if (!res.IsSuccess()) return res; if (!res.IsSuccess()) return res;
res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, 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);
if (!res.IsSuccess()) return res; if (!res.IsSuccess()) return res;
// 0x000A0000 - Profile username res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
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);
if (!res.IsSuccess()) return res; if (!res.IsSuccess()) return res;
// 0x000A0001 - Profile birthday res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
const u8 profile_birthday[2] = {3, 25}; // March 25th, 2014
res = CreateConfigInfoBlk(0x000A0001, sizeof(profile_birthday), 0xE, profile_birthday);
if (!res.IsSuccess()) return res; 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; 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; if (!res.IsSuccess()) return res;
u16_le country_name_buffer[16][0x40] = {}; 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]); std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
} }
// 0x000B0001 - Localized names for the profile Country // 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; if (!res.IsSuccess()) return res;
// 0x000B0002 - Localized names for the profile State/Province // 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; if (!res.IsSuccess()) return res;
// 0x000B0003 - Unknown, related to country/address (zip code?) // 0x000B0003 - Unknown, related to country/address (zip code?)
@ -383,10 +432,10 @@ ResultCode FormatConfig() {
if (!res.IsSuccess()) return res; if (!res.IsSuccess()) return res;
// 0x000D0000 - Accepted EULA version // 0x000D0000 - Accepted EULA version
res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer); res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer);
if (!res.IsSuccess()) return res; 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; if (!res.IsSuccess()) return res;
// 0x00170000 - Unknown // 0x00170000 - Unknown
@ -400,11 +449,7 @@ ResultCode FormatConfig() {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
void Init() { ResultCode LoadConfigNANDSaveFile() {
AddService(new CFG_I_Interface);
AddService(new CFG_S_Interface);
AddService(new CFG_U_Interface);
// Open the SystemSaveData archive 0x00010017 // Open the SystemSaveData archive 0x00010017
FileSys::Path archive_path(cfg_system_savedata_id); FileSys::Path archive_path(cfg_system_savedata_id);
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
@ -432,25 +477,75 @@ void Init() {
if (config_result.Succeeded()) { if (config_result.Succeeded()) {
auto config = config_result.MoveFrom(); auto config = config_result.MoveFrom();
config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
return; return RESULT_SUCCESS;
} }
// Initialize the Username block return FormatConfig();
// 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;
// Copy string to buffer and pad with zeros at the end void Init() {
auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14); AddService(new CFG_I_Interface);
std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size, AddService(new CFG_S_Interface);
std::end(CONSOLE_USERNAME_BLOCK.username), 0); AddService(new CFG_U_Interface);
FormatConfig(); LoadConfigNANDSaveFile();
} }
void Shutdown() { 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 CFG
} // namespace Service } // namespace Service

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <string>
#include "common/common_types.h" #include "common/common_types.h"
@ -35,7 +36,14 @@ enum SystemLanguage {
LANGUAGE_KO = 7, LANGUAGE_KO = 7,
LANGUAGE_NL = 8, LANGUAGE_NL = 8,
LANGUAGE_PT = 9, 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 /// Block header in the config savedata file
@ -177,6 +185,22 @@ void GetConfigInfoBlk2(Service::Interface* self);
*/ */
void GetConfigInfoBlk8(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 * CFG::UpdateConfigNANDSavegame service function
* Inputs: * Inputs:
@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self);
* @param output A pointer where we will write the read data * @param output A pointer where we will write the read data
* @returns ResultCode indicating the result of the operation, 0 on success * @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. * 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(); 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 /// Initialize the config service
void Init(); void Init();
/// Shutdown the config service /// Shutdown the config service
void Shutdown(); 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 CFG
} // namespace Service } // namespace Service

View File

@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
// cfg:i // cfg:i
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
{0x04050000, nullptr, "GetLocalFriendCodeSeed"}, {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x04080042, nullptr, "SecureInfoGetSerialNo"}, {0x04080042, nullptr, "SecureInfoGetSerialNo"},
{0x04090000, nullptr, "UpdateConfigBlk00040003"}, {0x04090000, nullptr, "UpdateConfigBlk00040003"},
{0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x08020082, nullptr, "SetConfigInfoBlk4"}, {0x08020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
{0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x080400C2, nullptr, "CreateConfigInfoBlk"}, {0x080400C2, nullptr, "CreateConfigInfoBlk"},
{0x08050000, nullptr, "DeleteConfigNANDSavefile"}, {0x08050000, nullptr, "DeleteConfigNANDSavefile"},

View File

@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
// cfg:s // cfg:s
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
{0x04050000, nullptr, "GetLocalFriendCodeSeed"}, {0x04050000, nullptr, "GetLocalFriendCodeSeed"},

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -2,16 +2,15 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/hle.h" #include "core/hle/result.h"
#include "core/hle/service/dlp_srvr.h" #include "core/hle/service/dlp/dlp_srvr.h"
//////////////////////////////////////////////////////////////////////////////////////////////////// namespace Service {
// Namespace DLP_SRVR namespace DLP {
namespace DLP_SRVR { static void unk_0x000E0040(Interface* self) {
static void unk_0x000E0040(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
@ -23,14 +22,13 @@ static void unk_0x000E0040(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = { const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"}, {0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"}, {0x00020000, nullptr, "Finalize"},
{0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
{0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, {0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// DLP_SRVR_Interface::DLP_SRVR_Interface() {
// Interface class
Interface::Interface() {
Register(FunctionTable); Register(FunctionTable);
} }
} // namespace } // namespace DLP
} // namespace Service

View File

@ -6,18 +6,17 @@
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
//////////////////////////////////////////////////////////////////////////////////////////////////// namespace Service {
// Namespace DLP_SRVR namespace DLP {
namespace DLP_SRVR { class DLP_SRVR_Interface final : public Interface {
class Interface : public Service::Interface {
public: public:
Interface(); DLP_SRVR_Interface();
std::string GetPortName() const override { std::string GetPortName() const override {
return "dlp:SRVR"; return "dlp:SRVR";
} }
}; };
} // namespace } // namespace DLP
} // namespace Service

View File

@ -140,12 +140,15 @@ static void LoadComponent(Service::Interface* self) {
// TODO(bunnei): Implement real DSP firmware loading // TODO(bunnei): Implement real DSP firmware loading
ASSERT(Memory::GetPointer(buffer) != nullptr); ASSERT(Memory::IsValidVirtualAddress(buffer));
ASSERT(size > 0x37C);
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. // 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", 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); size, prog_mask, data_mask, buffer);
@ -285,7 +288,7 @@ static void WriteProcessPipe(Service::Interface* self) {
return; 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); std::vector<u8> message(size);
for (u32 i = 0; i < size; i++) { 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); 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[0] = IPC::MakeHeader(0x10, 1, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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); 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) { if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);

View File

@ -58,6 +58,10 @@ namespace FS {
const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS, const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent); 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 // Command to access archive file
enum class FileCommand : u32 { enum class FileCommand : u32 {
Dummy1 = 0x000100C6, Dummy1 = 0x000100C6,
@ -108,13 +112,14 @@ ResultVal<bool> File::SyncRequest() {
offset, length, backend->GetSize()); 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()) { if (read.Failed()) {
cmd_buff[1] = read.Code().raw; cmd_buff[1] = read.Code().raw;
return read.Code(); return read.Code();
} }
Memory::WriteBlock(address, data.data(), *read);
cmd_buff[2] = static_cast<u32>(*read); cmd_buff[2] = static_cast<u32>(*read);
Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length);
break; 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", 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); 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()) { if (written.Failed()) {
cmd_buff[1] = written.Code().raw; cmd_buff[1] = written.Code().raw;
return written.Code(); return written.Code();
@ -216,12 +223,14 @@ ResultVal<bool> Directory::SyncRequest() {
{ {
u32 count = cmd_buff[1]; u32 count = cmd_buff[1];
u32 address = cmd_buff[3]; 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", LOG_TRACE(Service_FS, "Read %s %s: count=%d",
GetTypeName().c_str(), GetName().c_str(), count); GetTypeName().c_str(), GetName().c_str(), count);
// Number of entries actually read // 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; break;
} }
@ -250,7 +259,7 @@ using FileSys::ArchiveFactory;
/** /**
* Map of registered archives, identified by id code. Once an archive is registered here, it is * 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; 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) { ResultCode CloseArchive(ArchiveHandle handle) {
if (handle_map.erase(handle) == 0) if (handle_map.erase(handle) == 0)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
else else
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -309,7 +318,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
const FileSys::Path& path, const FileSys::Mode mode) { const FileSys::Path& path, const FileSys::Mode mode) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
auto backend = archive->OpenFile(path, mode); auto backend = archive->OpenFile(path, mode);
if (backend.Failed()) if (backend.Failed())
@ -322,7 +331,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
return archive->DeleteFile(path); return archive->DeleteFile(path);
} }
@ -332,7 +341,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* src_archive = GetArchive(src_archive_handle);
ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
if (src_archive == nullptr || dest_archive == nullptr) if (src_archive == nullptr || dest_archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) { if (src_archive == dest_archive) {
if (src_archive->RenameFile(src_path, dest_path)) 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) { ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
if (archive->DeleteDirectory(path)) if (archive->DeleteDirectory(path))
return RESULT_SUCCESS; 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) { ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
return archive->CreateFile(path, file_size); 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) { ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
if (archive->CreateDirectory(path)) if (archive->CreateDirectory(path))
return RESULT_SUCCESS; return RESULT_SUCCESS;
@ -383,7 +392,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* src_archive = GetArchive(src_archive_handle);
ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
if (src_archive == nullptr || dest_archive == nullptr) if (src_archive == nullptr || dest_archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) { if (src_archive == dest_archive) {
if (src_archive->RenameDirectory(src_path, dest_path)) if (src_archive->RenameDirectory(src_path, dest_path))
@ -403,7 +412,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
const FileSys::Path& path) { const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
if (backend == nullptr) { if (backend == nullptr) {
@ -418,7 +427,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
ArchiveBackend* archive = GetArchive(archive_handle); ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr) if (archive == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_ARCHIVE_HANDLE;
return MakeResult<u64>(archive->GetFreeBytes()); return MakeResult<u64>(archive->GetFreeBytes());
} }
@ -456,11 +465,12 @@ ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon
if (result.IsError()) if (result.IsError())
return result; return result;
u8* smdh_icon = Memory::GetPointer(icon_buffer); if (!Memory::IsValidVirtualAddress(icon_buffer))
if (!smdh_icon)
return ResultCode(-1); // TODO(Subv): Find the right error code 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; return RESULT_SUCCESS;
} }
@ -510,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
/// Initialize archives void RegisterArchiveTypes() {
void ArchiveInit() {
next_handle = 1;
AddService(new FS::Interface);
// TODO(Subv): Add the other archive types (see here for the known types: // TODO(Subv): Add the other archive types (see here for the known types:
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
@ -552,10 +557,23 @@ void ArchiveInit() {
RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); 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 /// Shutdown archives
void ArchiveShutdown() { void ArchiveShutdown() {
handle_map.clear(); handle_map.clear();
id_code_map.clear(); UnregisterArchiveTypes();
} }
} // namespace FS } // namespace FS

View File

@ -235,5 +235,11 @@ void ArchiveInit();
/// Shutdown archives /// Shutdown archives
void ArchiveShutdown(); void ArchiveShutdown();
/// Register all archive types
void RegisterArchiveTypes();
/// Unregister all archive types
void UnregisterArchiveTypes();
} // namespace FS } // namespace FS
} // namespace Service } // namespace Service

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