diff options
142 files changed, 2692 insertions, 1611 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9727c5712..ae11db988 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,7 @@ <!--- Please read the FAQ: -https://citra-emu.org/wiki/FAQ +https://citra-emu.org/wiki/faq/ THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO: https://community.citra-emu.org/ @@ -22,4 +22,4 @@ When submitting an issue, please check the following: - For graphical issues, comparison screenshots with real hardware. - For emulation inaccuracies, a test-case (if able). ---->
\ No newline at end of file +---> diff --git a/.gitmodules b/.gitmodules index f98725622..36caa59f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "cryptopp"] path = externals/cryptopp/cryptopp url = https://github.com/weidai11/cryptopp.git +[submodule "fmt"] + path = externals/fmt + url = https://github.com/fmtlib/fmt.git diff --git a/.travis-deps.sh b/.travis-deps.sh index 1404fe19f..451886984 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -11,7 +11,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then if [ ! -e $HOME/.local/bin/cmake ]; then echo "CMake not found in the cache, get and extract it..." - curl -L http://www.cmake.org/files/v3.2/cmake-3.2.0-Linux-i386.tar.gz \ + curl -L http://www.cmake.org/files/v3.6/cmake-3.6.3-Linux-x86_64.tar.gz \ | tar -xz -C $HOME/.local --strip-components=1 else echo "Using cached CMake" diff --git a/CMakeLists.txt b/CMakeLists.txt index 306959e24..1f0af2d41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,25 @@ -# CMake 3.2 required for cmake to know the right flags for CXX standard on OSX -cmake_minimum_required(VERSION 3.2) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) +# CMake 3.6 required for FindBoost to define IMPORTED libs properly on unknown Boost versions +cmake_minimum_required(VERSION 3.6) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") -function(download_bundled_external remote_path lib_name prefix_var) - set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") - if (NOT EXISTS "${prefix}") - message(STATUS "Downloading binaries for ${lib_name}...") - file(DOWNLOAD - https://github.com/citra-emu/ext-windows-bin/raw/master/${remote_path}${lib_name}.7z - "${CMAKE_BINARY_DIR}/externals/${lib_name}.7z" SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${CMAKE_BINARY_DIR}/externals/${lib_name}.7z" - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals") - endif() - message(STATUS "Using bundled binaries at ${prefix}") - set(${prefix_var} "${prefix}" PARENT_SCOPE) -endfunction() +project(citra) + +option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) +option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF) + +option(ENABLE_QT "Enable the Qt frontend" ON) +option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) + +if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) + message(STATUS "Copying pre-commit hook") + file(COPY hooks/pre-commit + DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) +endif() + + +# Detect current compilation architecture and create standard definitions +# ======================================================================= include(CheckSymbolExists) function(detect_architecture symbol arch) @@ -33,20 +38,6 @@ function(detect_architecture symbol arch) endif() endfunction() -project(citra) - -option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) -option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF) - -option(ENABLE_QT "Enable the Qt frontend" ON) -option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) - -if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit) - message(STATUS "Copying pre-commit hook") - file(COPY hooks/pre-commit - DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks) -endif() - if (MSVC) detect_architecture("_M_AMD64" x86_64) detect_architecture("_M_IX86" x86) @@ -63,6 +54,10 @@ if (NOT DEFINED ARCHITECTURE) endif() message(STATUS "Target architecture: ${ARCHITECTURE}") + +# Configure compilation flags +# =========================== + set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -130,28 +125,44 @@ add_definitions(-DSINGLETHREADED) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) + +# System imported libraries +# ====================== + +# This function downloads a binary library package from our external repo. +# Params: +# remote_path: path to the file to download, relative to the remote repository root +# prefix_var: name of a variable which will be set with the path to the extracted contents +function(download_bundled_external remote_path lib_name prefix_var) + set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") + if (NOT EXISTS "${prefix}") + message(STATUS "Downloading binaries for ${lib_name}...") + file(DOWNLOAD + https://github.com/citra-emu/ext-windows-bin/raw/master/${remote_path}${lib_name}.7z + "${CMAKE_BINARY_DIR}/externals/${lib_name}.7z" SHOW_PROGRESS) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${CMAKE_BINARY_DIR}/externals/${lib_name}.7z" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals") + endif() + message(STATUS "Using bundled binaries at ${prefix}") + set(${prefix_var} "${prefix}" PARENT_SCOPE) +endfunction() + find_package(PNG QUIET) -if (PNG_FOUND) - add_definitions(-DHAVE_PNG) -else() +if (NOT PNG_FOUND) message(STATUS "libpng not found. Some debugging features have been disabled.") endif() -find_package(Boost 1.57.0 QUIET) +find_package(Boost 1.63.0 QUIET) if (NOT Boost_FOUND) - message(STATUS "Boost 1.57.0 or newer not found, falling back to externals") - set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost") -endif() -include_directories(${Boost_INCLUDE_DIR}) - -# Include bundled CMake modules -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") + message(STATUS "Boost 1.63.0 or newer not found, falling back to externals") -find_package(OpenGL REQUIRED) -include_directories(${OPENGL_INCLUDE_DIR}) + set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost") + set(Boost_NO_SYSTEM_PATHS OFF) + find_package(Boost QUIET REQUIRED) +endif() # Prefer the -pthread flag on Linux. -set (THREADS_PREFER_PTHREAD_FLAG ON) +set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) if (ENABLE_SDL2) @@ -174,10 +185,43 @@ if (ENABLE_SDL2) else() find_package(SDL2 REQUIRED) endif() + + if (SDL2_FOUND) + # TODO(yuriks): Make FindSDL2.cmake export an IMPORTED library instead + add_library(SDL2 INTERFACE) + target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}") + target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") + endif() else() set(SDL2_FOUND NO) endif() +if (ENABLE_QT) + if (CITRA_USE_BUNDLED_QT) + if (MSVC14 AND ARCHITECTURE_x86_64) + set(QT_VER qt-5.7-msvc2015_64) + else() + message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.") + endif() + + if (DEFINED QT_VER) + download_bundled_external("qt/" ${QT_VER} QT_PREFIX) + endif() + + set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") + else() + # Passing an empty HINTS seems to cause default system paths to get ignored in CMake 2.8 so + # make sure to not pass anything if we don't have one. + set(QT_PREFIX_HINT) + endif() + + find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) +endif() + + +# Platform-specific library requirements +# ====================================== + IF (APPLE) FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY}) @@ -206,28 +250,9 @@ if (UNIX OR MINGW) endif() endif() -if (ENABLE_QT) - if (CITRA_USE_BUNDLED_QT) - if (MSVC14 AND ARCHITECTURE_x86_64) - set(QT_VER qt-5.7-msvc2015_64) - else() - message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.") - endif() - - if (DEFINED QT_VER) - download_bundled_external("qt/" ${QT_VER} QT_PREFIX) - endif() - set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") - else() - # Passing an empty HINTS seems to cause default system paths to get ignored in CMake 2.8 so - # make sure to not pass anything if we don't have one. - set(QT_PREFIX_HINT) - endif() - - find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) - set(CITRA_QT_LIBS Qt5::Widgets Qt5::OpenGL) -endif() +# Include source code +# =================== # This function should be passed a list of all files in a target. It will automatically generate # file groups following the directory hierarchy, so that the layout of the files in IDEs matches the @@ -251,29 +276,13 @@ get_git_head_revision(GIT_REF_SPEC GIT_REV) git_describe(GIT_DESC --always --long --dirty) git_branch_name(GIT_BRANCH) -set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") -include_directories(${INI_PREFIX}) -add_subdirectory(${INI_PREFIX}) - add_subdirectory(externals) - -option(DYNARMIC_TESTS OFF) -add_subdirectory(externals/dynarmic) - -add_subdirectory(externals/glad) -include_directories(externals/microprofile) -include_directories(externals/nihstro/include) - -if (MSVC) - add_subdirectory(externals/getopt) -endif() - -# process subdirectories -add_subdirectory(externals/soundtouch) - +add_subdirectory(src) enable_testing() -add_subdirectory(src) + +# Installation instructions +# ========================= # Install freedesktop.org metadata files, following those specifications: # http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html @@ -7,7 +7,7 @@ Citra Emulator Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward. -Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/FAQ/) before getting started with the project. +Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project. Check out our [website](https://citra-emu.org/)! diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 309e98464..1e04931ee 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -1,11 +1,52 @@ +# Definitions for all external bundled libraries + +# Catch +add_library(catch-single-include INTERFACE) +target_include_directories(catch-single-include INTERFACE catch/single_include) + +# Crypto++ +add_subdirectory(cryptopp) + +# Dynarmic +# Dynarmic will skip defining xbyak if it's already defined, we then define it below +add_library(xbyak INTERFACE) +option(DYNARMIC_TESTS OFF) +set(DYNARMIC_NO_BUNDLED_FMT ON) +add_subdirectory(dynarmic) + +# libfmt +add_subdirectory(fmt) + +# getopt +if (MSVC) + add_subdirectory(getopt) +endif() + +# Glad +add_subdirectory(glad) + +# inih +add_subdirectory(inih) + +# MicroProfile +add_library(microprofile INTERFACE) +target_include_directories(microprofile INTERFACE ./microprofile) + +# Nihstro +add_library(nihstro-headers INTERFACE) +target_include_directories(nihstro-headers INTERFACE ./nihstro/include) + +# SoundTouch +add_subdirectory(soundtouch) +# The SoundTouch target doesn't export the necessary include paths as properties by default +target_include_directories(SoundTouch INTERFACE ./soundtouch/include) + # Xbyak if (ARCHITECTURE_x86_64) - add_library(xbyak INTERFACE) - target_include_directories(xbyak INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak) + # Defined before "dynarmic" above + # add_library(xbyak INTERFACE) + target_include_directories(xbyak INTERFACE ./xbyak/xbyak) if (NOT MSVC) target_compile_options(xbyak INTERFACE -fno-operator-names) endif() endif() - -add_subdirectory(cryptopp) - diff --git a/externals/cryptopp/CMakeLists.txt b/externals/cryptopp/CMakeLists.txt index 653af1e4b..864de18bb 100644 --- a/externals/cryptopp/CMakeLists.txt +++ b/externals/cryptopp/CMakeLists.txt @@ -10,6 +10,7 @@ # - disabled installation # - disabled documentation # - configured to build a static library only +# - adds include directories to the library target include(TestBigEndian) include(CheckCXXCompilerFlag) @@ -148,14 +149,15 @@ endif() # Compile targets #============================================================================ add_library(cryptopp STATIC ${cryptopp_SOURCES}) +target_include_directories(cryptopp INTERFACE .) #============================================================================ # Third-party libraries #============================================================================ if(WIN32) - target_link_libraries(cryptopp ws2_32) + target_link_libraries(cryptopp PRIVATE ws2_32) endif() find_package(Threads) -target_link_libraries(cryptopp ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(cryptopp PRIVATE ${CMAKE_THREAD_LIBS_INIT}) diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 358cf7c32205a5114964865c86a8455daf81073 +Subproject 7707ff13e981b0aecf87f3156ee0b641469f7bb diff --git a/externals/fmt b/externals/fmt new file mode 160000 +Subproject ac5484c4e7365b59d8c7e14db6778de26635e42 diff --git a/externals/glad/CMakeLists.txt b/externals/glad/CMakeLists.txt index a97d4aa73..6d35a844b 100644 --- a/externals/glad/CMakeLists.txt +++ b/externals/glad/CMakeLists.txt @@ -9,6 +9,7 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(glad STATIC ${SRCS} ${HEADERS}) target_include_directories(glad PUBLIC "include/") + if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") - target_link_libraries(glad dl) + target_link_libraries(glad PRIVATE dl) endif() diff --git a/externals/inih/CMakeLists.txt b/externals/inih/CMakeLists.txt index c87f78bfc..cff36a581 100644 --- a/externals/inih/CMakeLists.txt +++ b/externals/inih/CMakeLists.txt @@ -9,3 +9,4 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(inih ${SRCS} ${HEADERS}) +target_include_directories(inih INTERFACE .) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index a72a907ef..0ad86bb7a 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -27,20 +27,18 @@ set(HEADERS time_stretch.h ) -include_directories(../../externals/soundtouch/include) - if(SDL2_FOUND) set(SRCS ${SRCS} sdl2_sink.cpp) set(HEADERS ${HEADERS} sdl2_sink.h) - include_directories(${SDL2_INCLUDE_DIR}) endif() create_directory_groups(${SRCS} ${HEADERS}) add_library(audio_core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(audio_core SoundTouch) +target_link_libraries(audio_core PUBLIC common core) +target_link_libraries(audio_core PRIVATE SoundTouch) if(SDL2_FOUND) - target_link_libraries(audio_core ${SDL2_LIBRARY}) - set_property(TARGET audio_core APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) + target_link_libraries(audio_core PRIVATE SDL2) + target_compile_definitions(audio_core PRIVATE HAVE_SDL2) endif() diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 47231ba71..d72d2b5f4 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -15,15 +15,13 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) -include_directories(${SDL2_INCLUDE_DIR}) - add_executable(citra ${SRCS} ${HEADERS}) -target_link_libraries(citra core video_core audio_core common input_common) -target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) +target_link_libraries(citra PRIVATE common core input_common) +target_link_libraries(citra PRIVATE inih glad) if (MSVC) - target_link_libraries(citra getopt) + target_link_libraries(citra PRIVATE getopt) endif() -target_link_libraries(citra ${PLATFORM_LIBRARIES} Threads::Threads) +target_link_libraries(citra PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) if(UNIX AND NOT APPLE) install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 76f5caeb1..dd357ff72 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -33,7 +33,6 @@ #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" #include "core/settings.h" -#include "video_core/video_core.h" static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 @@ -145,7 +144,7 @@ int main(int argc, char** argv) { LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " "being used with Citra. \n\n For more information on dumping and " "decrypting games, please refer to: " - "https://citra-emu.org/wiki/Dumping-Game-Cartridges"); + "https://citra-emu.org/wiki/dumping-game-cartridges/"); return -1; case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); diff --git a/src/citra/config.cpp b/src/citra/config.cpp index a4162e9ad..f08b4069c 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -5,11 +5,11 @@ #include <memory> #include <SDL.h> #include <inih/cpp/INIReader.h> +#include "citra/config.h" #include "citra/default_ini.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/param_package.h" -#include "config.h" #include "core/settings.h" #include "input_common/main.h" @@ -21,6 +21,8 @@ Config::Config() { Reload(); } +Config::~Config() = default; + bool Config::LoadINI(const std::string& default_contents, bool retry) { const char* location = this->sdl2_config_loc.c_str(); if (sdl2_config->ParseError() < 0) { diff --git a/src/citra/config.h b/src/citra/config.h index b1c31f59c..abc90f642 100644 --- a/src/citra/config.h +++ b/src/citra/config.h @@ -6,7 +6,8 @@ #include <memory> #include <string> -#include <inih/cpp/INIReader.h> + +class INIReader; class Config { std::unique_ptr<INIReader> sdl2_config; @@ -17,6 +18,7 @@ class Config { public: Config(); + ~Config(); void Reload(); }; diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 6bc0b0d00..47aadd60c 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -12,10 +12,10 @@ #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/string_util.h" +#include "core/3ds.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" -#include "video_core/video_core.h" void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); @@ -80,12 +80,12 @@ EmuWindow_SDL2::EmuWindow_SDL2() { std::string window_title = Common::StringFromFormat("Citra %s| %s-%s ", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); - render_window = SDL_CreateWindow( - window_title.c_str(), - SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + render_window = + SDL_CreateWindow(window_title.c_str(), + SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); if (render_window == nullptr) { LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 4e837668e..4841cbf05 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -91,9 +91,9 @@ if (APPLE) else() add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) endif() -target_link_libraries(citra-qt core video_core audio_core common input_common) -target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) -target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) +target_link_libraries(citra-qt PRIVATE audio_core common core input_common video_core) +target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) +target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if(UNIX AND NOT APPLE) install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index bae576d6a..a8a4aed8b 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -12,12 +12,11 @@ #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/string_util.h" +#include "core/3ds.h" #include "core/core.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" -#include "video_core/debug_utils/debug_utils.h" -#include "video_core/video_core.h" EmuThread::EmuThread(GRenderWindow* render_window) : exec_step(false), running(false), stop_run(false), render_window(render_window) {} @@ -38,7 +37,10 @@ void EmuThread::run() { if (!was_active) emit DebugModeLeft(); - Core::System::GetInstance().RunLoop(); + Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); + if (result != Core::System::ResultStatus::Success) { + emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); + } was_active = running || exec_step; if (!was_active && !stop_run) @@ -266,8 +268,7 @@ void GRenderWindow::InitRenderTarget() { child = new GGLWidgetInternal(fmt, this); QBoxLayout* layout = new QHBoxLayout(this); - resize(VideoCore::kScreenTopWidth, - VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); + resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); layout->addWidget(child); layout->setMargin(0); setLayout(layout); diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 9d39f1af8..4b3a3b3cc 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -10,6 +10,7 @@ #include <QGLWidget> #include <QThread> #include "common/thread.h" +#include "core/core.h" #include "core/frontend/emu_window.h" #include "core/frontend/motion_emu.h" @@ -97,6 +98,8 @@ signals: * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) */ void DebugModeLeft(); + + void ErrorThrown(Core::System::ResultStatus, std::string); }; class GRenderWindow : public QWidget, public EmuWindow { diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp index c68fe753b..7d06ec28a 100644 --- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp @@ -17,6 +17,7 @@ #include "citra_qt/util/spinbox.h" #include "citra_qt/util/util.h" #include "common/vector_math.h" +#include "core/memory.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/pica_state.h" #include "video_core/regs.h" diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index b6ecf3819..8c244b6b2 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp @@ -10,6 +10,7 @@ #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" +#include "core/hle/kernel/wait_object.h" WaitTreeItem::~WaitTreeItem() {} diff --git a/src/citra_qt/debugger/wait_tree.h b/src/citra_qt/debugger/wait_tree.h index ee9708fc1..2b38712b9 100644 --- a/src/citra_qt/debugger/wait_tree.h +++ b/src/citra_qt/debugger/wait_tree.h @@ -4,12 +4,10 @@ #pragma once -#include <boost/container/flat_set.hpp> - #include <QAbstractItemModel> #include <QDockWidget> #include <QTreeView> - +#include <boost/container/flat_set.hpp> #include "core/core.h" #include "core/hle/kernel/kernel.h" @@ -20,7 +18,6 @@ class WaitObject; class Event; class Mutex; class Semaphore; -class Session; class Thread; class Timer; } diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index d1118ff7f..12212a3a4 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -10,10 +10,8 @@ #include <QStandardItem> #include <QString> #include "citra_qt/util/util.h" -#include "common/color.h" #include "common/string_util.h" #include "core/loader/smdh.h" -#include "video_core/utils.h" /** * Gets the game icon from SMDH data. diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index d7fad555f..4f5b2ddab 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -43,7 +43,6 @@ #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" #include "core/settings.h" -#include "video_core/video_core.h" #ifdef QT_STATICPLUGIN Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); @@ -94,6 +93,14 @@ void GMainWindow::InitializeWidgets() { ui.horizontalLayout->addWidget(game_list); // Create status bar + message_label = new QLabel(); + // Configured separately for left alignment + message_label->setVisible(false); + message_label->setFrameStyle(QFrame::NoFrame); + message_label->setContentsMargins(4, 0, 4, 0); + message_label->setAlignment(Qt::AlignLeft); + statusBar()->addPermanentWidget(message_label, 1); + emu_speed_label = new QLabel(); emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% " "indicate emulation is running faster or slower than a 3DS.")); @@ -109,7 +116,7 @@ void GMainWindow::InitializeWidgets() { label->setVisible(false); label->setFrameStyle(QFrame::NoFrame); label->setContentsMargins(4, 0, 4, 0); - statusBar()->addPermanentWidget(label); + statusBar()->addPermanentWidget(label, 0); } statusBar()->setVisible(true); setStyleSheet("QStatusBar::item{border: none;}"); @@ -301,9 +308,8 @@ bool GMainWindow::LoadROM(const QString& filename) { render_window->MakeCurrent(); if (!gladLoadGL()) { - QMessageBox::critical(this, tr("Error while starting Citra!"), - tr("Failed to initialize the video core!\n\n" - "Please ensure that your GPU supports OpenGL 3.3 and that you " + QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"), + tr("Your GPU may not support OpenGL 3.3, or you do not" "have the latest graphics driver.")); return false; } @@ -328,18 +334,17 @@ bool GMainWindow::LoadROM(const QString& filename) { break; case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: { - // Build the MessageBox ourselves to have clickable link - QMessageBox popup_error; - popup_error.setTextFormat(Qt::RichText); - popup_error.setWindowTitle(tr("Error while loading ROM!")); - popup_error.setText( + QMessageBox::critical( + this, tr("Error while loading ROM!"), tr("The game that you are trying to load must be decrypted before being used with " - "Citra.<br/><br/>" - "For more information on dumping and decrypting games, please see: <a " - "href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://" - "citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); - popup_error.setIcon(QMessageBox::Critical); - popup_error.exec(); + "Citra. A real 3DS is required.<br/><br/>" + "For more information on dumping and decrypting games, please see the following " + "wiki pages: <ul>" + "<li><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Dumping Game " + "Cartridges</a></li>" + "<li><a href='https://citra-emu.org/wiki/dumping-installed-titles/'>Dumping " + "Installed Titles</a></li>" + "</ul>")); break; } case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: @@ -347,8 +352,23 @@ bool GMainWindow::LoadROM(const QString& filename) { tr("The ROM format is not supported.")); break; + case Core::System::ResultStatus::ErrorVideoCore: + QMessageBox::critical( + this, tr("An error occured in the video core."), + tr("Citra has encountered an error while running the video core, please see the " + "log for more details." + "For more information on accessing the log, please see the following page: " + "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " + "to " + "Upload the Log File</a>." + "Ensure that you have the latest graphics drivers for your GPU.")); + + break; + default: - QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!")); + QMessageBox::critical( + this, tr("Error while loading ROM!"), + tr("An unknown error occured. Please see the log for more details.")); break; } return false; @@ -425,6 +445,7 @@ void GMainWindow::ShutdownGame() { // Disable status bar updates status_bar_update_timer.stop(); + message_label->setVisible(false); emu_speed_label->setVisible(false); game_fps_label->setVisible(false); emu_frametime_label->setVisible(false); @@ -531,6 +552,10 @@ void GMainWindow::OnMenuRecentFile() { void GMainWindow::OnStartGame() { emu_thread->SetRunning(true); + qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); + qRegisterMetaType<std::string>("std::string"); + connect(emu_thread.get(), SIGNAL(ErrorThrown(Core::System::ResultStatus, std::string)), this, + SLOT(OnCoreError(Core::System::ResultStatus, std::string))); ui.action_Start->setEnabled(false); ui.action_Start->setText(tr("Continue")); @@ -623,11 +648,74 @@ void GMainWindow::UpdateStatusBar() { emu_frametime_label->setVisible(true); } +void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { + QMessageBox::StandardButton answer; + QString status_message; + const QString common_message = + tr("The game you are trying to load requires additional files from your 3DS to be dumped " + "before playing.<br/><br/>For more information on dumping these files, please see the " + "following wiki page: <a " + "href='https://citra-emu.org/wiki/" + "dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Dumping System " + "Archives and the Shared Fonts from a 3DS Console</a>.<br/><br/>Would you like to quit " + "back to the game list? Continuing emulation may result in crashes, corrupted save " + "data, or other bugs."); + switch (result) { + case Core::System::ResultStatus::ErrorSystemFiles: { + QString message = "Citra was unable to locate a 3DS system archive"; + if (!details.empty()) { + message.append(tr(": %1. ").arg(details.c_str())); + } else { + message.append(". "); + } + message.append(common_message); + + answer = QMessageBox::question(this, tr("System Archive Not Found"), message, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + status_message = "System Archive Missing"; + break; + } + + case Core::System::ResultStatus::ErrorSharedFont: { + QString message = tr("Citra was unable to locate the 3DS shared fonts. "); + message.append(common_message); + answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + status_message = "Shared Font Missing"; + break; + } + + default: + answer = QMessageBox::question( + this, tr("Fatal Error"), + tr("Citra has encountered a fatal error, please see the log for more details. " + "For more information on accessing the log, please see the following page: " + "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to " + "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? " + "Continuing emulation may result in crashes, corrupted save data, or other bugs."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + status_message = "Fatal Error encountered"; + break; + } + + if (answer == QMessageBox::Yes) { + if (emu_thread) { + ShutdownGame(); + } + } else { + // Only show the message if the game is still running. + if (emu_thread) { + message_label->setText(status_message); + message_label->setVisible(true); + } + } +} + bool GMainWindow::ConfirmClose() { if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) return true; - auto answer = + QMessageBox::StandardButton answer = QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); return answer != QMessageBox::No; diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index cb2e87cbd..952a50974 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -8,6 +8,7 @@ #include <memory> #include <QMainWindow> #include <QTimer> +#include "core/core.h" #include "ui_main.h" class Config; @@ -125,6 +126,7 @@ private slots: void OnDisplayTitleBars(bool); void ToggleWindowMode(); void OnCreateGraphicsSurfaceViewer(); + void OnCoreError(Core::System::ResultStatus, std::string); private: void UpdateStatusBar(); @@ -135,6 +137,7 @@ private: GameList* game_list; // Status bar elements + QLabel* message_label = nullptr; QLabel* emu_speed_label = nullptr; QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 6905d2d50..7e83e64b0 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -27,7 +27,6 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU set(SRCS break_points.cpp file_util.cpp - framebuffer_layout.cpp hash.cpp logging/filter.cpp logging/text_formatter.cpp @@ -56,7 +55,6 @@ set(HEADERS common_paths.h common_types.h file_util.h - framebuffer_layout.h hash.h linear_disk_cache.h logging/text_formatter.h @@ -97,6 +95,7 @@ endif() create_directory_groups(${SRCS} ${HEADERS}) add_library(common STATIC ${SRCS} ${HEADERS}) +target_link_libraries(common PUBLIC Boost::boost microprofile) if (ARCHITECTURE_x86_64) - target_link_libraries(common xbyak) + target_link_libraries(common PRIVATE xbyak) endif() diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 030f7caeb..0cc0a1be0 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -108,7 +108,7 @@ * symptoms. */ #pragma pack(1) -template <std::size_t position, std::size_t bits, typename T> +template <std::size_t Position, std::size_t Bits, typename T> struct BitField { private: // We hide the copy assigment operator here, because the default copy @@ -117,7 +117,45 @@ private: // We don't delete it because we want BitField to be trivially copyable. BitField& operator=(const BitField&) = default; + // StorageType is T for non-enum types and the underlying type of T if + // T is an enumeration. Note that T is wrapped within an enable_if in the + // former case to workaround compile errors which arise when using + // std::underlying_type<T>::type directly. + using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, + std::enable_if<true, T>>::type; + + // Unsigned version of StorageType + using StorageTypeU = std::make_unsigned_t<StorageType>; + public: + /// Constants to allow limited introspection of fields if needed + static constexpr size_t position = Position; + static constexpr size_t bits = Bits; + static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; + + /** + * Formats a value by masking and shifting it according to the field parameters. A value + * containing several bitfields can be assembled by formatting each of their values and ORing + * the results together. + */ + static constexpr FORCE_INLINE StorageType FormatValue(const T& value) { + return ((StorageType)value << position) & mask; + } + + /** + * Extracts a value from the passed storage. In most situations prefer use the member functions + * (such as Value() or operator T), but this can be used to extract a value from a bitfield + * union in a constexpr context. + */ + static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { + if (std::numeric_limits<T>::is_signed) { + std::size_t shift = 8 * sizeof(T) - bits; + return (T)((storage << (shift - position)) >> shift); + } else { + return (T)((storage & mask) >> position); + } + } + // This constructor and assignment operator might be considered ambiguous: // Would they initialize the storage or just the bitfield? // Hence, delete them. Use the Assign method to set bitfield values! @@ -126,23 +164,18 @@ public: // Force default constructor to be created // so that we can use this within unions - BitField() = default; + constexpr BitField() = default; FORCE_INLINE operator T() const { return Value(); } FORCE_INLINE void Assign(const T& value) { - storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); + storage = (storage & ~mask) | FormatValue(value); } FORCE_INLINE T Value() const { - if (std::numeric_limits<T>::is_signed) { - std::size_t shift = 8 * sizeof(T) - bits; - return (T)((storage << (shift - position)) >> shift); - } else { - return (T)((storage & GetMask()) >> position); - } + return ExtractValue(storage); } // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015 @@ -151,20 +184,6 @@ public: } private: - // StorageType is T for non-enum types and the underlying type of T if - // T is an enumeration. Note that T is wrapped within an enable_if in the - // former case to workaround compile errors which arise when using - // std::underlying_type<T>::type directly. - typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>, - std::enable_if<true, T>>::type::type StorageType; - - // Unsigned version of StorageType - typedef typename std::make_unsigned<StorageType>::type StorageTypeU; - - FORCE_INLINE StorageType GetMask() const { - return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; - } - StorageType storage; static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp index 03a19acba..fa367a4ca 100644 --- a/src/common/break_points.cpp +++ b/src/common/break_points.cpp @@ -5,7 +5,6 @@ #include <algorithm> #include <sstream> #include "common/break_points.h" -#include "common/logging/log.h" bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const { auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index b141e79ed..2e7877500 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -7,7 +7,7 @@ #if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) #include <cstdlib> // for exit #endif -#include "common_types.h" +#include "common/common_types.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) diff --git a/src/common/hash.cpp b/src/common/hash.cpp index f3d390dc5..a02e9e5b9 100644 --- a/src/common/hash.cpp +++ b/src/common/hash.cpp @@ -5,9 +5,9 @@ #if defined(_MSC_VER) #include <stdlib.h> #endif -#include "common_funcs.h" -#include "common_types.h" -#include "hash.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/hash.h" namespace Common { diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 2cb3ab9cc..62f17fbb5 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -6,7 +6,7 @@ #include <string> #include <thread> #include "common/common_types.h" -#include "cpu_detect.h" +#include "common/x64/cpu_detect.h" #ifdef _MSC_VER #include <intrin.h> diff --git a/src/core/3ds.h b/src/core/3ds.h new file mode 100644 index 000000000..8715e27db --- /dev/null +++ b/src/core/3ds.h @@ -0,0 +1,21 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Core { + +// 3DS Video Constants +// ------------------- + +// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the +// framebuffers in video memory are stored in column-major order and rendered sideways, causing +// the widths and heights of the framebuffers read by the LCD to be switched compared to the +// heights and widths of the screens listed here. +constexpr int kScreenTopWidth = 400; ///< 3DS top screen width +constexpr int kScreenTopHeight = 240; ///< 3DS top screen height +constexpr int kScreenBottomWidth = 320; ///< 3DS bottom screen width +constexpr int kScreenBottomHeight = 240; ///< 3DS bottom screen height + +} // namespace Core diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 57504529f..b16a89990 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRCS frontend/camera/factory.cpp frontend/camera/interface.cpp frontend/emu_window.cpp + frontend/framebuffer_layout.cpp frontend/motion_emu.cpp gdbstub/gdbstub.cpp hle/config_mem.cpp @@ -44,6 +45,8 @@ set(SRCS hle/kernel/client_port.cpp hle/kernel/client_session.cpp hle/kernel/event.cpp + hle/kernel/handle_table.cpp + hle/kernel/hle_ipc.cpp hle/kernel/kernel.cpp hle/kernel/memory.cpp hle/kernel/mutex.cpp @@ -56,6 +59,7 @@ set(SRCS hle/kernel/thread.cpp hle/kernel/timer.cpp hle/kernel/vm_manager.cpp + hle/kernel/wait_object.cpp hle/service/ac/ac.cpp hle/service/ac/ac_i.cpp hle/service/ac/ac_u.cpp @@ -152,8 +156,9 @@ set(SRCS hle/service/qtm/qtm_sp.cpp hle/service/qtm/qtm_u.cpp hle/service/service.cpp + hle/service/sm/sm.cpp + hle/service/sm/srv.cpp hle/service/soc_u.cpp - hle/service/srv.cpp hle/service/ssl_c.cpp hle/service/y2r_u.cpp hle/shared_page.cpp @@ -178,6 +183,7 @@ set(SRCS ) set(HEADERS + 3ds.h arm/arm_interface.h arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_cp15.h @@ -216,6 +222,7 @@ set(HEADERS frontend/camera/factory.h frontend/camera/interface.h frontend/emu_window.h + frontend/framebuffer_layout.h frontend/input.h frontend/motion_emu.h gdbstub/gdbstub.h @@ -231,7 +238,10 @@ set(HEADERS hle/kernel/address_arbiter.h hle/kernel/client_port.h hle/kernel/client_session.h + hle/kernel/errors.h hle/kernel/event.h + hle/kernel/handle_table.h + hle/kernel/hle_ipc.h hle/kernel/kernel.h hle/kernel/memory.h hle/kernel/mutex.h @@ -245,6 +255,7 @@ set(HEADERS hle/kernel/thread.h hle/kernel/timer.h hle/kernel/vm_manager.h + hle/kernel/wait_object.h hle/result.h hle/service/ac/ac.h hle/service/ac/ac_i.h @@ -342,8 +353,9 @@ set(HEADERS hle/service/qtm/qtm_sp.h hle/service/qtm/qtm_u.h hle/service/service.h + hle/service/sm/sm.h + hle/service/sm/srv.h hle/service/soc_u.h - hle/service/srv.h hle/service/ssl_c.h hle/service/y2r_u.h hle/shared_page.h @@ -370,11 +382,7 @@ set(HEADERS telemetry_session.h ) -include_directories(../../externals/dynarmic/include) -include_directories(../../externals/cryptopp) - create_directory_groups(${SRCS} ${HEADERS}) - add_library(core STATIC ${SRCS} ${HEADERS}) - -target_link_libraries(core dynarmic cryptopp) +target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) diff --git a/src/core/core.cpp b/src/core/core.cpp index 450e7566d..5429bcb26 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <memory> - +#include <utility> #include "audio_core/audio_core.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" @@ -26,6 +26,7 @@ namespace Core { /*static*/ System System::s_instance; System::ResultStatus System::RunLoop(int tight_loop) { + status = ResultStatus::Success; if (!cpu_core) { return ResultStatus::ErrorNotInitialized; } @@ -59,7 +60,7 @@ System::ResultStatus System::RunLoop(int tight_loop) { HW::Update(); Reschedule(); - return ResultStatus::Success; + return status; } System::ResultStatus System::SingleStep() { @@ -73,14 +74,25 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str()); return ResultStatus::ErrorGetLoader; } + std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = + app_loader->LoadKernelSystemMode(); + + if (system_mode.second != Loader::ResultStatus::Success) { + LOG_CRITICAL(Core, "Failed to determine system mode (Error %i)!", + static_cast<int>(system_mode.second)); + System::Shutdown(); - boost::optional<u32> system_mode{app_loader->LoadKernelSystemMode()}; - if (!system_mode) { - LOG_CRITICAL(Core, "Failed to determine system mode!"); - return ResultStatus::ErrorSystemMode; + switch (system_mode.second) { + case Loader::ResultStatus::ErrorEncrypted: + return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorInvalidFormat: + return ResultStatus::ErrorLoader_ErrorInvalidFormat; + default: + return ResultStatus::ErrorSystemMode; + } } - ResultStatus init_result{Init(emu_window, system_mode.get())}; + ResultStatus init_result{Init(emu_window, system_mode.first.get())}; if (init_result != ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result); System::Shutdown(); @@ -101,7 +113,8 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file return ResultStatus::ErrorLoader; } } - return ResultStatus::Success; + status = ResultStatus::Success; + return status; } void System::PrepareReschedule() { diff --git a/src/core/core.h b/src/core/core.h index 6af772831..4e3b6b409 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -40,7 +40,10 @@ public: ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an /// invalid format + ErrorSystemFiles, ///< Error in finding system files + ErrorSharedFont, ///< Error in finding shared font ErrorVideoCore, ///< Error in the video core + ErrorUnknown ///< Any other error }; /** @@ -105,6 +108,17 @@ public: PerfStats perf_stats; FrameLimiter frame_limiter; + void SetStatus(ResultStatus new_status, const char* details = nullptr) { + status = new_status; + if (details) { + status_details = details; + } + } + + const std::string& GetStatusDetails() const { + return status_details; + } + private: /** * Initialize the emulated system. @@ -130,6 +144,9 @@ private: std::unique_ptr<Core::TelemetrySession> telemetry_session; static System s_instance; + + ResultStatus status = ResultStatus::Success; + std::string status_details = ""; }; inline ARM_Interface& CPU() { diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index f454e7840..4867c9d17 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -38,8 +38,7 @@ public: ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override { if (offset > size) { - return ResultCode(ErrorDescription::FS_WriteBeyondEnd, ErrorModule::FS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return ERR_WRITE_BEYOND_END; } else if (offset == size) { return MakeResult<size_t>(0); } @@ -191,11 +190,9 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData. // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist. if (!shared) { - return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, - ErrorSummary::InvalidState, ErrorLevel::Status); + return ERR_NOT_FOUND_INVALID_STATE; } else { - return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, - ErrorSummary::InvalidState, ErrorLevel::Status); + return ERR_NOT_FORMATTED; } } auto archive = std::make_unique<ExtSaveDataArchive>(fullpath); @@ -230,8 +227,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Pat if (!file.IsOpen()) { LOG_ERROR(Service_FS, "Could not open metadata information for archive"); // TODO(Subv): Verify error code - return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, - ErrorSummary::InvalidState, ErrorLevel::Status); + return ERR_NOT_FORMATTED; } ArchiveFormatInfo info = {}; diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index 89455e39c..6d9007731 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -9,7 +9,9 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/file_sys/archive_ncch.h" +#include "core/file_sys/errors.h" #include "core/file_sys/ivfc_archive.h" #include "core/hle/service/fs/archive.h" @@ -33,11 +35,44 @@ ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) { auto vec = path.AsBinary(); const u32* data = reinterpret_cast<u32*>(vec.data()); - std::string file_path = GetNCCHPath(mount_point, data[1], data[0]); + u32 high = data[1]; + u32 low = data[0]; + std::string file_path = GetNCCHPath(mount_point, high, low); auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); if (!file->IsOpen()) { - return ResultCode(-1); // TODO(Subv): Find the right error code + // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list). + constexpr u32 shared_data_archive = 0x0004009B; + constexpr u32 system_data_archive = 0x000400DB; + + // Low Title IDs. + constexpr u32 mii_data = 0x00010202; + constexpr u32 region_manifest = 0x00010402; + constexpr u32 ng_word_list = 0x00010302; + + LOG_DEBUG(Service_FS, "Full Path: %s. Category: 0x%X. Path: 0x%X.", path.DebugStr().c_str(), + high, low); + + if (high == shared_data_archive) { + if (low == mii_data) { + LOG_ERROR(Service_FS, "Failed to get a handle for shared data archive: Mii data. "); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles, + "Mii data"); + } else if (low == region_manifest) { + LOG_ERROR(Service_FS, + "Failed to get a handle for shared data archive: region manifest."); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles, + "Region manifest"); + } + } else if (high == system_data_archive) { + if (low == ng_word_list) { + LOG_ERROR(Service_FS, + "Failed to get a handle for system data archive: NG bad word list."); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles, + "NG bad word list"); + } + } + return ERROR_NOT_FOUND; } auto size = file->GetSize(); diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp index f31a68038..a7e331724 100644 --- a/src/core/file_sys/archive_source_sd_savedata.cpp +++ b/src/core/file_sys/archive_source_sd_savedata.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/archive_source_sd_savedata.h" +#include "core/file_sys/errors.h" #include "core/file_sys/savedata_archive.h" #include "core/hle/service/fs/archive.h" @@ -49,8 +50,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 pr // save file/directory structure expected by the game has not yet been initialized. // Returning the NotFormatted error code will signal the game to provision the SaveData // archive with the files and folders that it expects. - return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, - ErrorSummary::InvalidState, ErrorLevel::Status); + return ERR_NOT_FORMATTED; } auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point)); @@ -81,8 +81,7 @@ ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program if (!file.IsOpen()) { LOG_ERROR(Service_FS, "Could not open metadata information for archive"); // TODO(Subv): Verify error code - return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, - ErrorSummary::InvalidState, ErrorLevel::Status); + return ERR_NOT_FORMATTED; } ArchiveFormatInfo info = {}; diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 8986b5c0e..81423bffd 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -9,6 +9,7 @@ #include "common/file_util.h" #include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/errors.h" #include "core/file_sys/savedata_archive.h" #include "core/hle/service/fs/archive.h" @@ -53,8 +54,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c std::string fullpath = GetSystemSaveDataPath(base_path, path); if (!FileUtil::Exists(fullpath)) { // TODO(Subv): Check error code, this one is probably wrong - return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, - ErrorSummary::InvalidState, ErrorLevel::Status); + return ERR_NOT_FORMATTED; } auto archive = std::make_unique<SaveDataArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index a243d9a13..98d80aabc 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -9,6 +9,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "core/file_sys/disk_archive.h" +#include "core/file_sys/errors.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -17,8 +18,7 @@ namespace FileSys { ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { if (!mode.read_flag) - return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, - ErrorSummary::Canceled, ErrorLevel::Status); + return ERROR_INVALID_OPEN_FLAGS; file->Seek(offset, SEEK_SET); return MakeResult<size_t>(file->ReadBytes(buffer, length)); @@ -27,8 +27,7 @@ ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buff ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { if (!mode.write_flag) - return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, - ErrorSummary::Canceled, ErrorLevel::Status); + return ERROR_INVALID_OPEN_FLAGS; file->Seek(offset, SEEK_SET); size_t written = file->WriteBytes(buffer, length); diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 9fc8d753b..a974bc775 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -2,52 +2,93 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include "core/hle/result.h" namespace FileSys { -const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); -const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags, - ErrorModule::FS, ErrorSummary::NotSupported, - ErrorLevel::Usage); -const ResultCode ERROR_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, - ErrorSummary::Canceled, ErrorLevel::Status); -const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); -const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Status); -const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Status); -const ResultCode ERROR_NOT_FOUND(ErrorDescription::FS_NotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Status); -const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory, - ErrorModule::FS, ErrorSummary::NotSupported, - ErrorLevel::Usage); -const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile, - ErrorModule::FS, ErrorSummary::Canceled, - ErrorLevel::Status); -const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists, - ErrorModule::FS, ErrorSummary::NothingHappened, - ErrorLevel::Status); -const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS, - ErrorSummary::NothingHappened, ErrorLevel::Status); -const ResultCode ERROR_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, - ErrorSummary::NothingHappened, ErrorLevel::Status); -const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpty, ErrorModule::FS, - ErrorSummary::Canceled, ErrorLevel::Status); -const ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrorDescription::FS_GameCardNotInserted, - ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Status); -const ResultCode ERROR_INCORRECT_EXEFS_READ_SIZE(ErrorDescription::FS_IncorrectExeFSReadSize, - ErrorModule::FS, ErrorSummary::NotSupported, - ErrorLevel::Usage); -const ResultCode ERROR_ROMFS_NOT_FOUND(ErrorDescription::FS_RomFSNotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Status); -const ResultCode ERROR_COMMAND_NOT_ALLOWED(ErrorDescription::FS_CommandNotAllowed, ErrorModule::FS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); -const ResultCode ERROR_EXEFS_SECTION_NOT_FOUND(ErrorDescription::FS_ExeFSSectionNotFound, - ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Status); +namespace ErrCodes { +enum { + RomFSNotFound = 100, + ArchiveNotMounted = 101, + FileNotFound = 112, + PathNotFound = 113, + GameCardNotInserted = 141, + NotFound = 120, + FileAlreadyExists = 180, + DirectoryAlreadyExists = 185, + AlreadyExists = 190, + InvalidOpenFlags = 230, + DirectoryNotEmpty = 240, + NotAFile = 250, + NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive + ExeFSSectionNotFound = 567, + CommandNotAllowed = 630, + InvalidReadFlag = 700, + InvalidPath = 702, + WriteBeyondEnd = 705, + UnsupportedOpenFlags = 760, + IncorrectExeFSReadSize = 761, + UnexpectedFileOrDirectory = 770, +}; +} + +constexpr ResultCode ERROR_INVALID_PATH(ErrCodes::InvalidPath, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); +constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrCodes::UnsupportedOpenFlags, ErrorModule::FS, + ErrorSummary::NotSupported, ErrorLevel::Usage); +constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ErrCodes::InvalidOpenFlags, ErrorModule::FS, + ErrorSummary::Canceled, ErrorLevel::Status); +constexpr ResultCode ERROR_INVALID_READ_FLAG(ErrCodes::InvalidReadFlag, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); +constexpr ResultCode ERROR_FILE_NOT_FOUND(ErrCodes::FileNotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrCodes::PathNotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +constexpr ResultCode ERROR_NOT_FOUND(ErrCodes::NotFound, ErrorModule::FS, ErrorSummary::NotFound, + ErrorLevel::Status); +constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrCodes::UnexpectedFileOrDirectory, + ErrorModule::FS, ErrorSummary::NotSupported, + ErrorLevel::Usage); +constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrCodes::NotAFile, ErrorModule::FS, + ErrorSummary::Canceled, + ErrorLevel::Status); +constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrCodes::DirectoryAlreadyExists, + ErrorModule::FS, ErrorSummary::NothingHappened, + ErrorLevel::Status); +constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ErrCodes::FileAlreadyExists, ErrorModule::FS, + ErrorSummary::NothingHappened, ErrorLevel::Status); +constexpr ResultCode ERROR_ALREADY_EXISTS(ErrCodes::AlreadyExists, ErrorModule::FS, + ErrorSummary::NothingHappened, ErrorLevel::Status); +constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrCodes::DirectoryNotEmpty, ErrorModule::FS, + ErrorSummary::Canceled, ErrorLevel::Status); +constexpr ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrCodes::GameCardNotInserted, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +constexpr ResultCode ERROR_INCORRECT_EXEFS_READ_SIZE(ErrCodes::IncorrectExeFSReadSize, + ErrorModule::FS, ErrorSummary::NotSupported, + ErrorLevel::Usage); +constexpr ResultCode ERROR_ROMFS_NOT_FOUND(ErrCodes::RomFSNotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +constexpr ResultCode ERROR_COMMAND_NOT_ALLOWED(ErrCodes::CommandNotAllowed, ErrorModule::FS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent); +constexpr ResultCode ERROR_EXEFS_SECTION_NOT_FOUND(ErrCodes::ExeFSSectionNotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); + +/// Returned when a function is passed an invalid archive handle. +constexpr ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrCodes::ArchiveNotMounted, ErrorModule::FS, + ErrorSummary::NotFound, + ErrorLevel::Status); // 0xC8804465 +constexpr ResultCode ERR_WRITE_BEYOND_END(ErrCodes::WriteBeyondEnd, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + +/** + * Variant of ERROR_NOT_FOUND returned in some places in the code. Unknown if these usages are + * correct or a bug. + */ +constexpr ResultCode ERR_NOT_FOUND_INVALID_STATE(ErrCodes::NotFound, ErrorModule::FS, + ErrorSummary::InvalidState, ErrorLevel::Status); +constexpr ResultCode ERR_NOT_FORMATTED(ErrCodes::NotFormatted, ErrorModule::FS, + ErrorSummary::InvalidState, ErrorLevel::Status); } // namespace FileSys diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 5fdb3a7e8..4f7d54a33 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -5,10 +5,10 @@ #include <algorithm> #include <cmath> #include "common/assert.h" +#include "core/3ds.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/settings.h" -#include "video_core/video_core.h" /** * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout @@ -38,11 +38,9 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) return; - touch_x = VideoCore::kScreenBottomWidth * - (framebuffer_x - framebuffer_layout.bottom_screen.left) / + touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); - touch_y = VideoCore::kScreenBottomHeight * - (framebuffer_y - framebuffer_layout.bottom_screen.top) / + touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); touch_pressed = true; diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 36f2667fa..9414123a4 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -8,8 +8,8 @@ #include <tuple> #include <utility> #include "common/common_types.h" -#include "common/framebuffer_layout.h" #include "common/math_util.h" +#include "core/frontend/framebuffer_layout.h" /** * Abstraction class used to provide an interface between emulation code and the frontend diff --git a/src/common/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index a2a0e7dad..d2d02f9ff 100644 --- a/src/common/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -5,16 +5,20 @@ #include <cmath> #include "common/assert.h" -#include "common/framebuffer_layout.h" +#include "core/3ds.h" +#include "core/frontend/framebuffer_layout.h" #include "core/settings.h" -#include "video_core/video_core.h" namespace Layout { static const float TOP_SCREEN_ASPECT_RATIO = - static_cast<float>(VideoCore::kScreenTopHeight) / VideoCore::kScreenTopWidth; + static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth; static const float BOT_SCREEN_ASPECT_RATIO = - static_cast<float>(VideoCore::kScreenBottomHeight) / VideoCore::kScreenBottomWidth; + static_cast<float>(Core::kScreenBottomHeight) / Core::kScreenBottomWidth; + +float FramebufferLayout::GetScalingRatio() const { + return static_cast<float>(top_screen.GetWidth()) / Core::kScreenTopWidth; +} // Finds the largest size subrectangle contained in window area that is confined to the aspect ratio template <class T> @@ -106,10 +110,10 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped float window_aspect_ratio = static_cast<float>(height) / width; float emulation_aspect_ratio = swapped - ? VideoCore::kScreenBottomHeight * 4 / - (VideoCore::kScreenBottomWidth * 4.0f + VideoCore::kScreenTopWidth) - : VideoCore::kScreenTopHeight * 4 / - (VideoCore::kScreenTopWidth * 4.0f + VideoCore::kScreenBottomWidth); + ? Core::kScreenBottomHeight * 4 / + (Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth) + : Core::kScreenTopHeight * 4 / + (Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth); float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO; diff --git a/src/common/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index f1df5c55a..9a7738969 100644 --- a/src/common/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -5,7 +5,9 @@ #pragma once #include "common/math_util.h" + namespace Layout { + /// Describes the layout of the window framebuffer (size and top/bottom screen positions) struct FramebufferLayout { unsigned width; @@ -14,6 +16,12 @@ struct FramebufferLayout { bool bottom_screen_enabled; MathUtil::Rectangle<unsigned> top_screen; MathUtil::Rectangle<unsigned> bottom_screen; + + /** + * Returns the ration of pixel size of the top screen, compared to the native size of the 3DS + * screen. + */ + float GetScalingRatio() const; }; /** @@ -52,4 +60,5 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swap * @return Newly created FramebufferLayout object with default screen regions initialized */ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height); -} + +} // namespace Layout diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 07c7f5b99..89f08daa2 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -11,7 +11,6 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" -#include "video_core/video_core.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 059297fbc..fdf8807b0 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -14,7 +14,6 @@ #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/hid/hid.h" #include "core/memory.h" -#include "video_core/video_core.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index f6eb900f0..18b6e7017 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -53,7 +53,7 @@ void Wrap() { FuncReturn(retval); } -template <ResultCode func(u32*, s32, u32, u32, u32, s32)> +template <ResultCode func(u32*, u32, u32, u32, u32, s32)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 3a5d481a5..303ca090d 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -5,6 +5,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -43,6 +44,12 @@ inline u32* GetStaticBuffers(const int offset = 0) { namespace IPC { +// These errors are commonly returned by invalid IPC translations, so alias them here for +// convenience. +// TODO(yuriks): These will probably go away once translation is implemented inside the kernel. +using Kernel::ERR_INVALID_BUFFER_DESCRIPTOR; +constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; + enum DescriptorType : u32 { // Buffer related desciptors types (mask : 0x0F) StaticBuffer = 0x02, diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 06c4c5a85..d7348c09d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -3,7 +3,9 @@ // Refer to the license.txt file included. #pragma once + #include "core/hle/ipc.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" namespace IPC { diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 01fab123e..776d342f0 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -5,6 +5,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/kernel/address_arbiter.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -74,8 +75,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, default: LOG_ERROR(Kernel, "unknown type=%d", type); - return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, - ErrorSummary::WrongArgument, ErrorLevel::Usage); + return ERR_INVALID_ENUM_VALUE_FND; } // The calls that use a timeout seem to always return a Timeout error even if they did not put @@ -83,8 +83,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, if (type == ArbitrationType::WaitIfLessThanWithTimeout || type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { - return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, - ErrorLevel::Info); + return RESULT_TIMEOUT; } return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 6a7af93a9..1d24401b1 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" // Address arbiters are an underlying kernel synchronization object that can be created/used via // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index ddcf4c916..ce5d94e99 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -5,6 +5,8 @@ #include "common/assert.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" @@ -19,26 +21,22 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { // AcceptSession before returning from this call. if (active_sessions >= max_sessions) { - return ResultCode(ErrorDescription::MaxConnectionsReached, ErrorModule::OS, - ErrorSummary::WouldBlock, ErrorLevel::Temporary); + return ERR_MAX_CONNECTIONS_REACHED; } active_sessions++; // Create a new session pair, let the created sessions inherit the parent port's HLE handler. - auto sessions = - ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler, this); - auto client_session = std::get<SharedPtr<ClientSession>>(sessions); - auto server_session = std::get<SharedPtr<ServerSession>>(sessions); + auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this); if (server_port->hle_handler) - server_port->hle_handler->ClientConnected(server_session); + server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); else - server_port->pending_sessions.push_back(std::move(server_session)); + server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions)); // Wake the threads waiting on the ServerPort server_port->WakeupAllWaitingThreads(); - return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); + return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); } } // namespace diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 511490c7c..8f7d6ac44 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -7,6 +7,7 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index e297b7464..fef97af1f 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -5,7 +5,10 @@ #include "common/assert.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/session.h" namespace Kernel { @@ -14,9 +17,13 @@ ClientSession::~ClientSession() { // This destructor will be called automatically when the last ClientSession handle is closed by // the emulated application. - if (parent->server) { - if (parent->server->hle_handler) - parent->server->hle_handler->ClientDisconnected(parent->server); + // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they + // will be kept alive until after ClientDisconnected() returns. + SharedPtr<ServerSession> server = parent->server; + if (server) { + std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler; + if (hle_handler) + hle_handler->ClientDisconnected(server); // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set // their WaitSynchronization result to 0xC920181A. @@ -26,12 +33,13 @@ ClientSession::~ClientSession() { } ResultCode ClientSession::SendSyncRequest() { - // Signal the server session that new data is available - if (parent->server) - return parent->server->HandleSyncRequest(); + // Keep ServerSession alive until we're done working with it. + SharedPtr<ServerSession> server = parent->server; + if (server == nullptr) + return ERR_SESSION_CLOSED_BY_REMOTE; - return ResultCode(ErrorDescription::SessionClosedByRemote, ErrorModule::OS, - ErrorSummary::Canceled, ErrorLevel::Status); + // Signal the server session that new data is available + return server->HandleSyncRequest(); } } // namespace diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 9f3adb72b..2de379c09 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -6,10 +6,9 @@ #include <memory> #include <string> - #include "common/common_types.h" - #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h new file mode 100644 index 000000000..b3b60e7df --- /dev/null +++ b/src/core/hle/kernel/errors.h @@ -0,0 +1,98 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Kernel { + +namespace ErrCodes { +enum { + OutOfHandles = 19, + SessionClosedByRemote = 26, + PortNameTooLong = 30, + WrongPermission = 46, + InvalidBufferDescriptor = 48, + MaxConnectionsReached = 52, +}; +} + +// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always +// double check that the code matches before re-using the constant. + +constexpr ResultCode ERR_OUT_OF_HANDLES(ErrCodes::OutOfHandles, ErrorModule::Kernel, + ErrorSummary::OutOfResource, + ErrorLevel::Permanent); // 0xD8600413 +constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrCodes::SessionClosedByRemote, ErrorModule::OS, + ErrorSummary::Canceled, + ErrorLevel::Status); // 0xC920181A +constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrCodes::PortNameTooLong, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E0181E +constexpr ResultCode ERR_WRONG_PERMISSION(ErrCodes::WrongPermission, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent); +constexpr ResultCode ERR_INVALID_BUFFER_DESCRIPTOR(ErrCodes::InvalidBufferDescriptor, + ErrorModule::OS, ErrorSummary::WrongArgument, + ErrorLevel::Permanent); +constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrCodes::MaxConnectionsReached, ErrorModule::OS, + ErrorSummary::WouldBlock, + ErrorLevel::Temporary); // 0xD0401834 + +constexpr ResultCode ERR_NOT_AUTHORIZED(ErrorDescription::NotAuthorized, ErrorModule::OS, + ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9001BEA +constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E007ED +constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(ErrorDescription::InvalidEnumValue, + ErrorModule::FND, ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E093ED +constexpr ResultCode ERR_INVALID_COMBINATION(ErrorDescription::InvalidCombination, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E01BEE +constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorDescription::InvalidCombination, + ErrorModule::Kernel, + ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD90007EE +constexpr ResultCode ERR_MISALIGNED_ADDRESS(ErrorDescription::MisalignedAddress, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E01BF1 +constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E01BF2 +constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel, + ErrorSummary::OutOfResource, + ErrorLevel::Permanent); // 0xD86007F3 +constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E01BF4 +constexpr ResultCode ERR_INVALID_ADDRESS(ErrorDescription::InvalidAddress, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E01BF5 +constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorDescription::InvalidAddress, ErrorModule::OS, + ErrorSummary::InvalidState, + ErrorLevel::Usage); // 0xE0A01BF5 +constexpr ResultCode ERR_INVALID_POINTER(ErrorDescription::InvalidPointer, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E007F6 +constexpr ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E007F7 +/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. +constexpr ResultCode ERR_INVALID_HANDLE_OS(ErrorDescription::InvalidHandle, ErrorModule::OS, + ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9001BF7 +constexpr ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel, + ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA +constexpr ResultCode ERR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::OS, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E01BFD +constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E007FD +constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info); + +} // namespace Kernel diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 3e3673508..cc41abb85 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp new file mode 100644 index 000000000..c7322d883 --- /dev/null +++ b/src/core/hle/kernel/handle_table.cpp @@ -0,0 +1,97 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <utility> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +HandleTable g_handle_table; + +HandleTable::HandleTable() { + next_generation = 1; + Clear(); +} + +ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { + DEBUG_ASSERT(obj != nullptr); + + u16 slot = next_free_slot; + if (slot >= generations.size()) { + LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); + return ERR_OUT_OF_HANDLES; + } + next_free_slot = generations[slot]; + + u16 generation = next_generation++; + + // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. + // CTR-OS doesn't use generation 0, so skip straight to 1. + if (next_generation >= (1 << 15)) + next_generation = 1; + + generations[slot] = generation; + objects[slot] = std::move(obj); + + Handle handle = generation | (slot << 15); + return MakeResult<Handle>(handle); +} + +ResultVal<Handle> HandleTable::Duplicate(Handle handle) { + SharedPtr<Object> object = GetGeneric(handle); + if (object == nullptr) { + LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); + return ERR_INVALID_HANDLE; + } + return Create(std::move(object)); +} + +ResultCode HandleTable::Close(Handle handle) { + if (!IsValid(handle)) + return ERR_INVALID_HANDLE; + + u16 slot = GetSlot(handle); + + objects[slot] = nullptr; + + generations[slot] = next_free_slot; + next_free_slot = slot; + return RESULT_SUCCESS; +} + +bool HandleTable::IsValid(Handle handle) const { + size_t slot = GetSlot(handle); + u16 generation = GetGeneration(handle); + + return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; +} + +SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { + if (handle == CurrentThread) { + return GetCurrentThread(); + } else if (handle == CurrentProcess) { + return g_current_process; + } + + if (!IsValid(handle)) { + return nullptr; + } + return objects[GetSlot(handle)]; +} + +void HandleTable::Clear() { + for (u16 i = 0; i < MAX_COUNT; ++i) { + generations[i] = i + 1; + objects[i] = nullptr; + } + next_free_slot = 0; +} + +} // namespace diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h new file mode 100644 index 000000000..d6aaefbf7 --- /dev/null +++ b/src/core/hle/kernel/handle_table.h @@ -0,0 +1,126 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <cstddef> +#include "common/common_types.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" + +namespace Kernel { + +enum KernelHandle : Handle { + CurrentThread = 0xFFFF8000, + CurrentProcess = 0xFFFF8001, +}; + +/** + * This class allows the creation of Handles, which are references to objects that can be tested + * for validity and looked up. Here they are used to pass references to kernel objects to/from the + * emulated process. it has been designed so that it follows the same handle format and has + * approximately the same restrictions as the handle manager in the CTR-OS. + * + * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). + * The slot index is used to index into the arrays in this class to access the data corresponding + * to the Handle. + * + * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter + * is kept and incremented every time a Handle is created. This is the Handle's "generation". The + * value of the counter is stored into the Handle as well as in the handle table (in the + * "generations" array). When looking up a handle, the Handle's generation must match with the + * value stored on the class, otherwise the Handle is considered invalid. + * + * To find free slots when allocating a Handle without needing to scan the entire object array, the + * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. + * When a Handle is created, an index is popped off the list and used for the new Handle. When it + * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is + * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been + * verified and isn't likely to cause any problems. + */ +class HandleTable final : NonCopyable { +public: + HandleTable(); + + /** + * Allocates a handle for the given object. + * @return The created Handle or one of the following errors: + * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. + */ + ResultVal<Handle> Create(SharedPtr<Object> obj); + + /** + * Returns a new handle that points to the same object as the passed in handle. + * @return The duplicated Handle or one of the following errors: + * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. + * - Any errors returned by `Create()`. + */ + ResultVal<Handle> Duplicate(Handle handle); + + /** + * Closes a handle, removing it from the table and decreasing the object's ref-count. + * @return `RESULT_SUCCESS` or one of the following errors: + * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. + */ + ResultCode Close(Handle handle); + + /// Checks if a handle is valid and points to an existing object. + bool IsValid(Handle handle) const; + + /** + * Looks up a handle. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. + */ + SharedPtr<Object> GetGeneric(Handle handle) const; + + /** + * Looks up a handle while verifying its type. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its + * type differs from the requested one. + */ + template <class T> + SharedPtr<T> Get(Handle handle) const { + return DynamicObjectCast<T>(GetGeneric(handle)); + } + + /// Closes all handles held in this table. + void Clear(); + +private: + /** + * This is the maximum limit of handles allowed per process in CTR-OS. It can be further + * reduced by ExHeader values, but this is not emulated here. + */ + static const size_t MAX_COUNT = 4096; + + static u16 GetSlot(Handle handle) { + return handle >> 15; + } + static u16 GetGeneration(Handle handle) { + return handle & 0x7FFF; + } + + /// Stores the Object referenced by the handle or null if the slot is empty. + std::array<SharedPtr<Object>, MAX_COUNT> objects; + + /** + * The value of `next_generation` when the handle was created, used to check for validity. For + * empty slots, contains the index of the next free slot in the list. + */ + std::array<u16, MAX_COUNT> generations; + + /** + * Global counter of the number of created handles. Stored in `generations` when a handle is + * created, and wraps around to 1 when it hits 0x8000. + */ + u16 next_generation; + + /// Head of the free slots linked list. + u16 next_free_slot; +}; + +extern HandleTable g_handle_table; + +} // namespace diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp new file mode 100644 index 000000000..a60b8ef00 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -0,0 +1,26 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/range/algorithm_ext/erase.hpp> +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_session.h" + +namespace Kernel { + +void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) { + server_session->SetHleHandler(shared_from_this()); + connected_sessions.push_back(server_session); +} + +void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) { + server_session->SetHleHandler(nullptr); + boost::range::remove_erase(connected_sessions, server_session); +} + +HLERequestContext::~HLERequestContext() = default; + +} // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h new file mode 100644 index 000000000..c30184eab --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.h @@ -0,0 +1,87 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_session.h" + +namespace Service { +class ServiceFrameworkBase; +} + +namespace Kernel { + +/** + * Interface implemented by HLE Session handlers. + * This can be provided to a ServerSession in order to hook into several relevant events + * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. + */ +class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { +public: + virtual ~SessionRequestHandler() = default; + + /** + * Handles a sync request from the emulated application. + * @param server_session The ServerSession that was triggered for this sync request, + * it should be used to differentiate which client (As in ClientSession) we're answering to. + * TODO(Subv): Use a wrapper structure to hold all the information relevant to + * this request (ServerSession, Originator thread, Translated command buffer, etc). + * @returns ResultCode the result code of the translate operation. + */ + virtual void HandleSyncRequest(SharedPtr<ServerSession> server_session) = 0; + + /** + * Signals that a client has just connected to this HLE handler and keeps the + * associated ServerSession alive for the duration of the connection. + * @param server_session Owning pointer to the ServerSession associated with the connection. + */ + void ClientConnected(SharedPtr<ServerSession> server_session); + + /** + * Signals that a client has just disconnected from this HLE handler and releases the + * associated ServerSession. + * @param server_session ServerSession associated with the connection. + */ + void ClientDisconnected(SharedPtr<ServerSession> server_session); + +protected: + /// List of sessions that are connected to this handler. + /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list + // for the duration of the connection. + std::vector<SharedPtr<ServerSession>> connected_sessions; +}; + +/** + * Class containing information about an in-flight IPC request being handled by an HLE service + * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and + * when possible use the APIs in this class to service the request. + */ +class HLERequestContext { +public: + ~HLERequestContext(); + + /// Returns a pointer to the IPC command buffer for this request. + u32* CommandBuffer() const { + return cmd_buf; + } + + /** + * Returns the session through which this request was made. This can be used as a map key to + * access per-client data on services. + */ + SharedPtr<ServerSession> Session() const { + return session; + } + +private: + friend class Service::ServiceFrameworkBase; + + u32* cmd_buf = nullptr; + SharedPtr<ServerSession> session; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f599916f0..7470a97ca 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -2,10 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> -#include "common/assert.h" -#include "common/logging/log.h" #include "core/hle/config_mem.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" @@ -17,165 +15,6 @@ namespace Kernel { unsigned int Object::next_object_id; -HandleTable g_handle_table; - -void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { - auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); - if (itr == waiting_threads.end()) - waiting_threads.push_back(std::move(thread)); -} - -void WaitObject::RemoveWaitingThread(Thread* thread) { - auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); - // If a thread passed multiple handles to the same object, - // the kernel might attempt to remove the thread from the object's - // waiting threads list multiple times. - if (itr != waiting_threads.end()) - waiting_threads.erase(itr); -} - -SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { - Thread* candidate = nullptr; - s32 candidate_priority = THREADPRIO_LOWEST + 1; - - for (const auto& thread : waiting_threads) { - // The list of waiting threads must not contain threads that are not waiting to be awakened. - ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL, - "Inconsistent thread statuses in waiting_threads"); - - if (thread->current_priority >= candidate_priority) - continue; - - if (ShouldWait(thread.get())) - continue; - - // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or - // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. - bool ready_to_run = true; - if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { - ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), - [&thread](const SharedPtr<WaitObject>& object) { - return object->ShouldWait(thread.get()); - }); - } - - if (ready_to_run) { - candidate = thread.get(); - candidate_priority = thread->current_priority; - } - } - - return candidate; -} - -void WaitObject::WakeupAllWaitingThreads() { - while (auto thread = GetHighestPriorityReadyThread()) { - if (!thread->IsSleepingOnWaitAll()) { - Acquire(thread.get()); - // Set the output index of the WaitSynchronizationN call to the index of this object. - if (thread->wait_set_output) { - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); - thread->wait_set_output = false; - } - } else { - for (auto& object : thread->wait_objects) { - object->Acquire(thread.get()); - } - // Note: This case doesn't update the output index of WaitSynchronizationN. - } - - for (auto& object : thread->wait_objects) - object->RemoveWaitingThread(thread.get()); - thread->wait_objects.clear(); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->ResumeFromWait(); - } -} - -const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { - return waiting_threads; -} - -HandleTable::HandleTable() { - next_generation = 1; - Clear(); -} - -ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { - DEBUG_ASSERT(obj != nullptr); - - u16 slot = next_free_slot; - if (slot >= generations.size()) { - LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); - return ERR_OUT_OF_HANDLES; - } - next_free_slot = generations[slot]; - - u16 generation = next_generation++; - - // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. - // CTR-OS doesn't use generation 0, so skip straight to 1. - if (next_generation >= (1 << 15)) - next_generation = 1; - - generations[slot] = generation; - objects[slot] = std::move(obj); - - Handle handle = generation | (slot << 15); - return MakeResult<Handle>(handle); -} - -ResultVal<Handle> HandleTable::Duplicate(Handle handle) { - SharedPtr<Object> object = GetGeneric(handle); - if (object == nullptr) { - LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); - return ERR_INVALID_HANDLE; - } - return Create(std::move(object)); -} - -ResultCode HandleTable::Close(Handle handle) { - if (!IsValid(handle)) - return ERR_INVALID_HANDLE; - - u16 slot = GetSlot(handle); - - objects[slot] = nullptr; - - generations[slot] = next_free_slot; - next_free_slot = slot; - return RESULT_SUCCESS; -} - -bool HandleTable::IsValid(Handle handle) const { - size_t slot = GetSlot(handle); - u16 generation = GetGeneration(handle); - - return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; -} - -SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { - if (handle == CurrentThread) { - return GetCurrentThread(); - } else if (handle == CurrentProcess) { - return g_current_process; - } - - if (!IsValid(handle)) { - return nullptr; - } - return objects[GetSlot(handle)]; -} - -void HandleTable::Clear() { - for (u16 i = 0; i < MAX_COUNT; ++i) { - generations[i] = i + 1; - objects[i] = nullptr; - } - next_free_slot = 0; -} /// Initialize the kernel void Init(u32 system_mode) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index bb8b99bb5..9cf288b08 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,33 +4,16 @@ #pragma once -#include <algorithm> -#include <array> #include <cstddef> #include <string> -#include <vector> +#include <utility> #include <boost/smart_ptr/intrusive_ptr.hpp> #include "common/common_types.h" -#include "core/hle/result.h" namespace Kernel { using Handle = u32; -class Thread; - -// TODO: Verify code -const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, - ErrorSummary::OutOfResource, ErrorLevel::Temporary); -// TOOD: Verify code -const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - -enum KernelHandle : Handle { - CurrentThread = 0xFFFF8000, - CurrentProcess = 0xFFFF8001, -}; - enum class HandleType : u32 { Unknown, Event, @@ -128,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) { template <typename T> using SharedPtr = boost::intrusive_ptr<T>; -/// Class that represents a Kernel object that a thread can be waiting on -class WaitObject : public Object { -public: - /** - * Check if the specified thread should wait until the object is available - * @param thread The thread about which we're deciding. - * @return True if the current thread should wait due to this object being unavailable - */ - virtual bool ShouldWait(Thread* thread) const = 0; - - /// Acquire/lock the object for the specified thread if it is available - virtual void Acquire(Thread* thread) = 0; - - /** - * Add a thread to wait on this object - * @param thread Pointer to thread to add - */ - virtual void AddWaitingThread(SharedPtr<Thread> thread); - - /** - * Removes a thread from waiting on this object (e.g. if it was resumed already) - * @param thread Pointer to thread to remove - */ - virtual void RemoveWaitingThread(Thread* thread); - - /** - * Wake up all threads waiting on this object that can be awoken, in priority order, - * and set the synchronization result and output of the thread. - */ - virtual void WakeupAllWaitingThreads(); - - /// Obtains the highest priority thread that is ready to run from this object's waiting list. - SharedPtr<Thread> GetHighestPriorityReadyThread(); - - /// Get a const reference to the waiting threads list for debug use - const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; - -private: - /// Threads waiting for this object to become available - std::vector<SharedPtr<Thread>> waiting_threads; -}; - /** - * This class allows the creation of Handles, which are references to objects that can be tested - * for validity and looked up. Here they are used to pass references to kernel objects to/from the - * emulated process. it has been designed so that it follows the same handle format and has - * approximately the same restrictions as the handle manager in the CTR-OS. - * - * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). - * The slot index is used to index into the arrays in this class to access the data corresponding - * to the Handle. - * - * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter - * is kept and incremented every time a Handle is created. This is the Handle's "generation". The - * value of the counter is stored into the Handle as well as in the handle table (in the - * "generations" array). When looking up a handle, the Handle's generation must match with the - * value stored on the class, otherwise the Handle is considered invalid. - * - * To find free slots when allocating a Handle without needing to scan the entire object array, the - * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. - * When a Handle is created, an index is popped off the list and used for the new Handle. When it - * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is - * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been - * verified and isn't likely to cause any problems. + * Attempts to downcast the given Object pointer to a pointer to T. + * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. */ -class HandleTable final : NonCopyable { -public: - HandleTable(); - - /** - * Allocates a handle for the given object. - * @return The created Handle or one of the following errors: - * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. - */ - ResultVal<Handle> Create(SharedPtr<Object> obj); - - /** - * Returns a new handle that points to the same object as the passed in handle. - * @return The duplicated Handle or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - * - Any errors returned by `Create()`. - */ - ResultVal<Handle> Duplicate(Handle handle); - - /** - * Closes a handle, removing it from the table and decreasing the object's ref-count. - * @return `RESULT_SUCCESS` or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - */ - ResultCode Close(Handle handle); - - /// Checks if a handle is valid and points to an existing object. - bool IsValid(Handle handle) const; - - /** - * Looks up a handle. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. - */ - SharedPtr<Object> GetGeneric(Handle handle) const; - - /** - * Looks up a handle while verifying its type. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its - * type differs from the handle type `T::HANDLE_TYPE`. - */ - template <class T> - SharedPtr<T> Get(Handle handle) const { - SharedPtr<Object> object = GetGeneric(handle); - if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { - return boost::static_pointer_cast<T>(std::move(object)); - } - return nullptr; - } - - /** - * Looks up a handle while verifying that it is an object that a thread can wait on - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is - * not a waitable object. - */ - SharedPtr<WaitObject> GetWaitObject(Handle handle) const { - SharedPtr<Object> object = GetGeneric(handle); - if (object != nullptr && object->IsWaitable()) { - return boost::static_pointer_cast<WaitObject>(std::move(object)); - } - return nullptr; - } - - /// Closes all handles held in this table. - void Clear(); - -private: - /** - * This is the maximum limit of handles allowed per process in CTR-OS. It can be further - * reduced by ExHeader values, but this is not emulated here. - */ - static const size_t MAX_COUNT = 4096; - - static u16 GetSlot(Handle handle) { - return handle >> 15; - } - static u16 GetGeneration(Handle handle) { - return handle & 0x7FFF; +template <typename T> +inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { + if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { + return boost::static_pointer_cast<T>(std::move(object)); } - - /// Stores the Object referenced by the handle or null if the slot is empty. - std::array<SharedPtr<Object>, MAX_COUNT> objects; - - /** - * The value of `next_generation` when the handle was created, used to check for validity. For - * empty slots, contains the index of the next free slot in the list. - */ - std::array<u16, MAX_COUNT> generations; - - /** - * Global counter of the number of created handles. Stored in `generations` when a handle is - * created, and wraps around to 1 when it hits 0x8000. - */ - u16 next_generation; - - /// Head of the free slots linked list. - u16 next_free_slot; -}; - -extern HandleTable g_handle_table; + return nullptr; +} /// Initialize the kernel with the specified system mode. void Init(u32 system_mode); diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 8250a90b5..804f23b1c 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include <map> #include <memory> diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index c57adf400..bacacd690 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -7,6 +7,7 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 32cb25fb7..1c31ec950 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 3f51bc5de..a8f10a3ee 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <cstring> +#include "common/assert.h" #include "common/logging/log.h" #include "core/hle/kernel/resource_limit.h" diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 8bda2f75d..fcf586728 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/assert.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/thread.h" @@ -16,8 +17,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou std::string name) { if (initial_count > max_count) - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); + return ERR_INVALID_COMBINATION_KERNEL; SharedPtr<Semaphore> semaphore(new Semaphore); @@ -42,8 +42,7 @@ void Semaphore::Acquire(Thread* thread) { ResultVal<s32> Semaphore::Release(s32 release_count) { if (max_count - available_count < release_count) - return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + return ERR_OUT_OF_RANGE_KERNEL; s32 previous_count = available_count; available_count += release_count; diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index cde94f7cc..7b0cacf2e 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -8,6 +8,8 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index fd3bbbcad..4d20c39a1 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -24,14 +24,12 @@ void ServerPort::Acquire(Thread* thread) { } std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( - u32 max_sessions, std::string name, - std::shared_ptr<Service::SessionRequestHandler> hle_handler) { + u32 max_sessions, std::string name) { SharedPtr<ServerPort> server_port(new ServerPort); SharedPtr<ClientPort> client_port(new ClientPort); server_port->name = name + "_Server"; - server_port->hle_handler = std::move(hle_handler); client_port->name = name + "_Client"; client_port->server_port = server_port; client_port->max_sessions = max_sessions; diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6f8bdb6a9..f1419cd46 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -9,28 +9,24 @@ #include <tuple> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" - -namespace Service { -class SessionRequestHandler; -} +#include "core/hle/kernel/wait_object.h" namespace Kernel { class ClientPort; +class SessionRequestHandler; 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 - * @param hle_handler Optional HLE handler template for the port, - * ServerSessions crated from this port will inherit a reference to this handler. * @return The created port tuple */ static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( - u32 max_sessions, std::string name = "UnknownPort", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + u32 max_sessions, std::string name = "UnknownPort"); std::string GetTypeName() const override { return "ServerPort"; @@ -44,6 +40,14 @@ public: return HANDLE_TYPE; } + /** + * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port + * will inherit a reference to this handler. + */ + void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + hle_handler = std::move(hle_handler_); + } + std::string name; ///< Name of port (optional) std::vector<SharedPtr<WaitObject>> @@ -51,7 +55,7 @@ public: /// This session's HLE request handler template (optional) /// ServerSessions created from this port inherit a reference to this handler. - std::shared_ptr<Service::SessionRequestHandler> hle_handler; + std::shared_ptr<SessionRequestHandler> hle_handler; bool ShouldWait(Thread* thread) const override; void Acquire(Thread* thread) override; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 500b909ab..2dc709bc9 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -4,8 +4,11 @@ #include <tuple> +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" namespace Kernel { @@ -25,16 +28,14 @@ ServerSession::~ServerSession() { parent->server = nullptr; } -ResultVal<SharedPtr<ServerSession>> ServerSession::Create( - std::string name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { +ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) { SharedPtr<ServerSession> server_session(new ServerSession); server_session->name = std::move(name); server_session->signaled = false; - server_session->hle_handler = std::move(hle_handler); server_session->parent = nullptr; - return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); + return MakeResult(std::move(server_session)); } bool ServerSession::ShouldWait(Thread* thread) const { @@ -68,13 +69,9 @@ ResultCode ServerSession::HandleSyncRequest() { return RESULT_SUCCESS; } -ServerSession::SessionPair ServerSession::CreateSessionPair( - const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler, - SharedPtr<ClientPort> port) { - - auto server_session = - ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom(); - +ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, + SharedPtr<ClientPort> port) { + auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); SharedPtr<ClientSession> client_session(new ClientSession); client_session->name = name + "_Client"; diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c907d487c..5365605da 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -9,10 +9,8 @@ #include "common/assert.h" #include "common/common_types.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" -#include "core/hle/service/service.h" #include "core/memory.h" namespace Kernel { @@ -20,6 +18,9 @@ namespace Kernel { class ClientSession; class ClientPort; class ServerSession; +class Session; +class SessionRequestHandler; +class Thread; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -49,14 +50,20 @@ public: /** * Creates a pair of ServerSession and an associated ClientSession. * @param name Optional name of the ports. - * @param hle_handler Optional HLE handler for this server session. * @param client_port Optional The ClientPort that spawned this session. * @return The created session tuple */ - static SessionPair CreateSessionPair( - const std::string& name = "Unknown", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr, - SharedPtr<ClientPort> client_port = nullptr); + static SessionPair CreateSessionPair(const std::string& name = "Unknown", + SharedPtr<ClientPort> client_port = nullptr); + + /** + * Sets the HLE handler for the session. This handler will be called to service IPC requests + * instead of the regular IPC machinery. (The regular IPC machinery is currently not + * implemented.) + */ + void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + hle_handler = std::move(hle_handler_); + } /** * Handle a sync request from the emulated application. @@ -71,7 +78,7 @@ public: std::string name; ///< The name of this session (optional) bool signaled; ///< Whether there's new data available to this ServerSession std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. - std::shared_ptr<Service::SessionRequestHandler> + std::shared_ptr<SessionRequestHandler> hle_handler; ///< This session's HLE request handler (optional) private: @@ -82,12 +89,9 @@ private: * Creates a server session. The server session can have an optional HLE handler, * which will be invoked to handle the IPC requests that this session receives. * @param name Optional name of the server session. - * @param hle_handler Optional HLE handler for this server session. * @return The created server session */ - static ResultVal<SharedPtr<ServerSession>> Create( - std::string name = "Unknown", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown"); }; /** diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index bc1560d12..922e5ab58 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -4,6 +4,7 @@ #include <cstring> #include "common/logging/log.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" @@ -102,24 +103,21 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return ERR_INVALID_COMBINATION; } // Error out if the requested permissions don't match what the creator process allows. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", GetObjectId(), address, name.c_str()); - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return ERR_INVALID_COMBINATION; } // Heap-backed memory blocks can not be mapped with other_permissions = DontCare if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", GetObjectId(), address, name.c_str()); - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return ERR_INVALID_COMBINATION; } // Error out if the provided permissions are not compatible with what the creator process needs. @@ -127,8 +125,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", GetObjectId(), address, name.c_str()); - return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); + return ERR_WRONG_PERMISSION; } // TODO(Subv): Check for the Shared Device Mem flag in the creator process. @@ -144,8 +141,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) { LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address", GetObjectId(), address, name.c_str()); - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return ERR_INVALID_ADDRESS; } } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3b7555d87..75ce626f8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -14,6 +14,8 @@ #include "core/arm/skyeye_common/armstate.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" @@ -241,9 +243,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { for (auto& object : thread->wait_objects) object->RemoveWaitingThread(thread.get()); thread->wait_objects.clear(); - thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, - ErrorLevel::Info)); + thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); } thread->ResumeFromWait(); @@ -351,10 +351,20 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_ context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode } -ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, +ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top) { - ASSERT_MSG(priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST, - "Invalid thread priority"); + // Check if priority is in ranged. Lowest priority -> highest priority id. + if (priority > THREADPRIO_LOWEST) { + LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); + return ERR_OUT_OF_RANGE; + } + + if (processor_id > THREADPROCESSORID_MAX) { + LOG_ERROR(Kernel_SVC, "Invalid processor id: %d", processor_id); + return ERR_OUT_OF_RANGE_KERNEL; + } + + // TODO(yuriks): Other checks, returning 0xD9001BEA if (!Memory::IsValidVirtualAddress(entry_point)) { LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); @@ -399,8 +409,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { LOG_ERROR(Kernel_SVC, "Not enough space in region to allocate a new TLS page for thread"); - return ResultCode(ErrorDescription::OutOfMemory, ErrorModule::Kernel, - ErrorSummary::OutOfResource, ErrorLevel::Permanent); + return ERR_OUT_OF_MEMORY; } u32 offset = linheap_memory->size(); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 6ab31c70b..6a3566f15 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" enum ThreadPriority : s32 { @@ -57,7 +58,7 @@ public: * @param stack_top The address of the thread's stack top * @return A shared pointer to the newly created thread */ - static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, + static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top); std::string GetName() const override { diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index a00c75679..6f2cf3b02 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core_timing.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index b0f818933..82552372d 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 6dd24f846..cef1f7fa8 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -4,6 +4,7 @@ #include <iterator> #include "common/assert.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" #include "core/memory_setup.h" diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 9055664b2..38e0d74d0 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -13,14 +13,6 @@ namespace Kernel { -const ResultCode ERR_INVALID_ADDRESS{// 0xE0E01BF5 - ErrorDescription::InvalidAddress, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage}; - -const ResultCode ERR_INVALID_ADDRESS_STATE{// 0xE0A01BF5 - ErrorDescription::InvalidAddress, ErrorModule::OS, - ErrorSummary::InvalidState, ErrorLevel::Usage}; - enum class VMAType : u8 { /// VMA represents an unmapped region of the address space. Free, diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp new file mode 100644 index 000000000..f245eda6c --- /dev/null +++ b/src/core/hle/kernel/wait_object.cpp @@ -0,0 +1,99 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/config_mem.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/timer.h" +#include "core/hle/shared_page.h" + +namespace Kernel { + +void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + if (itr == waiting_threads.end()) + waiting_threads.push_back(std::move(thread)); +} + +void WaitObject::RemoveWaitingThread(Thread* thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + // If a thread passed multiple handles to the same object, + // the kernel might attempt to remove the thread from the object's + // waiting threads list multiple times. + if (itr != waiting_threads.end()) + waiting_threads.erase(itr); +} + +SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { + Thread* candidate = nullptr; + s32 candidate_priority = THREADPRIO_LOWEST + 1; + + for (const auto& thread : waiting_threads) { + // The list of waiting threads must not contain threads that are not waiting to be awakened. + ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || + thread->status == THREADSTATUS_WAIT_SYNCH_ALL, + "Inconsistent thread statuses in waiting_threads"); + + if (thread->current_priority >= candidate_priority) + continue; + + if (ShouldWait(thread.get())) + continue; + + // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or + // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. + bool ready_to_run = true; + if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { + ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), + [&thread](const SharedPtr<WaitObject>& object) { + return object->ShouldWait(thread.get()); + }); + } + + if (ready_to_run) { + candidate = thread.get(); + candidate_priority = thread->current_priority; + } + } + + return candidate; +} + +void WaitObject::WakeupAllWaitingThreads() { + while (auto thread = GetHighestPriorityReadyThread()) { + if (!thread->IsSleepingOnWaitAll()) { + Acquire(thread.get()); + // Set the output index of the WaitSynchronizationN call to the index of this object. + if (thread->wait_set_output) { + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); + thread->wait_set_output = false; + } + } else { + for (auto& object : thread->wait_objects) { + object->Acquire(thread.get()); + } + // Note: This case doesn't update the output index of WaitSynchronizationN. + } + + for (auto& object : thread->wait_objects) + object->RemoveWaitingThread(thread.get()); + thread->wait_objects.clear(); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->ResumeFromWait(); + } +} + +const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { + return waiting_threads; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h new file mode 100644 index 000000000..861578186 --- /dev/null +++ b/src/core/hle/kernel/wait_object.h @@ -0,0 +1,67 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include <boost/smart_ptr/intrusive_ptr.hpp> +#include "common/common_types.h" +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class Thread; + +/// Class that represents a Kernel object that a thread can be waiting on +class WaitObject : public Object { +public: + /** + * Check if the specified thread should wait until the object is available + * @param thread The thread about which we're deciding. + * @return True if the current thread should wait due to this object being unavailable + */ + virtual bool ShouldWait(Thread* thread) const = 0; + + /// Acquire/lock the object for the specified thread if it is available + virtual void Acquire(Thread* thread) = 0; + + /** + * Add a thread to wait on this object + * @param thread Pointer to thread to add + */ + virtual void AddWaitingThread(SharedPtr<Thread> thread); + + /** + * Removes a thread from waiting on this object (e.g. if it was resumed already) + * @param thread Pointer to thread to remove + */ + virtual void RemoveWaitingThread(Thread* thread); + + /** + * Wake up all threads waiting on this object that can be awoken, in priority order, + * and set the synchronization result and output of the thread. + */ + virtual void WakeupAllWaitingThreads(); + + /// Obtains the highest priority thread that is ready to run from this object's waiting list. + SharedPtr<Thread> GetHighestPriorityReadyThread(); + + /// Get a const reference to the waiting threads list for debug use + const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; + +private: + /// Threads waiting for this object to become available + std::vector<SharedPtr<Thread>> waiting_threads; +}; + +// Specialization of DynamicObjectCast for WaitObjects +template <> +inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { + if (object != nullptr && object->IsWaitable()) { + return boost::static_pointer_cast<WaitObject>(std::move(object)); + } + return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 13b948871..5f2cdbb96 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -13,38 +13,15 @@ // All the constants in this file come from http://3dbrew.org/wiki/Error_codes -/// Detailed description of the error. This listing is likely incomplete. +/** + * Detailed description of the error. Code 0 always means success. Codes 1000 and above are + * considered "well-known" and have common values between all modules. The meaning of other codes + * vary by module. + */ enum class ErrorDescription : u32 { Success = 0, - SessionClosedByRemote = 26, - WrongPermission = 46, - OS_InvalidBufferDescriptor = 48, - MaxConnectionsReached = 52, - WrongAddress = 53, - FS_RomFSNotFound = 100, - FS_ArchiveNotMounted = 101, - FS_FileNotFound = 112, - FS_PathNotFound = 113, - FS_GameCardNotInserted = 141, - FS_NotFound = 120, - FS_FileAlreadyExists = 180, - FS_DirectoryAlreadyExists = 185, - FS_AlreadyExists = 190, - FS_InvalidOpenFlags = 230, - FS_DirectoryNotEmpty = 240, - FS_NotAFile = 250, - FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive - OutofRangeOrMisalignedAddress = - 513, // TODO(purpasmart): Check if this name fits its actual usage - GPU_FirstInitialization = 519, - FS_ExeFSSectionNotFound = 567, - FS_CommandNotAllowed = 630, - FS_InvalidReadFlag = 700, - FS_InvalidPath = 702, - FS_WriteBeyondEnd = 705, - FS_UnsupportedOpenFlags = 760, - FS_IncorrectExeFSReadSize = 761, - FS_UnexpectedFileOrDirectory = 770, + + // Codes 1000 and above are considered "well-known" and have common values between all modules. InvalidSection = 1000, TooLarge = 1001, NotAuthorized = 1002, @@ -218,7 +195,7 @@ enum class ErrorLevel : u32 { union ResultCode { u32 raw; - BitField<0, 10, ErrorDescription> description; + BitField<0, 10, u32> description; BitField<10, 8, ErrorModule> module; BitField<21, 6, ErrorSummary> summary; @@ -228,45 +205,46 @@ union ResultCode { // error BitField<31, 1, u32> is_error; - explicit ResultCode(u32 raw) : raw(raw) {} - ResultCode(ErrorDescription description_, ErrorModule module_, ErrorSummary summary_, - ErrorLevel level_) - : raw(0) { - description.Assign(description_); - module.Assign(module_); - summary.Assign(summary_); - level.Assign(level_); - } + constexpr explicit ResultCode(u32 raw) : raw(raw) {} - ResultCode& operator=(const ResultCode& o) { + constexpr ResultCode(ErrorDescription description, ErrorModule module, ErrorSummary summary, + ErrorLevel level) + : ResultCode(static_cast<u32>(description), module, summary, level) {} + + constexpr ResultCode(u32 description_, ErrorModule module_, ErrorSummary summary_, + ErrorLevel level_) + : raw(description.FormatValue(description_) | module.FormatValue(module_) | + summary.FormatValue(summary_) | level.FormatValue(level_)) {} + + constexpr ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; } - bool IsSuccess() const { - return is_error == 0; + constexpr bool IsSuccess() const { + return is_error.ExtractValue(raw) == 0; } - bool IsError() const { - return is_error == 1; + constexpr bool IsError() const { + return is_error.ExtractValue(raw) == 1; } }; -inline bool operator==(const ResultCode& a, const ResultCode& b) { +constexpr bool operator==(const ResultCode& a, const ResultCode& b) { return a.raw == b.raw; } -inline bool operator!=(const ResultCode& a, const ResultCode& b) { +constexpr bool operator!=(const ResultCode& a, const ResultCode& b) { return a.raw != b.raw; } // Convenience functions for creating some common kinds of errors: /// The default success `ResultCode`. -const ResultCode RESULT_SUCCESS(0); +constexpr ResultCode RESULT_SUCCESS(0); /// Might be returned instead of a dummy success for unimplemented APIs. -inline ResultCode UnimplementedFunction(ErrorModule module) { +constexpr ResultCode UnimplementedFunction(ErrorModule module) { return ResultCode(ErrorDescription::NotImplemented, module, ErrorSummary::NotSupported, ErrorLevel::Permanent); } @@ -438,6 +416,16 @@ ResultVal<T> MakeResult(Args&&... args) { } /** + * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just + * copy or move constructing. + */ +template <typename Arg> +ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { + return ResultVal<std::remove_reference_t<Arg>>::WithCode(RESULT_SUCCESS, + std::forward<Arg>(arg)); +} + +/** * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps * the contained value and assigns it to `target`, which can be either an l-value expression or a * variable declaration. If it fails the return code is returned from the current function. Thus it @@ -448,3 +436,12 @@ ResultVal<T> MakeResult(Args&&... args) { if (CONCAT2(check_result_L, __LINE__).Failed()) \ return CONCAT2(check_result_L, __LINE__).Code(); \ target = std::move(*CONCAT2(check_result_L, __LINE__)) + +/** + * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if + * non-success, or discarded otherwise. + */ +#define CASCADE_CODE(source) \ + auto CONCAT2(check_result_L, __LINE__) = source; \ + if (CONCAT2(check_result_L, __LINE__).IsError()) \ + return CONCAT2(check_result_L, __LINE__); diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index aa270a2c3..e3dd23949 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -4,11 +4,16 @@ #include <array> +#include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/result.h" #include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_i.h" #include "core/hle/service/ac/ac_u.h" +#include "core/memory.h" namespace Service { namespace AC { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d344a622f..961305e9f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,8 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <cinttypes> +#include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/am_app.h" #include "core/hle/service/am/am_net.h" @@ -176,8 +180,6 @@ void GetTicketList(Service::Interface* self) { } void Init() { - using namespace Kernel; - AddService(new AM_APP_Interface); AddService(new AM_NET_Interface); AddService(new AM_SYS_Interface); diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 366d1eacf..4c587e3c8 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -5,6 +5,7 @@ #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/applets/applet.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" @@ -74,6 +75,7 @@ void GetSharedFont(Service::Interface* self) { LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); rb.Push<u32>(-1); // TODO: Find the right error code rb.Skip(1 + 2, true); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); return; } diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index e63b61450..ee80926d2 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -4,6 +4,8 @@ #pragma once +#include <vector> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/kernel/kernel.h" diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index e0de037f8..2bba3aff6 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp @@ -3,6 +3,9 @@ // Refer to the license.txt file included. #include <cinttypes> +#include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/result.h" #include "core/hle/service/boss/boss.h" #include "core/hle/service/boss/boss_p.h" #include "core/hle/service/boss/boss_u.h" @@ -24,9 +27,7 @@ void InitializeSession(Service::Interface* self) { if (translation != IPC::CallingPidDesc()) { cmd_buff[0] = IPC::MakeHeader(0, 0x1, 0); // 0x40 - cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent) - .raw; + cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; LOG_ERROR(Service_BOSS, "The translation was invalid, translation=0x%08X", translation); return; } diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 95665e754..7394c844f 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -11,13 +11,17 @@ #include "common/logging/log.h" #include "core/core_timing.h" #include "core/frontend/camera/factory.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" +#include "core/hle/result.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_c.h" #include "core/hle/service/cam/cam_q.h" #include "core/hle/service/cam/cam_s.h" #include "core/hle/service/cam/cam_u.h" #include "core/hle/service/service.h" +#include "core/memory.h" #include "core/settings.h" namespace Service { @@ -55,7 +59,7 @@ struct PortConfig { u16 x1; // x-coordinate of ending position for trimming u16 y1; // y-coordinate of ending position for trimming - u32 transfer_bytes; + u16 transfer_bytes; Kernel::SharedPtr<Kernel::Event> completion_event; Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event; @@ -225,8 +229,7 @@ static void ActivatePort(int port_id, int camera_id) { template <int max_index> class CommandParamBitSet : public BitSet8 { public: - explicit CommandParamBitSet(u32 command_param) - : BitSet8(static_cast<u8>(command_param & 0xFF)) {} + explicit CommandParamBitSet(u8 command_param) : BitSet8(command_param) {} bool IsValid() const { return m_val < (1 << max_index); @@ -244,9 +247,10 @@ using CameraSet = CommandParamBitSet<3>; } // namespace void StartCapture(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - const PortSet port_select(cmd_buff[1]); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { @@ -267,21 +271,20 @@ void StartCapture(Service::Interface* self) { LOG_WARNING(Service_CAM, "port %u already started", i); } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void StopCapture(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - const PortSet port_select(cmd_buff[1]); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { @@ -293,21 +296,20 @@ void StopCapture(Service::Interface* self) { LOG_WARNING(Service_CAM, "port %u already stopped", i); } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void IsBusy(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - const PortSet port_select(cmd_buff[1]); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsValid()) { bool is_busy = true; @@ -315,80 +317,74 @@ void IsBusy(Service::Interface* self) { for (int i : port_select) { is_busy &= ports[i].is_busy; } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = is_busy ? 1 : 0; + rb.Push(RESULT_SUCCESS); + rb.Push(is_busy); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void ClearBuffer(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x04, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void GetVsyncInterruptEvent(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles( + Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[2] = 0; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.PushCopyHandles(0); } - cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2); - LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void GetBufferErrorInterruptEvent(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = - Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles( + Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[2] = 0; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.PushCopyHandles(0); } LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void SetReceiving(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const VAddr dest = cmd_buff[1]; - const PortSet port_select(cmd_buff[2]); - const u32 image_size = cmd_buff[3]; - const u32 trans_unit = cmd_buff[4] & 0xFFFF; - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 4, 2); + const VAddr dest = rp.Pop<u32>(); + const PortSet port_select(rp.Pop<u8>()); + const u32 image_size = rp.Pop<u32>(); + const u16 trans_unit = rp.Pop<u16>(); + rp.PopHandle(); // Handle to destination process. not used + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); if (port_select.IsSingle()) { int port_id = *port_select.begin(); PortConfig& port = ports[port_id]; @@ -403,149 +399,145 @@ void SetReceiving(Service::Interface* self) { port.is_pending_receiving = true; } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(port.completion_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.PushCopyHandles(0); } - cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); - LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest, port_select.m_val, image_size, trans_unit); } void IsFinishedReceiving(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = (ports[port].is_receiving || ports[port].is_pending_receiving) ? 0 : 1; + bool is_busy = ports[port].is_receiving || ports[port].is_pending_receiving; + rb.Push(RESULT_SUCCESS); + rb.Push(!is_busy); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTransferLines(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u32 transfer_lines = cmd_buff[2] & 0xFFFF; - const u32 width = cmd_buff[3] & 0xFFFF; - const u32 height = cmd_buff[4] & 0xFFFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x09, 4, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 transfer_lines = rp.Pop<u16>(); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].transfer_bytes = transfer_lines * width * 2; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); - LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u", port_select.m_val, transfer_lines, width, height); } void GetMaxLines(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0A, 2, 0); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); - const u32 width = cmd_buff[1] & 0xFFFF; - const u32 height = cmd_buff[2] & 0xFFFF; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 constexpr u32 MIN_TRANSFER_UNIT = 256; constexpr u32 MAX_BUFFER_SIZE = 2560; if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { - cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + rb.Push(ERROR_OUT_OF_RANGE); + rb.Skip(1, false); } else { u32 lines = MAX_BUFFER_SIZE / width; if (lines > height) { lines = height; } - cmd_buff[1] = RESULT_SUCCESS.raw; + ResultCode result = RESULT_SUCCESS; while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) { --lines; if (lines == 0) { - cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + result = ERROR_OUT_OF_RANGE; break; } } - cmd_buff[2] = lines; + rb.Push(result); + rb.Push(lines); } - cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); - LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); } void SetTransferBytes(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u32 transfer_bytes = cmd_buff[2] & 0xFFFF; - const u32 width = cmd_buff[3] & 0xFFFF; - const u32 height = cmd_buff[4] & 0xFFFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0B, 4, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 transfer_bytes = rp.Pop<u16>(); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].transfer_bytes = transfer_bytes; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); - LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u", port_select.m_val, transfer_bytes, width, height); } void GetTransferBytes(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0C, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = ports[port].transfer_bytes; + rb.Push(RESULT_SUCCESS); + rb.Push(ports[port].transfer_bytes); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); - LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val); } void GetMaxBytes(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 2, 0); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); - const u32 width = cmd_buff[1] & 0xFFFF; - const u32 height = cmd_buff[2] & 0xFFFF; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 constexpr u32 MIN_TRANSFER_UNIT = 256; constexpr u32 MAX_BUFFER_SIZE = 2560; if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { - cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + rb.Push(ERROR_OUT_OF_RANGE); + rb.Skip(1, false); } else { u32 bytes = MAX_BUFFER_SIZE; @@ -553,63 +545,59 @@ void GetMaxBytes(Service::Interface* self) { bytes -= MIN_TRANSFER_UNIT; } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = bytes; + rb.Push(RESULT_SUCCESS); + rb.Push(bytes); } - cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0); LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); } void SetTrimming(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const bool trim = (cmd_buff[2] & 0xFF) != 0; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0E, 2, 0); + const PortSet port_select(rp.Pop<u8>()); + const bool trim = rp.Pop<bool>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].is_trimming = trim; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim); } void IsTrimming(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = ports[port].is_trimming; + rb.Push(RESULT_SUCCESS); + rb.Push(ports[port].is_trimming); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTrimmingParams(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u16 x0 = static_cast<u16>(cmd_buff[2] & 0xFFFF); - const u16 y0 = static_cast<u16>(cmd_buff[3] & 0xFFFF); - const u16 x1 = static_cast<u16>(cmd_buff[4] & 0xFFFF); - const u16 y1 = static_cast<u16>(cmd_buff[5] & 0xFFFF); - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x10, 5, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 x0 = rp.Pop<u16>(); + const u16 y0 = rp.Pop<u16>(); + const u16 x1 = rp.Pop<u16>(); + const u16 y1 = rp.Pop<u16>(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].x0 = x0; @@ -617,49 +605,46 @@ void SetTrimmingParams(Service::Interface* self) { ports[i].x1 = x1; ports[i].y1 = y1; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val, x0, y0, x1, y1); } void GetTrimmingParams(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x11, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = ports[port].x0; - cmd_buff[3] = ports[port].y0; - cmd_buff[4] = ports[port].x1; - cmd_buff[5] = ports[port].y1; + rb.Push(RESULT_SUCCESS); + rb.Push(ports[port].x0); + rb.Push(ports[port].y0); + rb.Push(ports[port].x1); + rb.Push(ports[port].y1); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(4, false); } - cmd_buff[0] = IPC::MakeHeader(0x11, 5, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTrimmingParamsCenter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u16 trim_w = static_cast<u16>(cmd_buff[2] & 0xFFFF); - const u16 trim_h = static_cast<u16>(cmd_buff[3] & 0xFFFF); - const u16 cam_w = static_cast<u16>(cmd_buff[4] & 0xFFFF); - const u16 cam_h = static_cast<u16>(cmd_buff[5] & 0xFFFF); - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 5, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 trim_w = rp.Pop<u16>(); + const u16 trim_h = rp.Pop<u16>(); + const u16 cam_w = rp.Pop<u16>(); + const u16 cam_h = rp.Pop<u16>(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].x0 = (cam_w - trim_w) / 2; @@ -667,23 +652,21 @@ void SetTrimmingParamsCenter(Service::Interface* self) { ports[i].x1 = ports[i].x0 + trim_w; ports[i].y1 = ports[i].y0 + trim_h; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u", port_select.m_val, trim_w, trim_h, cam_w, cam_h); } void Activate(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x13, 1, 0); + const CameraSet camera_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid()) { if (camera_select.m_val == 0) { // deactive all for (int i = 0; i < 2; ++i) { @@ -694,10 +677,10 @@ void Activate(Service::Interface* self) { } ports[i].is_active = false; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else if (camera_select[0] && camera_select[1]) { LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated"); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } else { if (camera_select[0]) { ActivatePort(0, 0); @@ -708,24 +691,22 @@ void Activate(Service::Interface* self) { if (camera_select[2]) { ActivatePort(1, 2); } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val); } void SwitchContext(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const ContextSet context_select(cmd_buff[2]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 2, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsSingle()) { int context = *context_select.begin(); for (int camera : camera_select) { @@ -736,26 +717,24 @@ void SwitchContext(Service::Interface* self) { cameras[camera].impl->SetFormat(context_config.format); cameras[camera].impl->SetResolution(context_config.resolution); } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); } void FlipImage(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const Flip flip = static_cast<Flip>(cmd_buff[2] & 0xFF); - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const Flip flip = static_cast<Flip>(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -765,32 +744,30 @@ void FlipImage(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u", camera_select.m_val, static_cast<int>(flip), context_select.m_val); } void SetDetailSize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 8, 0); + const CameraSet camera_select(rp.Pop<u8>()); Resolution resolution; - resolution.width = static_cast<u16>(cmd_buff[2] & 0xFFFF); - resolution.height = static_cast<u16>(cmd_buff[3] & 0xFFFF); - resolution.crop_x0 = static_cast<u16>(cmd_buff[4] & 0xFFFF); - resolution.crop_y0 = static_cast<u16>(cmd_buff[5] & 0xFFFF); - resolution.crop_x1 = static_cast<u16>(cmd_buff[6] & 0xFFFF); - resolution.crop_y1 = static_cast<u16>(cmd_buff[7] & 0xFFFF); - const ContextSet context_select(cmd_buff[8]); - + resolution.width = rp.Pop<u16>(); + resolution.height = rp.Pop<u16>(); + resolution.crop_x0 = rp.Pop<u16>(); + resolution.crop_y0 = rp.Pop<u16>(); + resolution.crop_x1 = rp.Pop<u16>(); + resolution.crop_y1 = rp.Pop<u16>(); + const ContextSet context_select(rp.Pop<u8>()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -800,15 +777,13 @@ void SetDetailSize(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, " "crop_x1=%u, crop_y1=%u, context_select=%u", camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0, @@ -816,12 +791,12 @@ void SetDetailSize(Service::Interface* self) { } void SetSize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const u32 size = cmd_buff[2] & 0xFF; - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const u8 size = rp.Pop<u8>(); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -831,49 +806,45 @@ void SetSize(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u", camera_select.m_val, size, context_select.m_val); } void SetFrameRate(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const FrameRate frame_rate = static_cast<FrameRate>(cmd_buff[2] & 0xFF); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x20, 2, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const FrameRate frame_rate = static_cast<FrameRate>(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid()) { for (int camera : camera_select) { cameras[camera].frame_rate = frame_rate; // TODO(wwylele): consider hinting the actual camera with the expected frame rate } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); - LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d", camera_select.m_val, static_cast<int>(frame_rate)); } void SetEffect(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const Effect effect = static_cast<Effect>(cmd_buff[2] & 0xFF); - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x22, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const Effect effect = static_cast<Effect>(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -883,26 +854,24 @@ void SetEffect(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u", camera_select.m_val, static_cast<int>(effect), context_select.m_val); } void SetOutputFormat(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const OutputFormat format = static_cast<OutputFormat>(cmd_buff[2] & 0xFF); - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x25, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const OutputFormat format = static_cast<OutputFormat>(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -912,34 +881,32 @@ void SetOutputFormat(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x25, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u", camera_select.m_val, static_cast<int>(format), context_select.m_val); } void SynchronizeVsyncTiming(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x29, 2, 0); + const u8 camera_select1 = rp.Pop<u8>(); + const u8 camera_select2 = rp.Pop<u8>(); - const u32 camera_select1 = cmd_buff[1] & 0xFF; - const u32 camera_select2 = cmd_buff[2] & 0xFF; - - cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u", camera_select1, camera_select2); } void GetStereoCameraCalibrationData(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestBuilder rb = + IPC::RequestParser(Kernel::GetCommandBuffer(), 0x2B, 0, 0).MakeBuilder(17, 0); // Default values taken from yuriks' 3DS. Valid data is required here or games using the // calibration get stuck in an infinite CPU loop. @@ -958,34 +925,28 @@ void GetStereoCameraCalibrationData(Service::Interface* self) { data.imageWidth = 640; data.imageHeight = 480; - cmd_buff[0] = IPC::MakeHeader(0x2B, 17, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - memcpy(&cmd_buff[2], &data, sizeof(data)); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(data); LOG_TRACE(Service_CAM, "called"); } void SetPackageParameterWithoutContext(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x33, 11, 0); PackageParameterWithoutContext package; - std::memcpy(&package, cmd_buff + 1, sizeof(package)); + rp.PopRaw(package); - cmd_buff[0] = IPC::MakeHeader(0x33, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called"); } -template <typename PackageParameterType, int command_id> -static void SetPackageParameter() { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - PackageParameterType package; - std::memcpy(&package, cmd_buff + 1, sizeof(package)); - - const CameraSet camera_select(static_cast<u32>(package.camera_select)); - const ContextSet context_select(static_cast<u32>(package.context_select)); +template <typename PackageParameterType> +static ResultCode SetPackageParameter(const PackageParameterType& package) { + const CameraSet camera_select(package.camera_select); + const ContextSet context_select(package.context_select); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera_id : camera_select) { @@ -1002,53 +963,66 @@ static void SetPackageParameter() { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + return RESULT_SUCCESS; } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select, package.context_select); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + return ERROR_INVALID_ENUM_VALUE; } - - cmd_buff[0] = IPC::MakeHeader(command_id, 1, 0); - - LOG_DEBUG(Service_CAM, "called"); } -Resolution PackageParameterWithContext::GetResolution() { +Resolution PackageParameterWithContext::GetResolution() const { return PRESET_RESOLUTION[static_cast<int>(size)]; } void SetPackageParameterWithContext(Service::Interface* self) { - SetPackageParameter<PackageParameterWithContext, 0x34>(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x34, 5, 0); + + PackageParameterWithContext package; + rp.PopRaw(package); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + ResultCode result = SetPackageParameter(package); + rb.Push(result); + + LOG_DEBUG(Service_CAM, "called"); } void SetPackageParameterWithContextDetail(Service::Interface* self) { - SetPackageParameter<PackageParameterWithContextDetail, 0x35>(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x35, 7, 0); + + PackageParameterWithContextDetail package; + rp.PopRaw(package); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + ResultCode result = SetPackageParameter(package); + rb.Push(result); + + LOG_DEBUG(Service_CAM, "called"); } void GetSuitableY2rStandardCoefficient(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[0] = IPC::MakeHeader(0x36, 2, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x36, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); LOG_WARNING(Service_CAM, "(STUBBED) called"); } void PlayShutterSound(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u8 sound_id = cmd_buff[1] & 0xFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x38, 1, 0); + u8 sound_id = rp.Pop<u8>(); - cmd_buff[0] = IPC::MakeHeader(0x38, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id); } void DriverInitialize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x39, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); for (int camera_id = 0; camera_id < NumCameras; ++camera_id) { CameraConfig& camera = cameras[camera_id]; @@ -1074,14 +1048,14 @@ void DriverInitialize(Service::Interface* self) { port.Clear(); } - cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_CAM, "called"); } void DriverFinalize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3A, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); CancelReceiving(0); CancelReceiving(1); @@ -1090,8 +1064,7 @@ void DriverFinalize(Service::Interface* self) { camera.impl = nullptr; } - cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_CAM, "called"); } diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index 34a9c8479..b6da721d8 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -184,9 +184,10 @@ struct PackageParameterWithoutContext { s16 auto_white_balance_window_y; s16 auto_white_balance_window_width; s16 auto_white_balance_window_height; + INSERT_PADDING_WORDS(4); }; -static_assert(sizeof(PackageParameterWithoutContext) == 28, +static_assert(sizeof(PackageParameterWithoutContext) == 44, "PackageParameterCameraWithoutContext structure size is wrong"); struct PackageParameterWithContext { @@ -196,11 +197,12 @@ struct PackageParameterWithContext { Effect effect; Size size; INSERT_PADDING_BYTES(3); + INSERT_PADDING_WORDS(3); - Resolution GetResolution(); + Resolution GetResolution() const; }; -static_assert(sizeof(PackageParameterWithContext) == 8, +static_assert(sizeof(PackageParameterWithContext) == 20, "PackageParameterWithContext structure size is wrong"); struct PackageParameterWithContextDetail { @@ -209,13 +211,14 @@ struct PackageParameterWithContextDetail { Flip flip; Effect effect; Resolution resolution; + INSERT_PADDING_WORDS(3); - Resolution GetResolution() { + Resolution GetResolution() const { return resolution; } }; -static_assert(sizeof(PackageParameterWithContextDetail) == 16, +static_assert(sizeof(PackageParameterWithContextDetail) == 28, "PackageParameterWithContextDetail structure size is wrong"); /** diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index eb04273db..bd9814244 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -3,7 +3,10 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/result.h" #include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_ndm.h" #include "core/hle/service/cecd/cecd_s.h" diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 8c8c1ec77..5a7878b31 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -11,7 +11,10 @@ #include "common/string_util.h" #include "common/swap.h" #include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" @@ -20,6 +23,7 @@ #include "core/hle/service/cfg/cfg_u.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/service.h" +#include "core/memory.h" #include "core/settings.h" namespace Service { @@ -411,7 +415,7 @@ ResultCode UpdateConfigNANDSavegame() { ResultCode FormatConfig() { ResultCode res = DeleteConfigNANDSaveFile(); // The delete command fails if the file doesn't exist, so we have to check that too - if (!res.IsSuccess() && res.description != ErrorDescription::FS_FileNotFound) { + if (!res.IsSuccess() && res != FileSys::ERROR_FILE_NOT_FOUND) { return res; } // Delete the old data @@ -534,7 +538,7 @@ ResultCode LoadConfigNANDSaveFile() { Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); // If the archive didn't exist, create the files inside - if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { + if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) { // Format the archive to create the directories Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, FileSys::ArchiveFormatInfo(), archive_path); diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 6cf62f9bc..1455f20ca 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -4,9 +4,12 @@ #include <cstring> #include "common/alignment.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/csnd_snd.h" +#include "core/memory.h" namespace Service { namespace CSND { diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 25c07f401..32cfa2c44 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -4,6 +4,7 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/result.h" #include "core/hle/service/dlp/dlp_srvr.h" diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 39711ea97..363066d14 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -3,12 +3,18 @@ // Refer to the license.txt file included. #include <algorithm> +#include <array> #include <cinttypes> #include "audio_core/hle/pipe.h" +#include "common/assert.h" #include "common/hash.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/result.h" #include "core/hle/service/dsp_dsp.h" +#include "core/memory.h" using DspPipe = DSP::HLE::DspPipe; @@ -289,9 +295,7 @@ static void WriteProcessPipe(Service::Interface* self) { "size=0x%X, buffer=0x%08X", cmd_buff[3], pipe_index, size, buffer); cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent) - .raw; + cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; return; } diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 9da55f328..7c8f4339f 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -6,10 +6,11 @@ #include <chrono> #include <iomanip> #include <sstream> - #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/ipc.h" #include "core/hle/result.h" #include "core/hle/service/err_f.h" @@ -172,6 +173,7 @@ static void ThrowFatalError(Interface* self) { const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); LOG_CRITICAL(Service_ERR, "Fatal error type: %s", GetErrType(errinfo->errinfo_common.specifier).c_str()); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorUnknown); // Generic Info LogGenericInfo(errinfo->errinfo_common); diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index 34fdf7f53..76ecda8b7 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -2,11 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" +#include "common/logging/log.h" #include "common/string_util.h" +#include "core/hle/ipc.h" +#include "core/hle/result.h" #include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_a.h" #include "core/hle/service/frd/frd_u.h" #include "core/hle/service/service.h" +#include "core/memory.h" namespace Service { namespace FRD { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 6cddc1fdb..3605ef175 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -22,8 +22,13 @@ #include "core/file_sys/archive_sdmcwriteonly.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/directory_backend.h" +#include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -50,16 +55,6 @@ static constexpr Kernel::Handle INVALID_HANDLE{}; namespace Service { namespace FS { -// TODO: Verify code -/// Returned when a function is passed an invalid handle. -const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - -/// Returned when a function is passed an invalid archive handle. -const ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS, - ErrorSummary::NotFound, - ErrorLevel::Status); // 0xC8804465 - // Command to access archive file enum class FileCommand : u32 { Dummy1 = 0x000100C6, @@ -92,6 +87,10 @@ File::File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& File::~File() {} void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { + using Kernel::ClientSession; + using Kernel::ServerSession; + using Kernel::SharedPtr; + u32* cmd_buff = Kernel::GetCommandBuffer(); FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); switch (cmd) { @@ -170,10 +169,9 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses case FileCommand::OpenLinkFile: { LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); - auto sessions = Kernel::ServerSession::CreateSessionPair(GetName(), shared_from_this()); - ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + auto sessions = ServerSession::CreateSessionPair(GetName()); + ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + cmd_buff[3] = Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)) .ValueOr(INVALID_HANDLE); break; } @@ -267,9 +265,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi auto itr = id_code_map.find(id_code); if (itr == id_code_map.end()) { - // TODO: Verify error against hardware - return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Permanent); + return FileSys::ERROR_NOT_FOUND; } CASCADE_RESULT(std::unique_ptr<ArchiveBackend> res, itr->second->Open(archive_path)); @@ -284,7 +280,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi ResultCode CloseArchive(ArchiveHandle handle) { if (handle_map.erase(handle) == 0) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; else return RESULT_SUCCESS; } @@ -309,7 +305,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl const FileSys::Mode mode) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; auto backend = archive->OpenFile(path, mode); if (backend.Failed()) @@ -322,7 +318,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; return archive->DeleteFile(path); } @@ -334,7 +330,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { return src_archive->RenameFile(src_path, dest_path); @@ -347,7 +343,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; return archive->DeleteDirectory(path); } @@ -356,7 +352,7 @@ ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; return archive->DeleteDirectoryRecursively(path); } @@ -365,7 +361,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path u64 file_size) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; return archive->CreateFile(path, file_size); } @@ -373,7 +369,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; return archive->CreateDirectory(path); } @@ -385,7 +381,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, ArchiveBackend* src_archive = GetArchive(src_archive_handle); ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { return src_archive->RenameDirectory(src_path, dest_path); @@ -399,7 +395,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc const FileSys::Path& path) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; auto backend = archive->OpenDirectory(path); if (backend.Failed()) @@ -412,7 +408,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return ERR_INVALID_ARCHIVE_HANDLE; + return FileSys::ERR_INVALID_ARCHIVE_HANDLE; return MakeResult<u64>(archive->GetFreeBytes()); } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 2ea956e0b..3a3371c88 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -8,7 +8,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/archive_backend.h" -#include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/result.h" namespace FileSys { @@ -43,7 +43,7 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 }; typedef u64 ArchiveHandle; -class File final : public SessionRequestHandler, public std::enable_shared_from_this<File> { +class File final : public Kernel::SessionRequestHandler { public: File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path); ~File(); @@ -60,7 +60,7 @@ protected: void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; }; -class Directory final : public SessionRequestHandler { +class Directory final : public Kernel::SessionRequestHandler { public: Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path); ~Directory(); diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 33b290699..34e1783ec 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -8,7 +8,13 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/errors.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -17,8 +23,9 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace FS_User -using Kernel::SharedPtr; +using Kernel::ClientSession; using Kernel::ServerSession; +using Kernel::SharedPtr; namespace Service { namespace FS { @@ -76,11 +83,11 @@ static void OpenFile(Service::Interface* self) { rb.Push(file_res.Code()); if (file_res.Succeeded()) { std::shared_ptr<File> file = *file_res; - auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); - file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - rb.PushMoveHandles(Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom()); + auto sessions = ServerSession::CreateSessionPair(file->GetName()); + file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + + rb.PushMoveHandles( + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom()); } else { rb.PushMoveHandles(0); LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); @@ -129,7 +136,7 @@ static void OpenFileDirectly(Service::Interface* self) { ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); if (archive_handle.Failed()) { LOG_ERROR(Service_FS, - "failed to get a handle for archive archive_id=0x%08X archive_path=%s", + "Failed to get a handle for archive archive_id=0x%08X archive_path=%s", static_cast<u32>(archive_id), archive_path.DebugStr().c_str()); cmd_buff[1] = archive_handle.Code().raw; cmd_buff[3] = 0; @@ -142,11 +149,11 @@ static void OpenFileDirectly(Service::Interface* self) { cmd_buff[1] = file_res.Code().raw; if (file_res.Succeeded()) { std::shared_ptr<File> file = *file_res; - auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); - file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom(); + auto sessions = ServerSession::CreateSessionPair(file->GetName()); + file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + + cmd_buff[3] = + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", @@ -409,11 +416,11 @@ static void OpenDirectory(Service::Interface* self) { cmd_buff[1] = dir_res.Code().raw; if (dir_res.Succeeded()) { std::shared_ptr<Directory> directory = *dir_res; - auto sessions = ServerSession::CreateSessionPair(directory->GetName(), directory); - directory->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom(); + auto sessions = ServerSession::CreateSessionPair(directory->GetName()); + directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + + cmd_buff[3] = + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); } else { LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); @@ -539,9 +546,7 @@ static void FormatSaveData(Service::Interface* self) { if (archive_id != FS::ArchiveIdCode::SaveData) { LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", static_cast<u32>(archive_id)); - cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage) - .raw; + cmd_buff[1] = FileSys::ERROR_INVALID_PATH.raw; return; } @@ -802,9 +807,7 @@ static void InitializeWithSdkVersion(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; } else { LOG_ERROR(Service_FS, "ProcessId Header must be 0x20"); - cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent) - .raw; + cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; } } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index a960778a7..6ff0f4812 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -5,14 +5,16 @@ #include "common/bit_field.h" #include "common/microprofile.h" #include "core/core.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" +#include "core/hle/service/gsp_gpu.h" #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" #include "core/memory.h" -#include "gsp_gpu.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/gpu_debugger.h" @@ -25,13 +27,24 @@ namespace GSP { // Beginning address of HW regs const u32 REGS_BEGIN = 0x1EB00000; -const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED( - ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, ErrorSummary::InvalidArgument, - ErrorLevel::Usage); // 0xE0E02A01 -const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, +namespace ErrCodes { +enum { + // TODO(purpasmart): Check if this name fits its actual usage + OutofRangeOrMisalignedAddress = 513, + FirstInitialization = 519, +}; +} + +constexpr ResultCode RESULT_FIRST_INITIALIZATION(ErrCodes::FirstInitialization, ErrorModule::GX, + ErrorSummary::Success, ErrorLevel::Success); +constexpr ResultCode ERR_REGS_OUTOFRANGE_OR_MISALIGNED(ErrCodes::OutofRangeOrMisalignedAddress, + ErrorModule::GX, + ErrorSummary::InvalidArgument, + ErrorLevel::Usage); // 0xE0E02A01 +constexpr ResultCode ERR_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2 -const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, +constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC @@ -93,11 +106,11 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", base_address, size_in_bytes); - return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; + return ERR_REGS_OUTOFRANGE_OR_MISALIGNED; } else if (size_in_bytes <= max_size_in_bytes) { if (size_in_bytes & 3) { LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); - return ERR_GSP_REGS_MISALIGNED; + return ERR_REGS_MISALIGNED; } else { while (size_in_bytes > 0) { WriteSingleHWReg(base_address, Memory::Read32(data_vaddr)); @@ -111,7 +124,7 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va } else { LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); - return ERR_GSP_REGS_INVALID_SIZE; + return ERR_REGS_INVALID_SIZE; } } @@ -134,11 +147,11 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", base_address, size_in_bytes); - return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; + return ERR_REGS_OUTOFRANGE_OR_MISALIGNED; } else if (size_in_bytes <= max_size_in_bytes) { if (size_in_bytes & 3) { LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); - return ERR_GSP_REGS_MISALIGNED; + return ERR_REGS_MISALIGNED; } else { while (size_in_bytes > 0) { const u32 reg_address = base_address + REGS_BEGIN; @@ -164,7 +177,7 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr } else { LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); - return ERR_GSP_REGS_INVALID_SIZE; + return ERR_REGS_INVALID_SIZE; } } @@ -372,9 +385,7 @@ static void RegisterInterruptRelayQueue(Interface* self) { if (first_initialization) { // This specific code is required for a successful initialization, rather than 0 first_initialization = false; - cmd_buff[1] = ResultCode(ErrorDescription::GPU_FirstInitialization, ErrorModule::GX, - ErrorSummary::Success, ErrorLevel::Success) - .raw; + cmd_buff[1] = RESULT_FIRST_INITIALIZATION.raw; } else { cmd_buff[1] = RESULT_SUCCESS.raw; } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 64d01cdd7..5255f6dc8 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -10,7 +10,9 @@ #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_spvr.h" diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 53807cd91..0de698003 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -6,6 +6,7 @@ #include "common/bit_field.h" #include "core/core_timing.h" #include "core/frontend/input.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index 226af0083..fdecdce64 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -7,6 +7,7 @@ #include <boost/optional.hpp> #include "common/string_util.h" #include "common/swap.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/extra_hid.h" @@ -267,8 +268,7 @@ static void InitializeIrNopShared(Interface* self) { shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); if (!shared_memory) { LOG_CRITICAL(Service_IR, "invalid shared memory handle 0x%08X", handle); - rb.Push(ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent)); + rb.Push(IPC::ERR_INVALID_HANDLE); return; } shared_memory->name = "IR_USER: shared memory"; diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index d1e6d869f..7255ea026 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/ldr_ro/cro_helper.h" diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index e98388560..35212b59b 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -3,7 +3,9 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/mic_u.h" diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp index 5eb97f0d3..096c0cdac 100644 --- a/src/core/hle/service/ndm/ndm.cpp +++ b/src/core/hle/service/ndm/ndm.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/service/ndm/ndm.h" #include "core/hle/service/ndm/ndm_u.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index fd3c7d9c2..b44a9f668 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc_m.h" #include "core/hle/service/nfc/nfc_u.h" diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 63c334cb2..d5624fe54 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -4,6 +4,7 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_aoc.h" #include "core/hle/service/nim/nim_s.h" diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 581816e81..6c4600f25 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core_timing.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" @@ -422,6 +423,102 @@ static void SetApplicationData(Interface* self) { rb.Push(RESULT_SUCCESS); } +/** + * NWM_UDS::DecryptBeaconData service function. + * Decrypts the encrypted data tags contained in the 802.11 beacons. + * Inputs: + * 1 : Input network struct buffer descriptor. + * 2 : Input network struct buffer ptr. + * 3 : Input tag0 encrypted buffer descriptor. + * 4 : Input tag0 encrypted buffer ptr. + * 5 : Input tag1 encrypted buffer descriptor. + * 6 : Input tag1 encrypted buffer ptr. + * 64 : Output buffer descriptor. + * 65 : Output buffer ptr. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void DecryptBeaconData(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 0, 6); + + size_t desc_size; + const VAddr network_struct_addr = rp.PopStaticBuffer(&desc_size); + ASSERT(desc_size == sizeof(NetworkInfo)); + + size_t data0_size; + const VAddr encrypted_data0_addr = rp.PopStaticBuffer(&data0_size); + + size_t data1_size; + const VAddr encrypted_data1_addr = rp.PopStaticBuffer(&data1_size); + + size_t output_buffer_size; + const VAddr output_buffer_addr = rp.PeekStaticBuffer(0, &output_buffer_size); + + // This size is hardcoded in the 3DS UDS code. + ASSERT(output_buffer_size == sizeof(NodeInfo) * UDSMaxNodes); + + LOG_WARNING(Service_NWM, "called in0=%08X in1=%08X out=%08X", encrypted_data0_addr, + encrypted_data1_addr, output_buffer_addr); + + NetworkInfo net_info; + Memory::ReadBlock(network_struct_addr, &net_info, sizeof(net_info)); + + // Read the encrypted data. + // The first 4 bytes should be the OUI and the OUI Type of the tags. + std::array<u8, 3> oui; + Memory::ReadBlock(encrypted_data0_addr, oui.data(), oui.size()); + ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI"); + Memory::ReadBlock(encrypted_data1_addr, oui.data(), oui.size()); + ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI"); + + ASSERT_MSG(Memory::Read8(encrypted_data0_addr + 3) == + static_cast<u8>(NintendoTagId::EncryptedData0), + "Unexpected tag id"); + ASSERT_MSG(Memory::Read8(encrypted_data1_addr + 3) == + static_cast<u8>(NintendoTagId::EncryptedData1), + "Unexpected tag id"); + + std::vector<u8> beacon_data(data0_size + data1_size); + Memory::ReadBlock(encrypted_data0_addr + 4, beacon_data.data(), data0_size); + Memory::ReadBlock(encrypted_data1_addr + 4, beacon_data.data() + data0_size, data1_size); + + // Decrypt the data + DecryptBeaconData(net_info, beacon_data); + + // The beacon data header contains the MD5 hash of the data. + BeaconData beacon_header; + std::memcpy(&beacon_header, beacon_data.data(), sizeof(beacon_header)); + + // TODO(Subv): Verify the MD5 hash of the data and return 0xE1211005 if invalid. + + u8 num_nodes = net_info.max_nodes; + + std::vector<NodeInfo> nodes; + + for (int i = 0; i < num_nodes; ++i) { + BeaconNodeInfo info; + std::memcpy(&info, beacon_data.data() + sizeof(beacon_header) + i * sizeof(info), + sizeof(info)); + + // Deserialize the node information. + NodeInfo node{}; + node.friend_code_seed = info.friend_code_seed; + node.network_node_id = info.network_node_id; + for (int i = 0; i < info.username.size(); ++i) + node.username[i] = info.username[i]; + + nodes.push_back(node); + } + + Memory::ZeroBlock(output_buffer_addr, sizeof(NodeInfo) * UDSMaxNodes); + Memory::WriteBlock(output_buffer_addr, nodes.data(), sizeof(NodeInfo) * nodes.size()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.PushStaticBuffer(output_buffer_addr, output_buffer_size, 0); + rb.Push(RESULT_SUCCESS); +} + // Sends a 802.11 beacon frame with information about the current network. static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { // Don't do anything if we're not actually hosting a network @@ -462,7 +559,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, {0x001E0084, nullptr, "ConnectToNetwork"}, - {0x001F0006, nullptr, "DecryptBeaconData"}, + {0x001F0006, DecryptBeaconData, "DecryptBeaconData"}, {0x00200040, nullptr, "Flush"}, {0x00210080, nullptr, "SetProbeResponseParam"}, {0x00220402, nullptr, "ScanOnConnection"}, diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp index c6e5bc5f1..6332b404c 100644 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -3,14 +3,13 @@ // Refer to the license.txt file included. #include <cstring> - -#include "core/hle/service/nwm/nwm_uds.h" -#include "core/hle/service/nwm/uds_beacon.h" - #include <cryptopp/aes.h> #include <cryptopp/md5.h> #include <cryptopp/modes.h> #include <cryptopp/sha.h> +#include "common/assert.h" +#include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_beacon.h" namespace Service { namespace NWM { diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 319e8c946..39382ef09 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" @@ -134,7 +135,7 @@ void Init() { auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); // If the archive didn't exist, create the files inside - if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { + if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) { // Format the archive to create the directories Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, FileSys::ArchiveFormatInfo(), archive_path); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0672ac2e3..d34968428 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -2,11 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <boost/range/algorithm_ext/erase.hpp> - +#include <fmt/format.h> #include "common/logging/log.h" #include "common/string_util.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/service/ac/ac.h" #include "core/hle/service/act/act.h" #include "core/hle/service/am/am.h" @@ -39,15 +40,20 @@ #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/qtm/qtm.h" #include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/srv.h" #include "core/hle/service/soc_u.h" -#include "core/hle/service/srv.h" #include "core/hle/service/ssl_c.h" #include "core/hle/service/y2r_u.h" +using Kernel::ClientPort; +using Kernel::ServerPort; +using Kernel::ServerSession; +using Kernel::SharedPtr; + namespace Service { -std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; -std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; +std::unordered_map<std::string, SharedPtr<ClientPort>> g_kernel_named_ports; /** * Creates a function string for logging, complete with the name (or header code, depending @@ -66,20 +72,10 @@ static std::string MakeFunctionString(const char* name, const char* port_name, return function_string; } -void SessionRequestHandler::ClientConnected( - Kernel::SharedPtr<Kernel::ServerSession> server_session) { - connected_sessions.push_back(server_session); -} - -void SessionRequestHandler::ClientDisconnected( - Kernel::SharedPtr<Kernel::ServerSession> server_session) { - boost::range::remove_erase(connected_sessions, server_session); -} - Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} Interface::~Interface() = default; -void Interface::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { +void Interface::HandleSyncRequest(SharedPtr<ServerSession> server_session) { // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which // session triggered each command. @@ -113,27 +109,106 @@ void Interface::Register(const FunctionInfo* functions, size_t n) { } //////////////////////////////////////////////////////////////////////////////////////////////////// + +ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, + InvokerFn* handler_invoker) + : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} + +ServiceFrameworkBase::~ServiceFrameworkBase() = default; + +void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { + ASSERT(port == nullptr); + port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); + port->SetHleHandler(shared_from_this()); +} + +void ServiceFrameworkBase::InstallAsNamedPort() { + ASSERT(port == nullptr); + SharedPtr<ServerPort> server_port; + SharedPtr<ClientPort> client_port; + std::tie(server_port, client_port) = ServerPort::CreatePortPair(max_sessions, service_name); + server_port->SetHleHandler(shared_from_this()); + AddNamedPort(service_name, std::move(client_port)); +} + +void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) { + handlers.reserve(handlers.size() + n); + for (size_t i = 0; i < n; ++i) { + // Usually this array is sorted by id already, so hint to insert at the end + handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); + } +} + +void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info) { + IPC::Header header{cmd_buf[0]}; + int num_params = header.normal_params_size + header.translate_params_size; + std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name; + + fmt::MemoryWriter w; + w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name, + cmd_buf[0]); + for (int i = 1; i <= num_params; ++i) { + w.write(", [{}]={:#x}", i, cmd_buf[i]); + } + w << '}'; + + LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str()); + // TODO(bunnei): Hack - ignore error + cmd_buf[1] = 0; +} + +void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) { + u32* cmd_buf = Kernel::GetCommandBuffer(); + + // TODO(yuriks): The kernel should be the one handling this as part of translation after + // everything else is migrated + Kernel::HLERequestContext context; + context.cmd_buf = cmd_buf; + context.session = std::move(server_session); + + u32 header_code = cmd_buf[0]; + auto itr = handlers.find(header_code); + const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; + if (info == nullptr || info->handler_callback == nullptr) { + return ReportUnimplementedFunction(cmd_buf, info); + } + + LOG_TRACE(Service, "%s", + MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); + handler_invoker(this, info->handler_callback, context); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// // Module interface +// TODO(yuriks): Move to kernel +void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { + g_kernel_named_ports.emplace(std::move(name), std::move(port)); +} + static void AddNamedPort(Interface* interface_) { - auto ports = - Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), - std::shared_ptr<Interface>(interface_)); - auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); - g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port)); + SharedPtr<ServerPort> server_port; + SharedPtr<ClientPort> client_port; + std::tie(server_port, client_port) = + ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName()); + + server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); + AddNamedPort(interface_->GetPortName(), std::move(client_port)); } void AddService(Interface* interface_) { - auto ports = - Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), - std::shared_ptr<Interface>(interface_)); - auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); - g_srv_services.emplace(interface_->GetPortName(), std::move(client_port)); + auto server_port = + SM::g_service_manager + ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) + .MoveFrom(); + server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); } /// Initialize ServiceManager void Init() { - AddNamedPort(new SRV::SRV); + SM::g_service_manager = std::make_shared<SM::ServiceManager>(); + SM::ServiceManager::InstallInterfaces(SM::g_service_manager); + AddNamedPort(new ERR::ERR_F); FS::ArchiveInit(); @@ -194,7 +269,7 @@ void Shutdown() { AC::Shutdown(); FS::ArchiveShutdown(); - g_srv_services.clear(); + SM::g_service_manager = nullptr; g_kernel_named_ports.clear(); LOG_DEBUG(Service, "shutdown OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index e6a5f1417..281ff99bb 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -8,70 +8,38 @@ #include <string> #include <unordered_map> #include <boost/container/flat_map.hpp> +#include "common/bit_field.h" #include "common/common_types.h" -#include "core/hle/ipc.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/result.h" -#include "core/memory.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace Service namespace Kernel { +class ClientPort; +class ServerPort; class ServerSession; } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Service - namespace Service { +namespace SM { +class ServiceManager; +} + static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) /// Arbitrary default number of maximum connections to an HLE service. static const u32 DefaultMaxSessions = 10; /** - * Interface implemented by HLE Session handlers. - * This can be provided to a ServerSession in order to hook into several relevant events - * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. - */ -class SessionRequestHandler { -public: - /** - * Handles a sync request from the emulated application. - * @param server_session The ServerSession that was triggered for this sync request, - * it should be used to differentiate which client (As in ClientSession) we're answering to. - * TODO(Subv): Use a wrapper structure to hold all the information relevant to - * this request (ServerSession, Originator thread, Translated command buffer, etc). - * @returns ResultCode the result code of the translate operation. - */ - virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0; - - /** - * Signals that a client has just connected to this HLE handler and keeps the - * associated ServerSession alive for the duration of the connection. - * @param server_session Owning pointer to the ServerSession associated with the connection. - */ - void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); - - /** - * Signals that a client has just disconnected from this HLE handler and releases the - * associated ServerSession. - * @param server_session ServerSession associated with the connection. - */ - void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); - -protected: - /// List of sessions that are connected to this handler. - /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list - // for the duration of the connection. - std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions; -}; - -/** * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a * table mapping header ids to handler functions. + * + * @deprecated Use ServiceFramework for new services instead. It allows services to be stateful and + * is more extensible going forward. */ -class Interface : public SessionRequestHandler { +class Interface : public Kernel::SessionRequestHandler { public: /** * Creates an HLE interface with the specified max sessions. @@ -141,6 +109,146 @@ private: boost::container::flat_map<u32, FunctionInfo> m_functions; }; +/** + * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it + * is not meant to be used directly. + * + * @see ServiceFramework + */ +class ServiceFrameworkBase : public Kernel::SessionRequestHandler { +public: + /// Returns the string identifier used to connect to the service. + std::string GetServiceName() const { + return service_name; + } + + /** + * Returns the maximum number of sessions that can be connected to this service at the same + * time. + */ + u32 GetMaxSessions() const { + return max_sessions; + } + + /// Creates a port pair and registers this service with the given ServiceManager. + void InstallAsService(SM::ServiceManager& service_manager); + /// Creates a port pair and registers it on the kernel's global port registry. + void InstallAsNamedPort(); + + void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; + +protected: + /// Member-function pointer type of SyncRequest handlers. + template <typename Self> + using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); + +private: + template <typename T> + friend class ServiceFramework; + + struct FunctionInfoBase { + u32 expected_header; + HandlerFnP<ServiceFrameworkBase> handler_callback; + const char* name; + }; + + using InvokerFn = void(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member, + Kernel::HLERequestContext& ctx); + + ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); + ~ServiceFrameworkBase(); + + void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); + void ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info); + + /// Identifier string used to connect to the service. + std::string service_name; + /// Maximum number of concurrent sessions that this service can handle. + u32 max_sessions; + + /** + * Port where incoming connections will be received. Only created when InstallAsService() or + * InstallAsNamedPort() are called. + */ + Kernel::SharedPtr<Kernel::ServerPort> port; + + /// Function used to safely up-cast pointers to the derived class before invoking a handler. + InvokerFn* handler_invoker; + boost::container::flat_map<u32, FunctionInfoBase> handlers; +}; + +/** + * Framework for implementing HLE services. Dispatches on the header id of incoming SyncRequests + * based on a table mapping header ids to handler functions. Service implementations should inherit + * from ServiceFramework using the CRTP (`class Foo : public ServiceFramework<Foo> { ... };`) and + * populate it with handlers by calling #RegisterHandlers. + * + * In order to avoid duplicating code in the binary and exposing too many implementation details in + * the header, this class is split into a non-templated base (ServiceFrameworkBase) and a template + * deriving from it (ServiceFramework). The functions in this class will mostly only erase the type + * of the passed in function pointers and then delegate the actual work to the implementation in the + * base class. + */ +template <typename Self> +class ServiceFramework : public ServiceFrameworkBase { +protected: + /// Contains information about a request type which is handled by the service. + struct FunctionInfo : FunctionInfoBase { + // TODO(yuriks): This function could be constexpr, but clang is the only compiler that + // doesn't emit an ICE or a wrong diagnostic because of the static_cast. + + /** + * Constructs a FunctionInfo for a function. + * + * @param expected_header request header in the command buffer which will trigger dispatch + * to this handler + * @param handler_callback member function in this service which will be called to handle + * the request + * @param name human-friendly name for the request. Used mostly for logging purposes. + */ + FunctionInfo(u32 expected_header, HandlerFnP<Self> handler_callback, const char* name) + : FunctionInfoBase{ + expected_header, + // Type-erase member function pointer by casting it down to the base class. + static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback), name} {} + }; + + /** + * Initializes the handler with no functions installed. + * @param max_sessions Maximum number of sessions that can be + * connected to this service at the same time. + */ + ServiceFramework(const char* service_name, u32 max_sessions = DefaultMaxSessions) + : ServiceFrameworkBase(service_name, max_sessions, Invoker) {} + + /// Registers handlers in the service. + template <size_t N> + void RegisterHandlers(const FunctionInfo (&functions)[N]) { + RegisterHandlers(functions, N); + } + + /** + * Registers handlers in the service. Usually prefer using the other RegisterHandlers + * overload in order to avoid needing to specify the array size. + */ + void RegisterHandlers(const FunctionInfo* functions, size_t n) { + RegisterHandlersBase(functions, n); + } + +private: + /** + * This function is used to allow invocation of pointers to handlers stored in the base class + * without needing to expose the type of this derived class. Pointers-to-member may require a + * fixup when being up or downcast, and thus code that does that needs to know the concrete type + * of the derived class in order to invoke one of it's functions through a pointer. + */ + static void Invoker(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member, + Kernel::HLERequestContext& ctx) { + // Cast back up to our original types and call the member function + (static_cast<Self*>(object)->*static_cast<HandlerFnP<Self>>(member))(ctx); + } +}; + /// Initialize ServiceManager void Init(); @@ -149,9 +257,9 @@ void Shutdown(); /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; -/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. -extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; +/// Adds a port to the named port table +void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port); /// Adds a service to the services table void AddService(Interface* interface_); diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp new file mode 100644 index 000000000..5e7fc68f9 --- /dev/null +++ b/src/core/hle/service/sm/sm.cpp @@ -0,0 +1,69 @@ +// Copyright 2017 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/client_session.h" +#include "core/hle/kernel/server_port.h" +#include "core/hle/result.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/srv.h" + +namespace Service { +namespace SM { + +static ResultCode ValidateServiceName(const std::string& name) { + if (name.size() <= 0 || name.size() > 8) { + return ERR_INVALID_NAME_SIZE; + } + if (name.find('\0') != std::string::npos) { + return ERR_NAME_CONTAINS_NUL; + } + return RESULT_SUCCESS; +} + +void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self) { + ASSERT(self->srv_interface.expired()); + + auto srv = std::make_shared<SRV>(self); + srv->InstallAsNamedPort(); + self->srv_interface = srv; +} + +ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService( + std::string name, unsigned int max_sessions) { + + CASCADE_CODE(ValidateServiceName(name)); + Kernel::SharedPtr<Kernel::ServerPort> server_port; + Kernel::SharedPtr<Kernel::ClientPort> client_port; + std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); + + registered_services.emplace(std::move(name), std::move(client_port)); + return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); +} + +ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( + const std::string& name) { + + CASCADE_CODE(ValidateServiceName(name)); + auto it = registered_services.find(name); + if (it == registered_services.end()) { + return ERR_SERVICE_NOT_REGISTERED; + } + + return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second); +} + +ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( + const std::string& name) { + + CASCADE_RESULT(auto client_port, GetServicePort(name)); + return client_port->Connect(); +} + +std::shared_ptr<ServiceManager> g_service_manager; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h new file mode 100644 index 000000000..8f0dbf2db --- /dev/null +++ b/src/core/hle/service/sm/sm.h @@ -0,0 +1,55 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <unordered_map> +#include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class ClientPort; +class ClientSession; +class ServerPort; +class SessionRequestHandler; +} // namespace Kernel + +namespace Service { +namespace SM { + +class SRV; + +constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(1, ErrorModule::SRV, ErrorSummary::WouldBlock, + ErrorLevel::Temporary); // 0xD0406401 +constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(2, ErrorModule::SRV, ErrorSummary::WouldBlock, + ErrorLevel::Temporary); // 0xD0406402 +constexpr ResultCode ERR_INVALID_NAME_SIZE(5, ErrorModule::SRV, ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9006405 +constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E06406 +constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9006407 + +class ServiceManager { +public: + static void InstallInterfaces(std::shared_ptr<ServiceManager> self); + + ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, + unsigned int max_sessions); + ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); + ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); + +private: + std::weak_ptr<SRV> srv_interface; + + /// Map of registered services, retrieved using GetServicePort or ConnectToService. + std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services; +}; + +extern std::shared_ptr<ServiceManager> g_service_manager; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp new file mode 100644 index 000000000..b8b62b068 --- /dev/null +++ b/src/core/hle/service/sm/srv.cpp @@ -0,0 +1,204 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <tuple> + +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/semaphore.h" +#include "core/hle/kernel/server_session.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/srv.h" + +namespace Service { +namespace SM { + +constexpr int MAX_PENDING_NOTIFICATIONS = 16; + +/** + * SRV::RegisterClient service function + * Inputs: + * 0: 0x00010002 + * 1: ProcessId Header (must be 0x20) + * Outputs: + * 0: 0x00010040 + * 1: ResultCode + */ +void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { + u32* cmd_buff = ctx.CommandBuffer(); + + if (cmd_buff[1] != IPC::CallingPidDesc()) { + cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40 + cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; + return; + } + cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called"); +} + +/** + * SRV::EnableNotification service function + * Inputs: + * 0: 0x00020000 + * Outputs: + * 0: 0x00020042 + * 1: ResultCode + * 2: Translation descriptor: 0x20 + * 3: Handle to semaphore signaled on process notification + */ +void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { + u32* cmd_buff = ctx.CommandBuffer(); + + notification_semaphore = + Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); + + cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = IPC::CopyHandleDesc(1); + cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom(); + LOG_WARNING(Service_SRV, "(STUBBED) called"); +} + +/** + * SRV::GetServiceHandle service function + * Inputs: + * 0: 0x00050100 + * 1-2: 8-byte UTF-8 service name + * 3: Name length + * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable) + * Outputs: + * 1: ResultCode + * 3: Service handle + */ +void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { + ResultCode res = RESULT_SUCCESS; + u32* cmd_buff = ctx.CommandBuffer(); + + size_t name_len = cmd_buff[3]; + if (name_len > Service::kMaxPortSize) { + cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw; + LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len, + cmd_buff[1]); + return; + } + std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len); + bool return_port_on_failure = (cmd_buff[4] & 1) == 0; + + // TODO(yuriks): Permission checks go here + + auto client_port = service_manager->GetServicePort(name); + if (client_port.Failed()) { + cmd_buff[1] = client_port.Code().raw; + LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), + cmd_buff[1]); + return; + } + + auto session = client_port.Unwrap()->Connect(); + cmd_buff[1] = session.Code().raw; + if (session.Succeeded()) { + cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom(); + LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(), + cmd_buff[3]); + } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { + cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw; + cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom(); + LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(), + cmd_buff[3]); + } else { + LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), + cmd_buff[1]); + } +} + +/** + * SRV::Subscribe service function + * Inputs: + * 0: 0x00090040 + * 1: Notification ID + * Outputs: + * 0: 0x00090040 + * 1: ResultCode + */ +void SRV::Subscribe(Kernel::HLERequestContext& ctx) { + u32* cmd_buff = ctx.CommandBuffer(); + + u32 notification_id = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); +} + +/** + * SRV::Unsubscribe service function + * Inputs: + * 0: 0x000A0040 + * 1: Notification ID + * Outputs: + * 0: 0x000A0040 + * 1: ResultCode + */ +void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { + u32* cmd_buff = ctx.CommandBuffer(); + + u32 notification_id = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); +} + +/** + * SRV::PublishToSubscriber service function + * Inputs: + * 0: 0x000C0080 + * 1: Notification ID + * 2: Flags (bit0: only fire if not fired, bit1: report errors) + * Outputs: + * 0: 0x000C0040 + * 1: ResultCode + */ +void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { + u32* cmd_buff = ctx.CommandBuffer(); + + u32 notification_id = cmd_buff[1]; + u8 flags = cmd_buff[2] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040 + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, + flags); +} + +SRV::SRV(std::shared_ptr<ServiceManager> service_manager) + : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { + static const FunctionInfo functions[] = { + {0x00010002, &SRV::RegisterClient, "RegisterClient"}, + {0x00020000, &SRV::EnableNotification, "EnableNotification"}, + {0x00030100, nullptr, "RegisterService"}, + {0x000400C0, nullptr, "UnregisterService"}, + {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, + {0x000600C2, nullptr, "RegisterPort"}, + {0x000700C0, nullptr, "UnregisterPort"}, + {0x00080100, nullptr, "GetPort"}, + {0x00090040, &SRV::Subscribe, "Subscribe"}, + {0x000A0040, &SRV::Unsubscribe, "Unsubscribe"}, + {0x000B0000, nullptr, "ReceiveNotification"}, + {0x000C0080, &SRV::PublishToSubscriber, "PublishToSubscriber"}, + {0x000D0040, nullptr, "PublishAndGetSubscriber"}, + {0x000E00C0, nullptr, "IsServiceRegistered"}, + }; + RegisterHandlers(functions); +} + +SRV::~SRV() = default; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h new file mode 100644 index 000000000..75cca5184 --- /dev/null +++ b/src/core/hle/service/sm/srv.h @@ -0,0 +1,37 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +class Semaphore; +} + +namespace Service { +namespace SM { + +/// Interface to "srv:" service +class SRV final : public ServiceFramework<SRV> { +public: + explicit SRV(std::shared_ptr<ServiceManager> service_manager); + ~SRV(); + +private: + void RegisterClient(Kernel::HLERequestContext& ctx); + void EnableNotification(Kernel::HLERequestContext& ctx); + void GetServiceHandle(Kernel::HLERequestContext& ctx); + void Subscribe(Kernel::HLERequestContext& ctx); + void Unsubscribe(Kernel::HLERequestContext& ctx); + void PublishToSubscriber(Kernel::HLERequestContext& ctx); + + std::shared_ptr<ServiceManager> service_manager; + Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; +}; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 530614e6f..3d215d42d 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/scope_exit.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/soc_u.h" diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp deleted file mode 100644 index 3bd787147..000000000 --- a/src/core/hle/service/srv.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <tuple> - -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/server_session.h" -#include "core/hle/service/srv.h" - -namespace Service { -namespace SRV { - -static Kernel::SharedPtr<Kernel::Event> event_handle; - -/** - * SRV::RegisterClient service function - * Inputs: - * 0: 0x00010002 - * 1: ProcessId Header (must be 0x20) - * Outputs: - * 0: 0x00010040 - * 1: ResultCode - */ -static void RegisterClient(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - if (cmd_buff[1] != IPC::CallingPidDesc()) { - cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40 - cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent) - .raw; - return; - } - cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040 - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_SRV, "(STUBBED) called"); -} - -/** - * SRV::EnableNotification service function - * Inputs: - * 0: 0x00020000 - * Outputs: - * 0: 0x00020042 - * 1: ResultCode - * 2: Translation descriptor: 0x20 - * 3: Handle to semaphore signaled on process notification - */ -static void EnableNotification(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(bunnei): Change to a semaphore once these have been implemented - event_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "SRV:Event"); - event_handle->Clear(); - - cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042 - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = IPC::CopyHandleDesc(1); - cmd_buff[3] = Kernel::g_handle_table.Create(event_handle).MoveFrom(); - LOG_WARNING(Service_SRV, "(STUBBED) called"); -} - -/** - * SRV::GetServiceHandle service function - * Inputs: - * 0: 0x00050100 - * 1-2: 8-byte UTF-8 service name - * 3: Name length - * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable) - * Outputs: - * 1: ResultCode - * 3: Service handle - */ -static void GetServiceHandle(Interface* self) { - ResultCode res = RESULT_SUCCESS; - u32* cmd_buff = Kernel::GetCommandBuffer(); - - std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); - auto it = Service::g_srv_services.find(port_name); - - if (it != Service::g_srv_services.end()) { - auto client_port = it->second; - - auto client_session = client_port->Connect(); - res = client_session.Code(); - - if (client_session.Succeeded()) { - // Return the client session - cmd_buff[3] = Kernel::g_handle_table.Create(*client_session).MoveFrom(); - } - LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); - } else { - LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); - res = UnimplementedFunction(ErrorModule::SRV); - } - cmd_buff[1] = res.raw; -} - -/** - * SRV::Subscribe service function - * Inputs: - * 0: 0x00090040 - * 1: Notification ID - * Outputs: - * 0: 0x00090040 - * 1: ResultCode - */ -static void Subscribe(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 notification_id = cmd_buff[1]; - - cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040 - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); -} - -/** - * SRV::Unsubscribe service function - * Inputs: - * 0: 0x000A0040 - * 1: Notification ID - * Outputs: - * 0: 0x000A0040 - * 1: ResultCode - */ -static void Unsubscribe(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 notification_id = cmd_buff[1]; - - cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040 - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); -} - -/** - * SRV::PublishToSubscriber service function - * Inputs: - * 0: 0x000C0080 - * 1: Notification ID - * 2: Flags (bit0: only fire if not fired, bit1: report errors) - * Outputs: - * 0: 0x000C0040 - * 1: ResultCode - */ -static void PublishToSubscriber(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 notification_id = cmd_buff[1]; - u8 flags = cmd_buff[2] & 0xFF; - - cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040 - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, - flags); -} - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010002, RegisterClient, "RegisterClient"}, - {0x00020000, EnableNotification, "EnableNotification"}, - {0x00030100, nullptr, "RegisterService"}, - {0x000400C0, nullptr, "UnregisterService"}, - {0x00050100, GetServiceHandle, "GetServiceHandle"}, - {0x000600C2, nullptr, "RegisterPort"}, - {0x000700C0, nullptr, "UnregisterPort"}, - {0x00080100, nullptr, "GetPort"}, - {0x00090040, Subscribe, "Subscribe"}, - {0x000A0040, Unsubscribe, "Unsubscribe"}, - {0x000B0000, nullptr, "ReceiveNotification"}, - {0x000C0080, PublishToSubscriber, "PublishToSubscriber"}, - {0x000D0040, nullptr, "PublishAndGetSubscriber"}, - {0x000E00C0, nullptr, "IsServiceRegistered"}, -}; - -SRV::SRV() { - Register(FunctionTable); - event_handle = nullptr; -} - -SRV::~SRV() { - event_handle = nullptr; -} - -} // namespace SRV -} // namespace Service diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h deleted file mode 100644 index d3a9de879..000000000 --- a/src/core/hle/service/srv.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 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 SRV { - -/// Interface to "srv:" service -class SRV final : public Interface { -public: - SRV(); - ~SRV() override; - - std::string GetPortName() const override { - return "srv:"; - } -}; - -} // namespace SRV -} // namespace Service diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index 09ced9d7a..300acca75 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp @@ -4,7 +4,9 @@ #include <random> #include "common/common_types.h" +#include "core/hle/ipc.h" #include "core/hle/service/ssl_c.h" +#include "core/memory.h" namespace Service { namespace SSL { diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index c0837d49d..bb7bf2d67 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -6,6 +6,8 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/y2r_u.h" diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 8538cfc9d..e68b9f16a 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include <map> #include "common/logging/log.h" @@ -14,7 +15,9 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" @@ -26,6 +29,7 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" #include "core/hle/kernel/vm_manager.h" +#include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -37,25 +41,6 @@ using Kernel::ERR_INVALID_HANDLE; namespace SVC { -const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel, - ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA -const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, - ErrorSummary::InvalidArgument, - ErrorLevel::Usage); // 0xE0E0181E - -const ResultCode ERR_SYNC_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, ErrorLevel::Info); - -const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1 - ErrorDescription::MisalignedAddress, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage}; -const ResultCode ERR_MISALIGNED_SIZE{// 0xE0E01BF2 - ErrorDescription::MisalignedSize, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage}; -const ResultCode ERR_INVALID_COMBINATION{// 0xE0E01BEE - ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage}; - enum ControlMemoryOperation { MEMOP_FREE = 1, MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel @@ -195,8 +180,7 @@ static ResultCode MapMemoryBlock(Kernel::Handle handle, u32 addr, u32 permission LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); } - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_INVALID_COMBINATION; } static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { @@ -216,16 +200,16 @@ static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { /// Connect to an OS service given the port name, returns the handle to the port to out static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { if (port_name == nullptr) - return ERR_NOT_FOUND; + return Kernel::ERR_NOT_FOUND; if (std::strlen(port_name) > 11) - return ERR_PORT_NAME_TOO_LONG; + return Kernel::ERR_PORT_NAME_TOO_LONG; LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); auto it = Service::g_kernel_named_ports.find(port_name); if (it == Service::g_kernel_named_ports.end()) { LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); - return ERR_NOT_FOUND; + return Kernel::ERR_NOT_FOUND; } auto client_port = it->second; @@ -263,7 +247,7 @@ static ResultCode CloseHandle(Kernel::Handle handle) { /// Wait for a handle to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { - auto object = Kernel::g_handle_table.GetWaitObject(handle); + auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); Kernel::Thread* thread = Kernel::GetCurrentThread(); if (object == nullptr) @@ -275,7 +259,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) if (object->ShouldWait(thread)) { if (nano_seconds == 0) - return ERR_SYNC_TIMEOUT; + return Kernel::RESULT_TIMEOUT; thread->wait_objects = {object}; object->AddWaitingThread(thread); @@ -289,7 +273,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread // resumes due to a signal in its wait objects. // Otherwise we retain the default value of timeout. - return ERR_SYNC_TIMEOUT; + return Kernel::RESULT_TIMEOUT; } object->Acquire(thread); @@ -304,8 +288,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Check if 'handles' is invalid if (handles == nullptr) - return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + return Kernel::ERR_INVALID_POINTER; // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If // this happens, the running application will crash. @@ -313,14 +296,13 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Check if 'handle_count' is invalid if (handle_count < 0) - return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_OUT_OF_RANGE; using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>; std::vector<ObjectPtr> objects(handle_count); for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; @@ -344,7 +326,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // If a timeout value of 0 was provided, just return the Timeout error code instead of // suspending the thread. if (nano_seconds == 0) - return ERR_SYNC_TIMEOUT; + return Kernel::RESULT_TIMEOUT; // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH_ALL; @@ -365,7 +347,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha *out = -1; // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to // a signal in one of its wait objects. - return ERR_SYNC_TIMEOUT; + return Kernel::RESULT_TIMEOUT; } else { // Find the first object that is acquirable in the provided list of objects auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { @@ -385,7 +367,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // If a timeout value of 0 was provided, just return the Timeout error code instead of // suspending the thread. if (nano_seconds == 0) - return ERR_SYNC_TIMEOUT; + return Kernel::RESULT_TIMEOUT; // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH_ANY; @@ -411,7 +393,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Otherwise we retain the default value of timeout, and -1 in the out parameter thread->wait_set_output = true; *out = -1; - return ERR_SYNC_TIMEOUT; + return Kernel::RESULT_TIMEOUT; } } @@ -520,22 +502,20 @@ static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resour } /// Creates a new thread -static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 entry_point, u32 arg, +static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { using Kernel::Thread; std::string name = Common::StringFromFormat("unknown-%08" PRIX32, entry_point); if (priority > THREADPRIO_LOWEST) { - return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_OUT_OF_RANGE; } using Kernel::ResourceLimit; Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit; if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) { - return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); + return Kernel::ERR_NOT_AUTHORIZED; } switch (processor_id) { @@ -605,8 +585,7 @@ static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { /// Sets the priority for the specified thread static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { if (priority > THREADPRIO_LOWEST) { - return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_OUT_OF_RANGE; } SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); @@ -618,8 +597,7 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { // the one from the thread owner's resource limit. Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit; if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) { - return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); + return Kernel::ERR_NOT_AUTHORIZED; } thread->SetPriority(priority); @@ -743,8 +721,7 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf auto vma = process->vm_manager.FindVMA(addr); if (vma == Kernel::g_current_process->vm_manager.vma_map.end()) - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_INVALID_ADDRESS; memory_info->base_address = vma->second.base; memory_info->permission = static_cast<u32>(vma->second.permissions); @@ -842,8 +819,7 @@ static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) { LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); if (initial < 0 || interval < 0) { - return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + return Kernel::ERR_OUT_OF_RANGE_KERNEL; } SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); @@ -902,8 +878,7 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si using Kernel::SharedMemory; if (size % Memory::PAGE_SIZE != 0) - return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_MISALIGNED_SIZE; SharedPtr<SharedMemory> shared_memory = nullptr; @@ -924,16 +899,14 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) || !VerifyPermissions(static_cast<MemoryPermission>(other_permission))) - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_INVALID_COMBINATION; // TODO(Subv): Processes with memory type APPLICATION are not allowed // to create memory blocks with addr = 0, any attempts to do so // should return error 0xD92007EA. if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) && addr != 0) { - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); + return Kernel::ERR_INVALID_ADDRESS; } // When trying to create a memory block with address = 0, @@ -1035,7 +1008,7 @@ static ResultCode GetProcessInfo(s64* out, Kernel::Handle process_handle, u32 ty *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; if (*out % Memory::PAGE_SIZE != 0) { LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned"); - return ERR_MISALIGNED_SIZE; + return Kernel::ERR_MISALIGNED_SIZE; } break; case 1: @@ -1051,19 +1024,15 @@ static ResultCode GetProcessInfo(s64* out, Kernel::Handle process_handle, u32 ty case 20: *out = Memory::FCRAM_PADDR - process->GetLinearHeapBase(); break; + case 21: + case 22: + case 23: + // These return a different error value than higher invalid values + LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type); + return Kernel::ERR_NOT_IMPLEMENTED; default: LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type); - - if (type >= 21 && type <= 23) { - return ResultCode( // 0xE0E01BF4 - ErrorDescription::NotImplemented, ErrorModule::OS, ErrorSummary::InvalidArgument, - ErrorLevel::Usage); - } else { - return ResultCode( // 0xD8E007ED - ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - } - break; + return Kernel::ERR_INVALID_ENUM_VALUE; } return RESULT_SUCCESS; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 1d80766ae..48bbf687d 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -8,6 +8,7 @@ #include <initializer_list> #include <memory> #include <string> +#include <utility> #include <vector> #include <boost/optional.hpp> #include "common/common_types.h" @@ -100,11 +101,11 @@ public: * Loads the system mode that this application needs. * This function defaults to 2 (96MB allocated to the application) if it can't read the * information. - * @returns Optional with the kernel system mode + * @returns A pair with the optional system mode, and and the status. */ - virtual boost::optional<u32> LoadKernelSystemMode() { + virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() { // 96MB allocated to the application. - return 2; + return std::make_pair(2, ResultStatus::Success); } /** diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index beeb13ffa..ffc019560 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -121,12 +121,16 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -boost::optional<u32> AppLoader_NCCH::LoadKernelSystemMode() { +std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { if (!is_loaded) { - if (LoadExeFS() != ResultStatus::Success) - return boost::none; + ResultStatus res = LoadExeFS(); + if (res != ResultStatus::Success) { + return std::make_pair(boost::none, res); + } } - return exheader_header.arm11_system_local_caps.system_mode.Value(); + // Set the system mode as the one from the exheader. + return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(), + ResultStatus::Success); } ResultStatus AppLoader_NCCH::LoadExec() { diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 4ef95b5c6..0ebd47fd5 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -179,9 +179,9 @@ public: /** * Loads the Exheader and returns the system mode for this application. - * @return Optional with the kernel system mode + * @returns A pair with the optional system mode, and and the status. */ - boost::optional<u32> LoadKernelSystemMode() override; + std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() override; ResultStatus ReadCode(std::vector<u8>& buffer) override; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d2e7c6b97..d4f0429d1 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -6,7 +6,7 @@ #include "core/gdbstub/gdbstub.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir.h" -#include "settings.h" +#include "core/settings.h" #include "video_core/video_core.h" #include "core/frontend/emu_window.h" diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp index 276a5b288..55b3b5efc 100644 --- a/src/core/tracer/recorder.cpp +++ b/src/core/tracer/recorder.cpp @@ -6,7 +6,7 @@ #include "common/assert.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "recorder.h" +#include "core/tracer/recorder.h" namespace CiTrace { diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h index aea363b95..39e6ec4fd 100644 --- a/src/core/tracer/recorder.h +++ b/src/core/tracer/recorder.h @@ -8,8 +8,8 @@ #include <unordered_map> #include <vector> #include <boost/crc.hpp> -#include "citrace.h" #include "common/common_types.h" +#include "core/tracer/citrace.h" namespace CiTrace { diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index cfe5caaa3..e3e36ada7 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -13,15 +13,14 @@ set(HEADERS if(SDL2_FOUND) set(SRCS ${SRCS} sdl/sdl.cpp) set(HEADERS ${HEADERS} sdl/sdl.h) - include_directories(${SDL2_INCLUDE_DIR}) endif() create_directory_groups(${SRCS} ${HEADERS}) add_library(input_common STATIC ${SRCS} ${HEADERS}) -target_link_libraries(input_common common core) +target_link_libraries(input_common PUBLIC core PRIVATE common) if(SDL2_FOUND) - target_link_libraries(input_common ${SDL2_LIBRARY}) - set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) + target_link_libraries(input_common PRIVATE SDL2) + target_compile_definitions(input_common PRIVATE HAVE_SDL2) endif() diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index d1144ba77..00d7c636a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -10,10 +10,9 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) -include_directories(../../externals/catch/single_include/) - add_executable(tests ${SRCS} ${HEADERS}) -target_link_libraries(tests core video_core audio_core common) -target_link_libraries(tests ${PLATFORM_LIBRARIES} Threads::Threads) +target_link_libraries(tests PRIVATE common core) +target_link_libraries(tests PRIVATE glad) # To support linker work-around +target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) -add_test(NAME tests COMMAND $<TARGET_FILE:tests>) +add_test(NAME tests COMMAND tests) diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e00b88f71..0961a3251 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -79,13 +79,14 @@ endif() create_directory_groups(${SRCS} ${HEADERS}) add_library(video_core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(video_core glad) +target_link_libraries(video_core PUBLIC common core) +target_link_libraries(video_core PRIVATE glad nihstro-headers) + if (ARCHITECTURE_x86_64) - target_link_libraries(video_core xbyak) + target_link_libraries(video_core PRIVATE xbyak) endif() if (PNG_FOUND) - target_link_libraries(video_core ${PNG_LIBRARIES}) - include_directories(${PNG_INCLUDE_DIRS}) - add_definitions(${PNG_DEFINITIONS}) + target_link_libraries(video_core PRIVATE PNG::PNG) + target_compile_definitions(video_core PRIVATE HAVE_PNG) endif() diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h index e4038b41b..3f5355fa9 100644 --- a/src/video_core/regs_texturing.h +++ b/src/video_core/regs_texturing.h @@ -133,7 +133,32 @@ struct TexturingRegs { BitField<16, 1, u32> clear_texture_cache; // TODO: unimplemented } main_config; TextureConfig texture0; - INSERT_PADDING_WORDS(0x8); + + enum class CubeFace { + PositiveX = 0, + NegativeX = 1, + PositiveY = 2, + NegativeY = 3, + PositiveZ = 4, + NegativeZ = 5, + }; + + BitField<0, 22, u32> cube_address[5]; + + PAddr GetCubePhysicalAddress(CubeFace face) const { + PAddr address = texture0.address; + if (face != CubeFace::PositiveX) { + // Bits [22:27] from the main texture address is shared with all cubemap additional + // addresses. + auto& face_addr = cube_address[static_cast<size_t>(face) - 1]; + address &= ~face_addr.mask; + address |= face_addr; + } + // A multiplier of 8 is also needed in the same way as the main address. + return address * 8; + } + + INSERT_PADDING_WORDS(0x3); BitField<0, 4, TextureFormat> texture0_format; BitField<0, 1, u32> fragment_lighting_enable; INSERT_PADDING_WORDS(0x1); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 456443e86..8b717e43d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -561,20 +561,16 @@ RasterizerCacheOpenGL::GetFramebufferSurfaces( color_params.is_tiled = depth_params.is_tiled = true; // Set the internal resolution, assume the same scaling factor for top and bottom screens - const Layout::FramebufferLayout& layout = VideoCore::g_emu_window->GetFramebufferLayout(); - if (Settings::values.resolution_factor == 0.0f) { + float resolution_scale_factor = Settings::values.resolution_factor; + if (resolution_scale_factor == 0.0f) { // Auto - scale resolution to the window size - color_params.res_scale_width = depth_params.res_scale_width = - (float)layout.top_screen.GetWidth() / VideoCore::kScreenTopWidth; - color_params.res_scale_height = depth_params.res_scale_height = - (float)layout.top_screen.GetHeight() / VideoCore::kScreenTopHeight; - } else { - // Otherwise, scale the resolution by the specified factor - color_params.res_scale_width = Settings::values.resolution_factor; - depth_params.res_scale_width = Settings::values.resolution_factor; - color_params.res_scale_height = Settings::values.resolution_factor; - depth_params.res_scale_height = Settings::values.resolution_factor; + resolution_scale_factor = VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio(); } + // Scale the resolution by the specified factor + color_params.res_scale_width = resolution_scale_factor; + depth_params.res_scale_width = resolution_scale_factor; + color_params.res_scale_height = resolution_scale_factor; + depth_params.res_scale_height = resolution_scale_factor; color_params.addr = config.GetColorBufferPhysicalAddress(); color_params.pixel_format = CachedSurface::PixelFormatFromColorFormat(config.color_format); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3eeb47d33..db53710aa 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -152,12 +152,40 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) { stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1); } -static std::string TexCoord(const PicaShaderConfig& config, int texture_unit) { - if (texture_unit == 2 && config.state.texture2_use_coord1) { - return "texcoord[1]"; +static std::string SampleTexture(const PicaShaderConfig& config, unsigned texture_unit) { + const auto& state = config.state; + switch (texture_unit) { + case 0: + // Only unit 0 respects the texturing type + switch (state.texture0_type) { + case TexturingRegs::TextureConfig::Texture2D: + return "texture(tex[0], texcoord[0])"; + case TexturingRegs::TextureConfig::Projection2D: + return "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))"; + default: + LOG_CRITICAL(HW_GPU, "Unhandled texture type %x", + static_cast<int>(state.texture0_type)); + UNIMPLEMENTED(); + return "texture(tex[0], texcoord[0])"; + } + case 1: + return "texture(tex[1], texcoord[1])"; + case 2: + if (state.texture2_use_coord1) + return "texture(tex[2], texcoord[1])"; + else + return "texture(tex[2], texcoord[2])"; + case 3: + if (state.proctex.enable) { + return "ProcTex()"; + } else { + LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it"); + return "vec4(0.0)"; + } + default: + UNREACHABLE(); + return ""; } - - return "texcoord[" + std::to_string(texture_unit) + "]"; } /// Writes the specified TEV stage source component(s) @@ -176,35 +204,16 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config, out += "secondary_fragment_color"; break; case Source::Texture0: - // Only unit 0 respects the texturing type (according to 3DBrew) - switch (state.texture0_type) { - case TexturingRegs::TextureConfig::Texture2D: - out += "texture(tex[0], texcoord[0])"; - break; - case TexturingRegs::TextureConfig::Projection2D: - out += "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))"; - break; - default: - out += "texture(tex[0], texcoord[0])"; - LOG_CRITICAL(HW_GPU, "Unhandled texture type %x", - static_cast<int>(state.texture0_type)); - UNIMPLEMENTED(); - break; - } + out += SampleTexture(config, 0); break; case Source::Texture1: - out += "texture(tex[1], texcoord[1])"; + out += SampleTexture(config, 1); break; case Source::Texture2: - out += "texture(tex[2], " + TexCoord(config, 2) + ")"; + out += SampleTexture(config, 2); break; case Source::Texture3: - if (config.state.proctex.enable) { - out += "ProcTex()"; - } else { - LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it"); - out += "vec4(0.0)"; - } + out += SampleTexture(config, 3); break; case Source::PreviousBuffer: out += "combiner_buffer"; @@ -515,18 +524,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { // Bump mapping is enabled using a normal map, read perturbation vector from the selected // texture - if (lighting.bump_selector == 3) { - if (config.state.proctex.enable) { - out += "vec3 surface_normal = 2.0 * ProcTex().rgb - 1.0;\n"; - } else { - LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it"); - out += "vec3 surface_normal = vec3(-1.0);\n"; - } - } else { - std::string bump_selector = std::to_string(lighting.bump_selector); - out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], " + - TexCoord(config, lighting.bump_selector) + ").rgb - 1.0;\n"; - } + out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) + + ").rgb - 1.0;\n"; // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher // precision result @@ -545,8 +544,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { } // Rotate the surface-local normal by the interpolated normal quaternion to convert it to - // eyespace - out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n"; + // eyespace. + out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n"; // Gets the index into the specified lookup table for specular lighting auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, @@ -1030,7 +1029,9 @@ uniform sampler1D proctex_diff_lut; // Rotate the vector v by the quaternion q vec3 quaternion_rotate(vec4 q, vec3 v) { return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); -})"; +} + +)"; if (config.state.proctex.enable) AppendProcTexSampler(out, config); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e19375466..d90c776f9 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -94,14 +94,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons return matrix; } -/// RendererOpenGL constructor -RendererOpenGL::RendererOpenGL() { - resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth); - resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight; -} - -/// RendererOpenGL destructor -RendererOpenGL::~RendererOpenGL() {} +RendererOpenGL::RendererOpenGL() = default; +RendererOpenGL::~RendererOpenGL() = default; /// Swap buffers (render frame) void RendererOpenGL::SwapBuffers() { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 87c556cff..0b4f69e8f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -68,9 +68,6 @@ private: EmuWindow* render_window; ///< Handle to render window - int resolution_width; ///< Current resolution width - int resolution_height; ///< Current resolution height - OpenGLState state; // OpenGL object IDs diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index e9edf0360..8b7b1defb 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <array> #include <cmath> +#include <tuple> #include "common/assert.h" #include "common/bit_field.h" #include "common/color.h" @@ -70,6 +71,49 @@ static int SignedArea(const Math::Vec2<Fix12P4>& vtx1, const Math::Vec2<Fix12P4> return Math::Cross(vec1, vec2).z; }; +/// Convert a 3D vector for cube map coordinates to 2D texture coordinates along with the face name +static std::tuple<float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v, float24 w, + const TexturingRegs& regs) { + const float abs_u = std::abs(u.ToFloat32()); + const float abs_v = std::abs(v.ToFloat32()); + const float abs_w = std::abs(w.ToFloat32()); + float24 x, y, z; + PAddr addr; + if (abs_u > abs_v && abs_u > abs_w) { + if (u > float24::FromFloat32(0)) { + addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::PositiveX); + y = -v; + } else { + addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::NegativeX); + y = v; + } + x = -w; + z = u; + } else if (abs_v > abs_w) { + if (v > float24::FromFloat32(0)) { + addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::PositiveY); + x = u; + } else { + addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::NegativeY); + x = -u; + } + y = w; + z = v; + } else { + if (w > float24::FromFloat32(0)) { + addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::PositiveZ); + y = -v; + } else { + addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::NegativeZ); + y = v; + } + x = u; + z = w; + } + const float24 half = float24::FromFloat32(0.5f); + return std::make_tuple(x / z * half + half, y / z * half + half, addr); +} + MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); /** @@ -284,10 +328,16 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve // Only unit 0 respects the texturing type (according to 3DBrew) // TODO: Refactor so cubemaps and shadowmaps can be handled + PAddr texture_address = texture.config.GetPhysicalAddress(); if (i == 0) { switch (texture.config.type) { case TexturingRegs::TextureConfig::Texture2D: break; + case TexturingRegs::TextureConfig::TextureCube: { + auto w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); + std::tie(u, v, texture_address) = ConvertCubeCoord(u, v, w, regs.texturing); + break; + } case TexturingRegs::TextureConfig::Projection2D: { auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); u /= tc0_w; @@ -322,8 +372,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); - u8* texture_data = - Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); + const u8* texture_data = Memory::GetPhysicalPointer(texture_address); auto info = Texture::TextureInfo::FromPicaRegister(texture.config, texture.format); diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 4aba19ca0..94e0867f0 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -15,21 +15,6 @@ class RendererBase; namespace VideoCore { -// 3DS Video Constants -// ------------------- - -// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the -// framebuffers in video memory are stored in column-major order and rendered sideways, causing -// the widths and heights of the framebuffers read by the LCD to be switched compared to the -// heights and widths of the screens listed here. -static const int kScreenTopWidth = 400; ///< 3DS top screen width -static const int kScreenTopHeight = 240; ///< 3DS top screen height -static const int kScreenBottomWidth = 320; ///< 3DS bottom screen width -static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height - -// Video core renderer -// --------------------- - extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin extern EmuWindow* g_emu_window; ///< Emu window |