diff options
99 files changed, 1572 insertions, 981 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/.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 121b0f2f8..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,30 +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) -set(DYNARMIC_NO_BUNDLED_FMT ON) -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 57fc5d566..1e04931ee 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -1,12 +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) - -add_subdirectory(fmt) 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/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 c571213fc..0ad86bb7a 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -27,12 +27,9 @@ 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}) @@ -42,6 +39,6 @@ target_link_libraries(audio_core PUBLIC common core)  target_link_libraries(audio_core PRIVATE SoundTouch)  if(SDL2_FOUND) -    target_link_libraries(audio_core PRIVATE ${SDL2_LIBRARY}) +    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 9eddb342b..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 PRIVATE common core input_common) -target_link_libraries(citra PRIVATE ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) +target_link_libraries(citra PRIVATE inih glad)  if (MSVC)      target_link_libraries(citra PRIVATE getopt)  endif() -target_link_libraries(citra PRIVATE ${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 c0dac9e8f..dd357ff72 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -144,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_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 809e0b938..4841cbf05 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -92,7 +92,7 @@ else()      add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})  endif()  target_link_libraries(citra-qt PRIVATE audio_core common core input_common video_core) -target_link_libraries(citra-qt PRIVATE ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS} glad) +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) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 06b62f44c..a8a4aed8b 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -37,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) 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..06ef58ea7 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" 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 eb2c7d613..4f5b2ddab 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -93,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.")); @@ -108,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;}"); @@ -300,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;      } @@ -327,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: @@ -346,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; @@ -424,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); @@ -530,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")); @@ -622,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 a33a8cdbe..7e83e64b0 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -95,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 PRIVATE xbyak)  endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7aa81e885..6e602b0c5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -45,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 @@ -57,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 @@ -153,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 @@ -236,6 +240,8 @@ set(HEADERS              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 @@ -249,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 @@ -346,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 @@ -374,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 PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PRIVATE cryptopp dynarmic) +target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic) 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_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/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.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 03ffdece1..ce5d94e99 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -6,6 +6,7 @@  #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" @@ -25,20 +26,17 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {      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 783b1c061..6bc49ff64 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -5,6 +5,8 @@  #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"  namespace Kernel { 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/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..0922b3f47 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -0,0 +1,24 @@ +// 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); +} + +} // 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..14f682f44 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.h @@ -0,0 +1,53 @@ +// 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" + +namespace Kernel { + +class ServerSession; + +/** + * 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: +    /** +     * 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; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7f84e01aa..7470a97ca 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -2,11 +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/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/process.h" @@ -18,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 94f2025a0..9cf288b08 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,26 +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; - -enum KernelHandle : Handle { -    CurrentThread = 0xFFFF8000, -    CurrentProcess = 0xFFFF8001, -}; -  enum class HandleType : u32 {      Unknown,      Event, @@ -121,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/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.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..28f365b9e 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,9 +10,8 @@  #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 +19,8 @@ namespace Kernel {  class ClientSession;  class ClientPort;  class ServerSession; +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/thread.cpp b/src/core/hle/kernel/thread.cpp index 519ff51a8..75ce626f8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -15,6 +15,7 @@  #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" diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 7b5169cfc..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 { 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/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 c49650f7d..5f2cdbb96 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -416,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 @@ -426,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 91056189a..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" 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 caa41ded7..5a7878b31 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -13,6 +13,8 @@  #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" @@ -21,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 { 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 79171a0bc..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; 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 632712f2c..3605ef175 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -24,7 +24,11 @@  #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" @@ -83,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) { @@ -161,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;      } @@ -258,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)); 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 e53a970d3..34e1783ec 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -8,8 +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" @@ -18,8 +23,9 @@  ////////////////////////////////////////////////////////////////////////////////////////////////////  // Namespace FS_User -using Kernel::SharedPtr; +using Kernel::ClientSession;  using Kernel::ServerSession; +using Kernel::SharedPtr;  namespace Service {  namespace FS { @@ -77,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()); @@ -130,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; @@ -143,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", @@ -410,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()); diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 94f6b8a9c..6ff0f4812 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -5,7 +5,9 @@  #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" 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 369115f09..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" 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 a82cc9bd3..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" 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/service.cpp b/src/core/hle/service/service.cpp index 0672ac2e3..0d443aa44 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -2,11 +2,11 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include <boost/range/algorithm_ext/erase.hpp> -  #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 +39,15 @@  #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"  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;  /**   * Creates a function string for logging, complete with the name (or header code, depending @@ -66,16 +66,6 @@ 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; @@ -116,24 +106,27 @@ void Interface::Register(const FunctionInfo* functions, size_t n) {  // Module interface  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); +    Kernel::SharedPtr<Kernel::ServerPort> server_port; +    Kernel::SharedPtr<Kernel::ClientPort> client_port; +    std::tie(server_port, client_port) = +        Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName()); + +    server_port->SetHleHandler(std::shared_ptr<Interface>(interface_));      g_kernel_named_ports.emplace(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_unique<SM::ServiceManager>(); +    AddNamedPort(new SM::SRV);      AddNamedPort(new ERR::ERR_F);      FS::ArchiveInit(); @@ -194,7 +187,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..8933d57cc 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -8,21 +8,19 @@  #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 ServerSession;  } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Service -  namespace Service {  static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) @@ -30,48 +28,10 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character  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.   */ -class Interface : public SessionRequestHandler { +class Interface : public Kernel::SessionRequestHandler {  public:      /**       * Creates an HLE interface with the specified max sessions. @@ -149,8 +109,6 @@ 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 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..361f7a0a9 --- /dev/null +++ b/src/core/hle/service/sm/sm.cpp @@ -0,0 +1,59 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <tuple> +#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" + +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; +} + +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(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::unique_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..5fac5455c --- /dev/null +++ b/src/core/hle/service/sm/sm.h @@ -0,0 +1,49 @@ +// 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 { + +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: +    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: +    /// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. +    std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services; +}; + +extern std::unique_ptr<ServiceManager> g_service_manager; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/sm/srv.cpp index 130c9d25e..063b1b0fc 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -6,15 +6,21 @@  #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/event.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/srv.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/srv.h"  namespace Service { -namespace SRV { +namespace SM { -static Kernel::SharedPtr<Kernel::Event> event_handle; +constexpr int MAX_PENDING_NOTIFICATIONS = 16; + +static Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore;  /**   * SRV::RegisterClient service function @@ -51,14 +57,13 @@ static void RegisterClient(Interface* self) {  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(); +    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(event_handle).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom();      LOG_WARNING(Service_SRV, "(STUBBED) called");  } @@ -77,25 +82,41 @@ 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); +    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; -    if (it != Service::g_srv_services.end()) { -        auto client_port = it->second; +    // TODO(yuriks): Permission checks go here -        auto client_session = client_port->Connect(); -        res = client_session.Code(); +    auto client_port = g_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; +    } -        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]); +    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, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); -        res = UnimplementedFunction(ErrorModule::SRV); +        LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), +                  cmd_buff[1]);      } -    cmd_buff[1] = res.raw;  }  /** @@ -177,12 +198,12 @@ const Interface::FunctionInfo FunctionTable[] = {  SRV::SRV() {      Register(FunctionTable); -    event_handle = nullptr; +    notification_semaphore = nullptr;  }  SRV::~SRV() { -    event_handle = nullptr; +    notification_semaphore = nullptr;  } -} // namespace SRV +} // namespace SM  } // namespace Service diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/sm/srv.h index d3a9de879..4196ca1e2 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/sm/srv.h @@ -4,10 +4,11 @@  #pragma once +#include <string>  #include "core/hle/service/service.h"  namespace Service { -namespace SRV { +namespace SM {  /// Interface to "srv:" service  class SRV final : public Interface { @@ -20,5 +21,5 @@ public:      }  }; -} // namespace SRV +} // 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/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 30230d65a..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" @@ -16,6 +17,7 @@  #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" @@ -27,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" @@ -244,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) @@ -299,7 +302,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha      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; 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/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 5b306e42e..e3e36ada7 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -13,7 +13,6 @@ 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}) @@ -22,6 +21,6 @@ add_library(input_common STATIC ${SRCS} ${HEADERS})  target_link_libraries(input_common PUBLIC core PRIVATE common)  if(SDL2_FOUND) -    target_link_libraries(input_common PRIVATE ${SDL2_LIBRARY}) +    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 85f2f2985..00d7c636a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -10,11 +10,9 @@ set(HEADERS  create_directory_groups(${SRCS} ${HEADERS}) -include_directories(../../externals/catch/single_include/) -  add_executable(tests ${SRCS} ${HEADERS})  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} Threads::Threads) +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 e455f03bd..0961a3251 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -80,14 +80,13 @@ create_directory_groups(${SRCS} ${HEADERS})  add_library(video_core STATIC ${SRCS} ${HEADERS})  target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PRIVATE glad) +target_link_libraries(video_core PRIVATE glad nihstro-headers)  if (ARCHITECTURE_x86_64)      target_link_libraries(video_core PRIVATE xbyak)  endif()  if (PNG_FOUND) -    target_link_libraries(video_core PRIVATE ${PNG_LIBRARIES}) -    target_include_directories(video_core PRIVATE ${PNG_INCLUDE_DIRS}) -    target_compile_definitions(video_core PRIVATE ${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/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);  | 
