diff options
188 files changed, 4348 insertions, 1308 deletions
diff --git a/.travis-build.sh b/.travis-build.sh index 8ec2ed70c..22a3a9fd6 100755 --- a/.travis-build.sh +++ b/.travis-build.sh | |||
| @@ -11,8 +11,12 @@ fi | |||
| 11 | 11 | ||
| 12 | #if OS is linux or is not set | 12 | #if OS is linux or is not set |
| 13 | if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then | 13 | if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then |
| 14 | export CC=gcc-4.9 | ||
| 15 | export CXX=g++-4.9 | ||
| 16 | export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH | ||
| 17 | |||
| 14 | mkdir build && cd build | 18 | mkdir build && cd build |
| 15 | cmake -DUSE_QT5=OFF .. | 19 | cmake -DCITRA_FORCE_QT4=ON .. |
| 16 | make -j4 | 20 | make -j4 |
| 17 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then | 21 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then |
| 18 | export Qt5_DIR=$(brew --prefix)/opt/qt5 | 22 | export Qt5_DIR=$(brew --prefix)/opt/qt5 |
diff --git a/.travis-deps.sh b/.travis-deps.sh index b9561bb66..5c530dcb9 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh | |||
| @@ -5,26 +5,26 @@ set -x | |||
| 5 | 5 | ||
| 6 | #if OS is linux or is not set | 6 | #if OS is linux or is not set |
| 7 | if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then | 7 | if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then |
| 8 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y | 8 | export CC=gcc-4.9 |
| 9 | sudo apt-get -qq update | 9 | export CXX=g++-4.9 |
| 10 | sudo apt-get -qq install g++-4.9 xorg-dev libglu1-mesa-dev libxcursor-dev | 10 | mkdir -p $HOME/.local |
| 11 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90 | 11 | |
| 12 | curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ | ||
| 13 | | tar -xz -C $HOME/.local --strip-components=1 | ||
| 14 | |||
| 12 | ( | 15 | ( |
| 13 | git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1 | 16 | git clone https://github.com/glfw/glfw.git --branch 3.1.1 --depth 1 |
| 14 | mkdir glfw/build && cd glfw/build | 17 | mkdir glfw/build && cd glfw/build |
| 15 | cmake -DBUILD_SHARED_LIBS=ON \ | 18 | cmake -DBUILD_SHARED_LIBS=ON \ |
| 16 | -DGLFW_BUILD_EXAMPLES=OFF \ | 19 | -DGLFW_BUILD_EXAMPLES=OFF \ |
| 17 | -DGLFW_BUILD_TESTS=OFF \ | 20 | -DGLFW_BUILD_TESTS=OFF \ |
| 21 | -DCMAKE_INSTALL_PREFIX=$HOME/.local \ | ||
| 18 | .. | 22 | .. |
| 19 | make -j4 && sudo make install | 23 | make -j4 && make install |
| 20 | ) | 24 | ) |
| 21 | 25 | ||
| 22 | sudo apt-get install lib32stdc++6 | ||
| 23 | sudo mkdir -p /usr/local | ||
| 24 | curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ | ||
| 25 | | sudo tar -xz -C /usr/local --strip-components=1 | ||
| 26 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then | 26 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then |
| 27 | brew tap homebrew/versions | 27 | brew update > /dev/null # silence the very verbose output |
| 28 | brew install qt5 glfw3 pkgconfig | 28 | brew install qt5 glfw3 pkgconfig |
| 29 | gem install xcpretty | 29 | gem install xcpretty |
| 30 | fi | 30 | fi |
diff --git a/.travis-upload.sh b/.travis-upload.sh index 0904b646a..3a15e8f6a 100644..100755 --- a/.travis-upload.sh +++ b/.travis-upload.sh | |||
| @@ -7,7 +7,6 @@ if [ "$TRAVIS_BRANCH" = "master" ]; then | |||
| 7 | UPLOAD_DIR="/citra/nightly/linux-amd64" | 7 | UPLOAD_DIR="/citra/nightly/linux-amd64" |
| 8 | mkdir "$REV_NAME" | 8 | mkdir "$REV_NAME" |
| 9 | 9 | ||
| 10 | sudo apt-get -qq install lftp | ||
| 11 | cp build/src/citra/citra "$REV_NAME" | 10 | cp build/src/citra/citra "$REV_NAME" |
| 12 | cp build/src/citra_qt/citra-qt "$REV_NAME" | 11 | cp build/src/citra_qt/citra-qt "$REV_NAME" |
| 13 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then | 12 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then |
diff --git a/.travis.yml b/.travis.yml index 5c882a574..4d21257bc 100644 --- a/.travis.yml +++ b/.travis.yml | |||
| @@ -8,11 +8,21 @@ env: | |||
| 8 | global: | 8 | global: |
| 9 | - secure: "AXHFIafTmbGDsHD3mUVj5a4I397DQjti/WoqAJGUp2PglxTcc04BwxZ9Z+xLuf5N2Hs5r9ojAJLT8OGxJCLBDXzneQTNSqXbFuYSLbqrEAiIRlA9eRIotWCg+wYcO+5e8MKX+cHVKwiIWasUB21AtCdq6msh6Y3pUshZp212VPg=" | 9 | - secure: "AXHFIafTmbGDsHD3mUVj5a4I397DQjti/WoqAJGUp2PglxTcc04BwxZ9Z+xLuf5N2Hs5r9ojAJLT8OGxJCLBDXzneQTNSqXbFuYSLbqrEAiIRlA9eRIotWCg+wYcO+5e8MKX+cHVKwiIWasUB21AtCdq6msh6Y3pUshZp212VPg=" |
| 10 | 10 | ||
| 11 | before_install: | 11 | sudo: false |
| 12 | - sh .travis-deps.sh | ||
| 13 | 12 | ||
| 14 | script: | 13 | addons: |
| 15 | - sh .travis-build.sh | 14 | apt: |
| 15 | sources: | ||
| 16 | - ubuntu-toolchain-r-test | ||
| 17 | packages: | ||
| 18 | - gcc-4.9 | ||
| 19 | - g++-4.9 | ||
| 20 | - xorg-dev | ||
| 21 | - libglu1-mesa-dev | ||
| 22 | - libxcursor-dev | ||
| 23 | - lib32stdc++6 # For CMake | ||
| 24 | - lftp # To upload builds | ||
| 16 | 25 | ||
| 17 | after_success: | 26 | install: ./.travis-deps.sh |
| 18 | - sh .travis-upload.sh | 27 | script: ./.travis-build.sh |
| 28 | after_success: ./.travis-upload.sh | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6805ebed8..e945a6679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -7,8 +7,7 @@ project(citra) | |||
| 7 | if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit) | 7 | if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit) |
| 8 | message(STATUS "Copying pre-commit hook") | 8 | message(STATUS "Copying pre-commit hook") |
| 9 | file(COPY hooks/pre-commit | 9 | file(COPY hooks/pre-commit |
| 10 | DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks | 10 | DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks) |
| 11 | FILE_PERMISSIONS WORLD_EXECUTE ) | ||
| 12 | endif() | 11 | endif() |
| 13 | 12 | ||
| 14 | if (NOT MSVC) | 13 | if (NOT MSVC) |
| @@ -22,32 +21,34 @@ else() | |||
| 22 | 21 | ||
| 23 | # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) | 22 | # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) |
| 24 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | 23 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) |
| 25 | set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE STRING "" FORCE) | 24 | set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) |
| 26 | 25 | ||
| 27 | # Tweak optimization settings | 26 | # Tweak optimization settings |
| 28 | # As far as I can tell, there's no way to override the CMake defaults while leaving user | 27 | # As far as I can tell, there's no way to override the CMake defaults while leaving user |
| 29 | # changes intact, so we'll just clobber everything and say sorry. | 28 | # changes intact, so we'll just clobber everything and say sorry. |
| 30 | message(STATUS "Cache compiler flags ignored, please edit CMakeLists.txt to change the flags.") | 29 | message(STATUS "Cache compiler flags ignored, please edit CMakeLists.txt to change the flags.") |
| 31 | # /O2 - Optimization level 2 | 30 | |
| 32 | # /Oy- - Don't omit frame pointer | 31 | # /W3 - Level 3 warnings |
| 33 | # /GR- - Disable RTTI | ||
| 34 | # /GS- - No stack buffer overflow checks | ||
| 35 | # /EHsc - C++-only exception handling semantics | ||
| 36 | set(optimization_flags "/O2 /Oy- /GR- /GS- /EHsc") | ||
| 37 | # /MP - Multi-threaded compilation | 32 | # /MP - Multi-threaded compilation |
| 38 | # /Zi - Output debugging information | 33 | # /Zi - Output debugging information |
| 39 | # /Zo - enahnced debug info for optimized builds | 34 | # /Zo - enahnced debug info for optimized builds |
| 35 | set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo" CACHE STRING "" FORCE) | ||
| 36 | # /GR- - Disable RTTI | ||
| 37 | # /EHsc - C++-only exception handling semantics | ||
| 38 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /GR- /EHsc" CACHE STRING "" FORCE) | ||
| 39 | |||
| 40 | # /MDd - Multi-threaded Debug Runtime DLL | 40 | # /MDd - Multi-threaded Debug Runtime DLL |
| 41 | set(CMAKE_C_FLAGS_DEBUG "/MP /MDd /Zi" CACHE STRING "" FORCE) | 41 | set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE) |
| 42 | set(CMAKE_CXX_FLAGS_DEBUG "/MP /MDd /Zi" CACHE STRING "" FORCE) | 42 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}" CACHE STRING "" FORCE) |
| 43 | |||
| 44 | # /O2 - Optimization level 2 | ||
| 45 | # /GS- - No stack buffer overflow checks | ||
| 43 | # /MD - Multi-threaded runtime DLL | 46 | # /MD - Multi-threaded runtime DLL |
| 44 | set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /MP /MD" CACHE STRING "" FORCE) | 47 | set(CMAKE_C_FLAGS_RELEASE "/O2 /GS- /MD" CACHE STRING "" FORCE) |
| 45 | set(CMAKE_CXX_FLAGS_RELEASE "${optimization_flags} /MP /MD" CACHE STRING "" FORCE) | 48 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "" FORCE) |
| 46 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "${optimization_flags} /MP /MD /Zi /Zo" CACHE STRING "" FORCE) | ||
| 47 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${optimization_flags} /MP /MD /Zi /Zo" CACHE STRING "" FORCE) | ||
| 48 | 49 | ||
| 49 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG" CACHE STRING "" FORCE) | 50 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG" CACHE STRING "" FORCE) |
| 50 | set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG" CACHE STRING "" FORCE) | 51 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG" CACHE STRING "" FORCE) |
| 51 | endif() | 52 | endif() |
| 52 | 53 | ||
| 53 | add_definitions(-DSINGLETHREADED) | 54 | add_definitions(-DSINGLETHREADED) |
| @@ -201,6 +202,10 @@ add_subdirectory(${INI_PREFIX}) | |||
| 201 | 202 | ||
| 202 | include_directories(externals/nihstro/include) | 203 | include_directories(externals/nihstro/include) |
| 203 | 204 | ||
| 205 | if (MSVC) | ||
| 206 | add_subdirectory(externals/getopt) | ||
| 207 | endif() | ||
| 208 | |||
| 204 | # process subdirectories | 209 | # process subdirectories |
| 205 | if(ENABLE_QT) | 210 | if(ENABLE_QT) |
| 206 | include_directories(externals/qhexedit) | 211 | include_directories(externals/qhexedit) |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 906a4bc7d..f2dbdf1a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
| @@ -7,19 +7,14 @@ Citra is a brand new project, so we have a great opportunity to keep things clea | |||
| 7 | * Don't ever introduce new external dependencies into Core | 7 | * Don't ever introduce new external dependencies into Core |
| 8 | * Don't use any platform specific code in Core | 8 | * Don't use any platform specific code in Core |
| 9 | * Use namespaces often | 9 | * Use namespaces often |
| 10 | * Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Never use `const_cast` or `dynamic_cast` (we build with RTTI disabled). The only exception to this rule is for casting between two numeric types, where C-style casts are encouraged for brevity and readability. | ||
| 10 | 11 | ||
| 11 | ### Naming Rules | 12 | ### Naming Rules |
| 12 | * Functions | 13 | * Functions: `PascalCase` |
| 13 | * PascalCase, "_" may also be used for clarity (e.g. ARM_InitCore) | 14 | * Variables: `lower_case_underscored`. Prefix with `g_` if global. |
| 14 | * Variables | 15 | * Classes: `PascalCase` |
| 15 | * lower_case_underscored | 16 | * Files and Directories: `lower_case_underscored` |
| 16 | * Prefix "g_" if global | 17 | * Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`) |
| 17 | * Classes | ||
| 18 | * PascalCase, "_" may also be used for clarity (e.g. OGL_VideoInterface) | ||
| 19 | * Files/Folders | ||
| 20 | * lower_case_underscored | ||
| 21 | * Namespaces | ||
| 22 | * PascalCase, "_" may also be used for clarity (e.g. ARM_InitCore) | ||
| 23 | 18 | ||
| 24 | ### Indentation/Whitespace Style | 19 | ### Indentation/Whitespace Style |
| 25 | Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. | 20 | Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. |
| @@ -36,25 +31,25 @@ namespace Example { | |||
| 36 | 31 | ||
| 37 | // Declare globals at the top | 32 | // Declare globals at the top |
| 38 | int g_foo = 0; | 33 | int g_foo = 0; |
| 39 | char* g_some_pointer; // Notice the position of the * | 34 | char* g_some_pointer; // Pointer * and reference & stick to the type name |
| 40 | 35 | ||
| 41 | /// A colorful enum. | 36 | /// A colorful enum. |
| 42 | enum SomeEnum { | 37 | enum SomeEnum { |
| 43 | COLOR_RED, ///< The color of fire. | 38 | COLOR_RED, ///< The color of fire. |
| 44 | COLOR_GREEN, ///< The color of grass. | 39 | COLOR_GREEN, ///< The color of grass. |
| 45 | COLOR_BLUE ///< Not actually the color of water. | 40 | COLOR_BLUE, ///< Not actually the color of water. |
| 46 | }; | 41 | }; |
| 47 | 42 | ||
| 48 | /** | 43 | /** |
| 49 | * Very important struct that does a lot of stuff. | 44 | * Very important struct that does a lot of stuff. |
| 50 | * Note that the asterisks are indented by one space. | 45 | * Note that the asterisks are indented by one space to align to the first line. |
| 51 | */ | 46 | */ |
| 52 | struct Position { | 47 | struct Position { |
| 53 | int x, y; | 48 | int x, y; |
| 54 | }; | 49 | }; |
| 55 | 50 | ||
| 56 | // Use "typename" rather than "class" here, just to be consistent | 51 | // Use "typename" rather than "class" here |
| 57 | template | 52 | template <typename T> |
| 58 | void FooBar() { | 53 | void FooBar() { |
| 59 | int some_array[] = { | 54 | int some_array[] = { |
| 60 | 5, | 55 | 5, |
| @@ -72,7 +67,7 @@ void FooBar() { | |||
| 72 | // Comment directly above code when possible | 67 | // Comment directly above code when possible |
| 73 | if (some_condition) single_statement(); | 68 | if (some_condition) single_statement(); |
| 74 | 69 | ||
| 75 | // Place a single space after the for loop semicolons | 70 | // Place a single space after the for loop semicolons, prefer pre-increment |
| 76 | for (int i = 0; i != 25; ++i) { | 71 | for (int i = 0; i != 25; ++i) { |
| 77 | // This is how we write loops | 72 | // This is how we write loops |
| 78 | } | 73 | } |
| @@ -83,6 +78,9 @@ void FooBar() { | |||
| 83 | if (this || condition_takes_up_multiple && | 78 | if (this || condition_takes_up_multiple && |
| 84 | lines && like && this || everything || | 79 | lines && like && this || everything || |
| 85 | alright || then) { | 80 | alright || then) { |
| 81 | |||
| 82 | // Leave a blank space before the if block body if the condition was continued across | ||
| 83 | // several lines. | ||
| 86 | } | 84 | } |
| 87 | 85 | ||
| 88 | switch (var) { | 86 | switch (var) { |
| @@ -101,11 +99,7 @@ void FooBar() { | |||
| 101 | break; | 99 | break; |
| 102 | } | 100 | } |
| 103 | 101 | ||
| 104 | std::vector | 102 | std::vector<T> you_can_declare, a_few, variables, like_this; |
| 105 | you_can_declare, | ||
| 106 | a_few, | ||
| 107 | variables, | ||
| 108 | like_this; | ||
| 109 | } | 103 | } |
| 110 | 104 | ||
| 111 | } | 105 | } |
| @@ -7,7 +7,7 @@ Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C | |||
| 7 | 7 | ||
| 8 | Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project. | 8 | Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project. |
| 9 | 9 | ||
| 10 | For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra). | 10 | For development discussion, please join us @ #citra on freenode. |
| 11 | 11 | ||
| 12 | ### Development | 12 | ### Development |
| 13 | 13 | ||
diff --git a/appveyor.yml b/appveyor.yml index 7e9155e6d..5dc147639 100644 --- a/appveyor.yml +++ b/appveyor.yml | |||
| @@ -13,7 +13,7 @@ configuration: | |||
| 13 | - Release | 13 | - Release |
| 14 | 14 | ||
| 15 | install: | 15 | install: |
| 16 | - git submodule update --init --recursive --depth 20 | 16 | - git submodule update --init --recursive |
| 17 | 17 | ||
| 18 | before_build: | 18 | before_build: |
| 19 | - mkdir build | 19 | - mkdir build |
diff --git a/externals/getopt/CMakeLists.txt b/externals/getopt/CMakeLists.txt new file mode 100644 index 000000000..c8b745d55 --- /dev/null +++ b/externals/getopt/CMakeLists.txt | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | set(SRCS | ||
| 2 | getopt.c | ||
| 3 | ) | ||
| 4 | set(HEADERS | ||
| 5 | getopt.h | ||
| 6 | ) | ||
| 7 | |||
| 8 | create_directory_groups(${SRCS} ${HEADERS}) | ||
| 9 | add_library(getopt ${SRCS} ${HEADERS}) | ||
| 10 | target_compile_definitions(getopt PUBLIC STATIC_GETOPT) | ||
| 11 | target_include_directories(getopt INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
diff --git a/externals/getopt/getopt.c b/externals/getopt/getopt.c new file mode 100644 index 000000000..948d42683 --- /dev/null +++ b/externals/getopt/getopt.c | |||
| @@ -0,0 +1,962 @@ | |||
| 1 | /* Getopt for Microsoft C | ||
| 2 | This code is a modification of the Free Software Foundation, Inc. | ||
| 3 | Getopt library for parsing command line argument the purpose was | ||
| 4 | to provide a Microsoft Visual C friendly derivative. This code | ||
| 5 | provides functionality for both Unicode and Multibyte builds. | ||
| 6 | |||
| 7 | Date: 02/03/2011 - Ludvik Jerabek - Initial Release | ||
| 8 | Version: 1.0 | ||
| 9 | Comment: Supports getopt, getopt_long, and getopt_long_only | ||
| 10 | and POSIXLY_CORRECT environment flag | ||
| 11 | License: LGPL | ||
| 12 | |||
| 13 | Revisions: | ||
| 14 | |||
| 15 | 02/03/2011 - Ludvik Jerabek - Initial Release | ||
| 16 | 02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4 | ||
| 17 | 07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs | ||
| 18 | 08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception | ||
| 19 | 08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB | ||
| 20 | 02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file | ||
| 21 | 08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi | ||
| 22 | 10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features | ||
| 23 | 06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable | ||
| 24 | |||
| 25 | **DISCLAIMER** | ||
| 26 | THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, | ||
| 27 | EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE | ||
| 28 | IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | ||
| 29 | PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE | ||
| 30 | EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT | ||
| 31 | APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY | ||
| 32 | DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY | ||
| 33 | USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST | ||
| 34 | PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON | ||
| 35 | YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE | ||
| 36 | EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | ||
| 37 | */ | ||
| 38 | #include <stdlib.h> | ||
| 39 | #include <stdio.h> | ||
| 40 | #ifdef _MSC_VER | ||
| 41 | #include <malloc.h> | ||
| 42 | #endif | ||
| 43 | #include "getopt.h" | ||
| 44 | |||
| 45 | #ifdef __cplusplus | ||
| 46 | #define _GETOPT_THROW throw() | ||
| 47 | #else | ||
| 48 | #define _GETOPT_THROW | ||
| 49 | #endif | ||
| 50 | |||
| 51 | int optind = 1; | ||
| 52 | int opterr = 1; | ||
| 53 | int optopt = '?'; | ||
| 54 | enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER }; | ||
| 55 | |||
| 56 | |||
| 57 | static struct _getopt_data_a | ||
| 58 | { | ||
| 59 | int optind; | ||
| 60 | int opterr; | ||
| 61 | int optopt; | ||
| 62 | char *optarg; | ||
| 63 | int __initialized; | ||
| 64 | char *__nextchar; | ||
| 65 | enum ENUM_ORDERING __ordering; | ||
| 66 | int __posixly_correct; | ||
| 67 | int __first_nonopt; | ||
| 68 | int __last_nonopt; | ||
| 69 | } getopt_data_a; | ||
| 70 | char *optarg_a; | ||
| 71 | |||
| 72 | static void exchange_a(char **argv, struct _getopt_data_a *d) | ||
| 73 | { | ||
| 74 | int bottom = d->__first_nonopt; | ||
| 75 | int middle = d->__last_nonopt; | ||
| 76 | int top = d->optind; | ||
| 77 | char *tem; | ||
| 78 | while (top > middle && middle > bottom) | ||
| 79 | { | ||
| 80 | if (top - middle > middle - bottom) | ||
| 81 | { | ||
| 82 | int len = middle - bottom; | ||
| 83 | register int i; | ||
| 84 | for (i = 0; i < len; i++) | ||
| 85 | { | ||
| 86 | tem = argv[bottom + i]; | ||
| 87 | argv[bottom + i] = argv[top - (middle - bottom) + i]; | ||
| 88 | argv[top - (middle - bottom) + i] = tem; | ||
| 89 | } | ||
| 90 | top -= len; | ||
| 91 | } | ||
| 92 | else | ||
| 93 | { | ||
| 94 | int len = top - middle; | ||
| 95 | register int i; | ||
| 96 | for (i = 0; i < len; i++) | ||
| 97 | { | ||
| 98 | tem = argv[bottom + i]; | ||
| 99 | argv[bottom + i] = argv[middle + i]; | ||
| 100 | argv[middle + i] = tem; | ||
| 101 | } | ||
| 102 | bottom += len; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | d->__first_nonopt += (d->optind - d->__last_nonopt); | ||
| 106 | d->__last_nonopt = d->optind; | ||
| 107 | } | ||
| 108 | static const char *_getopt_initialize_a(const char *optstring, struct _getopt_data_a *d, int posixly_correct) | ||
| 109 | { | ||
| 110 | d->__first_nonopt = d->__last_nonopt = d->optind; | ||
| 111 | d->__nextchar = NULL; | ||
| 112 | d->__posixly_correct = posixly_correct | !!getenv("POSIXLY_CORRECT"); | ||
| 113 | if (optstring[0] == '-') | ||
| 114 | { | ||
| 115 | d->__ordering = RETURN_IN_ORDER; | ||
| 116 | ++optstring; | ||
| 117 | } | ||
| 118 | else if (optstring[0] == '+') | ||
| 119 | { | ||
| 120 | d->__ordering = REQUIRE_ORDER; | ||
| 121 | ++optstring; | ||
| 122 | } | ||
| 123 | else if (d->__posixly_correct) | ||
| 124 | d->__ordering = REQUIRE_ORDER; | ||
| 125 | else | ||
| 126 | d->__ordering = PERMUTE; | ||
| 127 | return optstring; | ||
| 128 | } | ||
| 129 | int _getopt_internal_r_a(int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct) | ||
| 130 | { | ||
| 131 | int print_errors = d->opterr; | ||
| 132 | if (argc < 1) | ||
| 133 | return -1; | ||
| 134 | d->optarg = NULL; | ||
| 135 | if (d->optind == 0 || !d->__initialized) | ||
| 136 | { | ||
| 137 | if (d->optind == 0) | ||
| 138 | d->optind = 1; | ||
| 139 | optstring = _getopt_initialize_a(optstring, d, posixly_correct); | ||
| 140 | d->__initialized = 1; | ||
| 141 | } | ||
| 142 | else if (optstring[0] == '-' || optstring[0] == '+') | ||
| 143 | optstring++; | ||
| 144 | if (optstring[0] == ':') | ||
| 145 | print_errors = 0; | ||
| 146 | if (d->__nextchar == NULL || *d->__nextchar == '\0') | ||
| 147 | { | ||
| 148 | if (d->__last_nonopt > d->optind) | ||
| 149 | d->__last_nonopt = d->optind; | ||
| 150 | if (d->__first_nonopt > d->optind) | ||
| 151 | d->__first_nonopt = d->optind; | ||
| 152 | if (d->__ordering == PERMUTE) | ||
| 153 | { | ||
| 154 | if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) | ||
| 155 | exchange_a((char **)argv, d); | ||
| 156 | else if (d->__last_nonopt != d->optind) | ||
| 157 | d->__first_nonopt = d->optind; | ||
| 158 | while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')) | ||
| 159 | d->optind++; | ||
| 160 | d->__last_nonopt = d->optind; | ||
| 161 | } | ||
| 162 | if (d->optind != argc && !strcmp(argv[d->optind], "--")) | ||
| 163 | { | ||
| 164 | d->optind++; | ||
| 165 | if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) | ||
| 166 | exchange_a((char **)argv, d); | ||
| 167 | else if (d->__first_nonopt == d->__last_nonopt) | ||
| 168 | d->__first_nonopt = d->optind; | ||
| 169 | d->__last_nonopt = argc; | ||
| 170 | d->optind = argc; | ||
| 171 | } | ||
| 172 | if (d->optind == argc) | ||
| 173 | { | ||
| 174 | if (d->__first_nonopt != d->__last_nonopt) | ||
| 175 | d->optind = d->__first_nonopt; | ||
| 176 | return -1; | ||
| 177 | } | ||
| 178 | if ((argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')) | ||
| 179 | { | ||
| 180 | if (d->__ordering == REQUIRE_ORDER) | ||
| 181 | return -1; | ||
| 182 | d->optarg = argv[d->optind++]; | ||
| 183 | return 1; | ||
| 184 | } | ||
| 185 | d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == '-')); | ||
| 186 | } | ||
| 187 | if (longopts != NULL && (argv[d->optind][1] == '-' || (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1]))))) | ||
| 188 | { | ||
| 189 | char *nameend; | ||
| 190 | unsigned int namelen; | ||
| 191 | const struct option_a *p; | ||
| 192 | const struct option_a *pfound = NULL; | ||
| 193 | struct option_list | ||
| 194 | { | ||
| 195 | const struct option_a *p; | ||
| 196 | struct option_list *next; | ||
| 197 | } *ambig_list = NULL; | ||
| 198 | int exact = 0; | ||
| 199 | int indfound = -1; | ||
| 200 | int option_index; | ||
| 201 | for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++); | ||
| 202 | namelen = (unsigned int)(nameend - d->__nextchar); | ||
| 203 | for (p = longopts, option_index = 0; p->name; p++, option_index++) | ||
| 204 | if (!strncmp(p->name, d->__nextchar, namelen)) | ||
| 205 | { | ||
| 206 | if (namelen == (unsigned int)strlen(p->name)) | ||
| 207 | { | ||
| 208 | pfound = p; | ||
| 209 | indfound = option_index; | ||
| 210 | exact = 1; | ||
| 211 | break; | ||
| 212 | } | ||
| 213 | else if (pfound == NULL) | ||
| 214 | { | ||
| 215 | pfound = p; | ||
| 216 | indfound = option_index; | ||
| 217 | } | ||
| 218 | else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) | ||
| 219 | { | ||
| 220 | struct option_list *newp = (struct option_list*)alloca(sizeof(*newp)); | ||
| 221 | newp->p = p; | ||
| 222 | newp->next = ambig_list; | ||
| 223 | ambig_list = newp; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | if (ambig_list != NULL && !exact) | ||
| 227 | { | ||
| 228 | if (print_errors) | ||
| 229 | { | ||
| 230 | struct option_list first; | ||
| 231 | first.p = pfound; | ||
| 232 | first.next = ambig_list; | ||
| 233 | ambig_list = &first; | ||
| 234 | fprintf(stderr, "%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]); | ||
| 235 | do | ||
| 236 | { | ||
| 237 | fprintf(stderr, " '--%s'", ambig_list->p->name); | ||
| 238 | ambig_list = ambig_list->next; | ||
| 239 | } while (ambig_list != NULL); | ||
| 240 | fputc('\n', stderr); | ||
| 241 | } | ||
| 242 | d->__nextchar += strlen(d->__nextchar); | ||
| 243 | d->optind++; | ||
| 244 | d->optopt = 0; | ||
| 245 | return '?'; | ||
| 246 | } | ||
| 247 | if (pfound != NULL) | ||
| 248 | { | ||
| 249 | option_index = indfound; | ||
| 250 | d->optind++; | ||
| 251 | if (*nameend) | ||
| 252 | { | ||
| 253 | if (pfound->has_arg) | ||
| 254 | d->optarg = nameend + 1; | ||
| 255 | else | ||
| 256 | { | ||
| 257 | if (print_errors) | ||
| 258 | { | ||
| 259 | if (argv[d->optind - 1][1] == '-') | ||
| 260 | { | ||
| 261 | fprintf(stderr, "%s: option '--%s' doesn't allow an argument\n", argv[0], pfound->name); | ||
| 262 | } | ||
| 263 | else | ||
| 264 | { | ||
| 265 | fprintf(stderr, "%s: option '%c%s' doesn't allow an argument\n", argv[0], argv[d->optind - 1][0], pfound->name); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | d->__nextchar += strlen(d->__nextchar); | ||
| 269 | d->optopt = pfound->val; | ||
| 270 | return '?'; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | else if (pfound->has_arg == 1) | ||
| 274 | { | ||
| 275 | if (d->optind < argc) | ||
| 276 | d->optarg = argv[d->optind++]; | ||
| 277 | else | ||
| 278 | { | ||
| 279 | if (print_errors) | ||
| 280 | { | ||
| 281 | fprintf(stderr, "%s: option '--%s' requires an argument\n", argv[0], pfound->name); | ||
| 282 | } | ||
| 283 | d->__nextchar += strlen(d->__nextchar); | ||
| 284 | d->optopt = pfound->val; | ||
| 285 | return optstring[0] == ':' ? ':' : '?'; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | d->__nextchar += strlen(d->__nextchar); | ||
| 289 | if (longind != NULL) | ||
| 290 | *longind = option_index; | ||
| 291 | if (pfound->flag) | ||
| 292 | { | ||
| 293 | *(pfound->flag) = pfound->val; | ||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | return pfound->val; | ||
| 297 | } | ||
| 298 | if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL) | ||
| 299 | { | ||
| 300 | if (print_errors) | ||
| 301 | { | ||
| 302 | if (argv[d->optind][1] == '-') | ||
| 303 | { | ||
| 304 | fprintf(stderr, "%s: unrecognized option '--%s'\n", argv[0], d->__nextchar); | ||
| 305 | } | ||
| 306 | else | ||
| 307 | { | ||
| 308 | fprintf(stderr, "%s: unrecognized option '%c%s'\n", argv[0], argv[d->optind][0], d->__nextchar); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | d->__nextchar = (char *)""; | ||
| 312 | d->optind++; | ||
| 313 | d->optopt = 0; | ||
| 314 | return '?'; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | { | ||
| 318 | char c = *d->__nextchar++; | ||
| 319 | char *temp = (char*)strchr(optstring, c); | ||
| 320 | if (*d->__nextchar == '\0') | ||
| 321 | ++d->optind; | ||
| 322 | if (temp == NULL || c == ':' || c == ';') | ||
| 323 | { | ||
| 324 | if (print_errors) | ||
| 325 | { | ||
| 326 | fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c); | ||
| 327 | } | ||
| 328 | d->optopt = c; | ||
| 329 | return '?'; | ||
| 330 | } | ||
| 331 | if (temp[0] == 'W' && temp[1] == ';') | ||
| 332 | { | ||
| 333 | char *nameend; | ||
| 334 | const struct option_a *p; | ||
| 335 | const struct option_a *pfound = NULL; | ||
| 336 | int exact = 0; | ||
| 337 | int ambig = 0; | ||
| 338 | int indfound = 0; | ||
| 339 | int option_index; | ||
| 340 | if (longopts == NULL) | ||
| 341 | goto no_longs; | ||
| 342 | if (*d->__nextchar != '\0') | ||
| 343 | { | ||
| 344 | d->optarg = d->__nextchar; | ||
| 345 | d->optind++; | ||
| 346 | } | ||
| 347 | else if (d->optind == argc) | ||
| 348 | { | ||
| 349 | if (print_errors) | ||
| 350 | { | ||
| 351 | fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], c); | ||
| 352 | } | ||
| 353 | d->optopt = c; | ||
| 354 | if (optstring[0] == ':') | ||
| 355 | c = ':'; | ||
| 356 | else | ||
| 357 | c = '?'; | ||
| 358 | return c; | ||
| 359 | } | ||
| 360 | else | ||
| 361 | d->optarg = argv[d->optind++]; | ||
| 362 | for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; nameend++); | ||
| 363 | for (p = longopts, option_index = 0; p->name; p++, option_index++) | ||
| 364 | if (!strncmp(p->name, d->__nextchar, nameend - d->__nextchar)) | ||
| 365 | { | ||
| 366 | if ((unsigned int)(nameend - d->__nextchar) == strlen(p->name)) | ||
| 367 | { | ||
| 368 | pfound = p; | ||
| 369 | indfound = option_index; | ||
| 370 | exact = 1; | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | else if (pfound == NULL) | ||
| 374 | { | ||
| 375 | pfound = p; | ||
| 376 | indfound = option_index; | ||
| 377 | } | ||
| 378 | else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) | ||
| 379 | ambig = 1; | ||
| 380 | } | ||
| 381 | if (ambig && !exact) | ||
| 382 | { | ||
| 383 | if (print_errors) | ||
| 384 | { | ||
| 385 | fprintf(stderr, "%s: option '-W %s' is ambiguous\n", argv[0], d->optarg); | ||
| 386 | } | ||
| 387 | d->__nextchar += strlen(d->__nextchar); | ||
| 388 | d->optind++; | ||
| 389 | return '?'; | ||
| 390 | } | ||
| 391 | if (pfound != NULL) | ||
| 392 | { | ||
| 393 | option_index = indfound; | ||
| 394 | if (*nameend) | ||
| 395 | { | ||
| 396 | if (pfound->has_arg) | ||
| 397 | d->optarg = nameend + 1; | ||
| 398 | else | ||
| 399 | { | ||
| 400 | if (print_errors) | ||
| 401 | { | ||
| 402 | fprintf(stderr, "%s: option '-W %s' doesn't allow an argument\n", argv[0], pfound->name); | ||
| 403 | } | ||
| 404 | d->__nextchar += strlen(d->__nextchar); | ||
| 405 | return '?'; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | else if (pfound->has_arg == 1) | ||
| 409 | { | ||
| 410 | if (d->optind < argc) | ||
| 411 | d->optarg = argv[d->optind++]; | ||
| 412 | else | ||
| 413 | { | ||
| 414 | if (print_errors) | ||
| 415 | { | ||
| 416 | fprintf(stderr, "%s: option '-W %s' requires an argument\n", argv[0], pfound->name); | ||
| 417 | } | ||
| 418 | d->__nextchar += strlen(d->__nextchar); | ||
| 419 | return optstring[0] == ':' ? ':' : '?'; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | else | ||
| 423 | d->optarg = NULL; | ||
| 424 | d->__nextchar += strlen(d->__nextchar); | ||
| 425 | if (longind != NULL) | ||
| 426 | *longind = option_index; | ||
| 427 | if (pfound->flag) | ||
| 428 | { | ||
| 429 | *(pfound->flag) = pfound->val; | ||
| 430 | return 0; | ||
| 431 | } | ||
| 432 | return pfound->val; | ||
| 433 | } | ||
| 434 | no_longs: | ||
| 435 | d->__nextchar = NULL; | ||
| 436 | return 'W'; | ||
| 437 | } | ||
| 438 | if (temp[1] == ':') | ||
| 439 | { | ||
| 440 | if (temp[2] == ':') | ||
| 441 | { | ||
| 442 | if (*d->__nextchar != '\0') | ||
| 443 | { | ||
| 444 | d->optarg = d->__nextchar; | ||
| 445 | d->optind++; | ||
| 446 | } | ||
| 447 | else | ||
| 448 | d->optarg = NULL; | ||
| 449 | d->__nextchar = NULL; | ||
| 450 | } | ||
| 451 | else | ||
| 452 | { | ||
| 453 | if (*d->__nextchar != '\0') | ||
| 454 | { | ||
| 455 | d->optarg = d->__nextchar; | ||
| 456 | d->optind++; | ||
| 457 | } | ||
| 458 | else if (d->optind == argc) | ||
| 459 | { | ||
| 460 | if (print_errors) | ||
| 461 | { | ||
| 462 | fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], c); | ||
| 463 | } | ||
| 464 | d->optopt = c; | ||
| 465 | if (optstring[0] == ':') | ||
| 466 | c = ':'; | ||
| 467 | else | ||
| 468 | c = '?'; | ||
| 469 | } | ||
| 470 | else | ||
| 471 | d->optarg = argv[d->optind++]; | ||
| 472 | d->__nextchar = NULL; | ||
| 473 | } | ||
| 474 | } | ||
| 475 | return c; | ||
| 476 | } | ||
| 477 | } | ||
| 478 | int _getopt_internal_a(int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct) | ||
| 479 | { | ||
| 480 | int result; | ||
| 481 | getopt_data_a.optind = optind; | ||
| 482 | getopt_data_a.opterr = opterr; | ||
| 483 | result = _getopt_internal_r_a(argc, argv, optstring, longopts, longind, long_only, &getopt_data_a, posixly_correct); | ||
| 484 | optind = getopt_data_a.optind; | ||
| 485 | optarg_a = getopt_data_a.optarg; | ||
| 486 | optopt = getopt_data_a.optopt; | ||
| 487 | return result; | ||
| 488 | } | ||
| 489 | int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW | ||
| 490 | { | ||
| 491 | return _getopt_internal_a(argc, argv, optstring, (const struct option_a *) 0, (int *)0, 0, 0); | ||
| 492 | } | ||
| 493 | int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW | ||
| 494 | { | ||
| 495 | return _getopt_internal_a(argc, argv, options, long_options, opt_index, 0, 0); | ||
| 496 | } | ||
| 497 | int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW | ||
| 498 | { | ||
| 499 | return _getopt_internal_a(argc, argv, options, long_options, opt_index, 1, 0); | ||
| 500 | } | ||
| 501 | int _getopt_long_r_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d) | ||
| 502 | { | ||
| 503 | return _getopt_internal_r_a(argc, argv, options, long_options, opt_index, 0, d, 0); | ||
| 504 | } | ||
| 505 | int _getopt_long_only_r_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d) | ||
| 506 | { | ||
| 507 | return _getopt_internal_r_a(argc, argv, options, long_options, opt_index, 1, d, 0); | ||
| 508 | } | ||
| 509 | |||
| 510 | |||
| 511 | static struct _getopt_data_w | ||
| 512 | { | ||
| 513 | int optind; | ||
| 514 | int opterr; | ||
| 515 | int optopt; | ||
| 516 | wchar_t *optarg; | ||
| 517 | int __initialized; | ||
| 518 | wchar_t *__nextchar; | ||
| 519 | enum ENUM_ORDERING __ordering; | ||
| 520 | int __posixly_correct; | ||
| 521 | int __first_nonopt; | ||
| 522 | int __last_nonopt; | ||
| 523 | } getopt_data_w; | ||
| 524 | wchar_t *optarg_w; | ||
| 525 | |||
| 526 | static void exchange_w(wchar_t **argv, struct _getopt_data_w *d) | ||
| 527 | { | ||
| 528 | int bottom = d->__first_nonopt; | ||
| 529 | int middle = d->__last_nonopt; | ||
| 530 | int top = d->optind; | ||
| 531 | wchar_t *tem; | ||
| 532 | while (top > middle && middle > bottom) | ||
| 533 | { | ||
| 534 | if (top - middle > middle - bottom) | ||
| 535 | { | ||
| 536 | int len = middle - bottom; | ||
| 537 | register int i; | ||
| 538 | for (i = 0; i < len; i++) | ||
| 539 | { | ||
| 540 | tem = argv[bottom + i]; | ||
| 541 | argv[bottom + i] = argv[top - (middle - bottom) + i]; | ||
| 542 | argv[top - (middle - bottom) + i] = tem; | ||
| 543 | } | ||
| 544 | top -= len; | ||
| 545 | } | ||
| 546 | else | ||
| 547 | { | ||
| 548 | int len = top - middle; | ||
| 549 | register int i; | ||
| 550 | for (i = 0; i < len; i++) | ||
| 551 | { | ||
| 552 | tem = argv[bottom + i]; | ||
| 553 | argv[bottom + i] = argv[middle + i]; | ||
| 554 | argv[middle + i] = tem; | ||
| 555 | } | ||
| 556 | bottom += len; | ||
| 557 | } | ||
| 558 | } | ||
| 559 | d->__first_nonopt += (d->optind - d->__last_nonopt); | ||
| 560 | d->__last_nonopt = d->optind; | ||
| 561 | } | ||
| 562 | static const wchar_t *_getopt_initialize_w(const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct) | ||
| 563 | { | ||
| 564 | d->__first_nonopt = d->__last_nonopt = d->optind; | ||
| 565 | d->__nextchar = NULL; | ||
| 566 | d->__posixly_correct = posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT"); | ||
| 567 | if (optstring[0] == L'-') | ||
| 568 | { | ||
| 569 | d->__ordering = RETURN_IN_ORDER; | ||
| 570 | ++optstring; | ||
| 571 | } | ||
| 572 | else if (optstring[0] == L'+') | ||
| 573 | { | ||
| 574 | d->__ordering = REQUIRE_ORDER; | ||
| 575 | ++optstring; | ||
| 576 | } | ||
| 577 | else if (d->__posixly_correct) | ||
| 578 | d->__ordering = REQUIRE_ORDER; | ||
| 579 | else | ||
| 580 | d->__ordering = PERMUTE; | ||
| 581 | return optstring; | ||
| 582 | } | ||
| 583 | int _getopt_internal_r_w(int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct) | ||
| 584 | { | ||
| 585 | int print_errors = d->opterr; | ||
| 586 | if (argc < 1) | ||
| 587 | return -1; | ||
| 588 | d->optarg = NULL; | ||
| 589 | if (d->optind == 0 || !d->__initialized) | ||
| 590 | { | ||
| 591 | if (d->optind == 0) | ||
| 592 | d->optind = 1; | ||
| 593 | optstring = _getopt_initialize_w(optstring, d, posixly_correct); | ||
| 594 | d->__initialized = 1; | ||
| 595 | } | ||
| 596 | else if (optstring[0] == L'-' || optstring[0] == L'+') | ||
| 597 | optstring++; | ||
| 598 | if (optstring[0] == L':') | ||
| 599 | print_errors = 0; | ||
| 600 | if (d->__nextchar == NULL || *d->__nextchar == L'\0') | ||
| 601 | { | ||
| 602 | if (d->__last_nonopt > d->optind) | ||
| 603 | d->__last_nonopt = d->optind; | ||
| 604 | if (d->__first_nonopt > d->optind) | ||
| 605 | d->__first_nonopt = d->optind; | ||
| 606 | if (d->__ordering == PERMUTE) | ||
| 607 | { | ||
| 608 | if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) | ||
| 609 | exchange_w((wchar_t **)argv, d); | ||
| 610 | else if (d->__last_nonopt != d->optind) | ||
| 611 | d->__first_nonopt = d->optind; | ||
| 612 | while (d->optind < argc && (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0')) | ||
| 613 | d->optind++; | ||
| 614 | d->__last_nonopt = d->optind; | ||
| 615 | } | ||
| 616 | if (d->optind != argc && !wcscmp(argv[d->optind], L"--")) | ||
| 617 | { | ||
| 618 | d->optind++; | ||
| 619 | if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) | ||
| 620 | exchange_w((wchar_t **)argv, d); | ||
| 621 | else if (d->__first_nonopt == d->__last_nonopt) | ||
| 622 | d->__first_nonopt = d->optind; | ||
| 623 | d->__last_nonopt = argc; | ||
| 624 | d->optind = argc; | ||
| 625 | } | ||
| 626 | if (d->optind == argc) | ||
| 627 | { | ||
| 628 | if (d->__first_nonopt != d->__last_nonopt) | ||
| 629 | d->optind = d->__first_nonopt; | ||
| 630 | return -1; | ||
| 631 | } | ||
| 632 | if ((argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0')) | ||
| 633 | { | ||
| 634 | if (d->__ordering == REQUIRE_ORDER) | ||
| 635 | return -1; | ||
| 636 | d->optarg = argv[d->optind++]; | ||
| 637 | return 1; | ||
| 638 | } | ||
| 639 | d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == L'-')); | ||
| 640 | } | ||
| 641 | if (longopts != NULL && (argv[d->optind][1] == L'-' || (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1]))))) | ||
| 642 | { | ||
| 643 | wchar_t *nameend; | ||
| 644 | unsigned int namelen; | ||
| 645 | const struct option_w *p; | ||
| 646 | const struct option_w *pfound = NULL; | ||
| 647 | struct option_list | ||
| 648 | { | ||
| 649 | const struct option_w *p; | ||
| 650 | struct option_list *next; | ||
| 651 | } *ambig_list = NULL; | ||
| 652 | int exact = 0; | ||
| 653 | int indfound = -1; | ||
| 654 | int option_index; | ||
| 655 | for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++); | ||
| 656 | namelen = (unsigned int)(nameend - d->__nextchar); | ||
| 657 | for (p = longopts, option_index = 0; p->name; p++, option_index++) | ||
| 658 | if (!wcsncmp(p->name, d->__nextchar, namelen)) | ||
| 659 | { | ||
| 660 | if (namelen == (unsigned int)wcslen(p->name)) | ||
| 661 | { | ||
| 662 | pfound = p; | ||
| 663 | indfound = option_index; | ||
| 664 | exact = 1; | ||
| 665 | break; | ||
| 666 | } | ||
| 667 | else if (pfound == NULL) | ||
| 668 | { | ||
| 669 | pfound = p; | ||
| 670 | indfound = option_index; | ||
| 671 | } | ||
| 672 | else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) | ||
| 673 | { | ||
| 674 | struct option_list *newp = (struct option_list*)alloca(sizeof(*newp)); | ||
| 675 | newp->p = p; | ||
| 676 | newp->next = ambig_list; | ||
| 677 | ambig_list = newp; | ||
| 678 | } | ||
| 679 | } | ||
| 680 | if (ambig_list != NULL && !exact) | ||
| 681 | { | ||
| 682 | if (print_errors) | ||
| 683 | { | ||
| 684 | struct option_list first; | ||
| 685 | first.p = pfound; | ||
| 686 | first.next = ambig_list; | ||
| 687 | ambig_list = &first; | ||
| 688 | fwprintf(stderr, L"%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]); | ||
| 689 | do | ||
| 690 | { | ||
| 691 | fwprintf(stderr, L" '--%s'", ambig_list->p->name); | ||
| 692 | ambig_list = ambig_list->next; | ||
| 693 | } while (ambig_list != NULL); | ||
| 694 | fputwc(L'\n', stderr); | ||
| 695 | } | ||
| 696 | d->__nextchar += wcslen(d->__nextchar); | ||
| 697 | d->optind++; | ||
| 698 | d->optopt = 0; | ||
| 699 | return L'?'; | ||
| 700 | } | ||
| 701 | if (pfound != NULL) | ||
| 702 | { | ||
| 703 | option_index = indfound; | ||
| 704 | d->optind++; | ||
| 705 | if (*nameend) | ||
| 706 | { | ||
| 707 | if (pfound->has_arg) | ||
| 708 | d->optarg = nameend + 1; | ||
| 709 | else | ||
| 710 | { | ||
| 711 | if (print_errors) | ||
| 712 | { | ||
| 713 | if (argv[d->optind - 1][1] == L'-') | ||
| 714 | { | ||
| 715 | fwprintf(stderr, L"%s: option '--%s' doesn't allow an argument\n", argv[0], pfound->name); | ||
| 716 | } | ||
| 717 | else | ||
| 718 | { | ||
| 719 | fwprintf(stderr, L"%s: option '%c%s' doesn't allow an argument\n", argv[0], argv[d->optind - 1][0], pfound->name); | ||
| 720 | } | ||
| 721 | } | ||
| 722 | d->__nextchar += wcslen(d->__nextchar); | ||
| 723 | d->optopt = pfound->val; | ||
| 724 | return L'?'; | ||
| 725 | } | ||
| 726 | } | ||
| 727 | else if (pfound->has_arg == 1) | ||
| 728 | { | ||
| 729 | if (d->optind < argc) | ||
| 730 | d->optarg = argv[d->optind++]; | ||
| 731 | else | ||
| 732 | { | ||
| 733 | if (print_errors) | ||
| 734 | { | ||
| 735 | fwprintf(stderr, L"%s: option '--%s' requires an argument\n", argv[0], pfound->name); | ||
| 736 | } | ||
| 737 | d->__nextchar += wcslen(d->__nextchar); | ||
| 738 | d->optopt = pfound->val; | ||
| 739 | return optstring[0] == L':' ? L':' : L'?'; | ||
| 740 | } | ||
| 741 | } | ||
| 742 | d->__nextchar += wcslen(d->__nextchar); | ||
| 743 | if (longind != NULL) | ||
| 744 | *longind = option_index; | ||
| 745 | if (pfound->flag) | ||
| 746 | { | ||
| 747 | *(pfound->flag) = pfound->val; | ||
| 748 | return 0; | ||
| 749 | } | ||
| 750 | return pfound->val; | ||
| 751 | } | ||
| 752 | if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL) | ||
| 753 | { | ||
| 754 | if (print_errors) | ||
| 755 | { | ||
| 756 | if (argv[d->optind][1] == L'-') | ||
| 757 | { | ||
| 758 | fwprintf(stderr, L"%s: unrecognized option '--%s'\n", argv[0], d->__nextchar); | ||
| 759 | } | ||
| 760 | else | ||
| 761 | { | ||
| 762 | fwprintf(stderr, L"%s: unrecognized option '%c%s'\n", argv[0], argv[d->optind][0], d->__nextchar); | ||
| 763 | } | ||
| 764 | } | ||
| 765 | d->__nextchar = (wchar_t *)L""; | ||
| 766 | d->optind++; | ||
| 767 | d->optopt = 0; | ||
| 768 | return L'?'; | ||
| 769 | } | ||
| 770 | } | ||
| 771 | { | ||
| 772 | wchar_t c = *d->__nextchar++; | ||
| 773 | wchar_t *temp = (wchar_t*)wcschr(optstring, c); | ||
| 774 | if (*d->__nextchar == L'\0') | ||
| 775 | ++d->optind; | ||
| 776 | if (temp == NULL || c == L':' || c == L';') | ||
| 777 | { | ||
| 778 | if (print_errors) | ||
| 779 | { | ||
| 780 | fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c); | ||
| 781 | } | ||
| 782 | d->optopt = c; | ||
| 783 | return L'?'; | ||
| 784 | } | ||
| 785 | if (temp[0] == L'W' && temp[1] == L';') | ||
| 786 | { | ||
| 787 | wchar_t *nameend; | ||
| 788 | const struct option_w *p; | ||
| 789 | const struct option_w *pfound = NULL; | ||
| 790 | int exact = 0; | ||
| 791 | int ambig = 0; | ||
| 792 | int indfound = 0; | ||
| 793 | int option_index; | ||
| 794 | if (longopts == NULL) | ||
| 795 | goto no_longs; | ||
| 796 | if (*d->__nextchar != L'\0') | ||
| 797 | { | ||
| 798 | d->optarg = d->__nextchar; | ||
| 799 | d->optind++; | ||
| 800 | } | ||
| 801 | else if (d->optind == argc) | ||
| 802 | { | ||
| 803 | if (print_errors) | ||
| 804 | { | ||
| 805 | fwprintf(stderr, L"%s: option requires an argument -- '%c'\n", argv[0], c); | ||
| 806 | } | ||
| 807 | d->optopt = c; | ||
| 808 | if (optstring[0] == L':') | ||
| 809 | c = L':'; | ||
| 810 | else | ||
| 811 | c = L'?'; | ||
| 812 | return c; | ||
| 813 | } | ||
| 814 | else | ||
| 815 | d->optarg = argv[d->optind++]; | ||
| 816 | for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != L'='; nameend++); | ||
| 817 | for (p = longopts, option_index = 0; p->name; p++, option_index++) | ||
| 818 | if (!wcsncmp(p->name, d->__nextchar, nameend - d->__nextchar)) | ||
| 819 | { | ||
| 820 | if ((unsigned int)(nameend - d->__nextchar) == wcslen(p->name)) | ||
| 821 | { | ||
| 822 | pfound = p; | ||
| 823 | indfound = option_index; | ||
| 824 | exact = 1; | ||
| 825 | break; | ||
| 826 | } | ||
| 827 | else if (pfound == NULL) | ||
| 828 | { | ||
| 829 | pfound = p; | ||
| 830 | indfound = option_index; | ||
| 831 | } | ||
| 832 | else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) | ||
| 833 | ambig = 1; | ||
| 834 | } | ||
| 835 | if (ambig && !exact) | ||
| 836 | { | ||
| 837 | if (print_errors) | ||
| 838 | { | ||
| 839 | fwprintf(stderr, L"%s: option '-W %s' is ambiguous\n", argv[0], d->optarg); | ||
| 840 | } | ||
| 841 | d->__nextchar += wcslen(d->__nextchar); | ||
| 842 | d->optind++; | ||
| 843 | return L'?'; | ||
| 844 | } | ||
| 845 | if (pfound != NULL) | ||
| 846 | { | ||
| 847 | option_index = indfound; | ||
| 848 | if (*nameend) | ||
| 849 | { | ||
| 850 | if (pfound->has_arg) | ||
| 851 | d->optarg = nameend + 1; | ||
| 852 | else | ||
| 853 | { | ||
| 854 | if (print_errors) | ||
| 855 | { | ||
| 856 | fwprintf(stderr, L"%s: option '-W %s' doesn't allow an argument\n", argv[0], pfound->name); | ||
| 857 | } | ||
| 858 | d->__nextchar += wcslen(d->__nextchar); | ||
| 859 | return L'?'; | ||
| 860 | } | ||
| 861 | } | ||
| 862 | else if (pfound->has_arg == 1) | ||
| 863 | { | ||
| 864 | if (d->optind < argc) | ||
| 865 | d->optarg = argv[d->optind++]; | ||
| 866 | else | ||
| 867 | { | ||
| 868 | if (print_errors) | ||
| 869 | { | ||
| 870 | fwprintf(stderr, L"%s: option '-W %s' requires an argument\n", argv[0], pfound->name); | ||
| 871 | } | ||
| 872 | d->__nextchar += wcslen(d->__nextchar); | ||
| 873 | return optstring[0] == L':' ? L':' : L'?'; | ||
| 874 | } | ||
| 875 | } | ||
| 876 | else | ||
| 877 | d->optarg = NULL; | ||
| 878 | d->__nextchar += wcslen(d->__nextchar); | ||
| 879 | if (longind != NULL) | ||
| 880 | *longind = option_index; | ||
| 881 | if (pfound->flag) | ||
| 882 | { | ||
| 883 | *(pfound->flag) = pfound->val; | ||
| 884 | return 0; | ||
| 885 | } | ||
| 886 | return pfound->val; | ||
| 887 | } | ||
| 888 | no_longs: | ||
| 889 | d->__nextchar = NULL; | ||
| 890 | return L'W'; | ||
| 891 | } | ||
| 892 | if (temp[1] == L':') | ||
| 893 | { | ||
| 894 | if (temp[2] == L':') | ||
| 895 | { | ||
| 896 | if (*d->__nextchar != L'\0') | ||
| 897 | { | ||
| 898 | d->optarg = d->__nextchar; | ||
| 899 | d->optind++; | ||
| 900 | } | ||
| 901 | else | ||
| 902 | d->optarg = NULL; | ||
| 903 | d->__nextchar = NULL; | ||
| 904 | } | ||
| 905 | else | ||
| 906 | { | ||
| 907 | if (*d->__nextchar != L'\0') | ||
| 908 | { | ||
| 909 | d->optarg = d->__nextchar; | ||
| 910 | d->optind++; | ||
| 911 | } | ||
| 912 | else if (d->optind == argc) | ||
| 913 | { | ||
| 914 | if (print_errors) | ||
| 915 | { | ||
| 916 | fwprintf(stderr, L"%s: option requires an argument -- '%c'\n", argv[0], c); | ||
| 917 | } | ||
| 918 | d->optopt = c; | ||
| 919 | if (optstring[0] == L':') | ||
| 920 | c = L':'; | ||
| 921 | else | ||
| 922 | c = L'?'; | ||
| 923 | } | ||
| 924 | else | ||
| 925 | d->optarg = argv[d->optind++]; | ||
| 926 | d->__nextchar = NULL; | ||
| 927 | } | ||
| 928 | } | ||
| 929 | return c; | ||
| 930 | } | ||
| 931 | } | ||
| 932 | int _getopt_internal_w(int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct) | ||
| 933 | { | ||
| 934 | int result; | ||
| 935 | getopt_data_w.optind = optind; | ||
| 936 | getopt_data_w.opterr = opterr; | ||
| 937 | result = _getopt_internal_r_w(argc, argv, optstring, longopts, longind, long_only, &getopt_data_w, posixly_correct); | ||
| 938 | optind = getopt_data_w.optind; | ||
| 939 | optarg_w = getopt_data_w.optarg; | ||
| 940 | optopt = getopt_data_w.optopt; | ||
| 941 | return result; | ||
| 942 | } | ||
| 943 | int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW | ||
| 944 | { | ||
| 945 | return _getopt_internal_w(argc, argv, optstring, (const struct option_w *) 0, (int *)0, 0, 0); | ||
| 946 | } | ||
| 947 | int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW | ||
| 948 | { | ||
| 949 | return _getopt_internal_w(argc, argv, options, long_options, opt_index, 0, 0); | ||
| 950 | } | ||
| 951 | int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW | ||
| 952 | { | ||
| 953 | return _getopt_internal_w(argc, argv, options, long_options, opt_index, 1, 0); | ||
| 954 | } | ||
| 955 | int _getopt_long_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) | ||
| 956 | { | ||
| 957 | return _getopt_internal_r_w(argc, argv, options, long_options, opt_index, 0, d, 0); | ||
| 958 | } | ||
| 959 | int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) | ||
| 960 | { | ||
| 961 | return _getopt_internal_r_w(argc, argv, options, long_options, opt_index, 1, d, 0); | ||
| 962 | } \ No newline at end of file | ||
diff --git a/externals/getopt/getopt.h b/externals/getopt/getopt.h new file mode 100644 index 000000000..87e2ca396 --- /dev/null +++ b/externals/getopt/getopt.h | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | /* Getopt for Microsoft C | ||
| 2 | This code is a modification of the Free Software Foundation, Inc. | ||
| 3 | Getopt library for parsing command line argument the purpose was | ||
| 4 | to provide a Microsoft Visual C friendly derivative. This code | ||
| 5 | provides functionality for both Unicode and Multibyte builds. | ||
| 6 | |||
| 7 | Date: 02/03/2011 - Ludvik Jerabek - Initial Release | ||
| 8 | Version: 1.0 | ||
| 9 | Comment: Supports getopt, getopt_long, and getopt_long_only | ||
| 10 | and POSIXLY_CORRECT environment flag | ||
| 11 | License: LGPL | ||
| 12 | |||
| 13 | Revisions: | ||
| 14 | |||
| 15 | 02/03/2011 - Ludvik Jerabek - Initial Release | ||
| 16 | 02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4 | ||
| 17 | 07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs | ||
| 18 | 08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception | ||
| 19 | 08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB | ||
| 20 | 02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file | ||
| 21 | 08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi | ||
| 22 | 10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features | ||
| 23 | 06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable | ||
| 24 | |||
| 25 | **DISCLAIMER** | ||
| 26 | THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, | ||
| 27 | EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE | ||
| 28 | IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | ||
| 29 | PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE | ||
| 30 | EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT | ||
| 31 | APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY | ||
| 32 | DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY | ||
| 33 | USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST | ||
| 34 | PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON | ||
| 35 | YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE | ||
| 36 | EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | ||
| 37 | */ | ||
| 38 | #ifndef __GETOPT_H_ | ||
| 39 | #define __GETOPT_H_ | ||
| 40 | |||
| 41 | #ifdef _GETOPT_API | ||
| 42 | #undef _GETOPT_API | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT) | ||
| 46 | #error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually" | ||
| 47 | #elif defined(STATIC_GETOPT) | ||
| 48 | #pragma message("Warning static builds of getopt violate the Lesser GNU Public License") | ||
| 49 | #define _GETOPT_API | ||
| 50 | #elif defined(EXPORTS_GETOPT) | ||
| 51 | #pragma message("Exporting getopt library") | ||
| 52 | #define _GETOPT_API __declspec(dllexport) | ||
| 53 | #else | ||
| 54 | #pragma message("Importing getopt library") | ||
| 55 | #define _GETOPT_API __declspec(dllimport) | ||
| 56 | #endif | ||
| 57 | |||
| 58 | // Change behavior for C\C++ | ||
| 59 | #ifdef __cplusplus | ||
| 60 | #define _BEGIN_EXTERN_C extern "C" { | ||
| 61 | #define _END_EXTERN_C } | ||
| 62 | #define _GETOPT_THROW throw() | ||
| 63 | #else | ||
| 64 | #define _BEGIN_EXTERN_C | ||
| 65 | #define _END_EXTERN_C | ||
| 66 | #define _GETOPT_THROW | ||
| 67 | #endif | ||
| 68 | |||
| 69 | // Standard GNU options | ||
| 70 | #define null_argument 0 | ||
| 71 | #define no_argument 0 | ||
| 72 | #define required_argument 1 | ||
| 73 | #define optional_argument 2 | ||
| 74 | |||
| 75 | // Shorter Options | ||
| 76 | #define ARG_NULL 0 | ||
| 77 | #define ARG_NONE 0 | ||
| 78 | #define ARG_REQ 1 | ||
| 79 | #define ARG_OPT 2 | ||
| 80 | |||
| 81 | #include <string.h> | ||
| 82 | #include <wchar.h> | ||
| 83 | |||
| 84 | _BEGIN_EXTERN_C | ||
| 85 | |||
| 86 | extern _GETOPT_API int optind; | ||
| 87 | extern _GETOPT_API int opterr; | ||
| 88 | extern _GETOPT_API int optopt; | ||
| 89 | |||
| 90 | // Ansi | ||
| 91 | struct option_a | ||
| 92 | { | ||
| 93 | const char* name; | ||
| 94 | int has_arg; | ||
| 95 | int *flag; | ||
| 96 | int val; | ||
| 97 | }; | ||
| 98 | extern _GETOPT_API char *optarg_a; | ||
| 99 | extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW; | ||
| 100 | extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW; | ||
| 101 | extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW; | ||
| 102 | |||
| 103 | // Unicode | ||
| 104 | struct option_w | ||
| 105 | { | ||
| 106 | const wchar_t* name; | ||
| 107 | int has_arg; | ||
| 108 | int *flag; | ||
| 109 | int val; | ||
| 110 | }; | ||
| 111 | extern _GETOPT_API wchar_t *optarg_w; | ||
| 112 | extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW; | ||
| 113 | extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW; | ||
| 114 | extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW; | ||
| 115 | |||
| 116 | _END_EXTERN_C | ||
| 117 | |||
| 118 | #undef _BEGIN_EXTERN_C | ||
| 119 | #undef _END_EXTERN_C | ||
| 120 | #undef _GETOPT_THROW | ||
| 121 | #undef _GETOPT_API | ||
| 122 | |||
| 123 | #ifdef _UNICODE | ||
| 124 | #define getopt getopt_w | ||
| 125 | #define getopt_long getopt_long_w | ||
| 126 | #define getopt_long_only getopt_long_only_w | ||
| 127 | #define option option_w | ||
| 128 | #define optarg optarg_w | ||
| 129 | #else | ||
| 130 | #define getopt getopt_a | ||
| 131 | #define getopt_long getopt_long_a | ||
| 132 | #define getopt_long_only getopt_long_only_a | ||
| 133 | #define option option_a | ||
| 134 | #define optarg optarg_a | ||
| 135 | #endif | ||
| 136 | #endif // __GETOPT_H_ | ||
diff --git a/hooks/pre-commit b/hooks/pre-commit index bad84b14b..bad84b14b 100644..100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit | |||
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 713f49193..918687312 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt | |||
| @@ -16,6 +16,11 @@ create_directory_groups(${SRCS} ${HEADERS}) | |||
| 16 | add_executable(citra ${SRCS} ${HEADERS}) | 16 | add_executable(citra ${SRCS} ${HEADERS}) |
| 17 | target_link_libraries(citra core common video_core) | 17 | target_link_libraries(citra core common video_core) |
| 18 | target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) | 18 | target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) |
| 19 | if (MSVC) | ||
| 20 | target_link_libraries(citra getopt) | ||
| 21 | endif() | ||
| 19 | target_link_libraries(citra ${PLATFORM_LIBRARIES}) | 22 | target_link_libraries(citra ${PLATFORM_LIBRARIES}) |
| 20 | 23 | ||
| 21 | #install(TARGETS citra RUNTIME DESTINATION ${bindir}) | 24 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") |
| 25 | install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 26 | endif() \ No newline at end of file | ||
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index ce8d7dd25..182646f4c 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp | |||
| @@ -2,13 +2,20 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <string> | ||
| 5 | #include <thread> | 6 | #include <thread> |
| 7 | #include <iostream> | ||
| 8 | |||
| 9 | #ifdef _MSC_VER | ||
| 10 | #include <getopt.h> | ||
| 11 | #else | ||
| 12 | #include <unistd.h> | ||
| 13 | #include <getopt.h> | ||
| 14 | #endif | ||
| 6 | 15 | ||
| 7 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 8 | #include "common/logging/text_formatter.h" | ||
| 9 | #include "common/logging/backend.h" | 17 | #include "common/logging/backend.h" |
| 10 | #include "common/logging/filter.h" | 18 | #include "common/logging/filter.h" |
| 11 | #include "common/scope_exit.h" | ||
| 12 | 19 | ||
| 13 | #include "core/settings.h" | 20 | #include "core/settings.h" |
| 14 | #include "core/system.h" | 21 | #include "core/system.h" |
| @@ -20,12 +27,39 @@ | |||
| 20 | 27 | ||
| 21 | #include "video_core/video_core.h" | 28 | #include "video_core/video_core.h" |
| 22 | 29 | ||
| 30 | |||
| 31 | static void PrintHelp() | ||
| 32 | { | ||
| 33 | std::cout << "Usage: citra <filename>" << std::endl; | ||
| 34 | } | ||
| 35 | |||
| 23 | /// Application entry point | 36 | /// Application entry point |
| 24 | int main(int argc, char **argv) { | 37 | int main(int argc, char **argv) { |
| 38 | int option_index = 0; | ||
| 39 | std::string boot_filename; | ||
| 40 | static struct option long_options[] = { | ||
| 41 | { "help", no_argument, 0, 'h' }, | ||
| 42 | { 0, 0, 0, 0 } | ||
| 43 | }; | ||
| 44 | |||
| 45 | while (optind < argc) { | ||
| 46 | char arg = getopt_long(argc, argv, ":h", long_options, &option_index); | ||
| 47 | if (arg != -1) { | ||
| 48 | switch (arg) { | ||
| 49 | case 'h': | ||
| 50 | PrintHelp(); | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | } else { | ||
| 54 | boot_filename = argv[optind]; | ||
| 55 | optind++; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 25 | Log::Filter log_filter(Log::Level::Debug); | 59 | Log::Filter log_filter(Log::Level::Debug); |
| 26 | Log::SetFilter(&log_filter); | 60 | Log::SetFilter(&log_filter); |
| 27 | 61 | ||
| 28 | if (argc < 2) { | 62 | if (boot_filename.empty()) { |
| 29 | LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); | 63 | LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); |
| 30 | return -1; | 64 | return -1; |
| 31 | } | 65 | } |
| @@ -33,7 +67,7 @@ int main(int argc, char **argv) { | |||
| 33 | Config config; | 67 | Config config; |
| 34 | log_filter.ParseFilterString(Settings::values.log_filter); | 68 | log_filter.ParseFilterString(Settings::values.log_filter); |
| 35 | 69 | ||
| 36 | std::string boot_filename = argv[1]; | 70 | |
| 37 | EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | 71 | EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; |
| 38 | 72 | ||
| 39 | VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer; | 73 | VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer; |
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 1378567c1..506cb7939 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -2,7 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #define GLFW_INCLUDE_NONE | ||
| 5 | #include <GLFW/glfw3.h> | 6 | #include <GLFW/glfw3.h> |
| 7 | #include <inih/cpp/INIReader.h> | ||
| 6 | 8 | ||
| 7 | #include "citra/default_ini.h" | 9 | #include "citra/default_ini.h" |
| 8 | 10 | ||
| @@ -10,7 +12,6 @@ | |||
| 10 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 11 | 13 | ||
| 12 | #include "core/settings.h" | 14 | #include "core/settings.h" |
| 13 | #include "core/core.h" | ||
| 14 | 15 | ||
| 15 | #include "config.h" | 16 | #include "config.h" |
| 16 | 17 | ||
diff --git a/src/citra/config.h b/src/citra/config.h index 0eb176c7d..c326ec669 100644 --- a/src/citra/config.h +++ b/src/citra/config.h | |||
| @@ -4,11 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | 7 | #include <string> |
| 8 | 8 | ||
| 9 | #include <inih/cpp/INIReader.h> | 9 | class INIReader; |
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | 10 | ||
| 13 | class Config { | 11 | class Config { |
| 14 | INIReader* glfw_config; | 12 | INIReader* glfw_config; |
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 341b48d2a..42fb683a9 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp | |||
| @@ -2,13 +2,25 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <string> | ||
| 8 | |||
| 9 | // Let’s use our own GL header, instead of one from GLFW. | ||
| 10 | #include "video_core/renderer_opengl/generated/gl_3_2_core.h" | ||
| 11 | #define GLFW_INCLUDE_NONE | ||
| 5 | #include <GLFW/glfw3.h> | 12 | #include <GLFW/glfw3.h> |
| 6 | 13 | ||
| 14 | #include "common/assert.h" | ||
| 15 | #include "common/key_map.h" | ||
| 7 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 17 | #include "common/scm_rev.h" | ||
| 18 | #include "common/string_util.h" | ||
| 8 | 19 | ||
| 9 | #include "video_core/video_core.h" | 20 | #include "video_core/video_core.h" |
| 10 | 21 | ||
| 11 | #include "core/settings.h" | 22 | #include "core/settings.h" |
| 23 | #include "core/hle/service/hid/hid.h" | ||
| 12 | 24 | ||
| 13 | #include "citra/emu_window/emu_window_glfw.h" | 25 | #include "citra/emu_window/emu_window_glfw.h" |
| 14 | 26 | ||
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 16c109b79..7ccd5e6aa 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <utility> | ||
| 8 | |||
| 7 | #include "common/emu_window.h" | 9 | #include "common/emu_window.h" |
| 8 | 10 | ||
| 9 | struct GLFWwindow; | 11 | struct GLFWwindow; |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index c2d1ad240..47aaeca24 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -12,6 +12,7 @@ set(SRCS | |||
| 12 | debugger/graphics_breakpoints.cpp | 12 | debugger/graphics_breakpoints.cpp |
| 13 | debugger/graphics_cmdlists.cpp | 13 | debugger/graphics_cmdlists.cpp |
| 14 | debugger/graphics_framebuffer.cpp | 14 | debugger/graphics_framebuffer.cpp |
| 15 | debugger/graphics_tracing.cpp | ||
| 15 | debugger/graphics_vertex_shader.cpp | 16 | debugger/graphics_vertex_shader.cpp |
| 16 | debugger/profiler.cpp | 17 | debugger/profiler.cpp |
| 17 | debugger/ramview.cpp | 18 | debugger/ramview.cpp |
| @@ -35,6 +36,7 @@ set(HEADERS | |||
| 35 | debugger/graphics_breakpoints_p.h | 36 | debugger/graphics_breakpoints_p.h |
| 36 | debugger/graphics_cmdlists.h | 37 | debugger/graphics_cmdlists.h |
| 37 | debugger/graphics_framebuffer.h | 38 | debugger/graphics_framebuffer.h |
| 39 | debugger/graphics_tracing.h | ||
| 38 | debugger/graphics_vertex_shader.h | 40 | debugger/graphics_vertex_shader.h |
| 39 | debugger/profiler.h | 41 | debugger/profiler.h |
| 40 | debugger/ramview.h | 42 | debugger/ramview.h |
| @@ -73,7 +75,9 @@ target_link_libraries(citra-qt core common video_core qhexedit) | |||
| 73 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) | 75 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) |
| 74 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) | 76 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) |
| 75 | 77 | ||
| 76 | #install(TARGETS citra-qt RUNTIME DESTINATION ${bindir}) | 78 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") |
| 79 | install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 80 | endif() | ||
| 77 | 81 | ||
| 78 | if (Qt5_FOUND AND MSVC) | 82 | if (Qt5_FOUND AND MSVC) |
| 79 | set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") | 83 | set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") |
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 3db09c65b..fa7bce466 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -11,6 +11,10 @@ | |||
| 11 | #include "bootmanager.h" | 11 | #include "bootmanager.h" |
| 12 | #include "main.h" | 12 | #include "main.h" |
| 13 | 13 | ||
| 14 | #include "common/string_util.h" | ||
| 15 | #include "common/scm_rev.h" | ||
| 16 | #include "common/key_map.h" | ||
| 17 | |||
| 14 | #include "core/core.h" | 18 | #include "core/core.h" |
| 15 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| 16 | #include "core/system.h" | 20 | #include "core/system.h" |
| @@ -61,7 +65,7 @@ void EmuThread::run() { | |||
| 61 | was_active = false; | 65 | was_active = false; |
| 62 | } else { | 66 | } else { |
| 63 | std::unique_lock<std::mutex> lock(running_mutex); | 67 | std::unique_lock<std::mutex> lock(running_mutex); |
| 64 | running_cv.wait(lock, [this]{ return IsRunning() || stop_run; }); | 68 | running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); |
| 65 | } | 69 | } |
| 66 | } | 70 | } |
| 67 | 71 | ||
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 475124319..1a1e0e6a5 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h | |||
| @@ -35,7 +35,10 @@ public: | |||
| 35 | * Steps the emulation thread by a single CPU instruction (if the CPU is not already running) | 35 | * Steps the emulation thread by a single CPU instruction (if the CPU is not already running) |
| 36 | * @note This function is thread-safe | 36 | * @note This function is thread-safe |
| 37 | */ | 37 | */ |
| 38 | void ExecStep() { exec_step = true; } | 38 | void ExecStep() { |
| 39 | exec_step = true; | ||
| 40 | running_cv.notify_all(); | ||
| 41 | } | ||
| 39 | 42 | ||
| 40 | /** | 43 | /** |
| 41 | * Sets whether the emulation thread is running or not | 44 | * Sets whether the emulation thread is running or not |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 2a9af1f38..5c056446e 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -2,11 +2,11 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QSettings> | ||
| 5 | #include <QString> | 6 | #include <QString> |
| 6 | #include <QStringList> | 7 | #include <QStringList> |
| 7 | 8 | ||
| 8 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 11 | 11 | ||
| 12 | #include "config.h" | 12 | #include "config.h" |
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 4485cae73..dd0b2ef0b 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h | |||
| @@ -4,9 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <QSettings> | 7 | #include <string> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | class QSettings; |
| 10 | 10 | ||
| 11 | class Config { | 11 | class Config { |
| 12 | QSettings* qt_config; | 12 | QSettings* qt_config; |
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index e99ec1b30..b41c40a0e 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QShortcut> | ||
| 6 | |||
| 5 | #include "disassembler.h" | 7 | #include "disassembler.h" |
| 6 | 8 | ||
| 7 | #include "../bootmanager.h" | 9 | #include "../bootmanager.h" |
diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics_breakpoint_observer.h index f0d3361f8..02a0f4f4f 100644 --- a/src/citra_qt/debugger/graphics_breakpoint_observer.h +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | 13 | * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while |
| 14 | * the widget usually wants to perform reactions in the GUI thread. | 14 | * the widget usually wants to perform reactions in the GUI thread. |
| 15 | */ | 15 | */ |
| 16 | class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { | 16 | class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { |
| 17 | Q_OBJECT | 17 | Q_OBJECT |
| 18 | 18 | ||
| 19 | public: | 19 | public: |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 1da64f616..5202c168c 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <QMetaType> | 5 | #include <QMetaType> |
| 6 | #include <QPushButton> | 6 | #include <QPushButton> |
| 7 | #include <QTreeWidget> | 7 | #include <QTreeView> |
| 8 | #include <QVBoxLayout> | 8 | #include <QVBoxLayout> |
| 9 | #include <QLabel> | 9 | #include <QLabel> |
| 10 | 10 | ||
| @@ -23,7 +23,7 @@ BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_conte | |||
| 23 | 23 | ||
| 24 | int BreakPointModel::columnCount(const QModelIndex& parent) const | 24 | int BreakPointModel::columnCount(const QModelIndex& parent) const |
| 25 | { | 25 | { |
| 26 | return 2; | 26 | return 1; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | int BreakPointModel::rowCount(const QModelIndex& parent) const | 29 | int BreakPointModel::rowCount(const QModelIndex& parent) const |
| @@ -38,29 +38,29 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | |||
| 38 | switch (role) { | 38 | switch (role) { |
| 39 | case Qt::DisplayRole: | 39 | case Qt::DisplayRole: |
| 40 | { | 40 | { |
| 41 | switch (index.column()) { | 41 | if (index.column() == 0) { |
| 42 | case 0: | ||
| 43 | { | ||
| 44 | static const std::map<Pica::DebugContext::Event, QString> map = { | 42 | static const std::map<Pica::DebugContext::Event, QString> map = { |
| 45 | { Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") }, | 43 | { Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") }, |
| 46 | { Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") }, | 44 | { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, |
| 47 | { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, | 45 | { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, |
| 48 | { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, | 46 | { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, |
| 49 | { Pica::DebugContext::Event::VertexLoaded, tr("Vertex loaded") } | 47 | { Pica::DebugContext::Event::VertexLoaded, tr("Vertex loaded") }, |
| 48 | { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, | ||
| 49 | { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, | ||
| 50 | { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } | ||
| 50 | }; | 51 | }; |
| 51 | 52 | ||
| 52 | DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | 53 | DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); |
| 53 | |||
| 54 | return (map.find(event) != map.end()) ? map.at(event) : QString(); | 54 | return (map.find(event) != map.end()) ? map.at(event) : QString(); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | case 1: | 57 | break; |
| 58 | return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); | 58 | } |
| 59 | |||
| 60 | default: | ||
| 61 | break; | ||
| 62 | } | ||
| 63 | 59 | ||
| 60 | case Qt::CheckStateRole: | ||
| 61 | { | ||
| 62 | if (index.column() == 0) | ||
| 63 | return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; | ||
| 64 | break; | 64 | break; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| @@ -84,37 +84,34 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | |||
| 84 | return QVariant(); | 84 | return QVariant(); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const | 87 | Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const |
| 88 | { | 88 | { |
| 89 | switch(role) { | 89 | if (!index.isValid()) |
| 90 | case Qt::DisplayRole: | 90 | return 0; |
| 91 | { | ||
| 92 | if (section == 0) { | ||
| 93 | return tr("Event"); | ||
| 94 | } else if (section == 1) { | ||
| 95 | return tr("Status"); | ||
| 96 | } | ||
| 97 | |||
| 98 | break; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | 91 | ||
| 102 | return QVariant(); | 92 | Qt::ItemFlags flags = Qt::ItemIsEnabled; |
| 93 | if (index.column() == 0) | ||
| 94 | flags |= Qt::ItemIsUserCheckable; | ||
| 95 | return flags; | ||
| 103 | } | 96 | } |
| 104 | 97 | ||
| 98 | |||
| 105 | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) | 99 | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) |
| 106 | { | 100 | { |
| 107 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | 101 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); |
| 108 | 102 | ||
| 109 | switch (role) { | 103 | switch (role) { |
| 110 | case Role_IsEnabled: | 104 | case Qt::CheckStateRole: |
| 111 | { | 105 | { |
| 106 | if (index.column() != 0) | ||
| 107 | return false; | ||
| 108 | |||
| 112 | auto context = context_weak.lock(); | 109 | auto context = context_weak.lock(); |
| 113 | if (!context) | 110 | if (!context) |
| 114 | return false; | 111 | return false; |
| 115 | 112 | ||
| 116 | context->breakpoints[event].enabled = value.toBool(); | 113 | context->breakpoints[event].enabled = value == Qt::Checked; |
| 117 | QModelIndex changed_index = createIndex(index.row(), 1); | 114 | QModelIndex changed_index = createIndex(index.row(), 0); |
| 118 | emit dataChanged(changed_index, changed_index); | 115 | emit dataChanged(changed_index, changed_index); |
| 119 | return true; | 116 | return true; |
| 120 | } | 117 | } |
| @@ -133,7 +130,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | |||
| 133 | active_breakpoint = context->active_breakpoint; | 130 | active_breakpoint = context->active_breakpoint; |
| 134 | at_breakpoint = context->at_breakpoint; | 131 | at_breakpoint = context->at_breakpoint; |
| 135 | emit dataChanged(createIndex(static_cast<int>(event), 0), | 132 | emit dataChanged(createIndex(static_cast<int>(event), 0), |
| 136 | createIndex(static_cast<int>(event), 1)); | 133 | createIndex(static_cast<int>(event), 0)); |
| 137 | } | 134 | } |
| 138 | 135 | ||
| 139 | void BreakPointModel::OnResumed() | 136 | void BreakPointModel::OnResumed() |
| @@ -144,7 +141,7 @@ void BreakPointModel::OnResumed() | |||
| 144 | 141 | ||
| 145 | at_breakpoint = context->at_breakpoint; | 142 | at_breakpoint = context->at_breakpoint; |
| 146 | emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | 143 | emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), |
| 147 | createIndex(static_cast<int>(active_breakpoint), 1)); | 144 | createIndex(static_cast<int>(active_breakpoint), 0)); |
| 148 | active_breakpoint = context->active_breakpoint; | 145 | active_breakpoint = context->active_breakpoint; |
| 149 | } | 146 | } |
| 150 | 147 | ||
| @@ -162,13 +159,15 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug | |||
| 162 | 159 | ||
| 163 | breakpoint_model = new BreakPointModel(debug_context, this); | 160 | breakpoint_model = new BreakPointModel(debug_context, this); |
| 164 | breakpoint_list = new QTreeView; | 161 | breakpoint_list = new QTreeView; |
| 162 | breakpoint_list->setRootIsDecorated(false); | ||
| 163 | breakpoint_list->setHeaderHidden(true); | ||
| 165 | breakpoint_list->setModel(breakpoint_model); | 164 | breakpoint_list->setModel(breakpoint_model); |
| 166 | 165 | ||
| 167 | toggle_breakpoint_button = new QPushButton(tr("Enable")); | ||
| 168 | toggle_breakpoint_button->setEnabled(false); | ||
| 169 | |||
| 170 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | 166 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); |
| 171 | 167 | ||
| 168 | connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), | ||
| 169 | this, SLOT(OnItemDoubleClicked(const QModelIndex&))); | ||
| 170 | |||
| 172 | connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | 171 | connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); |
| 173 | 172 | ||
| 174 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | 173 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), |
| @@ -184,11 +183,6 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug | |||
| 184 | connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), | 183 | connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), |
| 185 | breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); | 184 | breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); |
| 186 | 185 | ||
| 187 | connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), | ||
| 188 | this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); | ||
| 189 | |||
| 190 | connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); | ||
| 191 | |||
| 192 | QWidget* main_widget = new QWidget; | 186 | QWidget* main_widget = new QWidget; |
| 193 | auto main_layout = new QVBoxLayout; | 187 | auto main_layout = new QVBoxLayout; |
| 194 | { | 188 | { |
| @@ -198,7 +192,6 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug | |||
| 198 | main_layout->addLayout(sub_layout); | 192 | main_layout->addLayout(sub_layout); |
| 199 | } | 193 | } |
| 200 | main_layout->addWidget(breakpoint_list); | 194 | main_layout->addWidget(breakpoint_list); |
| 201 | main_layout->addWidget(toggle_breakpoint_button); | ||
| 202 | main_widget->setLayout(main_layout); | 195 | main_widget->setLayout(main_layout); |
| 203 | 196 | ||
| 204 | setWidget(main_widget); | 197 | setWidget(main_widget); |
| @@ -234,32 +227,15 @@ void GraphicsBreakPointsWidget::OnResumeRequested() | |||
| 234 | context->Resume(); | 227 | context->Resume(); |
| 235 | } | 228 | } |
| 236 | 229 | ||
| 237 | void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) | 230 | void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) |
| 238 | { | 231 | { |
| 239 | if (!index.isValid()) { | 232 | if (!index.isValid()) |
| 240 | toggle_breakpoint_button->setEnabled(false); | ||
| 241 | return; | 233 | return; |
| 242 | } | ||
| 243 | 234 | ||
| 244 | toggle_breakpoint_button->setEnabled(true); | 235 | QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0); |
| 245 | UpdateToggleBreakpointButton(index); | 236 | QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole); |
| 246 | } | 237 | QVariant new_state = Qt::Unchecked; |
| 247 | 238 | if (enabled == Qt::Unchecked) | |
| 248 | void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() | 239 | new_state = Qt::Checked; |
| 249 | { | 240 | breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole); |
| 250 | QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); | ||
| 251 | bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); | ||
| 252 | |||
| 253 | breakpoint_model->setData(index, new_state, | ||
| 254 | BreakPointModel::Role_IsEnabled); | ||
| 255 | UpdateToggleBreakpointButton(index); | ||
| 256 | } | ||
| 257 | |||
| 258 | void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) | ||
| 259 | { | ||
| 260 | if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { | ||
| 261 | toggle_breakpoint_button->setText(tr("Disable")); | ||
| 262 | } else { | ||
| 263 | toggle_breakpoint_button->setText(tr("Enable")); | ||
| 264 | } | ||
| 265 | } | 241 | } |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.h b/src/citra_qt/debugger/graphics_breakpoints.h index 5b9ba324e..d900729da 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.h +++ b/src/citra_qt/debugger/graphics_breakpoints.h | |||
| @@ -31,10 +31,9 @@ public: | |||
| 31 | 31 | ||
| 32 | public slots: | 32 | public slots: |
| 33 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data); | 33 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data); |
| 34 | void OnItemDoubleClicked(const QModelIndex&); | ||
| 34 | void OnResumeRequested(); | 35 | void OnResumeRequested(); |
| 35 | void OnResumed(); | 36 | void OnResumed(); |
| 36 | void OnBreakpointSelectionChanged(const QModelIndex&); | ||
| 37 | void OnToggleBreakpointEnabled(); | ||
| 38 | 37 | ||
| 39 | signals: | 38 | signals: |
| 40 | void Resumed(); | 39 | void Resumed(); |
| @@ -42,11 +41,8 @@ signals: | |||
| 42 | void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | 41 | void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); |
| 43 | 42 | ||
| 44 | private: | 43 | private: |
| 45 | void UpdateToggleBreakpointButton(const QModelIndex& index); | ||
| 46 | |||
| 47 | QLabel* status_text; | 44 | QLabel* status_text; |
| 48 | QPushButton* resume_button; | 45 | QPushButton* resume_button; |
| 49 | QPushButton* toggle_breakpoint_button; | ||
| 50 | 46 | ||
| 51 | BreakPointModel* breakpoint_model; | 47 | BreakPointModel* breakpoint_model; |
| 52 | QTreeView* breakpoint_list; | 48 | QTreeView* breakpoint_list; |
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics_breakpoints_p.h index 34e72e859..00d8d5101 100644 --- a/src/citra_qt/debugger/graphics_breakpoints_p.h +++ b/src/citra_qt/debugger/graphics_breakpoints_p.h | |||
| @@ -23,7 +23,7 @@ public: | |||
| 23 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; | 23 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; |
| 24 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; | 24 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
| 25 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | 25 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |
| 26 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | 26 | Qt::ItemFlags flags(const QModelIndex &index) const; |
| 27 | 27 | ||
| 28 | bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; | 28 | bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; |
| 29 | 29 | ||
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index cabf5fe07..de10bce1f 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QApplication> | ||
| 6 | #include <QClipboard> | ||
| 5 | #include <QLabel> | 7 | #include <QLabel> |
| 6 | #include <QListView> | 8 | #include <QListView> |
| 7 | #include <QMainWindow> | 9 | #include <QMainWindow> |
| @@ -74,7 +76,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 74 | format_choice->addItem(tr("I8")); | 76 | format_choice->addItem(tr("I8")); |
| 75 | format_choice->addItem(tr("A8")); | 77 | format_choice->addItem(tr("A8")); |
| 76 | format_choice->addItem(tr("IA4")); | 78 | format_choice->addItem(tr("IA4")); |
| 77 | format_choice->addItem(tr("UNK10")); | 79 | format_choice->addItem(tr("I4")); |
| 78 | format_choice->addItem(tr("A4")); | 80 | format_choice->addItem(tr("A4")); |
| 79 | format_choice->addItem(tr("ETC1")); | 81 | format_choice->addItem(tr("ETC1")); |
| 80 | format_choice->addItem(tr("ETC1A4")); | 82 | format_choice->addItem(tr("ETC1A4")); |
| @@ -168,7 +170,7 @@ GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(p | |||
| 168 | } | 170 | } |
| 169 | 171 | ||
| 170 | int GPUCommandListModel::rowCount(const QModelIndex& parent) const { | 172 | int GPUCommandListModel::rowCount(const QModelIndex& parent) const { |
| 171 | return pica_trace.writes.size(); | 173 | return static_cast<int>(pica_trace.writes.size()); |
| 172 | } | 174 | } |
| 173 | 175 | ||
| 174 | int GPUCommandListModel::columnCount(const QModelIndex& parent) const { | 176 | int GPUCommandListModel::columnCount(const QModelIndex& parent) const { |
| @@ -304,16 +306,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi | |||
| 304 | this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); | 306 | this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); |
| 305 | 307 | ||
| 306 | toggle_tracing = new QPushButton(tr("Start Tracing")); | 308 | toggle_tracing = new QPushButton(tr("Start Tracing")); |
| 309 | QPushButton* copy_all = new QPushButton(tr("Copy All")); | ||
| 307 | 310 | ||
| 308 | connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); | 311 | connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); |
| 309 | connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), | 312 | connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), |
| 310 | model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); | 313 | model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); |
| 311 | 314 | ||
| 315 | connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); | ||
| 316 | |||
| 312 | command_info_widget = new QWidget; | 317 | command_info_widget = new QWidget; |
| 313 | 318 | ||
| 314 | QVBoxLayout* main_layout = new QVBoxLayout; | 319 | QVBoxLayout* main_layout = new QVBoxLayout; |
| 315 | main_layout->addWidget(list_widget); | 320 | main_layout->addWidget(list_widget); |
| 316 | main_layout->addWidget(toggle_tracing); | 321 | { |
| 322 | QHBoxLayout* sub_layout = new QHBoxLayout; | ||
| 323 | sub_layout->addWidget(toggle_tracing); | ||
| 324 | sub_layout->addWidget(copy_all); | ||
| 325 | main_layout->addLayout(sub_layout); | ||
| 326 | } | ||
| 317 | main_layout->addWidget(command_info_widget); | 327 | main_layout->addWidget(command_info_widget); |
| 318 | main_widget->setLayout(main_layout); | 328 | main_widget->setLayout(main_layout); |
| 319 | 329 | ||
| @@ -330,3 +340,21 @@ void GPUCommandListWidget::OnToggleTracing() { | |||
| 330 | toggle_tracing->setText(tr("Start Tracing")); | 340 | toggle_tracing->setText(tr("Start Tracing")); |
| 331 | } | 341 | } |
| 332 | } | 342 | } |
| 343 | |||
| 344 | void GPUCommandListWidget::CopyAllToClipboard() { | ||
| 345 | QClipboard* clipboard = QApplication::clipboard(); | ||
| 346 | QString text; | ||
| 347 | |||
| 348 | QAbstractItemModel* model = static_cast<QAbstractListModel*>(list_widget->model()); | ||
| 349 | |||
| 350 | for (int row = 0; row < model->rowCount({}); ++row) { | ||
| 351 | for (int col = 0; col < model->columnCount({}); ++col) { | ||
| 352 | QModelIndex index = model->index(row, col); | ||
| 353 | text += model->data(index).value<QString>(); | ||
| 354 | text += '\t'; | ||
| 355 | } | ||
| 356 | text += '\n'; | ||
| 357 | } | ||
| 358 | |||
| 359 | clipboard->setText(text); | ||
| 360 | } | ||
diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics_cmdlists.h index a465d044c..4859b6ec8 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.h +++ b/src/citra_qt/debugger/graphics_cmdlists.h | |||
| @@ -49,6 +49,8 @@ public slots: | |||
| 49 | 49 | ||
| 50 | void SetCommandInfo(const QModelIndex&); | 50 | void SetCommandInfo(const QModelIndex&); |
| 51 | 51 | ||
| 52 | void CopyAllToClipboard(); | ||
| 53 | |||
| 52 | signals: | 54 | signals: |
| 53 | void TracingFinished(const Pica::DebugUtils::PicaTrace&); | 55 | void TracingFinished(const Pica::DebugUtils::PicaTrace&); |
| 54 | 56 | ||
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index 6bbe7572c..39eefbf75 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp | |||
| @@ -55,7 +55,9 @@ GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::Debug | |||
| 55 | framebuffer_format_control->addItem(tr("RGBA4")); | 55 | framebuffer_format_control->addItem(tr("RGBA4")); |
| 56 | framebuffer_format_control->addItem(tr("D16")); | 56 | framebuffer_format_control->addItem(tr("D16")); |
| 57 | framebuffer_format_control->addItem(tr("D24")); | 57 | framebuffer_format_control->addItem(tr("D24")); |
| 58 | framebuffer_format_control->addItem(tr("D24S8")); | 58 | framebuffer_format_control->addItem(tr("D24X8")); |
| 59 | framebuffer_format_control->addItem(tr("X24S8")); | ||
| 60 | framebuffer_format_control->addItem(tr("(unknown)")); | ||
| 59 | 61 | ||
| 60 | // TODO: This QLabel should shrink the image to the available space rather than just expanding... | 62 | // TODO: This QLabel should shrink the image to the available space rather than just expanding... |
| 61 | framebuffer_picture_label = new QLabel; | 63 | framebuffer_picture_label = new QLabel; |
| @@ -184,8 +186,32 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 184 | framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); | 186 | framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); |
| 185 | framebuffer_width = framebuffer.GetWidth(); | 187 | framebuffer_width = framebuffer.GetWidth(); |
| 186 | framebuffer_height = framebuffer.GetHeight(); | 188 | framebuffer_height = framebuffer.GetHeight(); |
| 187 | // TODO: It's unknown how this format is actually specified | 189 | |
| 188 | framebuffer_format = Format::RGBA8; | 190 | switch (framebuffer.color_format) { |
| 191 | case Pica::Regs::ColorFormat::RGBA8: | ||
| 192 | framebuffer_format = Format::RGBA8; | ||
| 193 | break; | ||
| 194 | |||
| 195 | case Pica::Regs::ColorFormat::RGB8: | ||
| 196 | framebuffer_format = Format::RGB8; | ||
| 197 | break; | ||
| 198 | |||
| 199 | case Pica::Regs::ColorFormat::RGB5A1: | ||
| 200 | framebuffer_format = Format::RGB5A1; | ||
| 201 | break; | ||
| 202 | |||
| 203 | case Pica::Regs::ColorFormat::RGB565: | ||
| 204 | framebuffer_format = Format::RGB565; | ||
| 205 | break; | ||
| 206 | |||
| 207 | case Pica::Regs::ColorFormat::RGBA4: | ||
| 208 | framebuffer_format = Format::RGBA4; | ||
| 209 | break; | ||
| 210 | |||
| 211 | default: | ||
| 212 | framebuffer_format = Format::Unknown; | ||
| 213 | break; | ||
| 214 | } | ||
| 189 | 215 | ||
| 190 | break; | 216 | break; |
| 191 | } | 217 | } |
| @@ -197,7 +223,24 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 197 | framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress(); | 223 | framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress(); |
| 198 | framebuffer_width = framebuffer.GetWidth(); | 224 | framebuffer_width = framebuffer.GetWidth(); |
| 199 | framebuffer_height = framebuffer.GetHeight(); | 225 | framebuffer_height = framebuffer.GetHeight(); |
| 200 | framebuffer_format = Format::D16; | 226 | |
| 227 | switch (framebuffer.depth_format) { | ||
| 228 | case Pica::Regs::DepthFormat::D16: | ||
| 229 | framebuffer_format = Format::D16; | ||
| 230 | break; | ||
| 231 | |||
| 232 | case Pica::Regs::DepthFormat::D24: | ||
| 233 | framebuffer_format = Format::D24; | ||
| 234 | break; | ||
| 235 | |||
| 236 | case Pica::Regs::DepthFormat::D24S8: | ||
| 237 | framebuffer_format = Format::D24X8; | ||
| 238 | break; | ||
| 239 | |||
| 240 | default: | ||
| 241 | framebuffer_format = Format::Unknown; | ||
| 242 | break; | ||
| 243 | } | ||
| 201 | 244 | ||
| 202 | break; | 245 | break; |
| 203 | } | 246 | } |
| @@ -258,7 +301,7 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 258 | color.b() = (data >> 16) & 0xFF; | 301 | color.b() = (data >> 16) & 0xFF; |
| 259 | break; | 302 | break; |
| 260 | } | 303 | } |
| 261 | case Format::D24S8: | 304 | case Format::D24X8: |
| 262 | { | 305 | { |
| 263 | Math::Vec2<u32> data = Color::DecodeD24S8(pixel); | 306 | Math::Vec2<u32> data = Color::DecodeD24S8(pixel); |
| 264 | color.r() = data.x & 0xFF; | 307 | color.r() = data.x & 0xFF; |
| @@ -266,6 +309,12 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 266 | color.b() = (data.x >> 16) & 0xFF; | 309 | color.b() = (data.x >> 16) & 0xFF; |
| 267 | break; | 310 | break; |
| 268 | } | 311 | } |
| 312 | case Format::X24S8: | ||
| 313 | { | ||
| 314 | Math::Vec2<u32> data = Color::DecodeD24S8(pixel); | ||
| 315 | color.r() = color.g() = color.b() = data.y; | ||
| 316 | break; | ||
| 317 | } | ||
| 269 | default: | 318 | default: |
| 270 | qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); | 319 | qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); |
| 271 | break; | 320 | break; |
| @@ -286,7 +335,8 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 286 | u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) { | 335 | u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) { |
| 287 | switch (format) { | 336 | switch (format) { |
| 288 | case Format::RGBA8: | 337 | case Format::RGBA8: |
| 289 | case Format::D24S8: | 338 | case Format::D24X8: |
| 339 | case Format::X24S8: | ||
| 290 | return 4; | 340 | return 4; |
| 291 | case Format::RGB8: | 341 | case Format::RGB8: |
| 292 | case Format::D24: | 342 | case Format::D24: |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h index 4cb396ffe..e9eae679f 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.h +++ b/src/citra_qt/debugger/graphics_framebuffer.h | |||
| @@ -35,7 +35,9 @@ class GraphicsFramebufferWidget : public BreakPointObserverDock { | |||
| 35 | RGBA4 = 4, | 35 | RGBA4 = 4, |
| 36 | D16 = 5, | 36 | D16 = 5, |
| 37 | D24 = 6, | 37 | D24 = 6, |
| 38 | D24S8 = 7 | 38 | D24X8 = 7, |
| 39 | X24S8 = 8, | ||
| 40 | Unknown = 9 | ||
| 39 | }; | 41 | }; |
| 40 | 42 | ||
| 41 | static u32 BytesPerPixel(Format format); | 43 | static u32 BytesPerPixel(Format format); |
diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp new file mode 100644 index 000000000..3f20f149d --- /dev/null +++ b/src/citra_qt/debugger/graphics_tracing.cpp | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | |||
| 7 | #include <QBoxLayout> | ||
| 8 | #include <QComboBox> | ||
| 9 | #include <QFileDialog> | ||
| 10 | #include <QLabel> | ||
| 11 | #include <QMessageBox> | ||
| 12 | #include <QPushButton> | ||
| 13 | #include <QSpinBox> | ||
| 14 | |||
| 15 | #include <boost/range/algorithm/copy.hpp> | ||
| 16 | |||
| 17 | #include "core/hw/gpu.h" | ||
| 18 | #include "core/hw/lcd.h" | ||
| 19 | |||
| 20 | #include "video_core/pica.h" | ||
| 21 | |||
| 22 | #include "nihstro/float24.h" | ||
| 23 | |||
| 24 | #include "graphics_tracing.h" | ||
| 25 | |||
| 26 | GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 27 | QWidget* parent) | ||
| 28 | : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) { | ||
| 29 | |||
| 30 | setObjectName("CiTracing"); | ||
| 31 | |||
| 32 | QPushButton* start_recording = new QPushButton(tr("Start Recording")); | ||
| 33 | QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); | ||
| 34 | QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); | ||
| 35 | |||
| 36 | connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); | ||
| 37 | connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); | ||
| 38 | connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); | ||
| 39 | connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); | ||
| 40 | connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); | ||
| 41 | connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); | ||
| 42 | |||
| 43 | stop_recording->setVisible(false); | ||
| 44 | abort_recording->setVisible(false); | ||
| 45 | |||
| 46 | auto main_widget = new QWidget; | ||
| 47 | auto main_layout = new QVBoxLayout; | ||
| 48 | { | ||
| 49 | auto sub_layout = new QHBoxLayout; | ||
| 50 | sub_layout->addWidget(start_recording); | ||
| 51 | sub_layout->addWidget(stop_recording); | ||
| 52 | sub_layout->addWidget(abort_recording); | ||
| 53 | main_layout->addLayout(sub_layout); | ||
| 54 | } | ||
| 55 | main_widget->setLayout(main_layout); | ||
| 56 | setWidget(main_widget); | ||
| 57 | } | ||
| 58 | |||
| 59 | void GraphicsTracingWidget::StartRecording() { | ||
| 60 | auto context = context_weak.lock(); | ||
| 61 | if (!context) | ||
| 62 | return; | ||
| 63 | |||
| 64 | auto shader_binary = Pica::g_state.vs.program_code; | ||
| 65 | auto swizzle_data = Pica::g_state.vs.swizzle_data; | ||
| 66 | |||
| 67 | // Encode floating point numbers to 24-bit values | ||
| 68 | // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally. | ||
| 69 | std::array<uint32_t, 4 * 16> default_attributes; | ||
| 70 | for (unsigned i = 0; i < 16; ++i) { | ||
| 71 | for (unsigned comp = 0; comp < 3; ++comp) { | ||
| 72 | default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.default_attributes[i][comp].ToFloat32()); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | std::array<uint32_t, 4 * 96> vs_float_uniforms; | ||
| 77 | for (unsigned i = 0; i < 96; ++i) | ||
| 78 | for (unsigned comp = 0; comp < 3; ++comp) | ||
| 79 | vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); | ||
| 80 | |||
| 81 | CiTrace::Recorder::InitialState state; | ||
| 82 | std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); | ||
| 83 | std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); | ||
| 84 | std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); | ||
| 85 | boost::copy(default_attributes, std::back_inserter(state.default_attributes)); | ||
| 86 | boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); | ||
| 87 | boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); | ||
| 88 | boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); | ||
| 89 | //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary)); | ||
| 90 | //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data)); | ||
| 91 | //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms)); | ||
| 92 | |||
| 93 | auto recorder = new CiTrace::Recorder(state); | ||
| 94 | context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); | ||
| 95 | |||
| 96 | emit SetStartTracingButtonEnabled(false); | ||
| 97 | emit SetStopTracingButtonEnabled(true); | ||
| 98 | emit SetAbortTracingButtonEnabled(true); | ||
| 99 | } | ||
| 100 | |||
| 101 | void GraphicsTracingWidget::StopRecording() { | ||
| 102 | auto context = context_weak.lock(); | ||
| 103 | if (!context) | ||
| 104 | return; | ||
| 105 | |||
| 106 | QString filename = QFileDialog::getSaveFileName(this, tr("Save CiTrace"), "citrace.ctf", | ||
| 107 | tr("CiTrace File (*.ctf)")); | ||
| 108 | |||
| 109 | if (filename.isEmpty()) { | ||
| 110 | // If the user canceled the dialog, keep recording | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | context->recorder->Finish(filename.toStdString()); | ||
| 115 | context->recorder = nullptr; | ||
| 116 | |||
| 117 | emit SetStopTracingButtonEnabled(false); | ||
| 118 | emit SetAbortTracingButtonEnabled(false); | ||
| 119 | emit SetStartTracingButtonEnabled(true); | ||
| 120 | } | ||
| 121 | |||
| 122 | void GraphicsTracingWidget::AbortRecording() { | ||
| 123 | auto context = context_weak.lock(); | ||
| 124 | if (!context) | ||
| 125 | return; | ||
| 126 | |||
| 127 | context->recorder = nullptr; | ||
| 128 | |||
| 129 | emit SetStopTracingButtonEnabled(false); | ||
| 130 | emit SetAbortTracingButtonEnabled(false); | ||
| 131 | emit SetStartTracingButtonEnabled(true); | ||
| 132 | } | ||
| 133 | |||
| 134 | void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | ||
| 135 | widget()->setEnabled(true); | ||
| 136 | } | ||
| 137 | |||
| 138 | void GraphicsTracingWidget::OnResumed() { | ||
| 139 | widget()->setEnabled(false); | ||
| 140 | } | ||
| 141 | |||
| 142 | void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) { | ||
| 143 | // Disable tracing starting/stopping until a GPU breakpoint is reached | ||
| 144 | widget()->setEnabled(false); | ||
| 145 | } | ||
| 146 | |||
| 147 | void GraphicsTracingWidget::OnEmulationStopping() { | ||
| 148 | // TODO: Is it safe to access the context here? | ||
| 149 | |||
| 150 | auto context = context_weak.lock(); | ||
| 151 | if (!context) | ||
| 152 | return; | ||
| 153 | |||
| 154 | |||
| 155 | if (context->recorder) { | ||
| 156 | auto reply = QMessageBox::question(this, tr("CiTracing still active"), | ||
| 157 | tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), | ||
| 158 | QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); | ||
| 159 | |||
| 160 | if (reply == QMessageBox::Yes) { | ||
| 161 | StopRecording(); | ||
| 162 | } else { | ||
| 163 | AbortRecording(); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | // If the widget was disabled before, enable it now to allow starting | ||
| 168 | // tracing before starting the next emulation session | ||
| 169 | widget()->setEnabled(true); | ||
| 170 | } | ||
diff --git a/src/citra_qt/debugger/graphics_tracing.h b/src/citra_qt/debugger/graphics_tracing.h new file mode 100644 index 000000000..2a0e4819b --- /dev/null +++ b/src/citra_qt/debugger/graphics_tracing.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "graphics_breakpoint_observer.h" | ||
| 8 | |||
| 9 | class EmuThread; | ||
| 10 | |||
| 11 | class GraphicsTracingWidget : public BreakPointObserverDock { | ||
| 12 | Q_OBJECT | ||
| 13 | |||
| 14 | public: | ||
| 15 | GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); | ||
| 16 | |||
| 17 | private slots: | ||
| 18 | void StartRecording(); | ||
| 19 | void StopRecording(); | ||
| 20 | void AbortRecording(); | ||
| 21 | |||
| 22 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 23 | void OnResumed() override; | ||
| 24 | |||
| 25 | void OnEmulationStarting(EmuThread* emu_thread); | ||
| 26 | void OnEmulationStopping(); | ||
| 27 | |||
| 28 | signals: | ||
| 29 | void SetStartTracingButtonEnabled(bool enable); | ||
| 30 | void SetStopTracingButtonEnabled(bool enable); | ||
| 31 | void SetAbortTracingButtonEnabled(bool enable); | ||
| 32 | }; | ||
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp index 14d3f8f39..f42a2f4ce 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp | |||
| @@ -34,7 +34,7 @@ int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | |||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { | 36 | int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { |
| 37 | return info.code.size(); | 37 | return static_cast<int>(info.code.size()); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { | 40 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { |
| @@ -259,7 +259,7 @@ void GraphicsVertexShaderModel::OnUpdate() | |||
| 259 | for (auto pattern : Pica::g_state.vs.swizzle_data) | 259 | for (auto pattern : Pica::g_state.vs.swizzle_data) |
| 260 | info.swizzle_info.push_back({pattern}); | 260 | info.swizzle_info.push_back({pattern}); |
| 261 | 261 | ||
| 262 | info.labels.insert({ Pica::g_state.regs.vs_main_offset, "main" }); | 262 | info.labels.insert({ Pica::g_state.regs.vs.main_offset, "main" }); |
| 263 | 263 | ||
| 264 | endResetModel(); | 264 | endResetModel(); |
| 265 | } | 265 | } |
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 2ac1748b7..89b28c2f4 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp | |||
| @@ -74,7 +74,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const | |||
| 74 | if (parent.isValid()) { | 74 | if (parent.isValid()) { |
| 75 | return 0; | 75 | return 0; |
| 76 | } else { | 76 | } else { |
| 77 | return results.time_per_category.size() + 2; | 77 | return static_cast<int>(results.time_per_category.size() + 2); |
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index 322c25c9e..5ed6cf0b1 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp | |||
| @@ -2,10 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | ||
| 6 | |||
| 5 | #include <QKeySequence> | 7 | #include <QKeySequence> |
| 6 | #include <QSettings> | 8 | #include <QSettings> |
| 9 | #include <QShortcut> | ||
| 10 | |||
| 7 | #include "hotkeys.h" | 11 | #include "hotkeys.h" |
| 8 | #include <map> | ||
| 9 | 12 | ||
| 10 | struct Hotkey | 13 | struct Hotkey |
| 11 | { | 14 | { |
diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 75c7cc625..2317f8188 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h | |||
| @@ -2,12 +2,12 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QShortcut> | ||
| 6 | #include <QDialog> | ||
| 7 | #include "ui_hotkeys.h" | 5 | #include "ui_hotkeys.h" |
| 8 | 6 | ||
| 7 | class QDialog; | ||
| 9 | class QKeySequence; | 8 | class QKeySequence; |
| 10 | class QSettings; | 9 | class QSettings; |
| 10 | class QShortcut; | ||
| 11 | 11 | ||
| 12 | /** | 12 | /** |
| 13 | * Register a hotkey. | 13 | * Register a hotkey. |
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 8041816a0..2746de779 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -10,18 +10,16 @@ | |||
| 10 | #include "qhexedit.h" | 10 | #include "qhexedit.h" |
| 11 | #include "main.h" | 11 | #include "main.h" |
| 12 | 12 | ||
| 13 | #include "common/string_util.h" | ||
| 13 | #include "common/logging/text_formatter.h" | 14 | #include "common/logging/text_formatter.h" |
| 14 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 15 | #include "common/logging/backend.h" | 16 | #include "common/logging/backend.h" |
| 16 | #include "common/logging/filter.h" | 17 | #include "common/logging/filter.h" |
| 17 | #include "common/make_unique.h" | 18 | #include "common/make_unique.h" |
| 18 | #include "common/platform.h" | 19 | #include "common/platform.h" |
| 20 | #include "common/scm_rev.h" | ||
| 19 | #include "common/scope_exit.h" | 21 | #include "common/scope_exit.h" |
| 20 | 22 | ||
| 21 | #if EMU_PLATFORM == PLATFORM_LINUX | ||
| 22 | #include <unistd.h> | ||
| 23 | #endif | ||
| 24 | |||
| 25 | #include "bootmanager.h" | 23 | #include "bootmanager.h" |
| 26 | #include "hotkeys.h" | 24 | #include "hotkeys.h" |
| 27 | 25 | ||
| @@ -34,6 +32,7 @@ | |||
| 34 | #include "debugger/graphics_breakpoints.h" | 32 | #include "debugger/graphics_breakpoints.h" |
| 35 | #include "debugger/graphics_cmdlists.h" | 33 | #include "debugger/graphics_cmdlists.h" |
| 36 | #include "debugger/graphics_framebuffer.h" | 34 | #include "debugger/graphics_framebuffer.h" |
| 35 | #include "debugger/graphics_tracing.h" | ||
| 37 | #include "debugger/graphics_vertex_shader.h" | 36 | #include "debugger/graphics_vertex_shader.h" |
| 38 | #include "debugger/profiler.h" | 37 | #include "debugger/profiler.h" |
| 39 | 38 | ||
| @@ -96,6 +95,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
| 96 | addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); | 95 | addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); |
| 97 | graphicsVertexShaderWidget->hide(); | 96 | graphicsVertexShaderWidget->hide(); |
| 98 | 97 | ||
| 98 | auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this); | ||
| 99 | addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); | ||
| 100 | graphicsTracingWidget->hide(); | ||
| 101 | |||
| 99 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | 102 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |
| 100 | debug_menu->addAction(profilerWidget->toggleViewAction()); | 103 | debug_menu->addAction(profilerWidget->toggleViewAction()); |
| 101 | debug_menu->addAction(disasmWidget->toggleViewAction()); | 104 | debug_menu->addAction(disasmWidget->toggleViewAction()); |
| @@ -106,6 +109,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
| 106 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | 109 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |
| 107 | debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | 110 | debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); |
| 108 | debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); | 111 | debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); |
| 112 | debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); | ||
| 109 | 113 | ||
| 110 | // Set default UI state | 114 | // Set default UI state |
| 111 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half | 115 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half |
| @@ -150,6 +154,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
| 150 | connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); | 154 | connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); |
| 151 | connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); | 155 | connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); |
| 152 | connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); | 156 | connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); |
| 157 | connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); | ||
| 158 | connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); | ||
| 159 | |||
| 153 | 160 | ||
| 154 | // Setup hotkeys | 161 | // Setup hotkeys |
| 155 | RegisterHotkey("Main Window", "Load File", QKeySequence::Open); | 162 | RegisterHotkey("Main Window", "Load File", QKeySequence::Open); |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e78f4f144..4c086cd2f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -31,7 +31,6 @@ set(HEADERS | |||
| 31 | cpu_detect.h | 31 | cpu_detect.h |
| 32 | debug_interface.h | 32 | debug_interface.h |
| 33 | emu_window.h | 33 | emu_window.h |
| 34 | fifo_queue.h | ||
| 35 | file_util.h | 34 | file_util.h |
| 36 | key_map.h | 35 | key_map.h |
| 37 | linear_disk_cache.h | 36 | linear_disk_cache.h |
| @@ -53,7 +52,6 @@ set(HEADERS | |||
| 53 | synchronized_wrapper.h | 52 | synchronized_wrapper.h |
| 54 | thread.h | 53 | thread.h |
| 55 | thread_queue_list.h | 54 | thread_queue_list.h |
| 56 | thunk.h | ||
| 57 | timer.h | 55 | timer.h |
| 58 | vector_math.h | 56 | vector_math.h |
| 59 | ) | 57 | ) |
diff --git a/src/common/assert.h b/src/common/assert.h index 7b7d8bf28..6849778b7 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstdio> | ||
| 8 | #include <cstdlib> | 7 | #include <cstdlib> |
| 9 | 8 | ||
| 10 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 1f3ecf844..f64ebdaf6 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | 32 | ||
| 33 | #pragma once | 33 | #pragma once |
| 34 | 34 | ||
| 35 | #include <cstddef> | ||
| 35 | #include <limits> | 36 | #include <limits> |
| 36 | #include <type_traits> | 37 | #include <type_traits> |
| 37 | 38 | ||
| @@ -160,7 +161,7 @@ public: | |||
| 160 | if (std::numeric_limits<T>::is_signed) | 161 | if (std::numeric_limits<T>::is_signed) |
| 161 | { | 162 | { |
| 162 | std::size_t shift = 8 * sizeof(T)-bits; | 163 | std::size_t shift = 8 * sizeof(T)-bits; |
| 163 | return (T)(((storage & GetMask()) << (shift - position)) >> shift); | 164 | return (T)((storage << (shift - position)) >> shift); |
| 164 | } | 165 | } |
| 165 | else | 166 | else |
| 166 | { | 167 | { |
| @@ -188,7 +189,7 @@ private: | |||
| 188 | 189 | ||
| 189 | __forceinline StorageType GetMask() const | 190 | __forceinline StorageType GetMask() const |
| 190 | { | 191 | { |
| 191 | return ((~(StorageTypeU)0) >> (8 * sizeof(T)-bits)) << position; | 192 | return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position; |
| 192 | } | 193 | } |
| 193 | 194 | ||
| 194 | StorageType storage; | 195 | StorageType storage; |
diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h index dcd80525e..8be0b1109 100644 --- a/src/common/chunk_file.h +++ b/src/common/chunk_file.h | |||
| @@ -26,16 +26,18 @@ | |||
| 26 | // - Zero backwards/forwards compatibility | 26 | // - Zero backwards/forwards compatibility |
| 27 | // - Serialization code for anything complex has to be manually written. | 27 | // - Serialization code for anything complex has to be manually written. |
| 28 | 28 | ||
| 29 | #include <map> | 29 | #include <cstring> |
| 30 | #include <vector> | ||
| 31 | #include <deque> | 30 | #include <deque> |
| 32 | #include <string> | ||
| 33 | #include <list> | 31 | #include <list> |
| 32 | #include <map> | ||
| 34 | #include <set> | 33 | #include <set> |
| 34 | #include <string> | ||
| 35 | #include <type_traits> | 35 | #include <type_traits> |
| 36 | #include <utility> | ||
| 37 | #include <vector> | ||
| 36 | 38 | ||
| 39 | #include "common/assert.h" | ||
| 37 | #include "common/common_types.h" | 40 | #include "common/common_types.h" |
| 38 | #include "common/file_util.h" | ||
| 39 | #include "common/logging/log.h" | 41 | #include "common/logging/log.h" |
| 40 | 42 | ||
| 41 | template <class T> | 43 | template <class T> |
diff --git a/src/common/color.h b/src/common/color.h index 422fdc8af..9dafdca0c 100644 --- a/src/common/color.h +++ b/src/common/color.h | |||
| @@ -208,7 +208,32 @@ inline void EncodeD24(u32 value, u8* bytes) { | |||
| 208 | * @param bytes Pointer where to store the encoded value | 208 | * @param bytes Pointer where to store the encoded value |
| 209 | */ | 209 | */ |
| 210 | inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) { | 210 | inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) { |
| 211 | *reinterpret_cast<u32_le*>(bytes) = (stencil << 24) | depth; | 211 | bytes[0] = depth & 0xFF; |
| 212 | bytes[1] = (depth >> 8) & 0xFF; | ||
| 213 | bytes[2] = (depth >> 16) & 0xFF; | ||
| 214 | bytes[3] = stencil; | ||
| 215 | } | ||
| 216 | |||
| 217 | /** | ||
| 218 | * Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused) | ||
| 219 | * @param depth 24 bit source depth value to encode | ||
| 220 | * @param bytes Pointer where to store the encoded value | ||
| 221 | * @note unused bits will not be modified | ||
| 222 | */ | ||
| 223 | inline void EncodeD24X8(u32 depth, u8* bytes) { | ||
| 224 | bytes[0] = depth & 0xFF; | ||
| 225 | bytes[1] = (depth >> 8) & 0xFF; | ||
| 226 | bytes[2] = (depth >> 16) & 0xFF; | ||
| 227 | } | ||
| 228 | |||
| 229 | /** | ||
| 230 | * Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused) | ||
| 231 | * @param stencil 8 bit source stencil value to encode | ||
| 232 | * @param bytes Pointer where to store the encoded value | ||
| 233 | * @note unused bits will not be modified | ||
| 234 | */ | ||
| 235 | inline void EncodeX24S8(u8 stencil, u8* bytes) { | ||
| 236 | bytes[3] = stencil; | ||
| 212 | } | 237 | } |
| 213 | 238 | ||
| 214 | } // namespace | 239 | } // namespace |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 91b74c6bc..59bd16dbf 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -5,15 +5,6 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common_types.h" | 7 | #include "common_types.h" |
| 8 | #include <cstdlib> | ||
| 9 | |||
| 10 | |||
| 11 | #define b2(x) ( (x) | ( (x) >> 1) ) | ||
| 12 | #define b4(x) ( b2(x) | ( b2(x) >> 2) ) | ||
| 13 | #define b8(x) ( b4(x) | ( b4(x) >> 4) ) | ||
| 14 | #define b16(x) ( b8(x) | ( b8(x) >> 8) ) | ||
| 15 | #define b32(x) (b16(x) | (b16(x) >>16) ) | ||
| 16 | #define ROUND_UP_POW2(x) (b32(x - 1) + 1) | ||
| 17 | 8 | ||
| 18 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | 9 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| 19 | 10 | ||
| @@ -43,8 +34,6 @@ | |||
| 43 | 34 | ||
| 44 | #ifndef _MSC_VER | 35 | #ifndef _MSC_VER |
| 45 | 36 | ||
| 46 | #include <errno.h> | ||
| 47 | |||
| 48 | #if defined(__x86_64__) || defined(_M_X64) | 37 | #if defined(__x86_64__) || defined(_M_X64) |
| 49 | #define Crash() __asm__ __volatile__("int $3") | 38 | #define Crash() __asm__ __volatile__("int $3") |
| 50 | #elif defined(_M_ARM) | 39 | #elif defined(_M_ARM) |
| @@ -80,8 +69,10 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| 80 | } | 69 | } |
| 81 | 70 | ||
| 82 | #else // _MSC_VER | 71 | #else // _MSC_VER |
| 83 | // Function Cross-Compatibility | 72 | #if (_MSC_VER < 1900) |
| 84 | #define snprintf _snprintf | 73 | // Function Cross-Compatibility |
| 74 | #define snprintf _snprintf | ||
| 75 | #endif | ||
| 85 | 76 | ||
| 86 | // Locale Cross-Compatibility | 77 | // Locale Cross-Compatibility |
| 87 | #define locale_t _locale_t | 78 | #define locale_t _locale_t |
diff --git a/src/common/common_types.h b/src/common/common_types.h index f6de0adfc..fa3e0b8d6 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h | |||
| @@ -24,9 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | #pragma once | 25 | #pragma once |
| 26 | 26 | ||
| 27 | #include <cmath> | ||
| 28 | #include <cstdint> | 27 | #include <cstdint> |
| 29 | #include <cstdlib> | ||
| 30 | 28 | ||
| 31 | #ifdef _MSC_VER | 29 | #ifdef _MSC_VER |
| 32 | #ifndef __func__ | 30 | #ifndef __func__ |
| @@ -52,32 +50,6 @@ typedef double f64; ///< 64-bit floating point | |||
| 52 | typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. | 50 | typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. |
| 53 | typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. | 51 | typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. |
| 54 | 52 | ||
| 55 | /// Union for fast 16-bit type casting | ||
| 56 | union t16 { | ||
| 57 | u8 _u8[2]; ///< 8-bit unsigned char(s) | ||
| 58 | u16 _u16; ///< 16-bit unsigned shorts(s) | ||
| 59 | }; | ||
| 60 | |||
| 61 | /// Union for fast 32-bit type casting | ||
| 62 | union t32 { | ||
| 63 | f32 _f32; ///< 32-bit floating point(s) | ||
| 64 | u32 _u32; ///< 32-bit unsigned int(s) | ||
| 65 | s32 _s32; ///< 32-bit signed int(s) | ||
| 66 | u16 _u16[2]; ///< 16-bit unsigned shorts(s) | ||
| 67 | u8 _u8[4]; ///< 8-bit unsigned char(s) | ||
| 68 | }; | ||
| 69 | |||
| 70 | /// Union for fast 64-bit type casting | ||
| 71 | union t64 { | ||
| 72 | f64 _f64; ///< 64-bit floating point | ||
| 73 | u64 _u64; ///< 64-bit unsigned long | ||
| 74 | f32 _f32[2]; ///< 32-bit floating point(s) | ||
| 75 | u32 _u32[2]; ///< 32-bit unsigned int(s) | ||
| 76 | s32 _s32[2]; ///< 32-bit signed int(s) | ||
| 77 | u16 _u16[4]; ///< 16-bit unsigned shorts(s) | ||
| 78 | u8 _u8[8]; ///< 8-bit unsigned char(s) | ||
| 79 | }; | ||
| 80 | |||
| 81 | // An inheritable class to disallow the copy constructor and operator= functions | 53 | // An inheritable class to disallow the copy constructor and operator= functions |
| 82 | class NonCopyable { | 54 | class NonCopyable { |
| 83 | protected: | 55 | protected: |
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 43facb85c..b69b05cb9 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp | |||
| @@ -2,6 +2,12 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | |||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/key_map.h" | ||
| 10 | |||
| 5 | #include "emu_window.h" | 11 | #include "emu_window.h" |
| 6 | #include "video_core/video_core.h" | 12 | #include "video_core/video_core.h" |
| 7 | 13 | ||
diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 8eca6b5d5..a0ae4c9fa 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h | |||
| @@ -4,11 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <tuple> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | #include "common/key_map.h" | ||
| 9 | #include "common/math_util.h" | 11 | #include "common/math_util.h" |
| 10 | #include "common/scm_rev.h" | 12 | |
| 11 | #include "common/string_util.h" | 13 | #include "core/hle/service/hid/hid.h" |
| 14 | |||
| 15 | namespace KeyMap { | ||
| 16 | struct HostDeviceKey; | ||
| 17 | } | ||
| 12 | 18 | ||
| 13 | /** | 19 | /** |
| 14 | * Abstraction class used to provide an interface between emulation code and the frontend | 20 | * Abstraction class used to provide an interface between emulation code and the frontend |
diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h deleted file mode 100644 index b426e6596..000000000 --- a/src/common/fifo_queue.h +++ /dev/null | |||
| @@ -1,111 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | // a simple lockless thread-safe, | ||
| 4 | // single reader, single writer queue | ||
| 5 | |||
| 6 | #include "common/atomic.h" | ||
| 7 | |||
| 8 | namespace Common | ||
| 9 | { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | class FifoQueue | ||
| 13 | { | ||
| 14 | public: | ||
| 15 | FifoQueue() : m_size(0) | ||
| 16 | { | ||
| 17 | m_write_ptr = m_read_ptr = new ElementPtr(); | ||
| 18 | } | ||
| 19 | |||
| 20 | ~FifoQueue() | ||
| 21 | { | ||
| 22 | // this will empty out the whole queue | ||
| 23 | delete m_read_ptr; | ||
| 24 | } | ||
| 25 | |||
| 26 | u32 Size() const | ||
| 27 | { | ||
| 28 | return m_size; | ||
| 29 | } | ||
| 30 | |||
| 31 | bool Empty() const | ||
| 32 | { | ||
| 33 | //return (m_read_ptr == m_write_ptr); | ||
| 34 | return (0 == m_size); | ||
| 35 | } | ||
| 36 | |||
| 37 | T& Front() const | ||
| 38 | { | ||
| 39 | return *m_read_ptr->current; | ||
| 40 | } | ||
| 41 | |||
| 42 | template <typename Arg> | ||
| 43 | void Push(Arg&& t) | ||
| 44 | { | ||
| 45 | // create the element, add it to the queue | ||
| 46 | m_write_ptr->current = new T(std::forward<Arg>(t)); | ||
| 47 | // set the next pointer to a new element ptr | ||
| 48 | // then advance the write pointer | ||
| 49 | m_write_ptr = m_write_ptr->next = new ElementPtr(); | ||
| 50 | Common::AtomicIncrement(m_size); | ||
| 51 | } | ||
| 52 | |||
| 53 | void Pop() | ||
| 54 | { | ||
| 55 | Common::AtomicDecrement(m_size); | ||
| 56 | ElementPtr *const tmpptr = m_read_ptr; | ||
| 57 | // advance the read pointer | ||
| 58 | m_read_ptr = m_read_ptr->next; | ||
| 59 | // set the next element to NULL to stop the recursive deletion | ||
| 60 | tmpptr->next = nullptr; | ||
| 61 | delete tmpptr; // this also deletes the element | ||
| 62 | } | ||
| 63 | |||
| 64 | bool Pop(T& t) | ||
| 65 | { | ||
| 66 | if (Empty()) | ||
| 67 | return false; | ||
| 68 | |||
| 69 | t = std::move(Front()); | ||
| 70 | Pop(); | ||
| 71 | |||
| 72 | return true; | ||
| 73 | } | ||
| 74 | |||
| 75 | // not thread-safe | ||
| 76 | void Clear() | ||
| 77 | { | ||
| 78 | m_size = 0; | ||
| 79 | delete m_read_ptr; | ||
| 80 | m_write_ptr = m_read_ptr = new ElementPtr(); | ||
| 81 | } | ||
| 82 | |||
| 83 | private: | ||
| 84 | // stores a pointer to element | ||
| 85 | // and a pointer to the next ElementPtr | ||
| 86 | class ElementPtr | ||
| 87 | { | ||
| 88 | public: | ||
| 89 | ElementPtr() : current(nullptr), next(nullptr) {} | ||
| 90 | |||
| 91 | ~ElementPtr() | ||
| 92 | { | ||
| 93 | if (current) | ||
| 94 | { | ||
| 95 | delete current; | ||
| 96 | // recusion ftw | ||
| 97 | if (next) | ||
| 98 | delete next; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | T *volatile current; | ||
| 103 | ElementPtr *volatile next; | ||
| 104 | }; | ||
| 105 | |||
| 106 | ElementPtr *volatile m_write_ptr; | ||
| 107 | ElementPtr *volatile m_read_ptr; | ||
| 108 | volatile u32 m_size; | ||
| 109 | }; | ||
| 110 | |||
| 111 | } | ||
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 24648ea33..836b58d52 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | #include <direct.h> // getcwd | 17 | #include <direct.h> // getcwd |
| 18 | #include <tchar.h> | 18 | #include <tchar.h> |
| 19 | 19 | ||
| 20 | #include "common/string_util.h" | ||
| 21 | |||
| 20 | // 64 bit offsets for windows | 22 | // 64 bit offsets for windows |
| 21 | #define fseeko _fseeki64 | 23 | #define fseeko _fseeki64 |
| 22 | #define ftello _ftelli64 | 24 | #define ftello _ftelli64 |
| @@ -25,8 +27,13 @@ | |||
| 25 | #define fstat64 _fstat64 | 27 | #define fstat64 _fstat64 |
| 26 | #define fileno _fileno | 28 | #define fileno _fileno |
| 27 | #else | 29 | #else |
| 28 | #include <sys/param.h> | 30 | #ifdef __APPLE__ |
| 29 | #include <sys/types.h> | 31 | #include <sys/param.h> |
| 32 | #endif | ||
| 33 | #include <cctype> | ||
| 34 | #include <cerrno> | ||
| 35 | #include <cstdlib> | ||
| 36 | #include <cstring> | ||
| 30 | #include <dirent.h> | 37 | #include <dirent.h> |
| 31 | #include <pwd.h> | 38 | #include <pwd.h> |
| 32 | #include <unistd.h> | 39 | #include <unistd.h> |
diff --git a/src/common/file_util.h b/src/common/file_util.h index b65829291..d0dccdf69 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -6,13 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <fstream> | 8 | #include <fstream> |
| 9 | #include <cstddef> | ||
| 9 | #include <cstdio> | 10 | #include <cstdio> |
| 10 | #include <cstring> | ||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | 13 | ||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/string_util.h" | ||
| 16 | 15 | ||
| 17 | // User directory indices for GetUserPath | 16 | // User directory indices for GetUserPath |
| 18 | enum { | 17 | enum { |
| @@ -117,9 +116,6 @@ bool SetCurrentDir(const std::string &directory); | |||
| 117 | // directory. To be used in "multi-user" mode (that is, installed). | 116 | // directory. To be used in "multi-user" mode (that is, installed). |
| 118 | const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath=""); | 117 | const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath=""); |
| 119 | 118 | ||
| 120 | // probably doesn't belong here | ||
| 121 | //std::string GetThemeDir(const std::string& theme_name); | ||
| 122 | |||
| 123 | // Returns the path to where the sys file are | 119 | // Returns the path to where the sys file are |
| 124 | std::string GetSysDirectory(); | 120 | std::string GetSysDirectory(); |
| 125 | 121 | ||
| @@ -182,6 +178,10 @@ public: | |||
| 182 | template <typename T> | 178 | template <typename T> |
| 183 | size_t WriteArray(const T* data, size_t length) | 179 | size_t WriteArray(const T* data, size_t length) |
| 184 | { | 180 | { |
| 181 | static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects"); | ||
| 182 | // TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here. | ||
| 183 | //static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects"); | ||
| 184 | |||
| 185 | if (!IsOpen()) { | 185 | if (!IsOpen()) { |
| 186 | m_good = false; | 186 | m_good = false; |
| 187 | return -1; | 187 | return -1; |
| @@ -204,6 +204,12 @@ public: | |||
| 204 | return WriteArray(reinterpret_cast<const char*>(data), length); | 204 | return WriteArray(reinterpret_cast<const char*>(data), length); |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | template<typename T> | ||
| 208 | size_t WriteObject(const T& object) { | ||
| 209 | static_assert(!std::is_pointer<T>::value, "Given object is a pointer"); | ||
| 210 | return WriteArray(&object, 1); | ||
| 211 | } | ||
| 212 | |||
| 207 | bool IsOpen() { return nullptr != m_file; } | 213 | bool IsOpen() { return nullptr != m_file; } |
| 208 | 214 | ||
| 209 | // m_good is set to false when a read, write or other function fails | 215 | // m_good is set to false when a read, write or other function fails |
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 0b71ea3b2..a2b4eca43 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | 10 | ||
| 10 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 5b3a731e9..e16dde7fc 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -4,10 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cassert> | ||
| 8 | #include <chrono> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 12 | 8 | ||
| 13 | namespace Log { | 9 | namespace Log { |
diff --git a/src/common/make_unique.h b/src/common/make_unique.h index 2a7b76412..f6e7f017c 100644 --- a/src/common/make_unique.h +++ b/src/common/make_unique.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | 9 | ||
| 9 | namespace Common { | 10 | namespace Common { |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 20b791a10..2b3ace528 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp | |||
| @@ -3,14 +3,17 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 8 | #include "common/memory_util.h" | 7 | #include "common/memory_util.h" |
| 9 | #include "common/string_util.h" | ||
| 10 | 8 | ||
| 11 | #ifdef _WIN32 | 9 | #ifdef _WIN32 |
| 12 | #include <windows.h> | 10 | #include <windows.h> |
| 13 | #include <psapi.h> | 11 | #include <psapi.h> |
| 12 | #include "common/common_funcs.h" | ||
| 13 | #include "common/string_util.h" | ||
| 14 | #else | ||
| 15 | #include <cstdlib> | ||
| 16 | #include <sys/mman.h> | ||
| 14 | #endif | 17 | #endif |
| 15 | 18 | ||
| 16 | #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) | 19 | #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) |
diff --git a/src/common/memory_util.h b/src/common/memory_util.h index 9fdbf1f12..9bf37c44f 100644 --- a/src/common/memory_util.h +++ b/src/common/memory_util.h | |||
| @@ -4,9 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #ifndef _WIN32 | 7 | #include <cstddef> |
| 8 | #include <sys/mman.h> | ||
| 9 | #endif | ||
| 10 | #include <string> | 8 | #include <string> |
| 11 | 9 | ||
| 12 | void* AllocateExecutableMemory(size_t size, bool low = true); | 10 | void* AllocateExecutableMemory(size_t size, bool low = true); |
diff --git a/src/common/misc.cpp b/src/common/misc.cpp index 53cacf37c..d2a049b63 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp | |||
| @@ -2,12 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_funcs.h" | 5 | #include <cstddef> |
| 6 | 6 | ||
| 7 | #ifdef _WIN32 | 7 | #ifdef _WIN32 |
| 8 | #include <windows.h> | 8 | #include <windows.h> |
| 9 | #else | 9 | #else |
| 10 | #include <string.h> | 10 | #include <cerrno> |
| 11 | #include <cstring> | ||
| 11 | #endif | 12 | #endif |
| 12 | 13 | ||
| 13 | // Neither Android nor OS X support TLS | 14 | // Neither Android nor OS X support TLS |
diff --git a/src/common/platform.h b/src/common/platform.h index df780ac6f..0a912dda3 100644 --- a/src/common/platform.h +++ b/src/common/platform.h | |||
| @@ -24,66 +24,11 @@ | |||
| 24 | 24 | ||
| 25 | #pragma once | 25 | #pragma once |
| 26 | 26 | ||
| 27 | #include "common/common_types.h" | ||
| 28 | |||
| 29 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 30 | // Platform definitions | ||
| 31 | |||
| 32 | /// Enumeration for defining the supported platforms | ||
| 33 | #define PLATFORM_NULL 0 | ||
| 34 | #define PLATFORM_WINDOWS 1 | ||
| 35 | #define PLATFORM_MACOSX 2 | ||
| 36 | #define PLATFORM_LINUX 3 | ||
| 37 | #define PLATFORM_ANDROID 4 | ||
| 38 | |||
| 39 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 27 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 40 | // Platform detection | 28 | // Platform detection |
| 41 | 29 | ||
| 42 | #ifndef EMU_PLATFORM | ||
| 43 | |||
| 44 | #if defined( __WIN32__ ) || defined( _WIN32 ) | ||
| 45 | #define EMU_PLATFORM PLATFORM_WINDOWS | ||
| 46 | |||
| 47 | #elif defined( __APPLE__ ) || defined( __APPLE_CC__ ) | ||
| 48 | #define EMU_PLATFORM PLATFORM_MACOSX | ||
| 49 | |||
| 50 | #elif defined(__linux__) | ||
| 51 | #define EMU_PLATFORM PLATFORM_LINUX | ||
| 52 | |||
| 53 | #else // Assume linux otherwise | ||
| 54 | #define EMU_PLATFORM PLATFORM_LINUX | ||
| 55 | |||
| 56 | #endif | ||
| 57 | |||
| 58 | #endif | ||
| 59 | |||
| 60 | #if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) | 30 | #if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) |
| 61 | #define EMU_ARCH_BITS 64 | 31 | #define EMU_ARCH_BITS 64 |
| 62 | #elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM) | 32 | #elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM) |
| 63 | #define EMU_ARCH_BITS 32 | 33 | #define EMU_ARCH_BITS 32 |
| 64 | #endif | 34 | #endif |
| 65 | |||
| 66 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 67 | // Feature detection | ||
| 68 | |||
| 69 | #if defined _M_GENERIC | ||
| 70 | # define _M_SSE 0x0 | ||
| 71 | #elif defined __GNUC__ | ||
| 72 | # if defined __SSE4_2__ | ||
| 73 | # define _M_SSE 0x402 | ||
| 74 | # elif defined __SSE4_1__ | ||
| 75 | # define _M_SSE 0x401 | ||
| 76 | # elif defined __SSSE3__ | ||
| 77 | # define _M_SSE 0x301 | ||
| 78 | # elif defined __SSE3__ | ||
| 79 | # define _M_SSE 0x300 | ||
| 80 | # endif | ||
| 81 | #elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008 | ||
| 82 | # define _M_SSE 0x402 | ||
| 83 | #endif | ||
| 84 | |||
| 85 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 86 | // Compiler-Specific Definitions | ||
| 87 | |||
| 88 | #define GCC_VERSION_AVAILABLE(major, minor) (defined(__GNUC__) && (__GNUC__ > (major) || \ | ||
| 89 | (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))) | ||
diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index cf6b6b258..7792edd2f 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp | |||
| @@ -2,13 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstddef> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 5 | #include "common/profiler.h" | 10 | #include "common/profiler.h" |
| 6 | #include "common/profiler_reporting.h" | 11 | #include "common/profiler_reporting.h" |
| 7 | #include "common/assert.h" | 12 | #include "common/synchronized_wrapper.h" |
| 8 | 13 | ||
| 9 | #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. | 14 | #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. |
| 10 | #define WIN32_LEAN_AND_MEAN | 15 | #define WIN32_LEAN_AND_MEAN |
| 11 | #include <Windows.h> // For QueryPerformanceCounter/Frequency | 16 | #include <Windows.h> // For QueryPerformanceCounter/Frequency |
| 12 | #endif | 17 | #endif |
| 13 | 18 | ||
| 14 | namespace Common { | 19 | namespace Common { |
diff --git a/src/common/profiler_reporting.h b/src/common/profiler_reporting.h index 3abb73315..df98e05b7 100644 --- a/src/common/profiler_reporting.h +++ b/src/common/profiler_reporting.h | |||
| @@ -4,10 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <cstddef> |
| 8 | #include <chrono> | ||
| 9 | #include <mutex> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | 8 | #include <vector> |
| 12 | 9 | ||
| 13 | #include "common/profiler.h" | 10 | #include "common/profiler.h" |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 7dc0ba7ba..b2f7f7b1d 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -2,9 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <boost/range/algorithm.hpp> | 5 | #include <cctype> |
| 6 | #include <cerrno> | ||
| 7 | #include <cstdio> | ||
| 8 | #include <cstdlib> | ||
| 9 | #include <cstring> | ||
| 10 | #include <boost/range/algorithm/transform.hpp> | ||
| 6 | 11 | ||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_paths.h" | 12 | #include "common/common_paths.h" |
| 9 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| @@ -12,6 +16,7 @@ | |||
| 12 | #ifdef _MSC_VER | 16 | #ifdef _MSC_VER |
| 13 | #include <Windows.h> | 17 | #include <Windows.h> |
| 14 | #include <codecvt> | 18 | #include <codecvt> |
| 19 | #include "common/common_funcs.h" | ||
| 15 | #else | 20 | #else |
| 16 | #include <iconv.h> | 21 | #include <iconv.h> |
| 17 | #endif | 22 | #endif |
| @@ -308,7 +313,7 @@ static std::string UTF16ToUTF8(const std::wstring& input) | |||
| 308 | std::string output; | 313 | std::string output; |
| 309 | output.resize(size); | 314 | output.resize(size); |
| 310 | 315 | ||
| 311 | if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), &output[0], output.size(), nullptr, nullptr)) | 316 | if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr)) |
| 312 | output.clear(); | 317 | output.clear(); |
| 313 | 318 | ||
| 314 | return output; | 319 | return output; |
diff --git a/src/common/string_util.h b/src/common/string_util.h index fdc410499..c5c474c6f 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -5,9 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstdarg> | 7 | #include <cstdarg> |
| 8 | #include <cstddef> | ||
| 8 | #include <iomanip> | 9 | #include <iomanip> |
| 9 | #include <string> | ||
| 10 | #include <sstream> | 10 | #include <sstream> |
| 11 | #include <string> | ||
| 11 | #include <vector> | 12 | #include <vector> |
| 12 | 13 | ||
| 13 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
diff --git a/src/common/swap.h b/src/common/swap.h index 588cebc70..b92e5bfa4 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -17,12 +17,16 @@ | |||
| 17 | 17 | ||
| 18 | #pragma once | 18 | #pragma once |
| 19 | 19 | ||
| 20 | #if defined(__linux__) | 20 | #if defined(_MSC_VER) |
| 21 | #include <byteswap.h> | 21 | #include <cstdlib> |
| 22 | #elif defined(__linux__) | ||
| 23 | #include <byteswap.h> | ||
| 22 | #elif defined(__FreeBSD__) | 24 | #elif defined(__FreeBSD__) |
| 23 | #include <sys/endian.h> | 25 | #include <sys/endian.h> |
| 24 | #endif | 26 | #endif |
| 25 | 27 | ||
| 28 | #include "common/common_types.h" | ||
| 29 | |||
| 26 | // GCC 4.6+ | 30 | // GCC 4.6+ |
| 27 | #if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | 31 | #if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) |
| 28 | 32 | ||
diff --git a/src/common/synchronized_wrapper.h b/src/common/synchronized_wrapper.h index 946252b8c..ae5e8b1ed 100644 --- a/src/common/synchronized_wrapper.h +++ b/src/common/synchronized_wrapper.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 7 | #include <mutex> | 8 | #include <mutex> |
| 8 | 9 | ||
| 9 | namespace Common { | 10 | namespace Common { |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 8bf005857..7bbf080bc 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -5,11 +5,20 @@ | |||
| 5 | #include "common/thread.h" | 5 | #include "common/thread.h" |
| 6 | 6 | ||
| 7 | #ifdef __APPLE__ | 7 | #ifdef __APPLE__ |
| 8 | #include <mach/mach.h> | 8 | #include <mach/mach.h> |
| 9 | #elif defined(BSD4_4) || defined(__OpenBSD__) | ||
| 10 | #include <pthread_np.h> | ||
| 11 | #elif defined(_WIN32) | 9 | #elif defined(_WIN32) |
| 12 | #include <Windows.h> | 10 | #include <Windows.h> |
| 11 | #else | ||
| 12 | #if defined(BSD4_4) || defined(__OpenBSD__) | ||
| 13 | #include <pthread_np.h> | ||
| 14 | #else | ||
| 15 | #include <pthread.h> | ||
| 16 | #endif | ||
| 17 | #include <sched.h> | ||
| 18 | #endif | ||
| 19 | |||
| 20 | #ifndef _WIN32 | ||
| 21 | #include <unistd.h> | ||
| 13 | #endif | 22 | #endif |
| 14 | 23 | ||
| 15 | namespace Common | 24 | namespace Common |
diff --git a/src/common/thread.h b/src/common/thread.h index 7bc419497..8255ee6d3 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -4,24 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include <cstddef> |
| 8 | #include <cstdio> | ||
| 9 | #include <cstring> | ||
| 10 | #include <thread> | 8 | #include <thread> |
| 11 | #include <condition_variable> | 9 | #include <condition_variable> |
| 12 | #include <mutex> | 10 | #include <mutex> |
| 13 | 11 | ||
| 14 | // This may not be defined outside _WIN32 | 12 | #include "common/common_types.h" |
| 15 | #ifndef _WIN32 | ||
| 16 | #ifndef INFINITE | ||
| 17 | #define INFINITE 0xffffffff | ||
| 18 | #endif | ||
| 19 | |||
| 20 | //for gettimeofday and struct time(spec|val) | ||
| 21 | #include <time.h> | ||
| 22 | #include <sys/time.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | #endif | ||
| 25 | 13 | ||
| 26 | // Support for C++11's thread_local keyword was surprisingly spotty in compilers until very | 14 | // Support for C++11's thread_local keyword was surprisingly spotty in compilers until very |
| 27 | // recently. Fortunately, thread local variables have been well supported for compilers for a while, | 15 | // recently. Fortunately, thread local variables have been well supported for compilers for a while, |
diff --git a/src/common/thunk.h b/src/common/thunk.h deleted file mode 100644 index 533480056..000000000 --- a/src/common/thunk.h +++ /dev/null | |||
| @@ -1,42 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | // This simple class creates a wrapper around a C/C++ function that saves all fp state | ||
| 12 | // before entering it, and restores it upon exit. This is required to be able to selectively | ||
| 13 | // call functions from generated code, without inflicting the performance hit and increase | ||
| 14 | // of complexity that it means to protect the generated code from this problem. | ||
| 15 | |||
| 16 | // This process is called thunking. | ||
| 17 | |||
| 18 | // There will only ever be one level of thunking on the stack, plus, | ||
| 19 | // we don't want to pollute the stack, so we store away regs somewhere global. | ||
| 20 | // NOT THREAD SAFE. This may only be used from the CPU thread. | ||
| 21 | // Any other thread using this stuff will be FATAL. | ||
| 22 | |||
| 23 | class ThunkManager : public Gen::XCodeBlock | ||
| 24 | { | ||
| 25 | std::map<void *, const u8 *> thunks; | ||
| 26 | |||
| 27 | const u8 *save_regs; | ||
| 28 | const u8 *load_regs; | ||
| 29 | |||
| 30 | public: | ||
| 31 | ThunkManager() { | ||
| 32 | Init(); | ||
| 33 | } | ||
| 34 | ~ThunkManager() { | ||
| 35 | Shutdown(); | ||
| 36 | } | ||
| 37 | void *ProtectFunction(void *function, int num_params); | ||
| 38 | private: | ||
| 39 | void Init(); | ||
| 40 | void Shutdown(); | ||
| 41 | void Reset(); | ||
| 42 | }; | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4fcda4874..8267ee586 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -25,6 +25,8 @@ set(SRCS | |||
| 25 | file_sys/ivfc_archive.cpp | 25 | file_sys/ivfc_archive.cpp |
| 26 | hle/config_mem.cpp | 26 | hle/config_mem.cpp |
| 27 | hle/hle.cpp | 27 | hle/hle.cpp |
| 28 | hle/applets/applet.cpp | ||
| 29 | hle/applets/swkbd.cpp | ||
| 28 | hle/kernel/address_arbiter.cpp | 30 | hle/kernel/address_arbiter.cpp |
| 29 | hle/kernel/event.cpp | 31 | hle/kernel/event.cpp |
| 30 | hle/kernel/kernel.cpp | 32 | hle/kernel/kernel.cpp |
| @@ -113,6 +115,7 @@ set(SRCS | |||
| 113 | loader/elf.cpp | 115 | loader/elf.cpp |
| 114 | loader/loader.cpp | 116 | loader/loader.cpp |
| 115 | loader/ncch.cpp | 117 | loader/ncch.cpp |
| 118 | tracer/recorder.cpp | ||
| 116 | mem_map.cpp | 119 | mem_map.cpp |
| 117 | memory.cpp | 120 | memory.cpp |
| 118 | settings.cpp | 121 | settings.cpp |
| @@ -150,6 +153,8 @@ set(HEADERS | |||
| 150 | hle/config_mem.h | 153 | hle/config_mem.h |
| 151 | hle/function_wrappers.h | 154 | hle/function_wrappers.h |
| 152 | hle/hle.h | 155 | hle/hle.h |
| 156 | hle/applets/applet.h | ||
| 157 | hle/applets/swkbd.h | ||
| 153 | hle/kernel/address_arbiter.h | 158 | hle/kernel/address_arbiter.h |
| 154 | hle/kernel/event.h | 159 | hle/kernel/event.h |
| 155 | hle/kernel/kernel.h | 160 | hle/kernel/kernel.h |
| @@ -239,6 +244,8 @@ set(HEADERS | |||
| 239 | loader/elf.h | 244 | loader/elf.h |
| 240 | loader/loader.h | 245 | loader/loader.h |
| 241 | loader/ncch.h | 246 | loader/ncch.h |
| 247 | tracer/recorder.h | ||
| 248 | tracer/citrace.h | ||
| 242 | mem_map.h | 249 | mem_map.h |
| 243 | memory.h | 250 | memory.h |
| 244 | memory_setup.h | 251 | memory_setup.h |
diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp index 13d26d170..eb20bf6f7 100644 --- a/src/core/arm/disassembler/load_symbol_map.cpp +++ b/src/core/arm/disassembler/load_symbol_map.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sstream> | ||
| 5 | #include <string> | 6 | #include <string> |
| 6 | #include <vector> | 7 | #include <vector> |
| 7 | 8 | ||
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 2488c879c..cc9355722 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -10,6 +10,11 @@ | |||
| 10 | 10 | ||
| 11 | #include "core/arm/arm_interface.h" | 11 | #include "core/arm/arm_interface.h" |
| 12 | #include "core/arm/skyeye_common/armdefs.h" | 12 | #include "core/arm/skyeye_common/armdefs.h" |
| 13 | #include "core/arm/skyeye_common/arm_regformat.h" | ||
| 14 | |||
| 15 | namespace Core { | ||
| 16 | struct ThreadContext; | ||
| 17 | } | ||
| 13 | 18 | ||
| 14 | class ARM_DynCom final : virtual public ARM_Interface { | 19 | class ARM_DynCom final : virtual public ARM_Interface { |
| 15 | public: | 20 | public: |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index b00eb49a9..785f39566 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -4144,11 +4144,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | |||
| 4144 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | 4144 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 4145 | bx_inst* const inst_cream = (bx_inst*)inst_base->component; | 4145 | bx_inst* const inst_cream = (bx_inst*)inst_base->component; |
| 4146 | 4146 | ||
| 4147 | u32 address = RM; | ||
| 4148 | |||
| 4147 | if (inst_cream->Rm == 15) | 4149 | if (inst_cream->Rm == 15) |
| 4148 | LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]); | 4150 | address += 2 * GET_INST_SIZE(cpu); |
| 4149 | 4151 | ||
| 4150 | cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; | 4152 | cpu->TFlag = address & 1; |
| 4151 | cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe; | 4153 | cpu->Reg[15] = address & 0xfffffffe; |
| 4152 | INC_PC(sizeof(bx_inst)); | 4154 | INC_PC(sizeof(bx_inst)); |
| 4153 | goto DISPATCH; | 4155 | goto DISPATCH; |
| 4154 | } | 4156 | } |
| @@ -5695,7 +5697,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | |||
| 5695 | const s16 operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF); | 5697 | const s16 operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF); |
| 5696 | const s64 result = (s64)(s32)rn_val * (s64)(s32)operand2 + ((s64)(s32)ra_val << 16); | 5698 | const s64 result = (s64)(s32)rn_val * (s64)(s32)operand2 + ((s64)(s32)ra_val << 16); |
| 5697 | 5699 | ||
| 5698 | RD = (result & (0xFFFFFFFFFFFFFFFFLL >> 15)) >> 16; | 5700 | RD = BITS(result, 16, 47); |
| 5699 | 5701 | ||
| 5700 | if ((result >> 16) != (s32)RD) | 5702 | if ((result >> 16) != (s32)RD) |
| 5701 | cpu->Cpsr |= (1 << 27); | 5703 | cpu->Cpsr |= (1 << 27); |
| @@ -6246,7 +6248,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | |||
| 6246 | SWI_INST: | 6248 | SWI_INST: |
| 6247 | { | 6249 | { |
| 6248 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | 6250 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 6249 | SVC::CallSVC(Memory::Read32(cpu->Reg[15])); | 6251 | swi_inst* const inst_cream = (swi_inst*)inst_base->component; |
| 6252 | SVC::CallSVC(inst_cream->num & 0xFFFF); | ||
| 6250 | } | 6253 | } |
| 6251 | 6254 | ||
| 6252 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 6255 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.cpp b/src/core/arm/dyncom/arm_dyncom_thumb.cpp index 3e79c44c0..f10a5b70f 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.cpp +++ b/src/core/arm/dyncom/arm_dyncom_thumb.cpp | |||
| @@ -130,14 +130,13 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { | |||
| 130 | } | 130 | } |
| 131 | } else { | 131 | } else { |
| 132 | ARMword Rd = ((tinstr & 0x0007) >> 0); | 132 | ARMword Rd = ((tinstr & 0x0007) >> 0); |
| 133 | ARMword Rs = ((tinstr & 0x0038) >> 3); | 133 | ARMword Rs = ((tinstr & 0x0078) >> 3); |
| 134 | 134 | ||
| 135 | if (tinstr & (1 << 7)) | 135 | if (tinstr & (1 << 7)) |
| 136 | Rd += 8; | 136 | Rd += 8; |
| 137 | if (tinstr & (1 << 6)) | ||
| 138 | Rs += 8; | ||
| 139 | 137 | ||
| 140 | switch ((tinstr & 0x03C0) >> 6) { | 138 | switch ((tinstr & 0x03C0) >> 6) { |
| 139 | case 0x0: // ADD Rd,Rd,Rs | ||
| 141 | case 0x1: // ADD Rd,Rd,Hs | 140 | case 0x1: // ADD Rd,Rd,Hs |
| 142 | case 0x2: // ADD Hd,Hd,Rs | 141 | case 0x2: // ADD Hd,Hd,Rs |
| 143 | case 0x3: // ADD Hd,Hd,Hs | 142 | case 0x3: // ADD Hd,Hd,Hs |
| @@ -146,19 +145,19 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { | |||
| 146 | |(Rd << 12) // Rd | 145 | |(Rd << 12) // Rd |
| 147 | |(Rs << 0); // Rm | 146 | |(Rs << 0); // Rm |
| 148 | break; | 147 | break; |
| 148 | case 0x4: // CMP Rd,Rs | ||
| 149 | case 0x5: // CMP Rd,Hs | 149 | case 0x5: // CMP Rd,Hs |
| 150 | case 0x6: // CMP Hd,Rs | 150 | case 0x6: // CMP Hd,Rs |
| 151 | case 0x7: // CMP Hd,Hs | 151 | case 0x7: // CMP Hd,Hs |
| 152 | *ainstr = 0xE1500000 // base | 152 | *ainstr = 0xE1500000 // base |
| 153 | | (Rd << 16) // Rn | 153 | | (Rd << 16) // Rn |
| 154 | |(Rd << 12) // Rd | ||
| 155 | |(Rs << 0); // Rm | 154 | |(Rs << 0); // Rm |
| 156 | break; | 155 | break; |
| 156 | case 0x8: // MOV Rd,Rs | ||
| 157 | case 0x9: // MOV Rd,Hs | 157 | case 0x9: // MOV Rd,Hs |
| 158 | case 0xA: // MOV Hd,Rs | 158 | case 0xA: // MOV Hd,Rs |
| 159 | case 0xB: // MOV Hd,Hs | 159 | case 0xB: // MOV Hd,Hs |
| 160 | *ainstr = 0xE1A00000 // base | 160 | *ainstr = 0xE1A00000 // base |
| 161 | | (Rd << 16) // Rn | ||
| 162 | |(Rd << 12) // Rd | 161 | |(Rd << 12) // Rd |
| 163 | |(Rs << 0); // Rm | 162 | |(Rs << 0); // Rm |
| 164 | break; | 163 | break; |
| @@ -167,11 +166,6 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { | |||
| 167 | *ainstr = 0xE12FFF10 // base | 166 | *ainstr = 0xE12FFF10 // base |
| 168 | | ((tinstr & 0x0078) >> 3); // Rd | 167 | | ((tinstr & 0x0078) >> 3); // Rd |
| 169 | break; | 168 | break; |
| 170 | case 0x0: // UNDEFINED | ||
| 171 | case 0x4: // UNDEFINED | ||
| 172 | case 0x8: // UNDEFINED | ||
| 173 | valid = t_undefined; | ||
| 174 | break; | ||
| 175 | case 0xE: // BLX | 169 | case 0xE: // BLX |
| 176 | case 0xF: // BLX | 170 | case 0xF: // BLX |
| 177 | *ainstr = 0xE1200030 // base | 171 | *ainstr = 0xE1200030 // base |
diff --git a/src/core/arm/skyeye_common/arm_regformat.h b/src/core/arm/skyeye_common/arm_regformat.h index a92effbb4..d1c721809 100644 --- a/src/core/arm/skyeye_common/arm_regformat.h +++ b/src/core/arm/skyeye_common/arm_regformat.h | |||
| @@ -59,6 +59,8 @@ enum { | |||
| 59 | VFP_FPSID, | 59 | VFP_FPSID, |
| 60 | VFP_FPSCR, | 60 | VFP_FPSCR, |
| 61 | VFP_FPEXC, | 61 | VFP_FPEXC, |
| 62 | VFP_FPINST, | ||
| 63 | VFP_FPINST2, | ||
| 62 | VFP_MVFR0, | 64 | VFP_MVFR0, |
| 63 | VFP_MVFR1, | 65 | VFP_MVFR1, |
| 64 | 66 | ||
diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp index 571d6c2f2..1ffc1f9af 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.cpp +++ b/src/core/arm/skyeye_common/vfp/vfp.cpp | |||
| @@ -20,36 +20,27 @@ | |||
| 20 | 20 | ||
| 21 | /* Note: this file handles interface with arm core and vfp registers */ | 21 | /* Note: this file handles interface with arm core and vfp registers */ |
| 22 | 22 | ||
| 23 | #include "common/common_funcs.h" | ||
| 23 | #include "common/logging/log.h" | 24 | #include "common/logging/log.h" |
| 24 | 25 | ||
| 25 | #include "core/arm/skyeye_common/armdefs.h" | 26 | #include "core/arm/skyeye_common/armdefs.h" |
| 26 | #include "core/arm/skyeye_common/vfp/asm_vfp.h" | 27 | #include "core/arm/skyeye_common/vfp/asm_vfp.h" |
| 27 | #include "core/arm/skyeye_common/vfp/vfp.h" | 28 | #include "core/arm/skyeye_common/vfp/vfp.h" |
| 28 | 29 | ||
| 29 | unsigned VFPInit(ARMul_State* state) | 30 | void VFPInit(ARMul_State* state) |
| 30 | { | 31 | { |
| 31 | state->VFP[VFP_FPSID] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 | | 32 | state->VFP[VFP_FPSID] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 | |
| 32 | VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION; | 33 | VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION; |
| 33 | state->VFP[VFP_FPEXC] = 0; | 34 | state->VFP[VFP_FPEXC] = 0; |
| 34 | state->VFP[VFP_FPSCR] = 0; | 35 | state->VFP[VFP_FPSCR] = 0; |
| 35 | 36 | ||
| 37 | // ARM11 MPCore instruction register reset values. | ||
| 38 | state->VFP[VFP_FPINST] = 0xEE000A00; | ||
| 39 | state->VFP[VFP_FPINST2] = 0; | ||
| 40 | |||
| 36 | // ARM11 MPCore feature register values. | 41 | // ARM11 MPCore feature register values. |
| 37 | state->VFP[VFP_MVFR0] = 0x11111111; | 42 | state->VFP[VFP_MVFR0] = 0x11111111; |
| 38 | state->VFP[VFP_MVFR1] = 0; | 43 | state->VFP[VFP_MVFR1] = 0; |
| 39 | |||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | void VMSR(ARMul_State* state, ARMword reg, ARMword Rt) | ||
| 44 | { | ||
| 45 | if (reg == 1) | ||
| 46 | { | ||
| 47 | state->VFP[VFP_FPSCR] = state->Reg[Rt]; | ||
| 48 | } | ||
| 49 | else if (reg == 8) | ||
| 50 | { | ||
| 51 | state->VFP[VFP_FPEXC] = state->Reg[Rt]; | ||
| 52 | } | ||
| 53 | } | 44 | } |
| 54 | 45 | ||
| 55 | void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value) | 46 | void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value) |
| @@ -153,9 +144,8 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc | |||
| 153 | LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions); | 144 | LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions); |
| 154 | 145 | ||
| 155 | if (exceptions == VFP_EXCEPTION_ERROR) { | 146 | if (exceptions == VFP_EXCEPTION_ERROR) { |
| 156 | LOG_TRACE(Core_ARM11, "unhandled bounce %x\n", inst); | 147 | LOG_CRITICAL(Core_ARM11, "unhandled bounce %x\n", inst); |
| 157 | exit(-1); | 148 | Crash(); |
| 158 | return; | ||
| 159 | } | 149 | } |
| 160 | 150 | ||
| 161 | /* | 151 | /* |
diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h index acefae9bb..80ca93ccd 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.h +++ b/src/core/arm/skyeye_common/vfp/vfp.h | |||
| @@ -26,7 +26,7 @@ | |||
| 26 | #define CHECK_VFP_ENABLED | 26 | #define CHECK_VFP_ENABLED |
| 27 | #define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); | 27 | #define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); |
| 28 | 28 | ||
| 29 | unsigned VFPInit(ARMul_State* state); | 29 | void VFPInit(ARMul_State* state); |
| 30 | 30 | ||
| 31 | s32 vfp_get_float(ARMul_State* state, u32 reg); | 31 | s32 vfp_get_float(ARMul_State* state, u32 reg); |
| 32 | void vfp_put_float(ARMul_State* state, s32 val, u32 reg); | 32 | void vfp_put_float(ARMul_State* state, s32 val, u32 reg); |
| @@ -36,10 +36,8 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc | |||
| 36 | u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr); | 36 | u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr); |
| 37 | u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr); | 37 | u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr); |
| 38 | 38 | ||
| 39 | void VMSR(ARMul_State* state, ARMword reg, ARMword Rt); | ||
| 40 | void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value); | 39 | void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value); |
| 41 | void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); | 40 | void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); |
| 42 | void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); | 41 | void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); |
| 43 | void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm); | 42 | void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm); |
| 44 | void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm); | 43 | void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm); |
| 45 | |||
diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp index 67fe63aa4..8efcbab1c 100644 --- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp | |||
| @@ -995,7 +995,7 @@ VMOVBRS_INST: | |||
| 995 | #ifdef VFP_INTERPRETER_STRUCT | 995 | #ifdef VFP_INTERPRETER_STRUCT |
| 996 | struct vmsr_inst { | 996 | struct vmsr_inst { |
| 997 | unsigned int reg; | 997 | unsigned int reg; |
| 998 | unsigned int Rd; | 998 | unsigned int Rt; |
| 999 | }; | 999 | }; |
| 1000 | #endif | 1000 | #endif |
| 1001 | #ifdef VFP_INTERPRETER_TRANS | 1001 | #ifdef VFP_INTERPRETER_TRANS |
| @@ -1009,7 +1009,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) | |||
| 1009 | inst_base->br = NON_BRANCH; | 1009 | inst_base->br = NON_BRANCH; |
| 1010 | 1010 | ||
| 1011 | inst_cream->reg = BITS(inst, 16, 19); | 1011 | inst_cream->reg = BITS(inst, 16, 19); |
| 1012 | inst_cream->Rd = BITS(inst, 12, 15); | 1012 | inst_cream->Rt = BITS(inst, 12, 15); |
| 1013 | 1013 | ||
| 1014 | return inst_base; | 1014 | return inst_base; |
| 1015 | } | 1015 | } |
| @@ -1017,15 +1017,30 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) | |||
| 1017 | #ifdef VFP_INTERPRETER_IMPL | 1017 | #ifdef VFP_INTERPRETER_IMPL |
| 1018 | VMSR_INST: | 1018 | VMSR_INST: |
| 1019 | { | 1019 | { |
| 1020 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 1020 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 1021 | /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled , | 1021 | /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled , |
| 1022 | and in privileged mode */ | 1022 | and in privileged mode */ |
| 1023 | /* Exceptions must be checked, according to v7 ref manual */ | 1023 | /* Exceptions must be checked, according to v7 ref manual */ |
| 1024 | CHECK_VFP_ENABLED; | 1024 | CHECK_VFP_ENABLED; |
| 1025 | 1025 | ||
| 1026 | vmsr_inst *inst_cream = (vmsr_inst *)inst_base->component; | 1026 | vmsr_inst* const inst_cream = (vmsr_inst*)inst_base->component; |
| 1027 | |||
| 1028 | unsigned int reg = inst_cream->reg; | ||
| 1029 | unsigned int rt = inst_cream->Rt; | ||
| 1027 | 1030 | ||
| 1028 | VMSR(cpu, inst_cream->reg, inst_cream->Rd); | 1031 | if (reg == 1) |
| 1032 | { | ||
| 1033 | cpu->VFP[VFP_FPSCR] = cpu->Reg[rt]; | ||
| 1034 | } | ||
| 1035 | else if (InAPrivilegedMode(cpu)) | ||
| 1036 | { | ||
| 1037 | if (reg == 8) | ||
| 1038 | cpu->VFP[VFP_FPEXC] = cpu->Reg[rt]; | ||
| 1039 | else if (reg == 9) | ||
| 1040 | cpu->VFP[VFP_FPINST] = cpu->Reg[rt]; | ||
| 1041 | else if (reg == 10) | ||
| 1042 | cpu->VFP[VFP_FPINST2] = cpu->Reg[rt]; | ||
| 1043 | } | ||
| 1029 | } | 1044 | } |
| 1030 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 1045 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 1031 | INC_PC(sizeof(vmsr_inst)); | 1046 | INC_PC(sizeof(vmsr_inst)); |
| @@ -1111,19 +1126,22 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmrs)(unsigned int inst, int index) | |||
| 1111 | #ifdef VFP_INTERPRETER_IMPL | 1126 | #ifdef VFP_INTERPRETER_IMPL |
| 1112 | VMRS_INST: | 1127 | VMRS_INST: |
| 1113 | { | 1128 | { |
| 1114 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 1129 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 1115 | /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled, | 1130 | /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled, |
| 1116 | and in privileged mode */ | 1131 | and in privileged mode */ |
| 1117 | /* Exceptions must be checked, according to v7 ref manual */ | 1132 | /* Exceptions must be checked, according to v7 ref manual */ |
| 1118 | CHECK_VFP_ENABLED; | 1133 | CHECK_VFP_ENABLED; |
| 1119 | 1134 | ||
| 1120 | vmrs_inst *inst_cream = (vmrs_inst *)inst_base->component; | 1135 | vmrs_inst* const inst_cream = (vmrs_inst*)inst_base->component; |
| 1121 | 1136 | ||
| 1122 | if (inst_cream->reg == 1) /* FPSCR */ | 1137 | unsigned int reg = inst_cream->reg; |
| 1138 | unsigned int rt = inst_cream->Rt; | ||
| 1139 | |||
| 1140 | if (reg == 1) // FPSCR | ||
| 1123 | { | 1141 | { |
| 1124 | if (inst_cream->Rt != 15) | 1142 | if (rt != 15) |
| 1125 | { | 1143 | { |
| 1126 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSCR]; | 1144 | cpu->Reg[rt] = cpu->VFP[VFP_FPSCR]; |
| 1127 | } | 1145 | } |
| 1128 | else | 1146 | else |
| 1129 | { | 1147 | { |
| @@ -1133,25 +1151,26 @@ VMRS_INST: | |||
| 1133 | cpu->VFlag = (cpu->VFP[VFP_FPSCR] >> 28) & 1; | 1151 | cpu->VFlag = (cpu->VFP[VFP_FPSCR] >> 28) & 1; |
| 1134 | } | 1152 | } |
| 1135 | } | 1153 | } |
| 1136 | else | 1154 | else if (reg == 0) |
| 1137 | { | 1155 | { |
| 1138 | switch (inst_cream->reg) | 1156 | cpu->Reg[rt] = cpu->VFP[VFP_FPSID]; |
| 1139 | { | 1157 | } |
| 1140 | case 0: | 1158 | else if (reg == 6) |
| 1141 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSID]; | 1159 | { |
| 1142 | break; | 1160 | cpu->Reg[rt] = cpu->VFP[VFP_MVFR1]; |
| 1143 | case 6: | 1161 | } |
| 1144 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR1]; | 1162 | else if (reg == 7) |
| 1145 | break; | 1163 | { |
| 1146 | case 7: | 1164 | cpu->Reg[rt] = cpu->VFP[VFP_MVFR0]; |
| 1147 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR0]; | 1165 | } |
| 1148 | break; | 1166 | else if (InAPrivilegedMode(cpu)) |
| 1149 | case 8: | 1167 | { |
| 1150 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPEXC]; | 1168 | if (reg == 8) |
| 1151 | break; | 1169 | cpu->Reg[rt] = cpu->VFP[VFP_FPEXC]; |
| 1152 | default: | 1170 | else if (reg == 9) |
| 1153 | break; | 1171 | cpu->Reg[rt] = cpu->VFP[VFP_FPINST]; |
| 1154 | } | 1172 | else if (reg == 10) |
| 1173 | cpu->Reg[rt] = cpu->VFP[VFP_FPINST2]; | ||
| 1155 | } | 1174 | } |
| 1156 | } | 1175 | } |
| 1157 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 1176 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 5a655a6f2..e5d339252 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp | |||
| @@ -53,6 +53,8 @@ | |||
| 53 | 53 | ||
| 54 | #include <cinttypes> | 54 | #include <cinttypes> |
| 55 | 55 | ||
| 56 | #include "common/common_funcs.h" | ||
| 57 | #include "common/common_types.h" | ||
| 56 | #include "common/logging/log.h" | 58 | #include "common/logging/log.h" |
| 57 | 59 | ||
| 58 | #include "core/arm/skyeye_common/vfp/vfp_helper.h" | 60 | #include "core/arm/skyeye_common/vfp/vfp_helper.h" |
| @@ -1246,7 +1248,7 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) | |||
| 1246 | 1248 | ||
| 1247 | if (!fop->fn) { | 1249 | if (!fop->fn) { |
| 1248 | LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]); | 1250 | LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]); |
| 1249 | exit(-1); | 1251 | Crash(); |
| 1250 | goto invalid; | 1252 | goto invalid; |
| 1251 | } | 1253 | } |
| 1252 | 1254 | ||
diff --git a/src/core/core.cpp b/src/core/core.cpp index 79038cd52..dddc16708 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -2,15 +2,12 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 7 | 6 | ||
| 8 | #include "core/core.h" | 7 | #include "core/core.h" |
| 9 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 10 | 9 | ||
| 11 | #include "core/settings.h" | ||
| 12 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 13 | #include "core/arm/disassembler/arm_disasm.h" | ||
| 14 | #include "core/arm/dyncom/arm_dyncom.h" | 11 | #include "core/arm/dyncom/arm_dyncom.h" |
| 15 | #include "core/hle/hle.h" | 12 | #include "core/hle/hle.h" |
| 16 | #include "core/hle/kernel/thread.h" | 13 | #include "core/hle/kernel/thread.h" |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e53c2e606..20f2da0fe 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -3,12 +3,12 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include <cstdio> | ||
| 7 | #include <mutex> | 6 | #include <mutex> |
| 8 | #include <vector> | 7 | #include <vector> |
| 9 | 8 | ||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/chunk_file.h" | 9 | #include "common/chunk_file.h" |
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/string_util.h" | ||
| 12 | 12 | ||
| 13 | #include "core/arm/arm_interface.h" | 13 | #include "core/arm/arm_interface.h" |
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| @@ -502,7 +502,7 @@ void Advance() { | |||
| 502 | Core::g_app_core->down_count += diff; | 502 | Core::g_app_core->down_count += diff; |
| 503 | } | 503 | } |
| 504 | if (advance_callback) | 504 | if (advance_callback) |
| 505 | advance_callback(cycles_executed); | 505 | advance_callback(static_cast<int>(cycles_executed)); |
| 506 | } | 506 | } |
| 507 | 507 | ||
| 508 | void LogPendingEvents() { | 508 | void LogPendingEvents() { |
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp index 45a559ce8..3f81447df 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/archive_backend.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstddef> | ||
| 6 | #include <iomanip> | ||
| 5 | #include <sstream> | 7 | #include <sstream> |
| 6 | 8 | ||
| 7 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index e50c58a52..92dad8e6f 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sys/stat.h> | 5 | #include <algorithm> |
| 6 | #include <vector> | ||
| 6 | 7 | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 10 | #include "common/make_unique.h" | 11 | #include "common/make_unique.h" |
| 12 | #include "common/string_util.h" | ||
| 11 | 13 | ||
| 12 | #include "core/file_sys/archive_extsavedata.h" | 14 | #include "core/file_sys/archive_extsavedata.h" |
| 13 | #include "core/file_sys/disk_archive.h" | 15 | #include "core/file_sys/disk_archive.h" |
| 14 | #include "core/hle/service/fs/archive.h" | 16 | #include "core/hle/service/fs/archive.h" |
| 15 | #include "core/settings.h" | ||
| 16 | 17 | ||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 18 | // FileSys namespace | 19 | // FileSys namespace |
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index ef0b27bde..ec8d770fc 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h | |||
| @@ -4,10 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | |||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | 11 | ||
| 9 | #include "core/file_sys/disk_archive.h" | 12 | #include "core/file_sys/archive_backend.h" |
| 10 | #include "core/loader/loader.h" | 13 | #include "core/hle/result.h" |
| 11 | 14 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 13 | // FileSys namespace | 16 | // FileSys namespace |
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index d4a12ed10..696b51a94 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp | |||
| @@ -2,30 +2,30 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <memory> | 6 | #include <memory> |
| 6 | 7 | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | ||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/make_unique.h" | 10 | #include "common/make_unique.h" |
| 11 | 11 | ||
| 12 | #include "core/file_sys/archive_romfs.h" | 12 | #include "core/file_sys/archive_romfs.h" |
| 13 | #include "core/file_sys/ivfc_archive.h" | ||
| 13 | 14 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 15 | // FileSys namespace | 16 | // FileSys namespace |
| 16 | 17 | ||
| 17 | namespace FileSys { | 18 | namespace FileSys { |
| 18 | 19 | ||
| 19 | ArchiveFactory_RomFS::ArchiveFactory_RomFS(const Loader::AppLoader& app_loader) | 20 | ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) { |
| 20 | : romfs_data(std::make_shared<std::vector<u8>>()) { | ||
| 21 | // Load the RomFS from the app | 21 | // Load the RomFS from the app |
| 22 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(*romfs_data)) { | 22 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { |
| 23 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 23 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| 24 | } | 24 | } |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { | 27 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { |
| 28 | auto archive = Common::make_unique<IVFCArchive>(romfs_data); | 28 | auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); |
| 29 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | 29 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 409bc670a..2bedfa9c6 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h | |||
| @@ -5,11 +5,13 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 11 | 12 | ||
| 12 | #include "core/file_sys/ivfc_archive.h" | 13 | #include "core/file_sys/archive_backend.h" |
| 14 | #include "core/hle/result.h" | ||
| 13 | #include "core/loader/loader.h" | 15 | #include "core/loader/loader.h" |
| 14 | 16 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -20,14 +22,16 @@ namespace FileSys { | |||
| 20 | /// File system interface to the RomFS archive | 22 | /// File system interface to the RomFS archive |
| 21 | class ArchiveFactory_RomFS final : public ArchiveFactory { | 23 | class ArchiveFactory_RomFS final : public ArchiveFactory { |
| 22 | public: | 24 | public: |
| 23 | ArchiveFactory_RomFS(const Loader::AppLoader& app_loader); | 25 | ArchiveFactory_RomFS(Loader::AppLoader& app_loader); |
| 24 | 26 | ||
| 25 | std::string GetName() const override { return "RomFS"; } | 27 | std::string GetName() const override { return "RomFS"; } |
| 26 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | 28 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |
| 27 | ResultCode Format(const Path& path) override; | 29 | ResultCode Format(const Path& path) override; |
| 28 | 30 | ||
| 29 | private: | 31 | private: |
| 30 | std::shared_ptr<std::vector<u8>> romfs_data; | 32 | std::shared_ptr<FileUtil::IOFile> romfs_file; |
| 33 | u64 data_offset; | ||
| 34 | u64 data_size; | ||
| 31 | }; | 35 | }; |
| 32 | 36 | ||
| 33 | } // namespace FileSys | 37 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index a92309377..12876899f 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp | |||
| @@ -2,18 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sys/stat.h> | 5 | #include <algorithm> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/make_unique.h" | 10 | #include "common/make_unique.h" |
| 11 | #include "common/string_util.h" | ||
| 11 | 12 | ||
| 12 | #include "core/file_sys/archive_savedata.h" | 13 | #include "core/file_sys/archive_savedata.h" |
| 13 | #include "core/file_sys/disk_archive.h" | 14 | #include "core/file_sys/disk_archive.h" |
| 14 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/service/fs/archive.h" | 16 | #include "core/hle/service/fs/archive.h" |
| 16 | #include "core/settings.h" | ||
| 17 | 17 | ||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 19 | // FileSys namespace | 19 | // FileSys namespace |
| @@ -37,7 +37,7 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directo | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { | 39 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { |
| 40 | std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); | 40 | std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); |
| 41 | if (!FileUtil::Exists(concrete_mount_point)) { | 41 | if (!FileUtil::Exists(concrete_mount_point)) { |
| 42 | // When a SaveData archive is created for the first time, it is not yet formatted | 42 | // When a SaveData archive is created for the first time, it is not yet formatted |
| 43 | // and the save file/directory structure expected by the game has not yet been initialized. | 43 | // and the save file/directory structure expected by the game has not yet been initialized. |
| @@ -52,7 +52,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P | |||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | ResultCode ArchiveFactory_SaveData::Format(const Path& path) { | 54 | ResultCode ArchiveFactory_SaveData::Format(const Path& path) { |
| 55 | std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); | 55 | std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); |
| 56 | FileUtil::DeleteDirRecursively(concrete_mount_point); | 56 | FileUtil::DeleteDirRecursively(concrete_mount_point); |
| 57 | FileUtil::CreateFullPath(concrete_mount_point); | 57 | FileUtil::CreateFullPath(concrete_mount_point); |
| 58 | return RESULT_SUCCESS; | 58 | return RESULT_SUCCESS; |
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h index db17afc92..1f65297dd 100644 --- a/src/core/file_sys/archive_savedata.h +++ b/src/core/file_sys/archive_savedata.h | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include <memory> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "core/file_sys/disk_archive.h" | 10 | #include "core/file_sys/archive_backend.h" |
| 10 | #include "core/loader/loader.h" | 11 | #include "core/hle/result.h" |
| 11 | 12 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 13 | // FileSys namespace | 14 | // FileSys namespace |
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index e7e4fbf1d..ea1dfe2c7 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp | |||
| @@ -2,11 +2,17 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 5 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 6 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 7 | #include "common/make_unique.h" | 11 | #include "common/make_unique.h" |
| 12 | #include "common/string_util.h" | ||
| 8 | 13 | ||
| 9 | #include "core/file_sys/archive_savedatacheck.h" | 14 | #include "core/file_sys/archive_savedatacheck.h" |
| 15 | #include "core/file_sys/ivfc_archive.h" | ||
| 10 | #include "core/hle/service/fs/archive.h" | 16 | #include "core/hle/service/fs/archive.h" |
| 11 | 17 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -31,17 +37,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co | |||
| 31 | auto vec = path.AsBinary(); | 37 | auto vec = path.AsBinary(); |
| 32 | const u32* data = reinterpret_cast<u32*>(vec.data()); | 38 | const u32* data = reinterpret_cast<u32*>(vec.data()); |
| 33 | std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); | 39 | std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); |
| 34 | FileUtil::IOFile file(file_path, "rb"); | 40 | auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); |
| 35 | 41 | ||
| 36 | if (!file.IsOpen()) { | 42 | if (!file->IsOpen()) { |
| 37 | return ResultCode(-1); // TODO(Subv): Find the right error code | 43 | return ResultCode(-1); // TODO(Subv): Find the right error code |
| 38 | } | 44 | } |
| 39 | auto size = file.GetSize(); | 45 | auto size = file->GetSize(); |
| 40 | auto raw_data = std::make_shared<std::vector<u8>>(size); | ||
| 41 | file.ReadBytes(raw_data->data(), size); | ||
| 42 | file.Close(); | ||
| 43 | 46 | ||
| 44 | auto archive = Common::make_unique<IVFCArchive>(std::move(raw_data)); | 47 | auto archive = Common::make_unique<IVFCArchive>(file, 0, size); |
| 45 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | 48 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |
| 46 | } | 49 | } |
| 47 | 50 | ||
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_savedatacheck.h index f78a6f02e..b14aefe8b 100644 --- a/src/core/file_sys/archive_savedatacheck.h +++ b/src/core/file_sys/archive_savedatacheck.h | |||
| @@ -4,12 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | 7 | #include <memory> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "core/file_sys/archive_backend.h" |
| 10 | 11 | #include "core/hle/result.h" | |
| 11 | #include "core/file_sys/ivfc_archive.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 13 | 12 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 15 | // FileSys namespace | 14 | // FileSys namespace |
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index c1234a186..5c825f429 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp | |||
| @@ -2,9 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sys/stat.h> | 5 | #include <algorithm> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 10 | #include "common/make_unique.h" | 9 | #include "common/make_unique.h" |
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 1becf6c0f..10b273bdb 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include <memory> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "core/file_sys/disk_archive.h" | 10 | #include "core/file_sys/archive_backend.h" |
| 10 | #include "core/loader/loader.h" | 11 | #include "core/hle/result.h" |
| 11 | 12 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 13 | // FileSys namespace | 14 | // FileSys namespace |
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 4fe785c97..896f89529 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp | |||
| @@ -2,15 +2,17 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sys/stat.h> | 5 | #include <algorithm> |
| 6 | #include <vector> | ||
| 6 | 7 | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 9 | #include "common/make_unique.h" | 10 | #include "common/make_unique.h" |
| 11 | #include "common/string_util.h" | ||
| 10 | 12 | ||
| 11 | #include "core/file_sys/archive_systemsavedata.h" | 13 | #include "core/file_sys/archive_systemsavedata.h" |
| 14 | #include "core/file_sys/disk_archive.h" | ||
| 12 | #include "core/hle/service/fs/archive.h" | 15 | #include "core/hle/service/fs/archive.h" |
| 13 | #include "core/settings.h" | ||
| 14 | 16 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 16 | // FileSys namespace | 18 | // FileSys namespace |
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index 3431fed88..afc689848 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h | |||
| @@ -4,10 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | |||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | 11 | ||
| 9 | #include "core/file_sys/disk_archive.h" | 12 | #include "core/file_sys/archive_backend.h" |
| 10 | #include "core/loader/loader.h" | 13 | #include "core/hle/result.h" |
| 11 | 14 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 13 | // FileSys namespace | 16 | // FileSys namespace |
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 9980cced1..1096fd34d 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp | |||
| @@ -2,7 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sys/stat.h> | 5 | #include <algorithm> |
| 6 | #include <cstdio> | ||
| 6 | 7 | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| @@ -10,7 +11,6 @@ | |||
| 10 | #include "common/make_unique.h" | 11 | #include "common/make_unique.h" |
| 11 | 12 | ||
| 12 | #include "core/file_sys/disk_archive.h" | 13 | #include "core/file_sys/disk_archive.h" |
| 13 | #include "core/settings.h" | ||
| 14 | 14 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 16 | // FileSys namespace | 16 | // FileSys namespace |
| @@ -105,12 +105,12 @@ bool DiskFile::Open() { | |||
| 105 | return true; | 105 | return true; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { | 108 | size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { |
| 109 | file->Seek(offset, SEEK_SET); | 109 | file->Seek(offset, SEEK_SET); |
| 110 | return file->ReadBytes(buffer, length); | 110 | return file->ReadBytes(buffer, length); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | 113 | size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { |
| 114 | file->Seek(offset, SEEK_SET); | 114 | file->Seek(offset, SEEK_SET); |
| 115 | size_t written = file->WriteBytes(buffer, length); | 115 | size_t written = file->WriteBytes(buffer, length); |
| 116 | if (flush) | 116 | if (flush) |
| @@ -118,8 +118,8 @@ size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, cons | |||
| 118 | return written; | 118 | return written; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | size_t DiskFile::GetSize() const { | 121 | u64 DiskFile::GetSize() const { |
| 122 | return static_cast<size_t>(file->GetSize()); | 122 | return file->GetSize(); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | bool DiskFile::SetSize(const u64 size) const { | 125 | bool DiskFile::SetSize(const u64 size) const { |
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index a22d3837a..c5da07508 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h | |||
| @@ -4,13 +4,18 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 7 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 13 | #include "common/file_util.h" |
| 9 | 14 | ||
| 10 | #include "core/file_sys/archive_backend.h" | 15 | #include "core/file_sys/archive_backend.h" |
| 11 | #include "core/file_sys/directory_backend.h" | 16 | #include "core/file_sys/directory_backend.h" |
| 12 | #include "core/file_sys/file_backend.h" | 17 | #include "core/file_sys/file_backend.h" |
| 13 | #include "core/loader/loader.h" | 18 | #include "core/hle/result.h" |
| 14 | 19 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 16 | // FileSys namespace | 21 | // FileSys namespace |
| @@ -50,10 +55,10 @@ public: | |||
| 50 | DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); | 55 | DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); |
| 51 | 56 | ||
| 52 | bool Open() override; | 57 | bool Open() override; |
| 53 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | 58 | size_t Read(u64 offset, size_t length, u8* buffer) const override; |
| 54 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | 59 | size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; |
| 55 | size_t GetSize() const override; | 60 | u64 GetSize() const override; |
| 56 | bool SetSize(const u64 size) const override; | 61 | bool SetSize(u64 size) const override; |
| 57 | bool Close() const override; | 62 | bool Close() const override; |
| 58 | 63 | ||
| 59 | void Flush() const override { | 64 | void Flush() const override { |
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index 0fcff1845..df7165df3 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -29,7 +31,7 @@ public: | |||
| 29 | * @param buffer Buffer to read data into | 31 | * @param buffer Buffer to read data into |
| 30 | * @return Number of bytes read | 32 | * @return Number of bytes read |
| 31 | */ | 33 | */ |
| 32 | virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; | 34 | virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0; |
| 33 | 35 | ||
| 34 | /** | 36 | /** |
| 35 | * Write data to the file | 37 | * Write data to the file |
| @@ -39,20 +41,20 @@ public: | |||
| 39 | * @param buffer Buffer to read data from | 41 | * @param buffer Buffer to read data from |
| 40 | * @return Number of bytes written | 42 | * @return Number of bytes written |
| 41 | */ | 43 | */ |
| 42 | virtual size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const = 0; | 44 | virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0; |
| 43 | 45 | ||
| 44 | /** | 46 | /** |
| 45 | * Get the size of the file in bytes | 47 | * Get the size of the file in bytes |
| 46 | * @return Size of the file in bytes | 48 | * @return Size of the file in bytes |
| 47 | */ | 49 | */ |
| 48 | virtual size_t GetSize() const = 0; | 50 | virtual u64 GetSize() const = 0; |
| 49 | 51 | ||
| 50 | /** | 52 | /** |
| 51 | * Set the size of the file in bytes | 53 | * Set the size of the file in bytes |
| 52 | * @param size New size of the file | 54 | * @param size New size of the file |
| 53 | * @return true if successful | 55 | * @return true if successful |
| 54 | */ | 56 | */ |
| 55 | virtual bool SetSize(const u64 size) const = 0; | 57 | virtual bool SetSize(u64 size) const = 0; |
| 56 | 58 | ||
| 57 | /** | 59 | /** |
| 58 | * Close the file | 60 | * Close the file |
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index 2d2509d16..e16aa1491 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp | |||
| @@ -2,10 +2,10 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | ||
| 5 | #include <memory> | 6 | #include <memory> |
| 6 | 7 | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | ||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/make_unique.h" | 10 | #include "common/make_unique.h" |
| 11 | 11 | ||
| @@ -16,15 +16,12 @@ | |||
| 16 | 16 | ||
| 17 | namespace FileSys { | 17 | namespace FileSys { |
| 18 | 18 | ||
| 19 | IVFCArchive::IVFCArchive(std::shared_ptr<const std::vector<u8>> data) : data(data) { | ||
| 20 | } | ||
| 21 | |||
| 22 | std::string IVFCArchive::GetName() const { | 19 | std::string IVFCArchive::GetName() const { |
| 23 | return "IVFC"; | 20 | return "IVFC"; |
| 24 | } | 21 | } |
| 25 | 22 | ||
| 26 | std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | 23 | std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { |
| 27 | return Common::make_unique<IVFCFile>(data); | 24 | return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size); |
| 28 | } | 25 | } |
| 29 | 26 | ||
| 30 | bool IVFCArchive::DeleteFile(const Path& path) const { | 27 | bool IVFCArchive::DeleteFile(const Path& path) const { |
| @@ -64,19 +61,21 @@ std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) c | |||
| 64 | 61 | ||
| 65 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 62 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 66 | 63 | ||
| 67 | size_t IVFCFile::Read(const u64 offset, const u32 length, u8* buffer) const { | 64 | size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { |
| 68 | LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); | 65 | LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); |
| 69 | memcpy(buffer, data->data() + offset, length); | 66 | romfs_file->Seek(data_offset + offset, SEEK_SET); |
| 70 | return length; | 67 | size_t read_length = (size_t)std::min((u64)length, data_size - offset); |
| 68 | |||
| 69 | return romfs_file->ReadBytes(buffer, read_length); | ||
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | size_t IVFCFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | 72 | size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { |
| 74 | LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); | 73 | LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); |
| 75 | return 0; | 74 | return 0; |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | size_t IVFCFile::GetSize() const { | 77 | u64 IVFCFile::GetSize() const { |
| 79 | return sizeof(u8) * data->size(); | 78 | return data_size; |
| 80 | } | 79 | } |
| 81 | 80 | ||
| 82 | bool IVFCFile::SetSize(const u64 size) const { | 81 | bool IVFCFile::SetSize(const u64 size) const { |
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 10415798d..c15a6c4ae 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h | |||
| @@ -4,15 +4,18 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | ||
| 8 | #include <vector> | 10 | #include <vector> |
| 9 | 11 | ||
| 10 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/file_util.h" | ||
| 11 | 14 | ||
| 12 | #include "core/file_sys/archive_backend.h" | 15 | #include "core/file_sys/archive_backend.h" |
| 13 | #include "core/file_sys/directory_backend.h" | 16 | #include "core/file_sys/directory_backend.h" |
| 14 | #include "core/file_sys/file_backend.h" | 17 | #include "core/file_sys/file_backend.h" |
| 15 | #include "core/loader/loader.h" | 18 | #include "core/hle/result.h" |
| 16 | 19 | ||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 18 | // FileSys namespace | 21 | // FileSys namespace |
| @@ -26,7 +29,8 @@ namespace FileSys { | |||
| 26 | */ | 29 | */ |
| 27 | class IVFCArchive : public ArchiveBackend { | 30 | class IVFCArchive : public ArchiveBackend { |
| 28 | public: | 31 | public: |
| 29 | IVFCArchive(std::shared_ptr<const std::vector<u8>> data); | 32 | IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |
| 33 | : romfs_file(file), data_offset(offset), data_size(size) {} | ||
| 30 | 34 | ||
| 31 | std::string GetName() const override; | 35 | std::string GetName() const override; |
| 32 | 36 | ||
| @@ -40,23 +44,28 @@ public: | |||
| 40 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | 44 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |
| 41 | 45 | ||
| 42 | protected: | 46 | protected: |
| 43 | std::shared_ptr<const std::vector<u8>> data; | 47 | std::shared_ptr<FileUtil::IOFile> romfs_file; |
| 48 | u64 data_offset; | ||
| 49 | u64 data_size; | ||
| 44 | }; | 50 | }; |
| 45 | 51 | ||
| 46 | class IVFCFile : public FileBackend { | 52 | class IVFCFile : public FileBackend { |
| 47 | public: | 53 | public: |
| 48 | IVFCFile(std::shared_ptr<const std::vector<u8>> data) : data(data) {} | 54 | IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |
| 55 | : romfs_file(file), data_offset(offset), data_size(size) {} | ||
| 49 | 56 | ||
| 50 | bool Open() override { return true; } | 57 | bool Open() override { return true; } |
| 51 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | 58 | size_t Read(u64 offset, size_t length, u8* buffer) const override; |
| 52 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | 59 | size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; |
| 53 | size_t GetSize() const override; | 60 | u64 GetSize() const override; |
| 54 | bool SetSize(const u64 size) const override; | 61 | bool SetSize(u64 size) const override; |
| 55 | bool Close() const override { return false; } | 62 | bool Close() const override { return false; } |
| 56 | void Flush() const override { } | 63 | void Flush() const override { } |
| 57 | 64 | ||
| 58 | private: | 65 | private: |
| 59 | std::shared_ptr<const std::vector<u8>> data; | 66 | std::shared_ptr<FileUtil::IOFile> romfs_file; |
| 67 | u64 data_offset; | ||
| 68 | u64 data_size; | ||
| 60 | }; | 69 | }; |
| 61 | 70 | ||
| 62 | class IVFCDirectory : public DirectoryBackend { | 71 | class IVFCDirectory : public DirectoryBackend { |
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp new file mode 100644 index 000000000..826f6cbb6 --- /dev/null +++ b/src/core/hle/applets/applet.cpp | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstddef> | ||
| 6 | #include <memory> | ||
| 7 | #include <type_traits> | ||
| 8 | #include <unordered_map> | ||
| 9 | |||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | #include "core/core_timing.h" | ||
| 14 | #include "core/hle/applets/applet.h" | ||
| 15 | #include "core/hle/applets/swkbd.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | #include "core/hle/service/apt/apt.h" | ||
| 18 | |||
| 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 20 | |||
| 21 | // Specializes std::hash for AppletId, so that we can use it in std::unordered_map. | ||
| 22 | // Workaround for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 | ||
| 23 | namespace std { | ||
| 24 | template <> | ||
| 25 | struct hash<Service::APT::AppletId> { | ||
| 26 | typedef Service::APT::AppletId argument_type; | ||
| 27 | typedef std::size_t result_type; | ||
| 28 | |||
| 29 | result_type operator()(const argument_type& id_code) const { | ||
| 30 | typedef std::underlying_type<argument_type>::type Type; | ||
| 31 | return std::hash<Type>()(static_cast<Type>(id_code)); | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | namespace HLE { | ||
| 37 | namespace Applets { | ||
| 38 | |||
| 39 | static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets; | ||
| 40 | static u32 applet_update_event = -1; ///< The CoreTiming event identifier for the Applet update callback. | ||
| 41 | /// The interval at which the Applet update callback will be called, 16.6ms | ||
| 42 | static const u64 applet_update_interval_us = 16666; | ||
| 43 | |||
| 44 | ResultCode Applet::Create(Service::APT::AppletId id) { | ||
| 45 | switch (id) { | ||
| 46 | case Service::APT::AppletId::SoftwareKeyboard1: | ||
| 47 | case Service::APT::AppletId::SoftwareKeyboard2: | ||
| 48 | applets[id] = std::make_shared<SoftwareKeyboard>(id); | ||
| 49 | break; | ||
| 50 | default: | ||
| 51 | // TODO(Subv): Find the right error code | ||
| 52 | return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 53 | } | ||
| 54 | |||
| 55 | return RESULT_SUCCESS; | ||
| 56 | } | ||
| 57 | |||
| 58 | std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) { | ||
| 59 | auto itr = applets.find(id); | ||
| 60 | if (itr != applets.end()) | ||
| 61 | return itr->second; | ||
| 62 | return nullptr; | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Handles updating the current Applet every time it's called. | ||
| 66 | static void AppletUpdateEvent(u64 applet_id, int cycles_late) { | ||
| 67 | Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id); | ||
| 68 | std::shared_ptr<Applet> applet = Applet::Get(id); | ||
| 69 | ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); | ||
| 70 | |||
| 71 | applet->Update(); | ||
| 72 | |||
| 73 | // If the applet is still running after the last update, reschedule the event | ||
| 74 | if (applet->IsRunning()) { | ||
| 75 | CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, | ||
| 76 | applet_update_event, applet_id); | ||
| 77 | } else { | ||
| 78 | // Otherwise the applet has terminated, in which case we should clean it up | ||
| 79 | applets[id] = nullptr; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) { | ||
| 84 | ResultCode result = StartImpl(parameter); | ||
| 85 | if (result.IsError()) | ||
| 86 | return result; | ||
| 87 | // Schedule the update event | ||
| 88 | CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id)); | ||
| 89 | return result; | ||
| 90 | } | ||
| 91 | |||
| 92 | void Init() { | ||
| 93 | // Register the applet update callback | ||
| 94 | applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); | ||
| 95 | } | ||
| 96 | |||
| 97 | void Shutdown() { | ||
| 98 | } | ||
| 99 | |||
| 100 | } | ||
| 101 | } // namespace | ||
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h new file mode 100644 index 000000000..b235d0b8a --- /dev/null +++ b/src/core/hle/applets/applet.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/apt/apt.h" | ||
| 11 | |||
| 12 | namespace HLE { | ||
| 13 | namespace Applets { | ||
| 14 | |||
| 15 | class Applet { | ||
| 16 | public: | ||
| 17 | virtual ~Applet() { } | ||
| 18 | Applet(Service::APT::AppletId id) : id(id) { } | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Creates an instance of the Applet subclass identified by the parameter. | ||
| 22 | * and stores it in a global map. | ||
| 23 | * @param id Id of the applet to create. | ||
| 24 | * @returns ResultCode Whether the operation was successful or not. | ||
| 25 | */ | ||
| 26 | static ResultCode Create(Service::APT::AppletId id); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Retrieves the Applet instance identified by the specified id. | ||
| 30 | * @param id Id of the Applet to retrieve. | ||
| 31 | * @returns Requested Applet or nullptr if not found. | ||
| 32 | */ | ||
| 33 | static std::shared_ptr<Applet> Get(Service::APT::AppletId id); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Handles a parameter from the application. | ||
| 37 | * @param parameter Parameter data to handle. | ||
| 38 | * @returns ResultCode Whether the operation was successful or not. | ||
| 39 | */ | ||
| 40 | virtual ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) = 0; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Handles the Applet start event, triggered from the application. | ||
| 44 | * @param parameter Parameter data to handle. | ||
| 45 | * @returns ResultCode Whether the operation was successful or not. | ||
| 46 | */ | ||
| 47 | ResultCode Start(const Service::APT::AppletStartupParameter& parameter); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Whether the applet is currently executing instead of the host application or not. | ||
| 51 | */ | ||
| 52 | virtual bool IsRunning() const = 0; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Handles an update tick for the Applet, lets it update the screen, send commands, etc. | ||
| 56 | */ | ||
| 57 | virtual void Update() = 0; | ||
| 58 | |||
| 59 | protected: | ||
| 60 | /** | ||
| 61 | * Handles the Applet start event, triggered from the application. | ||
| 62 | * @param parameter Parameter data to handle. | ||
| 63 | * @returns ResultCode Whether the operation was successful or not. | ||
| 64 | */ | ||
| 65 | virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; | ||
| 66 | |||
| 67 | Service::APT::AppletId id; ///< Id of this Applet | ||
| 68 | }; | ||
| 69 | |||
| 70 | /// Initializes the HLE applets | ||
| 71 | void Init(); | ||
| 72 | |||
| 73 | /// Shuts down the HLE applets | ||
| 74 | void Shutdown(); | ||
| 75 | |||
| 76 | } | ||
| 77 | } // namespace | ||
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp new file mode 100644 index 000000000..1db6b5a17 --- /dev/null +++ b/src/core/hle/applets/swkbd.cpp | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/logging/log.h" | ||
| 10 | #include "common/string_util.h" | ||
| 11 | |||
| 12 | #include "core/hle/applets/swkbd.h" | ||
| 13 | #include "core/hle/kernel/kernel.h" | ||
| 14 | #include "core/hle/kernel/shared_memory.h" | ||
| 15 | #include "core/hle/service/hid/hid.h" | ||
| 16 | #include "core/hle/service/gsp_gpu.h" | ||
| 17 | #include "core/hle/result.h" | ||
| 18 | #include "core/memory.h" | ||
| 19 | |||
| 20 | #include "video_core/video_core.h" | ||
| 21 | |||
| 22 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 23 | |||
| 24 | namespace HLE { | ||
| 25 | namespace Applets { | ||
| 26 | |||
| 27 | SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { | ||
| 28 | // Create the SharedMemory that will hold the framebuffer data | ||
| 29 | // TODO(Subv): What size should we use here? | ||
| 30 | using Kernel::MemoryPermission; | ||
| 31 | framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); | ||
| 32 | } | ||
| 33 | |||
| 34 | ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { | ||
| 35 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { | ||
| 36 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); | ||
| 37 | UNIMPLEMENTED(); | ||
| 38 | // TODO(Subv): Find the right error code | ||
| 39 | return ResultCode(-1); | ||
| 40 | } | ||
| 41 | |||
| 42 | Service::APT::MessageParameter result; | ||
| 43 | // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo | ||
| 44 | result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); | ||
| 45 | result.data = nullptr; | ||
| 46 | result.buffer_size = 0; | ||
| 47 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | ||
| 48 | result.sender_id = static_cast<u32>(id); | ||
| 49 | result.object = framebuffer_memory; | ||
| 50 | |||
| 51 | Service::APT::SendParameter(result); | ||
| 52 | return RESULT_SUCCESS; | ||
| 53 | } | ||
| 54 | |||
| 55 | ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) { | ||
| 56 | ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong"); | ||
| 57 | |||
| 58 | memcpy(&config, parameter.data, parameter.buffer_size); | ||
| 59 | text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object); | ||
| 60 | |||
| 61 | // TODO(Subv): Verify if this is the correct behavior | ||
| 62 | memset(text_memory->GetPointer(), 0, text_memory->size); | ||
| 63 | |||
| 64 | DrawScreenKeyboard(); | ||
| 65 | |||
| 66 | started = true; | ||
| 67 | return RESULT_SUCCESS; | ||
| 68 | } | ||
| 69 | |||
| 70 | void SoftwareKeyboard::Update() { | ||
| 71 | // TODO(Subv): Handle input using the touch events from the HID module | ||
| 72 | |||
| 73 | // TODO(Subv): Remove this hardcoded text | ||
| 74 | std::u16string text = Common::UTF8ToUTF16("Citra"); | ||
| 75 | memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t)); | ||
| 76 | |||
| 77 | // TODO(Subv): Ask for input and write it to the shared memory | ||
| 78 | // TODO(Subv): Find out what are the possible values for the return code, | ||
| 79 | // some games seem to check for a hardcoded 2 | ||
| 80 | config.return_code = 2; | ||
| 81 | config.text_length = 6; | ||
| 82 | config.text_offset = 0; | ||
| 83 | |||
| 84 | // TODO(Subv): We're finalizing the applet immediately after it's started, | ||
| 85 | // but we should defer this call until after all the input has been collected. | ||
| 86 | Finalize(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void SoftwareKeyboard::DrawScreenKeyboard() { | ||
| 90 | auto bottom_screen = GSP_GPU::GetFrameBufferInfo(0, 1); | ||
| 91 | auto info = bottom_screen->framebuffer_info[bottom_screen->index]; | ||
| 92 | |||
| 93 | // TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer | ||
| 94 | memset(Memory::GetPointer(info.address_left), 0, info.stride * 320); | ||
| 95 | |||
| 96 | GSP_GPU::SetBufferSwap(1, info); | ||
| 97 | } | ||
| 98 | |||
| 99 | void SoftwareKeyboard::Finalize() { | ||
| 100 | // Let the application know that we're closing | ||
| 101 | Service::APT::MessageParameter message; | ||
| 102 | message.buffer_size = sizeof(SoftwareKeyboardConfig); | ||
| 103 | message.data = reinterpret_cast<u8*>(&config); | ||
| 104 | message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); | ||
| 105 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | ||
| 106 | message.sender_id = static_cast<u32>(id); | ||
| 107 | Service::APT::SendParameter(message); | ||
| 108 | |||
| 109 | started = false; | ||
| 110 | } | ||
| 111 | |||
| 112 | } | ||
| 113 | } // namespace | ||
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h new file mode 100644 index 000000000..cb95b8d90 --- /dev/null +++ b/src/core/hle/applets/swkbd.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | |||
| 10 | #include "core/hle/applets/applet.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/hle/kernel/shared_memory.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/apt/apt.h" | ||
| 15 | |||
| 16 | namespace HLE { | ||
| 17 | namespace Applets { | ||
| 18 | |||
| 19 | struct SoftwareKeyboardConfig { | ||
| 20 | INSERT_PADDING_WORDS(0x8); | ||
| 21 | |||
| 22 | u16 max_text_length; ///< Maximum length of the input text | ||
| 23 | |||
| 24 | INSERT_PADDING_BYTES(0x6E); | ||
| 25 | |||
| 26 | char16_t display_text[65]; ///< Text to display when asking the user for input | ||
| 27 | |||
| 28 | INSERT_PADDING_BYTES(0xE); | ||
| 29 | |||
| 30 | u32 default_text_offset; ///< Offset of the default text in the output SharedMemory | ||
| 31 | |||
| 32 | INSERT_PADDING_WORDS(0x3); | ||
| 33 | |||
| 34 | u32 shared_memory_size; ///< Size of the SharedMemory | ||
| 35 | |||
| 36 | INSERT_PADDING_WORDS(0x1); | ||
| 37 | |||
| 38 | u32 return_code; ///< Return code of the SoftwareKeyboard, usually 2, other values are unknown | ||
| 39 | |||
| 40 | INSERT_PADDING_WORDS(0x2); | ||
| 41 | |||
| 42 | u32 text_offset; ///< Offset in the SharedMemory where the output text starts | ||
| 43 | u16 text_length; ///< Length in characters of the output text | ||
| 44 | |||
| 45 | INSERT_PADDING_BYTES(0x2B6); | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * The size of this structure (0x400) has been verified via reverse engineering of multiple games | ||
| 50 | * that use the software keyboard. | ||
| 51 | */ | ||
| 52 | static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong"); | ||
| 53 | |||
| 54 | class SoftwareKeyboard final : public Applet { | ||
| 55 | public: | ||
| 56 | SoftwareKeyboard(Service::APT::AppletId id); | ||
| 57 | ~SoftwareKeyboard() {} | ||
| 58 | |||
| 59 | ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; | ||
| 60 | ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; | ||
| 61 | void Update() override; | ||
| 62 | bool IsRunning() const override { return started; } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Draws a keyboard to the current bottom screen framebuffer. | ||
| 66 | */ | ||
| 67 | void DrawScreenKeyboard(); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Sends the LibAppletClosing signal to the application, | ||
| 71 | * along with the relevant data buffers. | ||
| 72 | */ | ||
| 73 | void Finalize(); | ||
| 74 | |||
| 75 | /// TODO(Subv): Find out what this is actually used for. | ||
| 76 | /// It is believed that the application stores the current screen image here. | ||
| 77 | Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; | ||
| 78 | |||
| 79 | /// SharedMemory where the output text will be stored | ||
| 80 | Kernel::SharedPtr<Kernel::SharedMemory> text_memory; | ||
| 81 | |||
| 82 | /// Configuration of this instance of the SoftwareKeyboard, as received from the application | ||
| 83 | SoftwareKeyboardConfig config; | ||
| 84 | |||
| 85 | /// Whether this applet is currently running instead of the host application or not. | ||
| 86 | bool started; | ||
| 87 | }; | ||
| 88 | |||
| 89 | } | ||
| 90 | } // namespace | ||
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index ea0777438..1a0518926 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -87,8 +87,28 @@ template<ResultCode func(u32, s64)> void Wrap() { | |||
| 87 | } | 87 | } |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | template<ResultCode func(void*, void*, u32)> void Wrap(){ | 90 | template<ResultCode func(MemoryInfo*, PageInfo*, u32)> void Wrap() { |
| 91 | FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)).raw); | 91 | MemoryInfo memory_info = {}; |
| 92 | PageInfo page_info = {}; | ||
| 93 | u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; | ||
| 94 | Core::g_app_core->SetReg(1, memory_info.base_address); | ||
| 95 | Core::g_app_core->SetReg(2, memory_info.size); | ||
| 96 | Core::g_app_core->SetReg(3, memory_info.permission); | ||
| 97 | Core::g_app_core->SetReg(4, memory_info.state); | ||
| 98 | Core::g_app_core->SetReg(5, page_info.flags); | ||
| 99 | FuncReturn(retval); | ||
| 100 | } | ||
| 101 | |||
| 102 | template<ResultCode func(MemoryInfo*, PageInfo*, Handle, u32)> void Wrap() { | ||
| 103 | MemoryInfo memory_info = {}; | ||
| 104 | PageInfo page_info = {}; | ||
| 105 | u32 retval = func(&memory_info, &page_info, PARAM(2), PARAM(3)).raw; | ||
| 106 | Core::g_app_core->SetReg(1, memory_info.base_address); | ||
| 107 | Core::g_app_core->SetReg(2, memory_info.size); | ||
| 108 | Core::g_app_core->SetReg(3, memory_info.permission); | ||
| 109 | Core::g_app_core->SetReg(4, memory_info.state); | ||
| 110 | Core::g_app_core->SetReg(5, page_info.flags); | ||
| 111 | FuncReturn(retval); | ||
| 92 | } | 112 | } |
| 93 | 113 | ||
| 94 | template<ResultCode func(s32*, u32)> void Wrap(){ | 114 | template<ResultCode func(s32*, u32)> void Wrap(){ |
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index fdeb9a028..cd0a400dc 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "core/hle/hle.h" | 10 | #include "core/hle/hle.h" |
| 11 | #include "core/hle/config_mem.h" | 11 | #include "core/hle/config_mem.h" |
| 12 | #include "core/hle/shared_page.h" | 12 | #include "core/hle/shared_page.h" |
| 13 | #include "core/hle/kernel/thread.h" | ||
| 14 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 15 | 14 | ||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index f338f3266..53feebbc0 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -21,7 +21,7 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { | |||
| 21 | SharedPtr<Event> evt(new Event); | 21 | SharedPtr<Event> evt(new Event); |
| 22 | 22 | ||
| 23 | evt->signaled = false; | 23 | evt->signaled = false; |
| 24 | evt->reset_type = evt->intitial_reset_type = reset_type; | 24 | evt->reset_type = reset_type; |
| 25 | evt->name = std::move(name); | 25 | evt->name = std::move(name); |
| 26 | 26 | ||
| 27 | return evt; | 27 | return evt; |
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index fba960d2a..89d405236 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -26,7 +26,6 @@ public: | |||
| 26 | static const HandleType HANDLE_TYPE = HandleType::Event; | 26 | static const HandleType HANDLE_TYPE = HandleType::Event; |
| 27 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | 27 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 28 | 28 | ||
| 29 | ResetType intitial_reset_type; ///< ResetType specified at Event initialization | ||
| 30 | ResetType reset_type; ///< Current ResetType | 29 | ResetType reset_type; ///< Current ResetType |
| 31 | 30 | ||
| 32 | bool signaled; ///< Whether the event has already been signaled | 31 | bool signaled; ///< Whether the event has already been signaled |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 20e11da16..5711c0405 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -7,8 +7,6 @@ | |||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | 9 | ||
| 10 | #include "core/arm/arm_interface.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 13 | #include "core/hle/kernel/resource_limit.h" | 11 | #include "core/hle/kernel/resource_limit.h" |
| 14 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 64595f758..4d4276f7a 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <boost/intrusive_ptr.hpp> | 7 | #include <boost/smart_ptr/intrusive_ptr.hpp> |
| 8 | 8 | ||
| 9 | #include <algorithm> | ||
| 9 | #include <array> | 10 | #include <array> |
| 10 | #include <memory> | 11 | #include <cstddef> |
| 11 | #include <string> | 12 | #include <string> |
| 12 | #include <vector> | 13 | #include <vector> |
| 13 | 14 | ||
| @@ -16,8 +17,6 @@ | |||
| 16 | #include "core/hle/hle.h" | 17 | #include "core/hle/hle.h" |
| 17 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 18 | 19 | ||
| 19 | struct ApplicationInfo; | ||
| 20 | |||
| 21 | namespace Kernel { | 20 | namespace Kernel { |
| 22 | 21 | ||
| 23 | class Thread; | 22 | class Thread; |
| @@ -48,6 +47,7 @@ enum class HandleType : u32 { | |||
| 48 | Semaphore = 10, | 47 | Semaphore = 10, |
| 49 | Timer = 11, | 48 | Timer = 11, |
| 50 | ResourceLimit = 12, | 49 | ResourceLimit = 12, |
| 50 | CodeSet = 13, | ||
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | enum { | 53 | enum { |
| @@ -86,6 +86,7 @@ public: | |||
| 86 | case HandleType::Process: | 86 | case HandleType::Process: |
| 87 | case HandleType::AddressArbiter: | 87 | case HandleType::AddressArbiter: |
| 88 | case HandleType::ResourceLimit: | 88 | case HandleType::ResourceLimit: |
| 89 | case HandleType::CodeSet: | ||
| 89 | return false; | 90 | return false; |
| 90 | } | 91 | } |
| 91 | } | 92 | } |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b0e75ba59..a7892c652 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -5,24 +5,39 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_funcs.h" | 6 | #include "common/common_funcs.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/make_unique.h" | ||
| 8 | 9 | ||
| 9 | #include "core/hle/kernel/process.h" | 10 | #include "core/hle/kernel/process.h" |
| 10 | #include "core/hle/kernel/resource_limit.h" | 11 | #include "core/hle/kernel/resource_limit.h" |
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 13 | #include "core/hle/kernel/vm_manager.h" | ||
| 14 | #include "core/mem_map.h" | ||
| 12 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 13 | 16 | ||
| 14 | namespace Kernel { | 17 | namespace Kernel { |
| 15 | 18 | ||
| 19 | SharedPtr<CodeSet> CodeSet::Create(std::string name, u64 program_id) { | ||
| 20 | SharedPtr<CodeSet> codeset(new CodeSet); | ||
| 21 | |||
| 22 | codeset->name = std::move(name); | ||
| 23 | codeset->program_id = program_id; | ||
| 24 | |||
| 25 | return codeset; | ||
| 26 | } | ||
| 27 | |||
| 28 | CodeSet::CodeSet() {} | ||
| 29 | CodeSet::~CodeSet() {} | ||
| 30 | |||
| 16 | u32 Process::next_process_id; | 31 | u32 Process::next_process_id; |
| 17 | 32 | ||
| 18 | SharedPtr<Process> Process::Create(std::string name, u64 program_id) { | 33 | SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { |
| 19 | SharedPtr<Process> process(new Process); | 34 | SharedPtr<Process> process(new Process); |
| 20 | 35 | ||
| 21 | process->name = std::move(name); | 36 | process->codeset = std::move(code_set); |
| 22 | process->program_id = program_id; | ||
| 23 | |||
| 24 | process->flags.raw = 0; | 37 | process->flags.raw = 0; |
| 25 | process->flags.memory_region = MemoryRegion::APPLICATION; | 38 | process->flags.memory_region = MemoryRegion::APPLICATION; |
| 39 | process->address_space = Common::make_unique<VMManager>(); | ||
| 40 | Memory::InitLegacyAddressSpace(*process->address_space); | ||
| 26 | 41 | ||
| 27 | return process; | 42 | return process; |
| 28 | } | 43 | } |
| @@ -87,8 +102,19 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | |||
| 87 | } | 102 | } |
| 88 | } | 103 | } |
| 89 | 104 | ||
| 90 | void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | 105 | void Process::Run(s32 main_thread_priority, u32 stack_size) { |
| 91 | Kernel::SetupMainThread(entry_point, main_thread_priority); | 106 | auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { |
| 107 | auto vma = address_space->MapMemoryBlock(segment.addr, codeset->memory, | ||
| 108 | segment.offset, segment.size, memory_state).Unwrap(); | ||
| 109 | address_space->Reprotect(vma, permissions); | ||
| 110 | }; | ||
| 111 | |||
| 112 | MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); | ||
| 113 | MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); | ||
| 114 | MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); | ||
| 115 | |||
| 116 | address_space->LogLayout(); | ||
| 117 | Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); | ||
| 92 | } | 118 | } |
| 93 | 119 | ||
| 94 | Kernel::Process::Process() {} | 120 | Kernel::Process::Process() {} |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 7b8a68610..83d3aceae 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -5,6 +5,9 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <bitset> | 7 | #include <bitset> |
| 8 | #include <cstddef> | ||
| 9 | #include <memory> | ||
| 10 | #include <string> | ||
| 8 | 11 | ||
| 9 | #include <boost/container/static_vector.hpp> | 12 | #include <boost/container/static_vector.hpp> |
| 10 | 13 | ||
| @@ -12,7 +15,6 @@ | |||
| 12 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 13 | 16 | ||
| 14 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| 15 | #include "core/hle/result.h" | ||
| 16 | 18 | ||
| 17 | namespace Kernel { | 19 | namespace Kernel { |
| 18 | 20 | ||
| @@ -46,23 +48,51 @@ union ProcessFlags { | |||
| 46 | }; | 48 | }; |
| 47 | 49 | ||
| 48 | class ResourceLimit; | 50 | class ResourceLimit; |
| 51 | class VMManager; | ||
| 52 | |||
| 53 | struct CodeSet final : public Object { | ||
| 54 | static SharedPtr<CodeSet> Create(std::string name, u64 program_id); | ||
| 55 | |||
| 56 | std::string GetTypeName() const override { return "CodeSet"; } | ||
| 57 | std::string GetName() const override { return name; } | ||
| 58 | |||
| 59 | static const HandleType HANDLE_TYPE = HandleType::CodeSet; | ||
| 60 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 61 | |||
| 62 | /// Name of the process | ||
| 63 | std::string name; | ||
| 64 | /// Title ID corresponding to the process | ||
| 65 | u64 program_id; | ||
| 66 | |||
| 67 | std::shared_ptr<std::vector<u8>> memory; | ||
| 68 | |||
| 69 | struct Segment { | ||
| 70 | size_t offset = 0; | ||
| 71 | VAddr addr = 0; | ||
| 72 | u32 size = 0; | ||
| 73 | }; | ||
| 74 | |||
| 75 | Segment code, rodata, data; | ||
| 76 | VAddr entrypoint; | ||
| 77 | |||
| 78 | private: | ||
| 79 | CodeSet(); | ||
| 80 | ~CodeSet() override; | ||
| 81 | }; | ||
| 49 | 82 | ||
| 50 | class Process final : public Object { | 83 | class Process final : public Object { |
| 51 | public: | 84 | public: |
| 52 | static SharedPtr<Process> Create(std::string name, u64 program_id); | 85 | static SharedPtr<Process> Create(SharedPtr<CodeSet> code_set); |
| 53 | 86 | ||
| 54 | std::string GetTypeName() const override { return "Process"; } | 87 | std::string GetTypeName() const override { return "Process"; } |
| 55 | std::string GetName() const override { return name; } | 88 | std::string GetName() const override { return codeset->name; } |
| 56 | 89 | ||
| 57 | static const HandleType HANDLE_TYPE = HandleType::Process; | 90 | static const HandleType HANDLE_TYPE = HandleType::Process; |
| 58 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | 91 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 59 | 92 | ||
| 60 | static u32 next_process_id; | 93 | static u32 next_process_id; |
| 61 | 94 | ||
| 62 | /// Name of the process | 95 | SharedPtr<CodeSet> codeset; |
| 63 | std::string name; | ||
| 64 | /// Title ID corresponding to the process | ||
| 65 | u64 program_id; | ||
| 66 | /// Resource limit descriptor for this process | 96 | /// Resource limit descriptor for this process |
| 67 | SharedPtr<ResourceLimit> resource_limit; | 97 | SharedPtr<ResourceLimit> resource_limit; |
| 68 | 98 | ||
| @@ -80,6 +110,7 @@ public: | |||
| 80 | 110 | ||
| 81 | /// Bitmask of the used TLS slots | 111 | /// Bitmask of the used TLS slots |
| 82 | std::bitset<300> used_tls_slots; | 112 | std::bitset<300> used_tls_slots; |
| 113 | std::unique_ptr<VMManager> address_space; | ||
| 83 | 114 | ||
| 84 | /** | 115 | /** |
| 85 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | 116 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them |
| @@ -90,7 +121,7 @@ public: | |||
| 90 | /** | 121 | /** |
| 91 | * Applies address space changes and launches the process main thread. | 122 | * Applies address space changes and launches the process main thread. |
| 92 | */ | 123 | */ |
| 93 | void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); | 124 | void Run(s32 main_thread_priority, u32 stack_size); |
| 94 | 125 | ||
| 95 | private: | 126 | private: |
| 96 | Process(); | 127 | Process(); |
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 257da9105..adaffcafe 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h | |||
| @@ -4,8 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 7 | #include "core/hle/kernel/kernel.h" | 12 | #include "core/hle/kernel/kernel.h" |
| 8 | #include "core/hle/kernel/thread.h" | 13 | #include "core/hle/kernel/thread.h" |
| 14 | #include "core/hle/result.h" | ||
| 9 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 10 | 16 | ||
| 11 | namespace IPC { | 17 | namespace IPC { |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 204266896..7a2922776 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -4,9 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 9 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/result.h" | ||
| 10 | 13 | ||
| 11 | namespace Kernel { | 14 | namespace Kernel { |
| 12 | 15 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4729a7fe0..8b49fc7df 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -37,6 +37,10 @@ void Thread::Acquire() { | |||
| 37 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 37 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing | ||
| 41 | // us to simply use a pool index or similar. | ||
| 42 | static Kernel::HandleTable wakeup_callback_handle_table; | ||
| 43 | |||
| 40 | // Lists all thread ids that aren't deleted/etc. | 44 | // Lists all thread ids that aren't deleted/etc. |
| 41 | static std::vector<SharedPtr<Thread>> thread_list; | 45 | static std::vector<SharedPtr<Thread>> thread_list; |
| 42 | 46 | ||
| @@ -93,6 +97,8 @@ void Thread::Stop() { | |||
| 93 | 97 | ||
| 94 | // Cancel any outstanding wakeup events for this thread | 98 | // Cancel any outstanding wakeup events for this thread |
| 95 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 99 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); |
| 100 | wakeup_callback_handle_table.Close(callback_handle); | ||
| 101 | callback_handle = 0; | ||
| 96 | 102 | ||
| 97 | // Clean up thread from ready queue | 103 | // Clean up thread from ready queue |
| 98 | // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) | 104 | // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) |
| @@ -108,6 +114,7 @@ void Thread::Stop() { | |||
| 108 | for (auto& wait_object : wait_objects) { | 114 | for (auto& wait_object : wait_objects) { |
| 109 | wait_object->RemoveWaitingThread(this); | 115 | wait_object->RemoveWaitingThread(this); |
| 110 | } | 116 | } |
| 117 | wait_objects.clear(); | ||
| 111 | 118 | ||
| 112 | Kernel::g_current_process->used_tls_slots[tls_index] = false; | 119 | Kernel::g_current_process->used_tls_slots[tls_index] = false; |
| 113 | 120 | ||
| @@ -210,6 +217,14 @@ static void SwitchContext(Thread* new_thread) { | |||
| 210 | new_thread->context.pc -= thumb_mode ? 2 : 4; | 217 | new_thread->context.pc -= thumb_mode ? 2 : 4; |
| 211 | } | 218 | } |
| 212 | 219 | ||
| 220 | // Clean up the thread's wait_objects, they'll be restored if needed during | ||
| 221 | // the svcWaitSynchronization call | ||
| 222 | for (int i = 0; i < new_thread->wait_objects.size(); ++i) { | ||
| 223 | SharedPtr<WaitObject> object = new_thread->wait_objects[i]; | ||
| 224 | object->RemoveWaitingThread(new_thread); | ||
| 225 | } | ||
| 226 | new_thread->wait_objects.clear(); | ||
| 227 | |||
| 213 | ready_queue.remove(new_thread->current_priority, new_thread); | 228 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 214 | new_thread->status = THREADSTATUS_RUNNING; | 229 | new_thread->status = THREADSTATUS_RUNNING; |
| 215 | 230 | ||
| @@ -268,10 +283,6 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | |||
| 268 | thread->status = THREADSTATUS_WAIT_ARB; | 283 | thread->status = THREADSTATUS_WAIT_ARB; |
| 269 | } | 284 | } |
| 270 | 285 | ||
| 271 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing | ||
| 272 | // us to simply use a pool index or similar. | ||
| 273 | static Kernel::HandleTable wakeup_callback_handle_table; | ||
| 274 | |||
| 275 | /** | 286 | /** |
| 276 | * Callback that will wake up the thread it was scheduled for | 287 | * Callback that will wake up the thread it was scheduled for |
| 277 | * @param thread_handle The handle of the thread that's been awoken | 288 | * @param thread_handle The handle of the thread that's been awoken |
| @@ -503,12 +514,16 @@ void ThreadingInit() { | |||
| 503 | 514 | ||
| 504 | current_thread = nullptr; | 515 | current_thread = nullptr; |
| 505 | next_thread_id = 1; | 516 | next_thread_id = 1; |
| 506 | |||
| 507 | thread_list.clear(); | ||
| 508 | ready_queue.clear(); | ||
| 509 | } | 517 | } |
| 510 | 518 | ||
| 511 | void ThreadingShutdown() { | 519 | void ThreadingShutdown() { |
| 520 | current_thread = nullptr; | ||
| 521 | |||
| 522 | for (auto& t : thread_list) { | ||
| 523 | t->Stop(); | ||
| 524 | } | ||
| 525 | thread_list.clear(); | ||
| 526 | ready_queue.clear(); | ||
| 512 | } | 527 | } |
| 513 | 528 | ||
| 514 | } // namespace | 529 | } // namespace |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index b8160bb2c..1ff1d9b97 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | 15 | ||
| 16 | #include "core/hle/hle.h" | ||
| 16 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| 17 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 18 | 19 | ||
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index b2dd21542..205cc7b53 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <iterator> | ||
| 6 | |||
| 5 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 6 | 8 | ||
| 7 | #include "core/hle/kernel/vm_manager.h" | 9 | #include "core/hle/kernel/vm_manager.h" |
| @@ -33,6 +35,10 @@ VMManager::VMManager() { | |||
| 33 | Reset(); | 35 | Reset(); |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 38 | VMManager::~VMManager() { | ||
| 39 | Reset(); | ||
| 40 | } | ||
| 41 | |||
| 36 | void VMManager::Reset() { | 42 | void VMManager::Reset() { |
| 37 | vma_map.clear(); | 43 | vma_map.clear(); |
| 38 | 44 | ||
| @@ -128,6 +134,16 @@ void VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { | |||
| 128 | MergeAdjacent(iter); | 134 | MergeAdjacent(iter); |
| 129 | } | 135 | } |
| 130 | 136 | ||
| 137 | void VMManager::LogLayout() const { | ||
| 138 | for (const auto& p : vma_map) { | ||
| 139 | const VirtualMemoryArea& vma = p.second; | ||
| 140 | LOG_DEBUG(Kernel, "%08X - %08X size: %8X %c%c%c", vma.base, vma.base + vma.size, vma.size, | ||
| 141 | (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', | ||
| 142 | (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', | ||
| 143 | (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-'); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 131 | VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) { | 147 | VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) { |
| 132 | // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given | 148 | // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given |
| 133 | // non-const access to its container. | 149 | // non-const access to its container. |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 22b724603..b3795a94a 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | ||
| 10 | #include <vector> | 9 | #include <vector> |
| 11 | 10 | ||
| 12 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -102,7 +101,7 @@ struct VirtualMemoryArea { | |||
| 102 | * - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ | 101 | * - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ |
| 103 | * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ | 102 | * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ |
| 104 | */ | 103 | */ |
| 105 | class VMManager { | 104 | class VMManager final { |
| 106 | // TODO(yuriks): Make page tables switchable to support multiple VMManagers | 105 | // TODO(yuriks): Make page tables switchable to support multiple VMManagers |
| 107 | public: | 106 | public: |
| 108 | /** | 107 | /** |
| @@ -122,6 +121,7 @@ public: | |||
| 122 | using VMAHandle = decltype(vma_map)::const_iterator; | 121 | using VMAHandle = decltype(vma_map)::const_iterator; |
| 123 | 122 | ||
| 124 | VMManager(); | 123 | VMManager(); |
| 124 | ~VMManager(); | ||
| 125 | 125 | ||
| 126 | /// Clears the address space map, re-initializing with a single free area. | 126 | /// Clears the address space map, re-initializing with a single free area. |
| 127 | void Reset(); | 127 | void Reset(); |
| @@ -169,6 +169,9 @@ public: | |||
| 169 | /// Changes the permissions of the given VMA. | 169 | /// Changes the permissions of the given VMA. |
| 170 | void Reprotect(VMAHandle vma, VMAPermission new_perms); | 170 | void Reprotect(VMAHandle vma, VMAPermission new_perms); |
| 171 | 171 | ||
| 172 | /// Dumps the address space layout to the log, for debugging | ||
| 173 | void LogLayout() const; | ||
| 174 | |||
| 172 | private: | 175 | private: |
| 173 | using VMAIter = decltype(vma_map)::iterator; | 176 | using VMAIter = decltype(vma_map)::iterator; |
| 174 | 177 | ||
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index ce633d841..cb2d681e0 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <new> |
| 8 | #include <type_traits> | 8 | #include <type_traits> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 57dc1ece7..7332478fb 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -38,6 +38,15 @@ void GetTitleIDList(Service::Interface* self) { | |||
| 38 | LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type); | 38 | LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | void GetNumContentInfos(Service::Interface* self) { | ||
| 42 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 43 | |||
| 44 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 45 | cmd_buff[2] = 1; // Number of content infos plus one | ||
| 46 | |||
| 47 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 48 | } | ||
| 49 | |||
| 41 | void Init() { | 50 | void Init() { |
| 42 | using namespace Kernel; | 51 | using namespace Kernel; |
| 43 | 52 | ||
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 063b8bd09..0b78f5393 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -37,6 +37,19 @@ void TitleIDListGetTotal(Service::Interface* self); | |||
| 37 | */ | 37 | */ |
| 38 | void GetTitleIDList(Service::Interface* self); | 38 | void GetTitleIDList(Service::Interface* self); |
| 39 | 39 | ||
| 40 | /** | ||
| 41 | * AM::GetNumContentInfos service function | ||
| 42 | * Inputs: | ||
| 43 | * 0 : Command header (0x100100C0) | ||
| 44 | * 1 : Unknown | ||
| 45 | * 2 : Unknown | ||
| 46 | * 3 : Unknown | ||
| 47 | * Outputs: | ||
| 48 | * 1 : Result, 0 on success, otherwise error code | ||
| 49 | * 2 : Number of content infos plus one | ||
| 50 | */ | ||
| 51 | void GetNumContentInfos(Service::Interface* self); | ||
| 52 | |||
| 40 | /// Initialize AM service | 53 | /// Initialize AM service |
| 41 | void Init(); | 54 | void Init(); |
| 42 | 55 | ||
diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index c6fc81bc3..f40a87cb4 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp | |||
| @@ -9,11 +9,19 @@ | |||
| 9 | namespace Service { | 9 | namespace Service { |
| 10 | namespace AM { | 10 | namespace AM { |
| 11 | 11 | ||
| 12 | // Empty arrays are illegal -- commented out until an entry is added. | 12 | const Interface::FunctionInfo FunctionTable[] = { |
| 13 | //const Interface::FunctionInfo FunctionTable[] = { }; | 13 | {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, |
| 14 | {0x10020104, nullptr, "FindContentInfos"}, | ||
| 15 | {0x10030142, nullptr, "ListContentInfos"}, | ||
| 16 | {0x10040102, nullptr, "DeleteContents"}, | ||
| 17 | {0x10050084, nullptr, "GetDataTitleInfos"}, | ||
| 18 | {0x10070102, nullptr, "ListDataTitleTicketInfos"}, | ||
| 19 | {0x100900C0, nullptr, "IsDataTitleInUse"}, | ||
| 20 | {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, | ||
| 21 | }; | ||
| 14 | 22 | ||
| 15 | AM_APP_Interface::AM_APP_Interface() { | 23 | AM_APP_Interface::AM_APP_Interface() { |
| 16 | //Register(FunctionTable); | 24 | Register(FunctionTable); |
| 17 | } | 25 | } |
| 18 | 26 | ||
| 19 | } // namespace AM | 27 | } // namespace AM |
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index b454a2709..7b6ab4ce0 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | 8 | ||
| 9 | #include "core/hle/applets/applet.h" | ||
| 9 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 10 | #include "core/hle/service/apt/apt.h" | 11 | #include "core/hle/service/apt/apt.h" |
| 11 | #include "core/hle/service/apt/apt_a.h" | 12 | #include "core/hle/service/apt/apt_a.h" |
| @@ -34,12 +35,21 @@ static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; | |||
| 34 | 35 | ||
| 35 | static Kernel::SharedPtr<Kernel::Mutex> lock; | 36 | static Kernel::SharedPtr<Kernel::Mutex> lock; |
| 36 | static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event | 37 | static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event |
| 37 | static Kernel::SharedPtr<Kernel::Event> start_event; ///< APT start event | 38 | static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event |
| 38 | 39 | ||
| 39 | static std::vector<u8> shared_font; | 40 | static std::vector<u8> shared_font; |
| 40 | 41 | ||
| 41 | static u32 cpu_percent; ///< CPU time available to the running application | 42 | static u32 cpu_percent; ///< CPU time available to the running application |
| 42 | 43 | ||
| 44 | /// Parameter data to be returned in the next call to Glance/ReceiveParameter | ||
| 45 | static MessageParameter next_parameter; | ||
| 46 | |||
| 47 | void SendParameter(const MessageParameter& parameter) { | ||
| 48 | next_parameter = parameter; | ||
| 49 | // Signal the event to let the application know that a new parameter is ready to be read | ||
| 50 | parameter_event->Signal(); | ||
| 51 | } | ||
| 52 | |||
| 43 | void Initialize(Service::Interface* self) { | 53 | void Initialize(Service::Interface* self) { |
| 44 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 54 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 45 | u32 app_id = cmd_buff[1]; | 55 | u32 app_id = cmd_buff[1]; |
| @@ -47,18 +57,18 @@ void Initialize(Service::Interface* self) { | |||
| 47 | 57 | ||
| 48 | cmd_buff[2] = IPC::MoveHandleDesc(2); | 58 | cmd_buff[2] = IPC::MoveHandleDesc(2); |
| 49 | cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); | 59 | cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); |
| 50 | cmd_buff[4] = Kernel::g_handle_table.Create(start_event).MoveFrom(); | 60 | cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); |
| 51 | 61 | ||
| 52 | // TODO(bunnei): Check if these events are cleared every time Initialize is called. | 62 | // TODO(bunnei): Check if these events are cleared every time Initialize is called. |
| 53 | notification_event->Clear(); | 63 | notification_event->Clear(); |
| 54 | start_event->Clear(); | 64 | parameter_event->Clear(); |
| 55 | 65 | ||
| 56 | ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); | 66 | ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); |
| 57 | lock->Release(); | 67 | lock->Release(); |
| 58 | 68 | ||
| 59 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 69 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 60 | 70 | ||
| 61 | LOG_TRACE(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); | 71 | LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); |
| 62 | } | 72 | } |
| 63 | 73 | ||
| 64 | void GetSharedFont(Service::Interface* self) { | 74 | void GetSharedFont(Service::Interface* self) { |
| @@ -85,9 +95,6 @@ void GetSharedFont(Service::Interface* self) { | |||
| 85 | void NotifyToWait(Service::Interface* self) { | 95 | void NotifyToWait(Service::Interface* self) { |
| 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 96 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 87 | u32 app_id = cmd_buff[1]; | 97 | u32 app_id = cmd_buff[1]; |
| 88 | // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. | ||
| 89 | start_event->Signal(); | ||
| 90 | |||
| 91 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 98 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 92 | LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); | 99 | LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); |
| 93 | } | 100 | } |
| @@ -100,7 +107,7 @@ void GetLockHandle(Service::Interface* self) { | |||
| 100 | 107 | ||
| 101 | // Not sure what these parameters are used for, but retail apps check that they are 0 after | 108 | // Not sure what these parameters are used for, but retail apps check that they are 0 after |
| 102 | // GetLockHandle has been called. | 109 | // GetLockHandle has been called. |
| 103 | cmd_buff[2] = 0; | 110 | cmd_buff[2] = 0; // Applet Attributes, this value is passed to Enable. |
| 104 | cmd_buff[3] = 0; | 111 | cmd_buff[3] = 0; |
| 105 | cmd_buff[4] = 0; | 112 | cmd_buff[4] = 0; |
| 106 | 113 | ||
| @@ -110,9 +117,10 @@ void GetLockHandle(Service::Interface* self) { | |||
| 110 | 117 | ||
| 111 | void Enable(Service::Interface* self) { | 118 | void Enable(Service::Interface* self) { |
| 112 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 119 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 113 | u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? | 120 | u32 attributes = cmd_buff[1]; |
| 114 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 121 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 115 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); | 122 | parameter_event->Signal(); // Let the application know that it has been started |
| 123 | LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); | ||
| 116 | } | 124 | } |
| 117 | 125 | ||
| 118 | void GetAppletManInfo(Service::Interface* self) { | 126 | void GetAppletManInfo(Service::Interface* self) { |
| @@ -121,8 +129,8 @@ void GetAppletManInfo(Service::Interface* self) { | |||
| 121 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 129 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 122 | cmd_buff[2] = 0; | 130 | cmd_buff[2] = 0; |
| 123 | cmd_buff[3] = 0; | 131 | cmd_buff[3] = 0; |
| 124 | cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID | 132 | cmd_buff[4] = static_cast<u32>(AppletId::HomeMenu); // Home menu AppID |
| 125 | cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly | 133 | cmd_buff[5] = static_cast<u32>(AppletId::Application); // TODO(purpasmart96): Do this correctly |
| 126 | 134 | ||
| 127 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); | 135 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); |
| 128 | } | 136 | } |
| @@ -131,7 +139,13 @@ void IsRegistered(Service::Interface* self) { | |||
| 131 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 139 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 132 | u32 app_id = cmd_buff[1]; | 140 | u32 app_id = cmd_buff[1]; |
| 133 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 141 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 134 | cmd_buff[2] = 1; // Set to registered | 142 | /// TODO(Subv): It is currently unknown what this value (0x400) means, |
| 143 | /// but i believe it is used as a global "LibraryApplet" id, to verify if there's | ||
| 144 | /// any LibApplet currently running. This is not verified. | ||
| 145 | if (app_id != 0x400) | ||
| 146 | cmd_buff[2] = 1; // Set to registered | ||
| 147 | else | ||
| 148 | cmd_buff[2] = 0; // Set to not registered | ||
| 135 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); | 149 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); |
| 136 | } | 150 | } |
| 137 | 151 | ||
| @@ -145,50 +159,82 @@ void InquireNotification(Service::Interface* self) { | |||
| 145 | 159 | ||
| 146 | void SendParameter(Service::Interface* self) { | 160 | void SendParameter(Service::Interface* self) { |
| 147 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 148 | u32 src_app_id = cmd_buff[1]; | 162 | u32 src_app_id = cmd_buff[1]; |
| 149 | u32 dst_app_id = cmd_buff[2]; | 163 | u32 dst_app_id = cmd_buff[2]; |
| 150 | u32 signal_type = cmd_buff[3]; | 164 | u32 signal_type = cmd_buff[3]; |
| 151 | u32 buffer_size = cmd_buff[4]; | 165 | u32 buffer_size = cmd_buff[4]; |
| 152 | u32 value = cmd_buff[5]; | 166 | u32 value = cmd_buff[5]; |
| 153 | u32 handle = cmd_buff[6]; | 167 | u32 handle = cmd_buff[6]; |
| 154 | u32 size = cmd_buff[7]; | 168 | u32 size = cmd_buff[7]; |
| 155 | u32 in_param_buffer_ptr = cmd_buff[8]; | 169 | u32 buffer = cmd_buff[8]; |
| 170 | |||
| 171 | std::shared_ptr<HLE::Applets::Applet> dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); | ||
| 172 | |||
| 173 | if (dest_applet == nullptr) { | ||
| 174 | LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); | ||
| 175 | cmd_buff[1] = -1; // TODO(Subv): Find the right error code | ||
| 176 | return; | ||
| 177 | } | ||
| 156 | 178 | ||
| 157 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 179 | MessageParameter param; |
| 180 | param.buffer_size = buffer_size; | ||
| 181 | param.destination_id = dst_app_id; | ||
| 182 | param.sender_id = src_app_id; | ||
| 183 | param.object = Kernel::g_handle_table.GetGeneric(handle); | ||
| 184 | param.signal = signal_type; | ||
| 185 | param.data = Memory::GetPointer(buffer); | ||
| 186 | |||
| 187 | cmd_buff[1] = dest_applet->ReceiveParameter(param).raw; | ||
| 158 | 188 | ||
| 159 | LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," | 189 | LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," |
| 160 | "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", | 190 | "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", |
| 161 | src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr); | 191 | src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, buffer); |
| 162 | } | 192 | } |
| 163 | 193 | ||
| 164 | void ReceiveParameter(Service::Interface* self) { | 194 | void ReceiveParameter(Service::Interface* self) { |
| 165 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 195 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 166 | u32 app_id = cmd_buff[1]; | 196 | u32 app_id = cmd_buff[1]; |
| 167 | u32 buffer_size = cmd_buff[2]; | 197 | u32 buffer_size = cmd_buff[2]; |
| 198 | VAddr buffer = cmd_buff[0x104 >> 2]; | ||
| 199 | |||
| 168 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 200 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 169 | cmd_buff[2] = 0; | 201 | cmd_buff[2] = next_parameter.sender_id; |
| 170 | cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type | 202 | cmd_buff[3] = next_parameter.signal; // Signal type |
| 171 | cmd_buff[4] = 0x10; // Parameter buffer size (16) | 203 | cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size |
| 172 | cmd_buff[5] = 0; | 204 | cmd_buff[5] = 0x10; |
| 173 | cmd_buff[6] = 0; | 205 | cmd_buff[6] = 0; |
| 174 | cmd_buff[7] = 0; | 206 | if (next_parameter.object != nullptr) |
| 175 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | 207 | cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); |
| 208 | cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; | ||
| 209 | cmd_buff[8] = buffer; | ||
| 210 | |||
| 211 | if (next_parameter.data) | ||
| 212 | memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); | ||
| 213 | |||
| 214 | LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | ||
| 176 | } | 215 | } |
| 177 | 216 | ||
| 178 | void GlanceParameter(Service::Interface* self) { | 217 | void GlanceParameter(Service::Interface* self) { |
| 179 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 218 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 180 | u32 app_id = cmd_buff[1]; | 219 | u32 app_id = cmd_buff[1]; |
| 181 | u32 buffer_size = cmd_buff[2]; | 220 | u32 buffer_size = cmd_buff[2]; |
| 221 | VAddr buffer = cmd_buff[0x104 >> 2]; | ||
| 182 | 222 | ||
| 183 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 223 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 184 | cmd_buff[2] = 0; | 224 | cmd_buff[2] = next_parameter.sender_id; |
| 185 | cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type | 225 | cmd_buff[3] = next_parameter.signal; // Signal type |
| 186 | cmd_buff[4] = 0x10; // Parameter buffer size (16) | 226 | cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size |
| 187 | cmd_buff[5] = 0; | 227 | cmd_buff[5] = 0x10; |
| 188 | cmd_buff[6] = 0; | 228 | cmd_buff[6] = 0; |
| 189 | cmd_buff[7] = 0; | 229 | if (next_parameter.object != nullptr) |
| 230 | cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); | ||
| 231 | cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; | ||
| 232 | cmd_buff[8] = buffer; | ||
| 190 | 233 | ||
| 191 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | 234 | if (next_parameter.data) |
| 235 | memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); | ||
| 236 | |||
| 237 | LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | ||
| 192 | } | 238 | } |
| 193 | 239 | ||
| 194 | void CancelParameter(Service::Interface* self) { | 240 | void CancelParameter(Service::Interface* self) { |
| @@ -240,7 +286,7 @@ void AppletUtility(Service::Interface* self) { | |||
| 240 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 286 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 241 | 287 | ||
| 242 | // These are from 3dbrew - I'm not really sure what they're used for. | 288 | // These are from 3dbrew - I'm not really sure what they're used for. |
| 243 | u32 unk = cmd_buff[1]; | 289 | u32 command = cmd_buff[1]; |
| 244 | u32 buffer1_size = cmd_buff[2]; | 290 | u32 buffer1_size = cmd_buff[2]; |
| 245 | u32 buffer2_size = cmd_buff[3]; | 291 | u32 buffer2_size = cmd_buff[3]; |
| 246 | u32 buffer1_addr = cmd_buff[5]; | 292 | u32 buffer1_addr = cmd_buff[5]; |
| @@ -248,8 +294,8 @@ void AppletUtility(Service::Interface* self) { | |||
| 248 | 294 | ||
| 249 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 295 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 250 | 296 | ||
| 251 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " | 297 | LOG_WARNING(Service_APT, "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " |
| 252 | "buffer1_addr=0x%08X, buffer2_addr=0x%08X", unk, buffer1_size, buffer2_size, | 298 | "buffer1_addr=0x%08X, buffer2_addr=0x%08X", command, buffer1_size, buffer2_size, |
| 253 | buffer1_addr, buffer2_addr); | 299 | buffer1_addr, buffer2_addr); |
| 254 | } | 300 | } |
| 255 | 301 | ||
| @@ -281,11 +327,41 @@ void GetAppCpuTimeLimit(Service::Interface* self) { | |||
| 281 | LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); | 327 | LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); |
| 282 | } | 328 | } |
| 283 | 329 | ||
| 330 | void PrepareToStartLibraryApplet(Service::Interface* self) { | ||
| 331 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 332 | AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); | ||
| 333 | cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; | ||
| 334 | LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); | ||
| 335 | } | ||
| 336 | |||
| 337 | void StartLibraryApplet(Service::Interface* self) { | ||
| 338 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 339 | AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); | ||
| 340 | std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id); | ||
| 341 | |||
| 342 | LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); | ||
| 343 | |||
| 344 | if (applet == nullptr) { | ||
| 345 | LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id); | ||
| 346 | cmd_buff[1] = -1; // TODO(Subv): Find the right error code | ||
| 347 | return; | ||
| 348 | } | ||
| 349 | |||
| 350 | AppletStartupParameter parameter; | ||
| 351 | parameter.buffer_size = cmd_buff[2]; | ||
| 352 | parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); | ||
| 353 | parameter.data = Memory::GetPointer(cmd_buff[6]); | ||
| 354 | |||
| 355 | cmd_buff[1] = applet->Start(parameter).raw; | ||
| 356 | } | ||
| 357 | |||
| 284 | void Init() { | 358 | void Init() { |
| 285 | AddService(new APT_A_Interface); | 359 | AddService(new APT_A_Interface); |
| 286 | AddService(new APT_S_Interface); | 360 | AddService(new APT_S_Interface); |
| 287 | AddService(new APT_U_Interface); | 361 | AddService(new APT_U_Interface); |
| 288 | 362 | ||
| 363 | HLE::Applets::Init(); | ||
| 364 | |||
| 289 | // Load the shared system font (if available). | 365 | // Load the shared system font (if available). |
| 290 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | 366 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header |
| 291 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | 367 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided |
| @@ -318,7 +394,10 @@ void Init() { | |||
| 318 | 394 | ||
| 319 | // TODO(bunnei): Check if these are created in Initialize or on APT process startup. | 395 | // TODO(bunnei): Check if these are created in Initialize or on APT process startup. |
| 320 | notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); | 396 | notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); |
| 321 | start_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); | 397 | parameter_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); |
| 398 | |||
| 399 | next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); | ||
| 400 | next_parameter.destination_id = 0x300; | ||
| 322 | } | 401 | } |
| 323 | 402 | ||
| 324 | void Shutdown() { | 403 | void Shutdown() { |
| @@ -326,7 +405,11 @@ void Shutdown() { | |||
| 326 | shared_font_mem = nullptr; | 405 | shared_font_mem = nullptr; |
| 327 | lock = nullptr; | 406 | lock = nullptr; |
| 328 | notification_event = nullptr; | 407 | notification_event = nullptr; |
| 329 | start_event = nullptr; | 408 | parameter_event = nullptr; |
| 409 | |||
| 410 | next_parameter.object = nullptr; | ||
| 411 | |||
| 412 | HLE::Applets::Shutdown(); | ||
| 330 | } | 413 | } |
| 331 | 414 | ||
| 332 | } // namespace APT | 415 | } // namespace APT |
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a03e1712a..72972d05b 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h | |||
| @@ -4,13 +4,33 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/result.h" | 8 | |
| 9 | #include "core/hle/service/service.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 10 | 10 | ||
| 11 | namespace Service { | 11 | namespace Service { |
| 12 | |||
| 13 | class Interface; | ||
| 14 | |||
| 12 | namespace APT { | 15 | namespace APT { |
| 13 | 16 | ||
| 17 | /// Holds information about the parameters used in Send/Glance/ReceiveParameter | ||
| 18 | struct MessageParameter { | ||
| 19 | u32 sender_id = 0; | ||
| 20 | u32 destination_id = 0; | ||
| 21 | u32 signal = 0; | ||
| 22 | u32 buffer_size = 0; | ||
| 23 | Kernel::SharedPtr<Kernel::Object> object = nullptr; | ||
| 24 | u8* data = nullptr; | ||
| 25 | }; | ||
| 26 | |||
| 27 | /// Holds information about the parameters used in StartLibraryApplet | ||
| 28 | struct AppletStartupParameter { | ||
| 29 | u32 buffer_size = 0; | ||
| 30 | Kernel::SharedPtr<Kernel::Object> object = nullptr; | ||
| 31 | u8* data = nullptr; | ||
| 32 | }; | ||
| 33 | |||
| 14 | /// Signals used by APT functions | 34 | /// Signals used by APT functions |
| 15 | enum class SignalType : u32 { | 35 | enum class SignalType : u32 { |
| 16 | None = 0x0, | 36 | None = 0x0, |
| @@ -23,7 +43,7 @@ enum class SignalType : u32 { | |||
| 23 | }; | 43 | }; |
| 24 | 44 | ||
| 25 | /// App Id's used by APT functions | 45 | /// App Id's used by APT functions |
| 26 | enum class AppID : u32 { | 46 | enum class AppletId : u32 { |
| 27 | HomeMenu = 0x101, | 47 | HomeMenu = 0x101, |
| 28 | AlternateMenu = 0x103, | 48 | AlternateMenu = 0x103, |
| 29 | Camera = 0x110, | 49 | Camera = 0x110, |
| @@ -45,6 +65,9 @@ enum class AppID : u32 { | |||
| 45 | SoftwareKeyboard2 = 0x401, | 65 | SoftwareKeyboard2 = 0x401, |
| 46 | }; | 66 | }; |
| 47 | 67 | ||
| 68 | /// Send a parameter to the currently-running application, which will read it via ReceiveParameter | ||
| 69 | void SendParameter(const MessageParameter& parameter); | ||
| 70 | |||
| 48 | /** | 71 | /** |
| 49 | * APT::Initialize service function | 72 | * APT::Initialize service function |
| 50 | * Service function that initializes the APT process for the running application | 73 | * Service function that initializes the APT process for the running application |
| @@ -249,6 +272,33 @@ void SetAppCpuTimeLimit(Service::Interface* self); | |||
| 249 | */ | 272 | */ |
| 250 | void GetAppCpuTimeLimit(Service::Interface* self); | 273 | void GetAppCpuTimeLimit(Service::Interface* self); |
| 251 | 274 | ||
| 275 | /** | ||
| 276 | * APT::PrepareToStartLibraryApplet service function | ||
| 277 | * Inputs: | ||
| 278 | * 0 : Command header [0x00180040] | ||
| 279 | * 1 : Id of the applet to start | ||
| 280 | * Outputs: | ||
| 281 | * 0 : Return header | ||
| 282 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 283 | */ | ||
| 284 | void PrepareToStartLibraryApplet(Service::Interface* self); | ||
| 285 | |||
| 286 | /** | ||
| 287 | * APT::StartLibraryApplet service function | ||
| 288 | * Inputs: | ||
| 289 | * 0 : Command header [0x001E0084] | ||
| 290 | * 1 : Id of the applet to start | ||
| 291 | * 2 : Buffer size | ||
| 292 | * 3 : Always 0? | ||
| 293 | * 4 : Handle passed to the applet | ||
| 294 | * 5 : (Size << 14) | 2 | ||
| 295 | * 6 : Input buffer virtual address | ||
| 296 | * Outputs: | ||
| 297 | * 0 : Return header | ||
| 298 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 299 | */ | ||
| 300 | void StartLibraryApplet(Service::Interface* self); | ||
| 301 | |||
| 252 | /// Initialize the APT service | 302 | /// Initialize the APT service |
| 253 | void Init(); | 303 | void Init(); |
| 254 | 304 | ||
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 864934245..88de339f9 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp | |||
| @@ -10,19 +10,24 @@ namespace Service { | |||
| 10 | namespace APT { | 10 | namespace APT { |
| 11 | 11 | ||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | 12 | const Interface::FunctionInfo FunctionTable[] = { |
| 13 | {0x00010040, GetLockHandle, "GetLockHandle?"}, | 13 | {0x00010040, GetLockHandle, "GetLockHandle?"}, |
| 14 | {0x00020080, Initialize, "Initialize?"}, | 14 | {0x00020080, Initialize, "Initialize?"}, |
| 15 | {0x00030040, Enable, "Enable?"}, | 15 | {0x00030040, Enable, "Enable?"}, |
| 16 | {0x00040040, nullptr, "Finalize?"}, | 16 | {0x00040040, nullptr, "Finalize?"}, |
| 17 | {0x00050040, nullptr, "GetAppletManInfo?"}, | 17 | {0x00050040, nullptr, "GetAppletManInfo?"}, |
| 18 | {0x00060040, nullptr, "GetAppletInfo?"}, | 18 | {0x00060040, nullptr, "GetAppletInfo?"}, |
| 19 | {0x000D0080, ReceiveParameter, "ReceiveParameter?"}, | 19 | {0x00090040, IsRegistered, "IsRegistered"}, |
| 20 | {0x000E0080, GlanceParameter, "GlanceParameter?"}, | 20 | {0x000C0104, SendParameter, "SendParameter"}, |
| 21 | {0x003B0040, nullptr, "CancelLibraryApplet?"}, | 21 | {0x000D0080, ReceiveParameter, "ReceiveParameter"}, |
| 22 | {0x00430040, NotifyToWait, "NotifyToWait?"}, | 22 | {0x000E0080, GlanceParameter, "GlanceParameter"}, |
| 23 | {0x00440000, GetSharedFont, "GetSharedFont?"}, | 23 | {0x000F0100, CancelParameter, "CancelParameter"}, |
| 24 | {0x004B00C2, AppletUtility, "AppletUtility?"}, | 24 | {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, |
| 25 | {0x00550040, nullptr, "WriteInputToNsState?"}, | 25 | {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, |
| 26 | {0x003B0040, nullptr, "CancelLibraryApplet?"}, | ||
| 27 | {0x00430040, NotifyToWait, "NotifyToWait?"}, | ||
| 28 | {0x00440000, GetSharedFont, "GetSharedFont?"}, | ||
| 29 | {0x004B00C2, AppletUtility, "AppletUtility?"}, | ||
| 30 | {0x00550040, nullptr, "WriteInputToNsState?"}, | ||
| 26 | }; | 31 | }; |
| 27 | 32 | ||
| 28 | APT_A_Interface::APT_A_Interface() { | 33 | APT_A_Interface::APT_A_Interface() { |
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index d006b5930..b724cd72b 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp | |||
| @@ -35,13 +35,13 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 35 | {0x00150140, nullptr, "PrepareToStartApplication"}, | 35 | {0x00150140, nullptr, "PrepareToStartApplication"}, |
| 36 | {0x00160040, nullptr, "PreloadLibraryApplet"}, | 36 | {0x00160040, nullptr, "PreloadLibraryApplet"}, |
| 37 | {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, | 37 | {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, |
| 38 | {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, | 38 | {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, |
| 39 | {0x00190040, nullptr, "PrepareToStartSystemApplet"}, | 39 | {0x00190040, nullptr, "PrepareToStartSystemApplet"}, |
| 40 | {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, | 40 | {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, |
| 41 | {0x001B00C4, nullptr, "StartApplication"}, | 41 | {0x001B00C4, nullptr, "StartApplication"}, |
| 42 | {0x001C0000, nullptr, "WakeupApplication"}, | 42 | {0x001C0000, nullptr, "WakeupApplication"}, |
| 43 | {0x001D0000, nullptr, "CancelApplication"}, | 43 | {0x001D0000, nullptr, "CancelApplication"}, |
| 44 | {0x001E0084, nullptr, "StartLibraryApplet"}, | 44 | {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, |
| 45 | {0x001F0084, nullptr, "StartSystemApplet"}, | 45 | {0x001F0084, nullptr, "StartSystemApplet"}, |
| 46 | {0x00200044, nullptr, "StartNewestHomeMenu"}, | 46 | {0x00200044, nullptr, "StartNewestHomeMenu"}, |
| 47 | {0x00210000, nullptr, "OrderToCloseApplication"}, | 47 | {0x00210000, nullptr, "OrderToCloseApplication"}, |
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index af4adba84..a329514a6 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp | |||
| @@ -11,7 +11,9 @@ namespace CFG { | |||
| 11 | 11 | ||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | 12 | const Interface::FunctionInfo FunctionTable[] = { |
| 13 | {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, | 13 | {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, |
| 14 | {0x00020000, nullptr, "SecureInfoGetRegion"}, | 14 | {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, |
| 15 | {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, | ||
| 16 | {0x00050000, GetSystemModel, "GetSystemModel"}, | ||
| 15 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, | 17 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, |
| 16 | {0x04020082, nullptr, "SetConfigInfoBlk4"}, | 18 | {0x04020082, nullptr, "SetConfigInfoBlk4"}, |
| 17 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | 19 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index fafb43a2f..a8cb15d60 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -310,4 +310,9 @@ Interface::Interface() { | |||
| 310 | Register(FunctionTable); | 310 | Register(FunctionTable); |
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | Interface::~Interface() { | ||
| 314 | semaphore_event = nullptr; | ||
| 315 | interrupt_event = nullptr; | ||
| 316 | } | ||
| 317 | |||
| 313 | } // namespace | 318 | } // namespace |
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index fa13bfb7c..b6f611db5 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 7 | #include "core/hle/service/service.h" | 9 | #include "core/hle/service/service.h" |
| 8 | 10 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -14,6 +16,7 @@ namespace DSP_DSP { | |||
| 14 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 15 | public: | 17 | public: |
| 16 | Interface(); | 18 | Interface(); |
| 19 | ~Interface() override; | ||
| 17 | 20 | ||
| 18 | std::string GetPortName() const override { | 21 | std::string GetPortName() const override { |
| 19 | return "dsp::DSP"; | 22 | return "dsp::DSP"; |
diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 439c7282e..3a5897d06 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp | |||
| @@ -10,15 +10,26 @@ namespace Service { | |||
| 10 | namespace FRD { | 10 | namespace FRD { |
| 11 | 11 | ||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | 12 | const Interface::FunctionInfo FunctionTable[] = { |
| 13 | {0x00010000, nullptr, "HasLoggedIn"}, | ||
| 14 | {0x00030000, nullptr, "Login"}, | ||
| 15 | {0x00040000, nullptr, "Logout"}, | ||
| 13 | {0x00050000, nullptr, "GetFriendKey"}, | 16 | {0x00050000, nullptr, "GetFriendKey"}, |
| 14 | {0x00080000, nullptr, "GetMyPresence"}, | 17 | {0x00080000, nullptr, "GetMyPresence"}, |
| 18 | {0x00090000, nullptr, "GetMyScreenName"}, | ||
| 15 | {0x00100040, nullptr, "GetPassword"}, | 19 | {0x00100040, nullptr, "GetPassword"}, |
| 20 | {0x00110080, nullptr, "GetFriendKeyList"}, | ||
| 16 | {0x00190042, nullptr, "GetFriendFavoriteGame"}, | 21 | {0x00190042, nullptr, "GetFriendFavoriteGame"}, |
| 17 | {0x001A00C4, nullptr, "GetFriendInfo"}, | 22 | {0x001A00C4, nullptr, "GetFriendInfo"}, |
| 18 | {0x001B0080, nullptr, "IsOnFriendList"}, | 23 | {0x001B0080, nullptr, "IsOnFriendList"}, |
| 19 | {0x001C0042, nullptr, "DecodeLocalFriendCode"}, | 24 | {0x001C0042, nullptr, "DecodeLocalFriendCode"}, |
| 20 | {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, | 25 | {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, |
| 21 | {0x00320042, nullptr, "SetClientSdkVersion"} | 26 | {0x00230000, nullptr, "GetLastResponseResult"}, |
| 27 | {0x00270040, nullptr, "ResultToErrorCode"}, | ||
| 28 | {0x00280244, nullptr, "RequestGameAuthentication"}, | ||
| 29 | {0x00290000, nullptr, "GetGameAuthenticationData"}, | ||
| 30 | {0x002A0204, nullptr, "RequestServiceLocator"}, | ||
| 31 | {0x002B0000, nullptr, "GetServiceLocatorData"}, | ||
| 32 | {0x00320042, nullptr, "SetClientSdkVersion"}, | ||
| 22 | }; | 33 | }; |
| 23 | 34 | ||
| 24 | FRD_U_Interface::FRD_U_Interface() { | 35 | FRD_U_Interface::FRD_U_Interface() { |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 4e275cb13..6c0df67c3 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -2,29 +2,35 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstddef> | ||
| 6 | #include <system_error> | ||
| 7 | #include <type_traits> | ||
| 5 | #include <memory> | 8 | #include <memory> |
| 6 | #include <unordered_map> | 9 | #include <unordered_map> |
| 10 | #include <utility> | ||
| 7 | 11 | ||
| 8 | #include <boost/container/flat_map.hpp> | 12 | #include <boost/container/flat_map.hpp> |
| 9 | 13 | ||
| 14 | #include "common/assert.h" | ||
| 10 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 11 | #include "common/file_util.h" | 16 | #include "common/file_util.h" |
| 12 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 13 | #include "common/make_unique.h" | 18 | #include "common/make_unique.h" |
| 14 | #include "common/math_util.h" | ||
| 15 | 19 | ||
| 16 | #include "core/file_sys/archive_backend.h" | 20 | #include "core/file_sys/archive_backend.h" |
| 17 | #include "core/file_sys/archive_extsavedata.h" | 21 | #include "core/file_sys/archive_extsavedata.h" |
| 18 | #include "core/file_sys/archive_romfs.h" | ||
| 19 | #include "core/file_sys/archive_savedata.h" | 22 | #include "core/file_sys/archive_savedata.h" |
| 20 | #include "core/file_sys/archive_savedatacheck.h" | 23 | #include "core/file_sys/archive_savedatacheck.h" |
| 21 | #include "core/file_sys/archive_sdmc.h" | 24 | #include "core/file_sys/archive_sdmc.h" |
| 22 | #include "core/file_sys/archive_systemsavedata.h" | 25 | #include "core/file_sys/archive_systemsavedata.h" |
| 23 | #include "core/file_sys/directory_backend.h" | 26 | #include "core/file_sys/directory_backend.h" |
| 27 | #include "core/file_sys/file_backend.h" | ||
| 28 | #include "core/hle/hle.h" | ||
| 24 | #include "core/hle/service/service.h" | 29 | #include "core/hle/service/service.h" |
| 25 | #include "core/hle/service/fs/archive.h" | 30 | #include "core/hle/service/fs/archive.h" |
| 26 | #include "core/hle/service/fs/fs_user.h" | 31 | #include "core/hle/service/fs/fs_user.h" |
| 27 | #include "core/hle/result.h" | 32 | #include "core/hle/result.h" |
| 33 | #include "core/memory.h" | ||
| 28 | 34 | ||
| 29 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. | 35 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. |
| 30 | // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 | 36 | // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 |
| @@ -110,7 +116,7 @@ ResultVal<bool> File::SyncRequest() { | |||
| 110 | u32 address = cmd_buff[6]; | 116 | u32 address = cmd_buff[6]; |
| 111 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | 117 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", |
| 112 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | 118 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); |
| 113 | cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush, Memory::GetPointer(address))); | 119 | cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address))); |
| 114 | break; | 120 | break; |
| 115 | } | 121 | } |
| 116 | 122 | ||
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 357b6b096..f61125953 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -4,22 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | |||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | 11 | ||
| 9 | #include "core/file_sys/archive_backend.h" | 12 | #include "core/file_sys/archive_backend.h" |
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/session.h" | 13 | #include "core/hle/kernel/session.h" |
| 12 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 13 | 15 | ||
| 16 | namespace FileSys { | ||
| 17 | class DirectoryBackend; | ||
| 18 | class FileBackend; | ||
| 19 | } | ||
| 20 | |||
| 14 | /// The unique system identifier hash, also known as ID0 | 21 | /// The unique system identifier hash, also known as ID0 |
| 15 | extern const std::string SYSTEM_ID; | 22 | extern const std::string SYSTEM_ID; |
| 16 | /// The scrambled SD card CID, also known as ID1 | 23 | /// The scrambled SD card CID, also known as ID1 |
| 17 | extern const std::string SDCARD_ID; | 24 | extern const std::string SDCARD_ID; |
| 18 | 25 | ||
| 19 | namespace Kernel { | ||
| 20 | class Session; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Service { | 26 | namespace Service { |
| 24 | namespace FS { | 27 | namespace FS { |
| 25 | 28 | ||
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 0ad44e55e..ae52083f9 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -115,7 +115,8 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 115 | 115 | ||
| 116 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); | 116 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); |
| 117 | if (archive_handle.Failed()) { | 117 | if (archive_handle.Failed()) { |
| 118 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | 118 | LOG_ERROR(Service_FS, "failed to get a handle for archive archive_id=0x%08X archive_path=%s", |
| 119 | archive_id, archive_path.DebugStr().c_str()); | ||
| 119 | cmd_buff[1] = archive_handle.Code().raw; | 120 | cmd_buff[1] = archive_handle.Code().raw; |
| 120 | cmd_buff[3] = 0; | 121 | cmd_buff[3] = 0; |
| 121 | return; | 122 | return; |
| @@ -128,7 +129,8 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 128 | cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); | 129 | cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); |
| 129 | } else { | 130 | } else { |
| 130 | cmd_buff[3] = 0; | 131 | cmd_buff[3] = 0; |
| 131 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | 132 | LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%d", |
| 133 | file_path.DebugStr().c_str(), mode.hex, attributes); | ||
| 132 | } | 134 | } |
| 133 | } | 135 | } |
| 134 | 136 | ||
| @@ -347,7 +349,8 @@ static void OpenDirectory(Service::Interface* self) { | |||
| 347 | if (dir_res.Succeeded()) { | 349 | if (dir_res.Succeeded()) { |
| 348 | cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); | 350 | cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); |
| 349 | } else { | 351 | } else { |
| 350 | LOG_ERROR(Service_FS, "failed to get a handle for directory"); | 352 | LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", |
| 353 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 351 | } | 354 | } |
| 352 | } | 355 | } |
| 353 | 356 | ||
| @@ -382,7 +385,8 @@ static void OpenArchive(Service::Interface* self) { | |||
| 382 | cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; | 385 | cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; |
| 383 | } else { | 386 | } else { |
| 384 | cmd_buff[2] = cmd_buff[3] = 0; | 387 | cmd_buff[2] = cmd_buff[3] = 0; |
| 385 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | 388 | LOG_ERROR(Service_FS, "failed to get a handle for archive archive_id=0x%08X archive_path=%s", |
| 389 | archive_id, archive_path.DebugStr().c_str()); | ||
| 386 | } | 390 | } |
| 387 | } | 391 | } |
| 388 | 392 | ||
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 4b0b4229d..e93c1b436 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -9,14 +9,17 @@ | |||
| 9 | #include "core/hle/kernel/event.h" | 9 | #include "core/hle/kernel/event.h" |
| 10 | #include "core/hle/kernel/shared_memory.h" | 10 | #include "core/hle/kernel/shared_memory.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | #include "gsp_gpu.h" | ||
| 13 | #include "core/hw/hw.h" | 12 | #include "core/hw/hw.h" |
| 14 | #include "core/hw/gpu.h" | 13 | #include "core/hw/gpu.h" |
| 15 | #include "core/hw/lcd.h" | 14 | #include "core/hw/lcd.h" |
| 16 | 15 | ||
| 17 | #include "video_core/gpu_debugger.h" | 16 | #include "video_core/gpu_debugger.h" |
| 17 | #include "video_core/debug_utils/debug_utils.h" | ||
| 18 | #include "video_core/renderer_base.h" | ||
| 18 | #include "video_core/video_core.h" | 19 | #include "video_core/video_core.h" |
| 19 | 20 | ||
| 21 | #include "gsp_gpu.h" | ||
| 22 | |||
| 20 | // Main graphics debugger object - TODO: Here is probably not the best place for this | 23 | // Main graphics debugger object - TODO: Here is probably not the best place for this |
| 21 | GraphicsDebugger g_debugger; | 24 | GraphicsDebugger g_debugger; |
| 22 | 25 | ||
| @@ -40,7 +43,7 @@ static inline u8* GetCommandBuffer(u32 thread_id) { | |||
| 40 | return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); | 43 | return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); |
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { | 46 | FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { |
| 44 | DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index"); | 47 | DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index"); |
| 45 | 48 | ||
| 46 | // For each thread there are two FrameBufferUpdate fields | 49 | // For each thread there are two FrameBufferUpdate fields |
| @@ -203,7 +206,7 @@ static void ReadHWRegs(Service::Interface* self) { | |||
| 203 | } | 206 | } |
| 204 | } | 207 | } |
| 205 | 208 | ||
| 206 | static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | 209 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { |
| 207 | u32 base_address = 0x400000; | 210 | u32 base_address = 0x400000; |
| 208 | PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); | 211 | PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); |
| 209 | PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); | 212 | PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); |
| @@ -224,6 +227,9 @@ static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | |||
| 224 | &info.format); | 227 | &info.format); |
| 225 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, | 228 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, |
| 226 | &info.shown_fb); | 229 | &info.shown_fb); |
| 230 | |||
| 231 | if (Pica::g_debug_context) | ||
| 232 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); | ||
| 227 | } | 233 | } |
| 228 | 234 | ||
| 229 | /** | 235 | /** |
| @@ -347,7 +353,7 @@ void SignalInterrupt(InterruptId interrupt_id) { | |||
| 347 | /// Executes the next GSP command | 353 | /// Executes the next GSP command |
| 348 | static void ExecuteCommand(const Command& command, u32 thread_id) { | 354 | static void ExecuteCommand(const Command& command, u32 thread_id) { |
| 349 | // Utility function to convert register ID to address | 355 | // Utility function to convert register ID to address |
| 350 | auto WriteGPURegister = [](u32 id, u32 data) { | 356 | static auto WriteGPURegister = [](u32 id, u32 data) { |
| 351 | GPU::Write<u32>(0x1EF00000 + 4 * id, data); | 357 | GPU::Write<u32>(0x1EF00000 + 4 * id, data); |
| 352 | }; | 358 | }; |
| 353 | 359 | ||
| @@ -389,19 +395,24 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 389 | case CommandId::SET_MEMORY_FILL: | 395 | case CommandId::SET_MEMORY_FILL: |
| 390 | { | 396 | { |
| 391 | auto& params = command.memory_fill; | 397 | auto& params = command.memory_fill; |
| 392 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), | 398 | |
| 393 | Memory::VirtualToPhysicalAddress(params.start1) >> 3); | 399 | if (params.start1 != 0) { |
| 394 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), | 400 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), |
| 395 | Memory::VirtualToPhysicalAddress(params.end1) >> 3); | 401 | Memory::VirtualToPhysicalAddress(params.start1) >> 3); |
| 396 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); | 402 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), |
| 397 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); | 403 | Memory::VirtualToPhysicalAddress(params.end1) >> 3); |
| 398 | 404 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); | |
| 399 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), | 405 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); |
| 400 | Memory::VirtualToPhysicalAddress(params.start2) >> 3); | 406 | } |
| 401 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), | 407 | |
| 402 | Memory::VirtualToPhysicalAddress(params.end2) >> 3); | 408 | if (params.start2 != 0) { |
| 403 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); | 409 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), |
| 404 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); | 410 | Memory::VirtualToPhysicalAddress(params.start2) >> 3); |
| 411 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), | ||
| 412 | Memory::VirtualToPhysicalAddress(params.end2) >> 3); | ||
| 413 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); | ||
| 414 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); | ||
| 415 | } | ||
| 405 | break; | 416 | break; |
| 406 | } | 417 | } |
| 407 | 418 | ||
| @@ -446,6 +457,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 446 | default: | 457 | default: |
| 447 | LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); | 458 | LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); |
| 448 | } | 459 | } |
| 460 | |||
| 461 | if (Pica::g_debug_context) | ||
| 462 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::GSPCommandProcessed, (void*)&command); | ||
| 449 | } | 463 | } |
| 450 | 464 | ||
| 451 | /** | 465 | /** |
| @@ -582,7 +596,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 582 | Interface::Interface() { | 596 | Interface::Interface() { |
| 583 | Register(FunctionTable); | 597 | Register(FunctionTable); |
| 584 | 598 | ||
| 585 | g_interrupt_event = 0; | 599 | g_interrupt_event = nullptr; |
| 586 | 600 | ||
| 587 | using Kernel::MemoryPermission; | 601 | using Kernel::MemoryPermission; |
| 588 | g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, | 602 | g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, |
| @@ -591,4 +605,9 @@ Interface::Interface() { | |||
| 591 | g_thread_id = 0; | 605 | g_thread_id = 0; |
| 592 | } | 606 | } |
| 593 | 607 | ||
| 608 | Interface::~Interface() { | ||
| 609 | g_interrupt_event = nullptr; | ||
| 610 | g_shared_memory = nullptr; | ||
| 611 | } | ||
| 612 | |||
| 594 | } // namespace | 613 | } // namespace |
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index a435d418a..c89d0a467 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h | |||
| @@ -5,8 +5,11 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 10 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 11 | 14 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -158,6 +161,7 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec | |||
| 158 | class Interface : public Service::Interface { | 161 | class Interface : public Service::Interface { |
| 159 | public: | 162 | public: |
| 160 | Interface(); | 163 | Interface(); |
| 164 | ~Interface() override; | ||
| 161 | 165 | ||
| 162 | std::string GetPortName() const override { | 166 | std::string GetPortName() const override { |
| 163 | return "gsp::Gpu"; | 167 | return "gsp::Gpu"; |
| @@ -170,4 +174,14 @@ public: | |||
| 170 | */ | 174 | */ |
| 171 | void SignalInterrupt(InterruptId interrupt_id); | 175 | void SignalInterrupt(InterruptId interrupt_id); |
| 172 | 176 | ||
| 177 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); | ||
| 178 | |||
| 179 | /** | ||
| 180 | * Retrieves the framebuffer info stored in the GSP shared memory for the | ||
| 181 | * specified screen index and thread id. | ||
| 182 | * @param thread_id GSP thread id of the process that accesses the structure that we are requesting. | ||
| 183 | * @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1). | ||
| 184 | * @returns FramebufferUpdate Information about the specified framebuffer. | ||
| 185 | */ | ||
| 186 | FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); | ||
| 173 | } // namespace | 187 | } // namespace |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index c7c1bb5ab..70caa7d80 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "common/emu_window.h" | ||
| 6 | 7 | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | #include "core/hle/service/hid/hid.h" | 9 | #include "core/hle/service/hid/hid.h" |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 68e2bcee0..d50d479f8 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -6,16 +6,18 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | 8 | ||
| 9 | #include "core/hle/kernel/kernel.h" | 9 | #ifndef _MSC_VER |
| 10 | #include "core/hle/service/service.h" | 10 | #include <cstddef> |
| 11 | #include "common/bit_field.h" | 11 | #endif |
| 12 | 12 | ||
| 13 | namespace Kernel { | 13 | #include "common/bit_field.h" |
| 14 | class SharedMemory; | 14 | #include "common/common_funcs.h" |
| 15 | class Event; | 15 | #include "common/common_types.h" |
| 16 | } | ||
| 17 | 16 | ||
| 18 | namespace Service { | 17 | namespace Service { |
| 18 | |||
| 19 | class Interface; | ||
| 20 | |||
| 19 | namespace HID { | 21 | namespace HID { |
| 20 | 22 | ||
| 21 | /** | 23 | /** |
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index 25b01860e..18b22956f 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp | |||
| @@ -125,4 +125,8 @@ Interface::Interface() { | |||
| 125 | Register(FunctionTable); | 125 | Register(FunctionTable); |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | Interface::~Interface() { | ||
| 129 | handle_event = nullptr; | ||
| 130 | } | ||
| 131 | |||
| 128 | } // namespace | 132 | } // namespace |
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index 82abdff28..0ced2359c 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm_uds.h | |||
| @@ -16,6 +16,7 @@ namespace NWM_UDS { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface() override; | ||
| 19 | 20 | ||
| 20 | std::string GetPortName() const override { | 21 | std::string GetPortName() const override { |
| 21 | return "nwm::UDS"; | 22 | return "nwm::UDS"; |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index d681cc3dc..0de0b13a3 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -54,7 +54,7 @@ static std::string MakeFunctionString(const char* name, const char* port_name, c | |||
| 54 | 54 | ||
| 55 | std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); | 55 | std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); |
| 56 | for (int i = 1; i <= num_params; ++i) { | 56 | for (int i = 1; i <= num_params; ++i) { |
| 57 | function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); | 57 | function_string += Common::StringFromFormat(", cmd_buff[%i]=0x%X", i, cmd_buff[i]); |
| 58 | } | 58 | } |
| 59 | return function_string; | 59 | return function_string; |
| 60 | } | 60 | } |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 77bfb9ff1..f31135212 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 7 | #include <string> | 8 | #include <string> |
| 8 | #include <unordered_map> | 9 | #include <unordered_map> |
| 9 | 10 | ||
| @@ -11,8 +12,8 @@ | |||
| 11 | 12 | ||
| 12 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 13 | 14 | ||
| 14 | #include "core/hle/kernel/kernel.h" | ||
| 15 | #include "core/hle/kernel/session.h" | 15 | #include "core/hle/kernel/session.h" |
| 16 | #include "core/hle/result.h" | ||
| 16 | 17 | ||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 18 | // Namespace Service | 19 | // Namespace Service |
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 1e0f5df9b..d0e166fdf 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp | |||
| @@ -2,40 +2,47 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <unordered_map> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 5 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 6 | #include "common/platform.h" | 13 | #include "common/scope_exit.h" |
| 7 | |||
| 8 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 9 | #include <winsock2.h> | ||
| 10 | #include <ws2tcpip.h> | ||
| 11 | |||
| 12 | // MinGW does not define several errno constants | ||
| 13 | #ifndef _MSC_VER | ||
| 14 | #define EBADMSG 104 | ||
| 15 | #define ENODATA 120 | ||
| 16 | #define ENOMSG 122 | ||
| 17 | #define ENOSR 124 | ||
| 18 | #define ENOSTR 125 | ||
| 19 | #define ETIME 137 | ||
| 20 | #define EIDRM 2001 | ||
| 21 | #define ENOLINK 2002 | ||
| 22 | #endif // _MSC_VER | ||
| 23 | 14 | ||
| 15 | #include "core/hle/kernel/session.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | #include "core/hle/service/soc_u.h" | ||
| 18 | #include "core/memory.h" | ||
| 19 | |||
| 20 | #ifdef _WIN32 | ||
| 21 | #include <winsock2.h> | ||
| 22 | #include <ws2tcpip.h> | ||
| 23 | |||
| 24 | // MinGW does not define several errno constants | ||
| 25 | #ifndef _MSC_VER | ||
| 26 | #define EBADMSG 104 | ||
| 27 | #define ENODATA 120 | ||
| 28 | #define ENOMSG 122 | ||
| 29 | #define ENOSR 124 | ||
| 30 | #define ENOSTR 125 | ||
| 31 | #define ETIME 137 | ||
| 32 | #define EIDRM 2001 | ||
| 33 | #define ENOLINK 2002 | ||
| 34 | #endif // _MSC_VER | ||
| 24 | #else | 35 | #else |
| 25 | #include <sys/socket.h> | 36 | #include <cerrno> |
| 26 | #include <netinet/in.h> | 37 | #include <fcntl.h> |
| 27 | #include <netdb.h> | 38 | #include <netinet/in.h> |
| 28 | #include <arpa/inet.h> | 39 | #include <netdb.h> |
| 29 | #include <fcntl.h> | 40 | #include <poll.h> |
| 30 | #include <poll.h> | 41 | #include <sys/socket.h> |
| 42 | #include <unistd.h> | ||
| 31 | #endif | 43 | #endif |
| 32 | 44 | ||
| 33 | #include "common/scope_exit.h" | 45 | #ifdef _WIN32 |
| 34 | #include "core/hle/hle.h" | ||
| 35 | #include "core/hle/service/soc_u.h" | ||
| 36 | #include <unordered_map> | ||
| 37 | |||
| 38 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 39 | # define WSAEAGAIN WSAEWOULDBLOCK | 46 | # define WSAEAGAIN WSAEWOULDBLOCK |
| 40 | # define WSAEMULTIHOP -1 // Invalid dummy value | 47 | # define WSAEMULTIHOP -1 // Invalid dummy value |
| 41 | # define ERRNO(x) WSA##x | 48 | # define ERRNO(x) WSA##x |
| @@ -328,6 +335,7 @@ static void Socket(Service::Interface* self) { | |||
| 328 | if ((s32)socket_handle == SOCKET_ERROR_VALUE) | 335 | if ((s32)socket_handle == SOCKET_ERROR_VALUE) |
| 329 | result = TranslateError(GET_ERRNO); | 336 | result = TranslateError(GET_ERRNO); |
| 330 | 337 | ||
| 338 | cmd_buffer[0] = IPC::MakeHeader(2, 2, 0); | ||
| 331 | cmd_buffer[1] = result; | 339 | cmd_buffer[1] = result; |
| 332 | cmd_buffer[2] = socket_handle; | 340 | cmd_buffer[2] = socket_handle; |
| 333 | } | 341 | } |
| @@ -351,8 +359,9 @@ static void Bind(Service::Interface* self) { | |||
| 351 | if (res != 0) | 359 | if (res != 0) |
| 352 | result = TranslateError(GET_ERRNO); | 360 | result = TranslateError(GET_ERRNO); |
| 353 | 361 | ||
| 354 | cmd_buffer[2] = res; | 362 | cmd_buffer[0] = IPC::MakeHeader(5, 2, 0); |
| 355 | cmd_buffer[1] = result; | 363 | cmd_buffer[1] = result; |
| 364 | cmd_buffer[2] = res; | ||
| 356 | } | 365 | } |
| 357 | 366 | ||
| 358 | static void Fcntl(Service::Interface* self) { | 367 | static void Fcntl(Service::Interface* self) { |
| @@ -369,7 +378,7 @@ static void Fcntl(Service::Interface* self) { | |||
| 369 | }); | 378 | }); |
| 370 | 379 | ||
| 371 | if (ctr_cmd == 3) { // F_GETFL | 380 | if (ctr_cmd == 3) { // F_GETFL |
| 372 | #if EMU_PLATFORM == PLATFORM_WINDOWS | 381 | #ifdef _WIN32 |
| 373 | posix_ret = 0; | 382 | posix_ret = 0; |
| 374 | auto iter = open_sockets.find(socket_handle); | 383 | auto iter = open_sockets.find(socket_handle); |
| 375 | if (iter != open_sockets.end() && iter->second.blocking == false) | 384 | if (iter != open_sockets.end() && iter->second.blocking == false) |
| @@ -386,7 +395,7 @@ static void Fcntl(Service::Interface* self) { | |||
| 386 | posix_ret |= 4; // O_NONBLOCK | 395 | posix_ret |= 4; // O_NONBLOCK |
| 387 | #endif | 396 | #endif |
| 388 | } else if (ctr_cmd == 4) { // F_SETFL | 397 | } else if (ctr_cmd == 4) { // F_SETFL |
| 389 | #if EMU_PLATFORM == PLATFORM_WINDOWS | 398 | #ifdef _WIN32 |
| 390 | unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; | 399 | unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; |
| 391 | int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); | 400 | int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); |
| 392 | if (ret == SOCKET_ERROR_VALUE) { | 401 | if (ret == SOCKET_ERROR_VALUE) { |
| @@ -434,8 +443,9 @@ static void Listen(Service::Interface* self) { | |||
| 434 | if (ret != 0) | 443 | if (ret != 0) |
| 435 | result = TranslateError(GET_ERRNO); | 444 | result = TranslateError(GET_ERRNO); |
| 436 | 445 | ||
| 437 | cmd_buffer[2] = ret; | 446 | cmd_buffer[0] = IPC::MakeHeader(3, 2, 0); |
| 438 | cmd_buffer[1] = result; | 447 | cmd_buffer[1] = result; |
| 448 | cmd_buffer[2] = ret; | ||
| 439 | } | 449 | } |
| 440 | 450 | ||
| 441 | static void Accept(Service::Interface* self) { | 451 | static void Accept(Service::Interface* self) { |
| @@ -460,8 +470,10 @@ static void Accept(Service::Interface* self) { | |||
| 460 | Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len); | 470 | Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len); |
| 461 | } | 471 | } |
| 462 | 472 | ||
| 463 | cmd_buffer[2] = ret; | 473 | cmd_buffer[0] = IPC::MakeHeader(4, 2, 2); |
| 464 | cmd_buffer[1] = result; | 474 | cmd_buffer[1] = result; |
| 475 | cmd_buffer[2] = ret; | ||
| 476 | cmd_buffer[3] = IPC::StaticBufferDesc(static_cast<u32>(max_addr_len), 0); | ||
| 465 | } | 477 | } |
| 466 | 478 | ||
| 467 | static void GetHostId(Service::Interface* self) { | 479 | static void GetHostId(Service::Interface* self) { |
| @@ -669,26 +681,29 @@ static void Connect(Service::Interface* self) { | |||
| 669 | int result = 0; | 681 | int result = 0; |
| 670 | if (ret != 0) | 682 | if (ret != 0) |
| 671 | result = TranslateError(GET_ERRNO); | 683 | result = TranslateError(GET_ERRNO); |
| 672 | cmd_buffer[2] = ret; | 684 | |
| 685 | cmd_buffer[0] = IPC::MakeHeader(6, 2, 0); | ||
| 673 | cmd_buffer[1] = result; | 686 | cmd_buffer[1] = result; |
| 687 | cmd_buffer[2] = ret; | ||
| 674 | } | 688 | } |
| 675 | 689 | ||
| 676 | static void InitializeSockets(Service::Interface* self) { | 690 | static void InitializeSockets(Service::Interface* self) { |
| 677 | // TODO(Subv): Implement | 691 | // TODO(Subv): Implement |
| 678 | #if EMU_PLATFORM == PLATFORM_WINDOWS | 692 | #ifdef _WIN32 |
| 679 | WSADATA data; | 693 | WSADATA data; |
| 680 | WSAStartup(MAKEWORD(2, 2), &data); | 694 | WSAStartup(MAKEWORD(2, 2), &data); |
| 681 | #endif | 695 | #endif |
| 682 | 696 | ||
| 683 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | 697 | u32* cmd_buffer = Kernel::GetCommandBuffer(); |
| 684 | cmd_buffer[1] = 0; | 698 | cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); |
| 699 | cmd_buffer[1] = RESULT_SUCCESS.raw; | ||
| 685 | } | 700 | } |
| 686 | 701 | ||
| 687 | static void ShutdownSockets(Service::Interface* self) { | 702 | static void ShutdownSockets(Service::Interface* self) { |
| 688 | // TODO(Subv): Implement | 703 | // TODO(Subv): Implement |
| 689 | CleanupSockets(); | 704 | CleanupSockets(); |
| 690 | 705 | ||
| 691 | #if EMU_PLATFORM == PLATFORM_WINDOWS | 706 | #ifdef _WIN32 |
| 692 | WSACleanup(); | 707 | WSACleanup(); |
| 693 | #endif | 708 | #endif |
| 694 | 709 | ||
| @@ -739,7 +754,7 @@ Interface::Interface() { | |||
| 739 | 754 | ||
| 740 | Interface::~Interface() { | 755 | Interface::~Interface() { |
| 741 | CleanupSockets(); | 756 | CleanupSockets(); |
| 742 | #if EMU_PLATFORM == PLATFORM_WINDOWS | 757 | #ifdef _WIN32 |
| 743 | WSACleanup(); | 758 | WSACleanup(); |
| 744 | #endif | 759 | #endif |
| 745 | } | 760 | } |
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index 483b3111b..a091f597c 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 7 | #include "core/hle/service/service.h" | 9 | #include "core/hle/service/service.h" |
| 8 | 10 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 6c49fa6cf..3b8c7c0e4 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp | |||
| @@ -68,4 +68,8 @@ Interface::Interface() { | |||
| 68 | Register(FunctionTable); | 68 | Register(FunctionTable); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | Interface::~Interface() { | ||
| 72 | event_handle = nullptr; | ||
| 73 | } | ||
| 74 | |||
| 71 | } // namespace | 75 | } // namespace |
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 653aba5cb..96c89b025 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h | |||
| @@ -13,6 +13,7 @@ namespace SRV { | |||
| 13 | class Interface : public Service::Interface { | 13 | class Interface : public Service::Interface { |
| 14 | public: | 14 | public: |
| 15 | Interface(); | 15 | Interface(); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | std::string GetPortName() const override { | 18 | std::string GetPortName() const override { |
| 18 | return "srv:"; | 19 | return "srv:"; |
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index ac1967da8..6e7dafaad 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hw/y2r.h" | 12 | #include "core/hw/y2r.h" |
| 13 | #include "core/mem_map.h" | 13 | #include "core/mem_map.h" |
| 14 | 14 | ||
| 15 | #include "video_core/renderer_base.h" | ||
| 15 | #include "video_core/utils.h" | 16 | #include "video_core/utils.h" |
| 16 | #include "video_core/video_core.h" | 17 | #include "video_core/video_core.h" |
| 17 | 18 | ||
| @@ -409,4 +410,8 @@ Interface::Interface() { | |||
| 409 | Register(FunctionTable); | 410 | Register(FunctionTable); |
| 410 | } | 411 | } |
| 411 | 412 | ||
| 413 | Interface::~Interface() { | ||
| 414 | completion_event = nullptr; | ||
| 415 | } | ||
| 416 | |||
| 412 | } // namespace | 417 | } // namespace |
diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 7df47fcb9..3965a5545 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h | |||
| @@ -5,9 +5,11 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| 12 | #include "core/hle/result.h" | ||
| 11 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 12 | 14 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -110,6 +112,7 @@ struct ConversionConfiguration { | |||
| 110 | class Interface : public Service::Interface { | 112 | class Interface : public Service::Interface { |
| 111 | public: | 113 | public: |
| 112 | Interface(); | 114 | Interface(); |
| 115 | ~Interface() override; | ||
| 113 | 116 | ||
| 114 | std::string GetPortName() const override { | 117 | std::string GetPortName() const override { |
| 115 | return "y2r:u"; | 118 | return "y2r:u"; |
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 4014eee98..26d87c7e2 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp | |||
| @@ -4,12 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | |||
| 10 | #include "core/core.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | #include "core/hle/config_mem.h" | ||
| 13 | #include "core/hle/shared_page.h" | 7 | #include "core/hle/shared_page.h" |
| 14 | 8 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index fd2ab66a2..db6a5340b 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h | |||
| @@ -10,9 +10,12 @@ | |||
| 10 | * write access, according to 3dbrew; this is not emulated) | 10 | * write access, according to 3dbrew; this is not emulated) |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include "common/common_funcs.h" | ||
| 13 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 14 | #include "common/swap.h" | 15 | #include "common/swap.h" |
| 15 | 16 | ||
| 17 | #include "core/memory.h" | ||
| 18 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 17 | 20 | ||
| 18 | namespace SharedPage { | 21 | namespace SharedPage { |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index cc414743a..bb64fdfb7 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/kernel/shared_memory.h" | 22 | #include "core/hle/kernel/shared_memory.h" |
| 23 | #include "core/hle/kernel/thread.h" | 23 | #include "core/hle/kernel/thread.h" |
| 24 | #include "core/hle/kernel/timer.h" | 24 | #include "core/hle/kernel/timer.h" |
| 25 | #include "core/hle/kernel/vm_manager.h" | ||
| 25 | 26 | ||
| 26 | #include "core/hle/function_wrappers.h" | 27 | #include "core/hle/function_wrappers.h" |
| 27 | #include "core/hle/result.h" | 28 | #include "core/hle/result.h" |
| @@ -529,12 +530,33 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) | |||
| 529 | return RESULT_SUCCESS; | 530 | return RESULT_SUCCESS; |
| 530 | } | 531 | } |
| 531 | 532 | ||
| 532 | /// Query memory | 533 | /// Query process memory |
| 533 | static ResultCode QueryMemory(void* info, void* out, u32 addr) { | 534 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, Handle process_handle, u32 addr) { |
| 534 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); | 535 | using Kernel::Process; |
| 536 | Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); | ||
| 537 | if (process == nullptr) | ||
| 538 | return ERR_INVALID_HANDLE; | ||
| 539 | |||
| 540 | auto vma = process->address_space->FindVMA(addr); | ||
| 541 | |||
| 542 | if (vma == process->address_space->vma_map.end()) | ||
| 543 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 544 | |||
| 545 | memory_info->base_address = vma->second.base; | ||
| 546 | memory_info->permission = static_cast<u32>(vma->second.permissions); | ||
| 547 | memory_info->size = vma->second.size; | ||
| 548 | memory_info->state = static_cast<u32>(vma->second.meminfo_state); | ||
| 549 | |||
| 550 | page_info->flags = 0; | ||
| 551 | LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=0x%08X", process_handle, addr); | ||
| 535 | return RESULT_SUCCESS; | 552 | return RESULT_SUCCESS; |
| 536 | } | 553 | } |
| 537 | 554 | ||
| 555 | /// Query memory | ||
| 556 | static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) { | ||
| 557 | return QueryProcessMemory(memory_info, page_info, Kernel::CurrentProcess, addr); | ||
| 558 | } | ||
| 559 | |||
| 538 | /// Create an event | 560 | /// Create an event |
| 539 | static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { | 561 | static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { |
| 540 | using Kernel::Event; | 562 | using Kernel::Event; |
| @@ -806,13 +828,12 @@ static const FunctionDef SVC_Table[] = { | |||
| 806 | {0x7A, nullptr, "AddCodeSegment"}, | 828 | {0x7A, nullptr, "AddCodeSegment"}, |
| 807 | {0x7B, nullptr, "Backdoor"}, | 829 | {0x7B, nullptr, "Backdoor"}, |
| 808 | {0x7C, nullptr, "KernelSetState"}, | 830 | {0x7C, nullptr, "KernelSetState"}, |
| 809 | {0x7D, nullptr, "QueryProcessMemory"}, | 831 | {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, |
| 810 | }; | 832 | }; |
| 811 | 833 | ||
| 812 | Common::Profiling::TimingCategory profiler_svc("SVC Calls"); | 834 | Common::Profiling::TimingCategory profiler_svc("SVC Calls"); |
| 813 | 835 | ||
| 814 | static const FunctionDef* GetSVCInfo(u32 opcode) { | 836 | static const FunctionDef* GetSVCInfo(u32 func_num) { |
| 815 | u32 func_num = opcode & 0xFFFFFF; // 8 bits | ||
| 816 | if (func_num >= ARRAY_SIZE(SVC_Table)) { | 837 | if (func_num >= ARRAY_SIZE(SVC_Table)) { |
| 817 | LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); | 838 | LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); |
| 818 | return nullptr; | 839 | return nullptr; |
| @@ -820,10 +841,10 @@ static const FunctionDef* GetSVCInfo(u32 opcode) { | |||
| 820 | return &SVC_Table[func_num]; | 841 | return &SVC_Table[func_num]; |
| 821 | } | 842 | } |
| 822 | 843 | ||
| 823 | void CallSVC(u32 opcode) { | 844 | void CallSVC(u32 immediate) { |
| 824 | Common::Profiling::ScopeTimer timer_svc(profiler_svc); | 845 | Common::Profiling::ScopeTimer timer_svc(profiler_svc); |
| 825 | 846 | ||
| 826 | const FunctionDef *info = GetSVCInfo(opcode); | 847 | const FunctionDef* info = GetSVCInfo(immediate); |
| 827 | if (info) { | 848 | if (info) { |
| 828 | if (info->func) { | 849 | if (info->func) { |
| 829 | info->func(); | 850 | info->func(); |
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index 4389aa73d..12de9ffbe 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h | |||
| @@ -41,6 +41,6 @@ enum ArbitrationType { | |||
| 41 | 41 | ||
| 42 | namespace SVC { | 42 | namespace SVC { |
| 43 | 43 | ||
| 44 | void CallSVC(u32 opcode); | 44 | void CallSVC(u32 immediate); |
| 45 | 45 | ||
| 46 | } // namespace | 46 | } // namespace |
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7471def57..3ccbc03b2 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | ||
| 6 | #include <type_traits> | ||
| 7 | |||
| 5 | #include "common/color.h" | 8 | #include "common/color.h" |
| 6 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 7 | 10 | #include "common/logging/log.h" | |
| 8 | #include "core/arm/arm_interface.h" | 11 | #include "common/vector_math.h" |
| 9 | 12 | ||
| 10 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 13 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 14 | 16 | ||
| 15 | #include "core/hle/hle.h" | ||
| 16 | #include "core/hle/service/gsp_gpu.h" | 17 | #include "core/hle/service/gsp_gpu.h" |
| 17 | #include "core/hle/service/dsp_dsp.h" | 18 | #include "core/hle/service/dsp_dsp.h" |
| 18 | #include "core/hle/service/hid/hid.h" | 19 | #include "core/hle/service/hid/hid.h" |
| @@ -20,10 +21,17 @@ | |||
| 20 | #include "core/hw/hw.h" | 21 | #include "core/hw/hw.h" |
| 21 | #include "core/hw/gpu.h" | 22 | #include "core/hw/gpu.h" |
| 22 | 23 | ||
| 24 | #include "core/tracer/recorder.h" | ||
| 25 | |||
| 23 | #include "video_core/command_processor.h" | 26 | #include "video_core/command_processor.h" |
| 27 | #include "video_core/hwrasterizer_base.h" | ||
| 28 | #include "video_core/renderer_base.h" | ||
| 24 | #include "video_core/utils.h" | 29 | #include "video_core/utils.h" |
| 25 | #include "video_core/video_core.h" | 30 | #include "video_core/video_core.h" |
| 26 | 31 | ||
| 32 | #include "video_core/debug_utils/debug_utils.h" | ||
| 33 | |||
| 34 | |||
| 27 | namespace GPU { | 35 | namespace GPU { |
| 28 | 36 | ||
| 29 | Regs g_regs; | 37 | Regs g_regs; |
| @@ -53,6 +61,29 @@ inline void Read(T &var, const u32 raw_addr) { | |||
| 53 | var = g_regs[addr / 4]; | 61 | var = g_regs[addr / 4]; |
| 54 | } | 62 | } |
| 55 | 63 | ||
| 64 | static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_pixel) { | ||
| 65 | switch (input_format) { | ||
| 66 | case Regs::PixelFormat::RGBA8: | ||
| 67 | return Color::DecodeRGBA8(src_pixel); | ||
| 68 | |||
| 69 | case Regs::PixelFormat::RGB8: | ||
| 70 | return Color::DecodeRGB8(src_pixel); | ||
| 71 | |||
| 72 | case Regs::PixelFormat::RGB565: | ||
| 73 | return Color::DecodeRGB565(src_pixel); | ||
| 74 | |||
| 75 | case Regs::PixelFormat::RGB5A1: | ||
| 76 | return Color::DecodeRGB5A1(src_pixel); | ||
| 77 | |||
| 78 | case Regs::PixelFormat::RGBA4: | ||
| 79 | return Color::DecodeRGBA4(src_pixel); | ||
| 80 | |||
| 81 | default: | ||
| 82 | LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format); | ||
| 83 | return {0, 0, 0, 0}; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 56 | template <typename T> | 87 | template <typename T> |
| 57 | inline void Write(u32 addr, const T data) { | 88 | inline void Write(u32 addr, const T data) { |
| 58 | addr -= HW::VADDR_GPU; | 89 | addr -= HW::VADDR_GPU; |
| @@ -75,39 +106,43 @@ inline void Write(u32 addr, const T data) { | |||
| 75 | const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].trigger)); | 106 | const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].trigger)); |
| 76 | auto& config = g_regs.memory_fill_config[is_second_filler]; | 107 | auto& config = g_regs.memory_fill_config[is_second_filler]; |
| 77 | 108 | ||
| 78 | if (config.address_start && config.trigger) { | 109 | if (config.trigger) { |
| 79 | u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); | 110 | if (config.address_start) { // Some games pass invalid values here |
| 80 | u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); | 111 | u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); |
| 81 | 112 | u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); | |
| 82 | if (config.fill_24bit) { | 113 | |
| 83 | // fill with 24-bit values | 114 | if (config.fill_24bit) { |
| 84 | for (u8* ptr = start; ptr < end; ptr += 3) { | 115 | // fill with 24-bit values |
| 85 | ptr[0] = config.value_24bit_r; | 116 | for (u8* ptr = start; ptr < end; ptr += 3) { |
| 86 | ptr[1] = config.value_24bit_g; | 117 | ptr[0] = config.value_24bit_r; |
| 87 | ptr[2] = config.value_24bit_b; | 118 | ptr[1] = config.value_24bit_g; |
| 119 | ptr[2] = config.value_24bit_b; | ||
| 120 | } | ||
| 121 | } else if (config.fill_32bit) { | ||
| 122 | // fill with 32-bit values | ||
| 123 | for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) | ||
| 124 | *ptr = config.value_32bit; | ||
| 125 | } else { | ||
| 126 | // fill with 16-bit values | ||
| 127 | for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) | ||
| 128 | *ptr = config.value_16bit; | ||
| 88 | } | 129 | } |
| 89 | } else if (config.fill_32bit) { | ||
| 90 | // fill with 32-bit values | ||
| 91 | for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) | ||
| 92 | *ptr = config.value_32bit; | ||
| 93 | } else { | ||
| 94 | // fill with 16-bit values | ||
| 95 | for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) | ||
| 96 | *ptr = config.value_16bit; | ||
| 97 | } | ||
| 98 | 130 | ||
| 99 | LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); | 131 | LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); |
| 100 | 132 | ||
| 101 | config.trigger = 0; | 133 | if (!is_second_filler) { |
| 102 | config.finished = 1; | 134 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); |
| 135 | } else { | ||
| 136 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); | ||
| 137 | } | ||
| 103 | 138 | ||
| 104 | if (!is_second_filler) { | 139 | VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); |
| 105 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); | ||
| 106 | } else { | ||
| 107 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); | ||
| 108 | } | 140 | } |
| 109 | 141 | ||
| 110 | VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); | 142 | // Reset "trigger" flag and set the "finish" flag |
| 143 | // NOTE: This was confirmed to happen on hardware even if "address_start" is zero. | ||
| 144 | config.trigger = 0; | ||
| 145 | config.finished = 1; | ||
| 111 | } | 146 | } |
| 112 | break; | 147 | break; |
| 113 | } | 148 | } |
| @@ -116,6 +151,10 @@ inline void Write(u32 addr, const T data) { | |||
| 116 | { | 151 | { |
| 117 | const auto& config = g_regs.display_transfer_config; | 152 | const auto& config = g_regs.display_transfer_config; |
| 118 | if (config.trigger & 1) { | 153 | if (config.trigger & 1) { |
| 154 | |||
| 155 | if (Pica::g_debug_context) | ||
| 156 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr); | ||
| 157 | |||
| 119 | u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); | 158 | u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); |
| 120 | u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); | 159 | u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); |
| 121 | 160 | ||
| @@ -125,11 +164,18 @@ inline void Write(u32 addr, const T data) { | |||
| 125 | break; | 164 | break; |
| 126 | } | 165 | } |
| 127 | 166 | ||
| 128 | unsigned horizontal_scale = (config.scaling != config.NoScale) ? 2 : 1; | 167 | if (config.output_tiled && |
| 129 | unsigned vertical_scale = (config.scaling == config.ScaleXY) ? 2 : 1; | 168 | (config.scaling == config.ScaleXY || config.scaling == config.ScaleX)) { |
| 169 | LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); | ||
| 170 | UNIMPLEMENTED(); | ||
| 171 | break; | ||
| 172 | } | ||
| 130 | 173 | ||
| 131 | u32 output_width = config.output_width / horizontal_scale; | 174 | bool horizontal_scale = config.scaling != config.NoScale; |
| 132 | u32 output_height = config.output_height / vertical_scale; | 175 | bool vertical_scale = config.scaling == config.ScaleXY; |
| 176 | |||
| 177 | u32 output_width = config.output_width >> horizontal_scale; | ||
| 178 | u32 output_height = config.output_height >> vertical_scale; | ||
| 133 | 179 | ||
| 134 | u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); | 180 | u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); |
| 135 | u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); | 181 | u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); |
| @@ -153,16 +199,14 @@ inline void Write(u32 addr, const T data) { | |||
| 153 | break; | 199 | break; |
| 154 | } | 200 | } |
| 155 | 201 | ||
| 156 | // TODO(Subv): Implement the box filter when scaling is enabled | ||
| 157 | // right now we're just skipping the extra pixels. | ||
| 158 | for (u32 y = 0; y < output_height; ++y) { | 202 | for (u32 y = 0; y < output_height; ++y) { |
| 159 | for (u32 x = 0; x < output_width; ++x) { | 203 | for (u32 x = 0; x < output_width; ++x) { |
| 160 | Math::Vec4<u8> src_color = { 0, 0, 0, 0 }; | 204 | Math::Vec4<u8> src_color; |
| 161 | 205 | ||
| 162 | // Calculate the [x,y] position of the input image | 206 | // Calculate the [x,y] position of the input image |
| 163 | // based on the current output position and the scale | 207 | // based on the current output position and the scale |
| 164 | u32 input_x = x * horizontal_scale; | 208 | u32 input_x = x << horizontal_scale; |
| 165 | u32 input_y = y * vertical_scale; | 209 | u32 input_y = y << vertical_scale; |
| 166 | 210 | ||
| 167 | if (config.flip_vertically) { | 211 | if (config.flip_vertically) { |
| 168 | // Flip the y value of the output data, | 212 | // Flip the y value of the output data, |
| @@ -177,46 +221,49 @@ inline void Write(u32 addr, const T data) { | |||
| 177 | u32 dst_offset; | 221 | u32 dst_offset; |
| 178 | 222 | ||
| 179 | if (config.output_tiled) { | 223 | if (config.output_tiled) { |
| 180 | // Interpret the input as linear and the output as tiled | 224 | if (!config.dont_swizzle) { |
| 181 | u32 coarse_y = y & ~7; | 225 | // Interpret the input as linear and the output as tiled |
| 182 | u32 stride = output_width * dst_bytes_per_pixel; | 226 | u32 coarse_y = y & ~7; |
| 183 | 227 | u32 stride = output_width * dst_bytes_per_pixel; | |
| 184 | src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; | 228 | |
| 185 | dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; | 229 | src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; |
| 230 | dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; | ||
| 231 | } else { | ||
| 232 | // Both input and output are linear | ||
| 233 | src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; | ||
| 234 | dst_offset = (x + y * output_width) * dst_bytes_per_pixel; | ||
| 235 | } | ||
| 186 | } else { | 236 | } else { |
| 187 | // Interpret the input as tiled and the output as linear | 237 | if (!config.dont_swizzle) { |
| 188 | u32 coarse_y = input_y & ~7; | 238 | // Interpret the input as tiled and the output as linear |
| 189 | u32 stride = config.input_width * src_bytes_per_pixel; | 239 | u32 coarse_y = input_y & ~7; |
| 190 | 240 | u32 stride = config.input_width * src_bytes_per_pixel; | |
| 191 | src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; | 241 | |
| 192 | dst_offset = (x + y * output_width) * dst_bytes_per_pixel; | 242 | src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; |
| 243 | dst_offset = (x + y * output_width) * dst_bytes_per_pixel; | ||
| 244 | } else { | ||
| 245 | // Both input and output are tiled | ||
| 246 | u32 out_coarse_y = y & ~7; | ||
| 247 | u32 out_stride = output_width * dst_bytes_per_pixel; | ||
| 248 | |||
| 249 | u32 in_coarse_y = input_y & ~7; | ||
| 250 | u32 in_stride = config.input_width * src_bytes_per_pixel; | ||
| 251 | |||
| 252 | src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; | ||
| 253 | dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; | ||
| 254 | } | ||
| 193 | } | 255 | } |
| 194 | 256 | ||
| 195 | const u8* src_pixel = src_pointer + src_offset; | 257 | const u8* src_pixel = src_pointer + src_offset; |
| 196 | switch (config.input_format) { | 258 | src_color = DecodePixel(config.input_format, src_pixel); |
| 197 | case Regs::PixelFormat::RGBA8: | 259 | if (config.scaling == config.ScaleX) { |
| 198 | src_color = Color::DecodeRGBA8(src_pixel); | 260 | Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); |
| 199 | break; | 261 | src_color = ((src_color + pixel) / 2).Cast<u8>(); |
| 200 | 262 | } else if (config.scaling == config.ScaleXY) { | |
| 201 | case Regs::PixelFormat::RGB8: | 263 | Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); |
| 202 | src_color = Color::DecodeRGB8(src_pixel); | 264 | Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); |
| 203 | break; | 265 | Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); |
| 204 | 266 | src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); | |
| 205 | case Regs::PixelFormat::RGB565: | ||
| 206 | src_color = Color::DecodeRGB565(src_pixel); | ||
| 207 | break; | ||
| 208 | |||
| 209 | case Regs::PixelFormat::RGB5A1: | ||
| 210 | src_color = Color::DecodeRGB5A1(src_pixel); | ||
| 211 | break; | ||
| 212 | |||
| 213 | case Regs::PixelFormat::RGBA4: | ||
| 214 | src_color = Color::DecodeRGBA4(src_pixel); | ||
| 215 | break; | ||
| 216 | |||
| 217 | default: | ||
| 218 | LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value()); | ||
| 219 | break; | ||
| 220 | } | 267 | } |
| 221 | 268 | ||
| 222 | u8* dst_pixel = dst_pointer + dst_offset; | 269 | u8* dst_pixel = dst_pointer + dst_offset; |
| @@ -254,6 +301,7 @@ inline void Write(u32 addr, const T data) { | |||
| 254 | config.GetPhysicalOutputAddress(), output_width, output_height, | 301 | config.GetPhysicalOutputAddress(), output_width, output_height, |
| 255 | config.output_format.Value(), config.flags); | 302 | config.output_format.Value(), config.flags); |
| 256 | 303 | ||
| 304 | g_regs.display_transfer_config.trigger = 0; | ||
| 257 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); | 305 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); |
| 258 | 306 | ||
| 259 | VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); | 307 | VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); |
| @@ -268,7 +316,14 @@ inline void Write(u32 addr, const T data) { | |||
| 268 | if (config.trigger & 1) | 316 | if (config.trigger & 1) |
| 269 | { | 317 | { |
| 270 | u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress()); | 318 | u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress()); |
| 319 | |||
| 320 | if (Pica::g_debug_context && Pica::g_debug_context->recorder) { | ||
| 321 | Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size * sizeof(u32), config.GetPhysicalAddress()); | ||
| 322 | } | ||
| 323 | |||
| 271 | Pica::CommandProcessor::ProcessCommandList(buffer, config.size); | 324 | Pica::CommandProcessor::ProcessCommandList(buffer, config.size); |
| 325 | |||
| 326 | g_regs.command_processor_config.trigger = 0; | ||
| 272 | } | 327 | } |
| 273 | break; | 328 | break; |
| 274 | } | 329 | } |
| @@ -276,6 +331,13 @@ inline void Write(u32 addr, const T data) { | |||
| 276 | default: | 331 | default: |
| 277 | break; | 332 | break; |
| 278 | } | 333 | } |
| 334 | |||
| 335 | // Notify tracer about the register write | ||
| 336 | // This is happening *after* handling the write to make sure we properly catch all memory reads. | ||
| 337 | if (Pica::g_debug_context && Pica::g_debug_context->recorder) { | ||
| 338 | // addr + GPU VBase - IO VBase + IO PBase | ||
| 339 | Pica::g_debug_context->recorder->RegisterWritten<T>(addr + 0x1EF00000 - 0x1EC00000 + 0x10100000, data); | ||
| 340 | } | ||
| 279 | } | 341 | } |
| 280 | 342 | ||
| 281 | // Explicitly instantiate template functions because we aren't defining this in the header: | 343 | // Explicitly instantiate template functions because we aren't defining this in the header: |
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 699bcd2a5..daad506fe 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <type_traits> | ||
| 8 | 9 | ||
| 9 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 10 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| @@ -202,6 +203,7 @@ struct Regs { | |||
| 202 | BitField< 0, 1, u32> flip_vertically; // flips input data vertically | 203 | BitField< 0, 1, u32> flip_vertically; // flips input data vertically |
| 203 | BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format | 204 | BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format |
| 204 | BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing | 205 | BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing |
| 206 | BitField< 5, 1, u32> dont_swizzle; | ||
| 205 | BitField< 8, 3, PixelFormat> input_format; | 207 | BitField< 8, 3, PixelFormat> input_format; |
| 206 | BitField<12, 3, PixelFormat> output_format; | 208 | BitField<12, 3, PixelFormat> output_format; |
| 207 | 209 | ||
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index c7006a498..b5fdbf9c1 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp | |||
| @@ -15,6 +15,21 @@ template <typename T> | |||
| 15 | inline void Read(T &var, const u32 addr) { | 15 | inline void Read(T &var, const u32 addr) { |
| 16 | switch (addr & 0xFFFFF000) { | 16 | switch (addr & 0xFFFFF000) { |
| 17 | case VADDR_GPU: | 17 | case VADDR_GPU: |
| 18 | case VADDR_GPU + 0x1000: | ||
| 19 | case VADDR_GPU + 0x2000: | ||
| 20 | case VADDR_GPU + 0x3000: | ||
| 21 | case VADDR_GPU + 0x4000: | ||
| 22 | case VADDR_GPU + 0x5000: | ||
| 23 | case VADDR_GPU + 0x6000: | ||
| 24 | case VADDR_GPU + 0x7000: | ||
| 25 | case VADDR_GPU + 0x8000: | ||
| 26 | case VADDR_GPU + 0x9000: | ||
| 27 | case VADDR_GPU + 0xA000: | ||
| 28 | case VADDR_GPU + 0xB000: | ||
| 29 | case VADDR_GPU + 0xC000: | ||
| 30 | case VADDR_GPU + 0xD000: | ||
| 31 | case VADDR_GPU + 0xE000: | ||
| 32 | case VADDR_GPU + 0xF000: | ||
| 18 | GPU::Read(var, addr); | 33 | GPU::Read(var, addr); |
| 19 | break; | 34 | break; |
| 20 | case VADDR_LCD: | 35 | case VADDR_LCD: |
| @@ -29,6 +44,21 @@ template <typename T> | |||
| 29 | inline void Write(u32 addr, const T data) { | 44 | inline void Write(u32 addr, const T data) { |
| 30 | switch (addr & 0xFFFFF000) { | 45 | switch (addr & 0xFFFFF000) { |
| 31 | case VADDR_GPU: | 46 | case VADDR_GPU: |
| 47 | case VADDR_GPU + 0x1000: | ||
| 48 | case VADDR_GPU + 0x2000: | ||
| 49 | case VADDR_GPU + 0x3000: | ||
| 50 | case VADDR_GPU + 0x4000: | ||
| 51 | case VADDR_GPU + 0x5000: | ||
| 52 | case VADDR_GPU + 0x6000: | ||
| 53 | case VADDR_GPU + 0x7000: | ||
| 54 | case VADDR_GPU + 0x8000: | ||
| 55 | case VADDR_GPU + 0x9000: | ||
| 56 | case VADDR_GPU + 0xA000: | ||
| 57 | case VADDR_GPU + 0xB000: | ||
| 58 | case VADDR_GPU + 0xC000: | ||
| 59 | case VADDR_GPU + 0xD000: | ||
| 60 | case VADDR_GPU + 0xE000: | ||
| 61 | case VADDR_GPU + 0xF000: | ||
| 32 | GPU::Write(addr, data); | 62 | GPU::Write(addr, data); |
| 33 | break; | 63 | break; |
| 34 | case VADDR_LCD: | 64 | case VADDR_LCD: |
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index 963c8d981..6f93709e3 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp | |||
| @@ -7,11 +7,12 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | 9 | ||
| 10 | #include "core/arm/arm_interface.h" | ||
| 11 | #include "core/hle/hle.h" | ||
| 12 | #include "core/hw/hw.h" | 10 | #include "core/hw/hw.h" |
| 13 | #include "core/hw/lcd.h" | 11 | #include "core/hw/lcd.h" |
| 14 | 12 | ||
| 13 | #include "core/tracer/recorder.h" | ||
| 14 | #include "video_core/debug_utils/debug_utils.h" | ||
| 15 | |||
| 15 | namespace LCD { | 16 | namespace LCD { |
| 16 | 17 | ||
| 17 | Regs g_regs; | 18 | Regs g_regs; |
| @@ -42,6 +43,13 @@ inline void Write(u32 addr, const T data) { | |||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | g_regs[index] = static_cast<u32>(data); | 45 | g_regs[index] = static_cast<u32>(data); |
| 46 | |||
| 47 | // Notify tracer about the register write | ||
| 48 | // This is happening *after* handling the write to make sure we properly catch all memory reads. | ||
| 49 | if (Pica::g_debug_context && Pica::g_debug_context->recorder) { | ||
| 50 | // addr + GPU VBase - IO VBase + IO PBase | ||
| 51 | Pica::g_debug_context->recorder->RegisterWritten<T>(addr + HW::VADDR_LCD - 0x1EC00000 + 0x10100000, data); | ||
| 52 | } | ||
| 45 | } | 53 | } |
| 46 | 54 | ||
| 47 | // Explicitly instantiate template functions because we aren't defining this in the header: | 55 | // Explicitly instantiate template functions because we aren't defining this in the header: |
diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 8631eb201..bcce6d8cf 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <type_traits> | ||
| 8 | 9 | ||
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp index 5b7fb39e1..f80e26ecd 100644 --- a/src/core/hw/y2r.cpp +++ b/src/core/hw/y2r.cpp | |||
| @@ -2,8 +2,10 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <array> | 6 | #include <array> |
| 6 | #include <numeric> | 7 | #include <cstddef> |
| 8 | #include <memory> | ||
| 7 | 9 | ||
| 8 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 9 | #include "common/color.h" | 11 | #include "common/color.h" |
| @@ -109,7 +111,7 @@ static void SendData(const u32* input, ConversionBuffer& buf, int amount_of_data | |||
| 109 | while (output < unit_end) { | 111 | while (output < unit_end) { |
| 110 | u32 color = *input++; | 112 | u32 color = *input++; |
| 111 | Math::Vec4<u8> col_vec{ | 113 | Math::Vec4<u8> col_vec{ |
| 112 | (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF, alpha, | 114 | (u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), alpha |
| 113 | }; | 115 | }; |
| 114 | 116 | ||
| 115 | switch (output_format) { | 117 | switch (output_format) { |
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 14aeebebb..530837d08 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp | |||
| @@ -19,7 +19,7 @@ | |||
| 19 | 19 | ||
| 20 | namespace Loader { | 20 | namespace Loader { |
| 21 | 21 | ||
| 22 | /** | 22 | /* |
| 23 | * File layout: | 23 | * File layout: |
| 24 | * - File header | 24 | * - File header |
| 25 | * - Code, rodata and data relocation table headers | 25 | * - Code, rodata and data relocation table headers |
| @@ -39,13 +39,16 @@ namespace Loader { | |||
| 39 | * The entrypoint is always the start of the code segment. | 39 | * The entrypoint is always the start of the code segment. |
| 40 | * The BSS section must be cleared manually by the application. | 40 | * The BSS section must be cleared manually by the application. |
| 41 | */ | 41 | */ |
| 42 | |||
| 42 | enum THREEDSX_Error { | 43 | enum THREEDSX_Error { |
| 43 | ERROR_NONE = 0, | 44 | ERROR_NONE = 0, |
| 44 | ERROR_READ = 1, | 45 | ERROR_READ = 1, |
| 45 | ERROR_FILE = 2, | 46 | ERROR_FILE = 2, |
| 46 | ERROR_ALLOC = 3 | 47 | ERROR_ALLOC = 3 |
| 47 | }; | 48 | }; |
| 49 | |||
| 48 | static const u32 RELOCBUFSIZE = 512; | 50 | static const u32 RELOCBUFSIZE = 512; |
| 51 | static const unsigned int NUM_SEGMENTS = 3; | ||
| 49 | 52 | ||
| 50 | // File header | 53 | // File header |
| 51 | #pragma pack(1) | 54 | #pragma pack(1) |
| @@ -98,7 +101,10 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets) | |||
| 98 | return loadinfo->seg_addrs[2] + addr - offsets[1]; | 101 | return loadinfo->seg_addrs[2] + addr - offsets[1]; |
| 99 | } | 102 | } |
| 100 | 103 | ||
| 101 | static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | 104 | using Kernel::SharedPtr; |
| 105 | using Kernel::CodeSet; | ||
| 106 | |||
| 107 | static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, SharedPtr<CodeSet>* out_codeset) | ||
| 102 | { | 108 | { |
| 103 | if (!file.IsOpen()) | 109 | if (!file.IsOpen()) |
| 104 | return ERROR_FILE; | 110 | return ERROR_FILE; |
| @@ -116,15 +122,13 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 116 | loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF; | 122 | loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF; |
| 117 | loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF; | 123 | loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF; |
| 118 | u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] }; | 124 | u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] }; |
| 119 | u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF; | 125 | u32 n_reloc_tables = hdr.reloc_hdr_size / sizeof(u32); |
| 120 | u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size; | 126 | std::vector<u8> program_image(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); |
| 121 | u32 n_reloc_tables = hdr.reloc_hdr_size / 4; | ||
| 122 | std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables); | ||
| 123 | 127 | ||
| 124 | loadinfo.seg_addrs[0] = base_addr; | 128 | loadinfo.seg_addrs[0] = base_addr; |
| 125 | loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; | 129 | loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; |
| 126 | loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; | 130 | loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; |
| 127 | loadinfo.seg_ptrs[0] = &all_mem[0]; | 131 | loadinfo.seg_ptrs[0] = program_image.data(); |
| 128 | loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; | 132 | loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; |
| 129 | loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; | 133 | loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; |
| 130 | 134 | ||
| @@ -132,10 +136,9 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 132 | file.Seek(hdr.header_size, SEEK_SET); | 136 | file.Seek(hdr.header_size, SEEK_SET); |
| 133 | 137 | ||
| 134 | // Read the relocation headers | 138 | // Read the relocation headers |
| 135 | u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); | 139 | std::vector<u32> relocs(n_reloc_tables * NUM_SEGMENTS); |
| 136 | 140 | for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { | |
| 137 | for (unsigned current_segment : {0, 1, 2}) { | 141 | size_t size = n_reloc_tables * sizeof(u32); |
| 138 | size_t size = n_reloc_tables * 4; | ||
| 139 | if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) | 142 | if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) |
| 140 | return ERROR_READ; | 143 | return ERROR_READ; |
| 141 | } | 144 | } |
| @@ -152,7 +155,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 152 | memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); | 155 | memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); |
| 153 | 156 | ||
| 154 | // Relocate the segments | 157 | // Relocate the segments |
| 155 | for (unsigned current_segment : {0, 1, 2}) { | 158 | for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { |
| 156 | for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { | 159 | for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { |
| 157 | u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; | 160 | u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; |
| 158 | if (current_segment_reloc_table >= 2) { | 161 | if (current_segment_reloc_table >= 2) { |
| @@ -160,7 +163,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 160 | file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); | 163 | file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); |
| 161 | continue; | 164 | continue; |
| 162 | } | 165 | } |
| 163 | static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; | 166 | THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; |
| 164 | 167 | ||
| 165 | u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; | 168 | u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; |
| 166 | const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); | 169 | const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); |
| @@ -179,7 +182,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 179 | pos += table.skip; | 182 | pos += table.skip; |
| 180 | s32 num_patches = table.patch; | 183 | s32 num_patches = table.patch; |
| 181 | while (0 < num_patches && pos < end_pos) { | 184 | while (0 < num_patches && pos < end_pos) { |
| 182 | u32 in_addr = (char*)pos - (char*)&all_mem[0]; | 185 | u32 in_addr = (u8*)pos - program_image.data(); |
| 183 | u32 addr = TranslateAddr(*pos, &loadinfo, offsets); | 186 | u32 addr = TranslateAddr(*pos, &loadinfo, offsets); |
| 184 | LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", | 187 | LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", |
| 185 | base_addr + in_addr, addr, current_segment_reloc_table, *pos); | 188 | base_addr + in_addr, addr, current_segment_reloc_table, *pos); |
| @@ -188,7 +191,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 188 | *pos = (addr); | 191 | *pos = (addr); |
| 189 | break; | 192 | break; |
| 190 | case 1: | 193 | case 1: |
| 191 | *pos = (addr - in_addr); | 194 | *pos = static_cast<u32>(addr - in_addr); |
| 192 | break; | 195 | break; |
| 193 | default: | 196 | default: |
| 194 | break; //this should never happen | 197 | break; //this should never happen |
| @@ -201,14 +204,29 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) | |||
| 201 | } | 204 | } |
| 202 | } | 205 | } |
| 203 | 206 | ||
| 204 | // Write the data | 207 | // Create the CodeSet |
| 205 | memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); | 208 | SharedPtr<CodeSet> code_set = CodeSet::Create("", 0); |
| 209 | |||
| 210 | code_set->code.offset = loadinfo.seg_ptrs[0] - program_image.data(); | ||
| 211 | code_set->code.addr = loadinfo.seg_addrs[0]; | ||
| 212 | code_set->code.size = loadinfo.seg_sizes[0]; | ||
| 213 | |||
| 214 | code_set->rodata.offset = loadinfo.seg_ptrs[1] - program_image.data(); | ||
| 215 | code_set->rodata.addr = loadinfo.seg_addrs[1]; | ||
| 216 | code_set->rodata.size = loadinfo.seg_sizes[1]; | ||
| 206 | 217 | ||
| 207 | LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000); | 218 | code_set->data.offset = loadinfo.seg_ptrs[2] - program_image.data(); |
| 208 | LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000); | 219 | code_set->data.addr = loadinfo.seg_addrs[2]; |
| 209 | LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000); | 220 | code_set->data.size = loadinfo.seg_sizes[2]; |
| 210 | LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000); | ||
| 211 | 221 | ||
| 222 | code_set->entrypoint = code_set->code.addr; | ||
| 223 | code_set->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 224 | |||
| 225 | LOG_DEBUG(Loader, "code size: 0x%X", loadinfo.seg_sizes[0]); | ||
| 226 | LOG_DEBUG(Loader, "rodata size: 0x%X", loadinfo.seg_sizes[1]); | ||
| 227 | LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2], hdr.bss_size); | ||
| 228 | |||
| 229 | *out_codeset = code_set; | ||
| 212 | return ERROR_NONE; | 230 | return ERROR_NONE; |
| 213 | } | 231 | } |
| 214 | 232 | ||
| @@ -228,19 +246,22 @@ ResultStatus AppLoader_THREEDSX::Load() { | |||
| 228 | if (is_loaded) | 246 | if (is_loaded) |
| 229 | return ResultStatus::ErrorAlreadyLoaded; | 247 | return ResultStatus::ErrorAlreadyLoaded; |
| 230 | 248 | ||
| 231 | if (!file->IsOpen()) | 249 | if (!file.IsOpen()) |
| 250 | return ResultStatus::Error; | ||
| 251 | |||
| 252 | SharedPtr<CodeSet> codeset; | ||
| 253 | if (Load3DSXFile(file, Memory::PROCESS_IMAGE_VADDR, &codeset) != ERROR_NONE) | ||
| 232 | return ResultStatus::Error; | 254 | return ResultStatus::Error; |
| 255 | codeset->name = filename; | ||
| 233 | 256 | ||
| 234 | Kernel::g_current_process = Kernel::Process::Create(filename, 0); | 257 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); |
| 235 | Kernel::g_current_process->svc_access_mask.set(); | 258 | Kernel::g_current_process->svc_access_mask.set(); |
| 236 | Kernel::g_current_process->address_mappings = default_address_mappings; | 259 | Kernel::g_current_process->address_mappings = default_address_mappings; |
| 237 | 260 | ||
| 238 | // Attach the default resource limit (APPLICATION) to the process | 261 | // Attach the default resource limit (APPLICATION) to the process |
| 239 | Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 262 | Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 240 | 263 | ||
| 241 | Load3DSXFile(*file, Memory::PROCESS_IMAGE_VADDR); | 264 | Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); |
| 242 | |||
| 243 | Kernel::g_current_process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); | ||
| 244 | 265 | ||
| 245 | is_loaded = true; | 266 | is_loaded = true; |
| 246 | return ResultStatus::Success; | 267 | return ResultStatus::Success; |
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 096b3ec20..a0aa0c533 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h | |||
| @@ -17,7 +17,7 @@ namespace Loader { | |||
| 17 | /// Loads an 3DSX file | 17 | /// Loads an 3DSX file |
| 18 | class AppLoader_THREEDSX final : public AppLoader { | 18 | class AppLoader_THREEDSX final : public AppLoader { |
| 19 | public: | 19 | public: |
| 20 | AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) | 20 | AppLoader_THREEDSX(FileUtil::IOFile&& file, std::string filename) |
| 21 | : AppLoader(std::move(file)), filename(std::move(filename)) {} | 21 | : AppLoader(std::move(file)), filename(std::move(filename)) {} |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index f00753a79..5d7264f12 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | ||
| 5 | #include <string> | 6 | #include <string> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | 8 | ||
| @@ -10,11 +11,14 @@ | |||
| 10 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 11 | #include "common/symbols.h" | 12 | #include "common/symbols.h" |
| 12 | 13 | ||
| 13 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/process.h" |
| 14 | #include "core/hle/kernel/resource_limit.h" | 15 | #include "core/hle/kernel/resource_limit.h" |
| 15 | #include "core/loader/elf.h" | 16 | #include "core/loader/elf.h" |
| 16 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 17 | 18 | ||
| 19 | using Kernel::SharedPtr; | ||
| 20 | using Kernel::CodeSet; | ||
| 21 | |||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 22 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 19 | // ELF Header Constants | 23 | // ELF Header Constants |
| 20 | 24 | ||
| @@ -96,6 +100,12 @@ enum ElfSectionFlags | |||
| 96 | #define PT_LOPROC 0x70000000 | 100 | #define PT_LOPROC 0x70000000 |
| 97 | #define PT_HIPROC 0x7FFFFFFF | 101 | #define PT_HIPROC 0x7FFFFFFF |
| 98 | 102 | ||
| 103 | // Segment flags | ||
| 104 | #define PF_X 0x1 | ||
| 105 | #define PF_W 0x2 | ||
| 106 | #define PF_R 0x4 | ||
| 107 | #define PF_MASKPROC 0xF0000000 | ||
| 108 | |||
| 99 | typedef unsigned int Elf32_Addr; | 109 | typedef unsigned int Elf32_Addr; |
| 100 | typedef unsigned short Elf32_Half; | 110 | typedef unsigned short Elf32_Half; |
| 101 | typedef unsigned int Elf32_Off; | 111 | typedef unsigned int Elf32_Off; |
| @@ -192,7 +202,7 @@ public: | |||
| 192 | ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } | 202 | ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } |
| 193 | u32 GetEntryPoint() const { return entryPoint; } | 203 | u32 GetEntryPoint() const { return entryPoint; } |
| 194 | u32 GetFlags() const { return (u32)(header->e_flags); } | 204 | u32 GetFlags() const { return (u32)(header->e_flags); } |
| 195 | void LoadInto(u32 vaddr); | 205 | SharedPtr<CodeSet> LoadInto(u32 vaddr); |
| 196 | bool LoadSymbols(); | 206 | bool LoadSymbols(); |
| 197 | 207 | ||
| 198 | int GetNumSegments() const { return (int)(header->e_phnum); } | 208 | int GetNumSegments() const { return (int)(header->e_phnum); } |
| @@ -248,7 +258,7 @@ const char *ElfReader::GetSectionName(int section) const { | |||
| 248 | return nullptr; | 258 | return nullptr; |
| 249 | } | 259 | } |
| 250 | 260 | ||
| 251 | void ElfReader::LoadInto(u32 vaddr) { | 261 | SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { |
| 252 | LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); | 262 | LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); |
| 253 | 263 | ||
| 254 | // Should we relocate? | 264 | // Should we relocate? |
| @@ -263,22 +273,63 @@ void ElfReader::LoadInto(u32 vaddr) { | |||
| 263 | LOG_DEBUG(Loader, "%i segments:", header->e_phnum); | 273 | LOG_DEBUG(Loader, "%i segments:", header->e_phnum); |
| 264 | 274 | ||
| 265 | // First pass : Get the bits into RAM | 275 | // First pass : Get the bits into RAM |
| 266 | u32 segment_addr[32]; | ||
| 267 | u32 base_addr = relocate ? vaddr : 0; | 276 | u32 base_addr = relocate ? vaddr : 0; |
| 268 | 277 | ||
| 269 | for (unsigned i = 0; i < header->e_phnum; i++) { | 278 | u32 total_image_size = 0; |
| 270 | Elf32_Phdr* p = segments + i; | 279 | for (unsigned int i = 0; i < header->e_phnum; ++i) { |
| 271 | LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, | 280 | Elf32_Phdr* p = &segments[i]; |
| 281 | if (p->p_type == PT_LOAD) { | ||
| 282 | total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | std::vector<u8> program_image(total_image_size); | ||
| 287 | size_t current_image_position = 0; | ||
| 288 | |||
| 289 | SharedPtr<CodeSet> codeset = CodeSet::Create("", 0); | ||
| 290 | |||
| 291 | for (unsigned int i = 0; i < header->e_phnum; ++i) { | ||
| 292 | Elf32_Phdr* p = &segments[i]; | ||
| 293 | LOG_DEBUG(Loader, "Type: %i Vaddr: %08X Filesz: %8X Memsz: %8X ", p->p_type, p->p_vaddr, | ||
| 272 | p->p_filesz, p->p_memsz); | 294 | p->p_filesz, p->p_memsz); |
| 273 | 295 | ||
| 274 | if (p->p_type == PT_LOAD) { | 296 | if (p->p_type == PT_LOAD) { |
| 275 | segment_addr[i] = base_addr + p->p_vaddr; | 297 | CodeSet::Segment* codeset_segment; |
| 276 | memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); | 298 | u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); |
| 277 | LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], | 299 | if (permission_flags == (PF_R | PF_X)) { |
| 278 | p->p_memsz); | 300 | codeset_segment = &codeset->code; |
| 301 | } else if (permission_flags == (PF_R)) { | ||
| 302 | codeset_segment = &codeset->rodata; | ||
| 303 | } else if (permission_flags == (PF_R | PF_W)) { | ||
| 304 | codeset_segment = &codeset->data; | ||
| 305 | } else { | ||
| 306 | LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id %u with flags %X", i, p->p_flags); | ||
| 307 | continue; | ||
| 308 | } | ||
| 309 | |||
| 310 | if (codeset_segment->size != 0) { | ||
| 311 | LOG_ERROR(Loader, "ELF has more than one segment of the same type. Skipping extra segment (id %i)", i); | ||
| 312 | continue; | ||
| 313 | } | ||
| 314 | |||
| 315 | u32 segment_addr = base_addr + p->p_vaddr; | ||
| 316 | u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; | ||
| 317 | |||
| 318 | codeset_segment->offset = current_image_position; | ||
| 319 | codeset_segment->addr = segment_addr; | ||
| 320 | codeset_segment->size = aligned_size; | ||
| 321 | |||
| 322 | memcpy(&program_image[current_image_position], GetSegmentPtr(i), p->p_filesz); | ||
| 323 | current_image_position += aligned_size; | ||
| 279 | } | 324 | } |
| 280 | } | 325 | } |
| 326 | |||
| 327 | codeset->entrypoint = base_addr + header->e_entry; | ||
| 328 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 329 | |||
| 281 | LOG_DEBUG(Loader, "Done loading."); | 330 | LOG_DEBUG(Loader, "Done loading."); |
| 331 | |||
| 332 | return codeset; | ||
| 282 | } | 333 | } |
| 283 | 334 | ||
| 284 | SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { | 335 | SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { |
| @@ -340,29 +391,29 @@ ResultStatus AppLoader_ELF::Load() { | |||
| 340 | if (is_loaded) | 391 | if (is_loaded) |
| 341 | return ResultStatus::ErrorAlreadyLoaded; | 392 | return ResultStatus::ErrorAlreadyLoaded; |
| 342 | 393 | ||
| 343 | if (!file->IsOpen()) | 394 | if (!file.IsOpen()) |
| 344 | return ResultStatus::Error; | 395 | return ResultStatus::Error; |
| 345 | 396 | ||
| 346 | // Reset read pointer in case this file has been read before. | 397 | // Reset read pointer in case this file has been read before. |
| 347 | file->Seek(0, SEEK_SET); | 398 | file.Seek(0, SEEK_SET); |
| 348 | 399 | ||
| 349 | u32 size = static_cast<u32>(file->GetSize()); | 400 | size_t size = file.GetSize(); |
| 350 | std::unique_ptr<u8[]> buffer(new u8[size]); | 401 | std::unique_ptr<u8[]> buffer(new u8[size]); |
| 351 | if (file->ReadBytes(&buffer[0], size) != size) | 402 | if (file.ReadBytes(&buffer[0], size) != size) |
| 352 | return ResultStatus::Error; | 403 | return ResultStatus::Error; |
| 353 | 404 | ||
| 354 | Kernel::g_current_process = Kernel::Process::Create(filename, 0); | 405 | ElfReader elf_reader(&buffer[0]); |
| 406 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | ||
| 407 | codeset->name = filename; | ||
| 408 | |||
| 409 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); | ||
| 355 | Kernel::g_current_process->svc_access_mask.set(); | 410 | Kernel::g_current_process->svc_access_mask.set(); |
| 356 | Kernel::g_current_process->address_mappings = default_address_mappings; | 411 | Kernel::g_current_process->address_mappings = default_address_mappings; |
| 357 | 412 | ||
| 358 | // Attach the default resource limit (APPLICATION) to the process | 413 | // Attach the default resource limit (APPLICATION) to the process |
| 359 | Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 414 | Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 360 | 415 | ||
| 361 | ElfReader elf_reader(&buffer[0]); | 416 | Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); |
| 362 | elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | ||
| 363 | // TODO: Fill application title | ||
| 364 | |||
| 365 | Kernel::g_current_process->Run(elf_reader.GetEntryPoint(), 48, Kernel::DEFAULT_STACK_SIZE); | ||
| 366 | 417 | ||
| 367 | is_loaded = true; | 418 | is_loaded = true; |
| 368 | return ResultStatus::Success; | 419 | return ResultStatus::Success; |
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 32841606a..c6a5ebe99 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -17,7 +17,7 @@ namespace Loader { | |||
| 17 | /// Loads an ELF/AXF file | 17 | /// Loads an ELF/AXF file |
| 18 | class AppLoader_ELF final : public AppLoader { | 18 | class AppLoader_ELF final : public AppLoader { |
| 19 | public: | 19 | public: |
| 20 | AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) | 20 | AppLoader_ELF(FileUtil::IOFile&& file, std::string filename) |
| 21 | : AppLoader(std::move(file)), filename(std::move(filename)) { } | 21 | : AppLoader(std::move(file)), filename(std::move(filename)) { } |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 8b14edf00..9ef2f8900 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -2,10 +2,12 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | ||
| 5 | #include <string> | 6 | #include <string> |
| 6 | 7 | ||
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "common/make_unique.h" | 9 | #include "common/make_unique.h" |
| 10 | #include "common/string_util.h" | ||
| 9 | 11 | ||
| 10 | #include "core/file_sys/archive_romfs.h" | 12 | #include "core/file_sys/archive_romfs.h" |
| 11 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| @@ -88,8 +90,8 @@ static const char* GetFileTypeString(FileType type) { | |||
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | ResultStatus LoadFile(const std::string& filename) { | 92 | ResultStatus LoadFile(const std::string& filename) { |
| 91 | std::unique_ptr<FileUtil::IOFile> file(new FileUtil::IOFile(filename, "rb")); | 93 | FileUtil::IOFile file(filename, "rb"); |
| 92 | if (!file->IsOpen()) { | 94 | if (!file.IsOpen()) { |
| 93 | LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); | 95 | LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); |
| 94 | return ResultStatus::Error; | 96 | return ResultStatus::Error; |
| 95 | } | 97 | } |
| @@ -97,7 +99,7 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 97 | std::string filename_filename, filename_extension; | 99 | std::string filename_filename, filename_extension; |
| 98 | Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension); | 100 | Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension); |
| 99 | 101 | ||
| 100 | FileType type = IdentifyFile(*file); | 102 | FileType type = IdentifyFile(file); |
| 101 | FileType filename_type = GuessFromExtension(filename_extension); | 103 | FileType filename_type = GuessFromExtension(filename_extension); |
| 102 | 104 | ||
| 103 | if (type != filename_type) { | 105 | if (type != filename_type) { |
| @@ -122,7 +124,7 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 122 | case FileType::CXI: | 124 | case FileType::CXI: |
| 123 | case FileType::CCI: | 125 | case FileType::CCI: |
| 124 | { | 126 | { |
| 125 | AppLoader_NCCH app_loader(std::move(file)); | 127 | AppLoader_NCCH app_loader(std::move(file), filename); |
| 126 | 128 | ||
| 127 | // Load application and RomFS | 129 | // Load application and RomFS |
| 128 | if (ResultStatus::Success == app_loader.Load()) { | 130 | if (ResultStatus::Success == app_loader.Load()) { |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 87e16fb98..a37d3348c 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -4,12 +4,18 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 8 | #include <initializer_list> | ||
| 9 | #include <memory> | ||
| 10 | #include <string> | ||
| 7 | #include <vector> | 11 | #include <vector> |
| 8 | 12 | ||
| 9 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 10 | #include "common/file_util.h" | 14 | #include "common/file_util.h" |
| 11 | 15 | ||
| 12 | #include "core/hle/kernel/process.h" | 16 | namespace Kernel { |
| 17 | struct AddressMapping; | ||
| 18 | } | ||
| 13 | 19 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 15 | // Loader namespace | 21 | // Loader namespace |
| @@ -46,7 +52,7 @@ static inline u32 MakeMagic(char a, char b, char c, char d) { | |||
| 46 | /// Interface for loading an application | 52 | /// Interface for loading an application |
| 47 | class AppLoader : NonCopyable { | 53 | class AppLoader : NonCopyable { |
| 48 | public: | 54 | public: |
| 49 | AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { } | 55 | AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) { } |
| 50 | virtual ~AppLoader() { } | 56 | virtual ~AppLoader() { } |
| 51 | 57 | ||
| 52 | /** | 58 | /** |
| @@ -60,7 +66,7 @@ public: | |||
| 60 | * @param buffer Reference to buffer to store data | 66 | * @param buffer Reference to buffer to store data |
| 61 | * @return ResultStatus result of function | 67 | * @return ResultStatus result of function |
| 62 | */ | 68 | */ |
| 63 | virtual ResultStatus ReadCode(std::vector<u8>& buffer) const { | 69 | virtual ResultStatus ReadCode(std::vector<u8>& buffer) { |
| 64 | return ResultStatus::ErrorNotImplemented; | 70 | return ResultStatus::ErrorNotImplemented; |
| 65 | } | 71 | } |
| 66 | 72 | ||
| @@ -69,7 +75,7 @@ public: | |||
| 69 | * @param buffer Reference to buffer to store data | 75 | * @param buffer Reference to buffer to store data |
| 70 | * @return ResultStatus result of function | 76 | * @return ResultStatus result of function |
| 71 | */ | 77 | */ |
| 72 | virtual ResultStatus ReadIcon(std::vector<u8>& buffer) const { | 78 | virtual ResultStatus ReadIcon(std::vector<u8>& buffer) { |
| 73 | return ResultStatus::ErrorNotImplemented; | 79 | return ResultStatus::ErrorNotImplemented; |
| 74 | } | 80 | } |
| 75 | 81 | ||
| @@ -78,7 +84,7 @@ public: | |||
| 78 | * @param buffer Reference to buffer to store data | 84 | * @param buffer Reference to buffer to store data |
| 79 | * @return ResultStatus result of function | 85 | * @return ResultStatus result of function |
| 80 | */ | 86 | */ |
| 81 | virtual ResultStatus ReadBanner(std::vector<u8>& buffer) const { | 87 | virtual ResultStatus ReadBanner(std::vector<u8>& buffer) { |
| 82 | return ResultStatus::ErrorNotImplemented; | 88 | return ResultStatus::ErrorNotImplemented; |
| 83 | } | 89 | } |
| 84 | 90 | ||
| @@ -87,22 +93,25 @@ public: | |||
| 87 | * @param buffer Reference to buffer to store data | 93 | * @param buffer Reference to buffer to store data |
| 88 | * @return ResultStatus result of function | 94 | * @return ResultStatus result of function |
| 89 | */ | 95 | */ |
| 90 | virtual ResultStatus ReadLogo(std::vector<u8>& buffer) const { | 96 | virtual ResultStatus ReadLogo(std::vector<u8>& buffer) { |
| 91 | return ResultStatus::ErrorNotImplemented; | 97 | return ResultStatus::ErrorNotImplemented; |
| 92 | } | 98 | } |
| 93 | 99 | ||
| 94 | /** | 100 | /** |
| 95 | * Get the RomFS of the application | 101 | * Get the RomFS of the application |
| 96 | * @param buffer Reference to buffer to store data | 102 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer |
| 103 | * @param romfs_file The file containing the RomFS | ||
| 104 | * @param offset The offset the romfs begins on | ||
| 105 | * @param size The size of the romfs | ||
| 97 | * @return ResultStatus result of function | 106 | * @return ResultStatus result of function |
| 98 | */ | 107 | */ |
| 99 | virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { | 108 | virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { |
| 100 | return ResultStatus::ErrorNotImplemented; | 109 | return ResultStatus::ErrorNotImplemented; |
| 101 | } | 110 | } |
| 102 | 111 | ||
| 103 | protected: | 112 | protected: |
| 104 | std::unique_ptr<FileUtil::IOFile> file; | 113 | FileUtil::IOFile file; |
| 105 | bool is_loaded = false; | 114 | bool is_loaded = false; |
| 106 | }; | 115 | }; |
| 107 | 116 | ||
| 108 | /** | 117 | /** |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 08993c4fa..094d74100 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstring> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | 8 | ||
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| @@ -10,7 +11,7 @@ | |||
| 10 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 11 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 12 | 13 | ||
| 13 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/process.h" |
| 14 | #include "core/hle/kernel/resource_limit.h" | 15 | #include "core/hle/kernel/resource_limit.h" |
| 15 | #include "core/loader/ncch.h" | 16 | #include "core/loader/ncch.h" |
| 16 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| @@ -116,7 +117,10 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { | |||
| 116 | return FileType::Error; | 117 | return FileType::Error; |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 119 | ResultStatus AppLoader_NCCH::LoadExec() const { | 120 | ResultStatus AppLoader_NCCH::LoadExec() { |
| 121 | using Kernel::SharedPtr; | ||
| 122 | using Kernel::CodeSet; | ||
| 123 | |||
| 120 | if (!is_loaded) | 124 | if (!is_loaded) |
| 121 | return ResultStatus::ErrorNotLoaded; | 125 | return ResultStatus::ErrorNotLoaded; |
| 122 | 126 | ||
| @@ -125,7 +129,30 @@ ResultStatus AppLoader_NCCH::LoadExec() const { | |||
| 125 | std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( | 129 | std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( |
| 126 | (const char*)exheader_header.codeset_info.name, 8); | 130 | (const char*)exheader_header.codeset_info.name, 8); |
| 127 | u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]); | 131 | u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]); |
| 128 | Kernel::g_current_process = Kernel::Process::Create(process_name, program_id); | 132 | |
| 133 | SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id); | ||
| 134 | |||
| 135 | codeset->code.offset = 0; | ||
| 136 | codeset->code.addr = exheader_header.codeset_info.text.address; | ||
| 137 | codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; | ||
| 138 | |||
| 139 | codeset->rodata.offset = codeset->code.offset + codeset->code.size; | ||
| 140 | codeset->rodata.addr = exheader_header.codeset_info.ro.address; | ||
| 141 | codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; | ||
| 142 | |||
| 143 | // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just | ||
| 144 | // to the regular size. Playing it safe for now. | ||
| 145 | u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; | ||
| 146 | code.resize(code.size() + bss_page_size, 0); | ||
| 147 | |||
| 148 | codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; | ||
| 149 | codeset->data.addr = exheader_header.codeset_info.data.address; | ||
| 150 | codeset->data.size = exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; | ||
| 151 | |||
| 152 | codeset->entrypoint = codeset->code.addr; | ||
| 153 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); | ||
| 154 | |||
| 155 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); | ||
| 129 | 156 | ||
| 130 | // Attach a resource limit to the process based on the resource limit category | 157 | // Attach a resource limit to the process based on the resource limit category |
| 131 | Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory( | 158 | Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory( |
| @@ -136,18 +163,16 @@ ResultStatus AppLoader_NCCH::LoadExec() const { | |||
| 136 | std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); | 163 | std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); |
| 137 | Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); | 164 | Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); |
| 138 | 165 | ||
| 139 | Memory::WriteBlock(entry_point, &code[0], code.size()); | ||
| 140 | |||
| 141 | s32 priority = exheader_header.arm11_system_local_caps.priority; | 166 | s32 priority = exheader_header.arm11_system_local_caps.priority; |
| 142 | u32 stack_size = exheader_header.codeset_info.stack_size; | 167 | u32 stack_size = exheader_header.codeset_info.stack_size; |
| 143 | Kernel::g_current_process->Run(entry_point, priority, stack_size); | 168 | Kernel::g_current_process->Run(priority, stack_size); |
| 144 | return ResultStatus::Success; | 169 | return ResultStatus::Success; |
| 145 | } | 170 | } |
| 146 | return ResultStatus::Error; | 171 | return ResultStatus::Error; |
| 147 | } | 172 | } |
| 148 | 173 | ||
| 149 | ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const { | 174 | ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) { |
| 150 | if (!file->IsOpen()) | 175 | if (!file.IsOpen()) |
| 151 | return ResultStatus::Error; | 176 | return ResultStatus::Error; |
| 152 | 177 | ||
| 153 | LOG_DEBUG(Loader, "%d sections:", kMaxSections); | 178 | LOG_DEBUG(Loader, "%d sections:", kMaxSections); |
| @@ -161,7 +186,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | |||
| 161 | section.offset, section.size, section.name); | 186 | section.offset, section.size, section.name); |
| 162 | 187 | ||
| 163 | s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); | 188 | s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); |
| 164 | file->Seek(section_offset, SEEK_SET); | 189 | file.Seek(section_offset, SEEK_SET); |
| 165 | 190 | ||
| 166 | if (is_compressed) { | 191 | if (is_compressed) { |
| 167 | // Section is compressed, read compressed .code section... | 192 | // Section is compressed, read compressed .code section... |
| @@ -172,7 +197,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | |||
| 172 | return ResultStatus::ErrorMemoryAllocationFailed; | 197 | return ResultStatus::ErrorMemoryAllocationFailed; |
| 173 | } | 198 | } |
| 174 | 199 | ||
| 175 | if (file->ReadBytes(&temp_buffer[0], section.size) != section.size) | 200 | if (file.ReadBytes(&temp_buffer[0], section.size) != section.size) |
| 176 | return ResultStatus::Error; | 201 | return ResultStatus::Error; |
| 177 | 202 | ||
| 178 | // Decompress .code section... | 203 | // Decompress .code section... |
| @@ -183,7 +208,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | |||
| 183 | } else { | 208 | } else { |
| 184 | // Section is uncompressed... | 209 | // Section is uncompressed... |
| 185 | buffer.resize(section.size); | 210 | buffer.resize(section.size); |
| 186 | if (file->ReadBytes(&buffer[0], section.size) != section.size) | 211 | if (file.ReadBytes(&buffer[0], section.size) != section.size) |
| 187 | return ResultStatus::Error; | 212 | return ResultStatus::Error; |
| 188 | } | 213 | } |
| 189 | return ResultStatus::Success; | 214 | return ResultStatus::Success; |
| @@ -196,21 +221,21 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 196 | if (is_loaded) | 221 | if (is_loaded) |
| 197 | return ResultStatus::ErrorAlreadyLoaded; | 222 | return ResultStatus::ErrorAlreadyLoaded; |
| 198 | 223 | ||
| 199 | if (!file->IsOpen()) | 224 | if (!file.IsOpen()) |
| 200 | return ResultStatus::Error; | 225 | return ResultStatus::Error; |
| 201 | 226 | ||
| 202 | // Reset read pointer in case this file has been read before. | 227 | // Reset read pointer in case this file has been read before. |
| 203 | file->Seek(0, SEEK_SET); | 228 | file.Seek(0, SEEK_SET); |
| 204 | 229 | ||
| 205 | if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) | 230 | if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) |
| 206 | return ResultStatus::Error; | 231 | return ResultStatus::Error; |
| 207 | 232 | ||
| 208 | // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... | 233 | // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... |
| 209 | if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { | 234 | if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { |
| 210 | LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); | 235 | LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); |
| 211 | ncch_offset = 0x4000; | 236 | ncch_offset = 0x4000; |
| 212 | file->Seek(ncch_offset, SEEK_SET); | 237 | file.Seek(ncch_offset, SEEK_SET); |
| 213 | file->ReadBytes(&ncch_header, sizeof(NCCH_Header)); | 238 | file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); |
| 214 | } | 239 | } |
| 215 | 240 | ||
| 216 | // Verify we are loading the correct file type... | 241 | // Verify we are loading the correct file type... |
| @@ -219,7 +244,7 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 219 | 244 | ||
| 220 | // Read ExHeader... | 245 | // Read ExHeader... |
| 221 | 246 | ||
| 222 | if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) | 247 | if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) |
| 223 | return ResultStatus::Error; | 248 | return ResultStatus::Error; |
| 224 | 249 | ||
| 225 | is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; | 250 | is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; |
| @@ -239,7 +264,6 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 239 | LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size); | 264 | LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size); |
| 240 | LOG_DEBUG(Loader, "Core version: %d" , core_version); | 265 | LOG_DEBUG(Loader, "Core version: %d" , core_version); |
| 241 | LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority); | 266 | LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority); |
| 242 | LOG_DEBUG(Loader, "Resource limit descriptor: 0x%08X", exheader_header.arm11_system_local_caps.resource_limit_descriptor); | ||
| 243 | LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category); | 267 | LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category); |
| 244 | 268 | ||
| 245 | // Read ExeFS... | 269 | // Read ExeFS... |
| @@ -250,8 +274,8 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 250 | LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); | 274 | LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); |
| 251 | LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); | 275 | LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); |
| 252 | 276 | ||
| 253 | file->Seek(exefs_offset + ncch_offset, SEEK_SET); | 277 | file.Seek(exefs_offset + ncch_offset, SEEK_SET); |
| 254 | if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) | 278 | if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) |
| 255 | return ResultStatus::Error; | 279 | return ResultStatus::Error; |
| 256 | 280 | ||
| 257 | is_loaded = true; // Set state to loaded | 281 | is_loaded = true; // Set state to loaded |
| @@ -259,24 +283,24 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 259 | return LoadExec(); // Load the executable into memory for booting | 283 | return LoadExec(); // Load the executable into memory for booting |
| 260 | } | 284 | } |
| 261 | 285 | ||
| 262 | ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const { | 286 | ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { |
| 263 | return LoadSectionExeFS(".code", buffer); | 287 | return LoadSectionExeFS(".code", buffer); |
| 264 | } | 288 | } |
| 265 | 289 | ||
| 266 | ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const { | 290 | ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { |
| 267 | return LoadSectionExeFS("icon", buffer); | 291 | return LoadSectionExeFS("icon", buffer); |
| 268 | } | 292 | } |
| 269 | 293 | ||
| 270 | ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const { | 294 | ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { |
| 271 | return LoadSectionExeFS("banner", buffer); | 295 | return LoadSectionExeFS("banner", buffer); |
| 272 | } | 296 | } |
| 273 | 297 | ||
| 274 | ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const { | 298 | ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { |
| 275 | return LoadSectionExeFS("logo", buffer); | 299 | return LoadSectionExeFS("logo", buffer); |
| 276 | } | 300 | } |
| 277 | 301 | ||
| 278 | ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | 302 | ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { |
| 279 | if (!file->IsOpen()) | 303 | if (!file.IsOpen()) |
| 280 | return ResultStatus::Error; | 304 | return ResultStatus::Error; |
| 281 | 305 | ||
| 282 | // Check if the NCCH has a RomFS... | 306 | // Check if the NCCH has a RomFS... |
| @@ -287,12 +311,17 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | |||
| 287 | LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); | 311 | LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); |
| 288 | LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); | 312 | LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); |
| 289 | 313 | ||
| 290 | buffer.resize(romfs_size); | 314 | if (file.GetSize () < romfs_offset + romfs_size) |
| 315 | return ResultStatus::Error; | ||
| 291 | 316 | ||
| 292 | file->Seek(romfs_offset, SEEK_SET); | 317 | // We reopen the file, to allow its position to be independent from file's |
| 293 | if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size) | 318 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); |
| 319 | if (!romfs_file->IsOpen()) | ||
| 294 | return ResultStatus::Error; | 320 | return ResultStatus::Error; |
| 295 | 321 | ||
| 322 | offset = romfs_offset; | ||
| 323 | size = romfs_size; | ||
| 324 | |||
| 296 | return ResultStatus::Success; | 325 | return ResultStatus::Success; |
| 297 | } | 326 | } |
| 298 | LOG_DEBUG(Loader, "NCCH has no RomFS"); | 327 | LOG_DEBUG(Loader, "NCCH has no RomFS"); |
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 29e39d2c0..b4374a476 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h | |||
| @@ -163,7 +163,8 @@ namespace Loader { | |||
| 163 | /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | 163 | /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) |
| 164 | class AppLoader_NCCH final : public AppLoader { | 164 | class AppLoader_NCCH final : public AppLoader { |
| 165 | public: | 165 | public: |
| 166 | AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } | 166 | AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) |
| 167 | : AppLoader(std::move(file)), filepath(filepath) { } | ||
| 167 | 168 | ||
| 168 | /** | 169 | /** |
| 169 | * Returns the type of the file | 170 | * Returns the type of the file |
| @@ -183,35 +184,35 @@ public: | |||
| 183 | * @param buffer Reference to buffer to store data | 184 | * @param buffer Reference to buffer to store data |
| 184 | * @return ResultStatus result of function | 185 | * @return ResultStatus result of function |
| 185 | */ | 186 | */ |
| 186 | ResultStatus ReadCode(std::vector<u8>& buffer) const override; | 187 | ResultStatus ReadCode(std::vector<u8>& buffer) override; |
| 187 | 188 | ||
| 188 | /** | 189 | /** |
| 189 | * Get the icon (typically icon section) of the application | 190 | * Get the icon (typically icon section) of the application |
| 190 | * @param buffer Reference to buffer to store data | 191 | * @param buffer Reference to buffer to store data |
| 191 | * @return ResultStatus result of function | 192 | * @return ResultStatus result of function |
| 192 | */ | 193 | */ |
| 193 | ResultStatus ReadIcon(std::vector<u8>& buffer) const override; | 194 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 194 | 195 | ||
| 195 | /** | 196 | /** |
| 196 | * Get the banner (typically banner section) of the application | 197 | * Get the banner (typically banner section) of the application |
| 197 | * @param buffer Reference to buffer to store data | 198 | * @param buffer Reference to buffer to store data |
| 198 | * @return ResultStatus result of function | 199 | * @return ResultStatus result of function |
| 199 | */ | 200 | */ |
| 200 | ResultStatus ReadBanner(std::vector<u8>& buffer) const override; | 201 | ResultStatus ReadBanner(std::vector<u8>& buffer) override; |
| 201 | 202 | ||
| 202 | /** | 203 | /** |
| 203 | * Get the logo (typically logo section) of the application | 204 | * Get the logo (typically logo section) of the application |
| 204 | * @param buffer Reference to buffer to store data | 205 | * @param buffer Reference to buffer to store data |
| 205 | * @return ResultStatus result of function | 206 | * @return ResultStatus result of function |
| 206 | */ | 207 | */ |
| 207 | ResultStatus ReadLogo(std::vector<u8>& buffer) const override; | 208 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; |
| 208 | 209 | ||
| 209 | /** | 210 | /** |
| 210 | * Get the RomFS of the application | 211 | * Get the RomFS of the application |
| 211 | * @param buffer Reference to buffer to store data | 212 | * @param buffer Reference to buffer to store data |
| 212 | * @return ResultStatus result of function | 213 | * @return ResultStatus result of function |
| 213 | */ | 214 | */ |
| 214 | ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; | 215 | ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) override; |
| 215 | 216 | ||
| 216 | private: | 217 | private: |
| 217 | 218 | ||
| @@ -221,13 +222,13 @@ private: | |||
| 221 | * @param buffer Vector to read data into | 222 | * @param buffer Vector to read data into |
| 222 | * @return ResultStatus result of function | 223 | * @return ResultStatus result of function |
| 223 | */ | 224 | */ |
| 224 | ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const; | 225 | ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer); |
| 225 | 226 | ||
| 226 | /** | 227 | /** |
| 227 | * Loads .code section into memory for booting | 228 | * Loads .code section into memory for booting |
| 228 | * @return ResultStatus result of function | 229 | * @return ResultStatus result of function |
| 229 | */ | 230 | */ |
| 230 | ResultStatus LoadExec() const; | 231 | ResultStatus LoadExec(); |
| 231 | 232 | ||
| 232 | bool is_compressed = false; | 233 | bool is_compressed = false; |
| 233 | 234 | ||
| @@ -244,6 +245,8 @@ private: | |||
| 244 | NCCH_Header ncch_header; | 245 | NCCH_Header ncch_header; |
| 245 | ExeFs_Header exefs_header; | 246 | ExeFs_Header exefs_header; |
| 246 | ExHeader_Header exheader_header; | 247 | ExHeader_Header exheader_header; |
| 248 | |||
| 249 | std::string filepath; | ||
| 247 | }; | 250 | }; |
| 248 | 251 | ||
| 249 | } // namespace Loader | 252 | } // namespace Loader |
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index bf814b945..cbe993fbe 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp | |||
| @@ -3,13 +3,14 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | 5 | #include <map> |
| 6 | #include <memory> | ||
| 7 | #include <utility> | ||
| 8 | #include <vector> | ||
| 6 | 9 | ||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 9 | 12 | ||
| 10 | #include "core/hle/config_mem.h" | 13 | #include "core/hle/config_mem.h" |
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/hle/kernel/shared_memory.h" | ||
| 13 | #include "core/hle/kernel/vm_manager.h" | 14 | #include "core/hle/kernel/vm_manager.h" |
| 14 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 15 | #include "core/hle/shared_page.h" | 16 | #include "core/hle/shared_page.h" |
| @@ -31,7 +32,6 @@ struct MemoryArea { | |||
| 31 | 32 | ||
| 32 | // We don't declare the IO regions in here since its handled by other means. | 33 | // We don't declare the IO regions in here since its handled by other means. |
| 33 | static MemoryArea memory_areas[] = { | 34 | static MemoryArea memory_areas[] = { |
| 34 | {PROCESS_IMAGE_VADDR, PROCESS_IMAGE_MAX_SIZE, "Process Image"}, // ExeFS:/.code is loaded here | ||
| 35 | {HEAP_VADDR, HEAP_SIZE, "Heap"}, // Application heap (main memory) | 35 | {HEAP_VADDR, HEAP_SIZE, "Heap"}, // Application heap (main memory) |
| 36 | {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory | 36 | {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory |
| 37 | {LINEAR_HEAP_VADDR, LINEAR_HEAP_SIZE, "Linear Heap"}, // Linear heap (main memory) | 37 | {LINEAR_HEAP_VADDR, LINEAR_HEAP_SIZE, "Linear Heap"}, // Linear heap (main memory) |
| @@ -131,13 +131,13 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { | |||
| 131 | return addr | 0x80000000; | 131 | return addr | 0x80000000; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | // TODO(yuriks): Move this into Process | ||
| 135 | static Kernel::VMManager address_space; | ||
| 136 | |||
| 137 | void Init() { | 134 | void Init() { |
| 138 | using namespace Kernel; | ||
| 139 | |||
| 140 | InitMemoryMap(); | 135 | InitMemoryMap(); |
| 136 | LOG_DEBUG(HW_Memory, "initialized OK"); | ||
| 137 | } | ||
| 138 | |||
| 139 | void InitLegacyAddressSpace(Kernel::VMManager& address_space) { | ||
| 140 | using namespace Kernel; | ||
| 141 | 141 | ||
| 142 | for (MemoryArea& area : memory_areas) { | 142 | for (MemoryArea& area : memory_areas) { |
| 143 | auto block = std::make_shared<std::vector<u8>>(area.size); | 143 | auto block = std::make_shared<std::vector<u8>>(area.size); |
| @@ -151,14 +151,11 @@ void Init() { | |||
| 151 | auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, | 151 | auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, |
| 152 | (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); | 152 | (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); |
| 153 | address_space.Reprotect(shared_page_vma, VMAPermission::Read); | 153 | address_space.Reprotect(shared_page_vma, VMAPermission::Read); |
| 154 | |||
| 155 | LOG_DEBUG(HW_Memory, "initialized OK"); | ||
| 156 | } | 154 | } |
| 157 | 155 | ||
| 158 | void Shutdown() { | 156 | void Shutdown() { |
| 159 | heap_map.clear(); | 157 | heap_map.clear(); |
| 160 | heap_linear_map.clear(); | 158 | heap_linear_map.clear(); |
| 161 | address_space.Reset(); | ||
| 162 | 159 | ||
| 163 | LOG_DEBUG(HW_Memory, "shutdown OK"); | 160 | LOG_DEBUG(HW_Memory, "shutdown OK"); |
| 164 | } | 161 | } |
diff --git a/src/core/mem_map.h b/src/core/mem_map.h index ba50914a8..229ef82c5 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h | |||
| @@ -6,9 +6,14 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | namespace Kernel { | ||
| 10 | class VMManager; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Memory { | 13 | namespace Memory { |
| 10 | 14 | ||
| 11 | void Init(); | 15 | void Init(); |
| 16 | void InitLegacyAddressSpace(Kernel::VMManager& address_space); | ||
| 12 | void Shutdown(); | 17 | void Shutdown(); |
| 13 | 18 | ||
| 14 | /** | 19 | /** |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 28844a915..1f66bb27d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -9,9 +9,6 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | 11 | ||
| 12 | #include "core/hle/config_mem.h" | ||
| 13 | #include "core/hle/shared_page.h" | ||
| 14 | #include "core/hw/hw.h" | ||
| 15 | #include "core/mem_map.h" | 12 | #include "core/mem_map.h" |
| 16 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 17 | #include "core/memory_setup.h" | 14 | #include "core/memory_setup.h" |
| @@ -62,14 +59,12 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | |||
| 62 | while (base != end) { | 59 | while (base != end) { |
| 63 | ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); | 60 | ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); |
| 64 | 61 | ||
| 65 | if (current_page_table->attributes[base] != PageType::Unmapped && type != PageType::Unmapped) { | ||
| 66 | LOG_ERROR(HW_Memory, "overlapping memory ranges at %08X", base * PAGE_SIZE); | ||
| 67 | } | ||
| 68 | current_page_table->attributes[base] = type; | 62 | current_page_table->attributes[base] = type; |
| 69 | current_page_table->pointers[base] = memory; | 63 | current_page_table->pointers[base] = memory; |
| 70 | 64 | ||
| 71 | base += 1; | 65 | base += 1; |
| 72 | memory += PAGE_SIZE; | 66 | if (memory != nullptr) |
| 67 | memory += PAGE_SIZE; | ||
| 73 | } | 68 | } |
| 74 | } | 69 | } |
| 75 | 70 | ||
diff --git a/src/core/memory.h b/src/core/memory.h index 0b8ff9ec4..418609de0 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 9 | namespace Memory { | 11 | namespace Memory { |
diff --git a/src/core/tracer/citrace.h b/src/core/tracer/citrace.h new file mode 100644 index 000000000..5deb6ce9e --- /dev/null +++ b/src/core/tracer/citrace.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstdint> | ||
| 8 | |||
| 9 | namespace CiTrace { | ||
| 10 | |||
| 11 | // NOTE: Things are stored in little-endian | ||
| 12 | |||
| 13 | #pragma pack(1) | ||
| 14 | |||
| 15 | struct CTHeader { | ||
| 16 | static const char* ExpectedMagicWord() { | ||
| 17 | return "CiTr"; | ||
| 18 | } | ||
| 19 | |||
| 20 | static uint32_t ExpectedVersion() { | ||
| 21 | return 1; | ||
| 22 | } | ||
| 23 | |||
| 24 | char magic[4]; | ||
| 25 | uint32_t version; | ||
| 26 | uint32_t header_size; | ||
| 27 | |||
| 28 | struct { | ||
| 29 | // NOTE: Register range sizes are technically hardware-constants, but the actual limits | ||
| 30 | // aren't known. Hence we store the presumed limits along the offsets. | ||
| 31 | // Sizes are given in uint32_t units. | ||
| 32 | uint32_t gpu_registers; | ||
| 33 | uint32_t gpu_registers_size; | ||
| 34 | uint32_t lcd_registers; | ||
| 35 | uint32_t lcd_registers_size; | ||
| 36 | uint32_t pica_registers; | ||
| 37 | uint32_t pica_registers_size; | ||
| 38 | uint32_t default_attributes; | ||
| 39 | uint32_t default_attributes_size; | ||
| 40 | uint32_t vs_program_binary; | ||
| 41 | uint32_t vs_program_binary_size; | ||
| 42 | uint32_t vs_swizzle_data; | ||
| 43 | uint32_t vs_swizzle_data_size; | ||
| 44 | uint32_t vs_float_uniforms; | ||
| 45 | uint32_t vs_float_uniforms_size; | ||
| 46 | uint32_t gs_program_binary; | ||
| 47 | uint32_t gs_program_binary_size; | ||
| 48 | uint32_t gs_swizzle_data; | ||
| 49 | uint32_t gs_swizzle_data_size; | ||
| 50 | uint32_t gs_float_uniforms; | ||
| 51 | uint32_t gs_float_uniforms_size; | ||
| 52 | |||
| 53 | // Other things we might want to store here: | ||
| 54 | // - Initial framebuffer data, maybe even a full copy of FCRAM/VRAM | ||
| 55 | // - Lookup tables for fragment lighting | ||
| 56 | // - Lookup tables for procedural textures | ||
| 57 | } initial_state_offsets; | ||
| 58 | |||
| 59 | uint32_t stream_offset; | ||
| 60 | uint32_t stream_size; | ||
| 61 | }; | ||
| 62 | |||
| 63 | enum CTStreamElementType : uint32_t { | ||
| 64 | FrameMarker = 0xE1, | ||
| 65 | MemoryLoad = 0xE2, | ||
| 66 | RegisterWrite = 0xE3, | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct CTMemoryLoad { | ||
| 70 | uint32_t file_offset; | ||
| 71 | uint32_t size; | ||
| 72 | uint32_t physical_address; | ||
| 73 | uint32_t pad; | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct CTRegisterWrite { | ||
| 77 | uint32_t physical_address; | ||
| 78 | |||
| 79 | enum : uint32_t { | ||
| 80 | SIZE_8 = 0xD1, | ||
| 81 | SIZE_16 = 0xD2, | ||
| 82 | SIZE_32 = 0xD3, | ||
| 83 | SIZE_64 = 0xD4 | ||
| 84 | } size; | ||
| 85 | |||
| 86 | // TODO: Make it clearer which bits of this member are used for sizes other than 32 bits | ||
| 87 | uint64_t value; | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct CTStreamElement { | ||
| 91 | CTStreamElementType type; | ||
| 92 | |||
| 93 | union { | ||
| 94 | CTMemoryLoad memory_load; | ||
| 95 | CTRegisterWrite register_write; | ||
| 96 | }; | ||
| 97 | }; | ||
| 98 | |||
| 99 | #pragma pack() | ||
| 100 | |||
| 101 | } | ||
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp new file mode 100644 index 000000000..656706c0c --- /dev/null +++ b/src/core/tracer/recorder.cpp | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | #include "common/logging/log.h" | ||
| 10 | |||
| 11 | #include "recorder.h" | ||
| 12 | |||
| 13 | namespace CiTrace { | ||
| 14 | |||
| 15 | Recorder::Recorder(const InitialState& initial_state) : initial_state(initial_state) { | ||
| 16 | |||
| 17 | } | ||
| 18 | |||
| 19 | void Recorder::Finish(const std::string& filename) { | ||
| 20 | // Setup CiTrace header | ||
| 21 | CTHeader header; | ||
| 22 | std::memcpy(header.magic, CTHeader::ExpectedMagicWord(), 4); | ||
| 23 | header.version = CTHeader::ExpectedVersion(); | ||
| 24 | header.header_size = sizeof(CTHeader); | ||
| 25 | |||
| 26 | // Calculate file offsets | ||
| 27 | auto& initial = header.initial_state_offsets; | ||
| 28 | |||
| 29 | initial.gpu_registers_size = initial_state.gpu_registers.size(); | ||
| 30 | initial.lcd_registers_size = initial_state.lcd_registers.size(); | ||
| 31 | initial.pica_registers_size = initial_state.pica_registers.size(); | ||
| 32 | initial.default_attributes_size = initial_state.default_attributes.size(); | ||
| 33 | initial.vs_program_binary_size = initial_state.vs_program_binary.size(); | ||
| 34 | initial.vs_swizzle_data_size = initial_state.vs_swizzle_data.size(); | ||
| 35 | initial.vs_float_uniforms_size = initial_state.vs_float_uniforms.size(); | ||
| 36 | initial.gs_program_binary_size = initial_state.gs_program_binary.size(); | ||
| 37 | initial.gs_swizzle_data_size = initial_state.gs_swizzle_data.size(); | ||
| 38 | initial.gs_float_uniforms_size = initial_state.gs_float_uniforms.size(); | ||
| 39 | header.stream_size = stream.size(); | ||
| 40 | |||
| 41 | initial.gpu_registers = sizeof(header); | ||
| 42 | initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32); | ||
| 43 | initial.pica_registers = initial.lcd_registers + initial.lcd_registers_size * sizeof(u32);; | ||
| 44 | initial.default_attributes = initial.pica_registers + initial.pica_registers_size * sizeof(u32); | ||
| 45 | initial.vs_program_binary = initial.default_attributes + initial.default_attributes_size * sizeof(u32); | ||
| 46 | initial.vs_swizzle_data = initial.vs_program_binary + initial.vs_program_binary_size * sizeof(u32); | ||
| 47 | initial.vs_float_uniforms = initial.vs_swizzle_data + initial.vs_swizzle_data_size * sizeof(u32); | ||
| 48 | initial.gs_program_binary = initial.vs_float_uniforms + initial.vs_float_uniforms_size * sizeof(u32); | ||
| 49 | initial.gs_swizzle_data = initial.gs_program_binary + initial.gs_program_binary_size * sizeof(u32); | ||
| 50 | initial.gs_float_uniforms = initial.gs_swizzle_data + initial.gs_swizzle_data_size * sizeof(u32); | ||
| 51 | header.stream_offset = initial.gs_float_uniforms + initial.gs_float_uniforms_size * sizeof(u32); | ||
| 52 | |||
| 53 | // Iterate through stream elements, update relevant stream element data | ||
| 54 | for (auto& stream_element : stream) { | ||
| 55 | switch (stream_element.data.type) { | ||
| 56 | case MemoryLoad: | ||
| 57 | { | ||
| 58 | auto& file_offset = memory_regions[stream_element.hash]; | ||
| 59 | if (!stream_element.uses_existing_data) { | ||
| 60 | file_offset = header.stream_offset; | ||
| 61 | } | ||
| 62 | stream_element.data.memory_load.file_offset = file_offset; | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | |||
| 66 | default: | ||
| 67 | // Other commands don't use any extra data | ||
| 68 | DEBUG_ASSERT(stream_element.extra_data.size() == 0); | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | header.stream_offset += stream_element.extra_data.size(); | ||
| 72 | } | ||
| 73 | |||
| 74 | try { | ||
| 75 | // Open file and write header | ||
| 76 | FileUtil::IOFile file(filename, "wb"); | ||
| 77 | size_t written = file.WriteObject(header); | ||
| 78 | if (written != 1 || file.Tell() != initial.gpu_registers) | ||
| 79 | throw "Failed to write header"; | ||
| 80 | |||
| 81 | // Write initial state | ||
| 82 | written = file.WriteArray(initial_state.gpu_registers.data(), initial_state.gpu_registers.size()); | ||
| 83 | if (written != initial_state.gpu_registers.size() || file.Tell() != initial.lcd_registers) | ||
| 84 | throw "Failed to write GPU registers"; | ||
| 85 | |||
| 86 | written = file.WriteArray(initial_state.lcd_registers.data(), initial_state.lcd_registers.size()); | ||
| 87 | if (written != initial_state.lcd_registers.size() || file.Tell() != initial.pica_registers) | ||
| 88 | throw "Failed to write LCD registers"; | ||
| 89 | |||
| 90 | written = file.WriteArray(initial_state.pica_registers.data(), initial_state.pica_registers.size()); | ||
| 91 | if (written != initial_state.pica_registers.size() || file.Tell() != initial.default_attributes) | ||
| 92 | throw "Failed to write Pica registers"; | ||
| 93 | |||
| 94 | written = file.WriteArray(initial_state.default_attributes.data(), initial_state.default_attributes.size()); | ||
| 95 | if (written != initial_state.default_attributes.size() || file.Tell() != initial.vs_program_binary) | ||
| 96 | throw "Failed to write default vertex attributes"; | ||
| 97 | |||
| 98 | written = file.WriteArray(initial_state.vs_program_binary.data(), initial_state.vs_program_binary.size()); | ||
| 99 | if (written != initial_state.vs_program_binary.size() || file.Tell() != initial.vs_swizzle_data) | ||
| 100 | throw "Failed to write vertex shader program binary"; | ||
| 101 | |||
| 102 | written = file.WriteArray(initial_state.vs_swizzle_data.data(), initial_state.vs_swizzle_data.size()); | ||
| 103 | if (written != initial_state.vs_swizzle_data.size() || file.Tell() != initial.vs_float_uniforms) | ||
| 104 | throw "Failed to write vertex shader swizzle data"; | ||
| 105 | |||
| 106 | written = file.WriteArray(initial_state.vs_float_uniforms.data(), initial_state.vs_float_uniforms.size()); | ||
| 107 | if (written != initial_state.vs_float_uniforms.size() || file.Tell() != initial.gs_program_binary) | ||
| 108 | throw "Failed to write vertex shader float uniforms"; | ||
| 109 | |||
| 110 | written = file.WriteArray(initial_state.gs_program_binary.data(), initial_state.gs_program_binary.size()); | ||
| 111 | if (written != initial_state.gs_program_binary.size() || file.Tell() != initial.gs_swizzle_data) | ||
| 112 | throw "Failed to write geomtry shader program binary"; | ||
| 113 | |||
| 114 | written = file.WriteArray(initial_state.gs_swizzle_data.data(), initial_state.gs_swizzle_data.size()); | ||
| 115 | if (written != initial_state.gs_swizzle_data.size() || file.Tell() != initial.gs_float_uniforms) | ||
| 116 | throw "Failed to write geometry shader swizzle data"; | ||
| 117 | |||
| 118 | written = file.WriteArray(initial_state.gs_float_uniforms.data(), initial_state.gs_float_uniforms.size()); | ||
| 119 | if (written != initial_state.gs_float_uniforms.size() || file.Tell() != initial.gs_float_uniforms + sizeof(u32) * initial.gs_float_uniforms_size) | ||
| 120 | throw "Failed to write geometry shader float uniforms"; | ||
| 121 | |||
| 122 | // Iterate through stream elements, write "extra data" | ||
| 123 | for (const auto& stream_element : stream) { | ||
| 124 | if (stream_element.extra_data.size() == 0) | ||
| 125 | continue; | ||
| 126 | |||
| 127 | written = file.WriteBytes(stream_element.extra_data.data(), stream_element.extra_data.size()); | ||
| 128 | if (written != stream_element.extra_data.size()) | ||
| 129 | throw "Failed to write extra data"; | ||
| 130 | } | ||
| 131 | |||
| 132 | if (file.Tell() != header.stream_offset) | ||
| 133 | throw "Unexpected end of extra data"; | ||
| 134 | |||
| 135 | // Write actual stream elements | ||
| 136 | for (const auto& stream_element : stream) { | ||
| 137 | if (1 != file.WriteObject(stream_element.data)) | ||
| 138 | throw "Failed to write stream element"; | ||
| 139 | } | ||
| 140 | } catch(const char* str) { | ||
| 141 | LOG_ERROR(HW_GPU, "Writing CiTrace file failed: %s", str); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | void Recorder::FrameFinished() { | ||
| 146 | stream.push_back( { FrameMarker } ); | ||
| 147 | } | ||
| 148 | |||
| 149 | void Recorder::MemoryAccessed(const u8* data, u32 size, u32 physical_address) { | ||
| 150 | StreamElement element = { MemoryLoad }; | ||
| 151 | element.data.memory_load.size = size; | ||
| 152 | element.data.memory_load.physical_address = physical_address; | ||
| 153 | |||
| 154 | // Compute hash over given memory region to check if the contents are already stored internally | ||
| 155 | boost::crc_32_type result; | ||
| 156 | result.process_bytes(data, size); | ||
| 157 | element.hash = result.checksum(); | ||
| 158 | |||
| 159 | element.uses_existing_data = (memory_regions.find(element.hash) != memory_regions.end()); | ||
| 160 | if (!element.uses_existing_data) { | ||
| 161 | element.extra_data.resize(size); | ||
| 162 | memcpy(element.extra_data.data(), data, size); | ||
| 163 | memory_regions.insert({element.hash, 0}); // file offset will be initialized in Finish() | ||
| 164 | } | ||
| 165 | |||
| 166 | stream.push_back(element); | ||
| 167 | } | ||
| 168 | |||
| 169 | template<typename T> | ||
| 170 | void Recorder::RegisterWritten(u32 physical_address, T value) { | ||
| 171 | StreamElement element = { RegisterWrite }; | ||
| 172 | element.data.register_write.size = (sizeof(T) == 1) ? CTRegisterWrite::SIZE_8 | ||
| 173 | : (sizeof(T) == 2) ? CTRegisterWrite::SIZE_16 | ||
| 174 | : (sizeof(T) == 4) ? CTRegisterWrite::SIZE_32 | ||
| 175 | : CTRegisterWrite::SIZE_64; | ||
| 176 | element.data.register_write.physical_address = physical_address; | ||
| 177 | element.data.register_write.value = value; | ||
| 178 | |||
| 179 | stream.push_back(element); | ||
| 180 | } | ||
| 181 | |||
| 182 | template void Recorder::RegisterWritten(u32,u8); | ||
| 183 | template void Recorder::RegisterWritten(u32,u16); | ||
| 184 | template void Recorder::RegisterWritten(u32,u32); | ||
| 185 | template void Recorder::RegisterWritten(u32,u64); | ||
| 186 | |||
| 187 | } | ||
diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h new file mode 100644 index 000000000..6e4b70015 --- /dev/null +++ b/src/core/tracer/recorder.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <unordered_map> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include <boost/crc.hpp> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | #include "citrace.h" | ||
| 15 | |||
| 16 | namespace CiTrace { | ||
| 17 | |||
| 18 | class Recorder { | ||
| 19 | public: | ||
| 20 | struct InitialState { | ||
| 21 | std::vector<u32> gpu_registers; | ||
| 22 | std::vector<u32> lcd_registers; | ||
| 23 | std::vector<u32> pica_registers; | ||
| 24 | std::vector<u32> default_attributes; | ||
| 25 | std::vector<u32> vs_program_binary; | ||
| 26 | std::vector<u32> vs_swizzle_data; | ||
| 27 | std::vector<u32> vs_float_uniforms; | ||
| 28 | std::vector<u32> gs_program_binary; | ||
| 29 | std::vector<u32> gs_swizzle_data; | ||
| 30 | std::vector<u32> gs_float_uniforms; | ||
| 31 | }; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Recorder constructor | ||
| 35 | * @param default_attributes Pointer to an array of 32-bit-aligned 24-bit floating point values. | ||
| 36 | * @param vs_float_uniforms Pointer to an array of 32-bit-aligned 24-bit floating point values. | ||
| 37 | */ | ||
| 38 | Recorder(const InitialState& initial_state); | ||
| 39 | |||
| 40 | /// Finish recording of this Citrace and save it using the given filename. | ||
| 41 | void Finish(const std::string& filename); | ||
| 42 | |||
| 43 | /// Mark end of a frame | ||
| 44 | void FrameFinished(); | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Store a copy of the given memory range in the recording. | ||
| 48 | * @note Use this whenever the GPU is about to access a particular memory region. | ||
| 49 | * @note The implementation will make sure to minimize redundant memory updates. | ||
| 50 | */ | ||
| 51 | void MemoryAccessed(const u8* data, u32 size, u32 physical_address); | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Record a register write. | ||
| 55 | * @note Use this whenever a GPU-related MMIO register has been written to. | ||
| 56 | */ | ||
| 57 | template<typename T> | ||
| 58 | void RegisterWritten(u32 physical_address, T value); | ||
| 59 | |||
| 60 | private: | ||
| 61 | // Initial state of recording start | ||
| 62 | InitialState initial_state; | ||
| 63 | |||
| 64 | // Command stream | ||
| 65 | struct StreamElement { | ||
| 66 | CTStreamElement data; | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Extra data to store along "core" data. | ||
| 70 | * This is e.g. used for data used in MemoryUpdates. | ||
| 71 | */ | ||
| 72 | std::vector<u8> extra_data; | ||
| 73 | |||
| 74 | /// Optional CRC hash (e.g. for hashing memory regions) | ||
| 75 | boost::crc_32_type::value_type hash; | ||
| 76 | |||
| 77 | /// If true, refer to data already written to the output file instead of extra_data | ||
| 78 | bool uses_existing_data; | ||
| 79 | }; | ||
| 80 | |||
| 81 | std::vector<StreamElement> stream; | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Internal cache which maps hashes of memory contents to file offsets at which those memory | ||
| 85 | * contents are stored. | ||
| 86 | */ | ||
| 87 | std::unordered_map<boost::crc_32_type::value_type /*hash*/, u32 /*file_offset*/> memory_regions; | ||
| 88 | }; | ||
| 89 | |||
| 90 | } // namespace | ||
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp index 943f3eb35..558b49d60 100644 --- a/src/video_core/clipper.cpp +++ b/src/video_core/clipper.cpp | |||
| @@ -94,7 +94,7 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { | |||
| 94 | // NOTE: We clip against a w=epsilon plane to guarantee that the output has a positive w value. | 94 | // NOTE: We clip against a w=epsilon plane to guarantee that the output has a positive w value. |
| 95 | // TODO: Not sure if this is a valid approach. Also should probably instead use the smallest | 95 | // TODO: Not sure if this is a valid approach. Also should probably instead use the smallest |
| 96 | // epsilon possible within float24 accuracy. | 96 | // epsilon possible within float24 accuracy. |
| 97 | static const float24 EPSILON = float24::FromFloat32(0.00001); | 97 | static const float24 EPSILON = float24::FromFloat32(0.00001f); |
| 98 | static const float24 f0 = float24::FromFloat32(0.0); | 98 | static const float24 f0 = float24::FromFloat32(0.0); |
| 99 | static const float24 f1 = float24::FromFloat32(1.0); | 99 | static const float24 f1 = float24::FromFloat32(1.0); |
| 100 | static const std::array<ClippingEdge, 7> clipping_edges = {{ | 100 | static const std::array<ClippingEdge, 7> clipping_edges = {{ |
| @@ -153,7 +153,7 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { | |||
| 153 | "Triangle %lu/%lu at position (%.3f, %.3f, %.3f, %.3f), " | 153 | "Triangle %lu/%lu at position (%.3f, %.3f, %.3f, %.3f), " |
| 154 | "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " | 154 | "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " |
| 155 | "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", | 155 | "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", |
| 156 | i, output_list->size(), | 156 | i + 1, output_list->size() - 2, |
| 157 | vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), | 157 | vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), |
| 158 | vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), | 158 | vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), |
| 159 | vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), | 159 | vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index b46fadd9f..43ae06181 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -6,18 +6,20 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/profiler.h" | 7 | #include "common/profiler.h" |
| 8 | 8 | ||
| 9 | #include "core/hle/service/gsp_gpu.h" | ||
| 10 | #include "core/hw/gpu.h" | ||
| 11 | #include "core/settings.h" | ||
| 12 | |||
| 13 | #include "debug_utils/debug_utils.h" | ||
| 14 | |||
| 9 | #include "clipper.h" | 15 | #include "clipper.h" |
| 10 | #include "command_processor.h" | 16 | #include "command_processor.h" |
| 11 | #include "math.h" | 17 | #include "math.h" |
| 12 | #include "pica.h" | 18 | #include "pica.h" |
| 13 | #include "primitive_assembly.h" | 19 | #include "primitive_assembly.h" |
| 20 | #include "renderer_base.h" | ||
| 14 | #include "vertex_shader.h" | 21 | #include "vertex_shader.h" |
| 15 | #include "video_core.h" | 22 | #include "video_core.h" |
| 16 | #include "core/hle/service/gsp_gpu.h" | ||
| 17 | #include "core/hw/gpu.h" | ||
| 18 | #include "core/settings.h" | ||
| 19 | |||
| 20 | #include "debug_utils/debug_utils.h" | ||
| 21 | 23 | ||
| 22 | namespace Pica { | 24 | namespace Pica { |
| 23 | 25 | ||
| @@ -43,12 +45,12 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 43 | if (GPU::g_skip_frame && id != PICA_REG_INDEX(trigger_irq)) | 45 | if (GPU::g_skip_frame && id != PICA_REG_INDEX(trigger_irq)) |
| 44 | return; | 46 | return; |
| 45 | 47 | ||
| 46 | // TODO: Figure out how register masking acts on e.g. vs_uniform_setup.set_value | 48 | // TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value |
| 47 | u32 old_value = regs[id]; | 49 | u32 old_value = regs[id]; |
| 48 | regs[id] = (old_value & ~mask) | (value & mask); | 50 | regs[id] = (old_value & ~mask) | (value & mask); |
| 49 | 51 | ||
| 50 | if (g_debug_context) | 52 | if (g_debug_context) |
| 51 | g_debug_context->OnEvent(DebugContext::Event::CommandLoaded, reinterpret_cast<void*>(&id)); | 53 | g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, reinterpret_cast<void*>(&id)); |
| 52 | 54 | ||
| 53 | DebugUtils::OnPicaRegWrite(id, regs[id]); | 55 | DebugUtils::OnPicaRegWrite(id, regs[id]); |
| 54 | 56 | ||
| @@ -58,10 +60,50 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 58 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); | 60 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); |
| 59 | break; | 61 | break; |
| 60 | 62 | ||
| 63 | // Load default vertex input attributes | ||
| 64 | case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233): | ||
| 65 | case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234): | ||
| 66 | case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235): | ||
| 67 | { | ||
| 68 | // TODO: Does actual hardware indeed keep an intermediate buffer or does | ||
| 69 | // it directly write the values? | ||
| 70 | default_attr_write_buffer[default_attr_counter++] = value; | ||
| 71 | |||
| 72 | // Default attributes are written in a packed format such that four float24 values are encoded in | ||
| 73 | // three 32-bit numbers. We write to internal memory once a full such vector is | ||
| 74 | // written. | ||
| 75 | if (default_attr_counter >= 3) { | ||
| 76 | default_attr_counter = 0; | ||
| 77 | |||
| 78 | auto& setup = regs.vs_default_attributes_setup; | ||
| 79 | |||
| 80 | if (setup.index >= 16) { | ||
| 81 | LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index); | ||
| 82 | break; | ||
| 83 | } | ||
| 84 | |||
| 85 | Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; | ||
| 86 | |||
| 87 | // NOTE: The destination component order indeed is "backwards" | ||
| 88 | attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); | ||
| 89 | attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); | ||
| 90 | attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); | ||
| 91 | attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); | ||
| 92 | |||
| 93 | LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, | ||
| 94 | attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), | ||
| 95 | attribute.w.ToFloat32()); | ||
| 96 | |||
| 97 | // TODO: Verify that this actually modifies the register! | ||
| 98 | setup.index = setup.index + 1; | ||
| 99 | } | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | |||
| 61 | case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c): | 103 | case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c): |
| 62 | case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d): | 104 | case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d): |
| 63 | { | 105 | { |
| 64 | unsigned index = id - PICA_REG_INDEX(command_buffer.trigger[0]); | 106 | unsigned index = static_cast<unsigned>(id - PICA_REG_INDEX(command_buffer.trigger[0])); |
| 65 | u32* head_ptr = (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index)); | 107 | u32* head_ptr = (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index)); |
| 66 | g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr; | 108 | g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr; |
| 67 | g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32); | 109 | g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32); |
| @@ -121,12 +163,55 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 121 | PrimitiveAssembler<VertexShader::OutputVertex> primitive_assembler(regs.triangle_topology.Value()); | 163 | PrimitiveAssembler<VertexShader::OutputVertex> primitive_assembler(regs.triangle_topology.Value()); |
| 122 | PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value()); | 164 | PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value()); |
| 123 | 165 | ||
| 166 | if (g_debug_context) { | ||
| 167 | for (int i = 0; i < 3; ++i) { | ||
| 168 | const auto texture = regs.GetTextures()[i]; | ||
| 169 | if (!texture.enabled) | ||
| 170 | continue; | ||
| 171 | |||
| 172 | u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); | ||
| 173 | if (g_debug_context && Pica::g_debug_context->recorder) | ||
| 174 | g_debug_context->recorder->MemoryAccessed(texture_data, Pica::Regs::NibblesPerPixel(texture.format) * texture.config.width / 2 * texture.config.height, texture.config.GetPhysicalAddress()); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | class { | ||
| 179 | /// Combine overlapping and close ranges | ||
| 180 | void SimplifyRanges() { | ||
| 181 | for (auto it = ranges.begin(); it != ranges.end(); ++it) { | ||
| 182 | // NOTE: We add 32 to the range end address to make sure "close" ranges are combined, too | ||
| 183 | auto it2 = std::next(it); | ||
| 184 | while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) { | ||
| 185 | it->second = std::max(it->second, it2->first + it2->second - it->first); | ||
| 186 | it2 = ranges.erase(it2); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | public: | ||
| 192 | /// Record a particular memory access in the list | ||
| 193 | void AddAccess(u32 paddr, u32 size) { | ||
| 194 | // Create new range or extend existing one | ||
| 195 | ranges[paddr] = std::max(ranges[paddr], size); | ||
| 196 | |||
| 197 | // Simplify ranges... | ||
| 198 | SimplifyRanges(); | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Map of accessed ranges (mapping start address to range size) | ||
| 202 | std::map<u32, u32> ranges; | ||
| 203 | } memory_accesses; | ||
| 204 | |||
| 124 | for (unsigned int index = 0; index < regs.num_vertices; ++index) | 205 | for (unsigned int index = 0; index < regs.num_vertices; ++index) |
| 125 | { | 206 | { |
| 126 | unsigned int vertex = is_indexed ? (index_u16 ? index_address_16[index] : index_address_8[index]) : index; | 207 | unsigned int vertex = is_indexed ? (index_u16 ? index_address_16[index] : index_address_8[index]) : index; |
| 127 | 208 | ||
| 128 | if (is_indexed) { | 209 | if (is_indexed) { |
| 129 | // TODO: Implement some sort of vertex cache! | 210 | // TODO: Implement some sort of vertex cache! |
| 211 | if (g_debug_context && Pica::g_debug_context->recorder) { | ||
| 212 | int size = index_u16 ? 2 : 1; | ||
| 213 | memory_accesses.AddAccess(base_address + index_info.offset + size * index, size); | ||
| 214 | } | ||
| 130 | } | 215 | } |
| 131 | 216 | ||
| 132 | // Initialize data for the current vertex | 217 | // Initialize data for the current vertex |
| @@ -149,7 +234,14 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 149 | 234 | ||
| 150 | // Load per-vertex data from the loader arrays | 235 | // Load per-vertex data from the loader arrays |
| 151 | for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { | 236 | for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { |
| 152 | const u8* srcdata = Memory::GetPhysicalPointer(vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]); | 237 | u32 source_addr = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]; |
| 238 | const u8* srcdata = Memory::GetPhysicalPointer(source_addr); | ||
| 239 | |||
| 240 | if (g_debug_context && Pica::g_debug_context->recorder) { | ||
| 241 | memory_accesses.AddAccess(source_addr, | ||
| 242 | (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT) ? 4 | ||
| 243 | : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1); | ||
| 244 | } | ||
| 153 | 245 | ||
| 154 | const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata : | 246 | const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata : |
| 155 | (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata : | 247 | (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata : |
| @@ -190,7 +282,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 190 | &geometry_dumper, _1, _2, _3)); | 282 | &geometry_dumper, _1, _2, _3)); |
| 191 | 283 | ||
| 192 | // Send to vertex shader | 284 | // Send to vertex shader |
| 193 | VertexShader::OutputVertex output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes()); | 285 | VertexShader::OutputVertex output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes(), g_state.regs.vs, g_state.vs); |
| 194 | 286 | ||
| 195 | if (is_indexed) { | 287 | if (is_indexed) { |
| 196 | // TODO: Add processed vertex to vertex cache! | 288 | // TODO: Add processed vertex to vertex cache! |
| @@ -211,47 +303,53 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 211 | } | 303 | } |
| 212 | } | 304 | } |
| 213 | 305 | ||
| 306 | for (auto& range : memory_accesses.ranges) { | ||
| 307 | g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first), | ||
| 308 | range.second, range.first); | ||
| 309 | } | ||
| 310 | |||
| 214 | if (Settings::values.use_hw_renderer) { | 311 | if (Settings::values.use_hw_renderer) { |
| 215 | VideoCore::g_renderer->hw_rasterizer->DrawTriangles(); | 312 | VideoCore::g_renderer->hw_rasterizer->DrawTriangles(); |
| 216 | } | 313 | } |
| 217 | 314 | ||
| 218 | geometry_dumper.Dump(); | 315 | geometry_dumper.Dump(); |
| 219 | 316 | ||
| 220 | if (g_debug_context) | 317 | if (g_debug_context) { |
| 221 | g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr); | 318 | g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr); |
| 319 | } | ||
| 222 | 320 | ||
| 223 | break; | 321 | break; |
| 224 | } | 322 | } |
| 225 | 323 | ||
| 226 | case PICA_REG_INDEX(vs_bool_uniforms): | 324 | case PICA_REG_INDEX(vs.bool_uniforms): |
| 227 | for (unsigned i = 0; i < 16; ++i) | 325 | for (unsigned i = 0; i < 16; ++i) |
| 228 | g_state.vs.uniforms.b[i] = (regs.vs_bool_uniforms.Value() & (1 << i)) != 0; | 326 | g_state.vs.uniforms.b[i] = (regs.vs.bool_uniforms.Value() & (1 << i)) != 0; |
| 229 | 327 | ||
| 230 | break; | 328 | break; |
| 231 | 329 | ||
| 232 | case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[0], 0x2b1): | 330 | case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1): |
| 233 | case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[1], 0x2b2): | 331 | case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[1], 0x2b2): |
| 234 | case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[2], 0x2b3): | 332 | case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[2], 0x2b3): |
| 235 | case PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[3], 0x2b4): | 333 | case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[3], 0x2b4): |
| 236 | { | 334 | { |
| 237 | int index = (id - PICA_REG_INDEX_WORKAROUND(vs_int_uniforms[0], 0x2b1)); | 335 | int index = (id - PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1)); |
| 238 | auto values = regs.vs_int_uniforms[index]; | 336 | auto values = regs.vs.int_uniforms[index]; |
| 239 | g_state.vs.uniforms.i[index] = Math::Vec4<u8>(values.x, values.y, values.z, values.w); | 337 | g_state.vs.uniforms.i[index] = Math::Vec4<u8>(values.x, values.y, values.z, values.w); |
| 240 | LOG_TRACE(HW_GPU, "Set integer uniform %d to %02x %02x %02x %02x", | 338 | LOG_TRACE(HW_GPU, "Set integer uniform %d to %02x %02x %02x %02x", |
| 241 | index, values.x.Value(), values.y.Value(), values.z.Value(), values.w.Value()); | 339 | index, values.x.Value(), values.y.Value(), values.z.Value(), values.w.Value()); |
| 242 | break; | 340 | break; |
| 243 | } | 341 | } |
| 244 | 342 | ||
| 245 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[0], 0x2c1): | 343 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[0], 0x2c1): |
| 246 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[1], 0x2c2): | 344 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[1], 0x2c2): |
| 247 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[2], 0x2c3): | 345 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[2], 0x2c3): |
| 248 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[3], 0x2c4): | 346 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[3], 0x2c4): |
| 249 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[4], 0x2c5): | 347 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[4], 0x2c5): |
| 250 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[5], 0x2c6): | 348 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[5], 0x2c6): |
| 251 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[6], 0x2c7): | 349 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[6], 0x2c7): |
| 252 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[7], 0x2c8): | 350 | case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[7], 0x2c8): |
| 253 | { | 351 | { |
| 254 | auto& uniform_setup = regs.vs_uniform_setup; | 352 | auto& uniform_setup = regs.vs.uniform_setup; |
| 255 | 353 | ||
| 256 | // TODO: Does actual hardware indeed keep an intermediate buffer or does | 354 | // TODO: Does actual hardware indeed keep an intermediate buffer or does |
| 257 | // it directly write the values? | 355 | // it directly write the values? |
| @@ -293,73 +391,33 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 293 | break; | 391 | break; |
| 294 | } | 392 | } |
| 295 | 393 | ||
| 296 | // Load default vertex input attributes | ||
| 297 | case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233): | ||
| 298 | case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234): | ||
| 299 | case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235): | ||
| 300 | { | ||
| 301 | // TODO: Does actual hardware indeed keep an intermediate buffer or does | ||
| 302 | // it directly write the values? | ||
| 303 | default_attr_write_buffer[default_attr_counter++] = value; | ||
| 304 | |||
| 305 | // Default attributes are written in a packed format such that four float24 values are encoded in | ||
| 306 | // three 32-bit numbers. We write to internal memory once a full such vector is | ||
| 307 | // written. | ||
| 308 | if (default_attr_counter >= 3) { | ||
| 309 | default_attr_counter = 0; | ||
| 310 | |||
| 311 | auto& setup = regs.vs_default_attributes_setup; | ||
| 312 | |||
| 313 | if (setup.index >= 16) { | ||
| 314 | LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index); | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | |||
| 318 | Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; | ||
| 319 | |||
| 320 | // NOTE: The destination component order indeed is "backwards" | ||
| 321 | attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); | ||
| 322 | attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); | ||
| 323 | attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); | ||
| 324 | attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); | ||
| 325 | |||
| 326 | LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, | ||
| 327 | attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), | ||
| 328 | attribute.w.ToFloat32()); | ||
| 329 | |||
| 330 | // TODO: Verify that this actually modifies the register! | ||
| 331 | setup.index = setup.index + 1; | ||
| 332 | } | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | |||
| 336 | // Load shader program code | 394 | // Load shader program code |
| 337 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[0], 0x2cc): | 395 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[0], 0x2cc): |
| 338 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[1], 0x2cd): | 396 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[1], 0x2cd): |
| 339 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[2], 0x2ce): | 397 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[2], 0x2ce): |
| 340 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[3], 0x2cf): | 398 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[3], 0x2cf): |
| 341 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[4], 0x2d0): | 399 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[4], 0x2d0): |
| 342 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[5], 0x2d1): | 400 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[5], 0x2d1): |
| 343 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[6], 0x2d2): | 401 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[6], 0x2d2): |
| 344 | case PICA_REG_INDEX_WORKAROUND(vs_program.set_word[7], 0x2d3): | 402 | case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[7], 0x2d3): |
| 345 | { | 403 | { |
| 346 | g_state.vs.program_code[regs.vs_program.offset] = value; | 404 | g_state.vs.program_code[regs.vs.program.offset] = value; |
| 347 | regs.vs_program.offset++; | 405 | regs.vs.program.offset++; |
| 348 | break; | 406 | break; |
| 349 | } | 407 | } |
| 350 | 408 | ||
| 351 | // Load swizzle pattern data | 409 | // Load swizzle pattern data |
| 352 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[0], 0x2d6): | 410 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[0], 0x2d6): |
| 353 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[1], 0x2d7): | 411 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[1], 0x2d7): |
| 354 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[2], 0x2d8): | 412 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[2], 0x2d8): |
| 355 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[3], 0x2d9): | 413 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[3], 0x2d9): |
| 356 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[4], 0x2da): | 414 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[4], 0x2da): |
| 357 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[5], 0x2db): | 415 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[5], 0x2db): |
| 358 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[6], 0x2dc): | 416 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[6], 0x2dc): |
| 359 | case PICA_REG_INDEX_WORKAROUND(vs_swizzle_patterns.set_word[7], 0x2dd): | 417 | case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[7], 0x2dd): |
| 360 | { | 418 | { |
| 361 | g_state.vs.swizzle_data[regs.vs_swizzle_patterns.offset] = value; | 419 | g_state.vs.swizzle_data[regs.vs.swizzle_patterns.offset] = value; |
| 362 | regs.vs_swizzle_patterns.offset++; | 420 | regs.vs.swizzle_patterns.offset++; |
| 363 | break; | 421 | break; |
| 364 | } | 422 | } |
| 365 | 423 | ||
| @@ -370,7 +428,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 370 | VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id); | 428 | VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id); |
| 371 | 429 | ||
| 372 | if (g_debug_context) | 430 | if (g_debug_context) |
| 373 | g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); | 431 | g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id)); |
| 374 | } | 432 | } |
| 375 | 433 | ||
| 376 | void ProcessCommandList(const u32* list, u32 size) { | 434 | void ProcessCommandList(const u32* list, u32 size) { |
diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h index bb3d4150f..022a71f5e 100644 --- a/src/video_core/command_processor.h +++ b/src/video_core/command_processor.h | |||
| @@ -4,11 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <type_traits> | ||
| 8 | |||
| 7 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 8 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 9 | 11 | ||
| 10 | #include "pica.h" | ||
| 11 | |||
| 12 | namespace Pica { | 12 | namespace Pica { |
| 13 | 13 | ||
| 14 | namespace CommandProcessor { | 14 | namespace CommandProcessor { |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 7b8ab72b6..c3f8321c6 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "common/vector_math.h" | 23 | #include "common/vector_math.h" |
| 24 | 24 | ||
| 25 | #include "video_core/pica.h" | 25 | #include "video_core/pica.h" |
| 26 | #include "video_core/renderer_base.h" | ||
| 26 | #include "video_core/utils.h" | 27 | #include "video_core/utils.h" |
| 27 | #include "video_core/video_core.h" | 28 | #include "video_core/video_core.h" |
| 28 | 29 | ||
| @@ -84,7 +85,7 @@ void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { | |||
| 84 | vertices.push_back(v1); | 85 | vertices.push_back(v1); |
| 85 | vertices.push_back(v2); | 86 | vertices.push_back(v2); |
| 86 | 87 | ||
| 87 | int num_vertices = vertices.size(); | 88 | int num_vertices = (int)vertices.size(); |
| 88 | faces.push_back({ num_vertices-3, num_vertices-2, num_vertices-1 }); | 89 | faces.push_back({ num_vertices-3, num_vertices-2, num_vertices-1 }); |
| 89 | } | 90 | } |
| 90 | 91 | ||
| @@ -240,8 +241,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data | |||
| 240 | 241 | ||
| 241 | dvle.main_offset_words = main_offset; | 242 | dvle.main_offset_words = main_offset; |
| 242 | dvle.output_register_table_offset = write_offset - dvlb.dvle_offset; | 243 | dvle.output_register_table_offset = write_offset - dvlb.dvle_offset; |
| 243 | dvle.output_register_table_size = output_info_table.size(); | 244 | dvle.output_register_table_size = static_cast<uint32_t>(output_info_table.size()); |
| 244 | QueueForWriting((u8*)output_info_table.data(), output_info_table.size() * sizeof(OutputRegisterInfo)); | 245 | QueueForWriting((u8*)output_info_table.data(), static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo))); |
| 245 | 246 | ||
| 246 | // TODO: Create a label table for "main" | 247 | // TODO: Create a label table for "main" |
| 247 | 248 | ||
| @@ -496,31 +497,31 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 496 | // Lookup base value | 497 | // Lookup base value |
| 497 | Math::Vec3<int> ret; | 498 | Math::Vec3<int> ret; |
| 498 | if (differential_mode) { | 499 | if (differential_mode) { |
| 499 | ret.r() = differential.r; | 500 | ret.r() = static_cast<int>(differential.r); |
| 500 | ret.g() = differential.g; | 501 | ret.g() = static_cast<int>(differential.g); |
| 501 | ret.b() = differential.b; | 502 | ret.b() = static_cast<int>(differential.b); |
| 502 | if (x >= 2) { | 503 | if (x >= 2) { |
| 503 | ret.r() += differential.dr; | 504 | ret.r() += static_cast<int>(differential.dr); |
| 504 | ret.g() += differential.dg; | 505 | ret.g() += static_cast<int>(differential.dg); |
| 505 | ret.b() += differential.db; | 506 | ret.b() += static_cast<int>(differential.db); |
| 506 | } | 507 | } |
| 507 | ret.r() = Color::Convert5To8(ret.r()); | 508 | ret.r() = Color::Convert5To8(ret.r()); |
| 508 | ret.g() = Color::Convert5To8(ret.g()); | 509 | ret.g() = Color::Convert5To8(ret.g()); |
| 509 | ret.b() = Color::Convert5To8(ret.b()); | 510 | ret.b() = Color::Convert5To8(ret.b()); |
| 510 | } else { | 511 | } else { |
| 511 | if (x < 2) { | 512 | if (x < 2) { |
| 512 | ret.r() = Color::Convert4To8(separate.r1); | 513 | ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1)); |
| 513 | ret.g() = Color::Convert4To8(separate.g1); | 514 | ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1)); |
| 514 | ret.b() = Color::Convert4To8(separate.b1); | 515 | ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1)); |
| 515 | } else { | 516 | } else { |
| 516 | ret.r() = Color::Convert4To8(separate.r2); | 517 | ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2)); |
| 517 | ret.g() = Color::Convert4To8(separate.g2); | 518 | ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2)); |
| 518 | ret.b() = Color::Convert4To8(separate.b2); | 519 | ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2)); |
| 519 | } | 520 | } |
| 520 | } | 521 | } |
| 521 | 522 | ||
| 522 | // Add modifier | 523 | // Add modifier |
| 523 | unsigned table_index = (x < 2) ? table_index_1.Value() : table_index_2.Value(); | 524 | unsigned table_index = static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value()); |
| 524 | 525 | ||
| 525 | static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{ | 526 | static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{ |
| 526 | { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, | 527 | { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, |
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 7926d64ec..3f109dcb7 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | 14 | ||
| 15 | #include "common/vector_math.h" | 15 | #include "common/vector_math.h" |
| 16 | 16 | ||
| 17 | #include "core/tracer/recorder.h" | ||
| 18 | |||
| 17 | #include "video_core/pica.h" | 19 | #include "video_core/pica.h" |
| 18 | 20 | ||
| 19 | namespace Pica { | 21 | namespace Pica { |
| @@ -23,11 +25,14 @@ public: | |||
| 23 | enum class Event { | 25 | enum class Event { |
| 24 | FirstEvent = 0, | 26 | FirstEvent = 0, |
| 25 | 27 | ||
| 26 | CommandLoaded = FirstEvent, | 28 | PicaCommandLoaded = FirstEvent, |
| 27 | CommandProcessed, | 29 | PicaCommandProcessed, |
| 28 | IncomingPrimitiveBatch, | 30 | IncomingPrimitiveBatch, |
| 29 | FinishedPrimitiveBatch, | 31 | FinishedPrimitiveBatch, |
| 30 | VertexLoaded, | 32 | VertexLoaded, |
| 33 | IncomingDisplayTransfer, | ||
| 34 | GSPCommandProcessed, | ||
| 35 | BufferSwapped, | ||
| 31 | 36 | ||
| 32 | NumEvents | 37 | NumEvents |
| 33 | }; | 38 | }; |
| @@ -129,6 +134,8 @@ public: | |||
| 129 | Event active_breakpoint; | 134 | Event active_breakpoint; |
| 130 | bool at_breakpoint = false; | 135 | bool at_breakpoint = false; |
| 131 | 136 | ||
| 137 | std::shared_ptr<CiTrace::Recorder> recorder = nullptr; | ||
| 138 | |||
| 132 | private: | 139 | private: |
| 133 | /** | 140 | /** |
| 134 | * Private default constructor to make sure people always construct this through Construct() | 141 | * Private default constructor to make sure people always construct this through Construct() |
diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/hwrasterizer_base.h index dec193f8b..c8746c608 100644 --- a/src/video_core/hwrasterizer_base.h +++ b/src/video_core/hwrasterizer_base.h | |||
| @@ -4,8 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/emu_window.h" | 7 | #include "common/common_types.h" |
| 8 | #include "video_core/vertex_shader.h" | 8 | |
| 9 | namespace Pica { | ||
| 10 | namespace VertexShader { | ||
| 11 | struct OutputVertex; | ||
| 12 | } | ||
| 13 | } | ||
| 9 | 14 | ||
| 10 | class HWRasterizer { | 15 | class HWRasterizer { |
| 11 | public: | 16 | public: |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 9628a7589..38599a7a3 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -5,10 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cmath> | ||
| 8 | #include <cstddef> | 9 | #include <cstddef> |
| 9 | #include <initializer_list> | ||
| 10 | #include <map> | 10 | #include <map> |
| 11 | #include <vector> | 11 | #include <string> |
| 12 | 12 | ||
| 13 | #include "common/assert.h" | 13 | #include "common/assert.h" |
| 14 | #include "common/bit_field.h" | 14 | #include "common/bit_field.h" |
| @@ -114,11 +114,22 @@ struct Regs { | |||
| 114 | struct TextureConfig { | 114 | struct TextureConfig { |
| 115 | enum WrapMode : u32 { | 115 | enum WrapMode : u32 { |
| 116 | ClampToEdge = 0, | 116 | ClampToEdge = 0, |
| 117 | ClampToBorder = 1, | ||
| 117 | Repeat = 2, | 118 | Repeat = 2, |
| 118 | MirroredRepeat = 3, | 119 | MirroredRepeat = 3, |
| 119 | }; | 120 | }; |
| 120 | 121 | ||
| 121 | INSERT_PADDING_WORDS(0x1); | 122 | enum TextureFilter : u32 { |
| 123 | Nearest = 0, | ||
| 124 | Linear = 1 | ||
| 125 | }; | ||
| 126 | |||
| 127 | union { | ||
| 128 | BitField< 0, 8, u32> r; | ||
| 129 | BitField< 8, 8, u32> g; | ||
| 130 | BitField<16, 8, u32> b; | ||
| 131 | BitField<24, 8, u32> a; | ||
| 132 | } border_color; | ||
| 122 | 133 | ||
| 123 | union { | 134 | union { |
| 124 | BitField< 0, 16, u32> height; | 135 | BitField< 0, 16, u32> height; |
| @@ -126,8 +137,10 @@ struct Regs { | |||
| 126 | }; | 137 | }; |
| 127 | 138 | ||
| 128 | union { | 139 | union { |
| 129 | BitField< 8, 2, WrapMode> wrap_s; | 140 | BitField< 1, 1, TextureFilter> mag_filter; |
| 130 | BitField<12, 2, WrapMode> wrap_t; | 141 | BitField< 2, 1, TextureFilter> min_filter; |
| 142 | BitField< 8, 2, WrapMode> wrap_t; | ||
| 143 | BitField<12, 2, WrapMode> wrap_s; | ||
| 131 | }; | 144 | }; |
| 132 | 145 | ||
| 133 | INSERT_PADDING_WORDS(0x1); | 146 | INSERT_PADDING_WORDS(0x1); |
| @@ -194,6 +207,7 @@ struct Regs { | |||
| 194 | case TextureFormat::IA8: | 207 | case TextureFormat::IA8: |
| 195 | return 4; | 208 | return 4; |
| 196 | 209 | ||
| 210 | case TextureFormat::I4: | ||
| 197 | case TextureFormat::A4: | 211 | case TextureFormat::A4: |
| 198 | return 1; | 212 | return 1; |
| 199 | 213 | ||
| @@ -284,6 +298,7 @@ struct Regs { | |||
| 284 | AddSigned = 3, | 298 | AddSigned = 3, |
| 285 | Lerp = 4, | 299 | Lerp = 4, |
| 286 | Subtract = 5, | 300 | Subtract = 5, |
| 301 | Dot3_RGB = 6, | ||
| 287 | 302 | ||
| 288 | MultiplyThenAdd = 8, | 303 | MultiplyThenAdd = 8, |
| 289 | AddThenMultiply = 9, | 304 | AddThenMultiply = 9, |
| @@ -414,6 +429,11 @@ struct Regs { | |||
| 414 | GreaterThanOrEqual = 7, | 429 | GreaterThanOrEqual = 7, |
| 415 | }; | 430 | }; |
| 416 | 431 | ||
| 432 | enum class StencilAction : u32 { | ||
| 433 | Keep = 0, | ||
| 434 | Xor = 5, | ||
| 435 | }; | ||
| 436 | |||
| 417 | struct { | 437 | struct { |
| 418 | union { | 438 | union { |
| 419 | // If false, logic blending is used | 439 | // If false, logic blending is used |
| @@ -448,15 +468,35 @@ struct Regs { | |||
| 448 | BitField< 8, 8, u32> ref; | 468 | BitField< 8, 8, u32> ref; |
| 449 | } alpha_test; | 469 | } alpha_test; |
| 450 | 470 | ||
| 451 | union { | 471 | struct { |
| 452 | BitField< 0, 1, u32> stencil_test_enable; | 472 | union { |
| 453 | BitField< 4, 3, CompareFunc> stencil_test_func; | 473 | // If true, enable stencil testing |
| 454 | BitField< 8, 8, u32> stencil_replacement_value; | 474 | BitField< 0, 1, u32> enable; |
| 455 | BitField<16, 8, u32> stencil_reference_value; | ||
| 456 | BitField<24, 8, u32> stencil_mask; | ||
| 457 | } stencil_test; | ||
| 458 | 475 | ||
| 459 | INSERT_PADDING_WORDS(0x1); | 476 | // Comparison operation for stencil testing |
| 477 | BitField< 4, 3, CompareFunc> func; | ||
| 478 | |||
| 479 | // Value to calculate the new stencil value from | ||
| 480 | BitField< 8, 8, u32> replacement_value; | ||
| 481 | |||
| 482 | // Value to compare against for stencil testing | ||
| 483 | BitField<16, 8, u32> reference_value; | ||
| 484 | |||
| 485 | // Mask to apply on stencil test inputs | ||
| 486 | BitField<24, 8, u32> mask; | ||
| 487 | }; | ||
| 488 | |||
| 489 | union { | ||
| 490 | // Action to perform when the stencil test fails | ||
| 491 | BitField< 0, 3, StencilAction> action_stencil_fail; | ||
| 492 | |||
| 493 | // Action to perform when stencil testing passed but depth testing fails | ||
| 494 | BitField< 4, 3, StencilAction> action_depth_fail; | ||
| 495 | |||
| 496 | // Action to perform when both stencil and depth testing pass | ||
| 497 | BitField< 8, 3, StencilAction> action_depth_pass; | ||
| 498 | }; | ||
| 499 | } stencil_test; | ||
| 460 | 500 | ||
| 461 | union { | 501 | union { |
| 462 | BitField< 0, 1, u32> depth_test_enable; | 502 | BitField< 0, 1, u32> depth_test_enable; |
| @@ -506,7 +546,7 @@ struct Regs { | |||
| 506 | struct { | 546 | struct { |
| 507 | INSERT_PADDING_WORDS(0x6); | 547 | INSERT_PADDING_WORDS(0x6); |
| 508 | 548 | ||
| 509 | DepthFormat depth_format; | 549 | DepthFormat depth_format; // TODO: Should be a BitField! |
| 510 | BitField<16, 3, ColorFormat> color_format; | 550 | BitField<16, 3, ColorFormat> color_format; |
| 511 | 551 | ||
| 512 | INSERT_PADDING_WORDS(0x4); | 552 | INSERT_PADDING_WORDS(0x4); |
| @@ -752,112 +792,119 @@ struct Regs { | |||
| 752 | INSERT_PADDING_WORDS(0x20); | 792 | INSERT_PADDING_WORDS(0x20); |
| 753 | 793 | ||
| 754 | enum class TriangleTopology : u32 { | 794 | enum class TriangleTopology : u32 { |
| 755 | List = 0, | 795 | List = 0, |
| 756 | Strip = 1, | 796 | Strip = 1, |
| 757 | Fan = 2, | 797 | Fan = 2, |
| 758 | ListIndexed = 3, // TODO: No idea if this is correct | 798 | Shader = 3, // Programmable setup unit implemented in a geometry shader |
| 759 | }; | 799 | }; |
| 760 | 800 | ||
| 761 | BitField<8, 2, TriangleTopology> triangle_topology; | 801 | BitField<8, 2, TriangleTopology> triangle_topology; |
| 762 | 802 | ||
| 763 | INSERT_PADDING_WORDS(0x51); | 803 | INSERT_PADDING_WORDS(0x21); |
| 764 | 804 | ||
| 765 | BitField<0, 16, u32> vs_bool_uniforms; | 805 | struct ShaderConfig { |
| 766 | union { | 806 | BitField<0, 16, u32> bool_uniforms; |
| 767 | BitField< 0, 8, u32> x; | ||
| 768 | BitField< 8, 8, u32> y; | ||
| 769 | BitField<16, 8, u32> z; | ||
| 770 | BitField<24, 8, u32> w; | ||
| 771 | } vs_int_uniforms[4]; | ||
| 772 | 807 | ||
| 773 | INSERT_PADDING_WORDS(0x5); | 808 | union { |
| 809 | BitField< 0, 8, u32> x; | ||
| 810 | BitField< 8, 8, u32> y; | ||
| 811 | BitField<16, 8, u32> z; | ||
| 812 | BitField<24, 8, u32> w; | ||
| 813 | } int_uniforms[4]; | ||
| 774 | 814 | ||
| 775 | // Offset to shader program entry point (in words) | 815 | INSERT_PADDING_WORDS(0x5); |
| 776 | BitField<0, 16, u32> vs_main_offset; | ||
| 777 | 816 | ||
| 778 | union { | 817 | // Offset to shader program entry point (in words) |
| 779 | BitField< 0, 4, u64> attribute0_register; | 818 | BitField<0, 16, u32> main_offset; |
| 780 | BitField< 4, 4, u64> attribute1_register; | 819 | |
| 781 | BitField< 8, 4, u64> attribute2_register; | 820 | union { |
| 782 | BitField<12, 4, u64> attribute3_register; | 821 | BitField< 0, 4, u64> attribute0_register; |
| 783 | BitField<16, 4, u64> attribute4_register; | 822 | BitField< 4, 4, u64> attribute1_register; |
| 784 | BitField<20, 4, u64> attribute5_register; | 823 | BitField< 8, 4, u64> attribute2_register; |
| 785 | BitField<24, 4, u64> attribute6_register; | 824 | BitField<12, 4, u64> attribute3_register; |
| 786 | BitField<28, 4, u64> attribute7_register; | 825 | BitField<16, 4, u64> attribute4_register; |
| 787 | BitField<32, 4, u64> attribute8_register; | 826 | BitField<20, 4, u64> attribute5_register; |
| 788 | BitField<36, 4, u64> attribute9_register; | 827 | BitField<24, 4, u64> attribute6_register; |
| 789 | BitField<40, 4, u64> attribute10_register; | 828 | BitField<28, 4, u64> attribute7_register; |
| 790 | BitField<44, 4, u64> attribute11_register; | 829 | BitField<32, 4, u64> attribute8_register; |
| 791 | BitField<48, 4, u64> attribute12_register; | 830 | BitField<36, 4, u64> attribute9_register; |
| 792 | BitField<52, 4, u64> attribute13_register; | 831 | BitField<40, 4, u64> attribute10_register; |
| 793 | BitField<56, 4, u64> attribute14_register; | 832 | BitField<44, 4, u64> attribute11_register; |
| 794 | BitField<60, 4, u64> attribute15_register; | 833 | BitField<48, 4, u64> attribute12_register; |
| 795 | 834 | BitField<52, 4, u64> attribute13_register; | |
| 796 | int GetRegisterForAttribute(int attribute_index) const { | 835 | BitField<56, 4, u64> attribute14_register; |
| 797 | u64 fields[] = { | 836 | BitField<60, 4, u64> attribute15_register; |
| 798 | attribute0_register, attribute1_register, attribute2_register, attribute3_register, | 837 | |
| 799 | attribute4_register, attribute5_register, attribute6_register, attribute7_register, | 838 | int GetRegisterForAttribute(int attribute_index) const { |
| 800 | attribute8_register, attribute9_register, attribute10_register, attribute11_register, | 839 | u64 fields[] = { |
| 801 | attribute12_register, attribute13_register, attribute14_register, attribute15_register, | 840 | attribute0_register, attribute1_register, attribute2_register, attribute3_register, |
| 841 | attribute4_register, attribute5_register, attribute6_register, attribute7_register, | ||
| 842 | attribute8_register, attribute9_register, attribute10_register, attribute11_register, | ||
| 843 | attribute12_register, attribute13_register, attribute14_register, attribute15_register, | ||
| 844 | }; | ||
| 845 | return (int)fields[attribute_index]; | ||
| 846 | } | ||
| 847 | } input_register_map; | ||
| 848 | |||
| 849 | // OUTMAP_MASK, 0x28E, CODETRANSFER_END | ||
| 850 | INSERT_PADDING_WORDS(0x3); | ||
| 851 | |||
| 852 | struct { | ||
| 853 | enum Format : u32 | ||
| 854 | { | ||
| 855 | FLOAT24 = 0, | ||
| 856 | FLOAT32 = 1 | ||
| 802 | }; | 857 | }; |
| 803 | return (int)fields[attribute_index]; | ||
| 804 | } | ||
| 805 | } vs_input_register_map; | ||
| 806 | 858 | ||
| 807 | INSERT_PADDING_WORDS(0x3); | 859 | bool IsFloat32() const { |
| 860 | return format == FLOAT32; | ||
| 861 | } | ||
| 808 | 862 | ||
| 809 | struct { | 863 | union { |
| 810 | enum Format : u32 | 864 | // Index of the next uniform to write to |
| 811 | { | 865 | // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid indices |
| 812 | FLOAT24 = 0, | 866 | // TODO: Maybe the uppermost index is for the geometry shader? Investigate! |
| 813 | FLOAT32 = 1 | 867 | BitField<0, 7, u32> index; |
| 814 | }; | ||
| 815 | 868 | ||
| 816 | bool IsFloat32() const { | 869 | BitField<31, 1, Format> format; |
| 817 | return format == FLOAT32; | 870 | }; |
| 818 | } | ||
| 819 | 871 | ||
| 820 | union { | 872 | // Writing to these registers sets the current uniform. |
| 821 | // Index of the next uniform to write to | 873 | u32 set_value[8]; |
| 822 | // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid indices | ||
| 823 | BitField<0, 7, u32> index; | ||
| 824 | 874 | ||
| 825 | BitField<31, 1, Format> format; | 875 | } uniform_setup; |
| 826 | }; | ||
| 827 | 876 | ||
| 828 | // Writing to these registers sets the "current" uniform. | 877 | INSERT_PADDING_WORDS(0x2); |
| 829 | // TODO: It's not clear how the hardware stores what the "current" uniform is. | ||
| 830 | u32 set_value[8]; | ||
| 831 | 878 | ||
| 832 | } vs_uniform_setup; | 879 | struct { |
| 880 | // Offset of the next instruction to write code to. | ||
| 881 | // Incremented with each instruction write. | ||
| 882 | u32 offset; | ||
| 833 | 883 | ||
| 834 | INSERT_PADDING_WORDS(0x2); | 884 | // Writing to these registers sets the "current" word in the shader program. |
| 885 | u32 set_word[8]; | ||
| 886 | } program; | ||
| 835 | 887 | ||
| 836 | struct { | 888 | INSERT_PADDING_WORDS(0x1); |
| 837 | // Offset of the next instruction to write code to. | ||
| 838 | // Incremented with each instruction write. | ||
| 839 | u32 offset; | ||
| 840 | 889 | ||
| 841 | // Writing to these registers sets the "current" word in the shader program. | 890 | // This register group is used to load an internal table of swizzling patterns, |
| 842 | // TODO: It's not clear how the hardware stores what the "current" word is. | 891 | // which are indexed by each shader instruction to specify vector component swizzling. |
| 843 | u32 set_word[8]; | 892 | struct { |
| 844 | } vs_program; | 893 | // Offset of the next swizzle pattern to write code to. |
| 894 | // Incremented with each instruction write. | ||
| 895 | u32 offset; | ||
| 845 | 896 | ||
| 846 | INSERT_PADDING_WORDS(0x1); | 897 | // Writing to these registers sets the current swizzle pattern in the table. |
| 898 | u32 set_word[8]; | ||
| 899 | } swizzle_patterns; | ||
| 847 | 900 | ||
| 848 | // This register group is used to load an internal table of swizzling patterns, | 901 | INSERT_PADDING_WORDS(0x2); |
| 849 | // which are indexed by each shader instruction to specify vector component swizzling. | 902 | }; |
| 850 | struct { | ||
| 851 | // Offset of the next swizzle pattern to write code to. | ||
| 852 | // Incremented with each instruction write. | ||
| 853 | u32 offset; | ||
| 854 | 903 | ||
| 855 | // Writing to these registers sets the "current" swizzle pattern in the table. | 904 | ShaderConfig gs; |
| 856 | // TODO: It's not clear how the hardware stores what the "current" swizzle pattern is. | 905 | ShaderConfig vs; |
| 857 | u32 set_word[8]; | ||
| 858 | } vs_swizzle_patterns; | ||
| 859 | 906 | ||
| 860 | INSERT_PADDING_WORDS(0x22); | 907 | INSERT_PADDING_WORDS(0x20); |
| 861 | 908 | ||
| 862 | // Map register indices to names readable by humans | 909 | // Map register indices to names readable by humans |
| 863 | // Used for debugging purposes, so performance is not an issue here | 910 | // Used for debugging purposes, so performance is not an issue here |
| @@ -866,7 +913,7 @@ struct Regs { | |||
| 866 | 913 | ||
| 867 | #define ADD_FIELD(name) \ | 914 | #define ADD_FIELD(name) \ |
| 868 | do { \ | 915 | do { \ |
| 869 | map.insert({PICA_REG_INDEX(name), #name}); \ | 916 | map.insert({static_cast<u32>(PICA_REG_INDEX(name)), #name}); \ |
| 870 | /* TODO: change to Regs::name when VS2015 and other compilers support it */ \ | 917 | /* TODO: change to Regs::name when VS2015 and other compilers support it */ \ |
| 871 | for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \ | 918 | for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \ |
| 872 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ | 919 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ |
| @@ -904,13 +951,20 @@ struct Regs { | |||
| 904 | ADD_FIELD(vs_default_attributes_setup); | 951 | ADD_FIELD(vs_default_attributes_setup); |
| 905 | ADD_FIELD(command_buffer); | 952 | ADD_FIELD(command_buffer); |
| 906 | ADD_FIELD(triangle_topology); | 953 | ADD_FIELD(triangle_topology); |
| 907 | ADD_FIELD(vs_bool_uniforms); | 954 | ADD_FIELD(gs.bool_uniforms); |
| 908 | ADD_FIELD(vs_int_uniforms); | 955 | ADD_FIELD(gs.int_uniforms); |
| 909 | ADD_FIELD(vs_main_offset); | 956 | ADD_FIELD(gs.main_offset); |
| 910 | ADD_FIELD(vs_input_register_map); | 957 | ADD_FIELD(gs.input_register_map); |
| 911 | ADD_FIELD(vs_uniform_setup); | 958 | ADD_FIELD(gs.uniform_setup); |
| 912 | ADD_FIELD(vs_program); | 959 | ADD_FIELD(gs.program); |
| 913 | ADD_FIELD(vs_swizzle_patterns); | 960 | ADD_FIELD(gs.swizzle_patterns); |
| 961 | ADD_FIELD(vs.bool_uniforms); | ||
| 962 | ADD_FIELD(vs.int_uniforms); | ||
| 963 | ADD_FIELD(vs.main_offset); | ||
| 964 | ADD_FIELD(vs.input_register_map); | ||
| 965 | ADD_FIELD(vs.uniform_setup); | ||
| 966 | ADD_FIELD(vs.program); | ||
| 967 | ADD_FIELD(vs.swizzle_patterns); | ||
| 914 | 968 | ||
| 915 | #undef ADD_FIELD | 969 | #undef ADD_FIELD |
| 916 | 970 | ||
| @@ -982,17 +1036,14 @@ ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); | |||
| 982 | ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); | 1036 | ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); |
| 983 | ASSERT_REG_POSITION(command_buffer, 0x238); | 1037 | ASSERT_REG_POSITION(command_buffer, 0x238); |
| 984 | ASSERT_REG_POSITION(triangle_topology, 0x25e); | 1038 | ASSERT_REG_POSITION(triangle_topology, 0x25e); |
| 985 | ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | 1039 | ASSERT_REG_POSITION(gs, 0x280); |
| 986 | ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); | 1040 | ASSERT_REG_POSITION(vs, 0x2b0); |
| 987 | ASSERT_REG_POSITION(vs_main_offset, 0x2ba); | ||
| 988 | ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); | ||
| 989 | ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); | ||
| 990 | ASSERT_REG_POSITION(vs_program, 0x2cb); | ||
| 991 | ASSERT_REG_POSITION(vs_swizzle_patterns, 0x2d5); | ||
| 992 | 1041 | ||
| 993 | #undef ASSERT_REG_POSITION | 1042 | #undef ASSERT_REG_POSITION |
| 994 | #endif // !defined(_MSC_VER) | 1043 | #endif // !defined(_MSC_VER) |
| 995 | 1044 | ||
| 1045 | static_assert(sizeof(Regs::ShaderConfig) == 0x30 * sizeof(u32), "ShaderConfig structure has incorrect size"); | ||
| 1046 | |||
| 996 | // The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway. | 1047 | // The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway. |
| 997 | static_assert(sizeof(Regs) <= 0x300 * sizeof(u32), "Register set structure larger than it should be"); | 1048 | static_assert(sizeof(Regs) <= 0x300 * sizeof(u32), "Register set structure larger than it should be"); |
| 998 | static_assert(sizeof(Regs) >= 0x300 * sizeof(u32), "Register set structure smaller than it should be"); | 1049 | static_assert(sizeof(Regs) >= 0x300 * sizeof(u32), "Register set structure smaller than it should be"); |
| @@ -1014,7 +1065,7 @@ struct float24 { | |||
| 1014 | u32 mantissa = hex & 0xFFFF; | 1065 | u32 mantissa = hex & 0xFFFF; |
| 1015 | u32 exponent = (hex >> 16) & 0x7F; | 1066 | u32 exponent = (hex >> 16) & 0x7F; |
| 1016 | u32 sign = hex >> 23; | 1067 | u32 sign = hex >> 23; |
| 1017 | ret.value = powf(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * powf(2.0f, -16.f)); | 1068 | ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f)); |
| 1018 | if (sign) | 1069 | if (sign) |
| 1019 | ret.value = -ret.value; | 1070 | ret.value = -ret.value; |
| 1020 | } | 1071 | } |
| @@ -1102,7 +1153,7 @@ struct State { | |||
| 1102 | Regs regs; | 1153 | Regs regs; |
| 1103 | 1154 | ||
| 1104 | /// Vertex shader memory | 1155 | /// Vertex shader memory |
| 1105 | struct { | 1156 | struct ShaderSetup { |
| 1106 | struct { | 1157 | struct { |
| 1107 | Math::Vec4<float24> f[96]; | 1158 | Math::Vec4<float24> f[96]; |
| 1108 | std::array<bool, 16> b; | 1159 | std::array<bool, 16> b; |
| @@ -1113,7 +1164,10 @@ struct State { | |||
| 1113 | 1164 | ||
| 1114 | std::array<u32, 1024> program_code; | 1165 | std::array<u32, 1024> program_code; |
| 1115 | std::array<u32, 1024> swizzle_data; | 1166 | std::array<u32, 1024> swizzle_data; |
| 1116 | } vs; | 1167 | }; |
| 1168 | |||
| 1169 | ShaderSetup vs; | ||
| 1170 | ShaderSetup gs; | ||
| 1117 | 1171 | ||
| 1118 | /// Current Pica command list | 1172 | /// Current Pica command list |
| 1119 | struct { | 1173 | struct { |
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index 0120f2896..2f22bdcce 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp | |||
| @@ -20,8 +20,9 @@ template<typename VertexType> | |||
| 20 | void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler) | 20 | void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler) |
| 21 | { | 21 | { |
| 22 | switch (topology) { | 22 | switch (topology) { |
| 23 | // TODO: Figure out what's different with TriangleTopology::Shader. | ||
| 23 | case Regs::TriangleTopology::List: | 24 | case Regs::TriangleTopology::List: |
| 24 | case Regs::TriangleTopology::ListIndexed: | 25 | case Regs::TriangleTopology::Shader: |
| 25 | if (buffer_index < 2) { | 26 | if (buffer_index < 2) { |
| 26 | buffer[buffer_index++] = vtx; | 27 | buffer[buffer_index++] = vtx; |
| 27 | } else { | 28 | } else { |
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 59d156ee7..e2b90ad1c 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -126,6 +126,30 @@ static u32 GetDepth(int x, int y) { | |||
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | static u8 GetStencil(int x, int y) { | ||
| 130 | const auto& framebuffer = g_state.regs.framebuffer; | ||
| 131 | const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); | ||
| 132 | u8* depth_buffer = Memory::GetPhysicalPointer(addr); | ||
| 133 | |||
| 134 | y = framebuffer.height - y; | ||
| 135 | |||
| 136 | const u32 coarse_y = y & ~7; | ||
| 137 | u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format); | ||
| 138 | u32 stride = framebuffer.width * bytes_per_pixel; | ||
| 139 | |||
| 140 | u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; | ||
| 141 | u8* src_pixel = depth_buffer + src_offset; | ||
| 142 | |||
| 143 | switch (framebuffer.depth_format) { | ||
| 144 | case Regs::DepthFormat::D24S8: | ||
| 145 | return Color::DecodeD24S8(src_pixel).y; | ||
| 146 | |||
| 147 | default: | ||
| 148 | LOG_WARNING(HW_GPU, "GetStencil called for function which doesn't have a stencil component (format %u)", framebuffer.depth_format); | ||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 129 | static void SetDepth(int x, int y, u32 value) { | 153 | static void SetDepth(int x, int y, u32 value) { |
| 130 | const auto& framebuffer = g_state.regs.framebuffer; | 154 | const auto& framebuffer = g_state.regs.framebuffer; |
| 131 | const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); | 155 | const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); |
| @@ -144,13 +168,15 @@ static void SetDepth(int x, int y, u32 value) { | |||
| 144 | case Regs::DepthFormat::D16: | 168 | case Regs::DepthFormat::D16: |
| 145 | Color::EncodeD16(value, dst_pixel); | 169 | Color::EncodeD16(value, dst_pixel); |
| 146 | break; | 170 | break; |
| 171 | |||
| 147 | case Regs::DepthFormat::D24: | 172 | case Regs::DepthFormat::D24: |
| 148 | Color::EncodeD24(value, dst_pixel); | 173 | Color::EncodeD24(value, dst_pixel); |
| 149 | break; | 174 | break; |
| 175 | |||
| 150 | case Regs::DepthFormat::D24S8: | 176 | case Regs::DepthFormat::D24S8: |
| 151 | // TODO(Subv): Implement the stencil buffer | 177 | Color::EncodeD24X8(value, dst_pixel); |
| 152 | Color::EncodeD24S8(value, 0, dst_pixel); | ||
| 153 | break; | 178 | break; |
| 179 | |||
| 154 | default: | 180 | default: |
| 155 | LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); | 181 | LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); |
| 156 | UNIMPLEMENTED(); | 182 | UNIMPLEMENTED(); |
| @@ -158,6 +184,53 @@ static void SetDepth(int x, int y, u32 value) { | |||
| 158 | } | 184 | } |
| 159 | } | 185 | } |
| 160 | 186 | ||
| 187 | static void SetStencil(int x, int y, u8 value) { | ||
| 188 | const auto& framebuffer = g_state.regs.framebuffer; | ||
| 189 | const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress(); | ||
| 190 | u8* depth_buffer = Memory::GetPhysicalPointer(addr); | ||
| 191 | |||
| 192 | y = framebuffer.height - y; | ||
| 193 | |||
| 194 | const u32 coarse_y = y & ~7; | ||
| 195 | u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format); | ||
| 196 | u32 stride = framebuffer.width * bytes_per_pixel; | ||
| 197 | |||
| 198 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; | ||
| 199 | u8* dst_pixel = depth_buffer + dst_offset; | ||
| 200 | |||
| 201 | switch (framebuffer.depth_format) { | ||
| 202 | case Pica::Regs::DepthFormat::D16: | ||
| 203 | case Pica::Regs::DepthFormat::D24: | ||
| 204 | // Nothing to do | ||
| 205 | break; | ||
| 206 | |||
| 207 | case Pica::Regs::DepthFormat::D24S8: | ||
| 208 | Color::EncodeX24S8(value, dst_pixel); | ||
| 209 | break; | ||
| 210 | |||
| 211 | default: | ||
| 212 | LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); | ||
| 213 | UNIMPLEMENTED(); | ||
| 214 | break; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | // TODO: Should the stencil mask be applied to the "dest" or "ref" operands? Most likely not! | ||
| 219 | static u8 PerformStencilAction(Regs::StencilAction action, u8 dest, u8 ref) { | ||
| 220 | switch (action) { | ||
| 221 | case Regs::StencilAction::Keep: | ||
| 222 | return dest; | ||
| 223 | |||
| 224 | case Regs::StencilAction::Xor: | ||
| 225 | return dest ^ ref; | ||
| 226 | |||
| 227 | default: | ||
| 228 | LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action); | ||
| 229 | UNIMPLEMENTED(); | ||
| 230 | return 0; | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 161 | // NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values | 234 | // NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values |
| 162 | struct Fix12P4 { | 235 | struct Fix12P4 { |
| 163 | Fix12P4() {} | 236 | Fix12P4() {} |
| @@ -276,6 +349,9 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 276 | auto textures = regs.GetTextures(); | 349 | auto textures = regs.GetTextures(); |
| 277 | auto tev_stages = regs.GetTevStages(); | 350 | auto tev_stages = regs.GetTevStages(); |
| 278 | 351 | ||
| 352 | bool stencil_action_enable = g_state.regs.output_merger.stencil_test.enable && g_state.regs.framebuffer.depth_format == Regs::DepthFormat::D24S8; | ||
| 353 | const auto stencil_test = g_state.regs.output_merger.stencil_test; | ||
| 354 | |||
| 279 | // Enter rasterization loop, starting at the center of the topleft bounding box corner. | 355 | // Enter rasterization loop, starting at the center of the topleft bounding box corner. |
| 280 | // TODO: Not sure if looping through x first might be faster | 356 | // TODO: Not sure if looping through x first might be faster |
| 281 | for (u16 y = min_y + 8; y < max_y; y += 0x10) { | 357 | for (u16 y = min_y + 8; y < max_y; y += 0x10) { |
| @@ -349,6 +425,9 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 349 | val = std::min(val, (int)size - 1); | 425 | val = std::min(val, (int)size - 1); |
| 350 | return val; | 426 | return val; |
| 351 | 427 | ||
| 428 | case Regs::TextureConfig::ClampToBorder: | ||
| 429 | return val; | ||
| 430 | |||
| 352 | case Regs::TextureConfig::Repeat: | 431 | case Regs::TextureConfig::Repeat: |
| 353 | return (int)((unsigned)val % size); | 432 | return (int)((unsigned)val % size); |
| 354 | 433 | ||
| @@ -367,17 +446,24 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 367 | } | 446 | } |
| 368 | }; | 447 | }; |
| 369 | 448 | ||
| 370 | // Textures are laid out from bottom to top, hence we invert the t coordinate. | 449 | if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder && (s < 0 || s >= texture.config.width)) |
| 371 | // NOTE: This may not be the right place for the inversion. | 450 | || (texture.config.wrap_t == Regs::TextureConfig::ClampToBorder && (t < 0 || t >= texture.config.height))) { |
| 372 | // TODO: Check if this applies to ETC textures, too. | 451 | auto border_color = texture.config.border_color; |
| 373 | s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width); | 452 | texture_color[i] = { border_color.r, border_color.g, border_color.b, border_color.a }; |
| 374 | t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); | 453 | } else { |
| 375 | 454 | // Textures are laid out from bottom to top, hence we invert the t coordinate. | |
| 376 | u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); | 455 | // NOTE: This may not be the right place for the inversion. |
| 377 | auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); | 456 | // TODO: Check if this applies to ETC textures, too. |
| 378 | 457 | s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width); | |
| 379 | texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); | 458 | t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); |
| 380 | DebugUtils::DumpTexture(texture.config, texture_data); | 459 | |
| 460 | u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); | ||
| 461 | auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); | ||
| 462 | |||
| 463 | // TODO: Apply the min and mag filters to the texture | ||
| 464 | texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); | ||
| 465 | DebugUtils::DumpTexture(texture.config, texture_data); | ||
| 466 | } | ||
| 381 | } | 467 | } |
| 382 | 468 | ||
| 383 | // Texture environment - consists of 6 stages of color and alpha combining. | 469 | // Texture environment - consists of 6 stages of color and alpha combining. |
| @@ -556,7 +642,18 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 556 | result = (result * input[2].Cast<int>()) / 255; | 642 | result = (result * input[2].Cast<int>()) / 255; |
| 557 | return result.Cast<u8>(); | 643 | return result.Cast<u8>(); |
| 558 | } | 644 | } |
| 559 | 645 | case Operation::Dot3_RGB: | |
| 646 | { | ||
| 647 | // Not fully accurate. | ||
| 648 | // Worst case scenario seems to yield a +/-3 error | ||
| 649 | // Some HW results indicate that the per-component computation can't have a higher precision than 1/256, | ||
| 650 | // while dot3_rgb( (0x80,g0,b0),(0x7F,g1,b1) ) and dot3_rgb( (0x80,g0,b0),(0x80,g1,b1) ) give different results | ||
| 651 | int result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 + | ||
| 652 | ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 + | ||
| 653 | ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256; | ||
| 654 | result = std::max(0, std::min(255, result)); | ||
| 655 | return { (u8)result, (u8)result, (u8)result }; | ||
| 656 | } | ||
| 560 | default: | 657 | default: |
| 561 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); | 658 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); |
| 562 | UNIMPLEMENTED(); | 659 | UNIMPLEMENTED(); |
| @@ -638,6 +735,7 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 638 | } | 735 | } |
| 639 | 736 | ||
| 640 | const auto& output_merger = regs.output_merger; | 737 | const auto& output_merger = regs.output_merger; |
| 738 | // TODO: Does alpha testing happen before or after stencil? | ||
| 641 | if (output_merger.alpha_test.enable) { | 739 | if (output_merger.alpha_test.enable) { |
| 642 | bool pass = false; | 740 | bool pass = false; |
| 643 | 741 | ||
| @@ -679,6 +777,54 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 679 | continue; | 777 | continue; |
| 680 | } | 778 | } |
| 681 | 779 | ||
| 780 | u8 old_stencil = 0; | ||
| 781 | if (stencil_action_enable) { | ||
| 782 | old_stencil = GetStencil(x >> 4, y >> 4); | ||
| 783 | u8 dest = old_stencil & stencil_test.mask; | ||
| 784 | u8 ref = stencil_test.reference_value & stencil_test.mask; | ||
| 785 | |||
| 786 | bool pass = false; | ||
| 787 | switch (stencil_test.func) { | ||
| 788 | case Regs::CompareFunc::Never: | ||
| 789 | pass = false; | ||
| 790 | break; | ||
| 791 | |||
| 792 | case Regs::CompareFunc::Always: | ||
| 793 | pass = true; | ||
| 794 | break; | ||
| 795 | |||
| 796 | case Regs::CompareFunc::Equal: | ||
| 797 | pass = (ref == dest); | ||
| 798 | break; | ||
| 799 | |||
| 800 | case Regs::CompareFunc::NotEqual: | ||
| 801 | pass = (ref != dest); | ||
| 802 | break; | ||
| 803 | |||
| 804 | case Regs::CompareFunc::LessThan: | ||
| 805 | pass = (ref < dest); | ||
| 806 | break; | ||
| 807 | |||
| 808 | case Regs::CompareFunc::LessThanOrEqual: | ||
| 809 | pass = (ref <= dest); | ||
| 810 | break; | ||
| 811 | |||
| 812 | case Regs::CompareFunc::GreaterThan: | ||
| 813 | pass = (ref > dest); | ||
| 814 | break; | ||
| 815 | |||
| 816 | case Regs::CompareFunc::GreaterThanOrEqual: | ||
| 817 | pass = (ref >= dest); | ||
| 818 | break; | ||
| 819 | } | ||
| 820 | |||
| 821 | if (!pass) { | ||
| 822 | u8 new_stencil = PerformStencilAction(stencil_test.action_stencil_fail, old_stencil, stencil_test.replacement_value); | ||
| 823 | SetStencil(x >> 4, y >> 4, new_stencil); | ||
| 824 | continue; | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 682 | // TODO: Does depth indeed only get written even if depth testing is enabled? | 828 | // TODO: Does depth indeed only get written even if depth testing is enabled? |
| 683 | if (output_merger.depth_test_enable) { | 829 | if (output_merger.depth_test_enable) { |
| 684 | unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); | 830 | unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); |
| @@ -723,11 +869,22 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 723 | break; | 869 | break; |
| 724 | } | 870 | } |
| 725 | 871 | ||
| 726 | if (!pass) | 872 | if (!pass) { |
| 873 | if (stencil_action_enable) { | ||
| 874 | u8 new_stencil = PerformStencilAction(stencil_test.action_depth_fail, old_stencil, stencil_test.replacement_value); | ||
| 875 | SetStencil(x >> 4, y >> 4, new_stencil); | ||
| 876 | } | ||
| 727 | continue; | 877 | continue; |
| 878 | } | ||
| 728 | 879 | ||
| 729 | if (output_merger.depth_write_enable) | 880 | if (output_merger.depth_write_enable) |
| 730 | SetDepth(x >> 4, y >> 4, z); | 881 | SetDepth(x >> 4, y >> 4, z); |
| 882 | |||
| 883 | if (stencil_action_enable) { | ||
| 884 | // TODO: What happens if stencil testing is enabled, but depth testing is not? Will stencil get updated anyway? | ||
| 885 | u8 new_stencil = PerformStencilAction(stencil_test.action_depth_pass, old_stencil, stencil_test.replacement_value); | ||
| 886 | SetStencil(x >> 4, y >> 4, new_stencil); | ||
| 887 | } | ||
| 731 | } | 888 | } |
| 732 | 889 | ||
| 733 | auto dest = GetPixel(x >> 4, y >> 4); | 890 | auto dest = GetPixel(x >> 4, y >> 4); |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5757ac75d..6587bcf27 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -4,10 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 9 | #include "video_core/hwrasterizer_base.h" | 11 | #include "video_core/hwrasterizer_base.h" |
| 10 | 12 | ||
| 13 | class EmuWindow; | ||
| 14 | |||
| 11 | class RendererBase : NonCopyable { | 15 | class RendererBase : NonCopyable { |
| 12 | public: | 16 | public: |
| 13 | 17 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 518f79331..2db845da6 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -2,10 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | |||
| 5 | #include "common/color.h" | 8 | #include "common/color.h" |
| 9 | #include "common/math_util.h" | ||
| 6 | 10 | ||
| 7 | #include "core/settings.h" | ||
| 8 | #include "core/hw/gpu.h" | 11 | #include "core/hw/gpu.h" |
| 12 | #include "core/memory.h" | ||
| 13 | #include "core/settings.h" | ||
| 9 | 14 | ||
| 10 | #include "video_core/pica.h" | 15 | #include "video_core/pica.h" |
| 11 | #include "video_core/utils.h" | 16 | #include "video_core/utils.h" |
| @@ -16,8 +21,6 @@ | |||
| 16 | 21 | ||
| 17 | #include "generated/gl_3_2_core.h" | 22 | #include "generated/gl_3_2_core.h" |
| 18 | 23 | ||
| 19 | #include <memory> | ||
| 20 | |||
| 21 | static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { | 24 | static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { |
| 22 | return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && | 25 | return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && |
| 23 | stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && | 26 | stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && |
| @@ -813,12 +816,16 @@ void RasterizerOpenGL::ReloadColorBuffer() { | |||
| 813 | } | 816 | } |
| 814 | 817 | ||
| 815 | void RasterizerOpenGL::ReloadDepthBuffer() { | 818 | void RasterizerOpenGL::ReloadDepthBuffer() { |
| 819 | PAddr depth_buffer_addr = Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress(); | ||
| 820 | |||
| 821 | if (depth_buffer_addr == 0) | ||
| 822 | return; | ||
| 823 | |||
| 816 | // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil | 824 | // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil |
| 817 | u8* depth_buffer = Memory::GetPhysicalPointer(Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress()); | 825 | u8* depth_buffer = Memory::GetPhysicalPointer(depth_buffer_addr); |
| 818 | 826 | ||
| 819 | if (depth_buffer == nullptr) { | 827 | if (depth_buffer == nullptr) |
| 820 | return; | 828 | return; |
| 821 | } | ||
| 822 | 829 | ||
| 823 | u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); | 830 | u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); |
| 824 | 831 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d7d422b1f..ae7b26fc6 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -4,7 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 7 | #include "video_core/hwrasterizer_base.h" | 11 | #include "video_core/hwrasterizer_base.h" |
| 12 | #include "video_core/vertex_shader.h" | ||
| 8 | 13 | ||
| 9 | #include "gl_state.h" | 14 | #include "gl_state.h" |
| 10 | #include "gl_rasterizer_cache.h" | 15 | #include "gl_rasterizer_cache.h" |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 2e4110a88..dc3ffdf22 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -31,12 +31,18 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text | |||
| 31 | state.texture_units[texture_unit].texture_2d = new_texture->texture.handle; | 31 | state.texture_units[texture_unit].texture_2d = new_texture->texture.handle; |
| 32 | state.Apply(); | 32 | state.Apply(); |
| 33 | 33 | ||
| 34 | // TODO: Need to choose filters that correspond to PICA once register is declared | 34 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(config.config.mag_filter)); |
| 35 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 35 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(config.config.min_filter)); |
| 36 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 37 | 36 | ||
| 38 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(config.config.wrap_s)); | 37 | GLenum wrap_s = PicaToGL::WrapMode(config.config.wrap_s); |
| 39 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(config.config.wrap_t)); | 38 | GLenum wrap_t = PicaToGL::WrapMode(config.config.wrap_t); |
| 39 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); | ||
| 40 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t); | ||
| 41 | |||
| 42 | if (wrap_s == GL_CLAMP_TO_BORDER || wrap_t == GL_CLAMP_TO_BORDER) { | ||
| 43 | auto border_color = PicaToGL::ColorRGBA8((u8*)&config.config.border_color.r); | ||
| 44 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color.data()); | ||
| 45 | } | ||
| 40 | 46 | ||
| 41 | const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format); | 47 | const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format); |
| 42 | 48 | ||
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 3526e16d5..9efc15337 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -147,20 +147,17 @@ void OpenGLState::Apply() { | |||
| 147 | 147 | ||
| 148 | // Textures | 148 | // Textures |
| 149 | for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { | 149 | for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { |
| 150 | if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d) { | 150 | if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d || |
| 151 | texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) { | ||
| 152 | |||
| 151 | glActiveTexture(GL_TEXTURE0 + texture_index); | 153 | glActiveTexture(GL_TEXTURE0 + texture_index); |
| 152 | 154 | ||
| 153 | if (texture_units[texture_index].enabled_2d) { | 155 | if (texture_units[texture_index].enabled_2d) { |
| 154 | glEnable(GL_TEXTURE_2D); | 156 | glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d); |
| 155 | } else { | 157 | } else { |
| 156 | glDisable(GL_TEXTURE_2D); | 158 | glBindTexture(GL_TEXTURE_2D, 0); |
| 157 | } | 159 | } |
| 158 | } | 160 | } |
| 159 | |||
| 160 | if (texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) { | ||
| 161 | glActiveTexture(GL_TEXTURE0 + texture_index); | ||
| 162 | glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d); | ||
| 163 | } | ||
| 164 | } | 161 | } |
| 165 | 162 | ||
| 166 | // Framebuffer | 163 | // Framebuffer |
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index e566f9f7a..3b562da86 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h | |||
| @@ -12,10 +12,37 @@ | |||
| 12 | 12 | ||
| 13 | namespace PicaToGL { | 13 | namespace PicaToGL { |
| 14 | 14 | ||
| 15 | inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) { | ||
| 16 | static const GLenum filter_mode_table[] = { | ||
| 17 | GL_NEAREST, // TextureFilter::Nearest | ||
| 18 | GL_LINEAR // TextureFilter::Linear | ||
| 19 | }; | ||
| 20 | |||
| 21 | // Range check table for input | ||
| 22 | if (mode >= ARRAY_SIZE(filter_mode_table)) { | ||
| 23 | LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); | ||
| 24 | UNREACHABLE(); | ||
| 25 | |||
| 26 | return GL_LINEAR; | ||
| 27 | } | ||
| 28 | |||
| 29 | GLenum gl_mode = filter_mode_table[mode]; | ||
| 30 | |||
| 31 | // Check for dummy values indicating an unknown mode | ||
| 32 | if (gl_mode == 0) { | ||
| 33 | LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); | ||
| 34 | UNIMPLEMENTED(); | ||
| 35 | |||
| 36 | return GL_LINEAR; | ||
| 37 | } | ||
| 38 | |||
| 39 | return gl_mode; | ||
| 40 | } | ||
| 41 | |||
| 15 | inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) { | 42 | inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) { |
| 16 | static const GLenum wrap_mode_table[] = { | 43 | static const GLenum wrap_mode_table[] = { |
| 17 | GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge | 44 | GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge |
| 18 | 0, // Unknown | 45 | GL_CLAMP_TO_BORDER,// WrapMode::ClampToBorder |
| 19 | GL_REPEAT, // WrapMode::Repeat | 46 | GL_REPEAT, // WrapMode::Repeat |
| 20 | GL_MIRRORED_REPEAT // WrapMode::MirroredRepeat | 47 | GL_MIRRORED_REPEAT // WrapMode::MirroredRepeat |
| 21 | }; | 48 | }; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 3399ca123..96e12839a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -2,22 +2,27 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstddef> | ||
| 7 | #include <cstdlib> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/emu_window.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/profiler_reporting.h" | ||
| 13 | |||
| 5 | #include "core/hw/gpu.h" | 14 | #include "core/hw/gpu.h" |
| 6 | #include "core/hw/hw.h" | 15 | #include "core/hw/hw.h" |
| 7 | #include "core/hw/lcd.h" | 16 | #include "core/hw/lcd.h" |
| 8 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 9 | #include "core/settings.h" | 18 | #include "core/settings.h" |
| 10 | 19 | ||
| 11 | #include "common/emu_window.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/profiler_reporting.h" | ||
| 14 | |||
| 15 | #include "video_core/video_core.h" | 20 | #include "video_core/video_core.h" |
| 16 | #include "video_core/renderer_opengl/renderer_opengl.h" | 21 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 17 | #include "video_core/renderer_opengl/gl_shader_util.h" | 22 | #include "video_core/renderer_opengl/gl_shader_util.h" |
| 18 | #include "video_core/renderer_opengl/gl_shaders.h" | 23 | #include "video_core/renderer_opengl/gl_shaders.h" |
| 19 | 24 | ||
| 20 | #include <algorithm> | 25 | #include "video_core/debug_utils/debug_utils.h" |
| 21 | 26 | ||
| 22 | /** | 27 | /** |
| 23 | * Vertex structure that the drawn screen rectangles are composed of. | 28 | * Vertex structure that the drawn screen rectangles are composed of. |
| @@ -126,6 +131,10 @@ void RendererOpenGL::SwapBuffers() { | |||
| 126 | hw_rasterizer->Reset(); | 131 | hw_rasterizer->Reset(); |
| 127 | } | 132 | } |
| 128 | } | 133 | } |
| 134 | |||
| 135 | if (Pica::g_debug_context && Pica::g_debug_context->recorder) { | ||
| 136 | Pica::g_debug_context->recorder->FrameFinished(); | ||
| 137 | } | ||
| 129 | } | 138 | } |
| 130 | 139 | ||
| 131 | /** | 140 | /** |
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index 87006a832..b77503806 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp | |||
| @@ -221,7 +221,7 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 221 | for (int i = 0; i < num_components; ++i) | 221 | for (int i = 0; i < num_components; ++i) |
| 222 | dot = dot + src1[i] * src2[i]; | 222 | dot = dot + src1[i] * src2[i]; |
| 223 | 223 | ||
| 224 | for (int i = 0; i < num_components; ++i) { | 224 | for (int i = 0; i < 4; ++i) { |
| 225 | if (!swizzle.DestComponentEnabled(i)) | 225 | if (!swizzle.DestComponentEnabled(i)) |
| 226 | continue; | 226 | continue; |
| 227 | 227 | ||
| @@ -546,20 +546,18 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 546 | 546 | ||
| 547 | static Common::Profiling::TimingCategory shader_category("Vertex Shader"); | 547 | static Common::Profiling::TimingCategory shader_category("Vertex Shader"); |
| 548 | 548 | ||
| 549 | OutputVertex RunShader(const InputVertex& input, int num_attributes) { | 549 | OutputVertex RunShader(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup) { |
| 550 | Common::Profiling::ScopeTimer timer(shader_category); | 550 | Common::Profiling::ScopeTimer timer(shader_category); |
| 551 | 551 | ||
| 552 | const auto& regs = g_state.regs; | ||
| 553 | const auto& vs = g_state.vs; | ||
| 554 | VertexShaderState state; | 552 | VertexShaderState state; |
| 555 | 553 | ||
| 556 | const u32* main = &vs.program_code[regs.vs_main_offset]; | 554 | const u32* main = &setup.program_code[config.main_offset]; |
| 557 | state.program_counter = (u32*)main; | 555 | state.program_counter = (u32*)main; |
| 558 | state.debug.max_offset = 0; | 556 | state.debug.max_offset = 0; |
| 559 | state.debug.max_opdesc_id = 0; | 557 | state.debug.max_opdesc_id = 0; |
| 560 | 558 | ||
| 561 | // Setup input register table | 559 | // Setup input register table |
| 562 | const auto& attribute_register_map = regs.vs_input_register_map; | 560 | const auto& attribute_register_map = config.input_register_map; |
| 563 | float24 dummy_register; | 561 | float24 dummy_register; |
| 564 | boost::fill(state.input_register_table, &dummy_register); | 562 | boost::fill(state.input_register_table, &dummy_register); |
| 565 | 563 | ||
| @@ -584,16 +582,16 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) { | |||
| 584 | state.conditional_code[1] = false; | 582 | state.conditional_code[1] = false; |
| 585 | 583 | ||
| 586 | ProcessShaderCode(state); | 584 | ProcessShaderCode(state); |
| 587 | DebugUtils::DumpShader(vs.program_code.data(), state.debug.max_offset, vs.swizzle_data.data(), | 585 | DebugUtils::DumpShader(setup.program_code.data(), state.debug.max_offset, setup.swizzle_data.data(), |
| 588 | state.debug.max_opdesc_id, regs.vs_main_offset, | 586 | state.debug.max_opdesc_id, config.main_offset, |
| 589 | regs.vs_output_attributes); | 587 | g_state.regs.vs_output_attributes); // TODO: Don't hardcode VS here |
| 590 | 588 | ||
| 591 | // Setup output data | 589 | // Setup output data |
| 592 | OutputVertex ret; | 590 | OutputVertex ret; |
| 593 | // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to | 591 | // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to |
| 594 | // figure out what those circumstances are and enable the remaining outputs then. | 592 | // figure out what those circumstances are and enable the remaining outputs then. |
| 595 | for (int i = 0; i < 7; ++i) { | 593 | for (int i = 0; i < 7; ++i) { |
| 596 | const auto& output_register_map = regs.vs_output_attributes[i]; | 594 | const auto& output_register_map = g_state.regs.vs_output_attributes[i]; // TODO: Don't hardcode VS here |
| 597 | 595 | ||
| 598 | u32 semantics[4] = { | 596 | u32 semantics[4] = { |
| 599 | output_register_map.map_x, output_register_map.map_y, | 597 | output_register_map.map_x, output_register_map.map_y, |
diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index 7471a6de8..97f9250dd 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h | |||
| @@ -4,11 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <initializer_list> | 7 | #include <type_traits> |
| 8 | 8 | ||
| 9 | #include <common/common_types.h> | 9 | #include "common/vector_math.h" |
| 10 | 10 | ||
| 11 | #include "math.h" | ||
| 12 | #include "pica.h" | 11 | #include "pica.h" |
| 13 | 12 | ||
| 14 | namespace Pica { | 13 | namespace Pica { |
| @@ -66,7 +65,7 @@ struct OutputVertex { | |||
| 66 | static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); | 65 | static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); |
| 67 | static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); | 66 | static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); |
| 68 | 67 | ||
| 69 | OutputVertex RunShader(const InputVertex& input, int num_attributes); | 68 | OutputVertex RunShader(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup); |
| 70 | 69 | ||
| 71 | } // namespace | 70 | } // namespace |
| 72 | 71 | ||
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 3f24df7bd..14b33c9dd 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -4,12 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/emu_window.h" | ||
| 8 | |||
| 9 | #include "renderer_base.h" | ||
| 10 | |||
| 11 | #include <atomic> | 7 | #include <atomic> |
| 12 | 8 | ||
| 9 | class EmuWindow; | ||
| 10 | class RendererBase; | ||
| 11 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 14 | // Video Core namespace | 13 | // Video Core namespace |
| 15 | 14 | ||