diff options
231 files changed, 9592 insertions, 5202 deletions
diff --git a/.gitignore b/.gitignore index 659736ff5..ad8aea5da 100644 --- a/.gitignore +++ b/.gitignore | |||
| @@ -1,6 +1,26 @@ | |||
| 1 | # Build directory | 1 | # Build directory |
| 2 | build/ | 2 | [Bb]uild/ |
| 3 | doc-build/ | 3 | doc-build/ |
| 4 | 4 | ||
| 5 | # Generated source files | 5 | # Generated source files |
| 6 | src/common/scm_rev.cpp | 6 | src/common/scm_rev.cpp |
| 7 | |||
| 8 | # Project/editor files | ||
| 9 | *.swp | ||
| 10 | .idea/ | ||
| 11 | |||
| 12 | # *nix related | ||
| 13 | # Common convention for backup or temporary files | ||
| 14 | *~ | ||
| 15 | |||
| 16 | # OSX global filetypes | ||
| 17 | # Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) | ||
| 18 | .DS_Store | ||
| 19 | .AppleDouble | ||
| 20 | .LSOverride | ||
| 21 | .Spotlight-V100 | ||
| 22 | .Trashes | ||
| 23 | |||
| 24 | # Windows global filetypes | ||
| 25 | Thumbs.db | ||
| 26 | |||
diff --git a/.gitmodules b/.gitmodules index d7201387a..54714e5cd 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | [submodule "externals/inih/inih"] | 1 | [submodule "externals/inih/inih"] |
| 2 | path = externals/inih/inih | 2 | path = externals/inih/inih |
| 3 | url = https://github.com/svn2github/inih | 3 | url = https://github.com/svn2github/inih |
| 4 | [submodule "externals/boost"] | ||
| 5 | path = externals/boost | ||
| 6 | url = https://github.com/citra-emu/ext-boost.git | ||
diff --git a/.travis-build.sh b/.travis-build.sh index 672d282cc..b869b8b8f 100644 --- a/.travis-build.sh +++ b/.travis-build.sh | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | 2 | ||
| 3 | set -e | 3 | set -e |
| 4 | set -x | ||
| 4 | 5 | ||
| 5 | #if OS is linux or is not set | 6 | #if OS is linux or is not set |
| 6 | if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then | 7 | if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then |
| @@ -8,6 +9,7 @@ if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then | |||
| 8 | cmake -DUSE_QT5=OFF .. | 9 | cmake -DUSE_QT5=OFF .. |
| 9 | make -j4 | 10 | make -j4 |
| 10 | elif [ "$TRAVIS_OS_NAME" = osx ]; then | 11 | elif [ "$TRAVIS_OS_NAME" = osx ]; then |
| 12 | export Qt5_DIR=$(brew --prefix)/opt/qt5 | ||
| 11 | mkdir build && cd build | 13 | mkdir build && cd build |
| 12 | cmake .. -GXcode | 14 | cmake .. -GXcode |
| 13 | xcodebuild | 15 | xcodebuild |
diff --git a/.travis-deps.sh b/.travis-deps.sh index b8e8417b2..8e22a6358 100644 --- a/.travis-deps.sh +++ b/.travis-deps.sh | |||
| @@ -1,13 +1,14 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | 2 | ||
| 3 | set -e | 3 | set -e |
| 4 | set -x | ||
| 4 | 5 | ||
| 5 | #if OS is linux or is not set | 6 | #if OS is linux or is not set |
| 6 | if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then | 7 | if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then |
| 7 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y | 8 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y |
| 8 | sudo apt-get -qq update | 9 | sudo apt-get -qq update |
| 9 | sudo apt-get -qq install g++-4.8 xorg-dev libglu1-mesa-dev libxcursor-dev | 10 | sudo apt-get -qq install g++-4.9 xorg-dev libglu1-mesa-dev libxcursor-dev |
| 10 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 | 11 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90 |
| 11 | ( | 12 | ( |
| 12 | git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1 | 13 | git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1 |
| 13 | mkdir glfw/build && cd glfw/build | 14 | mkdir glfw/build && cd glfw/build |
diff --git a/CMakeLists.txt b/CMakeLists.txt index bbe9f76cd..63738b5ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -5,10 +5,13 @@ cmake_minimum_required(VERSION 2.8.11) | |||
| 5 | project(citra) | 5 | project(citra) |
| 6 | 6 | ||
| 7 | if (NOT MSVC) | 7 | if (NOT MSVC) |
| 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes") | 8 | # -std=c++14 is only supported on very new compilers, so use the old c++1y alias instead. |
| 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes") | ||
| 9 | else() | 10 | else() |
| 10 | # Silence deprecation warnings | 11 | # Silence deprecation warnings |
| 11 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) | 12 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) |
| 13 | # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) | ||
| 14 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | ||
| 12 | endif() | 15 | endif() |
| 13 | add_definitions(-DSINGLETHREADED) | 16 | add_definitions(-DSINGLETHREADED) |
| 14 | 17 | ||
| @@ -17,6 +20,14 @@ if (PNG_FOUND) | |||
| 17 | add_definitions(-DHAVE_PNG) | 20 | add_definitions(-DHAVE_PNG) |
| 18 | endif () | 21 | endif () |
| 19 | 22 | ||
| 23 | find_package(Boost) | ||
| 24 | if (Boost_FOUND) | ||
| 25 | include_directories(${Boost_INCLUDE_DIRS}) | ||
| 26 | else() | ||
| 27 | message(STATUS "Boost not found, falling back to externals") | ||
| 28 | include_directories(externals/boost) | ||
| 29 | endif() | ||
| 30 | |||
| 20 | # Include bundled CMake modules | 31 | # Include bundled CMake modules |
| 21 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") | 32 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") |
| 22 | 33 | ||
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb1067c92..c8c8e3884 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
| @@ -24,27 +24,37 @@ Citra is a brand new project, so we have a great opportunity to keep things clea | |||
| 24 | ### Indentation/Whitespace Style | 24 | ### Indentation/Whitespace Style |
| 25 | Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. | 25 | Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. |
| 26 | 26 | ||
| 27 | ### Comments | ||
| 28 | * For regular comments, use C++ style (`//`) comments, even for multi-line ones. | ||
| 29 | * For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`. | ||
| 30 | * For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places. | ||
| 31 | |||
| 27 | ```cpp | 32 | ```cpp |
| 28 | namespace Example { | 33 | namespace Example { |
| 29 | 34 | ||
| 30 | // Namespace contents are not indented | 35 | // Namespace contents are not indented |
| 31 | 36 | ||
| 32 | // Declare globals at the top | 37 | // Declare globals at the top |
| 33 | int g_foo = 0; | 38 | int g_foo = 0; |
| 34 | char* g_some_pointer; // Notice the position of the * | 39 | char* g_some_pointer; // Notice the position of the * |
| 35 | 40 | ||
| 41 | /// A colorful enum. | ||
| 36 | enum SomeEnum { | 42 | enum SomeEnum { |
| 37 | COLOR_RED, | 43 | COLOR_RED, ///< The color of fire. |
| 38 | COLOR_GREEN, | 44 | COLOR_GREEN, ///< The color of grass. |
| 39 | COLOR_BLUE | 45 | COLOR_BLUE ///< Not actually the color of water. |
| 40 | }; | 46 | }; |
| 41 | 47 | ||
| 48 | /** | ||
| 49 | * Very important struct that does a lot of stuff. | ||
| 50 | * Note that the asterisks are indented by one space. | ||
| 51 | */ | ||
| 42 | struct Position { | 52 | struct Position { |
| 43 | int x, y; | 53 | int x, y; |
| 44 | }; | 54 | }; |
| 45 | 55 | ||
| 46 | // Use "typename" rather than "class" here, just to be consistent | 56 | // Use "typename" rather than "class" here, just to be consistent |
| 47 | template | 57 | template |
| 48 | void FooBar() { | 58 | void FooBar() { |
| 49 | int some_array[] = { | 59 | int some_array[] = { |
| 50 | 5, | 60 | 5, |
| @@ -52,29 +62,29 @@ void FooBar() { | |||
| 52 | 7, | 62 | 7, |
| 53 | 42 | 63 | 42 |
| 54 | }; | 64 | }; |
| 55 | 65 | ||
| 56 | if (note == the_space_after_the_if) { | 66 | if (note == the_space_after_the_if) { |
| 57 | CallAfunction(); | 67 | CallAfunction(); |
| 58 | } else { | 68 | } else { |
| 59 | // Use a space after the // when commenting | 69 | // Use a space after the // when commenting |
| 60 | } | 70 | } |
| 61 | 71 | ||
| 62 | // Comment directly above code when possible | 72 | // Comment directly above code when possible |
| 63 | if (some_condition) single_statement(); | 73 | if (some_condition) single_statement(); |
| 64 | 74 | ||
| 65 | // Place a single space after the for loop semicolons | 75 | // Place a single space after the for loop semicolons |
| 66 | for (int i = 0; i != 25; ++i) { | 76 | for (int i = 0; i != 25; ++i) { |
| 67 | // This is how we write loops | 77 | // This is how we write loops |
| 68 | } | 78 | } |
| 69 | 79 | ||
| 70 | DoStuff(this, function, call, takes, up, multiple, | 80 | DoStuff(this, function, call, takes, up, multiple, |
| 71 | lines, like, this); | 81 | lines, like, this); |
| 72 | 82 | ||
| 73 | if (this || condition_takes_up_multiple && | 83 | if (this || condition_takes_up_multiple && |
| 74 | lines && like && this || everything || | 84 | lines && like && this || everything || |
| 75 | alright || then) { | 85 | alright || then) { |
| 76 | } | 86 | } |
| 77 | 87 | ||
| 78 | switch (var) { | 88 | switch (var) { |
| 79 | // No indentation for case label | 89 | // No indentation for case label |
| 80 | case 1: { | 90 | case 1: { |
| @@ -85,18 +95,18 @@ void FooBar() { | |||
| 85 | case 3: | 95 | case 3: |
| 86 | DoSomething(var); | 96 | DoSomething(var); |
| 87 | return; | 97 | return; |
| 88 | 98 | ||
| 89 | default: | 99 | default: |
| 90 | // Yes, even break for the last case | 100 | // Yes, even break for the last case |
| 91 | break; | 101 | break; |
| 92 | } | 102 | } |
| 93 | 103 | ||
| 94 | std::vector | 104 | std::vector |
| 95 | you_can_declare, | 105 | you_can_declare, |
| 96 | a_few, | 106 | a_few, |
| 97 | variables, | 107 | variables, |
| 98 | like_this; | 108 | like_this; |
| 99 | } | 109 | } |
| 100 | 110 | ||
| 101 | } | 111 | } |
| 102 | ``` | 112 | ``` |
| @@ -419,7 +419,7 @@ LOOKUP_CACHE_SIZE = 0 | |||
| 419 | # normally produced when WARNINGS is set to YES. | 419 | # normally produced when WARNINGS is set to YES. |
| 420 | # The default value is: NO. | 420 | # The default value is: NO. |
| 421 | 421 | ||
| 422 | EXTRACT_ALL = NO | 422 | EXTRACT_ALL = YES |
| 423 | 423 | ||
| 424 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will | 424 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will |
| 425 | # be included in the documentation. | 425 | # be included in the documentation. |
| @@ -1,10 +1,12 @@ | |||
| 1 | citra emulator | 1 | Citra Emulator |
| 2 | ============== | 2 | ============== |
| 3 | [](https://travis-ci.org/citra-emu/citra) | 3 | [](https://travis-ci.org/citra-emu/citra) |
| 4 | 4 | ||
| 5 | An experimental open-source Nintendo 3DS emulator/debugger written in C++. Citra is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. At this time, it only emulates a subset of 3DS hardware, and therefore is generally only useful for booting/debugging very simple homebrew demos. Citra is licensed under the GPLv2. 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. | 5 | Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. At this time, it only emulates a subset of 3DS hardware, and therefore is generally only useful for booting/debugging very simple homebrew demos. However, this is changing as more and more progress is being made on running commercial titles, too. |
| 6 | 6 | ||
| 7 | For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/). | 7 | Citra is licensed under the GPLv2. 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 | |||
| 9 | For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra). | ||
| 8 | 10 | ||
| 9 | ### Development | 11 | ### Development |
| 10 | 12 | ||
| @@ -15,3 +17,15 @@ If you want to contribute please take a took at the [Contributor's Guide](CONTRI | |||
| 15 | * __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Windows-Build) | 17 | * __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Windows-Build) |
| 16 | * __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Linux-Build) | 18 | * __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Linux-Build) |
| 17 | * __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/OS-X-Build) | 19 | * __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/OS-X-Build) |
| 20 | |||
| 21 | |||
| 22 | ### Support | ||
| 23 | If you like, you can [donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K899FANUJ2ZXW) - any donation received will go towards things like: | ||
| 24 | * 3DS consoles for developers to explore the hardware | ||
| 25 | * 3DS games for testing | ||
| 26 | * Any equipment required for homebrew | ||
| 27 | * Infrastructure setup | ||
| 28 | * Eventually 3D displays to get proper 3D output working | ||
| 29 | * ... etc ... | ||
| 30 | |||
| 31 | We also more than gladly accept used 3DS consoles, preferrably ones with firmware 4.5 or lower! If you would like to give yours away, don't hesitate to join our IRC channel #citra on [Freenode](http://webchat.freenode.net/?channels=citra) and talk to neobrain or bunnei. Mind you, IRC is slow-paced, so it might be a while until people reply. If you're in a hurry you can just leave contact details in the channel or via private message and we'll get back to you. | ||
diff --git a/externals/boost b/externals/boost new file mode 160000 | |||
| Subproject b060148c08ae87a3a5809c4f48cb26ac667487a | |||
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 6ac5c5dc5..d6e8a4ec7 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp | |||
| @@ -2,9 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <thread> | ||
| 6 | |||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | #include "common/log_manager.h" | 8 | #include "common/logging/text_formatter.h" |
| 9 | #include "common/logging/backend.h" | ||
| 10 | #include "common/logging/filter.h" | ||
| 11 | #include "common/scope_exit.h" | ||
| 7 | 12 | ||
| 13 | #include "core/settings.h" | ||
| 8 | #include "core/system.h" | 14 | #include "core/system.h" |
| 9 | #include "core/core.h" | 15 | #include "core/core.h" |
| 10 | #include "core/loader/loader.h" | 16 | #include "core/loader/loader.h" |
| @@ -14,14 +20,21 @@ | |||
| 14 | 20 | ||
| 15 | /// Application entry point | 21 | /// Application entry point |
| 16 | int __cdecl main(int argc, char **argv) { | 22 | int __cdecl main(int argc, char **argv) { |
| 17 | LogManager::Init(); | 23 | std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); |
| 24 | Log::Filter log_filter(Log::Level::Debug); | ||
| 25 | std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter); | ||
| 26 | SCOPE_EXIT({ | ||
| 27 | logger->Close(); | ||
| 28 | logging_thread.join(); | ||
| 29 | }); | ||
| 18 | 30 | ||
| 19 | if (argc < 2) { | 31 | if (argc < 2) { |
| 20 | ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | 32 | LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); |
| 21 | return -1; | 33 | return -1; |
| 22 | } | 34 | } |
| 23 | 35 | ||
| 24 | Config config; | 36 | Config config; |
| 37 | log_filter.ParseFilterString(Settings::values.log_filter); | ||
| 25 | 38 | ||
| 26 | std::string boot_filename = argv[1]; | 39 | std::string boot_filename = argv[1]; |
| 27 | EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | 40 | EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; |
| @@ -30,7 +43,7 @@ int __cdecl main(int argc, char **argv) { | |||
| 30 | 43 | ||
| 31 | Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); | 44 | Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); |
| 32 | if (Loader::ResultStatus::Success != load_result) { | 45 | if (Loader::ResultStatus::Success != load_result) { |
| 33 | ERROR_LOG(BOOT, "Failed to load ROM (Error %i)!", load_result); | 46 | LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); |
| 34 | return -1; | 47 | return -1; |
| 35 | } | 48 | } |
| 36 | 49 | ||
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index c5ce8a164..92764809e 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -22,21 +22,22 @@ Config::Config() { | |||
| 22 | bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) { | 22 | bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) { |
| 23 | if (config->ParseError() < 0) { | 23 | if (config->ParseError() < 0) { |
| 24 | if (retry) { | 24 | if (retry) { |
| 25 | ERROR_LOG(CONFIG, "Failed to load %s. Creating file from defaults...", location); | 25 | LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location); |
| 26 | FileUtil::CreateFullPath(location); | 26 | FileUtil::CreateFullPath(location); |
| 27 | FileUtil::WriteStringToFile(true, default_contents, location); | 27 | FileUtil::WriteStringToFile(true, default_contents, location); |
| 28 | *config = INIReader(location); // Reopen file | 28 | *config = INIReader(location); // Reopen file |
| 29 | 29 | ||
| 30 | return LoadINI(config, location, default_contents, false); | 30 | return LoadINI(config, location, default_contents, false); |
| 31 | } | 31 | } |
| 32 | ERROR_LOG(CONFIG, "Failed."); | 32 | LOG_ERROR(Config, "Failed."); |
| 33 | return false; | 33 | return false; |
| 34 | } | 34 | } |
| 35 | INFO_LOG(CONFIG, "Successfully loaded %s", location); | 35 | LOG_INFO(Config, "Successfully loaded %s", location); |
| 36 | return true; | 36 | return true; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void Config::ReadControls() { | 39 | void Config::ReadValues() { |
| 40 | // Controls | ||
| 40 | Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A); | 41 | Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A); |
| 41 | Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S); | 42 | Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S); |
| 42 | Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z); | 43 | Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z); |
| @@ -54,22 +55,21 @@ void Config::ReadControls() { | |||
| 54 | Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN); | 55 | Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN); |
| 55 | Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT); | 56 | Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT); |
| 56 | Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT); | 57 | Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT); |
| 57 | } | ||
| 58 | 58 | ||
| 59 | void Config::ReadCore() { | 59 | // Core |
| 60 | Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter); | 60 | Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter); |
| 61 | Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60); | 61 | Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60); |
| 62 | } | ||
| 63 | 62 | ||
| 64 | void Config::ReadData() { | 63 | // Data Storage |
| 65 | Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); | 64 | Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); |
| 65 | |||
| 66 | // Miscellaneous | ||
| 67 | Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); | ||
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | void Config::Reload() { | 70 | void Config::Reload() { |
| 69 | LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file); | 71 | LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file); |
| 70 | ReadControls(); | 72 | ReadValues(); |
| 71 | ReadCore(); | ||
| 72 | ReadData(); | ||
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | Config::~Config() { | 75 | Config::~Config() { |
diff --git a/src/citra/config.h b/src/citra/config.h index 4f6551876..2b46fa8aa 100644 --- a/src/citra/config.h +++ b/src/citra/config.h | |||
| @@ -15,9 +15,7 @@ class Config { | |||
| 15 | std::string glfw_config_loc; | 15 | std::string glfw_config_loc; |
| 16 | 16 | ||
| 17 | bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true); | 17 | bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true); |
| 18 | void ReadControls(); | 18 | void ReadValues(); |
| 19 | void ReadCore(); | ||
| 20 | void ReadData(); | ||
| 21 | public: | 19 | public: |
| 22 | Config(); | 20 | Config(); |
| 23 | ~Config(); | 21 | ~Config(); |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 7352c70c2..7cf543e07 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -28,10 +28,13 @@ pad_sright = | |||
| 28 | 28 | ||
| 29 | [Core] | 29 | [Core] |
| 30 | cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental) | 30 | cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental) |
| 31 | gpu_refresh_rate = ## 60 (default), 1024 or 2048 may work better on the FastInterpreter | 31 | gpu_refresh_rate = ## 60 (default) |
| 32 | 32 | ||
| 33 | [Data Storage] | 33 | [Data Storage] |
| 34 | use_virtual_sd = | 34 | use_virtual_sd = |
| 35 | |||
| 36 | [Miscellaneous] | ||
| 37 | log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||
| 35 | )"; | 38 | )"; |
| 36 | 39 | ||
| 37 | } | 40 | } |
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 0c774bbc5..929e09f43 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <GLFW/glfw3.h> | ||
| 6 | |||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | 8 | ||
| 7 | #include "video_core/video_core.h" | 9 | #include "video_core/video_core.h" |
| @@ -10,22 +12,21 @@ | |||
| 10 | 12 | ||
| 11 | #include "citra/emu_window/emu_window_glfw.h" | 13 | #include "citra/emu_window/emu_window_glfw.h" |
| 12 | 14 | ||
| 15 | EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) { | ||
| 16 | return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win)); | ||
| 17 | } | ||
| 18 | |||
| 13 | /// Called by GLFW when a key event occurs | 19 | /// Called by GLFW when a key event occurs |
| 14 | void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { | 20 | void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { |
| 15 | 21 | ||
| 16 | if (!VideoCore::g_emu_window) { | 22 | int keyboard_id = GetEmuWindow(win)->keyboard_id; |
| 17 | return; | ||
| 18 | } | ||
| 19 | |||
| 20 | int keyboard_id = ((EmuWindow_GLFW*)VideoCore::g_emu_window)->keyboard_id; | ||
| 21 | 23 | ||
| 22 | if (action == GLFW_PRESS) { | 24 | if (action == GLFW_PRESS) { |
| 23 | EmuWindow::KeyPressed({key, keyboard_id}); | 25 | EmuWindow::KeyPressed({key, keyboard_id}); |
| 24 | } | 26 | } else if (action == GLFW_RELEASE) { |
| 25 | |||
| 26 | if (action == GLFW_RELEASE) { | ||
| 27 | EmuWindow::KeyReleased({key, keyboard_id}); | 27 | EmuWindow::KeyReleased({key, keyboard_id}); |
| 28 | } | 28 | } |
| 29 | |||
| 29 | HID_User::PadUpdateComplete(); | 30 | HID_User::PadUpdateComplete(); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| @@ -34,15 +35,36 @@ const bool EmuWindow_GLFW::IsOpen() { | |||
| 34 | return glfwWindowShouldClose(m_render_window) == 0; | 35 | return glfwWindowShouldClose(m_render_window) == 0; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 38 | void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) { | ||
| 39 | _dbg_assert_(Frontend, width > 0); | ||
| 40 | _dbg_assert_(Frontend, height > 0); | ||
| 41 | |||
| 42 | GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||
| 43 | } | ||
| 44 | |||
| 45 | void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) { | ||
| 46 | _dbg_assert_(Frontend, width > 0); | ||
| 47 | _dbg_assert_(Frontend, height > 0); | ||
| 48 | |||
| 49 | // NOTE: GLFW provides no proper way to set a minimal window size. | ||
| 50 | // Hence, we just ignore the corresponding EmuWindow hint. | ||
| 51 | |||
| 52 | GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||
| 53 | } | ||
| 54 | |||
| 37 | /// EmuWindow_GLFW constructor | 55 | /// EmuWindow_GLFW constructor |
| 38 | EmuWindow_GLFW::EmuWindow_GLFW() { | 56 | EmuWindow_GLFW::EmuWindow_GLFW() { |
| 39 | keyboard_id = KeyMap::NewDeviceId(); | 57 | keyboard_id = KeyMap::NewDeviceId(); |
| 40 | 58 | ||
| 41 | ReloadSetKeymaps(); | 59 | ReloadSetKeymaps(); |
| 42 | 60 | ||
| 61 | glfwSetErrorCallback([](int error, const char *desc){ | ||
| 62 | LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc); | ||
| 63 | }); | ||
| 64 | |||
| 43 | // Initialize the window | 65 | // Initialize the window |
| 44 | if(glfwInit() != GL_TRUE) { | 66 | if(glfwInit() != GL_TRUE) { |
| 45 | printf("Failed to initialize GLFW! Exiting..."); | 67 | LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting..."); |
| 46 | exit(1); | 68 | exit(1); |
| 47 | } | 69 | } |
| 48 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | 70 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); |
| @@ -50,19 +72,31 @@ EmuWindow_GLFW::EmuWindow_GLFW() { | |||
| 50 | // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context. | 72 | // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context. |
| 51 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | 73 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); |
| 52 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | 74 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); |
| 53 | |||
| 54 | m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, | ||
| 55 | (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), | ||
| 56 | m_window_title.c_str(), NULL, NULL); | ||
| 57 | 75 | ||
| 58 | if (m_render_window == NULL) { | 76 | std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |
| 59 | printf("Failed to create GLFW window! Exiting..."); | 77 | m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, |
| 78 | (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), | ||
| 79 | window_title.c_str(), nullptr, nullptr); | ||
| 80 | |||
| 81 | if (m_render_window == nullptr) { | ||
| 82 | LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting..."); | ||
| 60 | exit(1); | 83 | exit(1); |
| 61 | } | 84 | } |
| 62 | 85 | ||
| 63 | // Setup callbacks | ||
| 64 | glfwSetWindowUserPointer(m_render_window, this); | 86 | glfwSetWindowUserPointer(m_render_window, this); |
| 87 | |||
| 88 | // Notify base interface about window state | ||
| 89 | int width, height; | ||
| 90 | glfwGetFramebufferSize(m_render_window, &width, &height); | ||
| 91 | OnFramebufferResizeEvent(m_render_window, width, height); | ||
| 92 | |||
| 93 | glfwGetWindowSize(m_render_window, &width, &height); | ||
| 94 | OnClientAreaResizeEvent(m_render_window, width, height); | ||
| 95 | |||
| 96 | // Setup callbacks | ||
| 65 | glfwSetKeyCallback(m_render_window, OnKeyEvent); | 97 | glfwSetKeyCallback(m_render_window, OnKeyEvent); |
| 98 | glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); | ||
| 99 | glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); | ||
| 66 | 100 | ||
| 67 | DoneCurrent(); | 101 | DoneCurrent(); |
| 68 | } | 102 | } |
| @@ -89,7 +123,7 @@ void EmuWindow_GLFW::MakeCurrent() { | |||
| 89 | 123 | ||
| 90 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 124 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread |
| 91 | void EmuWindow_GLFW::DoneCurrent() { | 125 | void EmuWindow_GLFW::DoneCurrent() { |
| 92 | glfwMakeContextCurrent(NULL); | 126 | glfwMakeContextCurrent(nullptr); |
| 93 | } | 127 | } |
| 94 | 128 | ||
| 95 | void EmuWindow_GLFW::ReloadSetKeymaps() { | 129 | void EmuWindow_GLFW::ReloadSetKeymaps() { |
| @@ -110,3 +144,15 @@ void EmuWindow_GLFW::ReloadSetKeymaps() { | |||
| 110 | KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP); | 144 | KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP); |
| 111 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); | 145 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); |
| 112 | } | 146 | } |
| 147 | |||
| 148 | void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||
| 149 | std::pair<int,int> current_size; | ||
| 150 | glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second); | ||
| 151 | |||
| 152 | _dbg_assert_(Frontend, (int)minimal_size.first > 0 && (int)minimal_size.second > 0); | ||
| 153 | int new_width = std::max(current_size.first, (int)minimal_size.first); | ||
| 154 | int new_height = std::max(current_size.second, (int)minimal_size.second); | ||
| 155 | |||
| 156 | if (current_size != std::make_pair(new_width, new_height)) | ||
| 157 | glfwSetWindowSize(m_render_window, new_width, new_height); | ||
| 158 | } | ||
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 7c3072145..5b04e87bb 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h | |||
| @@ -4,10 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <GLFW/glfw3.h> | ||
| 8 | |||
| 9 | #include "common/emu_window.h" | 7 | #include "common/emu_window.h" |
| 10 | 8 | ||
| 9 | struct GLFWwindow; | ||
| 10 | |||
| 11 | class EmuWindow_GLFW : public EmuWindow { | 11 | class EmuWindow_GLFW : public EmuWindow { |
| 12 | public: | 12 | public: |
| 13 | EmuWindow_GLFW(); | 13 | EmuWindow_GLFW(); |
| @@ -16,12 +16,12 @@ public: | |||
| 16 | /// Swap buffers to display the next frame | 16 | /// Swap buffers to display the next frame |
| 17 | void SwapBuffers() override; | 17 | void SwapBuffers() override; |
| 18 | 18 | ||
| 19 | /// Polls window events | 19 | /// Polls window events |
| 20 | void PollEvents() override; | 20 | void PollEvents() override; |
| 21 | 21 | ||
| 22 | /// Makes the graphics context current for the caller thread | 22 | /// Makes the graphics context current for the caller thread |
| 23 | void MakeCurrent() override; | 23 | void MakeCurrent() override; |
| 24 | 24 | ||
| 25 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 25 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread |
| 26 | void DoneCurrent() override; | 26 | void DoneCurrent() override; |
| 27 | 27 | ||
| @@ -30,9 +30,17 @@ public: | |||
| 30 | /// Whether the window is still open, and a close request hasn't yet been sent | 30 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 31 | const bool IsOpen(); | 31 | const bool IsOpen(); |
| 32 | 32 | ||
| 33 | static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height); | ||
| 34 | |||
| 35 | static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height); | ||
| 36 | |||
| 33 | void ReloadSetKeymaps() override; | 37 | void ReloadSetKeymaps() override; |
| 34 | 38 | ||
| 35 | private: | 39 | private: |
| 40 | void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; | ||
| 41 | |||
| 42 | static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win); | ||
| 43 | |||
| 36 | GLFWwindow* m_render_window; ///< Internal GLFW render window | 44 | GLFWwindow* m_render_window; ///< Internal GLFW render window |
| 37 | 45 | ||
| 38 | /// Device id of keyboard for use with KeyMap | 46 | /// Device id of keyboard for use with KeyMap |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 98a48a69a..90e5c6aa6 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -8,9 +8,12 @@ set(SRCS | |||
| 8 | debugger/callstack.cpp | 8 | debugger/callstack.cpp |
| 9 | debugger/disassembler.cpp | 9 | debugger/disassembler.cpp |
| 10 | debugger/graphics.cpp | 10 | debugger/graphics.cpp |
| 11 | debugger/graphics_breakpoints.cpp | ||
| 11 | debugger/graphics_cmdlists.cpp | 12 | debugger/graphics_cmdlists.cpp |
| 13 | debugger/graphics_framebuffer.cpp | ||
| 12 | debugger/ramview.cpp | 14 | debugger/ramview.cpp |
| 13 | debugger/registers.cpp | 15 | debugger/registers.cpp |
| 16 | util/spinbox.cpp | ||
| 14 | bootmanager.cpp | 17 | bootmanager.cpp |
| 15 | hotkeys.cpp | 18 | hotkeys.cpp |
| 16 | main.cpp | 19 | main.cpp |
| @@ -23,9 +26,13 @@ set(HEADERS | |||
| 23 | debugger/callstack.hxx | 26 | debugger/callstack.hxx |
| 24 | debugger/disassembler.hxx | 27 | debugger/disassembler.hxx |
| 25 | debugger/graphics.hxx | 28 | debugger/graphics.hxx |
| 29 | debugger/graphics_breakpoints.hxx | ||
| 30 | debugger/graphics_breakpoints_p.hxx | ||
| 26 | debugger/graphics_cmdlists.hxx | 31 | debugger/graphics_cmdlists.hxx |
| 32 | debugger/graphics_framebuffer.hxx | ||
| 27 | debugger/ramview.hxx | 33 | debugger/ramview.hxx |
| 28 | debugger/registers.hxx | 34 | debugger/registers.hxx |
| 35 | util/spinbox.hxx | ||
| 29 | bootmanager.hxx | 36 | bootmanager.hxx |
| 30 | hotkeys.hxx | 37 | hotkeys.hxx |
| 31 | main.hxx | 38 | main.hxx |
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 8f3799351..6d08d6afc 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -2,12 +2,20 @@ | |||
| 2 | #include <QKeyEvent> | 2 | #include <QKeyEvent> |
| 3 | #include <QApplication> | 3 | #include <QApplication> |
| 4 | 4 | ||
| 5 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||
| 6 | // Required for screen DPI information | ||
| 7 | #include <QScreen> | ||
| 8 | #include <QWindow> | ||
| 9 | #endif | ||
| 10 | |||
| 5 | #include "common/common.h" | 11 | #include "common/common.h" |
| 6 | #include "bootmanager.hxx" | 12 | #include "bootmanager.hxx" |
| 7 | 13 | ||
| 8 | #include "core/core.h" | 14 | #include "core/core.h" |
| 9 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 10 | 16 | ||
| 17 | #include "video_core/debug_utils/debug_utils.h" | ||
| 18 | |||
| 11 | #include "video_core/video_core.h" | 19 | #include "video_core/video_core.h" |
| 12 | 20 | ||
| 13 | #include "citra_qt/version.h" | 21 | #include "citra_qt/version.h" |
| @@ -17,7 +25,7 @@ | |||
| 17 | #define APP_TITLE APP_NAME " " APP_VERSION | 25 | #define APP_TITLE APP_NAME " " APP_VERSION |
| 18 | #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" | 26 | #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" |
| 19 | 27 | ||
| 20 | EmuThread::EmuThread(GRenderWindow* render_window) : | 28 | EmuThread::EmuThread(GRenderWindow* render_window) : |
| 21 | filename(""), exec_cpu_step(false), cpu_running(false), | 29 | filename(""), exec_cpu_step(false), cpu_running(false), |
| 22 | stop_run(false), render_window(render_window) | 30 | stop_run(false), render_window(render_window) |
| 23 | { | 31 | { |
| @@ -33,19 +41,16 @@ void EmuThread::run() | |||
| 33 | stop_run = false; | 41 | stop_run = false; |
| 34 | while (!stop_run) | 42 | while (!stop_run) |
| 35 | { | 43 | { |
| 36 | for (int tight_loop = 0; tight_loop < 10000; ++tight_loop) | 44 | if (cpu_running) |
| 37 | { | 45 | { |
| 38 | if (cpu_running || exec_cpu_step) | 46 | Core::RunLoop(); |
| 39 | { | 47 | } |
| 40 | if (exec_cpu_step) | 48 | else if (exec_cpu_step) |
| 41 | exec_cpu_step = false; | 49 | { |
| 42 | 50 | exec_cpu_step = false; | |
| 43 | Core::SingleStep(); | 51 | Core::SingleStep(); |
| 44 | if (!cpu_running) { | 52 | emit CPUStepped(); |
| 45 | emit CPUStepped(); | 53 | yieldCurrentThread(); |
| 46 | yieldCurrentThread(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | 54 | } |
| 50 | } | 55 | } |
| 51 | render_window->moveContext(); | 56 | render_window->moveContext(); |
| @@ -57,26 +62,33 @@ void EmuThread::Stop() | |||
| 57 | { | 62 | { |
| 58 | if (!isRunning()) | 63 | if (!isRunning()) |
| 59 | { | 64 | { |
| 60 | INFO_LOG(MASTER_LOG, "EmuThread::Stop called while emu thread wasn't running, returning..."); | 65 | LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning..."); |
| 61 | return; | 66 | return; |
| 62 | } | 67 | } |
| 63 | stop_run = true; | 68 | stop_run = true; |
| 64 | 69 | ||
| 70 | // Release emu threads from any breakpoints, so that this doesn't hang forever. | ||
| 71 | Pica::g_debug_context->ClearBreakpoints(); | ||
| 72 | |||
| 65 | //core::g_state = core::SYS_DIE; | 73 | //core::g_state = core::SYS_DIE; |
| 66 | 74 | ||
| 67 | wait(500); | 75 | // TODO: Waiting here is just a bad workaround for retarded shutdown logic. |
| 76 | wait(1000); | ||
| 68 | if (isRunning()) | 77 | if (isRunning()) |
| 69 | { | 78 | { |
| 70 | WARN_LOG(MASTER_LOG, "EmuThread still running, terminating..."); | 79 | LOG_WARNING(Frontend, "EmuThread still running, terminating..."); |
| 71 | quit(); | 80 | quit(); |
| 72 | wait(1000); | 81 | |
| 82 | // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam | ||
| 83 | // queued... This should be fixed. | ||
| 84 | wait(50000); | ||
| 73 | if (isRunning()) | 85 | if (isRunning()) |
| 74 | { | 86 | { |
| 75 | WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here..."); | 87 | LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here..."); |
| 76 | terminate(); | 88 | terminate(); |
| 77 | } | 89 | } |
| 78 | } | 90 | } |
| 79 | INFO_LOG(MASTER_LOG, "EmuThread stopped"); | 91 | LOG_INFO(Frontend, "EmuThread stopped"); |
| 80 | } | 92 | } |
| 81 | 93 | ||
| 82 | 94 | ||
| @@ -85,20 +97,20 @@ void EmuThread::Stop() | |||
| 85 | class GGLWidgetInternal : public QGLWidget | 97 | class GGLWidgetInternal : public QGLWidget |
| 86 | { | 98 | { |
| 87 | public: | 99 | public: |
| 88 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent) | 100 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) |
| 89 | { | 101 | : QGLWidget(fmt, parent), parent(parent) { |
| 90 | parent_ = parent; | ||
| 91 | } | 102 | } |
| 92 | 103 | ||
| 93 | void paintEvent(QPaintEvent* ev) override | 104 | void paintEvent(QPaintEvent* ev) override { |
| 94 | { | ||
| 95 | } | 105 | } |
| 106 | |||
| 96 | void resizeEvent(QResizeEvent* ev) override { | 107 | void resizeEvent(QResizeEvent* ev) override { |
| 97 | parent_->SetClientAreaWidth(size().width()); | 108 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); |
| 98 | parent_->SetClientAreaHeight(size().height()); | 109 | parent->OnFramebufferSizeChanged(); |
| 99 | } | 110 | } |
| 111 | |||
| 100 | private: | 112 | private: |
| 101 | GRenderWindow* parent_; | 113 | GRenderWindow* parent; |
| 102 | }; | 114 | }; |
| 103 | 115 | ||
| 104 | EmuThread& GRenderWindow::GetEmuThread() | 116 | EmuThread& GRenderWindow::GetEmuThread() |
| @@ -108,6 +120,9 @@ EmuThread& GRenderWindow::GetEmuThread() | |||
| 108 | 120 | ||
| 109 | GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) | 121 | GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) |
| 110 | { | 122 | { |
| 123 | std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||
| 124 | setWindowTitle(QString::fromStdString(window_title)); | ||
| 125 | |||
| 111 | keyboard_id = KeyMap::NewDeviceId(); | 126 | keyboard_id = KeyMap::NewDeviceId(); |
| 112 | ReloadSetKeymaps(); | 127 | ReloadSetKeymaps(); |
| 113 | 128 | ||
| @@ -117,16 +132,25 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this | |||
| 117 | fmt.setProfile(QGLFormat::CoreProfile); | 132 | fmt.setProfile(QGLFormat::CoreProfile); |
| 118 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X | 133 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X |
| 119 | fmt.setOption(QGL::NoDeprecatedFunctions); | 134 | fmt.setOption(QGL::NoDeprecatedFunctions); |
| 120 | 135 | ||
| 121 | child = new GGLWidgetInternal(fmt, this); | 136 | child = new GGLWidgetInternal(fmt, this); |
| 122 | QBoxLayout* layout = new QHBoxLayout(this); | 137 | QBoxLayout* layout = new QHBoxLayout(this); |
| 123 | resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); | 138 | resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); |
| 124 | layout->addWidget(child); | 139 | layout->addWidget(child); |
| 125 | layout->setMargin(0); | 140 | layout->setMargin(0); |
| 126 | setLayout(layout); | 141 | setLayout(layout); |
| 127 | QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); | 142 | connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); |
| 143 | |||
| 144 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||
| 145 | |||
| 146 | OnFramebufferSizeChanged(); | ||
| 147 | NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height())); | ||
| 128 | 148 | ||
| 129 | BackupGeometry(); | 149 | BackupGeometry(); |
| 150 | |||
| 151 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||
| 152 | connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged())); | ||
| 153 | #endif | ||
| 130 | } | 154 | } |
| 131 | 155 | ||
| 132 | void GRenderWindow::moveContext() | 156 | void GRenderWindow::moveContext() |
| @@ -169,14 +193,28 @@ void GRenderWindow::DoneCurrent() | |||
| 169 | } | 193 | } |
| 170 | 194 | ||
| 171 | void GRenderWindow::PollEvents() { | 195 | void GRenderWindow::PollEvents() { |
| 172 | // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title | 196 | } |
| 173 | // from the main thread, but this should probably be in an event handler... | 197 | |
| 174 | /* | 198 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 175 | static char title[128]; | 199 | // |
| 176 | sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(), | 200 | // Older versions get the window size (density independent pixels), |
| 177 | video_core::g_renderer->current_fps()); | 201 | // and hence, do not support DPI scaling ("retina" displays). |
| 178 | setWindowTitle(title); | 202 | // The result will be a viewport that is smaller than the extent of the window. |
| 179 | */ | 203 | void GRenderWindow::OnFramebufferSizeChanged() |
| 204 | { | ||
| 205 | // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size | ||
| 206 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||
| 207 | // windowHandle() might not be accessible until the window is displayed to screen. | ||
| 208 | auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0; | ||
| 209 | |||
| 210 | unsigned width = child->QPaintDevice::width() * pixel_ratio; | ||
| 211 | unsigned height = child->QPaintDevice::height() * pixel_ratio; | ||
| 212 | #else | ||
| 213 | unsigned width = child->QPaintDevice::width(); | ||
| 214 | unsigned height = child->QPaintDevice::height(); | ||
| 215 | #endif | ||
| 216 | |||
| 217 | NotifyFramebufferSizeChanged(std::make_pair(width, height)); | ||
| 180 | } | 218 | } |
| 181 | 219 | ||
| 182 | void GRenderWindow::BackupGeometry() | 220 | void GRenderWindow::BackupGeometry() |
| @@ -201,7 +239,7 @@ QByteArray GRenderWindow::saveGeometry() | |||
| 201 | { | 239 | { |
| 202 | // If we are a top-level widget, store the current geometry | 240 | // If we are a top-level widget, store the current geometry |
| 203 | // otherwise, store the last backup | 241 | // otherwise, store the last backup |
| 204 | if (parent() == NULL) | 242 | if (parent() == nullptr) |
| 205 | return ((QGLWidget*)this)->saveGeometry(); | 243 | return ((QGLWidget*)this)->saveGeometry(); |
| 206 | else | 244 | else |
| 207 | return geometry; | 245 | return geometry; |
| @@ -239,3 +277,11 @@ void GRenderWindow::ReloadSetKeymaps() | |||
| 239 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); | 277 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); |
| 240 | } | 278 | } |
| 241 | 279 | ||
| 280 | void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) | ||
| 281 | { | ||
| 282 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | ||
| 283 | } | ||
| 284 | |||
| 285 | void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||
| 286 | setMinimumSize(minimal_size.first, minimal_size.second); | ||
| 287 | } | ||
diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx index f8afc403e..5f69f15ea 100644 --- a/src/citra_qt/bootmanager.hxx +++ b/src/citra_qt/bootmanager.hxx | |||
| @@ -1,12 +1,16 @@ | |||
| 1 | #include <atomic> | ||
| 2 | |||
| 1 | #include <QThread> | 3 | #include <QThread> |
| 2 | #include <QGLWidget> | 4 | #include <QGLWidget> |
| 3 | #include <atomic> | 5 | |
| 4 | #include "common/common.h" | 6 | #include "common/common.h" |
| 5 | #include "common/emu_window.h" | 7 | #include "common/emu_window.h" |
| 6 | 8 | ||
| 7 | class GRenderWindow; | 9 | class QScreen; |
| 8 | class QKeyEvent; | 10 | class QKeyEvent; |
| 9 | 11 | ||
| 12 | class GRenderWindow; | ||
| 13 | |||
| 10 | class EmuThread : public QThread | 14 | class EmuThread : public QThread |
| 11 | { | 15 | { |
| 12 | Q_OBJECT | 16 | Q_OBJECT |
| @@ -14,7 +18,7 @@ class EmuThread : public QThread | |||
| 14 | public: | 18 | public: |
| 15 | /** | 19 | /** |
| 16 | * Set image filename | 20 | * Set image filename |
| 17 | * | 21 | * |
| 18 | * @param filename | 22 | * @param filename |
| 19 | * @warning Only call when not running! | 23 | * @warning Only call when not running! |
| 20 | */ | 24 | */ |
| @@ -74,7 +78,7 @@ private: | |||
| 74 | signals: | 78 | signals: |
| 75 | /** | 79 | /** |
| 76 | * Emitted when CPU when we've finished processing a single Gekko instruction | 80 | * Emitted when CPU when we've finished processing a single Gekko instruction |
| 77 | * | 81 | * |
| 78 | * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) | 82 | * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) |
| 79 | * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) | 83 | * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) |
| 80 | */ | 84 | */ |
| @@ -100,7 +104,7 @@ public: | |||
| 100 | void BackupGeometry(); | 104 | void BackupGeometry(); |
| 101 | void RestoreGeometry(); | 105 | void RestoreGeometry(); |
| 102 | void restoreGeometry(const QByteArray& geometry); // overridden | 106 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 103 | QByteArray saveGeometry(); // overridden | 107 | QByteArray saveGeometry(); // overridden |
| 104 | 108 | ||
| 105 | EmuThread& GetEmuThread(); | 109 | EmuThread& GetEmuThread(); |
| 106 | 110 | ||
| @@ -109,10 +113,16 @@ public: | |||
| 109 | 113 | ||
| 110 | void ReloadSetKeymaps() override; | 114 | void ReloadSetKeymaps() override; |
| 111 | 115 | ||
| 116 | void OnClientAreaResized(unsigned width, unsigned height); | ||
| 117 | |||
| 118 | void OnFramebufferSizeChanged(); | ||
| 119 | |||
| 112 | public slots: | 120 | public slots: |
| 113 | void moveContext(); | 121 | void moveContext(); // overridden |
| 114 | 122 | ||
| 115 | private: | 123 | private: |
| 124 | void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; | ||
| 125 | |||
| 116 | QGLWidget* child; | 126 | QGLWidget* child; |
| 117 | 127 | ||
| 118 | EmuThread emu_thread; | 128 | EmuThread emu_thread; |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 63d396439..0ae6b8b2d 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -21,7 +21,7 @@ Config::Config() { | |||
| 21 | Reload(); | 21 | Reload(); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | void Config::ReadControls() { | 24 | void Config::ReadValues() { |
| 25 | qt_config->beginGroup("Controls"); | 25 | qt_config->beginGroup("Controls"); |
| 26 | Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt(); | 26 | Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt(); |
| 27 | Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt(); | 27 | Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt(); |
| @@ -41,9 +41,22 @@ void Config::ReadControls() { | |||
| 41 | Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt(); | 41 | Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt(); |
| 42 | Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt(); | 42 | Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt(); |
| 43 | qt_config->endGroup(); | 43 | qt_config->endGroup(); |
| 44 | |||
| 45 | qt_config->beginGroup("Core"); | ||
| 46 | Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt(); | ||
| 47 | Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt(); | ||
| 48 | qt_config->endGroup(); | ||
| 49 | |||
| 50 | qt_config->beginGroup("Data Storage"); | ||
| 51 | Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); | ||
| 52 | qt_config->endGroup(); | ||
| 53 | |||
| 54 | qt_config->beginGroup("Miscellaneous"); | ||
| 55 | Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); | ||
| 56 | qt_config->endGroup(); | ||
| 44 | } | 57 | } |
| 45 | 58 | ||
| 46 | void Config::SaveControls() { | 59 | void Config::SaveValues() { |
| 47 | qt_config->beginGroup("Controls"); | 60 | qt_config->beginGroup("Controls"); |
| 48 | qt_config->setValue("pad_a", Settings::values.pad_a_key); | 61 | qt_config->setValue("pad_a", Settings::values.pad_a_key); |
| 49 | qt_config->setValue("pad_b", Settings::values.pad_b_key); | 62 | qt_config->setValue("pad_b", Settings::values.pad_b_key); |
| @@ -63,44 +76,27 @@ void Config::SaveControls() { | |||
| 63 | qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key); | 76 | qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key); |
| 64 | qt_config->setValue("pad_sright", Settings::values.pad_sright_key); | 77 | qt_config->setValue("pad_sright", Settings::values.pad_sright_key); |
| 65 | qt_config->endGroup(); | 78 | qt_config->endGroup(); |
| 66 | } | ||
| 67 | |||
| 68 | void Config::ReadCore() { | ||
| 69 | qt_config->beginGroup("Core"); | ||
| 70 | Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt(); | ||
| 71 | Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt(); | ||
| 72 | qt_config->endGroup(); | ||
| 73 | } | ||
| 74 | 79 | ||
| 75 | void Config::SaveCore() { | ||
| 76 | qt_config->beginGroup("Core"); | 80 | qt_config->beginGroup("Core"); |
| 77 | qt_config->setValue("cpu_core", Settings::values.cpu_core); | 81 | qt_config->setValue("cpu_core", Settings::values.cpu_core); |
| 78 | qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate); | 82 | qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate); |
| 79 | qt_config->endGroup(); | 83 | qt_config->endGroup(); |
| 80 | } | ||
| 81 | 84 | ||
| 82 | void Config::ReadData() { | ||
| 83 | qt_config->beginGroup("Data Storage"); | 85 | qt_config->beginGroup("Data Storage"); |
| 84 | Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); | 86 | qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); |
| 85 | qt_config->endGroup(); | 87 | qt_config->endGroup(); |
| 86 | } | ||
| 87 | 88 | ||
| 88 | void Config::SaveData() { | 89 | qt_config->beginGroup("Miscellaneous"); |
| 89 | qt_config->beginGroup("Data Storage"); | 90 | qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); |
| 90 | qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); | ||
| 91 | qt_config->endGroup(); | 91 | qt_config->endGroup(); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | void Config::Reload() { | 94 | void Config::Reload() { |
| 95 | ReadControls(); | 95 | ReadValues(); |
| 96 | ReadCore(); | ||
| 97 | ReadData(); | ||
| 98 | } | 96 | } |
| 99 | 97 | ||
| 100 | void Config::Save() { | 98 | void Config::Save() { |
| 101 | SaveControls(); | 99 | SaveValues(); |
| 102 | SaveCore(); | ||
| 103 | SaveData(); | ||
| 104 | } | 100 | } |
| 105 | 101 | ||
| 106 | Config::~Config() { | 102 | Config::~Config() { |
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 782c26287..4c95d0cb9 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h | |||
| @@ -12,12 +12,8 @@ class Config { | |||
| 12 | QSettings* qt_config; | 12 | QSettings* qt_config; |
| 13 | std::string qt_config_loc; | 13 | std::string qt_config_loc; |
| 14 | 14 | ||
| 15 | void ReadControls(); | 15 | void ReadValues(); |
| 16 | void SaveControls(); | 16 | void SaveValues(); |
| 17 | void ReadCore(); | ||
| 18 | void SaveCore(); | ||
| 19 | void ReadData(); | ||
| 20 | void SaveData(); | ||
| 21 | public: | 17 | public: |
| 22 | Config(); | 18 | Config(); |
| 23 | ~Config(); | 19 | ~Config(); |
diff --git a/src/citra_qt/config/controller_config_util.cpp b/src/citra_qt/config/controller_config_util.cpp index c5426570b..aee3f8616 100644 --- a/src/citra_qt/config/controller_config_util.cpp +++ b/src/citra_qt/config/controller_config_util.cpp | |||
| @@ -48,7 +48,7 @@ void GKeyConfigButton::OnActivePortChanged(const common::Config::ControllerPort& | |||
| 48 | else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control"); | 48 | else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control"); |
| 49 | else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt"); | 49 | else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt"); |
| 50 | else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta"); | 50 | else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta"); |
| 51 | setText(text); | 51 | setText(text); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void GKeyConfigButton::OnClicked() | 54 | void GKeyConfigButton::OnClicked() |
| @@ -118,4 +118,4 @@ GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Cont | |||
| 118 | setLayout(layout); | 118 | setLayout(layout); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | */ \ No newline at end of file | 121 | */ |
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index 77fb0c9ed..895851be3 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp | |||
| @@ -28,7 +28,7 @@ void CallstackWidget::OnCPUStepped() | |||
| 28 | 28 | ||
| 29 | u32 sp = app_core->GetReg(13); //stack pointer | 29 | u32 sp = app_core->GetReg(13); //stack pointer |
| 30 | u32 addr, ret_addr, call_addr, func_addr; | 30 | u32 addr, ret_addr, call_addr, func_addr; |
| 31 | 31 | ||
| 32 | int counter = 0; | 32 | int counter = 0; |
| 33 | for (int addr = 0x10000000; addr >= sp; addr -= 4) | 33 | for (int addr = 0x10000000; addr >= sp; addr -= 4) |
| 34 | { | 34 | { |
| @@ -55,7 +55,7 @@ void CallstackWidget::OnCPUStepped() | |||
| 55 | callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); | 55 | callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); |
| 56 | callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); | 56 | callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); |
| 57 | callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); | 57 | callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); |
| 58 | 58 | ||
| 59 | name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; | 59 | name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; |
| 60 | callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) | 60 | callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) |
| 61 | .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); | 61 | .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); |
| @@ -63,4 +63,4 @@ void CallstackWidget::OnCPUStepped() | |||
| 63 | counter++; | 63 | counter++; |
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | } \ No newline at end of file | 66 | } |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp new file mode 100644 index 000000000..53394b6e6 --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QMetaType> | ||
| 6 | #include <QPushButton> | ||
| 7 | #include <QTreeWidget> | ||
| 8 | #include <QVBoxLayout> | ||
| 9 | #include <QLabel> | ||
| 10 | |||
| 11 | #include "graphics_breakpoints.hxx" | ||
| 12 | #include "graphics_breakpoints_p.hxx" | ||
| 13 | |||
| 14 | BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | ||
| 15 | : QAbstractListModel(parent), context_weak(debug_context), | ||
| 16 | at_breakpoint(debug_context->at_breakpoint), | ||
| 17 | active_breakpoint(debug_context->active_breakpoint) | ||
| 18 | { | ||
| 19 | |||
| 20 | } | ||
| 21 | |||
| 22 | int BreakPointModel::columnCount(const QModelIndex& parent) const | ||
| 23 | { | ||
| 24 | return 2; | ||
| 25 | } | ||
| 26 | |||
| 27 | int BreakPointModel::rowCount(const QModelIndex& parent) const | ||
| 28 | { | ||
| 29 | return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||
| 30 | } | ||
| 31 | |||
| 32 | QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||
| 33 | { | ||
| 34 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||
| 35 | |||
| 36 | switch (role) { | ||
| 37 | case Qt::DisplayRole: | ||
| 38 | { | ||
| 39 | switch (index.column()) { | ||
| 40 | case 0: | ||
| 41 | { | ||
| 42 | std::map<Pica::DebugContext::Event, QString> map; | ||
| 43 | map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")}); | ||
| 44 | map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")}); | ||
| 45 | map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")}); | ||
| 46 | map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}); | ||
| 47 | |||
| 48 | _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | ||
| 49 | |||
| 50 | return map[event]; | ||
| 51 | } | ||
| 52 | |||
| 53 | case 1: | ||
| 54 | return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); | ||
| 55 | |||
| 56 | default: | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | |||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 63 | case Qt::BackgroundRole: | ||
| 64 | { | ||
| 65 | if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | ||
| 66 | return QBrush(QColor(0xE0, 0xE0, 0x10)); | ||
| 67 | } | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | |||
| 71 | case Role_IsEnabled: | ||
| 72 | { | ||
| 73 | auto context = context_weak.lock(); | ||
| 74 | return context && context->breakpoints[event].enabled; | ||
| 75 | } | ||
| 76 | |||
| 77 | default: | ||
| 78 | break; | ||
| 79 | } | ||
| 80 | return QVariant(); | ||
| 81 | } | ||
| 82 | |||
| 83 | QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const | ||
| 84 | { | ||
| 85 | switch(role) { | ||
| 86 | case Qt::DisplayRole: | ||
| 87 | { | ||
| 88 | if (section == 0) { | ||
| 89 | return tr("Event"); | ||
| 90 | } else if (section == 1) { | ||
| 91 | return tr("Status"); | ||
| 92 | } | ||
| 93 | |||
| 94 | break; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | return QVariant(); | ||
| 99 | } | ||
| 100 | |||
| 101 | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||
| 102 | { | ||
| 103 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||
| 104 | |||
| 105 | switch (role) { | ||
| 106 | case Role_IsEnabled: | ||
| 107 | { | ||
| 108 | auto context = context_weak.lock(); | ||
| 109 | if (!context) | ||
| 110 | return false; | ||
| 111 | |||
| 112 | context->breakpoints[event].enabled = value.toBool(); | ||
| 113 | QModelIndex changed_index = createIndex(index.row(), 1); | ||
| 114 | emit dataChanged(changed_index, changed_index); | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | return false; | ||
| 120 | } | ||
| 121 | |||
| 122 | |||
| 123 | void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | ||
| 124 | { | ||
| 125 | auto context = context_weak.lock(); | ||
| 126 | if (!context) | ||
| 127 | return; | ||
| 128 | |||
| 129 | active_breakpoint = context->active_breakpoint; | ||
| 130 | at_breakpoint = context->at_breakpoint; | ||
| 131 | emit dataChanged(createIndex(static_cast<int>(event), 0), | ||
| 132 | createIndex(static_cast<int>(event), 1)); | ||
| 133 | } | ||
| 134 | |||
| 135 | void BreakPointModel::OnResumed() | ||
| 136 | { | ||
| 137 | auto context = context_weak.lock(); | ||
| 138 | if (!context) | ||
| 139 | return; | ||
| 140 | |||
| 141 | at_breakpoint = context->at_breakpoint; | ||
| 142 | emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | ||
| 143 | createIndex(static_cast<int>(active_breakpoint), 1)); | ||
| 144 | active_breakpoint = context->active_breakpoint; | ||
| 145 | } | ||
| 146 | |||
| 147 | |||
| 148 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 149 | QWidget* parent) | ||
| 150 | : QDockWidget(tr("Pica Breakpoints"), parent), | ||
| 151 | Pica::DebugContext::BreakPointObserver(debug_context) | ||
| 152 | { | ||
| 153 | setObjectName("PicaBreakPointsWidget"); | ||
| 154 | |||
| 155 | status_text = new QLabel(tr("Emulation running")); | ||
| 156 | resume_button = new QPushButton(tr("Resume")); | ||
| 157 | resume_button->setEnabled(false); | ||
| 158 | |||
| 159 | breakpoint_model = new BreakPointModel(debug_context, this); | ||
| 160 | breakpoint_list = new QTreeView; | ||
| 161 | breakpoint_list->setModel(breakpoint_model); | ||
| 162 | |||
| 163 | toggle_breakpoint_button = new QPushButton(tr("Enable")); | ||
| 164 | toggle_breakpoint_button->setEnabled(false); | ||
| 165 | |||
| 166 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||
| 167 | |||
| 168 | connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | ||
| 169 | |||
| 170 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 171 | this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 172 | Qt::BlockingQueuedConnection); | ||
| 173 | connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||
| 174 | |||
| 175 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 176 | breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), | ||
| 177 | Qt::BlockingQueuedConnection); | ||
| 178 | connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | ||
| 179 | |||
| 180 | connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), | ||
| 181 | breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); | ||
| 182 | |||
| 183 | connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), | ||
| 184 | this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); | ||
| 185 | |||
| 186 | connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); | ||
| 187 | |||
| 188 | QWidget* main_widget = new QWidget; | ||
| 189 | auto main_layout = new QVBoxLayout; | ||
| 190 | { | ||
| 191 | auto sub_layout = new QHBoxLayout; | ||
| 192 | sub_layout->addWidget(status_text); | ||
| 193 | sub_layout->addWidget(resume_button); | ||
| 194 | main_layout->addLayout(sub_layout); | ||
| 195 | } | ||
| 196 | main_layout->addWidget(breakpoint_list); | ||
| 197 | main_layout->addWidget(toggle_breakpoint_button); | ||
| 198 | main_widget->setLayout(main_layout); | ||
| 199 | |||
| 200 | setWidget(main_widget); | ||
| 201 | } | ||
| 202 | |||
| 203 | void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | ||
| 204 | { | ||
| 205 | // Process in GUI thread | ||
| 206 | emit BreakPointHit(event, data); | ||
| 207 | } | ||
| 208 | |||
| 209 | void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 210 | { | ||
| 211 | status_text->setText(tr("Emulation halted at breakpoint")); | ||
| 212 | resume_button->setEnabled(true); | ||
| 213 | } | ||
| 214 | |||
| 215 | void GraphicsBreakPointsWidget::OnPicaResume() | ||
| 216 | { | ||
| 217 | // Process in GUI thread | ||
| 218 | emit Resumed(); | ||
| 219 | } | ||
| 220 | |||
| 221 | void GraphicsBreakPointsWidget::OnResumed() | ||
| 222 | { | ||
| 223 | status_text->setText(tr("Emulation running")); | ||
| 224 | resume_button->setEnabled(false); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GraphicsBreakPointsWidget::OnResumeRequested() | ||
| 228 | { | ||
| 229 | if (auto context = context_weak.lock()) | ||
| 230 | context->Resume(); | ||
| 231 | } | ||
| 232 | |||
| 233 | void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) | ||
| 234 | { | ||
| 235 | if (!index.isValid()) { | ||
| 236 | toggle_breakpoint_button->setEnabled(false); | ||
| 237 | return; | ||
| 238 | } | ||
| 239 | |||
| 240 | toggle_breakpoint_button->setEnabled(true); | ||
| 241 | UpdateToggleBreakpointButton(index); | ||
| 242 | } | ||
| 243 | |||
| 244 | void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() | ||
| 245 | { | ||
| 246 | QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); | ||
| 247 | bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); | ||
| 248 | |||
| 249 | breakpoint_model->setData(index, new_state, | ||
| 250 | BreakPointModel::Role_IsEnabled); | ||
| 251 | UpdateToggleBreakpointButton(index); | ||
| 252 | } | ||
| 253 | |||
| 254 | void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) | ||
| 255 | { | ||
| 256 | if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { | ||
| 257 | toggle_breakpoint_button->setText(tr("Disable")); | ||
| 258 | } else { | ||
| 259 | toggle_breakpoint_button->setText(tr("Enable")); | ||
| 260 | } | ||
| 261 | } | ||
diff --git a/src/citra_qt/debugger/graphics_breakpoints.hxx b/src/citra_qt/debugger/graphics_breakpoints.hxx new file mode 100644 index 000000000..2142c6fa1 --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoints.hxx | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include <QAbstractListModel> | ||
| 10 | #include <QDockWidget> | ||
| 11 | |||
| 12 | #include "video_core/debug_utils/debug_utils.h" | ||
| 13 | |||
| 14 | class QLabel; | ||
| 15 | class QPushButton; | ||
| 16 | class QTreeView; | ||
| 17 | |||
| 18 | class BreakPointModel; | ||
| 19 | |||
| 20 | class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||
| 21 | Q_OBJECT | ||
| 22 | |||
| 23 | using Event = Pica::DebugContext::Event; | ||
| 24 | |||
| 25 | public: | ||
| 26 | GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 27 | QWidget* parent = nullptr); | ||
| 28 | |||
| 29 | void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 30 | void OnPicaResume() override; | ||
| 31 | |||
| 32 | public slots: | ||
| 33 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 34 | void OnResumeRequested(); | ||
| 35 | void OnResumed(); | ||
| 36 | void OnBreakpointSelectionChanged(const QModelIndex&); | ||
| 37 | void OnToggleBreakpointEnabled(); | ||
| 38 | |||
| 39 | signals: | ||
| 40 | void Resumed(); | ||
| 41 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 42 | void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||
| 43 | |||
| 44 | private: | ||
| 45 | void UpdateToggleBreakpointButton(const QModelIndex& index); | ||
| 46 | |||
| 47 | QLabel* status_text; | ||
| 48 | QPushButton* resume_button; | ||
| 49 | QPushButton* toggle_breakpoint_button; | ||
| 50 | |||
| 51 | BreakPointModel* breakpoint_model; | ||
| 52 | QTreeView* breakpoint_list; | ||
| 53 | }; | ||
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.hxx b/src/citra_qt/debugger/graphics_breakpoints_p.hxx new file mode 100644 index 000000000..bf5daf73d --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoints_p.hxx | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include <QAbstractListModel> | ||
| 10 | |||
| 11 | #include "video_core/debug_utils/debug_utils.h" | ||
| 12 | |||
| 13 | class BreakPointModel : public QAbstractListModel { | ||
| 14 | Q_OBJECT | ||
| 15 | |||
| 16 | public: | ||
| 17 | enum { | ||
| 18 | Role_IsEnabled = Qt::UserRole, | ||
| 19 | }; | ||
| 20 | |||
| 21 | BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent); | ||
| 22 | |||
| 23 | int columnCount(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; | ||
| 26 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||
| 27 | |||
| 28 | bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); | ||
| 29 | |||
| 30 | public slots: | ||
| 31 | void OnBreakPointHit(Pica::DebugContext::Event event); | ||
| 32 | void OnResumed(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | std::weak_ptr<Pica::DebugContext> context_weak; | ||
| 36 | bool at_breakpoint; | ||
| 37 | Pica::DebugContext::Event active_breakpoint; | ||
| 38 | }; | ||
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 71dd166cd..7f97cf143 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -2,30 +2,171 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QLabel> | ||
| 5 | #include <QListView> | 6 | #include <QListView> |
| 7 | #include <QMainWindow> | ||
| 6 | #include <QPushButton> | 8 | #include <QPushButton> |
| 7 | #include <QVBoxLayout> | 9 | #include <QVBoxLayout> |
| 8 | #include <QTreeView> | 10 | #include <QTreeView> |
| 11 | #include <QSpinBox> | ||
| 12 | #include <QComboBox> | ||
| 13 | |||
| 14 | #include "video_core/pica.h" | ||
| 15 | #include "video_core/math.h" | ||
| 16 | |||
| 17 | #include "video_core/debug_utils/debug_utils.h" | ||
| 9 | 18 | ||
| 10 | #include "graphics_cmdlists.hxx" | 19 | #include "graphics_cmdlists.hxx" |
| 11 | 20 | ||
| 12 | GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) | 21 | #include "util/spinbox.hxx" |
| 13 | { | 22 | |
| 23 | QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | ||
| 24 | QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); | ||
| 25 | for (int y = 0; y < info.height; ++y) { | ||
| 26 | for (int x = 0; x < info.width; ++x) { | ||
| 27 | Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info); | ||
| 28 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | return decoded_image; | ||
| 33 | } | ||
| 34 | |||
| 35 | class TextureInfoWidget : public QWidget { | ||
| 36 | public: | ||
| 37 | TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) { | ||
| 38 | QLabel* image_widget = new QLabel; | ||
| 39 | QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); | ||
| 40 | image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); | ||
| 41 | image_widget->setPixmap(image_pixmap); | ||
| 42 | |||
| 43 | QVBoxLayout* layout = new QVBoxLayout; | ||
| 44 | layout->addWidget(image_widget); | ||
| 45 | setLayout(layout); | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 49 | TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) | ||
| 50 | : QDockWidget(tr("Texture 0x%1").arg(info.address, 8, 16, QLatin1Char('0'))), | ||
| 51 | info(info) { | ||
| 52 | |||
| 53 | QWidget* main_widget = new QWidget; | ||
| 54 | |||
| 55 | QLabel* image_widget = new QLabel; | ||
| 56 | |||
| 57 | connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&))); | ||
| 58 | |||
| 59 | CSpinBox* phys_address_spinbox = new CSpinBox; | ||
| 60 | phys_address_spinbox->SetBase(16); | ||
| 61 | phys_address_spinbox->SetRange(0, 0xFFFFFFFF); | ||
| 62 | phys_address_spinbox->SetPrefix("0x"); | ||
| 63 | phys_address_spinbox->SetValue(info.address); | ||
| 64 | connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64))); | ||
| 65 | |||
| 66 | QComboBox* format_choice = new QComboBox; | ||
| 67 | format_choice->addItem(tr("RGBA8")); | ||
| 68 | format_choice->addItem(tr("RGB8")); | ||
| 69 | format_choice->addItem(tr("RGBA5551")); | ||
| 70 | format_choice->addItem(tr("RGB565")); | ||
| 71 | format_choice->addItem(tr("RGBA4")); | ||
| 72 | format_choice->setCurrentIndex(static_cast<int>(info.format)); | ||
| 73 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); | ||
| 74 | |||
| 75 | QSpinBox* width_spinbox = new QSpinBox; | ||
| 76 | width_spinbox->setMaximum(65535); | ||
| 77 | width_spinbox->setValue(info.width); | ||
| 78 | connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int))); | ||
| 79 | |||
| 80 | QSpinBox* height_spinbox = new QSpinBox; | ||
| 81 | height_spinbox->setMaximum(65535); | ||
| 82 | height_spinbox->setValue(info.height); | ||
| 83 | connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int))); | ||
| 84 | |||
| 85 | QSpinBox* stride_spinbox = new QSpinBox; | ||
| 86 | stride_spinbox->setMaximum(65535 * 4); | ||
| 87 | stride_spinbox->setValue(info.stride); | ||
| 88 | connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int))); | ||
| 89 | |||
| 90 | QVBoxLayout* main_layout = new QVBoxLayout; | ||
| 91 | main_layout->addWidget(image_widget); | ||
| 92 | |||
| 93 | { | ||
| 94 | QHBoxLayout* sub_layout = new QHBoxLayout; | ||
| 95 | sub_layout->addWidget(new QLabel(tr("Source Address:"))); | ||
| 96 | sub_layout->addWidget(phys_address_spinbox); | ||
| 97 | main_layout->addLayout(sub_layout); | ||
| 98 | } | ||
| 99 | |||
| 100 | { | ||
| 101 | QHBoxLayout* sub_layout = new QHBoxLayout; | ||
| 102 | sub_layout->addWidget(new QLabel(tr("Format"))); | ||
| 103 | sub_layout->addWidget(format_choice); | ||
| 104 | main_layout->addLayout(sub_layout); | ||
| 105 | } | ||
| 106 | |||
| 107 | { | ||
| 108 | QHBoxLayout* sub_layout = new QHBoxLayout; | ||
| 109 | sub_layout->addWidget(new QLabel(tr("Width:"))); | ||
| 110 | sub_layout->addWidget(width_spinbox); | ||
| 111 | sub_layout->addStretch(); | ||
| 112 | sub_layout->addWidget(new QLabel(tr("Height:"))); | ||
| 113 | sub_layout->addWidget(height_spinbox); | ||
| 114 | sub_layout->addStretch(); | ||
| 115 | sub_layout->addWidget(new QLabel(tr("Stride:"))); | ||
| 116 | sub_layout->addWidget(stride_spinbox); | ||
| 117 | main_layout->addLayout(sub_layout); | ||
| 118 | } | ||
| 119 | |||
| 120 | main_widget->setLayout(main_layout); | ||
| 121 | |||
| 122 | emit UpdatePixmap(ReloadPixmap()); | ||
| 123 | |||
| 124 | setWidget(main_widget); | ||
| 125 | } | ||
| 126 | |||
| 127 | void TextureInfoDockWidget::OnAddressChanged(qint64 value) { | ||
| 128 | info.address = value; | ||
| 129 | emit UpdatePixmap(ReloadPixmap()); | ||
| 130 | } | ||
| 131 | |||
| 132 | void TextureInfoDockWidget::OnFormatChanged(int value) { | ||
| 133 | info.format = static_cast<Pica::Regs::TextureFormat>(value); | ||
| 134 | emit UpdatePixmap(ReloadPixmap()); | ||
| 135 | } | ||
| 136 | |||
| 137 | void TextureInfoDockWidget::OnWidthChanged(int value) { | ||
| 138 | info.width = value; | ||
| 139 | emit UpdatePixmap(ReloadPixmap()); | ||
| 140 | } | ||
| 141 | |||
| 142 | void TextureInfoDockWidget::OnHeightChanged(int value) { | ||
| 143 | info.height = value; | ||
| 144 | emit UpdatePixmap(ReloadPixmap()); | ||
| 145 | } | ||
| 14 | 146 | ||
| 147 | void TextureInfoDockWidget::OnStrideChanged(int value) { | ||
| 148 | info.stride = value; | ||
| 149 | emit UpdatePixmap(ReloadPixmap()); | ||
| 15 | } | 150 | } |
| 16 | 151 | ||
| 17 | int GPUCommandListModel::rowCount(const QModelIndex& parent) const | 152 | QPixmap TextureInfoDockWidget::ReloadPixmap() const { |
| 18 | { | 153 | u8* src = Memory::GetPointer(info.address); |
| 154 | return QPixmap::fromImage(LoadTexture(src, info)); | ||
| 155 | } | ||
| 156 | |||
| 157 | GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { | ||
| 158 | |||
| 159 | } | ||
| 160 | |||
| 161 | int GPUCommandListModel::rowCount(const QModelIndex& parent) const { | ||
| 19 | return pica_trace.writes.size(); | 162 | return pica_trace.writes.size(); |
| 20 | } | 163 | } |
| 21 | 164 | ||
| 22 | int GPUCommandListModel::columnCount(const QModelIndex& parent) const | 165 | int GPUCommandListModel::columnCount(const QModelIndex& parent) const { |
| 23 | { | ||
| 24 | return 2; | 166 | return 2; |
| 25 | } | 167 | } |
| 26 | 168 | ||
| 27 | QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | 169 | QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { |
| 28 | { | ||
| 29 | if (!index.isValid()) | 170 | if (!index.isValid()) |
| 30 | return QVariant(); | 171 | return QVariant(); |
| 31 | 172 | ||
| @@ -36,21 +177,39 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | |||
| 36 | if (role == Qt::DisplayRole) { | 177 | if (role == Qt::DisplayRole) { |
| 37 | QString content; | 178 | QString content; |
| 38 | if (index.column() == 0) { | 179 | if (index.column() == 0) { |
| 39 | content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); | 180 | QString content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); |
| 40 | content.append(" "); | 181 | content.append(" "); |
| 182 | return content; | ||
| 41 | } else if (index.column() == 1) { | 183 | } else if (index.column() == 1) { |
| 42 | content.append(QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0'))); | 184 | QString content = QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0')); |
| 43 | content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0'))); | 185 | content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0'))); |
| 186 | return content; | ||
| 44 | } | 187 | } |
| 188 | } else if (role == CommandIdRole) { | ||
| 189 | return QVariant::fromValue<int>(cmd.cmd_id.Value()); | ||
| 190 | } | ||
| 45 | 191 | ||
| 46 | return QVariant(content); | 192 | return QVariant(); |
| 193 | } | ||
| 194 | |||
| 195 | QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const { | ||
| 196 | switch(role) { | ||
| 197 | case Qt::DisplayRole: | ||
| 198 | { | ||
| 199 | if (section == 0) { | ||
| 200 | return tr("Command Name"); | ||
| 201 | } else if (section == 1) { | ||
| 202 | return tr("Data"); | ||
| 203 | } | ||
| 204 | |||
| 205 | break; | ||
| 206 | } | ||
| 47 | } | 207 | } |
| 48 | 208 | ||
| 49 | return QVariant(); | 209 | return QVariant(); |
| 50 | } | 210 | } |
| 51 | 211 | ||
| 52 | void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) | 212 | void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) { |
| 53 | { | ||
| 54 | beginResetModel(); | 213 | beginResetModel(); |
| 55 | 214 | ||
| 56 | pica_trace = trace; | 215 | pica_trace = trace; |
| @@ -58,38 +217,82 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | |||
| 58 | endResetModel(); | 217 | endResetModel(); |
| 59 | } | 218 | } |
| 60 | 219 | ||
| 220 | #define COMMAND_IN_RANGE(cmd_id, reg_name) \ | ||
| 221 | (cmd_id >= PICA_REG_INDEX(reg_name) && \ | ||
| 222 | cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::registers.reg_name)) / 4) | ||
| 223 | |||
| 224 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | ||
| 225 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | ||
| 226 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 227 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | ||
| 228 | Pica::registers.texture0_format); | ||
| 229 | |||
| 230 | // TODO: Instead, emit a signal here to be caught by the main window widget. | ||
| 231 | auto main_window = static_cast<QMainWindow*>(parent()); | ||
| 232 | main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window)); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | ||
| 237 | QWidget* new_info_widget; | ||
| 238 | |||
| 239 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | ||
| 240 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 241 | u8* src = Memory::GetPointer(Pica::registers.texture0.GetPhysicalAddress()); | ||
| 242 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | ||
| 243 | Pica::registers.texture0_format); | ||
| 244 | new_info_widget = new TextureInfoWidget(src, info); | ||
| 245 | } else { | ||
| 246 | new_info_widget = new QWidget; | ||
| 247 | } | ||
| 248 | |||
| 249 | widget()->layout()->removeWidget(command_info_widget); | ||
| 250 | delete command_info_widget; | ||
| 251 | widget()->layout()->addWidget(new_info_widget); | ||
| 252 | command_info_widget = new_info_widget; | ||
| 253 | } | ||
| 254 | #undef COMMAND_IN_RANGE | ||
| 61 | 255 | ||
| 62 | GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) | 256 | GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) { |
| 63 | { | 257 | setObjectName("Pica Command List"); |
| 64 | GPUCommandListModel* model = new GPUCommandListModel(this); | 258 | GPUCommandListModel* model = new GPUCommandListModel(this); |
| 65 | 259 | ||
| 66 | QWidget* main_widget = new QWidget; | 260 | QWidget* main_widget = new QWidget; |
| 67 | 261 | ||
| 68 | QTreeView* list_widget = new QTreeView; | 262 | list_widget = new QTreeView; |
| 69 | list_widget->setModel(model); | 263 | list_widget->setModel(model); |
| 70 | list_widget->setFont(QFont("monospace")); | 264 | list_widget->setFont(QFont("monospace")); |
| 71 | list_widget->setRootIsDecorated(false); | 265 | list_widget->setRootIsDecorated(false); |
| 72 | 266 | ||
| 73 | QPushButton* toggle_tracing = new QPushButton(tr("Start Tracing")); | 267 | connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), |
| 268 | this, SLOT(SetCommandInfo(const QModelIndex&))); | ||
| 269 | connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), | ||
| 270 | this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); | ||
| 271 | |||
| 272 | toggle_tracing = new QPushButton(tr("Start Tracing")); | ||
| 74 | 273 | ||
| 75 | connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); | 274 | connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); |
| 76 | connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), | 275 | connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), |
| 77 | model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); | 276 | model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); |
| 78 | 277 | ||
| 278 | command_info_widget = new QWidget; | ||
| 279 | |||
| 79 | QVBoxLayout* main_layout = new QVBoxLayout; | 280 | QVBoxLayout* main_layout = new QVBoxLayout; |
| 80 | main_layout->addWidget(list_widget); | 281 | main_layout->addWidget(list_widget); |
| 81 | main_layout->addWidget(toggle_tracing); | 282 | main_layout->addWidget(toggle_tracing); |
| 283 | main_layout->addWidget(command_info_widget); | ||
| 82 | main_widget->setLayout(main_layout); | 284 | main_widget->setLayout(main_layout); |
| 83 | 285 | ||
| 84 | setWidget(main_widget); | 286 | setWidget(main_widget); |
| 85 | } | 287 | } |
| 86 | 288 | ||
| 87 | void GPUCommandListWidget::OnToggleTracing() | 289 | void GPUCommandListWidget::OnToggleTracing() { |
| 88 | { | ||
| 89 | if (!Pica::DebugUtils::IsPicaTracing()) { | 290 | if (!Pica::DebugUtils::IsPicaTracing()) { |
| 90 | Pica::DebugUtils::StartPicaTracing(); | 291 | Pica::DebugUtils::StartPicaTracing(); |
| 292 | toggle_tracing->setText(tr("Finish Tracing")); | ||
| 91 | } else { | 293 | } else { |
| 92 | pica_trace = Pica::DebugUtils::FinishPicaTracing(); | 294 | pica_trace = Pica::DebugUtils::FinishPicaTracing(); |
| 93 | emit TracingFinished(*pica_trace); | 295 | emit TracingFinished(*pica_trace); |
| 296 | toggle_tracing->setText(tr("Start Tracing")); | ||
| 94 | } | 297 | } |
| 95 | } | 298 | } |
diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx index 1523e724f..a459bba64 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.hxx +++ b/src/citra_qt/debugger/graphics_cmdlists.hxx | |||
| @@ -10,16 +10,24 @@ | |||
| 10 | #include "video_core/gpu_debugger.h" | 10 | #include "video_core/gpu_debugger.h" |
| 11 | #include "video_core/debug_utils/debug_utils.h" | 11 | #include "video_core/debug_utils/debug_utils.h" |
| 12 | 12 | ||
| 13 | class QPushButton; | ||
| 14 | class QTreeView; | ||
| 15 | |||
| 13 | class GPUCommandListModel : public QAbstractListModel | 16 | class GPUCommandListModel : public QAbstractListModel |
| 14 | { | 17 | { |
| 15 | Q_OBJECT | 18 | Q_OBJECT |
| 16 | 19 | ||
| 17 | public: | 20 | public: |
| 21 | enum { | ||
| 22 | CommandIdRole = Qt::UserRole, | ||
| 23 | }; | ||
| 24 | |||
| 18 | GPUCommandListModel(QObject* parent); | 25 | GPUCommandListModel(QObject* parent); |
| 19 | 26 | ||
| 20 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; | 27 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; |
| 21 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; | 28 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
| 22 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | 29 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |
| 30 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||
| 23 | 31 | ||
| 24 | public slots: | 32 | public slots: |
| 25 | void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); | 33 | void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); |
| @@ -37,10 +45,39 @@ public: | |||
| 37 | 45 | ||
| 38 | public slots: | 46 | public slots: |
| 39 | void OnToggleTracing(); | 47 | void OnToggleTracing(); |
| 48 | void OnCommandDoubleClicked(const QModelIndex&); | ||
| 49 | |||
| 50 | void SetCommandInfo(const QModelIndex&); | ||
| 40 | 51 | ||
| 41 | signals: | 52 | signals: |
| 42 | void TracingFinished(const Pica::DebugUtils::PicaTrace&); | 53 | void TracingFinished(const Pica::DebugUtils::PicaTrace&); |
| 43 | 54 | ||
| 44 | private: | 55 | private: |
| 45 | std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace; | 56 | std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace; |
| 57 | |||
| 58 | QTreeView* list_widget; | ||
| 59 | QWidget* command_info_widget; | ||
| 60 | QPushButton* toggle_tracing; | ||
| 61 | }; | ||
| 62 | |||
| 63 | class TextureInfoDockWidget : public QDockWidget { | ||
| 64 | Q_OBJECT | ||
| 65 | |||
| 66 | public: | ||
| 67 | TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr); | ||
| 68 | |||
| 69 | signals: | ||
| 70 | void UpdatePixmap(const QPixmap& pixmap); | ||
| 71 | |||
| 72 | private slots: | ||
| 73 | void OnAddressChanged(qint64 value); | ||
| 74 | void OnFormatChanged(int value); | ||
| 75 | void OnWidthChanged(int value); | ||
| 76 | void OnHeightChanged(int value); | ||
| 77 | void OnStrideChanged(int value); | ||
| 78 | |||
| 79 | private: | ||
| 80 | QPixmap ReloadPixmap() const; | ||
| 81 | |||
| 82 | Pica::DebugUtils::TextureInfo info; | ||
| 46 | }; | 83 | }; |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp new file mode 100644 index 000000000..ac47f298d --- /dev/null +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QBoxLayout> | ||
| 6 | #include <QComboBox> | ||
| 7 | #include <QDebug> | ||
| 8 | #include <QLabel> | ||
| 9 | #include <QMetaType> | ||
| 10 | #include <QPushButton> | ||
| 11 | #include <QSpinBox> | ||
| 12 | |||
| 13 | #include "video_core/pica.h" | ||
| 14 | |||
| 15 | #include "graphics_framebuffer.hxx" | ||
| 16 | |||
| 17 | #include "util/spinbox.hxx" | ||
| 18 | |||
| 19 | BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 20 | const QString& title, QWidget* parent) | ||
| 21 | : QDockWidget(title, parent), BreakPointObserver(debug_context) | ||
| 22 | { | ||
| 23 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||
| 24 | |||
| 25 | connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||
| 26 | |||
| 27 | // NOTE: This signal is emitted from a non-GUI thread, but connect() takes | ||
| 28 | // care of delaying its handling to the GUI thread. | ||
| 29 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 30 | this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 31 | Qt::BlockingQueuedConnection); | ||
| 32 | } | ||
| 33 | |||
| 34 | void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 35 | { | ||
| 36 | emit BreakPointHit(event, data); | ||
| 37 | } | ||
| 38 | |||
| 39 | void BreakPointObserverDock::OnPicaResume() | ||
| 40 | { | ||
| 41 | emit Resumed(); | ||
| 42 | } | ||
| 43 | |||
| 44 | |||
| 45 | GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 46 | QWidget* parent) | ||
| 47 | : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), | ||
| 48 | framebuffer_source(Source::PicaTarget) | ||
| 49 | { | ||
| 50 | setObjectName("PicaFramebuffer"); | ||
| 51 | |||
| 52 | framebuffer_source_list = new QComboBox; | ||
| 53 | framebuffer_source_list->addItem(tr("Active Render Target")); | ||
| 54 | framebuffer_source_list->addItem(tr("Custom")); | ||
| 55 | framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source)); | ||
| 56 | |||
| 57 | framebuffer_address_control = new CSpinBox; | ||
| 58 | framebuffer_address_control->SetBase(16); | ||
| 59 | framebuffer_address_control->SetRange(0, 0xFFFFFFFF); | ||
| 60 | framebuffer_address_control->SetPrefix("0x"); | ||
| 61 | |||
| 62 | framebuffer_width_control = new QSpinBox; | ||
| 63 | framebuffer_width_control->setMinimum(1); | ||
| 64 | framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum | ||
| 65 | |||
| 66 | framebuffer_height_control = new QSpinBox; | ||
| 67 | framebuffer_height_control->setMinimum(1); | ||
| 68 | framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum | ||
| 69 | |||
| 70 | framebuffer_format_control = new QComboBox; | ||
| 71 | framebuffer_format_control->addItem(tr("RGBA8")); | ||
| 72 | framebuffer_format_control->addItem(tr("RGB8")); | ||
| 73 | framebuffer_format_control->addItem(tr("RGBA5551")); | ||
| 74 | framebuffer_format_control->addItem(tr("RGB565")); | ||
| 75 | framebuffer_format_control->addItem(tr("RGBA4")); | ||
| 76 | |||
| 77 | // TODO: This QLabel should shrink the image to the available space rather than just expanding... | ||
| 78 | framebuffer_picture_label = new QLabel; | ||
| 79 | |||
| 80 | auto enlarge_button = new QPushButton(tr("Enlarge")); | ||
| 81 | |||
| 82 | // Connections | ||
| 83 | connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); | ||
| 84 | connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int))); | ||
| 85 | connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64))); | ||
| 86 | connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int))); | ||
| 87 | connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int))); | ||
| 88 | connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int))); | ||
| 89 | |||
| 90 | auto main_widget = new QWidget; | ||
| 91 | auto main_layout = new QVBoxLayout; | ||
| 92 | { | ||
| 93 | auto sub_layout = new QHBoxLayout; | ||
| 94 | sub_layout->addWidget(new QLabel(tr("Source:"))); | ||
| 95 | sub_layout->addWidget(framebuffer_source_list); | ||
| 96 | main_layout->addLayout(sub_layout); | ||
| 97 | } | ||
| 98 | { | ||
| 99 | auto sub_layout = new QHBoxLayout; | ||
| 100 | sub_layout->addWidget(new QLabel(tr("Virtual Address:"))); | ||
| 101 | sub_layout->addWidget(framebuffer_address_control); | ||
| 102 | main_layout->addLayout(sub_layout); | ||
| 103 | } | ||
| 104 | { | ||
| 105 | auto sub_layout = new QHBoxLayout; | ||
| 106 | sub_layout->addWidget(new QLabel(tr("Width:"))); | ||
| 107 | sub_layout->addWidget(framebuffer_width_control); | ||
| 108 | main_layout->addLayout(sub_layout); | ||
| 109 | } | ||
| 110 | { | ||
| 111 | auto sub_layout = new QHBoxLayout; | ||
| 112 | sub_layout->addWidget(new QLabel(tr("Height:"))); | ||
| 113 | sub_layout->addWidget(framebuffer_height_control); | ||
| 114 | main_layout->addLayout(sub_layout); | ||
| 115 | } | ||
| 116 | { | ||
| 117 | auto sub_layout = new QHBoxLayout; | ||
| 118 | sub_layout->addWidget(new QLabel(tr("Format:"))); | ||
| 119 | sub_layout->addWidget(framebuffer_format_control); | ||
| 120 | main_layout->addLayout(sub_layout); | ||
| 121 | } | ||
| 122 | main_layout->addWidget(framebuffer_picture_label); | ||
| 123 | main_layout->addWidget(enlarge_button); | ||
| 124 | main_widget->setLayout(main_layout); | ||
| 125 | setWidget(main_widget); | ||
| 126 | |||
| 127 | // Load current data - TODO: Make sure this works when emulation is not running | ||
| 128 | emit Update(); | ||
| 129 | widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint | ||
| 130 | } | ||
| 131 | |||
| 132 | void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 133 | { | ||
| 134 | emit Update(); | ||
| 135 | widget()->setEnabled(true); | ||
| 136 | } | ||
| 137 | |||
| 138 | void GraphicsFramebufferWidget::OnResumed() | ||
| 139 | { | ||
| 140 | widget()->setEnabled(false); | ||
| 141 | } | ||
| 142 | |||
| 143 | void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value) | ||
| 144 | { | ||
| 145 | framebuffer_source = static_cast<Source>(new_value); | ||
| 146 | emit Update(); | ||
| 147 | } | ||
| 148 | |||
| 149 | void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value) | ||
| 150 | { | ||
| 151 | if (framebuffer_address != new_value) { | ||
| 152 | framebuffer_address = static_cast<unsigned>(new_value); | ||
| 153 | |||
| 154 | framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 155 | emit Update(); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value) | ||
| 160 | { | ||
| 161 | if (framebuffer_width != new_value) { | ||
| 162 | framebuffer_width = new_value; | ||
| 163 | |||
| 164 | framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 165 | emit Update(); | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value) | ||
| 170 | { | ||
| 171 | if (framebuffer_height != new_value) { | ||
| 172 | framebuffer_height = new_value; | ||
| 173 | |||
| 174 | framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 175 | emit Update(); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value) | ||
| 180 | { | ||
| 181 | if (framebuffer_format != static_cast<Format>(new_value)) { | ||
| 182 | framebuffer_format = static_cast<Format>(new_value); | ||
| 183 | |||
| 184 | framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 185 | emit Update(); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | void GraphicsFramebufferWidget::OnUpdate() | ||
| 190 | { | ||
| 191 | QPixmap pixmap; | ||
| 192 | |||
| 193 | switch (framebuffer_source) { | ||
| 194 | case Source::PicaTarget: | ||
| 195 | { | ||
| 196 | // TODO: Store a reference to the registers in the debug context instead of accessing them directly... | ||
| 197 | |||
| 198 | auto framebuffer = Pica::registers.framebuffer; | ||
| 199 | using Framebuffer = decltype(framebuffer); | ||
| 200 | |||
| 201 | framebuffer_address = framebuffer.GetColorBufferAddress(); | ||
| 202 | framebuffer_width = framebuffer.GetWidth(); | ||
| 203 | framebuffer_height = framebuffer.GetHeight(); | ||
| 204 | framebuffer_format = static_cast<Format>(framebuffer.color_format); | ||
| 205 | |||
| 206 | break; | ||
| 207 | } | ||
| 208 | |||
| 209 | case Source::Custom: | ||
| 210 | { | ||
| 211 | // Keep user-specified values | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | |||
| 215 | default: | ||
| 216 | qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source); | ||
| 217 | break; | ||
| 218 | } | ||
| 219 | |||
| 220 | // TODO: Implement a good way to visualize alpha components! | ||
| 221 | // TODO: Unify this decoding code with the texture decoder | ||
| 222 | switch (framebuffer_format) { | ||
| 223 | case Format::RGBA8: | ||
| 224 | { | ||
| 225 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | ||
| 226 | u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address); | ||
| 227 | for (int y = 0; y < framebuffer_height; ++y) { | ||
| 228 | for (int x = 0; x < framebuffer_width; ++x) { | ||
| 229 | u32 value = *(color_buffer + x + y * framebuffer_width); | ||
| 230 | |||
| 231 | decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/)); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | pixmap = QPixmap::fromImage(decoded_image); | ||
| 235 | break; | ||
| 236 | } | ||
| 237 | |||
| 238 | case Format::RGB8: | ||
| 239 | { | ||
| 240 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | ||
| 241 | u8* color_buffer = Memory::GetPointer(framebuffer_address); | ||
| 242 | for (int y = 0; y < framebuffer_height; ++y) { | ||
| 243 | for (int x = 0; x < framebuffer_width; ++x) { | ||
| 244 | u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width; | ||
| 245 | |||
| 246 | decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/)); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | pixmap = QPixmap::fromImage(decoded_image); | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | |||
| 253 | case Format::RGBA5551: | ||
| 254 | { | ||
| 255 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | ||
| 256 | u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address); | ||
| 257 | for (int y = 0; y < framebuffer_height; ++y) { | ||
| 258 | for (int x = 0; x < framebuffer_width; ++x) { | ||
| 259 | u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2); | ||
| 260 | u8 r = (value >> 11) & 0x1F; | ||
| 261 | u8 g = (value >> 6) & 0x1F; | ||
| 262 | u8 b = (value >> 1) & 0x1F; | ||
| 263 | u8 a = value & 1; | ||
| 264 | |||
| 265 | decoded_image.setPixel(x, y, qRgba(r, g, b, 255/*a*/)); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | pixmap = QPixmap::fromImage(decoded_image); | ||
| 269 | break; | ||
| 270 | } | ||
| 271 | |||
| 272 | default: | ||
| 273 | qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); | ||
| 274 | break; | ||
| 275 | } | ||
| 276 | |||
| 277 | framebuffer_address_control->SetValue(framebuffer_address); | ||
| 278 | framebuffer_width_control->setValue(framebuffer_width); | ||
| 279 | framebuffer_height_control->setValue(framebuffer_height); | ||
| 280 | framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format)); | ||
| 281 | framebuffer_picture_label->setPixmap(pixmap); | ||
| 282 | } | ||
diff --git a/src/citra_qt/debugger/graphics_framebuffer.hxx b/src/citra_qt/debugger/graphics_framebuffer.hxx new file mode 100644 index 000000000..1151ee7a1 --- /dev/null +++ b/src/citra_qt/debugger/graphics_framebuffer.hxx | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QDockWidget> | ||
| 8 | |||
| 9 | #include "video_core/debug_utils/debug_utils.h" | ||
| 10 | |||
| 11 | class QComboBox; | ||
| 12 | class QLabel; | ||
| 13 | class QSpinBox; | ||
| 14 | |||
| 15 | class CSpinBox; | ||
| 16 | |||
| 17 | // Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. | ||
| 18 | // This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||
| 19 | // the widget usually wants to perform reactions in the GUI thread. | ||
| 20 | class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||
| 21 | Q_OBJECT | ||
| 22 | |||
| 23 | public: | ||
| 24 | BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, | ||
| 25 | QWidget* parent = nullptr); | ||
| 26 | |||
| 27 | void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 28 | void OnPicaResume() override; | ||
| 29 | |||
| 30 | private slots: | ||
| 31 | virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||
| 32 | virtual void OnResumed() = 0; | ||
| 33 | |||
| 34 | signals: | ||
| 35 | void Resumed(); | ||
| 36 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 37 | }; | ||
| 38 | |||
| 39 | class GraphicsFramebufferWidget : public BreakPointObserverDock { | ||
| 40 | Q_OBJECT | ||
| 41 | |||
| 42 | using Event = Pica::DebugContext::Event; | ||
| 43 | |||
| 44 | enum class Source { | ||
| 45 | PicaTarget = 0, | ||
| 46 | Custom = 1, | ||
| 47 | |||
| 48 | // TODO: Add GPU framebuffer sources! | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum class Format { | ||
| 52 | RGBA8 = 0, | ||
| 53 | RGB8 = 1, | ||
| 54 | RGBA5551 = 2, | ||
| 55 | RGB565 = 3, | ||
| 56 | RGBA4 = 4, | ||
| 57 | }; | ||
| 58 | |||
| 59 | public: | ||
| 60 | GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); | ||
| 61 | |||
| 62 | public slots: | ||
| 63 | void OnFramebufferSourceChanged(int new_value); | ||
| 64 | void OnFramebufferAddressChanged(qint64 new_value); | ||
| 65 | void OnFramebufferWidthChanged(int new_value); | ||
| 66 | void OnFramebufferHeightChanged(int new_value); | ||
| 67 | void OnFramebufferFormatChanged(int new_value); | ||
| 68 | void OnUpdate(); | ||
| 69 | |||
| 70 | private slots: | ||
| 71 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 72 | void OnResumed() override; | ||
| 73 | |||
| 74 | signals: | ||
| 75 | void Update(); | ||
| 76 | |||
| 77 | private: | ||
| 78 | |||
| 79 | QComboBox* framebuffer_source_list; | ||
| 80 | CSpinBox* framebuffer_address_control; | ||
| 81 | QSpinBox* framebuffer_width_control; | ||
| 82 | QSpinBox* framebuffer_height_control; | ||
| 83 | QComboBox* framebuffer_format_control; | ||
| 84 | |||
| 85 | QLabel* framebuffer_picture_label; | ||
| 86 | |||
| 87 | Source framebuffer_source; | ||
| 88 | unsigned framebuffer_address; | ||
| 89 | unsigned framebuffer_width; | ||
| 90 | unsigned framebuffer_height; | ||
| 91 | Format framebuffer_format; | ||
| 92 | }; | ||
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp index 96ceed480..ed17ee4b4 100644 --- a/src/citra_qt/debugger/registers.cpp +++ b/src/citra_qt/debugger/registers.cpp | |||
| @@ -46,18 +46,18 @@ void RegistersWidget::OnCPUStepped() | |||
| 46 | 46 | ||
| 47 | CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0'))); | 47 | CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0'))); |
| 48 | CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode | 48 | CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode |
| 49 | CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State | 49 | CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State |
| 50 | CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable | 50 | CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable |
| 51 | CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable | 51 | CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable |
| 52 | CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort | 52 | CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort |
| 53 | CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess | 53 | CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess |
| 54 | CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM) | 54 | CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM) |
| 55 | CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal | 55 | CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal |
| 56 | CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify | 56 | CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify |
| 57 | CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state | 57 | CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state |
| 58 | CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow | 58 | CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow |
| 59 | CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow | 59 | CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow |
| 60 | CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend | 60 | CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend |
| 61 | CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero | 61 | CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero |
| 62 | CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than | 62 | CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than |
| 63 | } | 63 | } |
diff --git a/src/citra_qt/debugger/registers.hxx b/src/citra_qt/debugger/registers.hxx index 9645feb2a..4cca957ce 100644 --- a/src/citra_qt/debugger/registers.hxx +++ b/src/citra_qt/debugger/registers.hxx | |||
| @@ -16,10 +16,10 @@ public slots: | |||
| 16 | void OnCPUStepped(); | 16 | void OnCPUStepped(); |
| 17 | 17 | ||
| 18 | private: | 18 | private: |
| 19 | Ui::ARMRegisters cpu_regs_ui; | 19 | Ui::ARMRegisters cpu_regs_ui; |
| 20 | 20 | ||
| 21 | QTreeWidget* tree; | 21 | QTreeWidget* tree; |
| 22 | 22 | ||
| 23 | QTreeWidgetItem* registers; | 23 | QTreeWidgetItem* registers; |
| 24 | QTreeWidgetItem* CSPR; | 24 | QTreeWidgetItem* CSPR; |
| 25 | }; | 25 | }; |
diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index bbaa4a8dc..5d0b52e4f 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | struct Hotkey | 6 | struct Hotkey |
| 7 | { | 7 | { |
| 8 | Hotkey() : shortcut(NULL), context(Qt::WindowShortcut) {} | 8 | Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} |
| 9 | 9 | ||
| 10 | QKeySequence keyseq; | 10 | QKeySequence keyseq; |
| 11 | QShortcut* shortcut; | 11 | QShortcut* shortcut; |
| @@ -81,7 +81,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge | |||
| 81 | Hotkey& hk = hotkey_groups[group][action]; | 81 | Hotkey& hk = hotkey_groups[group][action]; |
| 82 | 82 | ||
| 83 | if (!hk.shortcut) | 83 | if (!hk.shortcut) |
| 84 | hk.shortcut = new QShortcut(hk.keyseq, widget, NULL, NULL, hk.context); | 84 | hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); |
| 85 | 85 | ||
| 86 | return hk.shortcut; | 86 | return hk.shortcut; |
| 87 | } | 87 | } |
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 304c169b9..23d4925b8 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #include <thread> | ||
| 2 | |||
| 1 | #include <QtGui> | 3 | #include <QtGui> |
| 2 | #include <QDesktopWidget> | 4 | #include <QDesktopWidget> |
| 3 | #include <QFileDialog> | 5 | #include <QFileDialog> |
| @@ -5,8 +7,13 @@ | |||
| 5 | #include "main.hxx" | 7 | #include "main.hxx" |
| 6 | 8 | ||
| 7 | #include "common/common.h" | 9 | #include "common/common.h" |
| 10 | #include "common/logging/text_formatter.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/logging/backend.h" | ||
| 13 | #include "common/logging/filter.h" | ||
| 8 | #include "common/platform.h" | 14 | #include "common/platform.h" |
| 9 | #include "common/log_manager.h" | 15 | #include "common/scope_exit.h" |
| 16 | |||
| 10 | #if EMU_PLATFORM == PLATFORM_LINUX | 17 | #if EMU_PLATFORM == PLATFORM_LINUX |
| 11 | #include <unistd.h> | 18 | #include <unistd.h> |
| 12 | #endif | 19 | #endif |
| @@ -20,8 +27,11 @@ | |||
| 20 | #include "debugger/callstack.hxx" | 27 | #include "debugger/callstack.hxx" |
| 21 | #include "debugger/ramview.hxx" | 28 | #include "debugger/ramview.hxx" |
| 22 | #include "debugger/graphics.hxx" | 29 | #include "debugger/graphics.hxx" |
| 30 | #include "debugger/graphics_breakpoints.hxx" | ||
| 23 | #include "debugger/graphics_cmdlists.hxx" | 31 | #include "debugger/graphics_cmdlists.hxx" |
| 32 | #include "debugger/graphics_framebuffer.hxx" | ||
| 24 | 33 | ||
| 34 | #include "core/settings.h" | ||
| 25 | #include "core/system.h" | 35 | #include "core/system.h" |
| 26 | #include "core/core.h" | 36 | #include "core/core.h" |
| 27 | #include "core/loader/loader.h" | 37 | #include "core/loader/loader.h" |
| @@ -30,10 +40,10 @@ | |||
| 30 | 40 | ||
| 31 | #include "version.h" | 41 | #include "version.h" |
| 32 | 42 | ||
| 33 | |||
| 34 | GMainWindow::GMainWindow() | 43 | GMainWindow::GMainWindow() |
| 35 | { | 44 | { |
| 36 | LogManager::Init(); | 45 | Pica::g_debug_context = Pica::DebugContext::Construct(); |
| 46 | |||
| 37 | Config config; | 47 | Config config; |
| 38 | 48 | ||
| 39 | ui.setupUi(this); | 49 | ui.setupUi(this); |
| @@ -62,12 +72,22 @@ GMainWindow::GMainWindow() | |||
| 62 | addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); | 72 | addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); |
| 63 | graphicsCommandsWidget->hide(); | 73 | graphicsCommandsWidget->hide(); |
| 64 | 74 | ||
| 75 | auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this); | ||
| 76 | addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); | ||
| 77 | graphicsBreakpointsWidget->hide(); | ||
| 78 | |||
| 79 | auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this); | ||
| 80 | addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); | ||
| 81 | graphicsFramebufferWidget->hide(); | ||
| 82 | |||
| 65 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | 83 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |
| 66 | debug_menu->addAction(disasmWidget->toggleViewAction()); | 84 | debug_menu->addAction(disasmWidget->toggleViewAction()); |
| 67 | debug_menu->addAction(registersWidget->toggleViewAction()); | 85 | debug_menu->addAction(registersWidget->toggleViewAction()); |
| 68 | debug_menu->addAction(callstackWidget->toggleViewAction()); | 86 | debug_menu->addAction(callstackWidget->toggleViewAction()); |
| 69 | debug_menu->addAction(graphicsWidget->toggleViewAction()); | 87 | debug_menu->addAction(graphicsWidget->toggleViewAction()); |
| 70 | debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | 88 | debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); |
| 89 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||
| 90 | debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | ||
| 71 | 91 | ||
| 72 | // Set default UI state | 92 | // Set default UI state |
| 73 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half | 93 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half |
| @@ -112,7 +132,8 @@ GMainWindow::GMainWindow() | |||
| 112 | connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); | 132 | connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); |
| 113 | connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); | 133 | connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); |
| 114 | 134 | ||
| 115 | setWindowTitle(render_window->GetWindowTitle().c_str()); | 135 | std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |
| 136 | setWindowTitle(window_title.c_str()); | ||
| 116 | 137 | ||
| 117 | show(); | 138 | show(); |
| 118 | 139 | ||
| @@ -125,24 +146,20 @@ GMainWindow::GMainWindow() | |||
| 125 | GMainWindow::~GMainWindow() | 146 | GMainWindow::~GMainWindow() |
| 126 | { | 147 | { |
| 127 | // will get automatically deleted otherwise | 148 | // will get automatically deleted otherwise |
| 128 | if (render_window->parent() == NULL) | 149 | if (render_window->parent() == nullptr) |
| 129 | delete render_window; | 150 | delete render_window; |
| 151 | |||
| 152 | Pica::g_debug_context.reset(); | ||
| 130 | } | 153 | } |
| 131 | 154 | ||
| 132 | void GMainWindow::BootGame(std::string filename) | 155 | void GMainWindow::BootGame(std::string filename) |
| 133 | { | 156 | { |
| 134 | NOTICE_LOG(MASTER_LOG, "Citra starting...\n"); | 157 | LOG_INFO(Frontend, "Citra starting...\n"); |
| 135 | System::Init(render_window); | 158 | System::Init(render_window); |
| 136 | 159 | ||
| 137 | if (Core::Init()) { | ||
| 138 | ERROR_LOG(MASTER_LOG, "Core initialization failed, exiting..."); | ||
| 139 | Core::Stop(); | ||
| 140 | exit(1); | ||
| 141 | } | ||
| 142 | |||
| 143 | // Load a game or die... | 160 | // Load a game or die... |
| 144 | if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { | 161 | if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { |
| 145 | ERROR_LOG(BOOT, "Failed to load ROM!"); | 162 | LOG_CRITICAL(Frontend, "Failed to load ROM!"); |
| 146 | } | 163 | } |
| 147 | 164 | ||
| 148 | disasmWidget->Init(); | 165 | disasmWidget->Init(); |
| @@ -158,7 +175,7 @@ void GMainWindow::BootGame(std::string filename) | |||
| 158 | 175 | ||
| 159 | void GMainWindow::OnMenuLoadFile() | 176 | void GMainWindow::OnMenuLoadFile() |
| 160 | { | 177 | { |
| 161 | QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.bin *.cci *.cxi)")); | 178 | QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.3dsx *.elf *.axf *.bin *.cci *.cxi)")); |
| 162 | if (filename.size()) | 179 | if (filename.size()) |
| 163 | BootGame(filename.toLatin1().data()); | 180 | BootGame(filename.toLatin1().data()); |
| 164 | } | 181 | } |
| @@ -207,14 +224,14 @@ void GMainWindow::OnOpenHotkeysDialog() | |||
| 207 | void GMainWindow::ToggleWindowMode() | 224 | void GMainWindow::ToggleWindowMode() |
| 208 | { | 225 | { |
| 209 | bool enable = ui.action_Popout_Window_Mode->isChecked(); | 226 | bool enable = ui.action_Popout_Window_Mode->isChecked(); |
| 210 | if (enable && render_window->parent() != NULL) | 227 | if (enable && render_window->parent() != nullptr) |
| 211 | { | 228 | { |
| 212 | ui.horizontalLayout->removeWidget(render_window); | 229 | ui.horizontalLayout->removeWidget(render_window); |
| 213 | render_window->setParent(NULL); | 230 | render_window->setParent(nullptr); |
| 214 | render_window->setVisible(true); | 231 | render_window->setVisible(true); |
| 215 | render_window->RestoreGeometry(); | 232 | render_window->RestoreGeometry(); |
| 216 | } | 233 | } |
| 217 | else if (!enable && render_window->parent() == NULL) | 234 | else if (!enable && render_window->parent() == nullptr) |
| 218 | { | 235 | { |
| 219 | render_window->BackupGeometry(); | 236 | render_window->BackupGeometry(); |
| 220 | ui.horizontalLayout->addWidget(render_window); | 237 | ui.horizontalLayout->addWidget(render_window); |
| @@ -249,9 +266,21 @@ void GMainWindow::closeEvent(QCloseEvent* event) | |||
| 249 | 266 | ||
| 250 | int __cdecl main(int argc, char* argv[]) | 267 | int __cdecl main(int argc, char* argv[]) |
| 251 | { | 268 | { |
| 269 | std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger(); | ||
| 270 | Log::Filter log_filter(Log::Level::Info); | ||
| 271 | std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter); | ||
| 272 | SCOPE_EXIT({ | ||
| 273 | logger->Close(); | ||
| 274 | logging_thread.join(); | ||
| 275 | }); | ||
| 276 | |||
| 252 | QApplication::setAttribute(Qt::AA_X11InitThreads); | 277 | QApplication::setAttribute(Qt::AA_X11InitThreads); |
| 253 | QApplication app(argc, argv); | 278 | QApplication app(argc, argv); |
| 279 | |||
| 254 | GMainWindow main_window; | 280 | GMainWindow main_window; |
| 281 | // After settings have been loaded by GMainWindow, apply the filter | ||
| 282 | log_filter.ParseFilterString(Settings::values.log_filter); | ||
| 283 | |||
| 255 | main_window.show(); | 284 | main_window.show(); |
| 256 | return app.exec(); | 285 | return app.exec(); |
| 257 | } | 286 | } |
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp new file mode 100644 index 000000000..9672168f5 --- /dev/null +++ b/src/citra_qt/util/spinbox.cpp | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | // Licensed under GPLv2+ | ||
| 2 | // Refer to the license.txt file included. | ||
| 3 | |||
| 4 | |||
| 5 | // Copyright 2014 Tony Wasserka | ||
| 6 | // All rights reserved. | ||
| 7 | // | ||
| 8 | // Redistribution and use in source and binary forms, with or without | ||
| 9 | // modification, are permitted provided that the following conditions are met: | ||
| 10 | // | ||
| 11 | // * Redistributions of source code must retain the above copyright | ||
| 12 | // notice, this list of conditions and the following disclaimer. | ||
| 13 | // * Redistributions in binary form must reproduce the above copyright | ||
| 14 | // notice, this list of conditions and the following disclaimer in the | ||
| 15 | // documentation and/or other materials provided with the distribution. | ||
| 16 | // * Neither the name of the owner nor the names of its contributors may | ||
| 17 | // be used to endorse or promote products derived from this software | ||
| 18 | // without specific prior written permission. | ||
| 19 | // | ||
| 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | |||
| 32 | #include <QLineEdit> | ||
| 33 | #include <QRegExpValidator> | ||
| 34 | |||
| 35 | #include "common/log.h" | ||
| 36 | |||
| 37 | #include "spinbox.hxx" | ||
| 38 | |||
| 39 | CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), base(10), min_value(-100), max_value(100), value(0), num_digits(0) | ||
| 40 | { | ||
| 41 | // TODO: Might be nice to not immediately call the slot. | ||
| 42 | // Think of an address that is being replaced by a different one, in which case a lot | ||
| 43 | // invalid intermediate addresses would be read from during editing. | ||
| 44 | connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(OnEditingFinished())); | ||
| 45 | |||
| 46 | UpdateText(); | ||
| 47 | } | ||
| 48 | |||
| 49 | void CSpinBox::SetValue(qint64 val) | ||
| 50 | { | ||
| 51 | auto old_value = value; | ||
| 52 | value = std::max(std::min(val, max_value), min_value); | ||
| 53 | |||
| 54 | if (old_value != value) { | ||
| 55 | UpdateText(); | ||
| 56 | emit ValueChanged(value); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | void CSpinBox::SetRange(qint64 min, qint64 max) | ||
| 61 | { | ||
| 62 | min_value = min; | ||
| 63 | max_value = max; | ||
| 64 | |||
| 65 | SetValue(value); | ||
| 66 | UpdateText(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void CSpinBox::stepBy(int steps) | ||
| 70 | { | ||
| 71 | auto new_value = value; | ||
| 72 | // Scale number of steps by the currently selected digit | ||
| 73 | // TODO: Move this code elsewhere and enable it. | ||
| 74 | // TODO: Support for num_digits==0, too | ||
| 75 | // TODO: Support base!=16, too | ||
| 76 | // TODO: Make the cursor not jump back to the end of the line... | ||
| 77 | /*if (base == 16 && num_digits > 0) { | ||
| 78 | int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1; | ||
| 79 | digit = std::max(0, std::min(digit, num_digits - 1)); | ||
| 80 | steps <<= digit * 4; | ||
| 81 | }*/ | ||
| 82 | |||
| 83 | // Increment "new_value" by "steps", and perform annoying overflow checks, too. | ||
| 84 | if (steps < 0 && new_value + steps > new_value) { | ||
| 85 | new_value = std::numeric_limits<qint64>::min(); | ||
| 86 | } else if (steps > 0 && new_value + steps < new_value) { | ||
| 87 | new_value = std::numeric_limits<qint64>::max(); | ||
| 88 | } else { | ||
| 89 | new_value += steps; | ||
| 90 | } | ||
| 91 | |||
| 92 | SetValue(new_value); | ||
| 93 | UpdateText(); | ||
| 94 | } | ||
| 95 | |||
| 96 | QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const | ||
| 97 | { | ||
| 98 | StepEnabled ret = StepNone; | ||
| 99 | |||
| 100 | if (value > min_value) | ||
| 101 | ret |= StepDownEnabled; | ||
| 102 | |||
| 103 | if (value < max_value) | ||
| 104 | ret |= StepUpEnabled; | ||
| 105 | |||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | void CSpinBox::SetBase(int base) | ||
| 110 | { | ||
| 111 | this->base = base; | ||
| 112 | |||
| 113 | UpdateText(); | ||
| 114 | } | ||
| 115 | |||
| 116 | void CSpinBox::SetNumDigits(int num_digits) | ||
| 117 | { | ||
| 118 | this->num_digits = num_digits; | ||
| 119 | |||
| 120 | UpdateText(); | ||
| 121 | } | ||
| 122 | |||
| 123 | void CSpinBox::SetPrefix(const QString& prefix) | ||
| 124 | { | ||
| 125 | this->prefix = prefix; | ||
| 126 | |||
| 127 | UpdateText(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void CSpinBox::SetSuffix(const QString& suffix) | ||
| 131 | { | ||
| 132 | this->suffix = suffix; | ||
| 133 | |||
| 134 | UpdateText(); | ||
| 135 | } | ||
| 136 | |||
| 137 | static QString StringToInputMask(const QString& input) { | ||
| 138 | QString mask = input; | ||
| 139 | |||
| 140 | // ... replace any special characters by their escaped counterparts ... | ||
| 141 | mask.replace("\\", "\\\\"); | ||
| 142 | mask.replace("A", "\\A"); | ||
| 143 | mask.replace("a", "\\a"); | ||
| 144 | mask.replace("N", "\\N"); | ||
| 145 | mask.replace("n", "\\n"); | ||
| 146 | mask.replace("X", "\\X"); | ||
| 147 | mask.replace("x", "\\x"); | ||
| 148 | mask.replace("9", "\\9"); | ||
| 149 | mask.replace("0", "\\0"); | ||
| 150 | mask.replace("D", "\\D"); | ||
| 151 | mask.replace("d", "\\d"); | ||
| 152 | mask.replace("#", "\\#"); | ||
| 153 | mask.replace("H", "\\H"); | ||
| 154 | mask.replace("h", "\\h"); | ||
| 155 | mask.replace("B", "\\B"); | ||
| 156 | mask.replace("b", "\\b"); | ||
| 157 | mask.replace(">", "\\>"); | ||
| 158 | mask.replace("<", "\\<"); | ||
| 159 | mask.replace("!", "\\!"); | ||
| 160 | |||
| 161 | return mask; | ||
| 162 | } | ||
| 163 | |||
| 164 | void CSpinBox::UpdateText() | ||
| 165 | { | ||
| 166 | // If a fixed number of digits is used, we put the line edit in insertion mode by setting an | ||
| 167 | // input mask. | ||
| 168 | QString mask; | ||
| 169 | if (num_digits != 0) { | ||
| 170 | mask += StringToInputMask(prefix); | ||
| 171 | |||
| 172 | // For base 10 and negative range, demand a single sign character | ||
| 173 | if (HasSign()) | ||
| 174 | mask += "X"; // identified as "-" or "+" in the validator | ||
| 175 | |||
| 176 | // Uppercase digits greater than 9. | ||
| 177 | mask += ">"; | ||
| 178 | |||
| 179 | // The greatest signed 64-bit number has 19 decimal digits. | ||
| 180 | // TODO: Could probably make this more generic with some logarithms. | ||
| 181 | // For reference, unsigned 64-bit can have up to 20 decimal digits. | ||
| 182 | int digits = (num_digits != 0) ? num_digits | ||
| 183 | : (base == 16) ? 16 | ||
| 184 | : (base == 10) ? 19 | ||
| 185 | : 0xFF; // fallback case... | ||
| 186 | |||
| 187 | // Match num_digits digits | ||
| 188 | // Digits irrelevant to the chosen number base are filtered in the validator | ||
| 189 | mask += QString("H").repeated(std::max(num_digits, 1)); | ||
| 190 | |||
| 191 | // Switch off case conversion | ||
| 192 | mask += "!"; | ||
| 193 | |||
| 194 | mask += StringToInputMask(suffix); | ||
| 195 | } | ||
| 196 | lineEdit()->setInputMask(mask); | ||
| 197 | |||
| 198 | // Set new text without changing the cursor position. This will cause the cursor to briefly | ||
| 199 | // appear at the end of the line and then to jump back to its original position. That's | ||
| 200 | // a bit ugly, but better than having setText() move the cursor permanently all the time. | ||
| 201 | int cursor_position = lineEdit()->cursorPosition(); | ||
| 202 | lineEdit()->setText(TextFromValue()); | ||
| 203 | lineEdit()->setCursorPosition(cursor_position); | ||
| 204 | } | ||
| 205 | |||
| 206 | QString CSpinBox::TextFromValue() | ||
| 207 | { | ||
| 208 | return prefix | ||
| 209 | + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") | ||
| 210 | + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper() | ||
| 211 | + suffix; | ||
| 212 | } | ||
| 213 | |||
| 214 | qint64 CSpinBox::ValueFromText() | ||
| 215 | { | ||
| 216 | unsigned strpos = prefix.length(); | ||
| 217 | |||
| 218 | QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); | ||
| 219 | return num_string.toLongLong(nullptr, base); | ||
| 220 | } | ||
| 221 | |||
| 222 | bool CSpinBox::HasSign() const | ||
| 223 | { | ||
| 224 | return base == 10 && min_value < 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | void CSpinBox::OnEditingFinished() | ||
| 228 | { | ||
| 229 | // Only update for valid input | ||
| 230 | QString input = lineEdit()->text(); | ||
| 231 | int pos = 0; | ||
| 232 | if (QValidator::Acceptable == validate(input, pos)) | ||
| 233 | SetValue(ValueFromText()); | ||
| 234 | } | ||
| 235 | |||
| 236 | QValidator::State CSpinBox::validate(QString& input, int& pos) const | ||
| 237 | { | ||
| 238 | if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) | ||
| 239 | return QValidator::Invalid; | ||
| 240 | |||
| 241 | unsigned strpos = prefix.length(); | ||
| 242 | |||
| 243 | // Empty "numbers" allowed as intermediate values | ||
| 244 | if (strpos >= input.length() - HasSign() - suffix.length()) | ||
| 245 | return QValidator::Intermediate; | ||
| 246 | |||
| 247 | _dbg_assert_(Frontend, base <= 10 || base == 16); | ||
| 248 | QString regexp; | ||
| 249 | |||
| 250 | // Demand sign character for negative ranges | ||
| 251 | if (HasSign()) | ||
| 252 | regexp += "[+\\-]"; | ||
| 253 | |||
| 254 | // Match digits corresponding to the chosen number base. | ||
| 255 | regexp += QString("[0-%1").arg(std::min(base, 9)); | ||
| 256 | if (base == 16) { | ||
| 257 | regexp += "a-fA-F"; | ||
| 258 | } | ||
| 259 | regexp += "]"; | ||
| 260 | |||
| 261 | // Specify number of digits | ||
| 262 | if (num_digits > 0) { | ||
| 263 | regexp += QString("{%1}").arg(num_digits); | ||
| 264 | } else { | ||
| 265 | regexp += "+"; | ||
| 266 | } | ||
| 267 | |||
| 268 | // Match string | ||
| 269 | QRegExp num_regexp(regexp); | ||
| 270 | int num_pos = strpos; | ||
| 271 | QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length()); | ||
| 272 | |||
| 273 | if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0) | ||
| 274 | return QValidator::Invalid; | ||
| 275 | |||
| 276 | sub_input = sub_input.left(num_regexp.matchedLength()); | ||
| 277 | bool ok; | ||
| 278 | qint64 val = sub_input.toLongLong(&ok, base); | ||
| 279 | |||
| 280 | if (!ok) | ||
| 281 | return QValidator::Invalid; | ||
| 282 | |||
| 283 | // Outside boundaries => don't accept | ||
| 284 | if (val < min_value || val > max_value) | ||
| 285 | return QValidator::Invalid; | ||
| 286 | |||
| 287 | // Make sure we are actually at the end of this string... | ||
| 288 | strpos += num_regexp.matchedLength(); | ||
| 289 | |||
| 290 | if (!suffix.isEmpty() && input.mid(strpos) != suffix) { | ||
| 291 | return QValidator::Invalid; | ||
| 292 | } else { | ||
| 293 | strpos += suffix.length(); | ||
| 294 | } | ||
| 295 | |||
| 296 | if (strpos != input.length()) | ||
| 297 | return QValidator::Invalid; | ||
| 298 | |||
| 299 | // At this point we can say for sure that the input is fine. Let's fix it up a bit though | ||
| 300 | input.replace(num_pos, sub_input.length(), sub_input.toUpper()); | ||
| 301 | |||
| 302 | return QValidator::Acceptable; | ||
| 303 | } | ||
diff --git a/src/citra_qt/util/spinbox.hxx b/src/citra_qt/util/spinbox.hxx new file mode 100644 index 000000000..68f5b9894 --- /dev/null +++ b/src/citra_qt/util/spinbox.hxx | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // Licensed under GPLv2+ | ||
| 2 | // Refer to the license.txt file included. | ||
| 3 | |||
| 4 | |||
| 5 | // Copyright 2014 Tony Wasserka | ||
| 6 | // All rights reserved. | ||
| 7 | // | ||
| 8 | // Redistribution and use in source and binary forms, with or without | ||
| 9 | // modification, are permitted provided that the following conditions are met: | ||
| 10 | // | ||
| 11 | // * Redistributions of source code must retain the above copyright | ||
| 12 | // notice, this list of conditions and the following disclaimer. | ||
| 13 | // * Redistributions in binary form must reproduce the above copyright | ||
| 14 | // notice, this list of conditions and the following disclaimer in the | ||
| 15 | // documentation and/or other materials provided with the distribution. | ||
| 16 | // * Neither the name of the owner nor the names of its contributors may | ||
| 17 | // be used to endorse or promote products derived from this software | ||
| 18 | // without specific prior written permission. | ||
| 19 | // | ||
| 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | |||
| 32 | |||
| 33 | #pragma once | ||
| 34 | |||
| 35 | #include <QAbstractSpinBox> | ||
| 36 | #include <QtGlobal> | ||
| 37 | |||
| 38 | class QVariant; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * A custom spin box widget with enhanced functionality over Qt's QSpinBox | ||
| 42 | */ | ||
| 43 | class CSpinBox : public QAbstractSpinBox { | ||
| 44 | Q_OBJECT | ||
| 45 | |||
| 46 | public: | ||
| 47 | CSpinBox(QWidget* parent = nullptr); | ||
| 48 | |||
| 49 | void stepBy(int steps) override; | ||
| 50 | StepEnabled stepEnabled() const override; | ||
| 51 | |||
| 52 | void SetValue(qint64 val); | ||
| 53 | |||
| 54 | void SetRange(qint64 min, qint64 max); | ||
| 55 | |||
| 56 | void SetBase(int base); | ||
| 57 | |||
| 58 | void SetPrefix(const QString& prefix); | ||
| 59 | void SetSuffix(const QString& suffix); | ||
| 60 | |||
| 61 | void SetNumDigits(int num_digits); | ||
| 62 | |||
| 63 | QValidator::State validate(QString& input, int& pos) const override; | ||
| 64 | |||
| 65 | signals: | ||
| 66 | void ValueChanged(qint64 val); | ||
| 67 | |||
| 68 | private slots: | ||
| 69 | void OnEditingFinished(); | ||
| 70 | |||
| 71 | private: | ||
| 72 | void UpdateText(); | ||
| 73 | |||
| 74 | bool HasSign() const; | ||
| 75 | |||
| 76 | QString TextFromValue(); | ||
| 77 | qint64 ValueFromText(); | ||
| 78 | |||
| 79 | qint64 min_value, max_value; | ||
| 80 | |||
| 81 | qint64 value; | ||
| 82 | |||
| 83 | QString prefix, suffix; | ||
| 84 | |||
| 85 | int base; | ||
| 86 | |||
| 87 | int num_digits; | ||
| 88 | }; | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9d5a90762..15989708d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -3,14 +3,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU | |||
| 3 | 3 | ||
| 4 | set(SRCS | 4 | set(SRCS |
| 5 | break_points.cpp | 5 | break_points.cpp |
| 6 | console_listener.cpp | ||
| 7 | emu_window.cpp | 6 | emu_window.cpp |
| 8 | extended_trace.cpp | 7 | extended_trace.cpp |
| 9 | file_search.cpp | 8 | file_search.cpp |
| 10 | file_util.cpp | 9 | file_util.cpp |
| 11 | hash.cpp | 10 | hash.cpp |
| 12 | key_map.cpp | 11 | key_map.cpp |
| 13 | log_manager.cpp | 12 | logging/filter.cpp |
| 13 | logging/text_formatter.cpp | ||
| 14 | logging/backend.cpp | ||
| 14 | math_util.cpp | 15 | math_util.cpp |
| 15 | mem_arena.cpp | 16 | mem_arena.cpp |
| 16 | memory_util.cpp | 17 | memory_util.cpp |
| @@ -32,7 +33,7 @@ set(HEADERS | |||
| 32 | common_funcs.h | 33 | common_funcs.h |
| 33 | common_paths.h | 34 | common_paths.h |
| 34 | common_types.h | 35 | common_types.h |
| 35 | console_listener.h | 36 | concurrent_ring_buffer.h |
| 36 | cpu_detect.h | 37 | cpu_detect.h |
| 37 | debug_interface.h | 38 | debug_interface.h |
| 38 | emu_window.h | 39 | emu_window.h |
| @@ -44,13 +45,17 @@ set(HEADERS | |||
| 44 | key_map.h | 45 | key_map.h |
| 45 | linear_disk_cache.h | 46 | linear_disk_cache.h |
| 46 | log.h | 47 | log.h |
| 47 | log_manager.h | 48 | logging/text_formatter.h |
| 49 | logging/filter.h | ||
| 50 | logging/log.h | ||
| 51 | logging/backend.h | ||
| 48 | math_util.h | 52 | math_util.h |
| 49 | mem_arena.h | 53 | mem_arena.h |
| 50 | memory_util.h | 54 | memory_util.h |
| 51 | msg_handler.h | 55 | msg_handler.h |
| 52 | platform.h | 56 | platform.h |
| 53 | scm_rev.h | 57 | scm_rev.h |
| 58 | scope_exit.h | ||
| 54 | string_util.h | 59 | string_util.h |
| 55 | swap.h | 60 | swap.h |
| 56 | symbols.h | 61 | symbols.h |
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp index 25528b864..587dbf40a 100644 --- a/src/common/break_points.cpp +++ b/src/common/break_points.cpp | |||
| @@ -180,7 +180,7 @@ void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr, | |||
| 180 | { | 180 | { |
| 181 | if (Log) | 181 | if (Log) |
| 182 | { | 182 | { |
| 183 | INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", | 183 | LOG_DEBUG(Debug_Breakpoint, "CHK %08x (%s) %s%i %0*x at %08x (%s)", |
| 184 | pc, debug_interface->getDescription(pc).c_str(), | 184 | pc, debug_interface->getDescription(pc).c_str(), |
| 185 | write ? "Write" : "Read", size*8, size*2, iValue, addr, | 185 | write ? "Write" : "Read", size*8, size*2, iValue, addr, |
| 186 | debug_interface->getDescription(addr).c_str() | 186 | debug_interface->getDescription(addr).c_str() |
diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h index dc8ac1fd9..39a14dc81 100644 --- a/src/common/chunk_file.h +++ b/src/common/chunk_file.h | |||
| @@ -51,7 +51,7 @@ public: | |||
| 51 | PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { | 51 | PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { |
| 52 | } | 52 | } |
| 53 | ~PointerWrapSection(); | 53 | ~PointerWrapSection(); |
| 54 | 54 | ||
| 55 | bool operator == (const int &v) const { return ver_ == v; } | 55 | bool operator == (const int &v) const { return ver_ == v; } |
| 56 | bool operator != (const int &v) const { return ver_ != v; } | 56 | bool operator != (const int &v) const { return ver_ != v; } |
| 57 | bool operator <= (const int &v) const { return ver_ <= v; } | 57 | bool operator <= (const int &v) const { return ver_ <= v; } |
| @@ -154,7 +154,7 @@ public: | |||
| 154 | Do(foundVersion); | 154 | Do(foundVersion); |
| 155 | 155 | ||
| 156 | if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { | 156 | if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { |
| 157 | WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title); | 157 | LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title); |
| 158 | SetError(ERROR_FAILURE); | 158 | SetError(ERROR_FAILURE); |
| 159 | return PointerWrapSection(*this, -1, title); | 159 | return PointerWrapSection(*this, -1, title); |
| 160 | } | 160 | } |
| @@ -178,7 +178,14 @@ public: | |||
| 178 | case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; | 178 | case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; |
| 179 | case MODE_WRITE: memcpy(*ptr, data, size); break; | 179 | case MODE_WRITE: memcpy(*ptr, data, size); break; |
| 180 | case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything | 180 | case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything |
| 181 | case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; | 181 | case MODE_VERIFY: |
| 182 | for (int i = 0; i < size; i++) { | ||
| 183 | _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i], | ||
| 184 | "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||
| 185 | ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], | ||
| 186 | (*ptr)[i], (*ptr)[i], &(*ptr)[i]); | ||
| 187 | } | ||
| 188 | break; | ||
| 182 | default: break; // throw an error? | 189 | default: break; // throw an error? |
| 183 | } | 190 | } |
| 184 | (*ptr) += size; | 191 | (*ptr) += size; |
| @@ -191,12 +198,19 @@ public: | |||
| 191 | case MODE_READ: memcpy(data, *ptr, size); break; | 198 | case MODE_READ: memcpy(data, *ptr, size); break; |
| 192 | case MODE_WRITE: memcpy(*ptr, data, size); break; | 199 | case MODE_WRITE: memcpy(*ptr, data, size); break; |
| 193 | case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything | 200 | case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything |
| 194 | case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; | 201 | case MODE_VERIFY: |
| 202 | for (int i = 0; i < size; i++) { | ||
| 203 | _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i], | ||
| 204 | "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||
| 205 | ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], | ||
| 206 | (*ptr)[i], (*ptr)[i], &(*ptr)[i]); | ||
| 207 | } | ||
| 208 | break; | ||
| 195 | default: break; // throw an error? | 209 | default: break; // throw an error? |
| 196 | } | 210 | } |
| 197 | (*ptr) += size; | 211 | (*ptr) += size; |
| 198 | } | 212 | } |
| 199 | 213 | ||
| 200 | template<class K, class T> | 214 | template<class K, class T> |
| 201 | void Do(std::map<K, T *> &x) | 215 | void Do(std::map<K, T *> &x) |
| 202 | { | 216 | { |
| @@ -204,11 +218,11 @@ public: | |||
| 204 | { | 218 | { |
| 205 | for (auto it = x.begin(), end = x.end(); it != end; ++it) | 219 | for (auto it = x.begin(), end = x.end(); it != end; ++it) |
| 206 | { | 220 | { |
| 207 | if (it->second != NULL) | 221 | if (it->second != nullptr) |
| 208 | delete it->second; | 222 | delete it->second; |
| 209 | } | 223 | } |
| 210 | } | 224 | } |
| 211 | T *dv = NULL; | 225 | T *dv = nullptr; |
| 212 | DoMap(x, dv); | 226 | DoMap(x, dv); |
| 213 | } | 227 | } |
| 214 | 228 | ||
| @@ -264,11 +278,11 @@ public: | |||
| 264 | { | 278 | { |
| 265 | for (auto it = x.begin(), end = x.end(); it != end; ++it) | 279 | for (auto it = x.begin(), end = x.end(); it != end; ++it) |
| 266 | { | 280 | { |
| 267 | if (it->second != NULL) | 281 | if (it->second != nullptr) |
| 268 | delete it->second; | 282 | delete it->second; |
| 269 | } | 283 | } |
| 270 | } | 284 | } |
| 271 | T *dv = NULL; | 285 | T *dv = nullptr; |
| 272 | DoMultimap(x, dv); | 286 | DoMultimap(x, dv); |
| 273 | } | 287 | } |
| 274 | 288 | ||
| @@ -320,7 +334,7 @@ public: | |||
| 320 | template<class T> | 334 | template<class T> |
| 321 | void Do(std::vector<T *> &x) | 335 | void Do(std::vector<T *> &x) |
| 322 | { | 336 | { |
| 323 | T *dv = NULL; | 337 | T *dv = nullptr; |
| 324 | DoVector(x, dv); | 338 | DoVector(x, dv); |
| 325 | } | 339 | } |
| 326 | 340 | ||
| @@ -364,12 +378,12 @@ public: | |||
| 364 | if (vec_size > 0) | 378 | if (vec_size > 0) |
| 365 | DoArray(&x[0], vec_size); | 379 | DoArray(&x[0], vec_size); |
| 366 | } | 380 | } |
| 367 | 381 | ||
| 368 | // Store deques. | 382 | // Store deques. |
| 369 | template<class T> | 383 | template<class T> |
| 370 | void Do(std::deque<T *> &x) | 384 | void Do(std::deque<T *> &x) |
| 371 | { | 385 | { |
| 372 | T *dv = NULL; | 386 | T *dv = nullptr; |
| 373 | DoDeque(x, dv); | 387 | DoDeque(x, dv); |
| 374 | } | 388 | } |
| 375 | 389 | ||
| @@ -395,7 +409,7 @@ public: | |||
| 395 | template<class T> | 409 | template<class T> |
| 396 | void Do(std::list<T *> &x) | 410 | void Do(std::list<T *> &x) |
| 397 | { | 411 | { |
| 398 | T *dv = NULL; | 412 | T *dv = nullptr; |
| 399 | Do(x, dv); | 413 | Do(x, dv); |
| 400 | } | 414 | } |
| 401 | 415 | ||
| @@ -433,7 +447,7 @@ public: | |||
| 433 | { | 447 | { |
| 434 | for (auto it = x.begin(), end = x.end(); it != end; ++it) | 448 | for (auto it = x.begin(), end = x.end(); it != end; ++it) |
| 435 | { | 449 | { |
| 436 | if (*it != NULL) | 450 | if (*it != nullptr) |
| 437 | delete *it; | 451 | delete *it; |
| 438 | } | 452 | } |
| 439 | } | 453 | } |
| @@ -476,26 +490,31 @@ public: | |||
| 476 | break; | 490 | break; |
| 477 | 491 | ||
| 478 | default: | 492 | default: |
| 479 | ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode); | 493 | LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); |
| 480 | } | 494 | } |
| 481 | } | 495 | } |
| 482 | 496 | ||
| 483 | // Store strings. | 497 | // Store strings. |
| 484 | void Do(std::string &x) | 498 | void Do(std::string &x) |
| 485 | { | 499 | { |
| 486 | int stringLen = (int)x.length() + 1; | 500 | int stringLen = (int)x.length() + 1; |
| 487 | Do(stringLen); | 501 | Do(stringLen); |
| 488 | 502 | ||
| 489 | switch (mode) { | 503 | switch (mode) { |
| 490 | case MODE_READ: x = (char*)*ptr; break; | 504 | case MODE_READ: x = (char*)*ptr; break; |
| 491 | case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; | 505 | case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; |
| 492 | case MODE_MEASURE: break; | 506 | case MODE_MEASURE: break; |
| 493 | case MODE_VERIFY: _dbg_assert_msg_(COMMON, !strcmp(x.c_str(), (char*)*ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char*)*ptr, ptr); break; | 507 | case MODE_VERIFY: |
| 508 | _dbg_assert_msg_(Common, | ||
| 509 | !strcmp(x.c_str(), (char*)*ptr), | ||
| 510 | "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", | ||
| 511 | x.c_str(), (char*)*ptr, ptr); | ||
| 512 | break; | ||
| 494 | } | 513 | } |
| 495 | (*ptr) += stringLen; | 514 | (*ptr) += stringLen; |
| 496 | } | 515 | } |
| 497 | 516 | ||
| 498 | void Do(std::wstring &x) | 517 | void Do(std::wstring &x) |
| 499 | { | 518 | { |
| 500 | int stringLen = sizeof(wchar_t)*((int)x.length() + 1); | 519 | int stringLen = sizeof(wchar_t)*((int)x.length() + 1); |
| 501 | Do(stringLen); | 520 | Do(stringLen); |
| @@ -504,7 +523,11 @@ public: | |||
| 504 | case MODE_READ: x = (wchar_t*)*ptr; break; | 523 | case MODE_READ: x = (wchar_t*)*ptr; break; |
| 505 | case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; | 524 | case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; |
| 506 | case MODE_MEASURE: break; | 525 | case MODE_MEASURE: break; |
| 507 | case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break; | 526 | case MODE_VERIFY: |
| 527 | _dbg_assert_msg_(Common, x == (wchar_t*)*ptr, | ||
| 528 | "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", | ||
| 529 | x.c_str(), (wchar_t*)*ptr, ptr); | ||
| 530 | break; | ||
| 508 | } | 531 | } |
| 509 | (*ptr) += stringLen; | 532 | (*ptr) += stringLen; |
| 510 | } | 533 | } |
| @@ -518,7 +541,7 @@ public: | |||
| 518 | void DoClass(T *&x) { | 541 | void DoClass(T *&x) { |
| 519 | if (mode == MODE_READ) | 542 | if (mode == MODE_READ) |
| 520 | { | 543 | { |
| 521 | if (x != NULL) | 544 | if (x != nullptr) |
| 522 | delete x; | 545 | delete x; |
| 523 | x = new T(); | 546 | x = new T(); |
| 524 | } | 547 | } |
| @@ -534,7 +557,7 @@ public: | |||
| 534 | void Do(T &x) { | 557 | void Do(T &x) { |
| 535 | DoHelper<T>::Do(this, x); | 558 | DoHelper<T>::Do(this, x); |
| 536 | } | 559 | } |
| 537 | 560 | ||
| 538 | template<class T> | 561 | template<class T> |
| 539 | void DoPOD(T &x) { | 562 | void DoPOD(T &x) { |
| 540 | DoHelper<T>::Do(this, x); | 563 | DoHelper<T>::Do(this, x); |
| @@ -567,7 +590,7 @@ public: | |||
| 567 | { | 590 | { |
| 568 | if (mode == MODE_READ) | 591 | if (mode == MODE_READ) |
| 569 | { | 592 | { |
| 570 | cur->next = 0; | 593 | cur->next = nullptr; |
| 571 | list_cur = cur; | 594 | list_cur = cur; |
| 572 | if (prev) | 595 | if (prev) |
| 573 | prev->next = cur; | 596 | prev->next = cur; |
| @@ -586,13 +609,13 @@ public: | |||
| 586 | if (mode == MODE_READ) | 609 | if (mode == MODE_READ) |
| 587 | { | 610 | { |
| 588 | if (prev) | 611 | if (prev) |
| 589 | prev->next = 0; | 612 | prev->next = nullptr; |
| 590 | if (list_end) | 613 | if (list_end) |
| 591 | *list_end = prev; | 614 | *list_end = prev; |
| 592 | if (list_cur) | 615 | if (list_cur) |
| 593 | { | 616 | { |
| 594 | if (list_start == list_cur) | 617 | if (list_start == list_cur) |
| 595 | list_start = 0; | 618 | list_start = nullptr; |
| 596 | do | 619 | do |
| 597 | { | 620 | { |
| 598 | LinkedListItem<T>* next = list_cur->next; | 621 | LinkedListItem<T>* next = list_cur->next; |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index c18afe119..67b3679b0 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common_types.h" | ||
| 8 | #include <cstdlib> | ||
| 9 | |||
| 7 | #ifdef _WIN32 | 10 | #ifdef _WIN32 |
| 8 | #define SLEEP(x) Sleep(x) | 11 | #define SLEEP(x) Sleep(x) |
| 9 | #else | 12 | #else |
| @@ -17,7 +20,7 @@ template<> struct CompileTimeAssert<true> {}; | |||
| 17 | #define b2(x) ( (x) | ( (x) >> 1) ) | 20 | #define b2(x) ( (x) | ( (x) >> 1) ) |
| 18 | #define b4(x) ( b2(x) | ( b2(x) >> 2) ) | 21 | #define b4(x) ( b2(x) | ( b2(x) >> 2) ) |
| 19 | #define b8(x) ( b4(x) | ( b4(x) >> 4) ) | 22 | #define b8(x) ( b4(x) | ( b4(x) >> 4) ) |
| 20 | #define b16(x) ( b8(x) | ( b8(x) >> 8) ) | 23 | #define b16(x) ( b8(x) | ( b8(x) >> 8) ) |
| 21 | #define b32(x) (b16(x) | (b16(x) >>16) ) | 24 | #define b32(x) (b16(x) | (b16(x) >>16) ) |
| 22 | #define ROUND_UP_POW2(x) (b32(x - 1) + 1) | 25 | #define ROUND_UP_POW2(x) (b32(x - 1) + 1) |
| 23 | 26 | ||
| @@ -73,18 +76,20 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| 73 | } | 76 | } |
| 74 | 77 | ||
| 75 | #else // WIN32 | 78 | #else // WIN32 |
| 79 | #include <locale.h> | ||
| 80 | |||
| 76 | // Function Cross-Compatibility | 81 | // Function Cross-Compatibility |
| 77 | #define strcasecmp _stricmp | 82 | #define strcasecmp _stricmp |
| 78 | #define strncasecmp _strnicmp | 83 | #define strncasecmp _strnicmp |
| 79 | #define unlink _unlink | 84 | #define unlink _unlink |
| 80 | #define snprintf _snprintf | 85 | #define snprintf _snprintf |
| 81 | #define vscprintf _vscprintf | 86 | #define vscprintf _vscprintf |
| 82 | 87 | ||
| 83 | // Locale Cross-Compatibility | 88 | // Locale Cross-Compatibility |
| 84 | #define locale_t _locale_t | 89 | #define locale_t _locale_t |
| 85 | #define freelocale _free_locale | 90 | #define freelocale _free_locale |
| 86 | #define newlocale(mask, locale, base) _create_locale(mask, locale) | 91 | #define newlocale(mask, locale, base) _create_locale(mask, locale) |
| 87 | 92 | ||
| 88 | #define LC_GLOBAL_LOCALE ((locale_t)-1) | 93 | #define LC_GLOBAL_LOCALE ((locale_t)-1) |
| 89 | #define LC_ALL_MASK LC_ALL | 94 | #define LC_ALL_MASK LC_ALL |
| 90 | #define LC_COLLATE_MASK LC_COLLATE | 95 | #define LC_COLLATE_MASK LC_COLLATE |
| @@ -92,7 +97,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| 92 | #define LC_MONETARY_MASK LC_MONETARY | 97 | #define LC_MONETARY_MASK LC_MONETARY |
| 93 | #define LC_NUMERIC_MASK LC_NUMERIC | 98 | #define LC_NUMERIC_MASK LC_NUMERIC |
| 94 | #define LC_TIME_MASK LC_TIME | 99 | #define LC_TIME_MASK LC_TIME |
| 95 | 100 | ||
| 96 | inline locale_t uselocale(locale_t new_locale) | 101 | inline locale_t uselocale(locale_t new_locale) |
| 97 | { | 102 | { |
| 98 | // Retrieve the current per thread locale setting | 103 | // Retrieve the current per thread locale setting |
| @@ -106,7 +111,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| 106 | // Restore the global locale | 111 | // Restore the global locale |
| 107 | _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); | 112 | _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); |
| 108 | } | 113 | } |
| 109 | else if(new_locale != NULL) | 114 | else if(new_locale != nullptr) |
| 110 | { | 115 | { |
| 111 | // Configure the thread to set the locale only for this thread | 116 | // Configure the thread to set the locale only for this thread |
| 112 | _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); | 117 | _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); |
| @@ -168,8 +173,8 @@ inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);} | |||
| 168 | inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} | 173 | inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} |
| 169 | inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} | 174 | inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} |
| 170 | #elif _M_ARM | 175 | #elif _M_ARM |
| 171 | inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;} | 176 | inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;} |
| 172 | inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;} | 177 | inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;} |
| 173 | inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);} | 178 | inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);} |
| 174 | #elif __linux__ | 179 | #elif __linux__ |
| 175 | inline u16 swap16(u16 _data) {return bswap_16(_data);} | 180 | inline u16 swap16(u16 _data) {return bswap_16(_data);} |
| @@ -226,7 +231,7 @@ template <typename T> | |||
| 226 | inline T FromBigEndian(T data) | 231 | inline T FromBigEndian(T data) |
| 227 | { | 232 | { |
| 228 | //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types"); | 233 | //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types"); |
| 229 | 234 | ||
| 230 | swap<sizeof(data)>(reinterpret_cast<u8*>(&data)); | 235 | swap<sizeof(data)>(reinterpret_cast<u8*>(&data)); |
| 231 | return data; | 236 | return data; |
| 232 | } | 237 | } |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index ae08d082a..a86889756 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -29,19 +29,6 @@ | |||
| 29 | #endif | 29 | #endif |
| 30 | #endif | 30 | #endif |
| 31 | 31 | ||
| 32 | // Shared data dirs (Sys and shared User for linux) | ||
| 33 | #ifdef _WIN32 | ||
| 34 | #define SYSDATA_DIR "sys" | ||
| 35 | #else | ||
| 36 | #ifdef DATA_DIR | ||
| 37 | #define SYSDATA_DIR DATA_DIR "sys" | ||
| 38 | #define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP | ||
| 39 | #else | ||
| 40 | #define SYSDATA_DIR "sys" | ||
| 41 | #define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP | ||
| 42 | #endif | ||
| 43 | #endif | ||
| 44 | |||
| 45 | // Dirs in both User and Sys | 32 | // Dirs in both User and Sys |
| 46 | #define EUR_DIR "EUR" | 33 | #define EUR_DIR "EUR" |
| 47 | #define USA_DIR "USA" | 34 | #define USA_DIR "USA" |
| @@ -53,6 +40,8 @@ | |||
| 53 | #define MAPS_DIR "maps" | 40 | #define MAPS_DIR "maps" |
| 54 | #define CACHE_DIR "cache" | 41 | #define CACHE_DIR "cache" |
| 55 | #define SDMC_DIR "sdmc" | 42 | #define SDMC_DIR "sdmc" |
| 43 | #define SAVEDATA_DIR "savedata" | ||
| 44 | #define SYSDATA_DIR "sysdata" | ||
| 56 | #define SHADERCACHE_DIR "shader_cache" | 45 | #define SHADERCACHE_DIR "shader_cache" |
| 57 | #define STATESAVES_DIR "state_saves" | 46 | #define STATESAVES_DIR "state_saves" |
| 58 | #define SCREENSHOTS_DIR "screenShots" | 47 | #define SCREENSHOTS_DIR "screenShots" |
| @@ -70,6 +59,9 @@ | |||
| 70 | #define DEBUGGER_CONFIG "debugger.ini" | 59 | #define DEBUGGER_CONFIG "debugger.ini" |
| 71 | #define LOGGER_CONFIG "logger.ini" | 60 | #define LOGGER_CONFIG "logger.ini" |
| 72 | 61 | ||
| 62 | // Sys files | ||
| 63 | #define SHARED_FONT "shared_font.bin" | ||
| 64 | |||
| 73 | // Files in the directory returned by GetUserPath(D_LOGS_IDX) | 65 | // Files in the directory returned by GetUserPath(D_LOGS_IDX) |
| 74 | #define MAIN_LOG "emu.log" | 66 | #define MAIN_LOG "emu.log" |
| 75 | 67 | ||
diff --git a/src/common/common_types.h b/src/common/common_types.h index 7ce6b2240..c74c74f0f 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | * http://code.google.com/p/gekko-gc-emu/ | 22 | * http://code.google.com/p/gekko-gc-emu/ |
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | #pragma once | 25 | #pragma once |
| 26 | 26 | ||
| 27 | #include <cmath> | 27 | #include <cmath> |
| 28 | #include <cstdint> | 28 | #include <cstdint> |
| @@ -41,12 +41,10 @@ typedef std::int64_t s64; ///< 64-bit signed int | |||
| 41 | typedef float f32; ///< 32-bit floating point | 41 | typedef float f32; ///< 32-bit floating point |
| 42 | typedef double f64; ///< 64-bit floating point | 42 | typedef double f64; ///< 64-bit floating point |
| 43 | 43 | ||
| 44 | #include "common/common.h" | ||
| 45 | |||
| 46 | /// Union for fast 16-bit type casting | 44 | /// Union for fast 16-bit type casting |
| 47 | union t16 { | 45 | union t16 { |
| 48 | u8 _u8[2]; ///< 8-bit unsigned char(s) | 46 | u8 _u8[2]; ///< 8-bit unsigned char(s) |
| 49 | u16 _u16; ///< 16-bit unsigned shorts(s) | 47 | u16 _u16; ///< 16-bit unsigned shorts(s) |
| 50 | }; | 48 | }; |
| 51 | 49 | ||
| 52 | /// Union for fast 32-bit type casting | 50 | /// Union for fast 32-bit type casting |
diff --git a/src/common/concurrent_ring_buffer.h b/src/common/concurrent_ring_buffer.h new file mode 100644 index 000000000..2951d93db --- /dev/null +++ b/src/common/concurrent_ring_buffer.h | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <condition_variable> | ||
| 9 | #include <cstdint> | ||
| 10 | #include <mutex> | ||
| 11 | #include <thread> | ||
| 12 | |||
| 13 | #include "common/common.h" // for NonCopyable | ||
| 14 | #include "common/log.h" // for _dbg_assert_ | ||
| 15 | |||
| 16 | namespace Common { | ||
| 17 | |||
| 18 | /** | ||
| 19 | * A MPMC (Multiple-Producer Multiple-Consumer) concurrent ring buffer. This data structure permits | ||
| 20 | * multiple threads to push and pop from a queue of bounded size. | ||
| 21 | */ | ||
| 22 | template <typename T, size_t ArraySize> | ||
| 23 | class ConcurrentRingBuffer : private NonCopyable { | ||
| 24 | public: | ||
| 25 | /// Value returned by the popping functions when the queue has been closed. | ||
| 26 | static const size_t QUEUE_CLOSED = -1; | ||
| 27 | |||
| 28 | ConcurrentRingBuffer() {} | ||
| 29 | |||
| 30 | ~ConcurrentRingBuffer() { | ||
| 31 | // If for whatever reason the queue wasn't completely drained, destroy the left over items. | ||
| 32 | for (size_t i = reader_index, end = writer_index; i != end; i = (i + 1) % ArraySize) { | ||
| 33 | Data()[i].~T(); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Pushes a value to the queue. If the queue is full, this method will block. Does nothing if | ||
| 39 | * the queue is closed. | ||
| 40 | */ | ||
| 41 | void Push(T val) { | ||
| 42 | std::unique_lock<std::mutex> lock(mutex); | ||
| 43 | if (closed) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | |||
| 47 | // If the buffer is full, wait | ||
| 48 | writer.wait(lock, [&]{ | ||
| 49 | return (writer_index + 1) % ArraySize != reader_index; | ||
| 50 | }); | ||
| 51 | |||
| 52 | T* item = &Data()[writer_index]; | ||
| 53 | new (item) T(std::move(val)); | ||
| 54 | |||
| 55 | writer_index = (writer_index + 1) % ArraySize; | ||
| 56 | |||
| 57 | // Wake up waiting readers | ||
| 58 | lock.unlock(); | ||
| 59 | reader.notify_one(); | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will not | ||
| 64 | * block, and might return 0 values if there are no elements in the queue when it is called. | ||
| 65 | * | ||
| 66 | * @return The number of elements stored in `dest`. If the queue has been closed, returns | ||
| 67 | * `QUEUE_CLOSED`. | ||
| 68 | */ | ||
| 69 | size_t Pop(T* dest, size_t dest_len) { | ||
| 70 | std::unique_lock<std::mutex> lock(mutex); | ||
| 71 | if (closed && !CanRead()) { | ||
| 72 | return QUEUE_CLOSED; | ||
| 73 | } | ||
| 74 | return PopInternal(dest, dest_len); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will block | ||
| 79 | * if there are no elements in the queue when it is called. | ||
| 80 | * | ||
| 81 | * @return The number of elements stored in `dest`. If the queue has been closed, returns | ||
| 82 | * `QUEUE_CLOSED`. | ||
| 83 | */ | ||
| 84 | size_t BlockingPop(T* dest, size_t dest_len) { | ||
| 85 | std::unique_lock<std::mutex> lock(mutex); | ||
| 86 | if (closed && !CanRead()) { | ||
| 87 | return QUEUE_CLOSED; | ||
| 88 | } | ||
| 89 | |||
| 90 | while (!CanRead()) { | ||
| 91 | reader.wait(lock); | ||
| 92 | if (closed && !CanRead()) { | ||
| 93 | return QUEUE_CLOSED; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | _dbg_assert_(Common, CanRead()); | ||
| 97 | return PopInternal(dest, dest_len); | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Closes the queue. After calling this method, `Push` operations won't have any effect, and | ||
| 102 | * `PopMany` and `PopManyBlock` will start returning `QUEUE_CLOSED`. This is intended to allow | ||
| 103 | * a graceful shutdown of all consumers. | ||
| 104 | */ | ||
| 105 | void Close() { | ||
| 106 | std::unique_lock<std::mutex> lock(mutex); | ||
| 107 | closed = true; | ||
| 108 | // We need to wake up any reader that are waiting for an item that will never come. | ||
| 109 | lock.unlock(); | ||
| 110 | reader.notify_all(); | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Returns true if `Close()` has been called. | ||
| 114 | bool IsClosed() const { | ||
| 115 | return closed; | ||
| 116 | } | ||
| 117 | |||
| 118 | private: | ||
| 119 | size_t PopInternal(T* dest, size_t dest_len) { | ||
| 120 | size_t output_count = 0; | ||
| 121 | while (output_count < dest_len && CanRead()) { | ||
| 122 | _dbg_assert_(Common, CanRead()); | ||
| 123 | |||
| 124 | T* item = &Data()[reader_index]; | ||
| 125 | T out_val = std::move(*item); | ||
| 126 | item->~T(); | ||
| 127 | |||
| 128 | size_t prev_index = (reader_index + ArraySize - 1) % ArraySize; | ||
| 129 | reader_index = (reader_index + 1) % ArraySize; | ||
| 130 | if (writer_index == prev_index) { | ||
| 131 | writer.notify_one(); | ||
| 132 | } | ||
| 133 | dest[output_count++] = std::move(out_val); | ||
| 134 | } | ||
| 135 | return output_count; | ||
| 136 | } | ||
| 137 | |||
| 138 | bool CanRead() const { | ||
| 139 | return reader_index != writer_index; | ||
| 140 | } | ||
| 141 | |||
| 142 | T* Data() { | ||
| 143 | return static_cast<T*>(static_cast<void*>(&storage)); | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Storage for entries | ||
| 147 | typename std::aligned_storage<ArraySize * sizeof(T), | ||
| 148 | std::alignment_of<T>::value>::type storage; | ||
| 149 | |||
| 150 | /// Data is valid in the half-open interval [reader, writer). If they are `QUEUE_CLOSED` then the | ||
| 151 | /// queue has been closed. | ||
| 152 | size_t writer_index = 0, reader_index = 0; | ||
| 153 | // True if the queue has been closed. | ||
| 154 | bool closed = false; | ||
| 155 | |||
| 156 | /// Mutex that protects the entire data structure. | ||
| 157 | std::mutex mutex; | ||
| 158 | /// Signaling wakes up reader which is waiting for storage to be non-empty. | ||
| 159 | std::condition_variable reader; | ||
| 160 | /// Signaling wakes up writer which is waiting for storage to be non-full. | ||
| 161 | std::condition_variable writer; | ||
| 162 | }; | ||
| 163 | |||
| 164 | } // namespace | ||
diff --git a/src/common/console_listener.cpp b/src/common/console_listener.cpp deleted file mode 100644 index 53f20d754..000000000 --- a/src/common/console_listener.cpp +++ /dev/null | |||
| @@ -1,319 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #ifdef _WIN32 | ||
| 8 | #include <windows.h> | ||
| 9 | #include <array> | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #include "common/common.h" | ||
| 13 | #include "common/log_manager.h" // Common | ||
| 14 | #include "common/console_listener.h" // Common | ||
| 15 | |||
| 16 | ConsoleListener::ConsoleListener() | ||
| 17 | { | ||
| 18 | #ifdef _WIN32 | ||
| 19 | hConsole = NULL; | ||
| 20 | bUseColor = true; | ||
| 21 | #else | ||
| 22 | bUseColor = isatty(fileno(stdout)); | ||
| 23 | #endif | ||
| 24 | } | ||
| 25 | |||
| 26 | ConsoleListener::~ConsoleListener() | ||
| 27 | { | ||
| 28 | Close(); | ||
| 29 | } | ||
| 30 | |||
| 31 | // 100, 100, "Dolphin Log Console" | ||
| 32 | // Open console window - width and height is the size of console window | ||
| 33 | // Name is the window title | ||
| 34 | void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title) | ||
| 35 | { | ||
| 36 | #ifdef _WIN32 | ||
| 37 | if (!GetConsoleWindow()) | ||
| 38 | { | ||
| 39 | // Open the console window and create the window handle for GetStdHandle() | ||
| 40 | AllocConsole(); | ||
| 41 | // Hide | ||
| 42 | if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE); | ||
| 43 | // Save the window handle that AllocConsole() created | ||
| 44 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 45 | // Set the console window title | ||
| 46 | SetConsoleTitle(Common::UTF8ToTStr(Title).c_str()); | ||
| 47 | // Set letter space | ||
| 48 | LetterSpace(80, 4000); | ||
| 49 | //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true); | ||
| 50 | } | ||
| 51 | else | ||
| 52 | { | ||
| 53 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 54 | } | ||
| 55 | #endif | ||
| 56 | } | ||
| 57 | |||
| 58 | void ConsoleListener::UpdateHandle() | ||
| 59 | { | ||
| 60 | #ifdef _WIN32 | ||
| 61 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 62 | #endif | ||
| 63 | } | ||
| 64 | |||
| 65 | // Close the console window and close the eventual file handle | ||
| 66 | void ConsoleListener::Close() | ||
| 67 | { | ||
| 68 | #ifdef _WIN32 | ||
| 69 | if (hConsole == NULL) | ||
| 70 | return; | ||
| 71 | FreeConsole(); | ||
| 72 | hConsole = NULL; | ||
| 73 | #else | ||
| 74 | fflush(NULL); | ||
| 75 | #endif | ||
| 76 | } | ||
| 77 | |||
| 78 | bool ConsoleListener::IsOpen() | ||
| 79 | { | ||
| 80 | #ifdef _WIN32 | ||
| 81 | return (hConsole != NULL); | ||
| 82 | #else | ||
| 83 | return true; | ||
| 84 | #endif | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are | ||
| 89 | dependent on each other, that's the reason for the additional checks. | ||
| 90 | */ | ||
| 91 | void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst) | ||
| 92 | { | ||
| 93 | #ifdef _WIN32 | ||
| 94 | BOOL SB, SW; | ||
| 95 | if (BufferFirst) | ||
| 96 | { | ||
| 97 | // Change screen buffer size | ||
| 98 | COORD Co = {BufferWidth, BufferHeight}; | ||
| 99 | SB = SetConsoleScreenBufferSize(hConsole, Co); | ||
| 100 | // Change the screen buffer window size | ||
| 101 | SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom | ||
| 102 | SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); | ||
| 103 | } | ||
| 104 | else | ||
| 105 | { | ||
| 106 | // Change the screen buffer window size | ||
| 107 | SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom | ||
| 108 | SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); | ||
| 109 | // Change screen buffer size | ||
| 110 | COORD Co = {BufferWidth, BufferHeight}; | ||
| 111 | SB = SetConsoleScreenBufferSize(hConsole, Co); | ||
| 112 | } | ||
| 113 | #endif | ||
| 114 | } | ||
| 115 | void ConsoleListener::LetterSpace(int Width, int Height) | ||
| 116 | { | ||
| 117 | #ifdef _WIN32 | ||
| 118 | // Get console info | ||
| 119 | CONSOLE_SCREEN_BUFFER_INFO ConInfo; | ||
| 120 | GetConsoleScreenBufferInfo(hConsole, &ConInfo); | ||
| 121 | |||
| 122 | // | ||
| 123 | int OldBufferWidth = ConInfo.dwSize.X; | ||
| 124 | int OldBufferHeight = ConInfo.dwSize.Y; | ||
| 125 | int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left); | ||
| 126 | int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top); | ||
| 127 | // | ||
| 128 | int NewBufferWidth = Width; | ||
| 129 | int NewBufferHeight = Height; | ||
| 130 | int NewScreenWidth = NewBufferWidth - 1; | ||
| 131 | int NewScreenHeight = OldScreenHeight; | ||
| 132 | |||
| 133 | // Width | ||
| 134 | BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1)); | ||
| 135 | // Height | ||
| 136 | BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1)); | ||
| 137 | |||
| 138 | // Resize the window too | ||
| 139 | //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true); | ||
| 140 | #endif | ||
| 141 | } | ||
| 142 | #ifdef _WIN32 | ||
| 143 | COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth) | ||
| 144 | { | ||
| 145 | COORD Ret = {0, 0}; | ||
| 146 | // Full rows | ||
| 147 | int Step = (int)floor((float)BytesRead / (float)BufferWidth); | ||
| 148 | Ret.Y += Step; | ||
| 149 | // Partial row | ||
| 150 | Ret.X = BytesRead - (BufferWidth * Step); | ||
| 151 | return Ret; | ||
| 152 | } | ||
| 153 | #endif | ||
| 154 | void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize) | ||
| 155 | { | ||
| 156 | #ifdef _WIN32 | ||
| 157 | // Check size | ||
| 158 | if (Width < 8 || Height < 12) return; | ||
| 159 | |||
| 160 | bool DBef = true; | ||
| 161 | bool DAft = true; | ||
| 162 | std::string SLog = ""; | ||
| 163 | |||
| 164 | const HWND hWnd = GetConsoleWindow(); | ||
| 165 | const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 166 | |||
| 167 | // Get console info | ||
| 168 | CONSOLE_SCREEN_BUFFER_INFO ConInfo; | ||
| 169 | GetConsoleScreenBufferInfo(hConsole, &ConInfo); | ||
| 170 | DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y; | ||
| 171 | |||
| 172 | // --------------------------------------------------------------------- | ||
| 173 | // Save the current text | ||
| 174 | // ------------------------ | ||
| 175 | DWORD cCharsRead = 0; | ||
| 176 | COORD coordScreen = { 0, 0 }; | ||
| 177 | |||
| 178 | static const int MAX_BYTES = 1024 * 16; | ||
| 179 | |||
| 180 | std::vector<std::array<TCHAR, MAX_BYTES>> Str; | ||
| 181 | std::vector<std::array<WORD, MAX_BYTES>> Attr; | ||
| 182 | |||
| 183 | // ReadConsoleOutputAttribute seems to have a limit at this level | ||
| 184 | static const int ReadBufferSize = MAX_BYTES - 32; | ||
| 185 | |||
| 186 | DWORD cAttrRead = ReadBufferSize; | ||
| 187 | DWORD BytesRead = 0; | ||
| 188 | while (BytesRead < BufferSize) | ||
| 189 | { | ||
| 190 | Str.resize(Str.size() + 1); | ||
| 191 | if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead)) | ||
| 192 | SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error"); | ||
| 193 | |||
| 194 | Attr.resize(Attr.size() + 1); | ||
| 195 | if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead)) | ||
| 196 | SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error"); | ||
| 197 | |||
| 198 | // Break on error | ||
| 199 | if (cAttrRead == 0) break; | ||
| 200 | BytesRead += cAttrRead; | ||
| 201 | coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X); | ||
| 202 | } | ||
| 203 | // Letter space | ||
| 204 | int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f); | ||
| 205 | int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f); | ||
| 206 | int LBufWidth = LWidth + 1; | ||
| 207 | int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth); | ||
| 208 | // Change screen buffer size | ||
| 209 | LetterSpace(LBufWidth, LBufHeight); | ||
| 210 | |||
| 211 | |||
| 212 | ClearScreen(true); | ||
| 213 | coordScreen.Y = 0; | ||
| 214 | coordScreen.X = 0; | ||
| 215 | DWORD cCharsWritten = 0; | ||
| 216 | |||
| 217 | int BytesWritten = 0; | ||
| 218 | DWORD cAttrWritten = 0; | ||
| 219 | for (size_t i = 0; i < Attr.size(); i++) | ||
| 220 | { | ||
| 221 | if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten)) | ||
| 222 | SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error"); | ||
| 223 | if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten)) | ||
| 224 | SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error"); | ||
| 225 | |||
| 226 | BytesWritten += cAttrWritten; | ||
| 227 | coordScreen = GetCoordinates(BytesWritten, LBufWidth); | ||
| 228 | } | ||
| 229 | |||
| 230 | const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X; | ||
| 231 | COORD Coo = GetCoordinates(OldCursor, LBufWidth); | ||
| 232 | SetConsoleCursorPosition(hConsole, Coo); | ||
| 233 | |||
| 234 | if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str()); | ||
| 235 | |||
| 236 | // Resize the window too | ||
| 237 | if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true); | ||
| 238 | #endif | ||
| 239 | } | ||
| 240 | |||
| 241 | void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) | ||
| 242 | { | ||
| 243 | #if defined(_WIN32) | ||
| 244 | WORD Color; | ||
| 245 | |||
| 246 | switch (Level) | ||
| 247 | { | ||
| 248 | case OS_LEVEL: // light yellow | ||
| 249 | Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; | ||
| 250 | break; | ||
| 251 | case NOTICE_LEVEL: // light green | ||
| 252 | Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; | ||
| 253 | break; | ||
| 254 | case ERROR_LEVEL: // light red | ||
| 255 | Color = FOREGROUND_RED | FOREGROUND_INTENSITY; | ||
| 256 | break; | ||
| 257 | case WARNING_LEVEL: // light purple | ||
| 258 | Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; | ||
| 259 | break; | ||
| 260 | case INFO_LEVEL: // cyan | ||
| 261 | Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; | ||
| 262 | break; | ||
| 263 | case DEBUG_LEVEL: // gray | ||
| 264 | Color = FOREGROUND_INTENSITY; | ||
| 265 | break; | ||
| 266 | default: // off-white | ||
| 267 | Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | SetConsoleTextAttribute(hConsole, Color); | ||
| 271 | printf(Text); | ||
| 272 | #else | ||
| 273 | char ColorAttr[16] = ""; | ||
| 274 | char ResetAttr[16] = ""; | ||
| 275 | |||
| 276 | if (bUseColor) | ||
| 277 | { | ||
| 278 | strcpy(ResetAttr, "\033[0m"); | ||
| 279 | switch (Level) | ||
| 280 | { | ||
| 281 | case NOTICE_LEVEL: // light green | ||
| 282 | strcpy(ColorAttr, "\033[92m"); | ||
| 283 | break; | ||
| 284 | case ERROR_LEVEL: // light red | ||
| 285 | strcpy(ColorAttr, "\033[91m"); | ||
| 286 | break; | ||
| 287 | case WARNING_LEVEL: // light yellow | ||
| 288 | strcpy(ColorAttr, "\033[93m"); | ||
| 289 | break; | ||
| 290 | default: | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr); | ||
| 295 | #endif | ||
| 296 | } | ||
| 297 | // Clear console screen | ||
| 298 | void ConsoleListener::ClearScreen(bool Cursor) | ||
| 299 | { | ||
| 300 | #if defined(_WIN32) | ||
| 301 | COORD coordScreen = { 0, 0 }; | ||
| 302 | DWORD cCharsWritten; | ||
| 303 | CONSOLE_SCREEN_BUFFER_INFO csbi; | ||
| 304 | DWORD dwConSize; | ||
| 305 | |||
| 306 | HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 307 | |||
| 308 | GetConsoleScreenBufferInfo(hConsole, &csbi); | ||
| 309 | dwConSize = csbi.dwSize.X * csbi.dwSize.Y; | ||
| 310 | // Write space to the entire console | ||
| 311 | FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); | ||
| 312 | GetConsoleScreenBufferInfo(hConsole, &csbi); | ||
| 313 | FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); | ||
| 314 | // Reset cursor | ||
| 315 | if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen); | ||
| 316 | #endif | ||
| 317 | } | ||
| 318 | |||
| 319 | |||
diff --git a/src/common/console_listener.h b/src/common/console_listener.h deleted file mode 100644 index ebd90a105..000000000 --- a/src/common/console_listener.h +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/log_manager.h" | ||
| 8 | |||
| 9 | #ifdef _WIN32 | ||
| 10 | #include <windows.h> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | class ConsoleListener : public LogListener | ||
| 14 | { | ||
| 15 | public: | ||
| 16 | ConsoleListener(); | ||
| 17 | ~ConsoleListener(); | ||
| 18 | |||
| 19 | void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console"); | ||
| 20 | void UpdateHandle(); | ||
| 21 | void Close(); | ||
| 22 | bool IsOpen(); | ||
| 23 | void LetterSpace(int Width, int Height); | ||
| 24 | void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst); | ||
| 25 | void PixelSpace(int Left, int Top, int Width, int Height, bool); | ||
| 26 | #ifdef _WIN32 | ||
| 27 | COORD GetCoordinates(int BytesRead, int BufferWidth); | ||
| 28 | #endif | ||
| 29 | void Log(LogTypes::LOG_LEVELS, const char *Text) override; | ||
| 30 | void ClearScreen(bool Cursor = true); | ||
| 31 | |||
| 32 | private: | ||
| 33 | #ifdef _WIN32 | ||
| 34 | HWND GetHwnd(void); | ||
| 35 | HANDLE hConsole; | ||
| 36 | #endif | ||
| 37 | bool bUseColor; | ||
| 38 | }; | ||
diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 4d09acb8b..4cb94fed1 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h | |||
| @@ -9,17 +9,33 @@ | |||
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "common/key_map.h" | 10 | #include "common/key_map.h" |
| 11 | 11 | ||
| 12 | // Abstraction class used to provide an interface between emulation code and the frontend (e.g. SDL, | 12 | /** |
| 13 | // QGLWidget, GLFW, etc...) | 13 | * Abstraction class used to provide an interface between emulation code and the frontend |
| 14 | * (e.g. SDL, QGLWidget, GLFW, etc...). | ||
| 15 | * | ||
| 16 | * Design notes on the interaction between EmuWindow and the emulation core: | ||
| 17 | * - Generally, decisions on anything visible to the user should be left up to the GUI. | ||
| 18 | * For example, the emulation core should not try to dictate some window title or size. | ||
| 19 | * This stuff is not the core's business and only causes problems with regards to thread-safety | ||
| 20 | * anyway. | ||
| 21 | * - Under certain circumstances, it may be desirable for the core to politely request the GUI | ||
| 22 | * to set e.g. a minimum window size. However, the GUI should always be free to ignore any | ||
| 23 | * such hints. | ||
| 24 | * - EmuWindow may expose some of its state as read-only to the emulation core, however care | ||
| 25 | * should be taken to make sure the provided information is self-consistent. This requires | ||
| 26 | * some sort of synchronization (most of this is still a TODO). | ||
| 27 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | ||
| 28 | * re-read the upper points again and think about it if you don't see this. | ||
| 29 | */ | ||
| 14 | class EmuWindow | 30 | class EmuWindow |
| 15 | { | 31 | { |
| 16 | |||
| 17 | public: | 32 | public: |
| 18 | /// Data structure to store an emuwindow configuration | 33 | /// Data structure to store emuwindow configuration |
| 19 | struct WindowConfig { | 34 | struct WindowConfig { |
| 20 | bool fullscreen; | 35 | bool fullscreen; |
| 21 | int res_width; | 36 | int res_width; |
| 22 | int res_height; | 37 | int res_height; |
| 38 | std::pair<unsigned,unsigned> min_client_area_size; | ||
| 23 | }; | 39 | }; |
| 24 | 40 | ||
| 25 | /// Swap buffers to display the next frame | 41 | /// Swap buffers to display the next frame |
| @@ -42,52 +58,96 @@ public: | |||
| 42 | /// Signals a key release action to the HID module | 58 | /// Signals a key release action to the HID module |
| 43 | static void KeyReleased(KeyMap::HostDeviceKey key); | 59 | static void KeyReleased(KeyMap::HostDeviceKey key); |
| 44 | 60 | ||
| 45 | WindowConfig GetConfig() const { | 61 | /** |
| 46 | return m_config; | 62 | * Returns currently active configuration. |
| 63 | * @note Accesses to the returned object need not be consistent because it may be modified in another thread | ||
| 64 | */ | ||
| 65 | const WindowConfig& GetActiveConfig() const { | ||
| 66 | return active_config; | ||
| 47 | } | 67 | } |
| 48 | 68 | ||
| 69 | /** | ||
| 70 | * Requests the internal configuration to be replaced by the specified argument at some point in the future. | ||
| 71 | * @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active. | ||
| 72 | */ | ||
| 49 | void SetConfig(const WindowConfig& val) { | 73 | void SetConfig(const WindowConfig& val) { |
| 50 | m_config = val; | 74 | config = val; |
| 51 | } | ||
| 52 | |||
| 53 | int GetClientAreaWidth() const { | ||
| 54 | return m_client_area_width; | ||
| 55 | } | 75 | } |
| 56 | 76 | ||
| 57 | void SetClientAreaWidth(const int val) { | 77 | /** |
| 58 | m_client_area_width = val; | 78 | * Gets the framebuffer size in pixels. |
| 79 | * @note This method is thread-safe | ||
| 80 | */ | ||
| 81 | const std::pair<unsigned,unsigned> GetFramebufferSize() const { | ||
| 82 | return framebuffer_size; | ||
| 59 | } | 83 | } |
| 60 | 84 | ||
| 61 | int GetClientAreaHeight() const { | 85 | /** |
| 62 | return m_client_area_height; | 86 | * Gets window client area width in logical coordinates. |
| 87 | * @note For high-DPI systems, this is smaller than the framebuffer size. | ||
| 88 | * @note This method is thread-safe | ||
| 89 | */ | ||
| 90 | std::pair<unsigned,unsigned> GetClientAreaSize() const { | ||
| 91 | return std::make_pair(client_area_width, client_area_height); | ||
| 63 | } | 92 | } |
| 64 | 93 | ||
| 65 | void SetClientAreaHeight(const int val) { | 94 | protected: |
| 66 | m_client_area_height = val; | 95 | EmuWindow() |
| 96 | { | ||
| 97 | // TODO: Find a better place to set this. | ||
| 98 | config.min_client_area_size = std::make_pair(400u, 480u); | ||
| 99 | active_config = config; | ||
| 67 | } | 100 | } |
| 101 | virtual ~EmuWindow() {} | ||
| 68 | 102 | ||
| 69 | std::string GetWindowTitle() const { | 103 | /** |
| 70 | return m_window_title; | 104 | * Processes any pending configuration changes from the last SetConfig call. |
| 105 | * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration | ||
| 106 | * field changed. | ||
| 107 | * @note Implementations will usually want to call this from the GUI thread. | ||
| 108 | * @todo Actually call this in existing implementations. | ||
| 109 | */ | ||
| 110 | void ProcessConfigurationChanges() { | ||
| 111 | // TODO: For proper thread safety, we should eventually implement a proper | ||
| 112 | // multiple-writer/single-reader queue... | ||
| 113 | |||
| 114 | if (config.min_client_area_size != active_config.min_client_area_size) { | ||
| 115 | OnMinimalClientAreaChangeRequest(config.min_client_area_size); | ||
| 116 | config.min_client_area_size = active_config.min_client_area_size; | ||
| 117 | } | ||
| 71 | } | 118 | } |
| 72 | 119 | ||
| 73 | void SetWindowTitle(std::string val) { | 120 | /** |
| 74 | m_window_title = val; | 121 | * Update internal framebuffer size with the given parameter. |
| 122 | * @note EmuWindow implementations will usually use this in window resize event handlers. | ||
| 123 | */ | ||
| 124 | void NotifyFramebufferSizeChanged(const std::pair<unsigned,unsigned>& size) { | ||
| 125 | framebuffer_size = size; | ||
| 75 | } | 126 | } |
| 76 | 127 | ||
| 77 | protected: | 128 | /** |
| 78 | EmuWindow(): | 129 | * Update internal client area size with the given parameter. |
| 79 | m_client_area_width(640), | 130 | * @note EmuWindow implementations will usually use this in window resize event handlers. |
| 80 | m_client_area_height(480), | 131 | */ |
| 81 | m_window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) | 132 | void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) { |
| 82 | {} | 133 | client_area_width = size.first; |
| 83 | virtual ~EmuWindow() {} | 134 | client_area_height = size.second; |
| 135 | } | ||
| 84 | 136 | ||
| 85 | std::string m_window_title; ///< Current window title, should be used by window impl. | 137 | private: |
| 138 | /** | ||
| 139 | * Handler called when the minimal client area was requested to be changed via SetConfig. | ||
| 140 | * For the request to be honored, EmuWindow implementations will usually reimplement this function. | ||
| 141 | */ | ||
| 142 | virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||
| 143 | // By default, ignore this request and do nothing. | ||
| 144 | } | ||
| 86 | 145 | ||
| 87 | int m_client_area_width; ///< Current client width, should be set by window impl. | 146 | std::pair<unsigned,unsigned> framebuffer_size; |
| 88 | int m_client_area_height; ///< Current client height, should be set by window impl. | ||
| 89 | 147 | ||
| 90 | private: | 148 | unsigned client_area_width; ///< Current client width, should be set by window impl. |
| 91 | WindowConfig m_config; ///< Internal configuration | 149 | unsigned client_area_height; ///< Current client height, should be set by window impl. |
| 92 | 150 | ||
| 151 | WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) | ||
| 152 | WindowConfig active_config; ///< Internal active configuration | ||
| 93 | }; | 153 | }; |
diff --git a/src/common/extended_trace.cpp b/src/common/extended_trace.cpp index 9cd0398ed..cf7c346d4 100644 --- a/src/common/extended_trace.cpp +++ b/src/common/extended_trace.cpp | |||
| @@ -29,7 +29,7 @@ using namespace std; | |||
| 29 | void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) | 29 | void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) |
| 30 | { | 30 | { |
| 31 | #if defined(UNICODE)||defined(_UNICODE) | 31 | #if defined(UNICODE)||defined(_UNICODE) |
| 32 | ULONG index = 0; | 32 | ULONG index = 0; |
| 33 | PCSTR lpAct = lpszIn; | 33 | PCSTR lpAct = lpszIn; |
| 34 | 34 | ||
| 35 | for( ; ; lpAct++ ) | 35 | for( ; ; lpAct++ ) |
| @@ -37,7 +37,7 @@ void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) | |||
| 37 | lpszOut[index++] = (TCHAR)(*lpAct); | 37 | lpszOut[index++] = (TCHAR)(*lpAct); |
| 38 | if ( *lpAct == 0 ) | 38 | if ( *lpAct == 0 ) |
| 39 | break; | 39 | break; |
| 40 | } | 40 | } |
| 41 | #else | 41 | #else |
| 42 | // This is trivial :) | 42 | // This is trivial :) |
| 43 | strcpy( lpszOut, lpszIn ); | 43 | strcpy( lpszOut, lpszIn ); |
| @@ -82,7 +82,7 @@ static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) | |||
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | // Add user defined path | 84 | // Add user defined path |
| 85 | if ( lpszIniPath != NULL ) | 85 | if ( lpszIniPath != nullptr ) |
| 86 | if ( lpszIniPath[0] != '\0' ) | 86 | if ( lpszIniPath[0] != '\0' ) |
| 87 | { | 87 | { |
| 88 | strcat( lpszSymbolPath, ";" ); | 88 | strcat( lpszSymbolPath, ";" ); |
| @@ -101,7 +101,7 @@ BOOL InitSymInfo( PCSTR lpszInitialSymbolPath ) | |||
| 101 | CHAR lpszSymbolPath[BUFFERSIZE]; | 101 | CHAR lpszSymbolPath[BUFFERSIZE]; |
| 102 | DWORD symOptions = SymGetOptions(); | 102 | DWORD symOptions = SymGetOptions(); |
| 103 | 103 | ||
| 104 | symOptions |= SYMOPT_LOAD_LINES; | 104 | symOptions |= SYMOPT_LOAD_LINES; |
| 105 | symOptions &= ~SYMOPT_UNDNAME; | 105 | symOptions &= ~SYMOPT_UNDNAME; |
| 106 | SymSetOptions( symOptions ); | 106 | SymSetOptions( symOptions ); |
| 107 | InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); | 107 | InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); |
| @@ -138,7 +138,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
| 138 | DWORD dwSymSize = 10000; | 138 | DWORD dwSymSize = 10000; |
| 139 | TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); | 139 | TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); |
| 140 | CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; | 140 | CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; |
| 141 | LPTSTR lpszParamSep = NULL; | 141 | LPTSTR lpszParamSep = nullptr; |
| 142 | LPTSTR lpszParsed = lpszUnDSymbol; | 142 | LPTSTR lpszParsed = lpszUnDSymbol; |
| 143 | PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); | 143 | PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); |
| 144 | 144 | ||
| @@ -153,15 +153,15 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
| 153 | #ifndef _M_X64 | 153 | #ifndef _M_X64 |
| 154 | DWORD dwDisp = 0; | 154 | DWORD dwDisp = 0; |
| 155 | if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) | 155 | if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) |
| 156 | #else | 156 | #else |
| 157 | //makes it compile but hell im not sure if this works... | 157 | //makes it compile but hell im not sure if this works... |
| 158 | DWORD64 dwDisp = 0; | 158 | DWORD64 dwDisp = 0; |
| 159 | if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) | 159 | if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) |
| 160 | #endif | 160 | #endif |
| 161 | { | 161 | { |
| 162 | // Make the symbol readable for humans | 162 | // Make the symbol readable for humans |
| 163 | UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, | 163 | UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, |
| 164 | UNDNAME_COMPLETE | | 164 | UNDNAME_COMPLETE | |
| 165 | UNDNAME_NO_THISTYPE | | 165 | UNDNAME_NO_THISTYPE | |
| 166 | UNDNAME_NO_SPECIAL_SYMS | | 166 | UNDNAME_NO_SPECIAL_SYMS | |
| 167 | UNDNAME_NO_MEMBER_TYPE | | 167 | UNDNAME_NO_MEMBER_TYPE | |
| @@ -187,13 +187,13 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
| 187 | 187 | ||
| 188 | // Let's go through the stack, and modify the function prototype, and insert the actual | 188 | // Let's go through the stack, and modify the function prototype, and insert the actual |
| 189 | // parameter values from the stack | 189 | // parameter values from the stack |
| 190 | if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) | 190 | if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr) |
| 191 | { | 191 | { |
| 192 | ULONG index = 0; | 192 | ULONG index = 0; |
| 193 | for( ; ; index++ ) | 193 | for( ; ; index++ ) |
| 194 | { | 194 | { |
| 195 | lpszParamSep = _tcschr( lpszParsed, _T(',') ); | 195 | lpszParamSep = _tcschr( lpszParsed, _T(',') ); |
| 196 | if ( lpszParamSep == NULL ) | 196 | if ( lpszParamSep == nullptr ) |
| 197 | break; | 197 | break; |
| 198 | 198 | ||
| 199 | *lpszParamSep = _T('\0'); | 199 | *lpszParamSep = _T('\0'); |
| @@ -205,7 +205,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | lpszParamSep = _tcschr( lpszParsed, _T(')') ); | 207 | lpszParamSep = _tcschr( lpszParsed, _T(')') ); |
| 208 | if ( lpszParamSep != NULL ) | 208 | if ( lpszParamSep != nullptr ) |
| 209 | { | 209 | { |
| 210 | *lpszParamSep = _T('\0'); | 210 | *lpszParamSep = _T('\0'); |
| 211 | 211 | ||
| @@ -219,7 +219,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
| 219 | _tcscat( lpszSymbol, lpszParsed ); | 219 | _tcscat( lpszSymbol, lpszParsed ); |
| 220 | 220 | ||
| 221 | ret = TRUE; | 221 | ret = TRUE; |
| 222 | } | 222 | } |
| 223 | GlobalFree( pSym ); | 223 | GlobalFree( pSym ); |
| 224 | 224 | ||
| 225 | return ret; | 225 | return ret; |
| @@ -248,7 +248,7 @@ static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) | |||
| 248 | PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); | 248 | PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); |
| 249 | TCHAR fname[_MAX_FNAME]; | 249 | TCHAR fname[_MAX_FNAME]; |
| 250 | TCHAR ext[_MAX_EXT]; | 250 | TCHAR ext[_MAX_EXT]; |
| 251 | _tsplitpath(lpszFileName, NULL, NULL, fname, ext); | 251 | _tsplitpath(lpszFileName, nullptr, nullptr, fname, ext); |
| 252 | _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); | 252 | _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); |
| 253 | ret = TRUE; | 253 | ret = TRUE; |
| 254 | } | 254 | } |
| @@ -325,23 +325,23 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file ) | |||
| 325 | 325 | ||
| 326 | PrintFunctionAndSourceInfo(file, callStack); | 326 | PrintFunctionAndSourceInfo(file, callStack); |
| 327 | 327 | ||
| 328 | for( ULONG index = 0; ; index++ ) | 328 | for( ULONG index = 0; ; index++ ) |
| 329 | { | 329 | { |
| 330 | bResult = StackWalk( | 330 | bResult = StackWalk( |
| 331 | IMAGE_FILE_MACHINE_I386, | 331 | IMAGE_FILE_MACHINE_I386, |
| 332 | hProcess, | 332 | hProcess, |
| 333 | hThread, | 333 | hThread, |
| 334 | &callStack, | 334 | &callStack, |
| 335 | NULL, | 335 | nullptr, |
| 336 | NULL, | 336 | nullptr, |
| 337 | SymFunctionTableAccess, | 337 | SymFunctionTableAccess, |
| 338 | SymGetModuleBase, | 338 | SymGetModuleBase, |
| 339 | NULL); | 339 | nullptr); |
| 340 | 340 | ||
| 341 | if ( index == 0 ) | 341 | if ( index == 0 ) |
| 342 | continue; | 342 | continue; |
| 343 | 343 | ||
| 344 | if( !bResult || callStack.AddrFrame.Offset == 0 ) | 344 | if( !bResult || callStack.AddrFrame.Offset == 0 ) |
| 345 | break; | 345 | break; |
| 346 | 346 | ||
| 347 | PrintFunctionAndSourceInfo(file, callStack); | 347 | PrintFunctionAndSourceInfo(file, callStack); |
| @@ -382,23 +382,23 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, | |||
| 382 | 382 | ||
| 383 | PrintFunctionAndSourceInfo(file, callStack); | 383 | PrintFunctionAndSourceInfo(file, callStack); |
| 384 | 384 | ||
| 385 | for( ULONG index = 0; ; index++ ) | 385 | for( ULONG index = 0; ; index++ ) |
| 386 | { | 386 | { |
| 387 | bResult = StackWalk( | 387 | bResult = StackWalk( |
| 388 | IMAGE_FILE_MACHINE_I386, | 388 | IMAGE_FILE_MACHINE_I386, |
| 389 | hProcess, | 389 | hProcess, |
| 390 | hThread, | 390 | hThread, |
| 391 | &callStack, | 391 | &callStack, |
| 392 | NULL, | 392 | nullptr, |
| 393 | NULL, | 393 | nullptr, |
| 394 | SymFunctionTableAccess, | 394 | SymFunctionTableAccess, |
| 395 | SymGetModuleBase, | 395 | SymGetModuleBase, |
| 396 | NULL); | 396 | nullptr); |
| 397 | 397 | ||
| 398 | if ( index == 0 ) | 398 | if ( index == 0 ) |
| 399 | continue; | 399 | continue; |
| 400 | 400 | ||
| 401 | if( !bResult || callStack.AddrFrame.Offset == 0 ) | 401 | if( !bResult || callStack.AddrFrame.Offset == 0 ) |
| 402 | break; | 402 | break; |
| 403 | 403 | ||
| 404 | PrintFunctionAndSourceInfo(file, callStack); | 404 | PrintFunctionAndSourceInfo(file, callStack); |
diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h index 78a8f561d..b426e6596 100644 --- a/src/common/fifo_queue.h +++ b/src/common/fifo_queue.h | |||
| @@ -45,7 +45,7 @@ public: | |||
| 45 | // create the element, add it to the queue | 45 | // create the element, add it to the queue |
| 46 | m_write_ptr->current = new T(std::forward<Arg>(t)); | 46 | m_write_ptr->current = new T(std::forward<Arg>(t)); |
| 47 | // set the next pointer to a new element ptr | 47 | // set the next pointer to a new element ptr |
| 48 | // then advance the write pointer | 48 | // then advance the write pointer |
| 49 | m_write_ptr = m_write_ptr->next = new ElementPtr(); | 49 | m_write_ptr = m_write_ptr->next = new ElementPtr(); |
| 50 | Common::AtomicIncrement(m_size); | 50 | Common::AtomicIncrement(m_size); |
| 51 | } | 51 | } |
| @@ -57,7 +57,7 @@ public: | |||
| 57 | // advance the read pointer | 57 | // advance the read pointer |
| 58 | m_read_ptr = m_read_ptr->next; | 58 | m_read_ptr = m_read_ptr->next; |
| 59 | // set the next element to NULL to stop the recursive deletion | 59 | // set the next element to NULL to stop the recursive deletion |
| 60 | tmpptr->next = NULL; | 60 | tmpptr->next = nullptr; |
| 61 | delete tmpptr; // this also deletes the element | 61 | delete tmpptr; // this also deletes the element |
| 62 | } | 62 | } |
| 63 | 63 | ||
| @@ -86,7 +86,7 @@ private: | |||
| 86 | class ElementPtr | 86 | class ElementPtr |
| 87 | { | 87 | { |
| 88 | public: | 88 | public: |
| 89 | ElementPtr() : current(NULL), next(NULL) {} | 89 | ElementPtr() : current(nullptr), next(nullptr) {} |
| 90 | 90 | ||
| 91 | ~ElementPtr() | 91 | ~ElementPtr() |
| 92 | { | 92 | { |
diff --git a/src/common/file_search.cpp b/src/common/file_search.cpp index 63580f688..bfb54ce72 100644 --- a/src/common/file_search.cpp +++ b/src/common/file_search.cpp | |||
| @@ -43,7 +43,7 @@ void CFileSearch::FindFiles(const std::string& _searchString, const std::string& | |||
| 43 | bool bkeepLooping = true; | 43 | bool bkeepLooping = true; |
| 44 | 44 | ||
| 45 | while (bkeepLooping) | 45 | while (bkeepLooping) |
| 46 | { | 46 | { |
| 47 | if (findData.cFileName[0] != '.') | 47 | if (findData.cFileName[0] != '.') |
| 48 | { | 48 | { |
| 49 | std::string strFilename; | 49 | std::string strFilename; |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 35da07306..42cdf3262 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename) | |||
| 88 | #endif | 88 | #endif |
| 89 | 89 | ||
| 90 | if (result < 0) { | 90 | if (result < 0) { |
| 91 | WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", | 91 | LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", |
| 92 | filename.c_str(), GetLastErrorMsg()); | 92 | filename.c_str(), GetLastErrorMsg()); |
| 93 | return false; | 93 | return false; |
| 94 | } | 94 | } |
| @@ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename) | |||
| 100 | // Doesn't supports deleting a directory | 100 | // Doesn't supports deleting a directory |
| 101 | bool Delete(const std::string &filename) | 101 | bool Delete(const std::string &filename) |
| 102 | { | 102 | { |
| 103 | INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); | 103 | LOG_INFO(Common_Filesystem, "file %s", filename.c_str()); |
| 104 | 104 | ||
| 105 | // Return true because we care about the file no | 105 | // Return true because we care about the file no |
| 106 | // being there, not the actual delete. | 106 | // being there, not the actual delete. |
| 107 | if (!Exists(filename)) | 107 | if (!Exists(filename)) |
| 108 | { | 108 | { |
| 109 | WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); | 109 | LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str()); |
| 110 | return true; | 110 | return true; |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | // We can't delete a directory | 113 | // We can't delete a directory |
| 114 | if (IsDirectory(filename)) | 114 | if (IsDirectory(filename)) |
| 115 | { | 115 | { |
| 116 | WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); | 116 | LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str()); |
| 117 | return false; | 117 | return false; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | #ifdef _WIN32 | 120 | #ifdef _WIN32 |
| 121 | if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) | 121 | if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) |
| 122 | { | 122 | { |
| 123 | WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", | 123 | LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s", |
| 124 | filename.c_str(), GetLastErrorMsg()); | 124 | filename.c_str(), GetLastErrorMsg()); |
| 125 | return false; | 125 | return false; |
| 126 | } | 126 | } |
| 127 | #else | 127 | #else |
| 128 | if (unlink(filename.c_str()) == -1) { | 128 | if (unlink(filename.c_str()) == -1) { |
| 129 | WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", | 129 | LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s", |
| 130 | filename.c_str(), GetLastErrorMsg()); | 130 | filename.c_str(), GetLastErrorMsg()); |
| 131 | return false; | 131 | return false; |
| 132 | } | 132 | } |
| @@ -138,17 +138,17 @@ bool Delete(const std::string &filename) | |||
| 138 | // Returns true if successful, or path already exists. | 138 | // Returns true if successful, or path already exists. |
| 139 | bool CreateDir(const std::string &path) | 139 | bool CreateDir(const std::string &path) |
| 140 | { | 140 | { |
| 141 | INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); | 141 | LOG_TRACE(Common_Filesystem, "directory %s", path.c_str()); |
| 142 | #ifdef _WIN32 | 142 | #ifdef _WIN32 |
| 143 | if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), NULL)) | 143 | if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr)) |
| 144 | return true; | 144 | return true; |
| 145 | DWORD error = GetLastError(); | 145 | DWORD error = GetLastError(); |
| 146 | if (error == ERROR_ALREADY_EXISTS) | 146 | if (error == ERROR_ALREADY_EXISTS) |
| 147 | { | 147 | { |
| 148 | WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); | 148 | LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str()); |
| 149 | return true; | 149 | return true; |
| 150 | } | 150 | } |
| 151 | ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); | 151 | LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error); |
| 152 | return false; | 152 | return false; |
| 153 | #else | 153 | #else |
| 154 | if (mkdir(path.c_str(), 0755) == 0) | 154 | if (mkdir(path.c_str(), 0755) == 0) |
| @@ -158,11 +158,11 @@ bool CreateDir(const std::string &path) | |||
| 158 | 158 | ||
| 159 | if (err == EEXIST) | 159 | if (err == EEXIST) |
| 160 | { | 160 | { |
| 161 | WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); | 161 | LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str()); |
| 162 | return true; | 162 | return true; |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); | 165 | LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err)); |
| 166 | return false; | 166 | return false; |
| 167 | #endif | 167 | #endif |
| 168 | } | 168 | } |
| @@ -171,11 +171,11 @@ bool CreateDir(const std::string &path) | |||
| 171 | bool CreateFullPath(const std::string &fullPath) | 171 | bool CreateFullPath(const std::string &fullPath) |
| 172 | { | 172 | { |
| 173 | int panicCounter = 100; | 173 | int panicCounter = 100; |
| 174 | INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); | 174 | LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str()); |
| 175 | 175 | ||
| 176 | if (FileUtil::Exists(fullPath)) | 176 | if (FileUtil::Exists(fullPath)) |
| 177 | { | 177 | { |
| 178 | INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); | 178 | LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str()); |
| 179 | return true; | 179 | return true; |
| 180 | } | 180 | } |
| 181 | 181 | ||
| @@ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath) | |||
| 192 | // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") | 192 | // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") |
| 193 | std::string const subPath(fullPath.substr(0, position + 1)); | 193 | std::string const subPath(fullPath.substr(0, position + 1)); |
| 194 | if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { | 194 | if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { |
| 195 | ERROR_LOG(COMMON, "CreateFullPath: directory creation failed"); | 195 | LOG_ERROR(Common, "CreateFullPath: directory creation failed"); |
| 196 | return false; | 196 | return false; |
| 197 | } | 197 | } |
| 198 | 198 | ||
| @@ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath) | |||
| 200 | panicCounter--; | 200 | panicCounter--; |
| 201 | if (panicCounter <= 0) | 201 | if (panicCounter <= 0) |
| 202 | { | 202 | { |
| 203 | ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); | 203 | LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); |
| 204 | return false; | 204 | return false; |
| 205 | } | 205 | } |
| 206 | position++; | 206 | position++; |
| @@ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath) | |||
| 211 | // Deletes a directory filename, returns true on success | 211 | // Deletes a directory filename, returns true on success |
| 212 | bool DeleteDir(const std::string &filename) | 212 | bool DeleteDir(const std::string &filename) |
| 213 | { | 213 | { |
| 214 | INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); | 214 | LOG_INFO(Common_Filesystem, "directory %s", filename.c_str()); |
| 215 | 215 | ||
| 216 | // check if a directory | 216 | // check if a directory |
| 217 | if (!FileUtil::IsDirectory(filename)) | 217 | if (!FileUtil::IsDirectory(filename)) |
| 218 | { | 218 | { |
| 219 | ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); | 219 | LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str()); |
| 220 | return false; | 220 | return false; |
| 221 | } | 221 | } |
| 222 | 222 | ||
| @@ -227,33 +227,33 @@ bool DeleteDir(const std::string &filename) | |||
| 227 | if (rmdir(filename.c_str()) == 0) | 227 | if (rmdir(filename.c_str()) == 0) |
| 228 | return true; | 228 | return true; |
| 229 | #endif | 229 | #endif |
| 230 | ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); | 230 | LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg()); |
| 231 | 231 | ||
| 232 | return false; | 232 | return false; |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | // renames file srcFilename to destFilename, returns true on success | 235 | // renames file srcFilename to destFilename, returns true on success |
| 236 | bool Rename(const std::string &srcFilename, const std::string &destFilename) | 236 | bool Rename(const std::string &srcFilename, const std::string &destFilename) |
| 237 | { | 237 | { |
| 238 | INFO_LOG(COMMON, "Rename: %s --> %s", | 238 | LOG_TRACE(Common_Filesystem, "%s --> %s", |
| 239 | srcFilename.c_str(), destFilename.c_str()); | 239 | srcFilename.c_str(), destFilename.c_str()); |
| 240 | if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) | 240 | if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) |
| 241 | return true; | 241 | return true; |
| 242 | ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", | 242 | LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", |
| 243 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | 243 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); |
| 244 | return false; | 244 | return false; |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | // copies file srcFilename to destFilename, returns true on success | 247 | // copies file srcFilename to destFilename, returns true on success |
| 248 | bool Copy(const std::string &srcFilename, const std::string &destFilename) | 248 | bool Copy(const std::string &srcFilename, const std::string &destFilename) |
| 249 | { | 249 | { |
| 250 | INFO_LOG(COMMON, "Copy: %s --> %s", | 250 | LOG_TRACE(Common_Filesystem, "%s --> %s", |
| 251 | srcFilename.c_str(), destFilename.c_str()); | 251 | srcFilename.c_str(), destFilename.c_str()); |
| 252 | #ifdef _WIN32 | 252 | #ifdef _WIN32 |
| 253 | if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) | 253 | if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) |
| 254 | return true; | 254 | return true; |
| 255 | 255 | ||
| 256 | ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", | 256 | LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s", |
| 257 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | 257 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); |
| 258 | return false; | 258 | return false; |
| 259 | #else | 259 | #else |
| @@ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
| 267 | FILE *input = fopen(srcFilename.c_str(), "rb"); | 267 | FILE *input = fopen(srcFilename.c_str(), "rb"); |
| 268 | if (!input) | 268 | if (!input) |
| 269 | { | 269 | { |
| 270 | ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", | 270 | LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s", |
| 271 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | 271 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); |
| 272 | return false; | 272 | return false; |
| 273 | } | 273 | } |
| @@ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
| 277 | if (!output) | 277 | if (!output) |
| 278 | { | 278 | { |
| 279 | fclose(input); | 279 | fclose(input); |
| 280 | ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", | 280 | LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s", |
| 281 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | 281 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); |
| 282 | return false; | 282 | return false; |
| 283 | } | 283 | } |
| @@ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
| 291 | { | 291 | { |
| 292 | if (ferror(input) != 0) | 292 | if (ferror(input) != 0) |
| 293 | { | 293 | { |
| 294 | ERROR_LOG(COMMON, | 294 | LOG_ERROR(Common_Filesystem, |
| 295 | "Copy: failed reading from source, %s --> %s: %s", | 295 | "failed reading from source, %s --> %s: %s", |
| 296 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | 296 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); |
| 297 | goto bail; | 297 | goto bail; |
| 298 | } | 298 | } |
| @@ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
| 302 | int wnum = fwrite(buffer, sizeof(char), rnum, output); | 302 | int wnum = fwrite(buffer, sizeof(char), rnum, output); |
| 303 | if (wnum != rnum) | 303 | if (wnum != rnum) |
| 304 | { | 304 | { |
| 305 | ERROR_LOG(COMMON, | 305 | LOG_ERROR(Common_Filesystem, |
| 306 | "Copy: failed writing to output, %s --> %s: %s", | 306 | "failed writing to output, %s --> %s: %s", |
| 307 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | 307 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); |
| 308 | goto bail; | 308 | goto bail; |
| 309 | } | 309 | } |
| @@ -326,16 +326,16 @@ u64 GetSize(const std::string &filename) | |||
| 326 | { | 326 | { |
| 327 | if (!Exists(filename)) | 327 | if (!Exists(filename)) |
| 328 | { | 328 | { |
| 329 | WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); | 329 | LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str()); |
| 330 | return 0; | 330 | return 0; |
| 331 | } | 331 | } |
| 332 | 332 | ||
| 333 | if (IsDirectory(filename)) | 333 | if (IsDirectory(filename)) |
| 334 | { | 334 | { |
| 335 | WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); | 335 | LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str()); |
| 336 | return 0; | 336 | return 0; |
| 337 | } | 337 | } |
| 338 | 338 | ||
| 339 | struct stat64 buf; | 339 | struct stat64 buf; |
| 340 | #ifdef _WIN32 | 340 | #ifdef _WIN32 |
| 341 | if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0) | 341 | if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0) |
| @@ -343,12 +343,12 @@ u64 GetSize(const std::string &filename) | |||
| 343 | if (stat64(filename.c_str(), &buf) == 0) | 343 | if (stat64(filename.c_str(), &buf) == 0) |
| 344 | #endif | 344 | #endif |
| 345 | { | 345 | { |
| 346 | DEBUG_LOG(COMMON, "GetSize: %s: %lld", | 346 | LOG_TRACE(Common_Filesystem, "%s: %lld", |
| 347 | filename.c_str(), (long long)buf.st_size); | 347 | filename.c_str(), (long long)buf.st_size); |
| 348 | return buf.st_size; | 348 | return buf.st_size; |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", | 351 | LOG_ERROR(Common_Filesystem, "Stat failed %s: %s", |
| 352 | filename.c_str(), GetLastErrorMsg()); | 352 | filename.c_str(), GetLastErrorMsg()); |
| 353 | return 0; | 353 | return 0; |
| 354 | } | 354 | } |
| @@ -358,7 +358,7 @@ u64 GetSize(const int fd) | |||
| 358 | { | 358 | { |
| 359 | struct stat64 buf; | 359 | struct stat64 buf; |
| 360 | if (fstat64(fd, &buf) != 0) { | 360 | if (fstat64(fd, &buf) != 0) { |
| 361 | ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", | 361 | LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", |
| 362 | fd, GetLastErrorMsg()); | 362 | fd, GetLastErrorMsg()); |
| 363 | return 0; | 363 | return 0; |
| 364 | } | 364 | } |
| @@ -371,27 +371,27 @@ u64 GetSize(FILE *f) | |||
| 371 | // can't use off_t here because it can be 32-bit | 371 | // can't use off_t here because it can be 32-bit |
| 372 | u64 pos = ftello(f); | 372 | u64 pos = ftello(f); |
| 373 | if (fseeko(f, 0, SEEK_END) != 0) { | 373 | if (fseeko(f, 0, SEEK_END) != 0) { |
| 374 | ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | 374 | LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", |
| 375 | f, GetLastErrorMsg()); | 375 | f, GetLastErrorMsg()); |
| 376 | return 0; | 376 | return 0; |
| 377 | } | 377 | } |
| 378 | u64 size = ftello(f); | 378 | u64 size = ftello(f); |
| 379 | if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { | 379 | if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { |
| 380 | ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | 380 | LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s", |
| 381 | f, GetLastErrorMsg()); | 381 | f, GetLastErrorMsg()); |
| 382 | return 0; | 382 | return 0; |
| 383 | } | 383 | } |
| 384 | return size; | 384 | return size; |
| 385 | } | 385 | } |
| 386 | 386 | ||
| 387 | // creates an empty file filename, returns true on success | 387 | // creates an empty file filename, returns true on success |
| 388 | bool CreateEmptyFile(const std::string &filename) | 388 | bool CreateEmptyFile(const std::string &filename) |
| 389 | { | 389 | { |
| 390 | INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); | 390 | LOG_TRACE(Common_Filesystem, "%s", filename.c_str()); |
| 391 | 391 | ||
| 392 | if (!FileUtil::IOFile(filename, "wb")) | 392 | if (!FileUtil::IOFile(filename, "wb")) |
| 393 | { | 393 | { |
| 394 | ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", | 394 | LOG_ERROR(Common_Filesystem, "failed %s: %s", |
| 395 | filename.c_str(), GetLastErrorMsg()); | 395 | filename.c_str(), GetLastErrorMsg()); |
| 396 | return false; | 396 | return false; |
| 397 | } | 397 | } |
| @@ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename) | |||
| 404 | // results into parentEntry. Returns the number of files+directories found | 404 | // results into parentEntry. Returns the number of files+directories found |
| 405 | u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | 405 | u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) |
| 406 | { | 406 | { |
| 407 | INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); | 407 | LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str()); |
| 408 | // How many files + directories we found | 408 | // How many files + directories we found |
| 409 | u32 foundEntries = 0; | 409 | u32 foundEntries = 0; |
| 410 | #ifdef _WIN32 | 410 | #ifdef _WIN32 |
| @@ -423,7 +423,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
| 423 | FSTEntry entry; | 423 | FSTEntry entry; |
| 424 | const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); | 424 | const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); |
| 425 | #else | 425 | #else |
| 426 | struct dirent dirent, *result = NULL; | 426 | struct dirent dirent, *result = nullptr; |
| 427 | 427 | ||
| 428 | DIR *dirp = opendir(directory.c_str()); | 428 | DIR *dirp = opendir(directory.c_str()); |
| 429 | if (!dirp) | 429 | if (!dirp) |
| @@ -437,7 +437,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
| 437 | #endif | 437 | #endif |
| 438 | // check for "." and ".." | 438 | // check for "." and ".." |
| 439 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | 439 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || |
| 440 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | 440 | ((virtualName[0] == '.') && (virtualName[1] == '.') && |
| 441 | (virtualName[2] == '\0'))) | 441 | (virtualName[2] == '\0'))) |
| 442 | continue; | 442 | continue; |
| 443 | entry.virtualName = virtualName; | 443 | entry.virtualName = virtualName; |
| @@ -452,14 +452,14 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
| 452 | foundEntries += (u32)entry.size; | 452 | foundEntries += (u32)entry.size; |
| 453 | } | 453 | } |
| 454 | else | 454 | else |
| 455 | { // is a file | 455 | { // is a file |
| 456 | entry.isDirectory = false; | 456 | entry.isDirectory = false; |
| 457 | entry.size = GetSize(entry.physicalName.c_str()); | 457 | entry.size = GetSize(entry.physicalName.c_str()); |
| 458 | } | 458 | } |
| 459 | ++foundEntries; | 459 | ++foundEntries; |
| 460 | // Push into the tree | 460 | // Push into the tree |
| 461 | parentEntry.children.push_back(entry); | 461 | parentEntry.children.push_back(entry); |
| 462 | #ifdef _WIN32 | 462 | #ifdef _WIN32 |
| 463 | } while (FindNextFile(hFind, &ffd) != 0); | 463 | } while (FindNextFile(hFind, &ffd) != 0); |
| 464 | FindClose(hFind); | 464 | FindClose(hFind); |
| 465 | #else | 465 | #else |
| @@ -474,7 +474,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
| 474 | // Deletes the given directory and anything under it. Returns true on success. | 474 | // Deletes the given directory and anything under it. Returns true on success. |
| 475 | bool DeleteDirRecursively(const std::string &directory) | 475 | bool DeleteDirRecursively(const std::string &directory) |
| 476 | { | 476 | { |
| 477 | INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); | 477 | LOG_TRACE(Common_Filesystem, "%s", directory.c_str()); |
| 478 | #ifdef _WIN32 | 478 | #ifdef _WIN32 |
| 479 | // Find the first file in the directory. | 479 | // Find the first file in the directory. |
| 480 | WIN32_FIND_DATA ffd; | 480 | WIN32_FIND_DATA ffd; |
| @@ -491,7 +491,7 @@ bool DeleteDirRecursively(const std::string &directory) | |||
| 491 | { | 491 | { |
| 492 | const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); | 492 | const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); |
| 493 | #else | 493 | #else |
| 494 | struct dirent dirent, *result = NULL; | 494 | struct dirent dirent, *result = nullptr; |
| 495 | DIR *dirp = opendir(directory.c_str()); | 495 | DIR *dirp = opendir(directory.c_str()); |
| 496 | if (!dirp) | 496 | if (!dirp) |
| 497 | return false; | 497 | return false; |
| @@ -504,7 +504,7 @@ bool DeleteDirRecursively(const std::string &directory) | |||
| 504 | 504 | ||
| 505 | // check for "." and ".." | 505 | // check for "." and ".." |
| 506 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | 506 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || |
| 507 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | 507 | ((virtualName[0] == '.') && (virtualName[1] == '.') && |
| 508 | (virtualName[2] == '\0'))) | 508 | (virtualName[2] == '\0'))) |
| 509 | continue; | 509 | continue; |
| 510 | 510 | ||
| @@ -540,7 +540,7 @@ bool DeleteDirRecursively(const std::string &directory) | |||
| 540 | closedir(dirp); | 540 | closedir(dirp); |
| 541 | #endif | 541 | #endif |
| 542 | FileUtil::DeleteDir(directory); | 542 | FileUtil::DeleteDir(directory); |
| 543 | 543 | ||
| 544 | return true; | 544 | return true; |
| 545 | } | 545 | } |
| 546 | 546 | ||
| @@ -552,7 +552,7 @@ void CopyDir(const std::string &source_path, const std::string &dest_path) | |||
| 552 | if (!FileUtil::Exists(source_path)) return; | 552 | if (!FileUtil::Exists(source_path)) return; |
| 553 | if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path); | 553 | if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path); |
| 554 | 554 | ||
| 555 | struct dirent dirent, *result = NULL; | 555 | struct dirent dirent, *result = nullptr; |
| 556 | DIR *dirp = opendir(source_path.c_str()); | 556 | DIR *dirp = opendir(source_path.c_str()); |
| 557 | if (!dirp) return; | 557 | if (!dirp) return; |
| 558 | 558 | ||
| @@ -585,12 +585,12 @@ void CopyDir(const std::string &source_path, const std::string &dest_path) | |||
| 585 | std::string GetCurrentDir() | 585 | std::string GetCurrentDir() |
| 586 | { | 586 | { |
| 587 | char *dir; | 587 | char *dir; |
| 588 | // Get the current working directory (getcwd uses malloc) | 588 | // Get the current working directory (getcwd uses malloc) |
| 589 | if (!(dir = __getcwd(NULL, 0))) { | 589 | if (!(dir = __getcwd(nullptr, 0))) { |
| 590 | 590 | ||
| 591 | ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", | 591 | LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s", |
| 592 | GetLastErrorMsg()); | 592 | GetLastErrorMsg()); |
| 593 | return NULL; | 593 | return nullptr; |
| 594 | } | 594 | } |
| 595 | std::string strDir = dir; | 595 | std::string strDir = dir; |
| 596 | free(dir); | 596 | free(dir); |
| @@ -604,7 +604,7 @@ bool SetCurrentDir(const std::string &directory) | |||
| 604 | } | 604 | } |
| 605 | 605 | ||
| 606 | #if defined(__APPLE__) | 606 | #if defined(__APPLE__) |
| 607 | std::string GetBundleDirectory() | 607 | std::string GetBundleDirectory() |
| 608 | { | 608 | { |
| 609 | CFURLRef BundleRef; | 609 | CFURLRef BundleRef; |
| 610 | char AppBundlePath[MAXPATHLEN]; | 610 | char AppBundlePath[MAXPATHLEN]; |
| @@ -626,7 +626,7 @@ std::string& GetExeDirectory() | |||
| 626 | if (DolphinPath.empty()) | 626 | if (DolphinPath.empty()) |
| 627 | { | 627 | { |
| 628 | TCHAR Dolphin_exe_Path[2048]; | 628 | TCHAR Dolphin_exe_Path[2048]; |
| 629 | GetModuleFileName(NULL, Dolphin_exe_Path, 2048); | 629 | GetModuleFileName(nullptr, Dolphin_exe_Path, 2048); |
| 630 | DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path); | 630 | DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path); |
| 631 | DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); | 631 | DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); |
| 632 | } | 632 | } |
| @@ -647,7 +647,7 @@ std::string GetSysDirectory() | |||
| 647 | #endif | 647 | #endif |
| 648 | sysDir += DIR_SEP; | 648 | sysDir += DIR_SEP; |
| 649 | 649 | ||
| 650 | INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); | 650 | LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str()); |
| 651 | return sysDir; | 651 | return sysDir; |
| 652 | } | 652 | } |
| 653 | 653 | ||
| @@ -666,8 +666,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 666 | if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) | 666 | if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) |
| 667 | paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; | 667 | paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; |
| 668 | else | 668 | else |
| 669 | paths[D_USER_IDX] = std::string(getenv("HOME") ? | 669 | paths[D_USER_IDX] = std::string(getenv("HOME") ? |
| 670 | getenv("HOME") : getenv("PWD") ? | 670 | getenv("HOME") : getenv("PWD") ? |
| 671 | getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP; | 671 | getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP; |
| 672 | #endif | 672 | #endif |
| 673 | 673 | ||
| @@ -676,6 +676,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 676 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 676 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 677 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 677 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 679 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 680 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | ||
| 679 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 681 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 680 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 682 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 681 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 683 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
| @@ -694,7 +696,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 694 | { | 696 | { |
| 695 | if (!FileUtil::IsDirectory(newPath)) | 697 | if (!FileUtil::IsDirectory(newPath)) |
| 696 | { | 698 | { |
| 697 | WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); | 699 | LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str()); |
| 698 | return paths[DirIDX]; | 700 | return paths[DirIDX]; |
| 699 | } | 701 | } |
| 700 | else | 702 | else |
| @@ -717,6 +719,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 717 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 719 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 718 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 720 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 719 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 721 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 722 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 720 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 723 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 721 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 724 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 722 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 725 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
| @@ -749,23 +752,10 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 749 | paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | 752 | paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; |
| 750 | } | 753 | } |
| 751 | } | 754 | } |
| 752 | 755 | ||
| 753 | return paths[DirIDX]; | 756 | return paths[DirIDX]; |
| 754 | } | 757 | } |
| 755 | 758 | ||
| 756 | //std::string GetThemeDir(const std::string& theme_name) | ||
| 757 | //{ | ||
| 758 | // std::string dir = FileUtil::GetUserPath(D_THEMES_IDX) + theme_name + "/"; | ||
| 759 | // | ||
| 760 | //#if !defined(_WIN32) | ||
| 761 | // // If theme does not exist in user's dir load from shared directory | ||
| 762 | // if (!FileUtil::Exists(dir)) | ||
| 763 | // dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/"; | ||
| 764 | //#endif | ||
| 765 | // | ||
| 766 | // return dir; | ||
| 767 | //} | ||
| 768 | |||
| 769 | size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename) | 759 | size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename) |
| 770 | { | 760 | { |
| 771 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | 761 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); |
| @@ -826,7 +816,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam | |||
| 826 | } | 816 | } |
| 827 | 817 | ||
| 828 | IOFile::IOFile() | 818 | IOFile::IOFile() |
| 829 | : m_file(NULL), m_good(true) | 819 | : m_file(nullptr), m_good(true) |
| 830 | {} | 820 | {} |
| 831 | 821 | ||
| 832 | IOFile::IOFile(std::FILE* file) | 822 | IOFile::IOFile(std::FILE* file) |
| @@ -834,7 +824,7 @@ IOFile::IOFile(std::FILE* file) | |||
| 834 | {} | 824 | {} |
| 835 | 825 | ||
| 836 | IOFile::IOFile(const std::string& filename, const char openmode[]) | 826 | IOFile::IOFile(const std::string& filename, const char openmode[]) |
| 837 | : m_file(NULL), m_good(true) | 827 | : m_file(nullptr), m_good(true) |
| 838 | { | 828 | { |
| 839 | Open(filename, openmode); | 829 | Open(filename, openmode); |
| 840 | } | 830 | } |
| @@ -845,7 +835,7 @@ IOFile::~IOFile() | |||
| 845 | } | 835 | } |
| 846 | 836 | ||
| 847 | IOFile::IOFile(IOFile&& other) | 837 | IOFile::IOFile(IOFile&& other) |
| 848 | : m_file(NULL), m_good(true) | 838 | : m_file(nullptr), m_good(true) |
| 849 | { | 839 | { |
| 850 | Swap(other); | 840 | Swap(other); |
| 851 | } | 841 | } |
| @@ -880,14 +870,14 @@ bool IOFile::Close() | |||
| 880 | if (!IsOpen() || 0 != std::fclose(m_file)) | 870 | if (!IsOpen() || 0 != std::fclose(m_file)) |
| 881 | m_good = false; | 871 | m_good = false; |
| 882 | 872 | ||
| 883 | m_file = NULL; | 873 | m_file = nullptr; |
| 884 | return m_good; | 874 | return m_good; |
| 885 | } | 875 | } |
| 886 | 876 | ||
| 887 | std::FILE* IOFile::ReleaseHandle() | 877 | std::FILE* IOFile::ReleaseHandle() |
| 888 | { | 878 | { |
| 889 | std::FILE* const ret = m_file; | 879 | std::FILE* const ret = m_file; |
| 890 | m_file = NULL; | 880 | m_file = nullptr; |
| 891 | return ret; | 881 | return ret; |
| 892 | } | 882 | } |
| 893 | 883 | ||
diff --git a/src/common/file_util.h b/src/common/file_util.h index 173ce6623..e691b6139 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -16,33 +16,35 @@ | |||
| 16 | 16 | ||
| 17 | // User directory indices for GetUserPath | 17 | // User directory indices for GetUserPath |
| 18 | enum { | 18 | enum { |
| 19 | D_USER_IDX, | 19 | D_USER_IDX, |
| 20 | D_ROOT_IDX, | 20 | D_ROOT_IDX, |
| 21 | D_CONFIG_IDX, | 21 | D_CONFIG_IDX, |
| 22 | D_GAMECONFIG_IDX, | 22 | D_GAMECONFIG_IDX, |
| 23 | D_MAPS_IDX, | 23 | D_MAPS_IDX, |
| 24 | D_CACHE_IDX, | 24 | D_CACHE_IDX, |
| 25 | D_SHADERCACHE_IDX, | 25 | D_SHADERCACHE_IDX, |
| 26 | D_SHADERS_IDX, | 26 | D_SHADERS_IDX, |
| 27 | D_STATESAVES_IDX, | 27 | D_STATESAVES_IDX, |
| 28 | D_SCREENSHOTS_IDX, | 28 | D_SCREENSHOTS_IDX, |
| 29 | D_SDMC_IDX, | 29 | D_SDMC_IDX, |
| 30 | D_HIRESTEXTURES_IDX, | 30 | D_SAVEDATA_IDX, |
| 31 | D_DUMP_IDX, | 31 | D_SYSDATA_IDX, |
| 32 | D_DUMPFRAMES_IDX, | 32 | D_HIRESTEXTURES_IDX, |
| 33 | D_DUMPAUDIO_IDX, | 33 | D_DUMP_IDX, |
| 34 | D_DUMPTEXTURES_IDX, | 34 | D_DUMPFRAMES_IDX, |
| 35 | D_DUMPDSP_IDX, | 35 | D_DUMPAUDIO_IDX, |
| 36 | D_LOGS_IDX, | 36 | D_DUMPTEXTURES_IDX, |
| 37 | D_SYSCONF_IDX, | 37 | D_DUMPDSP_IDX, |
| 38 | F_EMUCONFIG_IDX, | 38 | D_LOGS_IDX, |
| 39 | F_DEBUGGERCONFIG_IDX, | 39 | D_SYSCONF_IDX, |
| 40 | F_LOGGERCONFIG_IDX, | 40 | F_EMUCONFIG_IDX, |
| 41 | F_MAINLOG_IDX, | 41 | F_DEBUGGERCONFIG_IDX, |
| 42 | F_RAMDUMP_IDX, | 42 | F_LOGGERCONFIG_IDX, |
| 43 | F_ARAMDUMP_IDX, | 43 | F_MAINLOG_IDX, |
| 44 | F_SYSCONF_IDX, | 44 | F_RAMDUMP_IDX, |
| 45 | NUM_PATH_INDICES | 45 | F_ARAMDUMP_IDX, |
| 46 | F_SYSCONF_IDX, | ||
| 47 | NUM_PATH_INDICES | ||
| 46 | }; | 48 | }; |
| 47 | 49 | ||
| 48 | namespace FileUtil | 50 | namespace FileUtil |
| @@ -51,11 +53,11 @@ namespace FileUtil | |||
| 51 | // FileSystem tree node/ | 53 | // FileSystem tree node/ |
| 52 | struct FSTEntry | 54 | struct FSTEntry |
| 53 | { | 55 | { |
| 54 | bool isDirectory; | 56 | bool isDirectory; |
| 55 | u64 size; // file length or number of entries from children | 57 | u64 size; // file length or number of entries from children |
| 56 | std::string physicalName; // name on disk | 58 | std::string physicalName; // name on disk |
| 57 | std::string virtualName; // name in FST names table | 59 | std::string virtualName; // name in FST names table |
| 58 | std::vector<FSTEntry> children; | 60 | std::vector<FSTEntry> children; |
| 59 | }; | 61 | }; |
| 60 | 62 | ||
| 61 | // Returns true if file filename exists | 63 | // Returns true if file filename exists |
| @@ -148,86 +150,86 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam | |||
| 148 | class IOFile : public NonCopyable | 150 | class IOFile : public NonCopyable |
| 149 | { | 151 | { |
| 150 | public: | 152 | public: |
| 151 | IOFile(); | 153 | IOFile(); |
| 152 | IOFile(std::FILE* file); | 154 | IOFile(std::FILE* file); |
| 153 | IOFile(const std::string& filename, const char openmode[]); | 155 | IOFile(const std::string& filename, const char openmode[]); |
| 154 | 156 | ||
| 155 | ~IOFile(); | 157 | ~IOFile(); |
| 156 | 158 | ||
| 157 | IOFile(IOFile&& other); | 159 | IOFile(IOFile&& other); |
| 158 | IOFile& operator=(IOFile&& other); | 160 | IOFile& operator=(IOFile&& other); |
| 159 | 161 | ||
| 160 | void Swap(IOFile& other); | 162 | void Swap(IOFile& other); |
| 161 | 163 | ||
| 162 | bool Open(const std::string& filename, const char openmode[]); | 164 | bool Open(const std::string& filename, const char openmode[]); |
| 163 | bool Close(); | 165 | bool Close(); |
| 164 | 166 | ||
| 165 | template <typename T> | 167 | template <typename T> |
| 166 | size_t ReadArray(T* data, size_t length) | 168 | size_t ReadArray(T* data, size_t length) |
| 167 | { | 169 | { |
| 168 | if (!IsOpen()) { | 170 | if (!IsOpen()) { |
| 169 | m_good = false; | 171 | m_good = false; |
| 170 | return -1; | 172 | return -1; |
| 171 | } | 173 | } |
| 172 | 174 | ||
| 173 | size_t items_read = std::fread(data, sizeof(T), length, m_file); | 175 | size_t items_read = std::fread(data, sizeof(T), length, m_file); |
| 174 | if (items_read != length) | 176 | if (items_read != length) |
| 175 | m_good = false; | 177 | m_good = false; |
| 176 | 178 | ||
| 177 | return items_read; | 179 | return items_read; |
| 178 | } | 180 | } |
| 179 | 181 | ||
| 180 | template <typename T> | 182 | template <typename T> |
| 181 | size_t WriteArray(const T* data, size_t length) | 183 | size_t WriteArray(const T* data, size_t length) |
| 182 | { | 184 | { |
| 183 | if (!IsOpen()) { | 185 | if (!IsOpen()) { |
| 184 | m_good = false; | 186 | m_good = false; |
| 185 | return -1; | 187 | return -1; |
| 186 | } | 188 | } |
| 187 | 189 | ||
| 188 | size_t items_written = std::fwrite(data, sizeof(T), length, m_file); | 190 | size_t items_written = std::fwrite(data, sizeof(T), length, m_file); |
| 189 | if (items_written != length) | 191 | if (items_written != length) |
| 190 | m_good = false; | 192 | m_good = false; |
| 191 | 193 | ||
| 192 | return items_written; | 194 | return items_written; |
| 193 | } | 195 | } |
| 194 | 196 | ||
| 195 | size_t ReadBytes(void* data, size_t length) | 197 | size_t ReadBytes(void* data, size_t length) |
| 196 | { | 198 | { |
| 197 | return ReadArray(reinterpret_cast<char*>(data), length); | 199 | return ReadArray(reinterpret_cast<char*>(data), length); |
| 198 | } | 200 | } |
| 199 | 201 | ||
| 200 | size_t WriteBytes(const void* data, size_t length) | 202 | size_t WriteBytes(const void* data, size_t length) |
| 201 | { | 203 | { |
| 202 | return WriteArray(reinterpret_cast<const char*>(data), length); | 204 | return WriteArray(reinterpret_cast<const char*>(data), length); |
| 203 | } | 205 | } |
| 204 | 206 | ||
| 205 | bool IsOpen() { return NULL != m_file; } | 207 | bool IsOpen() { return nullptr != m_file; } |
| 206 | 208 | ||
| 207 | // m_good is set to false when a read, write or other function fails | 209 | // m_good is set to false when a read, write or other function fails |
| 208 | bool IsGood() { return m_good; } | 210 | bool IsGood() { return m_good; } |
| 209 | operator void*() { return m_good ? m_file : NULL; } | 211 | operator void*() { return m_good ? m_file : nullptr; } |
| 210 | 212 | ||
| 211 | std::FILE* ReleaseHandle(); | 213 | std::FILE* ReleaseHandle(); |
| 212 | 214 | ||
| 213 | std::FILE* GetHandle() { return m_file; } | 215 | std::FILE* GetHandle() { return m_file; } |
| 214 | 216 | ||
| 215 | void SetHandle(std::FILE* file); | 217 | void SetHandle(std::FILE* file); |
| 216 | 218 | ||
| 217 | bool Seek(s64 off, int origin); | 219 | bool Seek(s64 off, int origin); |
| 218 | u64 Tell(); | 220 | u64 Tell(); |
| 219 | u64 GetSize(); | 221 | u64 GetSize(); |
| 220 | bool Resize(u64 size); | 222 | bool Resize(u64 size); |
| 221 | bool Flush(); | 223 | bool Flush(); |
| 222 | 224 | ||
| 223 | // clear error state | 225 | // clear error state |
| 224 | void Clear() { m_good = true; std::clearerr(m_file); } | 226 | void Clear() { m_good = true; std::clearerr(m_file); } |
| 225 | 227 | ||
| 226 | std::FILE* m_file; | 228 | std::FILE* m_file; |
| 227 | bool m_good; | 229 | bool m_good; |
| 228 | private: | 230 | private: |
| 229 | IOFile(IOFile&); | 231 | IOFile(IOFile&); |
| 230 | IOFile& operator=(IOFile& other); | 232 | IOFile& operator=(IOFile& other); |
| 231 | }; | 233 | }; |
| 232 | 234 | ||
| 233 | } // namespace | 235 | } // namespace |
| @@ -237,8 +239,8 @@ template <typename T> | |||
| 237 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) | 239 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) |
| 238 | { | 240 | { |
| 239 | #ifdef _WIN32 | 241 | #ifdef _WIN32 |
| 240 | fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); | 242 | fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); |
| 241 | #else | 243 | #else |
| 242 | fstream.open(filename.c_str(), openmode); | 244 | fstream.open(filename.c_str(), openmode); |
| 243 | #endif | 245 | #endif |
| 244 | } | 246 | } |
diff --git a/src/common/hash.cpp b/src/common/hash.cpp index d2ebc7341..2ddcfe6b7 100644 --- a/src/common/hash.cpp +++ b/src/common/hash.cpp | |||
| @@ -115,15 +115,15 @@ inline u64 getblock(const u64 * p, int i) | |||
| 115 | 115 | ||
| 116 | inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2) | 116 | inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2) |
| 117 | { | 117 | { |
| 118 | k1 *= c1; | 118 | k1 *= c1; |
| 119 | k1 = _rotl64(k1,23); | 119 | k1 = _rotl64(k1,23); |
| 120 | k1 *= c2; | 120 | k1 *= c2; |
| 121 | h1 ^= k1; | 121 | h1 ^= k1; |
| 122 | h1 += h2; | 122 | h1 += h2; |
| 123 | 123 | ||
| 124 | h2 = _rotl64(h2,41); | 124 | h2 = _rotl64(h2,41); |
| 125 | 125 | ||
| 126 | k2 *= c2; | 126 | k2 *= c2; |
| 127 | k2 = _rotl64(k2,23); | 127 | k2 = _rotl64(k2,23); |
| 128 | k2 *= c1; | 128 | k2 *= c1; |
| 129 | h2 ^= k2; | 129 | h2 ^= k2; |
| @@ -250,7 +250,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples) | |||
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | 252 | ||
| 253 | /* | 253 | /* |
| 254 | * NOTE: This hash function is used for custom texture loading/dumping, so | 254 | * NOTE: This hash function is used for custom texture loading/dumping, so |
| 255 | * it should not be changed, which would require all custom textures to be | 255 | * it should not be changed, which would require all custom textures to be |
| 256 | * recalculated for their new hash values. If the hashing function is | 256 | * recalculated for their new hash values. If the hashing function is |
| @@ -273,7 +273,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) | |||
| 273 | u64 k = data[0]; | 273 | u64 k = data[0]; |
| 274 | data+=Step; | 274 | data+=Step; |
| 275 | k *= m; | 275 | k *= m; |
| 276 | k ^= k >> r; | 276 | k ^= k >> r; |
| 277 | k *= m; | 277 | k *= m; |
| 278 | h ^= k; | 278 | h ^= k; |
| 279 | h *= m; | 279 | h *= m; |
| @@ -292,13 +292,13 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) | |||
| 292 | case 1: h ^= u64(data2[0]); | 292 | case 1: h ^= u64(data2[0]); |
| 293 | h *= m; | 293 | h *= m; |
| 294 | }; | 294 | }; |
| 295 | 295 | ||
| 296 | h ^= h >> r; | 296 | h ^= h >> r; |
| 297 | h *= m; | 297 | h *= m; |
| 298 | h ^= h >> r; | 298 | h ^= h >> r; |
| 299 | 299 | ||
| 300 | return h; | 300 | return h; |
| 301 | } | 301 | } |
| 302 | #else | 302 | #else |
| 303 | // CRC32 hash using the SSE4.2 instruction | 303 | // CRC32 hash using the SSE4.2 instruction |
| 304 | u64 GetCRC32(const u8 *src, int len, u32 samples) | 304 | u64 GetCRC32(const u8 *src, int len, u32 samples) |
| @@ -351,15 +351,15 @@ inline u32 fmix32(u32 h) | |||
| 351 | 351 | ||
| 352 | inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2) | 352 | inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2) |
| 353 | { | 353 | { |
| 354 | k1 *= c1; | 354 | k1 *= c1; |
| 355 | k1 = _rotl(k1,11); | 355 | k1 = _rotl(k1,11); |
| 356 | k1 *= c2; | 356 | k1 *= c2; |
| 357 | h1 ^= k1; | 357 | h1 ^= k1; |
| 358 | h1 += h2; | 358 | h1 += h2; |
| 359 | 359 | ||
| 360 | h2 = _rotl(h2,17); | 360 | h2 = _rotl(h2,17); |
| 361 | 361 | ||
| 362 | k2 *= c2; | 362 | k2 *= c2; |
| 363 | k2 = _rotl(k2,11); | 363 | k2 = _rotl(k2,11); |
| 364 | k2 *= c1; | 364 | k2 *= c1; |
| 365 | h2 ^= k2; | 365 | h2 ^= k2; |
| @@ -405,7 +405,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples) | |||
| 405 | 405 | ||
| 406 | //---------- | 406 | //---------- |
| 407 | // tail | 407 | // tail |
| 408 | 408 | ||
| 409 | const u8 * tail = (const u8*)(data + nblocks*8); | 409 | const u8 * tail = (const u8*)(data + nblocks*8); |
| 410 | 410 | ||
| 411 | u32 k1 = 0; | 411 | u32 k1 = 0; |
| @@ -439,7 +439,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples) | |||
| 439 | 439 | ||
| 440 | out[0] = h1; | 440 | out[0] = h1; |
| 441 | out[1] = h2; | 441 | out[1] = h2; |
| 442 | 442 | ||
| 443 | return *((u64 *)&out); | 443 | return *((u64 *)&out); |
| 444 | } | 444 | } |
| 445 | 445 | ||
| @@ -463,11 +463,11 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) | |||
| 463 | { | 463 | { |
| 464 | u64 k = data[0]; | 464 | u64 k = data[0]; |
| 465 | data+=Step; | 465 | data+=Step; |
| 466 | k *= m; | 466 | k *= m; |
| 467 | k ^= k >> r; | 467 | k ^= k >> r; |
| 468 | k *= m; | 468 | k *= m; |
| 469 | h ^= k; | 469 | h ^= k; |
| 470 | h *= m; | 470 | h *= m; |
| 471 | } | 471 | } |
| 472 | 472 | ||
| 473 | const u8 * data2 = (const u8*)end; | 473 | const u8 * data2 = (const u8*)end; |
| @@ -483,7 +483,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) | |||
| 483 | case 1: h ^= u64(data2[0]); | 483 | case 1: h ^= u64(data2[0]); |
| 484 | h *= m; | 484 | h *= m; |
| 485 | }; | 485 | }; |
| 486 | 486 | ||
| 487 | h ^= h >> r; | 487 | h ^= h >> r; |
| 488 | h *= m; | 488 | h *= m; |
| 489 | h ^= h >> r; | 489 | h ^= h >> r; |
diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h index 96dce3155..bb1b5174f 100644 --- a/src/common/linear_disk_cache.h +++ b/src/common/linear_disk_cache.h | |||
| @@ -64,13 +64,13 @@ public: | |||
| 64 | m_file.seekg(0, std::ios::beg); | 64 | m_file.seekg(0, std::ios::beg); |
| 65 | std::fstream::pos_type start_pos = m_file.tellg(); | 65 | std::fstream::pos_type start_pos = m_file.tellg(); |
| 66 | std::streamoff file_size = end_pos - start_pos; | 66 | std::streamoff file_size = end_pos - start_pos; |
| 67 | 67 | ||
| 68 | if (m_file.is_open() && ValidateHeader()) | 68 | if (m_file.is_open() && ValidateHeader()) |
| 69 | { | 69 | { |
| 70 | // good header, read some key/value pairs | 70 | // good header, read some key/value pairs |
| 71 | K key; | 71 | K key; |
| 72 | 72 | ||
| 73 | V *value = NULL; | 73 | V *value = nullptr; |
| 74 | u32 value_size; | 74 | u32 value_size; |
| 75 | u32 entry_number; | 75 | u32 entry_number; |
| 76 | 76 | ||
| @@ -87,7 +87,7 @@ public: | |||
| 87 | 87 | ||
| 88 | // read key/value and pass to reader | 88 | // read key/value and pass to reader |
| 89 | if (Read(&key) && | 89 | if (Read(&key) && |
| 90 | Read(value, value_size) && | 90 | Read(value, value_size) && |
| 91 | Read(&entry_number) && | 91 | Read(&entry_number) && |
| 92 | entry_number == m_num_entries+1) | 92 | entry_number == m_num_entries+1) |
| 93 | { | 93 | { |
| @@ -115,7 +115,7 @@ public: | |||
| 115 | WriteHeader(); | 115 | WriteHeader(); |
| 116 | return 0; | 116 | return 0; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | void Sync() | 119 | void Sync() |
| 120 | { | 120 | { |
| 121 | m_file.flush(); | 121 | m_file.flush(); |
diff --git a/src/common/log.h b/src/common/log.h index bfd73f8a5..663eda9ad 100644 --- a/src/common/log.h +++ b/src/common/log.h | |||
| @@ -4,104 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #ifndef LOGGING | 7 | #include "common/common_funcs.h" |
| 8 | #define LOGGING | 8 | #include "common/msg_handler.h" |
| 9 | #endif | 9 | #include "common/logging/log.h" |
| 10 | |||
| 11 | enum { | ||
| 12 | OS_LEVEL, // Printed by the emulated operating system | ||
| 13 | NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports. | ||
| 14 | ERROR_LEVEL, // Critical errors | ||
| 15 | WARNING_LEVEL, // Something is suspicious. | ||
| 16 | INFO_LEVEL, // General information. | ||
| 17 | DEBUG_LEVEL, // Detailed debugging - might make things slow. | ||
| 18 | }; | ||
| 19 | |||
| 20 | namespace LogTypes | ||
| 21 | { | ||
| 22 | |||
| 23 | enum LOG_TYPE { | ||
| 24 | ACTIONREPLAY, | ||
| 25 | AUDIO, | ||
| 26 | AUDIO_INTERFACE, | ||
| 27 | BOOT, | ||
| 28 | COMMANDPROCESSOR, | ||
| 29 | COMMON, | ||
| 30 | CONSOLE, | ||
| 31 | CONFIG, | ||
| 32 | DISCIO, | ||
| 33 | FILEMON, | ||
| 34 | DSPHLE, | ||
| 35 | DSPLLE, | ||
| 36 | DSP_MAIL, | ||
| 37 | DSPINTERFACE, | ||
| 38 | DVDINTERFACE, | ||
| 39 | DYNA_REC, | ||
| 40 | EXPANSIONINTERFACE, | ||
| 41 | GDB_STUB, | ||
| 42 | ARM11, | ||
| 43 | GSP, | ||
| 44 | OSHLE, | ||
| 45 | MASTER_LOG, | ||
| 46 | MEMMAP, | ||
| 47 | MEMCARD_MANAGER, | ||
| 48 | OSREPORT, | ||
| 49 | PAD, | ||
| 50 | PROCESSORINTERFACE, | ||
| 51 | PIXELENGINE, | ||
| 52 | SERIALINTERFACE, | ||
| 53 | SP1, | ||
| 54 | STREAMINGINTERFACE, | ||
| 55 | VIDEO, | ||
| 56 | VIDEOINTERFACE, | ||
| 57 | LOADER, | ||
| 58 | FILESYS, | ||
| 59 | WII_IPC_DVD, | ||
| 60 | WII_IPC_ES, | ||
| 61 | WII_IPC_FILEIO, | ||
| 62 | WII_IPC_HID, | ||
| 63 | KERNEL, | ||
| 64 | SVC, | ||
| 65 | NDMA, | ||
| 66 | HLE, | ||
| 67 | RENDER, | ||
| 68 | GPU, | ||
| 69 | HW, | ||
| 70 | TIME, | ||
| 71 | NETPLAY, | ||
| 72 | |||
| 73 | NUMBER_OF_LOGS // Must be last | ||
| 74 | }; | ||
| 75 | |||
| 76 | // FIXME: should this be removed? | ||
| 77 | enum LOG_LEVELS { | ||
| 78 | LOS = OS_LEVEL, | ||
| 79 | LNOTICE = NOTICE_LEVEL, | ||
| 80 | LERROR = ERROR_LEVEL, | ||
| 81 | LWARNING = WARNING_LEVEL, | ||
| 82 | LINFO = INFO_LEVEL, | ||
| 83 | LDEBUG = DEBUG_LEVEL, | ||
| 84 | }; | ||
| 85 | |||
| 86 | #define LOGTYPES_LEVELS LogTypes::LOG_LEVELS | ||
| 87 | #define LOGTYPES_TYPE LogTypes::LOG_TYPE | ||
| 88 | |||
| 89 | } // namespace | ||
| 90 | |||
| 91 | void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line, | ||
| 92 | const char* function, const char* fmt, ...) | ||
| 93 | #ifdef __GNUC__ | ||
| 94 | __attribute__((format(printf, 6, 7))) | ||
| 95 | #endif | ||
| 96 | ; | ||
| 97 | |||
| 98 | #if defined LOGGING || defined _DEBUG || defined DEBUGFAST | ||
| 99 | #define MAX_LOGLEVEL LDEBUG | ||
| 100 | #else | ||
| 101 | #ifndef MAX_LOGLEVEL | ||
| 102 | #define MAX_LOGLEVEL LWARNING | ||
| 103 | #endif // loglevel | ||
| 104 | #endif // logging | ||
| 105 | 10 | ||
| 106 | #ifdef _WIN32 | 11 | #ifdef _WIN32 |
| 107 | #ifndef __func__ | 12 | #ifndef __func__ |
| @@ -109,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| 109 | #endif | 14 | #endif |
| 110 | #endif | 15 | #endif |
| 111 | 16 | ||
| 112 | // Let the compiler optimize this out | 17 | #if _DEBUG |
| 113 | #define GENERIC_LOG(t, v, ...) { \ | ||
| 114 | if (v <= LogTypes::MAX_LOGLEVEL) \ | ||
| 115 | GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \ | ||
| 116 | } | ||
| 117 | |||
| 118 | #define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0) | ||
| 119 | #define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) | ||
| 120 | #define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) | ||
| 121 | #define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) | ||
| 122 | #define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0) | ||
| 123 | #define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0) | ||
| 124 | |||
| 125 | #if MAX_LOGLEVEL >= DEBUG_LEVEL | ||
| 126 | #define _dbg_assert_(_t_, _a_) \ | 18 | #define _dbg_assert_(_t_, _a_) \ |
| 127 | if (!(_a_)) {\ | 19 | if (!(_a_)) {\ |
| 128 | ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ | 20 | LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ |
| 129 | __LINE__, __FILE__, __TIME__); \ | 21 | __LINE__, __FILE__, __TIME__); \ |
| 130 | if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ | 22 | if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ |
| 131 | } | 23 | } |
| 132 | #define _dbg_assert_msg_(_t_, _a_, ...)\ | 24 | #define _dbg_assert_msg_(_t_, _a_, ...)\ |
| 133 | if (!(_a_)) {\ | 25 | if (!(_a_)) {\ |
| 134 | ERROR_LOG(_t_, __VA_ARGS__); \ | 26 | LOG_CRITICAL(_t_, __VA_ARGS__); \ |
| 135 | if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ | 27 | if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ |
| 136 | } | 28 | } |
| 137 | #define _dbg_update_() Host_UpdateLogDisplay(); | 29 | #define _dbg_update_() Host_UpdateLogDisplay(); |
| @@ -143,11 +35,10 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| 143 | #define _dbg_assert_(_t_, _a_) {} | 35 | #define _dbg_assert_(_t_, _a_) {} |
| 144 | #define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} | 36 | #define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} |
| 145 | #endif // dbg_assert | 37 | #endif // dbg_assert |
| 146 | #endif // MAX_LOGLEVEL DEBUG | 38 | #endif |
| 147 | 39 | ||
| 148 | #define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) | 40 | #define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) |
| 149 | 41 | ||
| 150 | #ifndef GEKKO | ||
| 151 | #ifdef _WIN32 | 42 | #ifdef _WIN32 |
| 152 | #define _assert_msg_(_t_, _a_, _fmt_, ...) \ | 43 | #define _assert_msg_(_t_, _a_, _fmt_, ...) \ |
| 153 | if (!(_a_)) {\ | 44 | if (!(_a_)) {\ |
| @@ -159,6 +50,3 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| 159 | if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ | 50 | if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ |
| 160 | } | 51 | } |
| 161 | #endif // WIN32 | 52 | #endif // WIN32 |
| 162 | #else // GEKKO | ||
| 163 | #define _assert_msg_(_t_, _a_, _fmt_, ...) | ||
| 164 | #endif | ||
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp deleted file mode 100644 index 4d590d98f..000000000 --- a/src/common/log_manager.cpp +++ /dev/null | |||
| @@ -1,199 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/log_manager.h" | ||
| 8 | #include "common/console_listener.h" | ||
| 9 | #include "common/timer.h" | ||
| 10 | #include "common/thread.h" | ||
| 11 | |||
| 12 | void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line, | ||
| 13 | const char* function, const char* fmt, ...) | ||
| 14 | { | ||
| 15 | va_list args; | ||
| 16 | va_start(args, fmt); | ||
| 17 | |||
| 18 | if (LogManager::GetInstance()) { | ||
| 19 | LogManager::GetInstance()->Log(level, type, | ||
| 20 | file, line, function, fmt, args); | ||
| 21 | } | ||
| 22 | va_end(args); | ||
| 23 | } | ||
| 24 | |||
| 25 | LogManager *LogManager::m_logManager = NULL; | ||
| 26 | |||
| 27 | LogManager::LogManager() | ||
| 28 | { | ||
| 29 | // create log files | ||
| 30 | m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log"); | ||
| 31 | m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot"); | ||
| 32 | m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); | ||
| 33 | m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration"); | ||
| 34 | m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO"); | ||
| 35 | m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor"); | ||
| 36 | m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad"); | ||
| 37 | m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine"); | ||
| 38 | m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc"); | ||
| 39 | m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt"); | ||
| 40 | m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt"); | ||
| 41 | m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt"); | ||
| 42 | m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap"); | ||
| 43 | m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1"); | ||
| 44 | m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt"); | ||
| 45 | m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface"); | ||
| 46 | m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface"); | ||
| 47 | m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP"); | ||
| 48 | m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt"); | ||
| 49 | m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub"); | ||
| 50 | m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt"); | ||
| 51 | m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11"); | ||
| 52 | m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE"); | ||
| 53 | m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE"); | ||
| 54 | m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE"); | ||
| 55 | m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails"); | ||
| 56 | m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend"); | ||
| 57 | m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator"); | ||
| 58 | m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT"); | ||
| 59 | m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console"); | ||
| 60 | m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport"); | ||
| 61 | m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing"); | ||
| 62 | m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); | ||
| 63 | m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); | ||
| 64 | m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); | ||
| 65 | m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE"); | ||
| 66 | m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); | ||
| 67 | m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); | ||
| 68 | m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); | ||
| 69 | m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); | ||
| 70 | m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU"); | ||
| 71 | m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); | ||
| 72 | m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA"); | ||
| 73 | m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); | ||
| 74 | m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); | ||
| 75 | m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); | ||
| 76 | m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); | ||
| 77 | m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); | ||
| 78 | |||
| 79 | m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); | ||
| 80 | m_consoleLog = new ConsoleListener(); | ||
| 81 | m_debuggerLog = new DebuggerLogListener(); | ||
| 82 | |||
| 83 | for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||
| 84 | { | ||
| 85 | m_Log[i]->SetEnable(true); | ||
| 86 | m_Log[i]->AddListener(m_fileLog); | ||
| 87 | m_Log[i]->AddListener(m_consoleLog); | ||
| 88 | #ifdef _MSC_VER | ||
| 89 | if (IsDebuggerPresent()) | ||
| 90 | m_Log[i]->AddListener(m_debuggerLog); | ||
| 91 | #endif | ||
| 92 | } | ||
| 93 | |||
| 94 | m_consoleLog->Open(); | ||
| 95 | } | ||
| 96 | |||
| 97 | LogManager::~LogManager() | ||
| 98 | { | ||
| 99 | for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||
| 100 | { | ||
| 101 | m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); | ||
| 102 | m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog); | ||
| 103 | m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog); | ||
| 104 | } | ||
| 105 | |||
| 106 | for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||
| 107 | delete m_Log[i]; | ||
| 108 | |||
| 109 | delete m_fileLog; | ||
| 110 | delete m_consoleLog; | ||
| 111 | delete m_debuggerLog; | ||
| 112 | } | ||
| 113 | |||
| 114 | void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, | ||
| 115 | int line, const char* function, const char *fmt, va_list args) | ||
| 116 | { | ||
| 117 | char temp[MAX_MSGLEN]; | ||
| 118 | char msg[MAX_MSGLEN * 2]; | ||
| 119 | LogContainer *log = m_Log[type]; | ||
| 120 | |||
| 121 | if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners()) | ||
| 122 | return; | ||
| 123 | |||
| 124 | Common::CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args); | ||
| 125 | |||
| 126 | static const char level_to_char[7] = "ONEWID"; | ||
| 127 | sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line, | ||
| 128 | level_to_char[(int)level], log->GetShortName(), function, temp); | ||
| 129 | |||
| 130 | #ifdef ANDROID | ||
| 131 | Host_SysMessage(msg); | ||
| 132 | #endif | ||
| 133 | log->Trigger(level, msg); | ||
| 134 | } | ||
| 135 | |||
| 136 | void LogManager::Init() | ||
| 137 | { | ||
| 138 | m_logManager = new LogManager(); | ||
| 139 | } | ||
| 140 | |||
| 141 | void LogManager::Shutdown() | ||
| 142 | { | ||
| 143 | delete m_logManager; | ||
| 144 | m_logManager = NULL; | ||
| 145 | } | ||
| 146 | |||
| 147 | LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable) | ||
| 148 | : m_enable(enable) | ||
| 149 | { | ||
| 150 | strncpy(m_fullName, fullName, 128); | ||
| 151 | strncpy(m_shortName, shortName, 32); | ||
| 152 | m_level = LogTypes::MAX_LOGLEVEL; | ||
| 153 | } | ||
| 154 | |||
| 155 | // LogContainer | ||
| 156 | void LogContainer::AddListener(LogListener *listener) | ||
| 157 | { | ||
| 158 | std::lock_guard<std::mutex> lk(m_listeners_lock); | ||
| 159 | m_listeners.insert(listener); | ||
| 160 | } | ||
| 161 | |||
| 162 | void LogContainer::RemoveListener(LogListener *listener) | ||
| 163 | { | ||
| 164 | std::lock_guard<std::mutex> lk(m_listeners_lock); | ||
| 165 | m_listeners.erase(listener); | ||
| 166 | } | ||
| 167 | |||
| 168 | void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg) | ||
| 169 | { | ||
| 170 | std::lock_guard<std::mutex> lk(m_listeners_lock); | ||
| 171 | |||
| 172 | std::set<LogListener*>::const_iterator i; | ||
| 173 | for (i = m_listeners.begin(); i != m_listeners.end(); ++i) | ||
| 174 | { | ||
| 175 | (*i)->Log(level, msg); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | FileLogListener::FileLogListener(const char *filename) | ||
| 180 | { | ||
| 181 | OpenFStream(m_logfile, filename, std::ios::app); | ||
| 182 | SetEnable(true); | ||
| 183 | } | ||
| 184 | |||
| 185 | void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) | ||
| 186 | { | ||
| 187 | if (!IsEnabled() || !IsValid()) | ||
| 188 | return; | ||
| 189 | |||
| 190 | std::lock_guard<std::mutex> lk(m_log_lock); | ||
| 191 | m_logfile << msg << std::flush; | ||
| 192 | } | ||
| 193 | |||
| 194 | void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) | ||
| 195 | { | ||
| 196 | #if _MSC_VER | ||
| 197 | ::OutputDebugStringA(msg); | ||
| 198 | #endif | ||
| 199 | } | ||
diff --git a/src/common/log_manager.h b/src/common/log_manager.h deleted file mode 100644 index de1d16ee5..000000000 --- a/src/common/log_manager.h +++ /dev/null | |||
| @@ -1,166 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/log.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "common/file_util.h" | ||
| 10 | |||
| 11 | #include <cstring> | ||
| 12 | #include <set> | ||
| 13 | #include <mutex> | ||
| 14 | |||
| 15 | #define MAX_MESSAGES 8000 | ||
| 16 | #define MAX_MSGLEN 1024 | ||
| 17 | |||
| 18 | |||
| 19 | // pure virtual interface | ||
| 20 | class LogListener | ||
| 21 | { | ||
| 22 | public: | ||
| 23 | virtual ~LogListener() {} | ||
| 24 | |||
| 25 | virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0; | ||
| 26 | }; | ||
| 27 | |||
| 28 | class FileLogListener : public LogListener | ||
| 29 | { | ||
| 30 | public: | ||
| 31 | FileLogListener(const char *filename); | ||
| 32 | |||
| 33 | void Log(LogTypes::LOG_LEVELS, const char *msg) override; | ||
| 34 | |||
| 35 | bool IsValid() { return !m_logfile.fail(); } | ||
| 36 | bool IsEnabled() const { return m_enable; } | ||
| 37 | void SetEnable(bool enable) { m_enable = enable; } | ||
| 38 | |||
| 39 | const char* GetName() const { return "file"; } | ||
| 40 | |||
| 41 | private: | ||
| 42 | std::mutex m_log_lock; | ||
| 43 | std::ofstream m_logfile; | ||
| 44 | bool m_enable; | ||
| 45 | }; | ||
| 46 | |||
| 47 | class DebuggerLogListener : public LogListener | ||
| 48 | { | ||
| 49 | public: | ||
| 50 | void Log(LogTypes::LOG_LEVELS, const char *msg) override; | ||
| 51 | }; | ||
| 52 | |||
| 53 | class LogContainer | ||
| 54 | { | ||
| 55 | public: | ||
| 56 | LogContainer(const char* shortName, const char* fullName, bool enable = false); | ||
| 57 | |||
| 58 | const char* GetShortName() const { return m_shortName; } | ||
| 59 | const char* GetFullName() const { return m_fullName; } | ||
| 60 | |||
| 61 | void AddListener(LogListener* listener); | ||
| 62 | void RemoveListener(LogListener* listener); | ||
| 63 | |||
| 64 | void Trigger(LogTypes::LOG_LEVELS, const char *msg); | ||
| 65 | |||
| 66 | bool IsEnabled() const { return m_enable; } | ||
| 67 | void SetEnable(bool enable) { m_enable = enable; } | ||
| 68 | |||
| 69 | LogTypes::LOG_LEVELS GetLevel() const { return m_level; } | ||
| 70 | |||
| 71 | void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; } | ||
| 72 | |||
| 73 | bool HasListeners() const { return !m_listeners.empty(); } | ||
| 74 | |||
| 75 | private: | ||
| 76 | char m_fullName[128]; | ||
| 77 | char m_shortName[32]; | ||
| 78 | bool m_enable; | ||
| 79 | LogTypes::LOG_LEVELS m_level; | ||
| 80 | std::mutex m_listeners_lock; | ||
| 81 | std::set<LogListener*> m_listeners; | ||
| 82 | }; | ||
| 83 | |||
| 84 | class ConsoleListener; | ||
| 85 | |||
| 86 | class LogManager : NonCopyable | ||
| 87 | { | ||
| 88 | private: | ||
| 89 | LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS]; | ||
| 90 | FileLogListener *m_fileLog; | ||
| 91 | ConsoleListener *m_consoleLog; | ||
| 92 | DebuggerLogListener *m_debuggerLog; | ||
| 93 | static LogManager *m_logManager; // Singleton. Ugh. | ||
| 94 | |||
| 95 | LogManager(); | ||
| 96 | ~LogManager(); | ||
| 97 | public: | ||
| 98 | |||
| 99 | static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; } | ||
| 100 | |||
| 101 | void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line, | ||
| 102 | const char* function, const char *fmt, va_list args); | ||
| 103 | |||
| 104 | void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level) | ||
| 105 | { | ||
| 106 | m_Log[type]->SetLevel(level); | ||
| 107 | } | ||
| 108 | |||
| 109 | void SetEnable(LogTypes::LOG_TYPE type, bool enable) | ||
| 110 | { | ||
| 111 | m_Log[type]->SetEnable(enable); | ||
| 112 | } | ||
| 113 | |||
| 114 | bool IsEnabled(LogTypes::LOG_TYPE type) const | ||
| 115 | { | ||
| 116 | return m_Log[type]->IsEnabled(); | ||
| 117 | } | ||
| 118 | |||
| 119 | const char* GetShortName(LogTypes::LOG_TYPE type) const | ||
| 120 | { | ||
| 121 | return m_Log[type]->GetShortName(); | ||
| 122 | } | ||
| 123 | |||
| 124 | const char* GetFullName(LogTypes::LOG_TYPE type) const | ||
| 125 | { | ||
| 126 | return m_Log[type]->GetFullName(); | ||
| 127 | } | ||
| 128 | |||
| 129 | void AddListener(LogTypes::LOG_TYPE type, LogListener *listener) | ||
| 130 | { | ||
| 131 | m_Log[type]->AddListener(listener); | ||
| 132 | } | ||
| 133 | |||
| 134 | void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener) | ||
| 135 | { | ||
| 136 | m_Log[type]->RemoveListener(listener); | ||
| 137 | } | ||
| 138 | |||
| 139 | FileLogListener *GetFileListener() const | ||
| 140 | { | ||
| 141 | return m_fileLog; | ||
| 142 | } | ||
| 143 | |||
| 144 | ConsoleListener *GetConsoleListener() const | ||
| 145 | { | ||
| 146 | return m_consoleLog; | ||
| 147 | } | ||
| 148 | |||
| 149 | DebuggerLogListener *GetDebuggerListener() const | ||
| 150 | { | ||
| 151 | return m_debuggerLog; | ||
| 152 | } | ||
| 153 | |||
| 154 | static LogManager* GetInstance() | ||
| 155 | { | ||
| 156 | return m_logManager; | ||
| 157 | } | ||
| 158 | |||
| 159 | static void SetInstance(LogManager *logManager) | ||
| 160 | { | ||
| 161 | m_logManager = logManager; | ||
| 162 | } | ||
| 163 | |||
| 164 | static void Init(); | ||
| 165 | static void Shutdown(); | ||
| 166 | }; | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp new file mode 100644 index 000000000..e79b84604 --- /dev/null +++ b/src/common/logging/backend.cpp | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/log.h" // For _dbg_assert_ | ||
| 8 | |||
| 9 | #include "common/logging/backend.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/logging/text_formatter.h" | ||
| 12 | |||
| 13 | namespace Log { | ||
| 14 | |||
| 15 | static std::shared_ptr<Logger> global_logger; | ||
| 16 | |||
| 17 | /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. | ||
| 18 | #define ALL_LOG_CLASSES() \ | ||
| 19 | CLS(Log) \ | ||
| 20 | CLS(Common) \ | ||
| 21 | SUB(Common, Filesystem) \ | ||
| 22 | SUB(Common, Memory) \ | ||
| 23 | CLS(Core) \ | ||
| 24 | SUB(Core, ARM11) \ | ||
| 25 | CLS(Config) \ | ||
| 26 | CLS(Debug) \ | ||
| 27 | SUB(Debug, Emulated) \ | ||
| 28 | SUB(Debug, GPU) \ | ||
| 29 | SUB(Debug, Breakpoint) \ | ||
| 30 | CLS(Kernel) \ | ||
| 31 | SUB(Kernel, SVC) \ | ||
| 32 | CLS(Service) \ | ||
| 33 | SUB(Service, SRV) \ | ||
| 34 | SUB(Service, FS) \ | ||
| 35 | SUB(Service, APT) \ | ||
| 36 | SUB(Service, GSP) \ | ||
| 37 | SUB(Service, AC) \ | ||
| 38 | SUB(Service, PTM) \ | ||
| 39 | SUB(Service, CFG) \ | ||
| 40 | SUB(Service, DSP) \ | ||
| 41 | SUB(Service, HID) \ | ||
| 42 | CLS(HW) \ | ||
| 43 | SUB(HW, Memory) \ | ||
| 44 | SUB(HW, GPU) \ | ||
| 45 | CLS(Frontend) \ | ||
| 46 | CLS(Render) \ | ||
| 47 | SUB(Render, Software) \ | ||
| 48 | SUB(Render, OpenGL) \ | ||
| 49 | CLS(Loader) | ||
| 50 | |||
| 51 | Logger::Logger() { | ||
| 52 | // Register logging classes so that they can be queried at runtime | ||
| 53 | size_t parent_class; | ||
| 54 | all_classes.reserve((size_t)Class::Count); | ||
| 55 | |||
| 56 | #define CLS(x) \ | ||
| 57 | all_classes.push_back(Class::x); \ | ||
| 58 | parent_class = all_classes.size() - 1; | ||
| 59 | #define SUB(x, y) \ | ||
| 60 | all_classes.push_back(Class::x##_##y); \ | ||
| 61 | all_classes[parent_class].num_children += 1; | ||
| 62 | |||
| 63 | ALL_LOG_CLASSES() | ||
| 64 | #undef CLS | ||
| 65 | #undef SUB | ||
| 66 | |||
| 67 | // Ensures that ALL_LOG_CLASSES isn't missing any entries. | ||
| 68 | _dbg_assert_(Log, all_classes.size() == (size_t)Class::Count); | ||
| 69 | } | ||
| 70 | |||
| 71 | // GetClassName is a macro defined by Windows.h, grrr... | ||
| 72 | const char* Logger::GetLogClassName(Class log_class) { | ||
| 73 | switch (log_class) { | ||
| 74 | #define CLS(x) case Class::x: return #x; | ||
| 75 | #define SUB(x, y) case Class::x##_##y: return #x "." #y; | ||
| 76 | ALL_LOG_CLASSES() | ||
| 77 | #undef CLS | ||
| 78 | #undef SUB | ||
| 79 | } | ||
| 80 | return "Unknown"; | ||
| 81 | } | ||
| 82 | |||
| 83 | const char* Logger::GetLevelName(Level log_level) { | ||
| 84 | #define LVL(x) case Level::x: return #x | ||
| 85 | switch (log_level) { | ||
| 86 | LVL(Trace); | ||
| 87 | LVL(Debug); | ||
| 88 | LVL(Info); | ||
| 89 | LVL(Warning); | ||
| 90 | LVL(Error); | ||
| 91 | LVL(Critical); | ||
| 92 | } | ||
| 93 | return "Unknown"; | ||
| 94 | #undef LVL | ||
| 95 | } | ||
| 96 | |||
| 97 | void Logger::LogMessage(Entry entry) { | ||
| 98 | ring_buffer.Push(std::move(entry)); | ||
| 99 | } | ||
| 100 | |||
| 101 | size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) { | ||
| 102 | return ring_buffer.BlockingPop(out_buffer, buffer_len); | ||
| 103 | } | ||
| 104 | |||
| 105 | std::shared_ptr<Logger> InitGlobalLogger() { | ||
| 106 | global_logger = std::make_shared<Logger>(); | ||
| 107 | return global_logger; | ||
| 108 | } | ||
| 109 | |||
| 110 | Entry CreateEntry(Class log_class, Level log_level, | ||
| 111 | const char* filename, unsigned int line_nr, const char* function, | ||
| 112 | const char* format, va_list args) { | ||
| 113 | using std::chrono::steady_clock; | ||
| 114 | using std::chrono::duration_cast; | ||
| 115 | |||
| 116 | static steady_clock::time_point time_origin = steady_clock::now(); | ||
| 117 | |||
| 118 | std::array<char, 4 * 1024> formatting_buffer; | ||
| 119 | |||
| 120 | Entry entry; | ||
| 121 | entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | ||
| 122 | entry.log_class = log_class; | ||
| 123 | entry.log_level = log_level; | ||
| 124 | |||
| 125 | snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr); | ||
| 126 | entry.location = std::string(formatting_buffer.data()); | ||
| 127 | |||
| 128 | vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); | ||
| 129 | entry.message = std::string(formatting_buffer.data()); | ||
| 130 | |||
| 131 | return std::move(entry); | ||
| 132 | } | ||
| 133 | |||
| 134 | void LogMessage(Class log_class, Level log_level, | ||
| 135 | const char* filename, unsigned int line_nr, const char* function, | ||
| 136 | const char* format, ...) { | ||
| 137 | va_list args; | ||
| 138 | va_start(args, format); | ||
| 139 | Entry entry = CreateEntry(log_class, log_level, | ||
| 140 | filename, line_nr, function, format, args); | ||
| 141 | va_end(args); | ||
| 142 | |||
| 143 | if (global_logger != nullptr && !global_logger->IsClosed()) { | ||
| 144 | global_logger->LogMessage(std::move(entry)); | ||
| 145 | } else { | ||
| 146 | // Fall back to directly printing to stderr | ||
| 147 | PrintMessage(entry); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | } | ||
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h new file mode 100644 index 000000000..ae270efe8 --- /dev/null +++ b/src/common/logging/backend.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstdarg> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/concurrent_ring_buffer.h" | ||
| 12 | |||
| 13 | #include "common/logging/log.h" | ||
| 14 | |||
| 15 | namespace Log { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * A log entry. Log entries are store in a structured format to permit more varied output | ||
| 19 | * formatting on different frontends, as well as facilitating filtering and aggregation. | ||
| 20 | */ | ||
| 21 | struct Entry { | ||
| 22 | std::chrono::microseconds timestamp; | ||
| 23 | Class log_class; | ||
| 24 | Level log_level; | ||
| 25 | std::string location; | ||
| 26 | std::string message; | ||
| 27 | |||
| 28 | Entry() = default; | ||
| 29 | |||
| 30 | // TODO(yuriks) Use defaulted move constructors once MSVC supports them | ||
| 31 | #define MOVE(member) member(std::move(o.member)) | ||
| 32 | Entry(Entry&& o) | ||
| 33 | : MOVE(timestamp), MOVE(log_class), MOVE(log_level), | ||
| 34 | MOVE(location), MOVE(message) | ||
| 35 | {} | ||
| 36 | #undef MOVE | ||
| 37 | |||
| 38 | Entry& operator=(const Entry&& o) { | ||
| 39 | #define MOVE(member) member = std::move(o.member) | ||
| 40 | MOVE(timestamp); | ||
| 41 | MOVE(log_class); | ||
| 42 | MOVE(log_level); | ||
| 43 | MOVE(location); | ||
| 44 | MOVE(message); | ||
| 45 | #undef MOVE | ||
| 46 | return *this; | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct ClassInfo { | ||
| 51 | Class log_class; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Total number of (direct or indirect) sub classes this class has. If any, they follow in | ||
| 55 | * sequence after this class in the class list. | ||
| 56 | */ | ||
| 57 | unsigned int num_children = 0; | ||
| 58 | |||
| 59 | ClassInfo(Class log_class) : log_class(log_class) {} | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Logging management class. This class has the dual purpose of acting as an exchange point between | ||
| 64 | * the logging clients and the log outputter, as well as containing reflection info about available | ||
| 65 | * log classes. | ||
| 66 | */ | ||
| 67 | class Logger { | ||
| 68 | private: | ||
| 69 | using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>; | ||
| 70 | |||
| 71 | public: | ||
| 72 | static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED; | ||
| 73 | |||
| 74 | Logger(); | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of | ||
| 78 | * classes and subclasses, which together with the `num_children` field in ClassInfo, allows | ||
| 79 | * you to recover the hierarchy. | ||
| 80 | */ | ||
| 81 | const std::vector<ClassInfo>& GetClasses() const { return all_classes; } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Returns the name of the passed log class as a C-string. Subclasses are separated by periods | ||
| 85 | * instead of underscores as in the enumeration. | ||
| 86 | */ | ||
| 87 | static const char* GetLogClassName(Class log_class); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Returns the name of the passed log level as a C-string. | ||
| 91 | */ | ||
| 92 | static const char* GetLevelName(Level log_level); | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Appends a messages to the log buffer. | ||
| 96 | * @note This function is thread safe. | ||
| 97 | */ | ||
| 98 | void LogMessage(Entry entry); | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Retrieves a batch of messages from the log buffer, blocking until they are available. | ||
| 102 | * @note This function is thread safe. | ||
| 103 | * | ||
| 104 | * @param out_buffer Destination buffer that will receive the log entries. | ||
| 105 | * @param buffer_len The maximum size of `out_buffer`. | ||
| 106 | * @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is | ||
| 107 | * returned, no entries are stored and the logger should shutdown. | ||
| 108 | */ | ||
| 109 | size_t GetEntries(Entry* out_buffer, size_t buffer_len); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Initiates a shutdown of the logger. This will indicate to log output clients that they | ||
| 113 | * should shutdown. | ||
| 114 | */ | ||
| 115 | void Close() { ring_buffer.Close(); } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Returns true if Close() has already been called on the Logger. | ||
| 119 | */ | ||
| 120 | bool IsClosed() const { return ring_buffer.IsClosed(); } | ||
| 121 | |||
| 122 | private: | ||
| 123 | Buffer ring_buffer; | ||
| 124 | std::vector<ClassInfo> all_classes; | ||
| 125 | }; | ||
| 126 | |||
| 127 | /// Creates a log entry by formatting the given source location, and message. | ||
| 128 | Entry CreateEntry(Class log_class, Level log_level, | ||
| 129 | const char* filename, unsigned int line_nr, const char* function, | ||
| 130 | const char* format, va_list args); | ||
| 131 | /// Initializes the default Logger. | ||
| 132 | std::shared_ptr<Logger> InitGlobalLogger(); | ||
| 133 | |||
| 134 | } | ||
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp new file mode 100644 index 000000000..0cf9b05e7 --- /dev/null +++ b/src/common/logging/filter.cpp | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/logging/filter.h" | ||
| 8 | #include "common/logging/backend.h" | ||
| 9 | #include "common/string_util.h" | ||
| 10 | |||
| 11 | namespace Log { | ||
| 12 | |||
| 13 | Filter::Filter(Level default_level) { | ||
| 14 | ResetAll(default_level); | ||
| 15 | } | ||
| 16 | |||
| 17 | void Filter::ResetAll(Level level) { | ||
| 18 | class_levels.fill(level); | ||
| 19 | } | ||
| 20 | |||
| 21 | void Filter::SetClassLevel(Class log_class, Level level) { | ||
| 22 | class_levels[static_cast<size_t>(log_class)] = level; | ||
| 23 | } | ||
| 24 | |||
| 25 | void Filter::SetSubclassesLevel(const ClassInfo& log_class, Level level) { | ||
| 26 | const size_t log_class_i = static_cast<size_t>(log_class.log_class); | ||
| 27 | |||
| 28 | const size_t begin = log_class_i + 1; | ||
| 29 | const size_t end = begin + log_class.num_children; | ||
| 30 | for (size_t i = begin; begin < end; ++i) { | ||
| 31 | class_levels[i] = level; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | void Filter::ParseFilterString(const std::string& filter_str) { | ||
| 36 | auto clause_begin = filter_str.cbegin(); | ||
| 37 | while (clause_begin != filter_str.cend()) { | ||
| 38 | auto clause_end = std::find(clause_begin, filter_str.cend(), ' '); | ||
| 39 | |||
| 40 | // If clause isn't empty | ||
| 41 | if (clause_end != clause_begin) { | ||
| 42 | ParseFilterRule(clause_begin, clause_end); | ||
| 43 | } | ||
| 44 | |||
| 45 | if (clause_end != filter_str.cend()) { | ||
| 46 | // Skip over the whitespace | ||
| 47 | ++clause_end; | ||
| 48 | } | ||
| 49 | clause_begin = clause_end; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | template <typename It> | ||
| 54 | static Level GetLevelByName(const It begin, const It end) { | ||
| 55 | for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) { | ||
| 56 | const char* level_name = Logger::GetLevelName(static_cast<Level>(i)); | ||
| 57 | if (Common::ComparePartialString(begin, end, level_name)) { | ||
| 58 | return static_cast<Level>(i); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | return Level::Count; | ||
| 62 | } | ||
| 63 | |||
| 64 | template <typename It> | ||
| 65 | static Class GetClassByName(const It begin, const It end) { | ||
| 66 | for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) { | ||
| 67 | const char* level_name = Logger::GetLogClassName(static_cast<Class>(i)); | ||
| 68 | if (Common::ComparePartialString(begin, end, level_name)) { | ||
| 69 | return static_cast<Class>(i); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | return Class::Count; | ||
| 73 | } | ||
| 74 | |||
| 75 | template <typename InputIt, typename T> | ||
| 76 | static InputIt find_last(InputIt begin, const InputIt end, const T& value) { | ||
| 77 | auto match = end; | ||
| 78 | while (begin != end) { | ||
| 79 | auto new_match = std::find(begin, end, value); | ||
| 80 | if (new_match != end) { | ||
| 81 | match = new_match; | ||
| 82 | ++new_match; | ||
| 83 | } | ||
| 84 | begin = new_match; | ||
| 85 | } | ||
| 86 | return match; | ||
| 87 | } | ||
| 88 | |||
| 89 | bool Filter::ParseFilterRule(const std::string::const_iterator begin, | ||
| 90 | const std::string::const_iterator end) { | ||
| 91 | auto level_separator = std::find(begin, end, ':'); | ||
| 92 | if (level_separator == end) { | ||
| 93 | LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", | ||
| 94 | std::string(begin, end).c_str()); | ||
| 95 | return false; | ||
| 96 | } | ||
| 97 | |||
| 98 | const Level level = GetLevelByName(level_separator + 1, end); | ||
| 99 | if (level == Level::Count) { | ||
| 100 | LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (Common::ComparePartialString(begin, level_separator, "*")) { | ||
| 105 | ResetAll(level); | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | auto class_name_end = find_last(begin, level_separator, '.'); | ||
| 110 | if (class_name_end != level_separator && | ||
| 111 | !Common::ComparePartialString(class_name_end + 1, level_separator, "*")) { | ||
| 112 | class_name_end = level_separator; | ||
| 113 | } | ||
| 114 | |||
| 115 | const Class log_class = GetClassByName(begin, class_name_end); | ||
| 116 | if (log_class == Class::Count) { | ||
| 117 | LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | if (class_name_end == level_separator) { | ||
| 122 | SetClassLevel(log_class, level); | ||
| 123 | } | ||
| 124 | SetSubclassesLevel(log_class, level); | ||
| 125 | return true; | ||
| 126 | } | ||
| 127 | |||
| 128 | bool Filter::CheckMessage(Class log_class, Level level) const { | ||
| 129 | return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]); | ||
| 130 | } | ||
| 131 | |||
| 132 | } | ||
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h new file mode 100644 index 000000000..32b14b159 --- /dev/null +++ b/src/common/logging/filter.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | |||
| 10 | namespace Log { | ||
| 11 | |||
| 12 | struct ClassInfo; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Implements a log message filter which allows different log classes to have different minimum | ||
| 16 | * severity levels. The filter can be changed at runtime and can be parsed from a string to allow | ||
| 17 | * editing via the interface or loading from a configuration file. | ||
| 18 | */ | ||
| 19 | class Filter { | ||
| 20 | public: | ||
| 21 | /// Initializes the filter with all classes having `default_level` as the minimum level. | ||
| 22 | Filter(Level default_level); | ||
| 23 | |||
| 24 | /// Resets the filter so that all classes have `level` as the minimum displayed level. | ||
| 25 | void ResetAll(Level level); | ||
| 26 | /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`. | ||
| 27 | void SetClassLevel(Class log_class, Level level); | ||
| 28 | /** | ||
| 29 | * Sets the minimum level of all of `log_class` subclasses to `level`. The level of `log_class` | ||
| 30 | * itself is not changed. | ||
| 31 | */ | ||
| 32 | void SetSubclassesLevel(const ClassInfo& log_class, Level level); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Parses a filter string and applies it to this filter. | ||
| 36 | * | ||
| 37 | * A filter string consists of a space-separated list of filter rules, each of the format | ||
| 38 | * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods. | ||
| 39 | * A rule for a given class also affects all of its subclasses. `*` wildcards are allowed and | ||
| 40 | * can be used to apply a rule to all classes or to all subclasses of a class without affecting | ||
| 41 | * the parent class. `<level>` a severity level name which will be set as the minimum logging | ||
| 42 | * level of the matched classes. Rules are applied left to right, with each rule overriding | ||
| 43 | * previous ones in the sequence. | ||
| 44 | * | ||
| 45 | * A few examples of filter rules: | ||
| 46 | * - `*:Info` -- Resets the level of all classes to Info. | ||
| 47 | * - `Service:Info` -- Sets the level of Service and all subclasses (Service.FS, Service.APT, | ||
| 48 | * etc.) to Info. | ||
| 49 | * - `Service.*:Debug` -- Sets the level of all Service subclasses to Debug, while leaving the | ||
| 50 | * level of Service unchanged. | ||
| 51 | * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace. | ||
| 52 | */ | ||
| 53 | void ParseFilterString(const std::string& filter_str); | ||
| 54 | bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end); | ||
| 55 | |||
| 56 | /// Matches class/level combination against the filter, returning true if it passed. | ||
| 57 | bool CheckMessage(Class log_class, Level level) const; | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::array<Level, (size_t)Class::Count> class_levels; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } | ||
diff --git a/src/common/logging/log.h b/src/common/logging/log.h new file mode 100644 index 000000000..1eec34fbb --- /dev/null +++ b/src/common/logging/log.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cassert> | ||
| 8 | #include <chrono> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Log { | ||
| 14 | |||
| 15 | /// Specifies the severity or level of detail of the log message. | ||
| 16 | enum class Level : u8 { | ||
| 17 | Trace, ///< Extremely detailed and repetitive debugging information that is likely to | ||
| 18 | /// pollute logs. | ||
| 19 | Debug, ///< Less detailed debugging information. | ||
| 20 | Info, ///< Status information from important points during execution. | ||
| 21 | Warning, ///< Minor or potential problems found during execution of a task. | ||
| 22 | Error, ///< Major problems found during execution of a task that prevent it from being | ||
| 23 | /// completed. | ||
| 24 | Critical, ///< Major problems during execution that threathen the stability of the entire | ||
| 25 | /// application. | ||
| 26 | |||
| 27 | Count ///< Total number of logging levels | ||
| 28 | }; | ||
| 29 | |||
| 30 | typedef u8 ClassType; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Specifies the sub-system that generated the log message. | ||
| 34 | * | ||
| 35 | * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp. | ||
| 36 | */ | ||
| 37 | enum class Class : ClassType { | ||
| 38 | Log, ///< Messages about the log system itself | ||
| 39 | Common, ///< Library routines | ||
| 40 | Common_Filesystem, ///< Filesystem interface library | ||
| 41 | Common_Memory, ///< Memory mapping and management functions | ||
| 42 | Core, ///< LLE emulation core | ||
| 43 | Core_ARM11, ///< ARM11 CPU core | ||
| 44 | Config, ///< Emulator configuration (including commandline) | ||
| 45 | Debug, ///< Debugging tools | ||
| 46 | Debug_Emulated, ///< Debug messages from the emulated programs | ||
| 47 | Debug_GPU, ///< GPU debugging tools | ||
| 48 | Debug_Breakpoint, ///< Logging breakpoints and watchpoints | ||
| 49 | Kernel, ///< The HLE implementation of the CTR kernel | ||
| 50 | Kernel_SVC, ///< Kernel system calls | ||
| 51 | Service, ///< HLE implementation of system services. Each major service | ||
| 52 | /// should have its own subclass. | ||
| 53 | Service_SRV, ///< The SRV (Service Directory) implementation | ||
| 54 | Service_FS, ///< The FS (Filesystem) service implementation | ||
| 55 | Service_APT, ///< The APT (Applets) service | ||
| 56 | Service_GSP, ///< The GSP (GPU control) service | ||
| 57 | Service_AC, ///< The AC (WiFi status) service | ||
| 58 | Service_PTM, ///< The PTM (Power status & misc.) service | ||
| 59 | Service_CFG, ///< The CFG (Configuration) service | ||
| 60 | Service_DSP, ///< The DSP (DSP control) service | ||
| 61 | Service_HID, ///< The HID (User input) service | ||
| 62 | HW, ///< Low-level hardware emulation | ||
| 63 | HW_Memory, ///< Memory-map and address translation | ||
| 64 | HW_GPU, ///< GPU control emulation | ||
| 65 | Frontend, ///< Emulator UI | ||
| 66 | Render, ///< Emulator video output and hardware acceleration | ||
| 67 | Render_Software, ///< Software renderer backend | ||
| 68 | Render_OpenGL, ///< OpenGL backend | ||
| 69 | Loader, ///< ROM loader | ||
| 70 | |||
| 71 | Count ///< Total number of logging classes | ||
| 72 | }; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Level below which messages are simply discarded without buffering regardless of the display | ||
| 76 | * settings. | ||
| 77 | */ | ||
| 78 | const Level MINIMUM_LEVEL = | ||
| 79 | #ifdef _DEBUG | ||
| 80 | Level::Trace; | ||
| 81 | #else | ||
| 82 | Level::Debug; | ||
| 83 | #endif | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Logs a message to the global logger. This proxy exists to avoid exposing the details of the | ||
| 87 | * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log | ||
| 88 | * messages, reducing unecessary recompilations. | ||
| 89 | */ | ||
| 90 | void LogMessage(Class log_class, Level log_level, | ||
| 91 | const char* filename, unsigned int line_nr, const char* function, | ||
| 92 | #ifdef _MSC_VER | ||
| 93 | _Printf_format_string_ | ||
| 94 | #endif | ||
| 95 | const char* format, ...) | ||
| 96 | #ifdef __GNUC__ | ||
| 97 | __attribute__((format(printf, 6, 7))) | ||
| 98 | #endif | ||
| 99 | ; | ||
| 100 | |||
| 101 | } // namespace Log | ||
| 102 | |||
| 103 | #define LOG_GENERIC(log_class, log_level, ...) \ | ||
| 104 | do { \ | ||
| 105 | if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \ | ||
| 106 | ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ | ||
| 107 | __FILE__, __LINE__, __func__, __VA_ARGS__); \ | ||
| 108 | } while (0) | ||
| 109 | |||
| 110 | #define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__) | ||
| 111 | #define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__) | ||
| 112 | #define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__) | ||
| 113 | #define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__) | ||
| 114 | #define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__) | ||
| 115 | #define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__) | ||
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp new file mode 100644 index 000000000..f6b02fd47 --- /dev/null +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <cstdio> | ||
| 7 | |||
| 8 | #ifdef _WIN32 | ||
| 9 | # define WIN32_LEAN_AND_MEAN | ||
| 10 | # include <Windows.h> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | #include "common/logging/backend.h" | ||
| 14 | #include "common/logging/filter.h" | ||
| 15 | #include "common/logging/log.h" | ||
| 16 | #include "common/logging/text_formatter.h" | ||
| 17 | |||
| 18 | #include "common/string_util.h" | ||
| 19 | |||
| 20 | namespace Log { | ||
| 21 | |||
| 22 | // TODO(bunnei): This should be moved to a generic path manipulation library | ||
| 23 | const char* TrimSourcePath(const char* path, const char* root) { | ||
| 24 | const char* p = path; | ||
| 25 | |||
| 26 | while (*p != '\0') { | ||
| 27 | const char* next_slash = p; | ||
| 28 | while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { | ||
| 29 | ++next_slash; | ||
| 30 | } | ||
| 31 | |||
| 32 | bool is_src = Common::ComparePartialString(p, next_slash, root); | ||
| 33 | p = next_slash; | ||
| 34 | |||
| 35 | if (*p != '\0') { | ||
| 36 | ++p; | ||
| 37 | } | ||
| 38 | if (is_src) { | ||
| 39 | path = p; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | return path; | ||
| 43 | } | ||
| 44 | |||
| 45 | void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) { | ||
| 46 | unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); | ||
| 47 | unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000); | ||
| 48 | |||
| 49 | const char* class_name = Logger::GetLogClassName(entry.log_class); | ||
| 50 | const char* level_name = Logger::GetLevelName(entry.log_level); | ||
| 51 | |||
| 52 | snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", | ||
| 53 | time_seconds, time_fractional, class_name, level_name, | ||
| 54 | TrimSourcePath(entry.location.c_str()), entry.message.c_str()); | ||
| 55 | } | ||
| 56 | |||
| 57 | void PrintMessage(const Entry& entry) { | ||
| 58 | std::array<char, 4 * 1024> format_buffer; | ||
| 59 | FormatLogMessage(entry, format_buffer.data(), format_buffer.size()); | ||
| 60 | fputs(format_buffer.data(), stderr); | ||
| 61 | fputc('\n', stderr); | ||
| 62 | } | ||
| 63 | |||
| 64 | void PrintColoredMessage(const Entry& entry) { | ||
| 65 | #ifdef _WIN32 | ||
| 66 | static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); | ||
| 67 | |||
| 68 | CONSOLE_SCREEN_BUFFER_INFO original_info = {0}; | ||
| 69 | GetConsoleScreenBufferInfo(console_handle, &original_info); | ||
| 70 | |||
| 71 | WORD color = 0; | ||
| 72 | switch (entry.log_level) { | ||
| 73 | case Level::Trace: // Grey | ||
| 74 | color = FOREGROUND_INTENSITY; break; | ||
| 75 | case Level::Debug: // Cyan | ||
| 76 | color = FOREGROUND_GREEN | FOREGROUND_BLUE; break; | ||
| 77 | case Level::Info: // Bright gray | ||
| 78 | color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; | ||
| 79 | case Level::Warning: // Bright yellow | ||
| 80 | color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; | ||
| 81 | case Level::Error: // Bright red | ||
| 82 | color = FOREGROUND_RED | FOREGROUND_INTENSITY; break; | ||
| 83 | case Level::Critical: // Bright magenta | ||
| 84 | color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; | ||
| 85 | } | ||
| 86 | |||
| 87 | SetConsoleTextAttribute(console_handle, color); | ||
| 88 | #else | ||
| 89 | # define ESC "\x1b" | ||
| 90 | const char* color = ""; | ||
| 91 | switch (entry.log_level) { | ||
| 92 | case Level::Trace: // Grey | ||
| 93 | color = ESC "[1;30m"; break; | ||
| 94 | case Level::Debug: // Cyan | ||
| 95 | color = ESC "[0;36m"; break; | ||
| 96 | case Level::Info: // Bright gray | ||
| 97 | color = ESC "[0;37m"; break; | ||
| 98 | case Level::Warning: // Bright yellow | ||
| 99 | color = ESC "[1;33m"; break; | ||
| 100 | case Level::Error: // Bright red | ||
| 101 | color = ESC "[1;31m"; break; | ||
| 102 | case Level::Critical: // Bright magenta | ||
| 103 | color = ESC "[1;35m"; break; | ||
| 104 | } | ||
| 105 | |||
| 106 | fputs(color, stderr); | ||
| 107 | #endif | ||
| 108 | |||
| 109 | PrintMessage(entry); | ||
| 110 | |||
| 111 | #ifdef _WIN32 | ||
| 112 | SetConsoleTextAttribute(console_handle, original_info.wAttributes); | ||
| 113 | #else | ||
| 114 | fputs(ESC "[0m", stderr); | ||
| 115 | # undef ESC | ||
| 116 | #endif | ||
| 117 | } | ||
| 118 | |||
| 119 | void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) { | ||
| 120 | std::array<Entry, 256> entry_buffer; | ||
| 121 | |||
| 122 | while (true) { | ||
| 123 | size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size()); | ||
| 124 | if (num_entries == Logger::QUEUE_CLOSED) { | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | for (size_t i = 0; i < num_entries; ++i) { | ||
| 128 | const Entry& entry = entry_buffer[i]; | ||
| 129 | if (filter->CheckMessage(entry.log_class, entry.log_level)) { | ||
| 130 | PrintColoredMessage(entry); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | } | ||
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h new file mode 100644 index 000000000..1f73ca44a --- /dev/null +++ b/src/common/logging/text_formatter.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | namespace Log { | ||
| 11 | |||
| 12 | class Logger; | ||
| 13 | struct Entry; | ||
| 14 | class Filter; | ||
| 15 | |||
| 16 | /** | ||
| 17 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | ||
| 18 | * intended to be used to strip a system-specific build directory from the `__FILE__` macro, | ||
| 19 | * leaving only the path relative to the sources root. | ||
| 20 | * | ||
| 21 | * @param path The input file path as a null-terminated string | ||
| 22 | * @param root The name of the root source directory as a null-terminated string. Path up to and | ||
| 23 | * including the last occurence of this name will be stripped | ||
| 24 | * @return A pointer to the same string passed as `path`, but starting at the trimmed portion | ||
| 25 | */ | ||
| 26 | const char* TrimSourcePath(const char* path, const char* root = "src"); | ||
| 27 | |||
| 28 | /// Formats a log entry into the provided text buffer. | ||
| 29 | void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len); | ||
| 30 | /// Formats and prints a log entry to stderr. | ||
| 31 | void PrintMessage(const Entry& entry); | ||
| 32 | /// Prints the same message as `PrintMessage`, but colored acoording to the severity level. | ||
| 33 | void PrintColoredMessage(const Entry& entry); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Logging loop that repeatedly reads messages from the provided logger and prints them to the | ||
| 37 | * console. It is the baseline barebones log outputter. | ||
| 38 | */ | ||
| 39 | void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter); | ||
| 40 | |||
| 41 | } | ||
diff --git a/src/common/math_util.cpp b/src/common/math_util.cpp index ab0e6b75c..3613e82a6 100644 --- a/src/common/math_util.cpp +++ b/src/common/math_util.cpp | |||
| @@ -18,7 +18,7 @@ u32 ClassifyDouble(double dvalue) | |||
| 18 | value.d = dvalue; | 18 | value.d = dvalue; |
| 19 | u64 sign = value.i & DOUBLE_SIGN; | 19 | u64 sign = value.i & DOUBLE_SIGN; |
| 20 | u64 exp = value.i & DOUBLE_EXP; | 20 | u64 exp = value.i & DOUBLE_EXP; |
| 21 | if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP) | 21 | if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP) |
| 22 | { | 22 | { |
| 23 | // Nice normalized number. | 23 | // Nice normalized number. |
| 24 | return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; | 24 | return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; |
| @@ -58,7 +58,7 @@ u32 ClassifyFloat(float fvalue) | |||
| 58 | value.f = fvalue; | 58 | value.f = fvalue; |
| 59 | u32 sign = value.i & FLOAT_SIGN; | 59 | u32 sign = value.i & FLOAT_SIGN; |
| 60 | u32 exp = value.i & FLOAT_EXP; | 60 | u32 exp = value.i & FLOAT_EXP; |
| 61 | if (exp > FLOAT_ZERO && exp < FLOAT_EXP) | 61 | if (exp > FLOAT_ZERO && exp < FLOAT_EXP) |
| 62 | { | 62 | { |
| 63 | // Nice normalized number. | 63 | // Nice normalized number. |
| 64 | return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; | 64 | return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; |
| @@ -77,13 +77,13 @@ u32 ClassifyFloat(float fvalue) | |||
| 77 | // Denormalized number. | 77 | // Denormalized number. |
| 78 | return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; | 78 | return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; |
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| 81 | else if (exp) | 81 | else if (exp) |
| 82 | { | 82 | { |
| 83 | // Infinite | 83 | // Infinite |
| 84 | return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; | 84 | return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; |
| 85 | } | 85 | } |
| 86 | else | 86 | else |
| 87 | { | 87 | { |
| 88 | //Zero | 88 | //Zero |
| 89 | return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; | 89 | return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; |
| @@ -143,7 +143,7 @@ void Matrix33::RotateY(Matrix33 &mtx, float rad) | |||
| 143 | mtx.data[0] = c; | 143 | mtx.data[0] = c; |
| 144 | mtx.data[2] = s; | 144 | mtx.data[2] = s; |
| 145 | mtx.data[4] = 1; | 145 | mtx.data[4] = 1; |
| 146 | mtx.data[6] = -s; | 146 | mtx.data[6] = -s; |
| 147 | mtx.data[8] = c; | 147 | mtx.data[8] = c; |
| 148 | } | 148 | } |
| 149 | 149 | ||
diff --git a/src/common/math_util.h b/src/common/math_util.h index b32e7bb14..b10a25c13 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/common.h" | 7 | #include "common/common.h" |
| 8 | 8 | ||
| 9 | #include <algorithm> | 9 | #include <algorithm> |
| 10 | #include <type_traits> | ||
| 10 | #include <vector> | 11 | #include <vector> |
| 11 | 12 | ||
| 12 | namespace MathUtil | 13 | namespace MathUtil |
| @@ -109,11 +110,11 @@ struct Rectangle | |||
| 109 | Rectangle(T theLeft, T theTop, T theRight, T theBottom) | 110 | Rectangle(T theLeft, T theTop, T theRight, T theBottom) |
| 110 | : left(theLeft), top(theTop), right(theRight), bottom(theBottom) | 111 | : left(theLeft), top(theTop), right(theRight), bottom(theBottom) |
| 111 | { } | 112 | { } |
| 112 | 113 | ||
| 113 | bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } | 114 | bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } |
| 114 | 115 | ||
| 115 | T GetWidth() const { return abs(right - left); } | 116 | T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); } |
| 116 | T GetHeight() const { return abs(bottom - top); } | 117 | T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); } |
| 117 | 118 | ||
| 118 | // If the rectangle is in a coordinate system with a lower-left origin, use | 119 | // If the rectangle is in a coordinate system with a lower-left origin, use |
| 119 | // this Clamp. | 120 | // this Clamp. |
| @@ -127,7 +128,7 @@ struct Rectangle | |||
| 127 | 128 | ||
| 128 | // If the rectangle is in a coordinate system with an upper-left origin, | 129 | // If the rectangle is in a coordinate system with an upper-left origin, |
| 129 | // use this Clamp. | 130 | // use this Clamp. |
| 130 | void ClampUL(T x1, T y1, T x2, T y2) | 131 | void ClampUL(T x1, T y1, T x2, T y2) |
| 131 | { | 132 | { |
| 132 | if (left < x1) left = x1; | 133 | if (left < x1) left = x1; |
| 133 | if (right > x2) right = x2; | 134 | if (right > x2) right = x2; |
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp index 40d9c03a2..9904d2472 100644 --- a/src/common/mem_arena.cpp +++ b/src/common/mem_arena.cpp | |||
| @@ -30,7 +30,7 @@ | |||
| 30 | #endif | 30 | #endif |
| 31 | 31 | ||
| 32 | #ifdef IOS | 32 | #ifdef IOS |
| 33 | void* globalbase = NULL; | 33 | void* globalbase = nullptr; |
| 34 | #endif | 34 | #endif |
| 35 | 35 | ||
| 36 | #ifdef ANDROID | 36 | #ifdef ANDROID |
| @@ -38,7 +38,7 @@ void* globalbase = NULL; | |||
| 38 | // Hopefully this ABI will never change... | 38 | // Hopefully this ABI will never change... |
| 39 | 39 | ||
| 40 | 40 | ||
| 41 | #define ASHMEM_DEVICE "/dev/ashmem" | 41 | #define ASHMEM_DEVICE "/dev/ashmem" |
| 42 | 42 | ||
| 43 | /* | 43 | /* |
| 44 | * ashmem_create_region - creates a new ashmem region and returns the file | 44 | * ashmem_create_region - creates a new ashmem region and returns the file |
| @@ -71,7 +71,7 @@ int ashmem_create_region(const char *name, size_t size) | |||
| 71 | return fd; | 71 | return fd; |
| 72 | 72 | ||
| 73 | error: | 73 | error: |
| 74 | ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret); | 74 | LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret); |
| 75 | close(fd); | 75 | close(fd); |
| 76 | return ret; | 76 | return ret; |
| 77 | } | 77 | } |
| @@ -121,7 +121,7 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
| 121 | { | 121 | { |
| 122 | #ifdef _WIN32 | 122 | #ifdef _WIN32 |
| 123 | #ifndef _XBOX | 123 | #ifndef _XBOX |
| 124 | hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL); | 124 | hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr); |
| 125 | GetSystemInfo(&sysInfo); | 125 | GetSystemInfo(&sysInfo); |
| 126 | #endif | 126 | #endif |
| 127 | #elif defined(ANDROID) | 127 | #elif defined(ANDROID) |
| @@ -130,7 +130,7 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
| 130 | // Note that it appears that ashmem is pinned by default, so no need to pin. | 130 | // Note that it appears that ashmem is pinned by default, so no need to pin. |
| 131 | if (fd < 0) | 131 | if (fd < 0) |
| 132 | { | 132 | { |
| 133 | ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno)); | 133 | LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno)); |
| 134 | return; | 134 | return; |
| 135 | } | 135 | } |
| 136 | #else | 136 | #else |
| @@ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
| 148 | } | 148 | } |
| 149 | else if (errno != EEXIST) | 149 | else if (errno != EEXIST) |
| 150 | { | 150 | { |
| 151 | ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno)); | 151 | LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno)); |
| 152 | return; | 152 | return; |
| 153 | } | 153 | } |
| 154 | } | 154 | } |
| 155 | if (ftruncate(fd, size) < 0) | 155 | if (ftruncate(fd, size) < 0) |
| 156 | ERROR_LOG(MEMMAP, "Failed to allocate low memory space"); | 156 | LOG_ERROR(Common_Memory, "Failed to allocate low memory space"); |
| 157 | #endif | 157 | #endif |
| 158 | } | 158 | } |
| 159 | 159 | ||
| @@ -178,7 +178,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| 178 | #ifdef _XBOX | 178 | #ifdef _XBOX |
| 179 | size = roundup(size); | 179 | size = roundup(size); |
| 180 | // use 64kb pages | 180 | // use 64kb pages |
| 181 | void * ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); | 181 | void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); |
| 182 | return ptr; | 182 | return ptr; |
| 183 | #else | 183 | #else |
| 184 | size = roundup(size); | 184 | size = roundup(size); |
| @@ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| 197 | 197 | ||
| 198 | if (retval == MAP_FAILED) | 198 | if (retval == MAP_FAILED) |
| 199 | { | 199 | { |
| 200 | NOTICE_LOG(MEMMAP, "mmap failed"); | 200 | LOG_ERROR(Common_Memory, "mmap failed"); |
| 201 | return nullptr; | 201 | return nullptr; |
| 202 | } | 202 | } |
| 203 | return retval; | 203 | return retval; |
| @@ -243,8 +243,8 @@ u8* MemArena::Find4GBBase() | |||
| 243 | return base; | 243 | return base; |
| 244 | #else | 244 | #else |
| 245 | #ifdef IOS | 245 | #ifdef IOS |
| 246 | void* base = NULL; | 246 | void* base = nullptr; |
| 247 | if (globalbase == NULL){ | 247 | if (globalbase == nullptr){ |
| 248 | base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE, | 248 | base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE, |
| 249 | MAP_ANON | MAP_SHARED, -1, 0); | 249 | MAP_ANON | MAP_SHARED, -1, 0); |
| 250 | if (base == MAP_FAILED) { | 250 | if (base == MAP_FAILED) { |
| @@ -272,11 +272,11 @@ u8* MemArena::Find4GBBase() | |||
| 272 | 272 | ||
| 273 | 273 | ||
| 274 | // yeah, this could also be done in like two bitwise ops... | 274 | // yeah, this could also be done in like two bitwise ops... |
| 275 | #define SKIP(a_flags, b_flags) | 275 | #define SKIP(a_flags, b_flags) |
| 276 | // if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) | 276 | //if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) |
| 277 | // continue; | 277 | // continue; |
| 278 | // if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) | 278 | //if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) |
| 279 | // continue; | 279 | // continue; |
| 280 | 280 | ||
| 281 | static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) { | 281 | static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) { |
| 282 | // OK, we know where to find free space. Now grab it! | 282 | // OK, we know where to find free space. Now grab it! |
| @@ -357,7 +357,7 @@ bail: | |||
| 357 | if (views[j].out_ptr_low && *views[j].out_ptr_low) | 357 | if (views[j].out_ptr_low && *views[j].out_ptr_low) |
| 358 | { | 358 | { |
| 359 | arena->ReleaseView(*views[j].out_ptr_low, views[j].size); | 359 | arena->ReleaseView(*views[j].out_ptr_low, views[j].size); |
| 360 | *views[j].out_ptr_low = NULL; | 360 | *views[j].out_ptr_low = nullptr; |
| 361 | } | 361 | } |
| 362 | if (*views[j].out_ptr) | 362 | if (*views[j].out_ptr) |
| 363 | { | 363 | { |
| @@ -369,7 +369,7 @@ bail: | |||
| 369 | arena->ReleaseView(*views[j].out_ptr, views[j].size); | 369 | arena->ReleaseView(*views[j].out_ptr, views[j].size); |
| 370 | } | 370 | } |
| 371 | #endif | 371 | #endif |
| 372 | *views[j].out_ptr = NULL; | 372 | *views[j].out_ptr = nullptr; |
| 373 | } | 373 | } |
| 374 | } | 374 | } |
| 375 | return false; | 375 | return false; |
| @@ -415,7 +415,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| 415 | #elif defined(_WIN32) | 415 | #elif defined(_WIN32) |
| 416 | // Try a whole range of possible bases. Return once we got a valid one. | 416 | // Try a whole range of possible bases. Return once we got a valid one. |
| 417 | u32 max_base_addr = 0x7FFF0000 - 0x10000000; | 417 | u32 max_base_addr = 0x7FFF0000 - 0x10000000; |
| 418 | u8 *base = NULL; | 418 | u8 *base = nullptr; |
| 419 | 419 | ||
| 420 | for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000) | 420 | for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000) |
| 421 | { | 421 | { |
| @@ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| 423 | base = (u8 *)base_addr; | 423 | base = (u8 *)base_addr; |
| 424 | if (Memory_TryBase(base, views, num_views, flags, arena)) | 424 | if (Memory_TryBase(base, views, num_views, flags, arena)) |
| 425 | { | 425 | { |
| 426 | INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts); | 426 | LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts); |
| 427 | base_attempts = 0; | 427 | base_attempts = 0; |
| 428 | break; | 428 | break; |
| 429 | } | 429 | } |
| @@ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| 442 | u8 *base = MemArena::Find4GBBase(); | 442 | u8 *base = MemArena::Find4GBBase(); |
| 443 | if (!Memory_TryBase(base, views, num_views, flags, arena)) | 443 | if (!Memory_TryBase(base, views, num_views, flags, arena)) |
| 444 | { | 444 | { |
| 445 | ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base."); | 445 | LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base."); |
| 446 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | 446 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); |
| 447 | return 0; | 447 | return 0; |
| 448 | } | 448 | } |
| @@ -463,8 +463,8 @@ void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemAr | |||
| 463 | arena->ReleaseView(*views[i].out_ptr_low, views[i].size); | 463 | arena->ReleaseView(*views[i].out_ptr_low, views[i].size); |
| 464 | if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low)) | 464 | if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low)) |
| 465 | arena->ReleaseView(*views[i].out_ptr, views[i].size); | 465 | arena->ReleaseView(*views[i].out_ptr, views[i].size); |
| 466 | *views[i].out_ptr = NULL; | 466 | *views[i].out_ptr = nullptr; |
| 467 | if (views[i].out_ptr_low) | 467 | if (views[i].out_ptr_low) |
| 468 | *views[i].out_ptr_low = NULL; | 468 | *views[i].out_ptr_low = nullptr; |
| 469 | } | 469 | } |
| 470 | } | 470 | } |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index bab7d9f7a..ca8a2a9e4 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp | |||
| @@ -47,7 +47,7 @@ void* AllocateExecutableMemory(size_t size, bool low) | |||
| 47 | 47 | ||
| 48 | // printf("Mapped executable memory at %p (size %ld)\n", ptr, | 48 | // printf("Mapped executable memory at %p (size %ld)\n", ptr, |
| 49 | // (unsigned long)size); | 49 | // (unsigned long)size); |
| 50 | 50 | ||
| 51 | #ifdef _WIN32 | 51 | #ifdef _WIN32 |
| 52 | if (ptr == nullptr) | 52 | if (ptr == nullptr) |
| 53 | { | 53 | { |
| @@ -55,7 +55,7 @@ void* AllocateExecutableMemory(size_t size, bool low) | |||
| 55 | if (ptr == MAP_FAILED) | 55 | if (ptr == MAP_FAILED) |
| 56 | { | 56 | { |
| 57 | ptr = nullptr; | 57 | ptr = nullptr; |
| 58 | #endif | 58 | #endif |
| 59 | PanicAlert("Failed to allocate executable memory"); | 59 | PanicAlert("Failed to allocate executable memory"); |
| 60 | } | 60 | } |
| 61 | #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) | 61 | #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) |
| @@ -93,7 +93,7 @@ void* AllocateMemoryPages(size_t size) | |||
| 93 | // printf("Mapped memory at %p (size %ld)\n", ptr, | 93 | // printf("Mapped memory at %p (size %ld)\n", ptr, |
| 94 | // (unsigned long)size); | 94 | // (unsigned long)size); |
| 95 | 95 | ||
| 96 | if (ptr == NULL) | 96 | if (ptr == nullptr) |
| 97 | PanicAlert("Failed to allocate raw memory"); | 97 | PanicAlert("Failed to allocate raw memory"); |
| 98 | 98 | ||
| 99 | return ptr; | 99 | return ptr; |
| @@ -104,19 +104,19 @@ void* AllocateAlignedMemory(size_t size,size_t alignment) | |||
| 104 | #ifdef _WIN32 | 104 | #ifdef _WIN32 |
| 105 | void* ptr = _aligned_malloc(size,alignment); | 105 | void* ptr = _aligned_malloc(size,alignment); |
| 106 | #else | 106 | #else |
| 107 | void* ptr = NULL; | 107 | void* ptr = nullptr; |
| 108 | #ifdef ANDROID | 108 | #ifdef ANDROID |
| 109 | ptr = memalign(alignment, size); | 109 | ptr = memalign(alignment, size); |
| 110 | #else | 110 | #else |
| 111 | if (posix_memalign(&ptr, alignment, size) != 0) | 111 | if (posix_memalign(&ptr, alignment, size) != 0) |
| 112 | ERROR_LOG(MEMMAP, "Failed to allocate aligned memory"); | 112 | LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); |
| 113 | #endif | 113 | #endif |
| 114 | #endif | 114 | #endif |
| 115 | 115 | ||
| 116 | // printf("Mapped memory at %p (size %ld)\n", ptr, | 116 | // printf("Mapped memory at %p (size %ld)\n", ptr, |
| 117 | // (unsigned long)size); | 117 | // (unsigned long)size); |
| 118 | 118 | ||
| 119 | if (ptr == NULL) | 119 | if (ptr == nullptr) |
| 120 | PanicAlert("Failed to allocate aligned memory"); | 120 | PanicAlert("Failed to allocate aligned memory"); |
| 121 | 121 | ||
| 122 | return ptr; | 122 | return ptr; |
| @@ -127,11 +127,11 @@ void FreeMemoryPages(void* ptr, size_t size) | |||
| 127 | if (ptr) | 127 | if (ptr) |
| 128 | { | 128 | { |
| 129 | #ifdef _WIN32 | 129 | #ifdef _WIN32 |
| 130 | 130 | ||
| 131 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) | 131 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) |
| 132 | PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); | 132 | PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); |
| 133 | ptr = NULL; // Is this our responsibility? | 133 | ptr = nullptr; // Is this our responsibility? |
| 134 | 134 | ||
| 135 | #else | 135 | #else |
| 136 | munmap(ptr, size); | 136 | munmap(ptr, size); |
| 137 | #endif | 137 | #endif |
| @@ -184,7 +184,7 @@ std::string MemUsage() | |||
| 184 | // Print information about the memory usage of the process. | 184 | // Print information about the memory usage of the process. |
| 185 | 185 | ||
| 186 | hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); | 186 | hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); |
| 187 | if (NULL == hProcess) return "MemUsage Error"; | 187 | if (nullptr == hProcess) return "MemUsage Error"; |
| 188 | 188 | ||
| 189 | if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) | 189 | if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) |
| 190 | Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); | 190 | Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); |
diff --git a/src/common/misc.cpp b/src/common/misc.cpp index cf6df44e8..bc9d26188 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp | |||
| @@ -23,9 +23,9 @@ const char* GetLastErrorMsg() | |||
| 23 | #ifdef _WIN32 | 23 | #ifdef _WIN32 |
| 24 | static __declspec(thread) char err_str[buff_size] = {}; | 24 | static __declspec(thread) char err_str[buff_size] = {}; |
| 25 | 25 | ||
| 26 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), | 26 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), |
| 27 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | 27 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| 28 | err_str, buff_size, NULL); | 28 | err_str, buff_size, nullptr); |
| 29 | #else | 29 | #else |
| 30 | static __thread char err_str[buff_size] = {}; | 30 | static __thread char err_str[buff_size] = {}; |
| 31 | 31 | ||
diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp index b3556aaa8..7ffedc45a 100644 --- a/src/common/msg_handler.cpp +++ b/src/common/msg_handler.cpp | |||
| @@ -75,7 +75,7 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...) | |||
| 75 | Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); | 75 | Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); |
| 76 | va_end(args); | 76 | va_end(args); |
| 77 | 77 | ||
| 78 | ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); | 78 | LOG_INFO(Common, "%s: %s", caption.c_str(), buffer); |
| 79 | 79 | ||
| 80 | // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored | 80 | // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored |
| 81 | if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) | 81 | if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) |
diff --git a/src/common/msg_handler.h b/src/common/msg_handler.h index 7c5319ec3..9bfdf950e 100644 --- a/src/common/msg_handler.h +++ b/src/common/msg_handler.h | |||
| @@ -15,7 +15,7 @@ enum MSG_TYPE | |||
| 15 | CRITICAL | 15 | CRITICAL |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | typedef bool (*MsgAlertHandler)(const char* caption, const char* text, | 18 | typedef bool (*MsgAlertHandler)(const char* caption, const char* text, |
| 19 | bool yes_no, int Style); | 19 | bool yes_no, int Style); |
| 20 | typedef std::string (*StringTranslator)(const char* text); | 20 | typedef std::string (*StringTranslator)(const char* text); |
| 21 | 21 | ||
| @@ -31,29 +31,29 @@ void SetEnableAlert(bool enable); | |||
| 31 | 31 | ||
| 32 | #ifndef GEKKO | 32 | #ifndef GEKKO |
| 33 | #ifdef _WIN32 | 33 | #ifdef _WIN32 |
| 34 | #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) | 34 | #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) |
| 35 | #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) | 35 | #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) |
| 36 | #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) | 36 | #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) |
| 37 | #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) | 37 | #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) |
| 38 | #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) | 38 | #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) |
| 39 | // Use these macros (that do the same thing) if the message should be translated. | 39 | // Use these macros (that do the same thing) if the message should be translated. |
| 40 | #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) | 40 | #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) |
| 41 | #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) | 41 | #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) |
| 42 | #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) | 42 | #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) |
| 43 | #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) | 43 | #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) |
| 44 | #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) | 44 | #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) |
| 45 | #else | 45 | #else |
| 46 | #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) | 46 | #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) |
| 47 | #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) | 47 | #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) |
| 48 | #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) | 48 | #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) |
| 49 | #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) | 49 | #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) |
| 50 | #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) | 50 | #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) |
| 51 | // Use these macros (that do the same thing) if the message should be translated. | 51 | // Use these macros (that do the same thing) if the message should be translated. |
| 52 | #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) | 52 | #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) |
| 53 | #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) | 53 | #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) |
| 54 | #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) | 54 | #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) |
| 55 | #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) | 55 | #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) |
| 56 | #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) | 56 | #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) |
| 57 | #endif | 57 | #endif |
| 58 | #else | 58 | #else |
| 59 | // GEKKO | 59 | // GEKKO |
diff --git a/src/common/platform.h b/src/common/platform.h index d9f095433..53d98fe74 100644 --- a/src/common/platform.h +++ b/src/common/platform.h | |||
| @@ -77,7 +77,7 @@ | |||
| 77 | inline struct tm* localtime_r(const time_t *clock, struct tm *result) { | 77 | inline struct tm* localtime_r(const time_t *clock, struct tm *result) { |
| 78 | if (localtime_s(result, clock) == 0) | 78 | if (localtime_s(result, clock) == 0) |
| 79 | return result; | 79 | return result; |
| 80 | return NULL; | 80 | return nullptr; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | #else | 83 | #else |
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h new file mode 100644 index 000000000..1d3e59319 --- /dev/null +++ b/src/common/scope_exit.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace detail { | ||
| 8 | template <typename Func> | ||
| 9 | struct ScopeExitHelper { | ||
| 10 | explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {} | ||
| 11 | ~ScopeExitHelper() { func(); } | ||
| 12 | |||
| 13 | Func func; | ||
| 14 | }; | ||
| 15 | |||
| 16 | template <typename Func> | ||
| 17 | ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); } | ||
| 18 | } | ||
| 19 | |||
| 20 | /** | ||
| 21 | * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy | ||
| 22 | * for doing ad-hoc clean-up tasks in a function with multiple returns. | ||
| 23 | * | ||
| 24 | * Example usage: | ||
| 25 | * \code | ||
| 26 | * const int saved_val = g_foo; | ||
| 27 | * g_foo = 55; | ||
| 28 | * SCOPE_EXIT({ g_foo = saved_val; }); | ||
| 29 | * | ||
| 30 | * if (Bar()) { | ||
| 31 | * return 0; | ||
| 32 | * } else { | ||
| 33 | * return 20; | ||
| 34 | * } | ||
| 35 | * \endcode | ||
| 36 | */ | ||
| 37 | #define SCOPE_EXIT(body) auto scope_exit_helper_##__LINE__ = detail::ScopeExit([&]() body) | ||
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 61f0939c4..6d9612fb5 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -2,13 +2,14 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <boost/range/algorithm.hpp> |
| 6 | 6 | ||
| 7 | #include "common/common.h" | 7 | #include "common/common.h" |
| 8 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 9 | 9 | ||
| 10 | #ifdef _WIN32 | 10 | #ifdef _WIN32 |
| 11 | #include <Windows.h> | 11 | #include <Windows.h> |
| 12 | #include <codecvt> | ||
| 12 | #else | 13 | #else |
| 13 | #include <iconv.h> | 14 | #include <iconv.h> |
| 14 | #endif | 15 | #endif |
| @@ -17,20 +18,20 @@ namespace Common { | |||
| 17 | 18 | ||
| 18 | /// Make a string lowercase | 19 | /// Make a string lowercase |
| 19 | std::string ToLower(std::string str) { | 20 | std::string ToLower(std::string str) { |
| 20 | std::transform(str.begin(), str.end(), str.begin(), ::tolower); | 21 | boost::transform(str, str.begin(), ::tolower); |
| 21 | return str; | 22 | return str; |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | /// Make a string uppercase | 25 | /// Make a string uppercase |
| 25 | std::string ToUpper(std::string str) { | 26 | std::string ToUpper(std::string str) { |
| 26 | std::transform(str.begin(), str.end(), str.begin(), ::toupper); | 27 | boost::transform(str, str.begin(), ::toupper); |
| 27 | return str; | 28 | return str; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | // faster than sscanf | 31 | // faster than sscanf |
| 31 | bool AsciiToHex(const char* _szValue, u32& result) | 32 | bool AsciiToHex(const char* _szValue, u32& result) |
| 32 | { | 33 | { |
| 33 | char *endptr = NULL; | 34 | char *endptr = nullptr; |
| 34 | const u32 value = strtoul(_szValue, &endptr, 16); | 35 | const u32 value = strtoul(_szValue, &endptr, 16); |
| 35 | 36 | ||
| 36 | if (!endptr || *endptr) | 37 | if (!endptr || *endptr) |
| @@ -68,7 +69,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar | |||
| 68 | // will be present in the middle of a multibyte sequence. | 69 | // will be present in the middle of a multibyte sequence. |
| 69 | // | 70 | // |
| 70 | // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l. | 71 | // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l. |
| 71 | static locale_t c_locale = NULL; | 72 | static locale_t c_locale = nullptr; |
| 72 | if (!c_locale) | 73 | if (!c_locale) |
| 73 | c_locale = _create_locale(LC_ALL, ".1252"); | 74 | c_locale = _create_locale(LC_ALL, ".1252"); |
| 74 | writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); | 75 | writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); |
| @@ -91,7 +92,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar | |||
| 91 | std::string StringFromFormat(const char* format, ...) | 92 | std::string StringFromFormat(const char* format, ...) |
| 92 | { | 93 | { |
| 93 | va_list args; | 94 | va_list args; |
| 94 | char *buf = NULL; | 95 | char *buf = nullptr; |
| 95 | #ifdef _WIN32 | 96 | #ifdef _WIN32 |
| 96 | int required = 0; | 97 | int required = 0; |
| 97 | 98 | ||
| @@ -106,7 +107,7 @@ std::string StringFromFormat(const char* format, ...) | |||
| 106 | #else | 107 | #else |
| 107 | va_start(args, format); | 108 | va_start(args, format); |
| 108 | if (vasprintf(&buf, format, args) < 0) | 109 | if (vasprintf(&buf, format, args) < 0) |
| 109 | ERROR_LOG(COMMON, "Unable to allocate memory for string"); | 110 | LOG_ERROR(Common, "Unable to allocate memory for string"); |
| 110 | va_end(args); | 111 | va_end(args); |
| 111 | 112 | ||
| 112 | std::string temp = buf; | 113 | std::string temp = buf; |
| @@ -120,11 +121,11 @@ std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces) | |||
| 120 | { | 121 | { |
| 121 | std::ostringstream oss; | 122 | std::ostringstream oss; |
| 122 | oss << std::setfill('0') << std::hex; | 123 | oss << std::setfill('0') << std::hex; |
| 123 | 124 | ||
| 124 | for (int line = 0; size; ++data, --size) | 125 | for (int line = 0; size; ++data, --size) |
| 125 | { | 126 | { |
| 126 | oss << std::setw(2) << (int)*data; | 127 | oss << std::setw(2) << (int)*data; |
| 127 | 128 | ||
| 128 | if (line_len == ++line) | 129 | if (line_len == ++line) |
| 129 | { | 130 | { |
| 130 | oss << '\n'; | 131 | oss << '\n'; |
| @@ -161,13 +162,13 @@ std::string StripQuotes(const std::string& s) | |||
| 161 | 162 | ||
| 162 | bool TryParse(const std::string &str, u32 *const output) | 163 | bool TryParse(const std::string &str, u32 *const output) |
| 163 | { | 164 | { |
| 164 | char *endptr = NULL; | 165 | char *endptr = nullptr; |
| 165 | 166 | ||
| 166 | // Reset errno to a value other than ERANGE | 167 | // Reset errno to a value other than ERANGE |
| 167 | errno = 0; | 168 | errno = 0; |
| 168 | 169 | ||
| 169 | unsigned long value = strtoul(str.c_str(), &endptr, 0); | 170 | unsigned long value = strtoul(str.c_str(), &endptr, 0); |
| 170 | 171 | ||
| 171 | if (!endptr || *endptr) | 172 | if (!endptr || *endptr) |
| 172 | return false; | 173 | return false; |
| 173 | 174 | ||
| @@ -293,7 +294,7 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st | |||
| 293 | //#include <string> | 294 | //#include <string> |
| 294 | //#include <assert.h> | 295 | //#include <assert.h> |
| 295 | 296 | ||
| 296 | const char HEX2DEC[256] = | 297 | const char HEX2DEC[256] = |
| 297 | { | 298 | { |
| 298 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | 299 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
| 299 | /* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | 300 | /* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, |
| @@ -326,7 +327,7 @@ std::string UriDecode(const std::string & sSrc) | |||
| 326 | const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); | 327 | const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); |
| 327 | const size_t SRC_LEN = sSrc.length(); | 328 | const size_t SRC_LEN = sSrc.length(); |
| 328 | const unsigned char * const SRC_END = pSrc + SRC_LEN; | 329 | const unsigned char * const SRC_END = pSrc + SRC_LEN; |
| 329 | const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%' | 330 | const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%' |
| 330 | 331 | ||
| 331 | char * const pStart = new char[SRC_LEN]; | 332 | char * const pStart = new char[SRC_LEN]; |
| 332 | char * pEnd = pStart; | 333 | char * pEnd = pStart; |
| @@ -393,7 +394,7 @@ std::string UriEncode(const std::string & sSrc) | |||
| 393 | 394 | ||
| 394 | for (; pSrc < SRC_END; ++pSrc) | 395 | for (; pSrc < SRC_END; ++pSrc) |
| 395 | { | 396 | { |
| 396 | if (SAFE[*pSrc]) | 397 | if (SAFE[*pSrc]) |
| 397 | *pEnd++ = *pSrc; | 398 | *pEnd++ = *pSrc; |
| 398 | else | 399 | else |
| 399 | { | 400 | { |
| @@ -411,7 +412,19 @@ std::string UriEncode(const std::string & sSrc) | |||
| 411 | 412 | ||
| 412 | #ifdef _WIN32 | 413 | #ifdef _WIN32 |
| 413 | 414 | ||
| 414 | std::string UTF16ToUTF8(const std::wstring& input) | 415 | std::string UTF16ToUTF8(const std::u16string& input) |
| 416 | { | ||
| 417 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; | ||
| 418 | return convert.to_bytes(input); | ||
| 419 | } | ||
| 420 | |||
| 421 | std::u16string UTF8ToUTF16(const std::string& input) | ||
| 422 | { | ||
| 423 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; | ||
| 424 | return convert.from_bytes(input); | ||
| 425 | } | ||
| 426 | |||
| 427 | static std::string UTF16ToUTF8(const std::wstring& input) | ||
| 415 | { | 428 | { |
| 416 | auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr); | 429 | auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr); |
| 417 | 430 | ||
| @@ -424,7 +437,7 @@ std::string UTF16ToUTF8(const std::wstring& input) | |||
| 424 | return output; | 437 | return output; |
| 425 | } | 438 | } |
| 426 | 439 | ||
| 427 | std::wstring CPToUTF16(u32 code_page, const std::string& input) | 440 | static std::wstring CPToUTF16(u32 code_page, const std::string& input) |
| 428 | { | 441 | { |
| 429 | auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0); | 442 | auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0); |
| 430 | 443 | ||
| @@ -437,7 +450,7 @@ std::wstring CPToUTF16(u32 code_page, const std::string& input) | |||
| 437 | return output; | 450 | return output; |
| 438 | } | 451 | } |
| 439 | 452 | ||
| 440 | std::wstring UTF8ToUTF16(const std::string& input) | 453 | std::wstring UTF8ToUTF16W(const std::string &input) |
| 441 | { | 454 | { |
| 442 | return CPToUTF16(CP_UTF8, input); | 455 | return CPToUTF16(CP_UTF8, input); |
| 443 | } | 456 | } |
| @@ -455,61 +468,123 @@ std::string CP1252ToUTF8(const std::string& input) | |||
| 455 | #else | 468 | #else |
| 456 | 469 | ||
| 457 | template <typename T> | 470 | template <typename T> |
| 458 | std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) | 471 | static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) |
| 459 | { | 472 | { |
| 460 | std::string result; | 473 | std::string result; |
| 461 | 474 | ||
| 462 | iconv_t const conv_desc = iconv_open("UTF-8", fromcode); | 475 | iconv_t const conv_desc = iconv_open("UTF-8", fromcode); |
| 463 | if ((iconv_t)-1 == conv_desc) | 476 | if ((iconv_t)(-1) == conv_desc) |
| 464 | { | 477 | { |
| 465 | ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); | 478 | LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); |
| 479 | iconv_close(conv_desc); | ||
| 480 | return {}; | ||
| 466 | } | 481 | } |
| 467 | else | ||
| 468 | { | ||
| 469 | size_t const in_bytes = sizeof(T) * input.size(); | ||
| 470 | size_t const out_buffer_size = 4 * in_bytes; | ||
| 471 | 482 | ||
| 472 | std::string out_buffer; | 483 | const size_t in_bytes = sizeof(T) * input.size(); |
| 473 | out_buffer.resize(out_buffer_size); | 484 | // Multiply by 4, which is the max number of bytes to encode a codepoint |
| 485 | const size_t out_buffer_size = 4 * in_bytes; | ||
| 474 | 486 | ||
| 475 | auto src_buffer = &input[0]; | 487 | std::string out_buffer; |
| 476 | size_t src_bytes = in_bytes; | 488 | out_buffer.resize(out_buffer_size); |
| 477 | auto dst_buffer = &out_buffer[0]; | ||
| 478 | size_t dst_bytes = out_buffer.size(); | ||
| 479 | 489 | ||
| 480 | while (src_bytes != 0) | 490 | auto src_buffer = &input[0]; |
| 481 | { | 491 | size_t src_bytes = in_bytes; |
| 482 | size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes, | 492 | auto dst_buffer = &out_buffer[0]; |
| 483 | &dst_buffer, &dst_bytes); | 493 | size_t dst_bytes = out_buffer.size(); |
| 484 | 494 | ||
| 485 | if ((size_t)-1 == iconv_result) | 495 | while (0 != src_bytes) |
| 496 | { | ||
| 497 | size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes, | ||
| 498 | &dst_buffer, &dst_bytes); | ||
| 499 | |||
| 500 | if (static_cast<size_t>(-1) == iconv_result) | ||
| 501 | { | ||
| 502 | if (EILSEQ == errno || EINVAL == errno) | ||
| 486 | { | 503 | { |
| 487 | if (EILSEQ == errno || EINVAL == errno) | 504 | // Try to skip the bad character |
| 488 | { | 505 | if (0 != src_bytes) |
| 489 | // Try to skip the bad character | ||
| 490 | if (src_bytes != 0) | ||
| 491 | { | ||
| 492 | --src_bytes; | ||
| 493 | ++src_buffer; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | else | ||
| 497 | { | 506 | { |
| 498 | ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); | 507 | --src_bytes; |
| 499 | break; | 508 | ++src_buffer; |
| 500 | } | 509 | } |
| 501 | } | 510 | } |
| 511 | else | ||
| 512 | { | ||
| 513 | LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno)); | ||
| 514 | break; | ||
| 515 | } | ||
| 502 | } | 516 | } |
| 517 | } | ||
| 518 | |||
| 519 | out_buffer.resize(out_buffer_size - dst_bytes); | ||
| 520 | out_buffer.swap(result); | ||
| 521 | |||
| 522 | iconv_close(conv_desc); | ||
| 523 | |||
| 524 | return result; | ||
| 525 | } | ||
| 526 | |||
| 527 | std::u16string UTF8ToUTF16(const std::string& input) | ||
| 528 | { | ||
| 529 | std::u16string result; | ||
| 503 | 530 | ||
| 504 | out_buffer.resize(out_buffer_size - dst_bytes); | 531 | iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); |
| 505 | out_buffer.swap(result); | 532 | if ((iconv_t)(-1) == conv_desc) |
| 506 | 533 | { | |
| 534 | LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno)); | ||
| 507 | iconv_close(conv_desc); | 535 | iconv_close(conv_desc); |
| 536 | return {}; | ||
| 508 | } | 537 | } |
| 509 | 538 | ||
| 539 | const size_t in_bytes = sizeof(char) * input.size(); | ||
| 540 | // Multiply by 4, which is the max number of bytes to encode a codepoint | ||
| 541 | const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; | ||
| 542 | |||
| 543 | std::u16string out_buffer; | ||
| 544 | out_buffer.resize(out_buffer_size); | ||
| 545 | |||
| 546 | char* src_buffer = const_cast<char*>(&input[0]); | ||
| 547 | size_t src_bytes = in_bytes; | ||
| 548 | char* dst_buffer = (char*)(&out_buffer[0]); | ||
| 549 | size_t dst_bytes = out_buffer.size(); | ||
| 550 | |||
| 551 | while (0 != src_bytes) | ||
| 552 | { | ||
| 553 | size_t const iconv_result = iconv(conv_desc, &src_buffer, &src_bytes, | ||
| 554 | &dst_buffer, &dst_bytes); | ||
| 555 | |||
| 556 | if (static_cast<size_t>(-1) == iconv_result) | ||
| 557 | { | ||
| 558 | if (EILSEQ == errno || EINVAL == errno) | ||
| 559 | { | ||
| 560 | // Try to skip the bad character | ||
| 561 | if (0 != src_bytes) | ||
| 562 | { | ||
| 563 | --src_bytes; | ||
| 564 | ++src_buffer; | ||
| 565 | } | ||
| 566 | } | ||
| 567 | else | ||
| 568 | { | ||
| 569 | LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno)); | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | out_buffer.resize(out_buffer_size - dst_bytes); | ||
| 576 | out_buffer.swap(result); | ||
| 577 | |||
| 578 | iconv_close(conv_desc); | ||
| 579 | |||
| 510 | return result; | 580 | return result; |
| 511 | } | 581 | } |
| 512 | 582 | ||
| 583 | std::string UTF16ToUTF8(const std::u16string& input) | ||
| 584 | { | ||
| 585 | return CodeToUTF8("UTF-16LE", input); | ||
| 586 | } | ||
| 587 | |||
| 513 | std::string CP1252ToUTF8(const std::string& input) | 588 | std::string CP1252ToUTF8(const std::string& input) |
| 514 | { | 589 | { |
| 515 | //return CodeToUTF8("CP1252//TRANSLIT", input); | 590 | //return CodeToUTF8("CP1252//TRANSLIT", input); |
| @@ -523,19 +598,6 @@ std::string SHIFTJISToUTF8(const std::string& input) | |||
| 523 | return CodeToUTF8("SJIS", input); | 598 | return CodeToUTF8("SJIS", input); |
| 524 | } | 599 | } |
| 525 | 600 | ||
| 526 | std::string UTF16ToUTF8(const std::wstring& input) | ||
| 527 | { | ||
| 528 | std::string result = | ||
| 529 | // CodeToUTF8("UCS-2", input); | ||
| 530 | // CodeToUTF8("UCS-2LE", input); | ||
| 531 | // CodeToUTF8("UTF-16", input); | ||
| 532 | CodeToUTF8("UTF-16LE", input); | ||
| 533 | |||
| 534 | // TODO: why is this needed? | ||
| 535 | result.erase(std::remove(result.begin(), result.end(), 0x00), result.end()); | ||
| 536 | return result; | ||
| 537 | } | ||
| 538 | |||
| 539 | #endif | 601 | #endif |
| 540 | 602 | ||
| 541 | } | 603 | } |
diff --git a/src/common/string_util.h b/src/common/string_util.h index a41ccc691..7d75691b1 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -63,7 +63,7 @@ template <typename N> | |||
| 63 | static bool TryParse(const std::string &str, N *const output) | 63 | static bool TryParse(const std::string &str, N *const output) |
| 64 | { | 64 | { |
| 65 | std::istringstream iss(str); | 65 | std::istringstream iss(str); |
| 66 | 66 | ||
| 67 | N tmp = 0; | 67 | N tmp = 0; |
| 68 | if (iss >> tmp) | 68 | if (iss >> tmp) |
| 69 | { | 69 | { |
| @@ -89,20 +89,22 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st | |||
| 89 | std::string UriDecode(const std::string & sSrc); | 89 | std::string UriDecode(const std::string & sSrc); |
| 90 | std::string UriEncode(const std::string & sSrc); | 90 | std::string UriEncode(const std::string & sSrc); |
| 91 | 91 | ||
| 92 | std::string UTF16ToUTF8(const std::u16string& input); | ||
| 93 | std::u16string UTF8ToUTF16(const std::string& input); | ||
| 94 | |||
| 92 | std::string CP1252ToUTF8(const std::string& str); | 95 | std::string CP1252ToUTF8(const std::string& str); |
| 93 | std::string SHIFTJISToUTF8(const std::string& str); | 96 | std::string SHIFTJISToUTF8(const std::string& str); |
| 94 | std::string UTF16ToUTF8(const std::wstring& str); | ||
| 95 | 97 | ||
| 96 | #ifdef _WIN32 | 98 | #ifdef _WIN32 |
| 97 | 99 | ||
| 98 | std::wstring UTF8ToUTF16(const std::string& str); | 100 | std::wstring UTF8ToUTF16W(const std::string& str); |
| 99 | 101 | ||
| 100 | #ifdef _UNICODE | 102 | #ifdef _UNICODE |
| 101 | inline std::string TStrToUTF8(const std::wstring& str) | 103 | inline std::string TStrToUTF8(const std::wstring& str) |
| 102 | { return UTF16ToUTF8(str); } | 104 | { return UTF16ToUTF8(str); } |
| 103 | 105 | ||
| 104 | inline std::wstring UTF8ToTStr(const std::string& str) | 106 | inline std::wstring UTF8ToTStr(const std::string& str) |
| 105 | { return UTF8ToUTF16(str); } | 107 | { return UTF8ToUTF16W(str); } |
| 106 | #else | 108 | #else |
| 107 | inline std::string TStrToUTF8(const std::string& str) | 109 | inline std::string TStrToUTF8(const std::string& str) |
| 108 | { return str; } | 110 | { return str; } |
| @@ -113,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str) | |||
| 113 | 115 | ||
| 114 | #endif | 116 | #endif |
| 115 | 117 | ||
| 118 | /** | ||
| 119 | * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string | ||
| 120 | * `other` for equality. | ||
| 121 | */ | ||
| 122 | template <typename InIt> | ||
| 123 | bool ComparePartialString(InIt begin, InIt end, const char* other) { | ||
| 124 | for (; begin != end && *other != '\0'; ++begin, ++other) { | ||
| 125 | if (*begin != *other) { | ||
| 126 | return false; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | // Only return true if both strings finished at the same point | ||
| 130 | return (begin == end) == (*other == '\0'); | ||
| 131 | } | ||
| 132 | |||
| 116 | } | 133 | } |
diff --git a/src/common/symbols.cpp b/src/common/symbols.cpp index d61f4c0c6..63ad6218b 100644 --- a/src/common/symbols.cpp +++ b/src/common/symbols.cpp | |||
| @@ -31,7 +31,7 @@ namespace Symbols | |||
| 31 | { | 31 | { |
| 32 | TSymbolsMap::iterator foundSymbolItr; | 32 | TSymbolsMap::iterator foundSymbolItr; |
| 33 | TSymbol symbol; | 33 | TSymbol symbol; |
| 34 | 34 | ||
| 35 | foundSymbolItr = g_symbols.find(_address); | 35 | foundSymbolItr = g_symbols.find(_address); |
| 36 | if (foundSymbolItr != g_symbols.end()) | 36 | if (foundSymbolItr != g_symbols.end()) |
| 37 | { | 37 | { |
| @@ -44,7 +44,7 @@ namespace Symbols | |||
| 44 | { | 44 | { |
| 45 | return GetSymbol(_address).name; | 45 | return GetSymbol(_address).name; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | void Remove(u32 _address) | 48 | void Remove(u32 _address) |
| 49 | { | 49 | { |
| 50 | g_symbols.erase(_address); | 50 | g_symbols.erase(_address); |
diff --git a/src/common/symbols.h b/src/common/symbols.h index b13a8001a..4560f5240 100644 --- a/src/common/symbols.h +++ b/src/common/symbols.h | |||
| @@ -33,5 +33,5 @@ namespace Symbols | |||
| 33 | const std::string GetName(u32 _address); | 33 | const std::string GetName(u32 _address); |
| 34 | void Remove(u32 _address); | 34 | void Remove(u32 _address); |
| 35 | void Clear(); | 35 | void Clear(); |
| 36 | }; | 36 | } |
| 37 | 37 | ||
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 60d8ed075..dc153ba71 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -25,7 +25,7 @@ int CurrentThreadId() | |||
| 25 | return 0; | 25 | return 0; |
| 26 | #endif | 26 | #endif |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | #ifdef _WIN32 | 29 | #ifdef _WIN32 |
| 30 | 30 | ||
| 31 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) | 31 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) |
| @@ -52,7 +52,7 @@ void SwitchCurrentThread() | |||
| 52 | // Sets the debugger-visible name of the current thread. | 52 | // Sets the debugger-visible name of the current thread. |
| 53 | // Uses undocumented (actually, it is now documented) trick. | 53 | // Uses undocumented (actually, it is now documented) trick. |
| 54 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp | 54 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp |
| 55 | 55 | ||
| 56 | // This is implemented much nicer in upcoming msvc++, see: | 56 | // This is implemented much nicer in upcoming msvc++, see: |
| 57 | // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx | 57 | // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx |
| 58 | void SetCurrentThreadName(const char* szThreadName) | 58 | void SetCurrentThreadName(const char* szThreadName) |
| @@ -81,7 +81,7 @@ void SetCurrentThreadName(const char* szThreadName) | |||
| 81 | __except(EXCEPTION_CONTINUE_EXECUTION) | 81 | __except(EXCEPTION_CONTINUE_EXECUTION) |
| 82 | {} | 82 | {} |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | #else // !WIN32, so must be POSIX threads | 85 | #else // !WIN32, so must be POSIX threads |
| 86 | 86 | ||
| 87 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) | 87 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) |
diff --git a/src/common/thread.h b/src/common/thread.h index f7ace21b4..8c36d3f07 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | //for gettimeofday and struct time(spec|val) | 21 | //for gettimeofday and struct time(spec|val) |
| 22 | #include <time.h> | 22 | #include <time.h> |
| 23 | #include <sys/time.h> | 23 | #include <sys/time.h> |
| 24 | #include <unistd.h> | ||
| 24 | #endif | 25 | #endif |
| 25 | 26 | ||
| 26 | namespace Common | 27 | namespace Common |
| @@ -30,13 +31,13 @@ int CurrentThreadId(); | |||
| 30 | 31 | ||
| 31 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); | 32 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); |
| 32 | void SetCurrentThreadAffinity(u32 mask); | 33 | void SetCurrentThreadAffinity(u32 mask); |
| 33 | 34 | ||
| 34 | class Event | 35 | class Event |
| 35 | { | 36 | { |
| 36 | public: | 37 | public: |
| 37 | Event() | 38 | Event() |
| 38 | : is_set(false) | 39 | : is_set(false) |
| 39 | {}; | 40 | {} |
| 40 | 41 | ||
| 41 | void Set() | 42 | void Set() |
| 42 | { | 43 | { |
| @@ -135,7 +136,7 @@ private: | |||
| 135 | const size_t m_count; | 136 | const size_t m_count; |
| 136 | volatile size_t m_waiting; | 137 | volatile size_t m_waiting; |
| 137 | }; | 138 | }; |
| 138 | 139 | ||
| 139 | void SleepCurrentThread(int ms); | 140 | void SleepCurrentThread(int ms); |
| 140 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms | 141 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms |
| 141 | 142 | ||
| @@ -146,7 +147,7 @@ inline void YieldCPU() | |||
| 146 | { | 147 | { |
| 147 | std::this_thread::yield(); | 148 | std::this_thread::yield(); |
| 148 | } | 149 | } |
| 149 | 150 | ||
| 150 | void SetCurrentThreadName(const char *name); | 151 | void SetCurrentThreadName(const char *name); |
| 151 | 152 | ||
| 152 | } // namespace Common | 153 | } // namespace Common |
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 4a89572f6..7e3b620c7 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -12,7 +12,7 @@ template<class IdType> | |||
| 12 | struct ThreadQueueList { | 12 | struct ThreadQueueList { |
| 13 | // Number of queues (number of priority levels starting at 0.) | 13 | // Number of queues (number of priority levels starting at 0.) |
| 14 | static const int NUM_QUEUES = 128; | 14 | static const int NUM_QUEUES = 128; |
| 15 | 15 | ||
| 16 | // Initial number of threads a single queue can handle. | 16 | // Initial number of threads a single queue can handle. |
| 17 | static const int INITIAL_CAPACITY = 32; | 17 | static const int INITIAL_CAPACITY = 32; |
| 18 | 18 | ||
| @@ -37,7 +37,7 @@ struct ThreadQueueList { | |||
| 37 | ~ThreadQueueList() { | 37 | ~ThreadQueueList() { |
| 38 | for (int i = 0; i < NUM_QUEUES; ++i) | 38 | for (int i = 0; i < NUM_QUEUES; ++i) |
| 39 | { | 39 | { |
| 40 | if (queues[i].data != NULL) | 40 | if (queues[i].data != nullptr) |
| 41 | free(queues[i].data); | 41 | free(queues[i].data); |
| 42 | } | 42 | } |
| 43 | } | 43 | } |
| @@ -46,7 +46,7 @@ struct ThreadQueueList { | |||
| 46 | int contains(const IdType uid) { | 46 | int contains(const IdType uid) { |
| 47 | for (int i = 0; i < NUM_QUEUES; ++i) | 47 | for (int i = 0; i < NUM_QUEUES; ++i) |
| 48 | { | 48 | { |
| 49 | if (queues[i].data == NULL) | 49 | if (queues[i].data == nullptr) |
| 50 | continue; | 50 | continue; |
| 51 | 51 | ||
| 52 | Queue *cur = &queues[i]; | 52 | Queue *cur = &queues[i]; |
| @@ -133,7 +133,7 @@ struct ThreadQueueList { | |||
| 133 | inline void clear() { | 133 | inline void clear() { |
| 134 | for (int i = 0; i < NUM_QUEUES; ++i) | 134 | for (int i = 0; i < NUM_QUEUES; ++i) |
| 135 | { | 135 | { |
| 136 | if (queues[i].data != NULL) | 136 | if (queues[i].data != nullptr) |
| 137 | free(queues[i].data); | 137 | free(queues[i].data); |
| 138 | } | 138 | } |
| 139 | memset(queues, 0, sizeof(queues)); | 139 | memset(queues, 0, sizeof(queues)); |
| @@ -147,7 +147,7 @@ struct ThreadQueueList { | |||
| 147 | 147 | ||
| 148 | inline void prepare(u32 priority) { | 148 | inline void prepare(u32 priority) { |
| 149 | Queue *cur = &queues[priority]; | 149 | Queue *cur = &queues[priority]; |
| 150 | if (cur->next == NULL) | 150 | if (cur->next == nullptr) |
| 151 | link(priority, INITIAL_CAPACITY); | 151 | link(priority, INITIAL_CAPACITY); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| @@ -176,7 +176,7 @@ private: | |||
| 176 | 176 | ||
| 177 | for (int i = (int) priority - 1; i >= 0; --i) | 177 | for (int i = (int) priority - 1; i >= 0; --i) |
| 178 | { | 178 | { |
| 179 | if (queues[i].next != NULL) | 179 | if (queues[i].next != nullptr) |
| 180 | { | 180 | { |
| 181 | cur->next = queues[i].next; | 181 | cur->next = queues[i].next; |
| 182 | queues[i].next = cur; | 182 | queues[i].next = cur; |
| @@ -193,7 +193,7 @@ private: | |||
| 193 | int size = cur->end - cur->first; | 193 | int size = cur->end - cur->first; |
| 194 | if (size >= cur->capacity - 2) { | 194 | if (size >= cur->capacity - 2) { |
| 195 | IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); | 195 | IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); |
| 196 | if (new_data != NULL) { | 196 | if (new_data != nullptr) { |
| 197 | cur->capacity *= 2; | 197 | cur->capacity *= 2; |
| 198 | cur->data = new_data; | 198 | cur->data = new_data; |
| 199 | } | 199 | } |
diff --git a/src/common/timer.cpp b/src/common/timer.cpp index ded4a344e..4a797f751 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp | |||
| @@ -25,7 +25,7 @@ u32 Timer::GetTimeMs() | |||
| 25 | return timeGetTime(); | 25 | return timeGetTime(); |
| 26 | #else | 26 | #else |
| 27 | struct timeval t; | 27 | struct timeval t; |
| 28 | (void)gettimeofday(&t, NULL); | 28 | (void)gettimeofday(&t, nullptr); |
| 29 | return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); | 29 | return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); |
| 30 | #endif | 30 | #endif |
| 31 | } | 31 | } |
| @@ -183,7 +183,7 @@ std::string Timer::GetTimeFormatted() | |||
| 183 | return StringFromFormat("%s:%03i", tmp, tp.millitm); | 183 | return StringFromFormat("%s:%03i", tmp, tp.millitm); |
| 184 | #else | 184 | #else |
| 185 | struct timeval t; | 185 | struct timeval t; |
| 186 | (void)gettimeofday(&t, NULL); | 186 | (void)gettimeofday(&t, nullptr); |
| 187 | return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000)); | 187 | return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000)); |
| 188 | #endif | 188 | #endif |
| 189 | } | 189 | } |
| @@ -197,7 +197,7 @@ double Timer::GetDoubleTime() | |||
| 197 | (void)::ftime(&tp); | 197 | (void)::ftime(&tp); |
| 198 | #else | 198 | #else |
| 199 | struct timeval t; | 199 | struct timeval t; |
| 200 | (void)gettimeofday(&t, NULL); | 200 | (void)gettimeofday(&t, nullptr); |
| 201 | #endif | 201 | #endif |
| 202 | // Get continuous timestamp | 202 | // Get continuous timestamp |
| 203 | u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); | 203 | u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); |
diff --git a/src/common/utf8.cpp b/src/common/utf8.cpp index be4ebc855..66a2f6339 100644 --- a/src/common/utf8.cpp +++ b/src/common/utf8.cpp | |||
| @@ -281,28 +281,28 @@ int u8_read_escape_sequence(const char *str, u32 *dest) | |||
| 281 | do { | 281 | do { |
| 282 | digs[dno++] = str[i++]; | 282 | digs[dno++] = str[i++]; |
| 283 | } while (octal_digit(str[i]) && dno < 3); | 283 | } while (octal_digit(str[i]) && dno < 3); |
| 284 | ch = strtol(digs, NULL, 8); | 284 | ch = strtol(digs, nullptr, 8); |
| 285 | } | 285 | } |
| 286 | else if (str[0] == 'x') { | 286 | else if (str[0] == 'x') { |
| 287 | while (hex_digit(str[i]) && dno < 2) { | 287 | while (hex_digit(str[i]) && dno < 2) { |
| 288 | digs[dno++] = str[i++]; | 288 | digs[dno++] = str[i++]; |
| 289 | } | 289 | } |
| 290 | if (dno > 0) | 290 | if (dno > 0) |
| 291 | ch = strtol(digs, NULL, 16); | 291 | ch = strtol(digs, nullptr, 16); |
| 292 | } | 292 | } |
| 293 | else if (str[0] == 'u') { | 293 | else if (str[0] == 'u') { |
| 294 | while (hex_digit(str[i]) && dno < 4) { | 294 | while (hex_digit(str[i]) && dno < 4) { |
| 295 | digs[dno++] = str[i++]; | 295 | digs[dno++] = str[i++]; |
| 296 | } | 296 | } |
| 297 | if (dno > 0) | 297 | if (dno > 0) |
| 298 | ch = strtol(digs, NULL, 16); | 298 | ch = strtol(digs, nullptr, 16); |
| 299 | } | 299 | } |
| 300 | else if (str[0] == 'U') { | 300 | else if (str[0] == 'U') { |
| 301 | while (hex_digit(str[i]) && dno < 8) { | 301 | while (hex_digit(str[i]) && dno < 8) { |
| 302 | digs[dno++] = str[i++]; | 302 | digs[dno++] = str[i++]; |
| 303 | } | 303 | } |
| 304 | if (dno > 0) | 304 | if (dno > 0) |
| 305 | ch = strtol(digs, NULL, 16); | 305 | ch = strtol(digs, nullptr, 16); |
| 306 | } | 306 | } |
| 307 | *dest = ch; | 307 | *dest = ch; |
| 308 | 308 | ||
| @@ -353,7 +353,7 @@ const char *u8_strchr(const char *s, u32 ch, int *charn) | |||
| 353 | lasti = i; | 353 | lasti = i; |
| 354 | (*charn)++; | 354 | (*charn)++; |
| 355 | } | 355 | } |
| 356 | return NULL; | 356 | return nullptr; |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) | 359 | const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) |
| @@ -378,7 +378,7 @@ const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) | |||
| 378 | lasti = i; | 378 | lasti = i; |
| 379 | (*charn)++; | 379 | (*charn)++; |
| 380 | } | 380 | } |
| 381 | return NULL; | 381 | return nullptr; |
| 382 | } | 382 | } |
| 383 | 383 | ||
| 384 | int u8_is_locale_utf8(const char *locale) | 384 | int u8_is_locale_utf8(const char *locale) |
| @@ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) { | |||
| 419 | 419 | ||
| 420 | std::string ConvertWStringToUTF8(const wchar_t *wstr) { | 420 | std::string ConvertWStringToUTF8(const wchar_t *wstr) { |
| 421 | int len = (int)wcslen(wstr); | 421 | int len = (int)wcslen(wstr); |
| 422 | int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, NULL, NULL); | 422 | int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, nullptr, nullptr); |
| 423 | std::string s; | 423 | std::string s; |
| 424 | s.resize(size); | 424 | s.resize(size); |
| 425 | if (size > 0) { | 425 | if (size > 0) { |
| 426 | WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, NULL, NULL); | 426 | WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, nullptr, nullptr); |
| 427 | } | 427 | } |
| 428 | return s; | 428 | return s; |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | std::string ConvertWStringToUTF8(const std::wstring &wstr) { | 431 | std::string ConvertWStringToUTF8(const std::wstring &wstr) { |
| 432 | int len = (int)wstr.size(); | 432 | int len = (int)wstr.size(); |
| 433 | int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, NULL, NULL); | 433 | int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, nullptr, nullptr); |
| 434 | std::string s; | 434 | std::string s; |
| 435 | s.resize(size); | 435 | s.resize(size); |
| 436 | if (size > 0) { | 436 | if (size > 0) { |
| 437 | WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, NULL, NULL); | 437 | WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, nullptr, nullptr); |
| 438 | } | 438 | } |
| 439 | return s; | 439 | return s; |
| 440 | } | 440 | } |
| 441 | 441 | ||
| 442 | void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { | 442 | void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { |
| 443 | int len = (int)source.size(); | 443 | int len = (int)source.size(); |
| 444 | int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0); | 444 | int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0); |
| 445 | MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size)); | 445 | MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size)); |
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | std::wstring ConvertUTF8ToWString(const std::string &source) { | 448 | std::wstring ConvertUTF8ToWString(const std::string &source) { |
| 449 | int len = (int)source.size(); | 449 | int len = (int)source.size(); |
| 450 | int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0); | 450 | int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0); |
| 451 | std::wstring str; | 451 | std::wstring str; |
| 452 | str.resize(size); | 452 | str.resize(size); |
| 453 | if (size > 0) { | 453 | if (size > 0) { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f67481359..f71232c1a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -18,29 +18,42 @@ set(SRCS | |||
| 18 | arm/skyeye_common/vfp/vfpinstr.cpp | 18 | arm/skyeye_common/vfp/vfpinstr.cpp |
| 19 | arm/skyeye_common/vfp/vfpsingle.cpp | 19 | arm/skyeye_common/vfp/vfpsingle.cpp |
| 20 | file_sys/archive_romfs.cpp | 20 | file_sys/archive_romfs.cpp |
| 21 | file_sys/archive_savedata.cpp | ||
| 21 | file_sys/archive_sdmc.cpp | 22 | file_sys/archive_sdmc.cpp |
| 23 | file_sys/disk_archive.cpp | ||
| 22 | file_sys/file_romfs.cpp | 24 | file_sys/file_romfs.cpp |
| 23 | file_sys/file_sdmc.cpp | ||
| 24 | file_sys/directory_romfs.cpp | 25 | file_sys/directory_romfs.cpp |
| 25 | file_sys/directory_sdmc.cpp | ||
| 26 | hle/kernel/address_arbiter.cpp | 26 | hle/kernel/address_arbiter.cpp |
| 27 | hle/kernel/archive.cpp | ||
| 28 | hle/kernel/event.cpp | 27 | hle/kernel/event.cpp |
| 29 | hle/kernel/kernel.cpp | 28 | hle/kernel/kernel.cpp |
| 30 | hle/kernel/mutex.cpp | 29 | hle/kernel/mutex.cpp |
| 30 | hle/kernel/semaphore.cpp | ||
| 31 | hle/kernel/shared_memory.cpp | 31 | hle/kernel/shared_memory.cpp |
| 32 | hle/kernel/thread.cpp | 32 | hle/kernel/thread.cpp |
| 33 | hle/service/ac_u.cpp | 33 | hle/service/ac_u.cpp |
| 34 | hle/service/am_app.cpp | ||
| 35 | hle/service/am_net.cpp | ||
| 34 | hle/service/apt_u.cpp | 36 | hle/service/apt_u.cpp |
| 37 | hle/service/boss_u.cpp | ||
| 38 | hle/service/cecd_u.cpp | ||
| 39 | hle/service/cfg_i.cpp | ||
| 35 | hle/service/cfg_u.cpp | 40 | hle/service/cfg_u.cpp |
| 41 | hle/service/csnd_snd.cpp | ||
| 36 | hle/service/dsp_dsp.cpp | 42 | hle/service/dsp_dsp.cpp |
| 37 | hle/service/err_f.cpp | 43 | hle/service/err_f.cpp |
| 38 | hle/service/fs_user.cpp | 44 | hle/service/fs/archive.cpp |
| 45 | hle/service/fs/fs_user.cpp | ||
| 46 | hle/service/frd_u.cpp | ||
| 39 | hle/service/gsp_gpu.cpp | 47 | hle/service/gsp_gpu.cpp |
| 40 | hle/service/hid_user.cpp | 48 | hle/service/hid_user.cpp |
| 49 | hle/service/ir_rst.cpp | ||
| 50 | hle/service/ir_u.cpp | ||
| 51 | hle/service/ldr_ro.cpp | ||
| 41 | hle/service/mic_u.cpp | 52 | hle/service/mic_u.cpp |
| 53 | hle/service/nim_aoc.cpp | ||
| 42 | hle/service/ndm_u.cpp | 54 | hle/service/ndm_u.cpp |
| 43 | hle/service/nwm_uds.cpp | 55 | hle/service/nwm_uds.cpp |
| 56 | hle/service/pm_app.cpp | ||
| 44 | hle/service/ptm_u.cpp | 57 | hle/service/ptm_u.cpp |
| 45 | hle/service/service.cpp | 58 | hle/service/service.cpp |
| 46 | hle/service/soc_u.cpp | 59 | hle/service/soc_u.cpp |
| @@ -51,10 +64,10 @@ set(SRCS | |||
| 51 | hle/svc.cpp | 64 | hle/svc.cpp |
| 52 | hw/gpu.cpp | 65 | hw/gpu.cpp |
| 53 | hw/hw.cpp | 66 | hw/hw.cpp |
| 54 | hw/ndma.cpp | ||
| 55 | loader/elf.cpp | 67 | loader/elf.cpp |
| 56 | loader/loader.cpp | 68 | loader/loader.cpp |
| 57 | loader/ncch.cpp | 69 | loader/ncch.cpp |
| 70 | loader/3dsx.cpp | ||
| 58 | core.cpp | 71 | core.cpp |
| 59 | core_timing.cpp | 72 | core_timing.cpp |
| 60 | mem_map.cpp | 73 | mem_map.cpp |
| @@ -84,48 +97,63 @@ set(HEADERS | |||
| 84 | arm/skyeye_common/vfp/vfp.h | 97 | arm/skyeye_common/vfp/vfp.h |
| 85 | arm/skyeye_common/vfp/vfp_helper.h | 98 | arm/skyeye_common/vfp/vfp_helper.h |
| 86 | arm/arm_interface.h | 99 | arm/arm_interface.h |
| 87 | file_sys/archive.h | 100 | file_sys/archive_backend.h |
| 88 | file_sys/archive_romfs.h | 101 | file_sys/archive_romfs.h |
| 102 | file_sys/archive_savedata.h | ||
| 89 | file_sys/archive_sdmc.h | 103 | file_sys/archive_sdmc.h |
| 90 | file_sys/file.h | 104 | file_sys/disk_archive.h |
| 105 | file_sys/file_backend.h | ||
| 91 | file_sys/file_romfs.h | 106 | file_sys/file_romfs.h |
| 92 | file_sys/file_sdmc.h | 107 | file_sys/directory_backend.h |
| 93 | file_sys/directory.h | ||
| 94 | file_sys/directory_romfs.h | 108 | file_sys/directory_romfs.h |
| 95 | file_sys/directory_sdmc.h | ||
| 96 | hle/kernel/address_arbiter.h | 109 | hle/kernel/address_arbiter.h |
| 97 | hle/kernel/archive.h | ||
| 98 | hle/kernel/event.h | 110 | hle/kernel/event.h |
| 99 | hle/kernel/kernel.h | 111 | hle/kernel/kernel.h |
| 100 | hle/kernel/mutex.h | 112 | hle/kernel/mutex.h |
| 113 | hle/kernel/semaphore.h | ||
| 114 | hle/kernel/session.h | ||
| 101 | hle/kernel/shared_memory.h | 115 | hle/kernel/shared_memory.h |
| 102 | hle/kernel/thread.h | 116 | hle/kernel/thread.h |
| 103 | hle/service/ac_u.h | 117 | hle/service/ac_u.h |
| 118 | hle/service/am_app.h | ||
| 119 | hle/service/am_net.h | ||
| 104 | hle/service/apt_u.h | 120 | hle/service/apt_u.h |
| 121 | hle/service/boss_u.h | ||
| 122 | hle/service/cecd_u.h | ||
| 123 | hle/service/cfg_i.h | ||
| 105 | hle/service/cfg_u.h | 124 | hle/service/cfg_u.h |
| 125 | hle/service/csnd_snd.h | ||
| 106 | hle/service/dsp_dsp.h | 126 | hle/service/dsp_dsp.h |
| 107 | hle/service/err_f.h | 127 | hle/service/err_f.h |
| 108 | hle/service/fs_user.h | 128 | hle/service/fs/archive.h |
| 129 | hle/service/fs/fs_user.h | ||
| 130 | hle/service/frd_u.h | ||
| 109 | hle/service/gsp_gpu.h | 131 | hle/service/gsp_gpu.h |
| 110 | hle/service/hid_user.h | 132 | hle/service/hid_user.h |
| 133 | hle/service/ir_rst.h | ||
| 134 | hle/service/ir_u.h | ||
| 135 | hle/service/ldr_ro.h | ||
| 111 | hle/service/mic_u.h | 136 | hle/service/mic_u.h |
| 137 | hle/service/nim_aoc.h | ||
| 112 | hle/service/ndm_u.h | 138 | hle/service/ndm_u.h |
| 113 | hle/service/nwm_uds.h | 139 | hle/service/nwm_uds.h |
| 140 | hle/service/pm_app.h | ||
| 114 | hle/service/ptm_u.h | 141 | hle/service/ptm_u.h |
| 115 | hle/service/service.h | 142 | hle/service/service.h |
| 116 | hle/service/soc_u.h | 143 | hle/service/soc_u.h |
| 117 | hle/service/srv.h | 144 | hle/service/srv.h |
| 118 | hle/service/ssl_c.h | 145 | hle/service/ssl_c.h |
| 119 | hle/config_mem.h | 146 | hle/config_mem.h |
| 147 | hle/result.h | ||
| 120 | hle/function_wrappers.h | 148 | hle/function_wrappers.h |
| 121 | hle/hle.h | 149 | hle/hle.h |
| 122 | hle/svc.h | 150 | hle/svc.h |
| 123 | hw/gpu.h | 151 | hw/gpu.h |
| 124 | hw/hw.h | 152 | hw/hw.h |
| 125 | hw/ndma.h | ||
| 126 | loader/elf.h | 153 | loader/elf.h |
| 127 | loader/loader.h | 154 | loader/loader.h |
| 128 | loader/ncch.h | 155 | loader/ncch.h |
| 156 | loader/3dsx.h | ||
| 129 | core.h | 157 | core.h |
| 130 | core_timing.h | 158 | core_timing.h |
| 131 | mem_map.h | 159 | mem_map.h |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index be677ae20..3ae528562 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -16,7 +16,7 @@ public: | |||
| 16 | num_instructions = 0; | 16 | num_instructions = 0; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | ~ARM_Interface() { | 19 | virtual ~ARM_Interface() { |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| @@ -63,7 +63,7 @@ public: | |||
| 63 | * Get the current CPSR register | 63 | * Get the current CPSR register |
| 64 | * @return Returns the value of the CPSR register | 64 | * @return Returns the value of the CPSR register |
| 65 | */ | 65 | */ |
| 66 | virtual u32 GetCPSR() const = 0; | 66 | virtual u32 GetCPSR() const = 0; |
| 67 | 67 | ||
| 68 | /** | 68 | /** |
| 69 | * Set the current CPSR register | 69 | * Set the current CPSR register |
| @@ -98,7 +98,7 @@ public: | |||
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | protected: | 100 | protected: |
| 101 | 101 | ||
| 102 | /** | 102 | /** |
| 103 | * Executes the given number of instructions | 103 | * Executes the given number of instructions |
| 104 | * @param num_instructions Number of instructions to executes | 104 | * @param num_instructions Number of instructions to executes |
diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp index 0f384ad3e..55278474b 100644 --- a/src/core/arm/disassembler/load_symbol_map.cpp +++ b/src/core/arm/disassembler/load_symbol_map.cpp | |||
| @@ -22,8 +22,8 @@ void LoadSymbolMap(std::string filename) { | |||
| 22 | 22 | ||
| 23 | while (std::getline(infile, line)) { | 23 | while (std::getline(infile, line)) { |
| 24 | std::istringstream iss(line); | 24 | std::istringstream iss(line); |
| 25 | if (!(iss >> address_str >> size >> function_name)) { | 25 | if (!(iss >> address_str >> size >> function_name)) { |
| 26 | break; // Error parsing | 26 | break; // Error parsing |
| 27 | } | 27 | } |
| 28 | u32 address = std::stoul(address_str, nullptr, 16); | 28 | u32 address = std::stoul(address_str, nullptr, 16); |
| 29 | 29 | ||
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 669b612fc..6c8ea211e 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/arm/skyeye_common/armcpu.h" | 5 | #include "core/arm/skyeye_common/armcpu.h" |
| 6 | #include "core/arm/skyeye_common/armemu.h" | 6 | #include "core/arm/skyeye_common/armemu.h" |
| @@ -60,7 +60,7 @@ void ARM_DynCom::SetPC(u32 pc) { | |||
| 60 | * @return Returns current PC | 60 | * @return Returns current PC |
| 61 | */ | 61 | */ |
| 62 | u32 ARM_DynCom::GetPC() const { | 62 | u32 ARM_DynCom::GetPC() const { |
| 63 | return state->pc; | 63 | return state->Reg[15]; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | /** | 66 | /** |
| @@ -110,9 +110,12 @@ u64 ARM_DynCom::GetTicks() const { | |||
| 110 | * @param num_instructions Number of instructions to executes | 110 | * @param num_instructions Number of instructions to executes |
| 111 | */ | 111 | */ |
| 112 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { | 112 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { |
| 113 | ticks += num_instructions; | ||
| 114 | state->NumInstrsToExecute = num_instructions; | 113 | state->NumInstrsToExecute = num_instructions; |
| 115 | InterpreterMainLoop(state.get()); | 114 | |
| 115 | // Dyncom only breaks on instruction dispatch. This only happens on every instruction when | ||
| 116 | // executing one instruction at a time. Otherwise, if a block is being executed, more | ||
| 117 | // instructions may actually be executed than specified. | ||
| 118 | ticks += InterpreterMainLoop(state.get()); | ||
| 116 | } | 119 | } |
| 117 | 120 | ||
| 118 | /** | 121 | /** |
| @@ -126,7 +129,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) { | |||
| 126 | 129 | ||
| 127 | ctx.sp = state->Reg[13]; | 130 | ctx.sp = state->Reg[13]; |
| 128 | ctx.lr = state->Reg[14]; | 131 | ctx.lr = state->Reg[14]; |
| 129 | ctx.pc = state->pc; | 132 | ctx.pc = state->Reg[15]; |
| 130 | ctx.cpsr = state->Cpsr; | 133 | ctx.cpsr = state->Cpsr; |
| 131 | 134 | ||
| 132 | ctx.fpscr = state->VFP[1]; | 135 | ctx.fpscr = state->VFP[1]; |
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 1f8cd3a3a..51eea41ed 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -19,7 +19,7 @@ public: | |||
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | * Set the Program Counter to an address | 21 | * Set the Program Counter to an address |
| 22 | * @param addr Address to set PC to | 22 | * @param pc Address to set PC to |
| 23 | */ | 23 | */ |
| 24 | void SetPC(u32 pc) override; | 24 | void SetPC(u32 pc) override; |
| 25 | 25 | ||
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index fe1501b59..68012bffd 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -26,7 +26,7 @@ | |||
| 26 | #define CITRA_IGNORE_EXIT(x) | 26 | #define CITRA_IGNORE_EXIT(x) |
| 27 | 27 | ||
| 28 | #include <algorithm> | 28 | #include <algorithm> |
| 29 | #include <map> | 29 | #include <unordered_map> |
| 30 | #include <stdio.h> | 30 | #include <stdio.h> |
| 31 | #include <assert.h> | 31 | #include <assert.h> |
| 32 | #include <cstdio> | 32 | #include <cstdio> |
| @@ -94,9 +94,8 @@ typedef unsigned int (*shtop_fp_t)(arm_processor *cpu, unsigned int sht_oper); | |||
| 94 | 94 | ||
| 95 | /* exclusive memory access */ | 95 | /* exclusive memory access */ |
| 96 | static int exclusive_detect(ARMul_State* state, ARMword addr){ | 96 | static int exclusive_detect(ARMul_State* state, ARMword addr){ |
| 97 | int i; | ||
| 98 | #if 0 | 97 | #if 0 |
| 99 | for(i = 0; i < 128; i++){ | 98 | for(int i = 0; i < 128; i++){ |
| 100 | if(state->exclusive_tag_array[i] == addr) | 99 | if(state->exclusive_tag_array[i] == addr) |
| 101 | return 0; | 100 | return 0; |
| 102 | } | 101 | } |
| @@ -108,9 +107,8 @@ static int exclusive_detect(ARMul_State* state, ARMword addr){ | |||
| 108 | } | 107 | } |
| 109 | 108 | ||
| 110 | static void add_exclusive_addr(ARMul_State* state, ARMword addr){ | 109 | static void add_exclusive_addr(ARMul_State* state, ARMword addr){ |
| 111 | int i; | ||
| 112 | #if 0 | 110 | #if 0 |
| 113 | for(i = 0; i < 128; i++){ | 111 | for(int i = 0; i < 128; i++){ |
| 114 | if(state->exclusive_tag_array[i] == 0xffffffff){ | 112 | if(state->exclusive_tag_array[i] == 0xffffffff){ |
| 115 | state->exclusive_tag_array[i] = addr; | 113 | state->exclusive_tag_array[i] = addr; |
| 116 | //DEBUG_LOG(ARM11, "In %s, add addr 0x%x\n", __func__, addr); | 114 | //DEBUG_LOG(ARM11, "In %s, add addr 0x%x\n", __func__, addr); |
| @@ -435,9 +433,7 @@ typedef struct _ldst_inst { | |||
| 435 | unsigned int inst; | 433 | unsigned int inst; |
| 436 | get_addr_fp_t get_addr; | 434 | get_addr_fp_t get_addr; |
| 437 | } ldst_inst; | 435 | } ldst_inst; |
| 438 | #define DEBUG_MSG DEBUG_LOG(ARM11, "in %s %d\n", __FUNCTION__, __LINE__); \ | 436 | #define DEBUG_MSG LOG_DEBUG(Core_ARM11, "inst is %x", inst); CITRA_IGNORE_EXIT(0) |
| 439 | DEBUG_LOG(ARM11, "inst is %x\n", inst); \ | ||
| 440 | CITRA_IGNORE_EXIT(0) | ||
| 441 | 437 | ||
| 442 | int CondPassed(arm_processor *cpu, unsigned int cond); | 438 | int CondPassed(arm_processor *cpu, unsigned int cond); |
| 443 | #define LnSWoUB(s) glue(LnSWoUB, s) | 439 | #define LnSWoUB(s) glue(LnSWoUB, s) |
| @@ -1425,7 +1421,7 @@ inline void *AllocBuffer(unsigned int size) | |||
| 1425 | int start = top; | 1421 | int start = top; |
| 1426 | top += size; | 1422 | top += size; |
| 1427 | if (top > CACHE_BUFFER_SIZE) { | 1423 | if (top > CACHE_BUFFER_SIZE) { |
| 1428 | DEBUG_LOG(ARM11, "inst_buf is full\n"); | 1424 | LOG_ERROR(Core_ARM11, "inst_buf is full"); |
| 1429 | CITRA_IGNORE_EXIT(-1); | 1425 | CITRA_IGNORE_EXIT(-1); |
| 1430 | } | 1426 | } |
| 1431 | return (void *)&inst_buf[start]; | 1427 | return (void *)&inst_buf[start]; |
| @@ -1611,6 +1607,10 @@ get_addr_fp_t get_calc_addr_op(unsigned int inst) | |||
| 1611 | #define CHECK_RM (inst_cream->Rm == 15) | 1607 | #define CHECK_RM (inst_cream->Rm == 15) |
| 1612 | #define CHECK_RS (inst_cream->Rs == 15) | 1608 | #define CHECK_RS (inst_cream->Rs == 15) |
| 1613 | 1609 | ||
| 1610 | #define UNIMPLEMENTED_INSTRUCTION(mnemonic) \ | ||
| 1611 | LOG_ERROR(Core_ARM11, "unimplemented instruction: %s", mnemonic); \ | ||
| 1612 | CITRA_IGNORE_EXIT(-1); \ | ||
| 1613 | return nullptr; | ||
| 1614 | 1614 | ||
| 1615 | ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index) | 1615 | ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index) |
| 1616 | { | 1616 | { |
| @@ -1725,7 +1725,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(unsigned int inst, int index) | |||
| 1725 | inst_base->br = INDIRECT_BRANCH; | 1725 | inst_base->br = INDIRECT_BRANCH; |
| 1726 | return inst_base; | 1726 | return inst_base; |
| 1727 | } | 1727 | } |
| 1728 | ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 1728 | ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BKPT"); } |
| 1729 | ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index) | 1729 | ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index) |
| 1730 | { | 1730 | { |
| 1731 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst)); | 1731 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst)); |
| @@ -1760,7 +1760,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bx)(unsigned int inst, int index) | |||
| 1760 | 1760 | ||
| 1761 | return inst_base; | 1761 | return inst_base; |
| 1762 | } | 1762 | } |
| 1763 | ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 1763 | ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BXJ"); } |
| 1764 | ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){ | 1764 | ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){ |
| 1765 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst)); | 1765 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst)); |
| 1766 | cdp_inst *inst_cream = (cdp_inst *)inst_base->component; | 1766 | cdp_inst *inst_cream = (cdp_inst *)inst_base->component; |
| @@ -1777,7 +1777,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){ | |||
| 1777 | inst_cream->opcode_1 = BITS(inst, 20, 23); | 1777 | inst_cream->opcode_1 = BITS(inst, 20, 23); |
| 1778 | inst_cream->inst = inst; | 1778 | inst_cream->inst = inst; |
| 1779 | 1779 | ||
| 1780 | DEBUG_LOG(ARM11, "in func %s inst %x index %x\n", __FUNCTION__, inst, index); | 1780 | LOG_TRACE(Core_ARM11, "inst %x index %x", inst, index); |
| 1781 | return inst_base; | 1781 | return inst_base; |
| 1782 | } | 1782 | } |
| 1783 | ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index) | 1783 | ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index) |
| @@ -2207,7 +2207,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(unsigned int inst, int index) | |||
| 2207 | inst_cream->inst = inst; | 2207 | inst_cream->inst = inst; |
| 2208 | return inst_base; | 2208 | return inst_base; |
| 2209 | } | 2209 | } |
| 2210 | ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2210 | ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MCRR"); } |
| 2211 | ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index) | 2211 | ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index) |
| 2212 | { | 2212 | { |
| 2213 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst)); | 2213 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst)); |
| @@ -2266,7 +2266,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(unsigned int inst, int index) | |||
| 2266 | inst_cream->inst = inst; | 2266 | inst_cream->inst = inst; |
| 2267 | return inst_base; | 2267 | return inst_base; |
| 2268 | } | 2268 | } |
| 2269 | ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2269 | ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MRRC"); } |
| 2270 | ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index) | 2270 | ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index) |
| 2271 | { | 2271 | { |
| 2272 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst)); | 2272 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst)); |
| @@ -2360,8 +2360,8 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(orr)(unsigned int inst, int index) | |||
| 2360 | } | 2360 | } |
| 2361 | return inst_base; | 2361 | return inst_base; |
| 2362 | } | 2362 | } |
| 2363 | ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2363 | ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHBT"); } |
| 2364 | ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2364 | ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHTB"); } |
| 2365 | ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) | 2365 | ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) |
| 2366 | { | 2366 | { |
| 2367 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst)); | 2367 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst)); |
| @@ -2373,16 +2373,16 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) | |||
| 2373 | 2373 | ||
| 2374 | return inst_base; | 2374 | return inst_base; |
| 2375 | } | 2375 | } |
| 2376 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2376 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); } |
| 2377 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2377 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD16"); } |
| 2378 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2378 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); } |
| 2379 | ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2379 | ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADDSUBX"); } |
| 2380 | ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2380 | ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); } |
| 2381 | ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2381 | ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); } |
| 2382 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2382 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB"); } |
| 2383 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2383 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB16"); } |
| 2384 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2384 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); } |
| 2385 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2385 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUBADDX"); } |
| 2386 | ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) | 2386 | ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) |
| 2387 | { | 2387 | { |
| 2388 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); | 2388 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); |
| @@ -2412,8 +2412,8 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rev16)(unsigned int inst, int index){ | |||
| 2412 | 2412 | ||
| 2413 | return inst_base; | 2413 | return inst_base; |
| 2414 | } | 2414 | } |
| 2415 | ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2415 | ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("REVSH"); } |
| 2416 | ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2416 | ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("RFE"); } |
| 2417 | ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index) | 2417 | ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index) |
| 2418 | { | 2418 | { |
| 2419 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst)); | 2419 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst)); |
| @@ -2462,9 +2462,9 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index) | |||
| 2462 | } | 2462 | } |
| 2463 | return inst_base; | 2463 | return inst_base; |
| 2464 | } | 2464 | } |
| 2465 | ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2465 | ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD16"); } |
| 2466 | ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2466 | ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); } |
| 2467 | ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2467 | ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADDSUBX"); } |
| 2468 | ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) | 2468 | ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) |
| 2469 | { | 2469 | { |
| 2470 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); | 2470 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); |
| @@ -2489,14 +2489,14 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) | |||
| 2489 | } | 2489 | } |
| 2490 | return inst_base; | 2490 | return inst_base; |
| 2491 | } | 2491 | } |
| 2492 | ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2492 | ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SEL"); } |
| 2493 | ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2493 | ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); } |
| 2494 | ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2494 | ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); } |
| 2495 | ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2495 | ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); } |
| 2496 | ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2496 | ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADDSUBX"); } |
| 2497 | ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2497 | ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB16"); } |
| 2498 | ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2498 | ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB8"); } |
| 2499 | ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2499 | ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUBADDX"); } |
| 2500 | ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index) | 2500 | ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index) |
| 2501 | { | 2501 | { |
| 2502 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst)); | 2502 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst)); |
| @@ -2555,15 +2555,15 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index) | |||
| 2555 | inst_base->load_r15 = 1; | 2555 | inst_base->load_r15 = 1; |
| 2556 | return inst_base; | 2556 | return inst_base; |
| 2557 | } | 2557 | } |
| 2558 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2558 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); } |
| 2559 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2559 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALD"); } |
| 2560 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2560 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLAW"); } |
| 2561 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2561 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSD"); } |
| 2562 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2562 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSLD"); } |
| 2563 | ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2563 | ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLA"); } |
| 2564 | ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2564 | ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLS"); } |
| 2565 | ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2565 | ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMUL"); } |
| 2566 | ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2566 | ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUAD"); } |
| 2567 | ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index) | 2567 | ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index) |
| 2568 | { | 2568 | { |
| 2569 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst)); | 2569 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst)); |
| @@ -2626,13 +2626,13 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smulw)(unsigned int inst, int index) | |||
| 2626 | inst_base->load_r15 = 1; | 2626 | inst_base->load_r15 = 1; |
| 2627 | return inst_base; | 2627 | return inst_base; |
| 2628 | } | 2628 | } |
| 2629 | ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2629 | ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUSD"); } |
| 2630 | ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2630 | ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); } |
| 2631 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2631 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT"); } |
| 2632 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2632 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); } |
| 2633 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2633 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB16"); } |
| 2634 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2634 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); } |
| 2635 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2635 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUBADDX"); } |
| 2636 | ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) | 2636 | ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) |
| 2637 | { | 2637 | { |
| 2638 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); | 2638 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); |
| @@ -2939,9 +2939,9 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){ | |||
| 2939 | 2939 | ||
| 2940 | return inst_base; | 2940 | return inst_base; |
| 2941 | } | 2941 | } |
| 2942 | ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2942 | ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTAB16"); } |
| 2943 | ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){ | 2943 | ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){ |
| 2944 | DEBUG_LOG(ARM11, "in func %s, SXTAH untested\n", __func__); | 2944 | LOG_WARNING(Core_ARM11, "SXTAH untested"); |
| 2945 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst)); | 2945 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst)); |
| 2946 | sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component; | 2946 | sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component; |
| 2947 | 2947 | ||
| @@ -2957,7 +2957,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){ | |||
| 2957 | 2957 | ||
| 2958 | return inst_base; | 2958 | return inst_base; |
| 2959 | } | 2959 | } |
| 2960 | ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 2960 | ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTB16"); } |
| 2961 | ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index) | 2961 | ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index) |
| 2962 | { | 2962 | { |
| 2963 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst)); | 2963 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst)); |
| @@ -3001,16 +3001,16 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index) | |||
| 3001 | inst_base->load_r15 = 1; | 3001 | inst_base->load_r15 = 1; |
| 3002 | return inst_base; | 3002 | return inst_base; |
| 3003 | } | 3003 | } |
| 3004 | ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3004 | ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD16"); } |
| 3005 | ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3005 | ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD8"); } |
| 3006 | ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3006 | ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADDSUBX"); } |
| 3007 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3007 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD16"); } |
| 3008 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3008 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD8"); } |
| 3009 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3009 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADDSUBX"); } |
| 3010 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3010 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); } |
| 3011 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3011 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); } |
| 3012 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3012 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); } |
| 3013 | ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3013 | ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); } |
| 3014 | ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) | 3014 | ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) |
| 3015 | { | 3015 | { |
| 3016 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); | 3016 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); |
| @@ -3113,21 +3113,21 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(blx_1_thumb)(unsigned int tinst, int index) | |||
| 3113 | return inst_base; | 3113 | return inst_base; |
| 3114 | } | 3114 | } |
| 3115 | 3115 | ||
| 3116 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3116 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD16"); } |
| 3117 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3117 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD8"); } |
| 3118 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3118 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADDSUBX"); } |
| 3119 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3119 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB16"); } |
| 3120 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3120 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB8"); } |
| 3121 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3121 | ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUBADDX"); } |
| 3122 | ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3122 | ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAD8"); } |
| 3123 | ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3123 | ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USADA8"); } |
| 3124 | ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3124 | ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT"); } |
| 3125 | ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3125 | ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT16"); } |
| 3126 | ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3126 | ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB16"); } |
| 3127 | ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3127 | ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB8"); } |
| 3128 | ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3128 | ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUBADDX"); } |
| 3129 | ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3129 | ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTAB16"); } |
| 3130 | ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} | 3130 | ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTB16"); } |
| 3131 | 3131 | ||
| 3132 | 3132 | ||
| 3133 | 3133 | ||
| @@ -3309,9 +3309,8 @@ const transop_fp_t arm_instruction_trans[] = { | |||
| 3309 | INTERPRETER_TRANSLATE(blx_1_thumb) | 3309 | INTERPRETER_TRANSLATE(blx_1_thumb) |
| 3310 | }; | 3310 | }; |
| 3311 | 3311 | ||
| 3312 | typedef map<unsigned int, int> bb_map; | 3312 | typedef std::unordered_map<u32, int> bb_map; |
| 3313 | bb_map CreamCache[65536]; | 3313 | bb_map CreamCache; |
| 3314 | bb_map ProfileCache[65536]; | ||
| 3315 | 3314 | ||
| 3316 | //#define USE_DUMMY_CACHE | 3315 | //#define USE_DUMMY_CACHE |
| 3317 | 3316 | ||
| @@ -3319,14 +3318,12 @@ bb_map ProfileCache[65536]; | |||
| 3319 | unsigned int DummyCache[0x100000]; | 3318 | unsigned int DummyCache[0x100000]; |
| 3320 | #endif | 3319 | #endif |
| 3321 | 3320 | ||
| 3322 | #define HASH(x) ((x + (x << 3) + (x >> 6)) % 65536) | ||
| 3323 | void insert_bb(unsigned int addr, int start) | 3321 | void insert_bb(unsigned int addr, int start) |
| 3324 | { | 3322 | { |
| 3325 | #ifdef USE_DUMMY_CACHE | 3323 | #ifdef USE_DUMMY_CACHE |
| 3326 | DummyCache[addr] = start; | 3324 | DummyCache[addr] = start; |
| 3327 | #else | 3325 | #else |
| 3328 | // CreamCache[addr] = start; | 3326 | CreamCache[addr] = start; |
| 3329 | CreamCache[HASH(addr)][addr] = start; | ||
| 3330 | #endif | 3327 | #endif |
| 3331 | } | 3328 | } |
| 3332 | 3329 | ||
| @@ -3341,8 +3338,8 @@ int find_bb(unsigned int addr, int &start) | |||
| 3341 | } else | 3338 | } else |
| 3342 | ret = -1; | 3339 | ret = -1; |
| 3343 | #else | 3340 | #else |
| 3344 | bb_map::const_iterator it = CreamCache[HASH(addr)].find(addr); | 3341 | bb_map::const_iterator it = CreamCache.find(addr); |
| 3345 | if (it != CreamCache[HASH(addr)].end()) { | 3342 | if (it != CreamCache.end()) { |
| 3346 | start = static_cast<int>(it->second); | 3343 | start = static_cast<int>(it->second); |
| 3347 | ret = 0; | 3344 | ret = 0; |
| 3348 | #if HYBRID_MODE | 3345 | #if HYBRID_MODE |
| @@ -3396,7 +3393,7 @@ static tdstate decode_thumb_instr(arm_processor *cpu, uint32_t inst, addr_t addr | |||
| 3396 | } | 3393 | } |
| 3397 | else{ | 3394 | else{ |
| 3398 | /* something wrong */ | 3395 | /* something wrong */ |
| 3399 | DEBUG_LOG(ARM11, "In %s, thumb decoder error\n", __FUNCTION__); | 3396 | LOG_ERROR(Core_ARM11, "thumb decoder error"); |
| 3400 | } | 3397 | } |
| 3401 | break; | 3398 | break; |
| 3402 | case 28: | 3399 | case 28: |
| @@ -3473,30 +3470,15 @@ void flush_bb(uint32_t addr) | |||
| 3473 | uint32_t start; | 3470 | uint32_t start; |
| 3474 | 3471 | ||
| 3475 | addr &= 0xfffff000; | 3472 | addr &= 0xfffff000; |
| 3476 | for (int i = 0; i < 65536; i ++) { | 3473 | for (it = CreamCache.begin(); it != CreamCache.end(); ) { |
| 3477 | for (it = CreamCache[i].begin(); it != CreamCache[i].end(); ) { | 3474 | start = static_cast<uint32_t>(it->first); |
| 3478 | start = static_cast<uint32_t>(it->first); | 3475 | //start = (start >> 12) << 12; |
| 3479 | //start = (start >> 12) << 12; | 3476 | start &= 0xfffff000; |
| 3480 | start &= 0xfffff000; | 3477 | if (start == addr) { |
| 3481 | if (start == addr) { | 3478 | //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first)); |
| 3482 | //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first)); | 3479 | CreamCache.erase(it++); |
| 3483 | CreamCache[i].erase(it ++); | 3480 | } else |
| 3484 | } else | 3481 | ++it; |
| 3485 | ++it; | ||
| 3486 | } | ||
| 3487 | } | ||
| 3488 | |||
| 3489 | for (int i = 0; i < 65536; i ++) { | ||
| 3490 | for (it = ProfileCache[i].begin(); it != ProfileCache[i].end(); ) { | ||
| 3491 | start = static_cast<uint32_t>(it->first); | ||
| 3492 | //start = (start >> 12) << 12; | ||
| 3493 | start &= 0xfffff000; | ||
| 3494 | if (start == addr) { | ||
| 3495 | //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first)); | ||
| 3496 | ProfileCache[i].erase(it ++); | ||
| 3497 | } else | ||
| 3498 | ++it; | ||
| 3499 | } | ||
| 3500 | } | 3482 | } |
| 3501 | 3483 | ||
| 3502 | //DEBUG_LOG(ARM11, "flush bb @ %x\n", addr); | 3484 | //DEBUG_LOG(ARM11, "flush bb @ %x\n", addr); |
| @@ -3619,7 +3601,7 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr) | |||
| 3619 | bank->bank_read(32, phys_addr, &inst); | 3601 | bank->bank_read(32, phys_addr, &inst); |
| 3620 | } | 3602 | } |
| 3621 | else { | 3603 | else { |
| 3622 | DEBUG_LOG(ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr); | 3604 | LOG_ERROR(Core_ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr); |
| 3623 | return FETCH_FAILURE; | 3605 | return FETCH_FAILURE; |
| 3624 | } | 3606 | } |
| 3625 | #else | 3607 | #else |
| @@ -3649,8 +3631,8 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr) | |||
| 3649 | 3631 | ||
| 3650 | ret = decode_arm_instr(inst, &idx); | 3632 | ret = decode_arm_instr(inst, &idx); |
| 3651 | if (ret == DECODE_FAILURE) { | 3633 | if (ret == DECODE_FAILURE) { |
| 3652 | DEBUG_LOG(ARM11, "[info] : Decode failure.\tPC : [0x%x]\tInstruction : [%x]\n", phys_addr, inst); | 3634 | LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : [%x]", phys_addr, inst); |
| 3653 | DEBUG_LOG(ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x\n", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); | 3635 | LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); |
| 3654 | CITRA_IGNORE_EXIT(-1); | 3636 | CITRA_IGNORE_EXIT(-1); |
| 3655 | } | 3637 | } |
| 3656 | // DEBUG_LOG(ARM11, "PC : [0x%x] INST : %s\n", cpu->translate_pc, arm_instruction[idx].name); | 3638 | // DEBUG_LOG(ARM11, "PC : [0x%x] INST : %s\n", cpu->translate_pc, arm_instruction[idx].name); |
| @@ -3694,7 +3676,7 @@ void InterpreterInitInstLength(unsigned long long int *ptr, size_t size) | |||
| 3694 | } | 3676 | } |
| 3695 | } | 3677 | } |
| 3696 | for (int i = 0; i < array_size - 4; i ++) | 3678 | for (int i = 0; i < array_size - 4; i ++) |
| 3697 | DEBUG_LOG(ARM11, "[%d]:%d\n", i, InstLength[i]); | 3679 | LOG_DEBUG(Core_ARM11, "[%d]:%d", i, InstLength[i]); |
| 3698 | } | 3680 | } |
| 3699 | 3681 | ||
| 3700 | int clz(unsigned int x) | 3682 | int clz(unsigned int x) |
| @@ -3718,7 +3700,7 @@ static bool InAPrivilegedMode(arm_core_t *core) | |||
| 3718 | } | 3700 | } |
| 3719 | 3701 | ||
| 3720 | /* r15 = r15 + 8 */ | 3702 | /* r15 = r15 + 8 */ |
| 3721 | void InterpreterMainLoop(ARMul_State* state) | 3703 | unsigned InterpreterMainLoop(ARMul_State* state) |
| 3722 | { | 3704 | { |
| 3723 | #define CRn inst_cream->crn | 3705 | #define CRn inst_cream->crn |
| 3724 | #define OPCODE_2 inst_cream->opcode_2 | 3706 | #define OPCODE_2 inst_cream->opcode_2 |
| @@ -3741,22 +3723,28 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 3741 | //if (debug_function(core)) \ | 3723 | //if (debug_function(core)) \ |
| 3742 | if (core->check_int_flag) \ | 3724 | if (core->check_int_flag) \ |
| 3743 | goto END | 3725 | goto END |
| 3744 | //DEBUG_LOG(ARM11, "icounter is %llx line is %d pc is %x\n", cpu->icounter, __LINE__, cpu->Reg[15]) | 3726 | //LOG_TRACE(Core_ARM11, "icounter is %llx pc is %x\n", cpu->icounter, cpu->Reg[15]) |
| 3745 | #else | 3727 | #else |
| 3746 | #define INC_ICOUNTER ; | 3728 | #define INC_ICOUNTER ; |
| 3747 | #endif | 3729 | #endif |
| 3748 | 3730 | ||
| 3749 | #define FETCH_INST if (inst_base->br != NON_BRANCH) \ | 3731 | #define FETCH_INST if (inst_base->br != NON_BRANCH) \ |
| 3750 | goto PROFILING; \ | 3732 | goto DISPATCH; \ |
| 3751 | inst_base = (arm_inst *)&inst_buf[ptr] | 3733 | inst_base = (arm_inst *)&inst_buf[ptr] |
| 3752 | #define INC_PC(l) ptr += sizeof(arm_inst) + l | 3734 | #define INC_PC(l) ptr += sizeof(arm_inst) + l |
| 3753 | 3735 | ||
| 3754 | // GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a | 3736 | // GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a |
| 3755 | // clunky switch statement. | 3737 | // clunky switch statement. |
| 3756 | #if defined __GNUC__ || defined __clang__ | 3738 | #if defined __GNUC__ || defined __clang__ |
| 3757 | #define GOTO_NEXT_INST goto *InstLabel[inst_base->idx] | 3739 | #define GOTO_NEXT_INST \ |
| 3740 | if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ | ||
| 3741 | num_instrs++; \ | ||
| 3742 | goto *InstLabel[inst_base->idx] | ||
| 3758 | #else | 3743 | #else |
| 3759 | #define GOTO_NEXT_INST switch(inst_base->idx) { \ | 3744 | #define GOTO_NEXT_INST \ |
| 3745 | if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ | ||
| 3746 | num_instrs++; \ | ||
| 3747 | switch(inst_base->idx) { \ | ||
| 3760 | case 0: goto VMLA_INST; \ | 3748 | case 0: goto VMLA_INST; \ |
| 3761 | case 1: goto VMLS_INST; \ | 3749 | case 1: goto VMLS_INST; \ |
| 3762 | case 2: goto VNMLA_INST; \ | 3750 | case 2: goto VNMLA_INST; \ |
| @@ -4028,20 +4016,15 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4028 | unsigned int addr; | 4016 | unsigned int addr; |
| 4029 | unsigned int phys_addr; | 4017 | unsigned int phys_addr; |
| 4030 | unsigned int last_pc = 0; | 4018 | unsigned int last_pc = 0; |
| 4019 | unsigned int num_instrs = 0; | ||
| 4031 | fault_t fault; | 4020 | fault_t fault; |
| 4032 | static unsigned int last_physical_base = 0, last_logical_base = 0; | 4021 | static unsigned int last_physical_base = 0, last_logical_base = 0; |
| 4033 | int ptr; | 4022 | int ptr; |
| 4023 | bool single_step = (cpu->NumInstrsToExecute == 1); | ||
| 4034 | 4024 | ||
| 4035 | LOAD_NZCVT; | 4025 | LOAD_NZCVT; |
| 4036 | DISPATCH: | 4026 | DISPATCH: |
| 4037 | { | 4027 | { |
| 4038 | if (cpu->NumInstrsToExecute == 0) | ||
| 4039 | return; | ||
| 4040 | |||
| 4041 | cpu->NumInstrsToExecute--; | ||
| 4042 | |||
| 4043 | //NOTICE_LOG(ARM11, "instr!"); | ||
| 4044 | |||
| 4045 | if (!cpu->NirqSig) { | 4028 | if (!cpu->NirqSig) { |
| 4046 | if (!(cpu->Cpsr & 0x80)) { | 4029 | if (!(cpu->Cpsr & 0x80)) { |
| 4047 | goto END; | 4030 | goto END; |
| @@ -4179,10 +4162,6 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4179 | inst_base = (arm_inst *)&inst_buf[ptr]; | 4162 | inst_base = (arm_inst *)&inst_buf[ptr]; |
| 4180 | GOTO_NEXT_INST; | 4163 | GOTO_NEXT_INST; |
| 4181 | } | 4164 | } |
| 4182 | PROFILING: | ||
| 4183 | { | ||
| 4184 | goto DISPATCH; | ||
| 4185 | } | ||
| 4186 | ADC_INST: | 4165 | ADC_INST: |
| 4187 | { | 4166 | { |
| 4188 | INC_ICOUNTER; | 4167 | INC_ICOUNTER; |
| @@ -4207,7 +4186,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4207 | } | 4186 | } |
| 4208 | if (inst_cream->Rd == 15) { | 4187 | if (inst_cream->Rd == 15) { |
| 4209 | INC_PC(sizeof(adc_inst)); | 4188 | INC_PC(sizeof(adc_inst)); |
| 4210 | goto PROFILING; | 4189 | goto DISPATCH; |
| 4211 | } | 4190 | } |
| 4212 | } | 4191 | } |
| 4213 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4192 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4241,7 +4220,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4241 | } | 4220 | } |
| 4242 | if (inst_cream->Rd == 15) { | 4221 | if (inst_cream->Rd == 15) { |
| 4243 | INC_PC(sizeof(add_inst)); | 4222 | INC_PC(sizeof(add_inst)); |
| 4244 | goto PROFILING; | 4223 | goto DISPATCH; |
| 4245 | } | 4224 | } |
| 4246 | } | 4225 | } |
| 4247 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4226 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4272,7 +4251,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4272 | } | 4251 | } |
| 4273 | if (inst_cream->Rd == 15) { | 4252 | if (inst_cream->Rd == 15) { |
| 4274 | INC_PC(sizeof(and_inst)); | 4253 | INC_PC(sizeof(and_inst)); |
| 4275 | goto PROFILING; | 4254 | goto DISPATCH; |
| 4276 | } | 4255 | } |
| 4277 | } | 4256 | } |
| 4278 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4257 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4290,11 +4269,11 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4290 | } | 4269 | } |
| 4291 | SET_PC; | 4270 | SET_PC; |
| 4292 | INC_PC(sizeof(bbl_inst)); | 4271 | INC_PC(sizeof(bbl_inst)); |
| 4293 | goto PROFILING; | 4272 | goto DISPATCH; |
| 4294 | } | 4273 | } |
| 4295 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4274 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 4296 | INC_PC(sizeof(bbl_inst)); | 4275 | INC_PC(sizeof(bbl_inst)); |
| 4297 | goto PROFILING; | 4276 | goto DISPATCH; |
| 4298 | } | 4277 | } |
| 4299 | BIC_INST: | 4278 | BIC_INST: |
| 4300 | { | 4279 | { |
| @@ -4322,7 +4301,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4322 | } | 4301 | } |
| 4323 | if (inst_cream->Rd == 15) { | 4302 | if (inst_cream->Rd == 15) { |
| 4324 | INC_PC(sizeof(bic_inst)); | 4303 | INC_PC(sizeof(bic_inst)); |
| 4325 | goto PROFILING; | 4304 | goto DISPATCH; |
| 4326 | } | 4305 | } |
| 4327 | } | 4306 | } |
| 4328 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4307 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4358,12 +4337,12 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4358 | //DEBUG_MSG; | 4337 | //DEBUG_MSG; |
| 4359 | } | 4338 | } |
| 4360 | INC_PC(sizeof(blx_inst)); | 4339 | INC_PC(sizeof(blx_inst)); |
| 4361 | goto PROFILING; | 4340 | goto DISPATCH; |
| 4362 | } | 4341 | } |
| 4363 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4342 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 4364 | // INC_PC(sizeof(bx_inst)); | 4343 | // INC_PC(sizeof(bx_inst)); |
| 4365 | INC_PC(sizeof(blx_inst)); | 4344 | INC_PC(sizeof(blx_inst)); |
| 4366 | goto PROFILING; | 4345 | goto DISPATCH; |
| 4367 | } | 4346 | } |
| 4368 | BX_INST: | 4347 | BX_INST: |
| 4369 | { | 4348 | { |
| @@ -4371,17 +4350,17 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4371 | bx_inst *inst_cream = (bx_inst *)inst_base->component; | 4350 | bx_inst *inst_cream = (bx_inst *)inst_base->component; |
| 4372 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 4351 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { |
| 4373 | if (inst_cream->Rm == 15) | 4352 | if (inst_cream->Rm == 15) |
| 4374 | DEBUG_LOG(ARM11, "In %s, BX at pc %x: use of Rm = R15 is discouraged\n", __FUNCTION__, cpu->Reg[15]); | 4353 | LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]); |
| 4375 | cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; | 4354 | cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; |
| 4376 | cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe; | 4355 | cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe; |
| 4377 | // cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; | 4356 | // cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; |
| 4378 | INC_PC(sizeof(bx_inst)); | 4357 | INC_PC(sizeof(bx_inst)); |
| 4379 | goto PROFILING; | 4358 | goto DISPATCH; |
| 4380 | } | 4359 | } |
| 4381 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4360 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 4382 | // INC_PC(sizeof(bx_inst)); | 4361 | // INC_PC(sizeof(bx_inst)); |
| 4383 | INC_PC(sizeof(bx_inst)); | 4362 | INC_PC(sizeof(bx_inst)); |
| 4384 | goto PROFILING; | 4363 | goto DISPATCH; |
| 4385 | } | 4364 | } |
| 4386 | BXJ_INST: | 4365 | BXJ_INST: |
| 4387 | CDP_INST: | 4366 | CDP_INST: |
| @@ -4393,12 +4372,13 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4393 | #define CP_ACCESS_ALLOW 0 | 4372 | #define CP_ACCESS_ALLOW 0 |
| 4394 | if(CP_ACCESS_ALLOW){ | 4373 | if(CP_ACCESS_ALLOW){ |
| 4395 | /* undefined instruction here */ | 4374 | /* undefined instruction here */ |
| 4396 | return; | 4375 | cpu->NumInstrsToExecute = 0; |
| 4376 | return num_instrs; | ||
| 4397 | } | 4377 | } |
| 4398 | ERROR_LOG(ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]); | 4378 | LOG_ERROR(Core_ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]); |
| 4399 | unsigned cpab = (cpu->CDP[inst_cream->cp_num]) (cpu, ARMul_FIRST, inst_cream->inst); | 4379 | unsigned cpab = (cpu->CDP[inst_cream->cp_num]) (cpu, ARMul_FIRST, inst_cream->inst); |
| 4400 | if(cpab != ARMul_DONE){ | 4380 | if(cpab != ARMul_DONE){ |
| 4401 | ERROR_LOG(ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num); | 4381 | LOG_ERROR(Core_ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num); |
| 4402 | //CITRA_IGNORE_EXIT(-1); | 4382 | //CITRA_IGNORE_EXIT(-1); |
| 4403 | } | 4383 | } |
| 4404 | } | 4384 | } |
| @@ -4522,7 +4502,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4522 | // RD = RM; | 4502 | // RD = RM; |
| 4523 | if ((inst_cream->Rd == 15)) { | 4503 | if ((inst_cream->Rd == 15)) { |
| 4524 | INC_PC(sizeof(mov_inst)); | 4504 | INC_PC(sizeof(mov_inst)); |
| 4525 | goto PROFILING; | 4505 | goto DISPATCH; |
| 4526 | } | 4506 | } |
| 4527 | } | 4507 | } |
| 4528 | // DEBUG_LOG(ARM11, "cpy inst %x\n", cpu->Reg[15]); | 4508 | // DEBUG_LOG(ARM11, "cpy inst %x\n", cpu->Reg[15]); |
| @@ -4558,7 +4538,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4558 | } | 4538 | } |
| 4559 | if (inst_cream->Rd == 15) { | 4539 | if (inst_cream->Rd == 15) { |
| 4560 | INC_PC(sizeof(eor_inst)); | 4540 | INC_PC(sizeof(eor_inst)); |
| 4561 | goto PROFILING; | 4541 | goto DISPATCH; |
| 4562 | } | 4542 | } |
| 4563 | } | 4543 | } |
| 4564 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4544 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4717,7 +4697,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4717 | } | 4697 | } |
| 4718 | if (BIT(inst, 15)) { | 4698 | if (BIT(inst, 15)) { |
| 4719 | INC_PC(sizeof(ldst_inst)); | 4699 | INC_PC(sizeof(ldst_inst)); |
| 4720 | goto PROFILING; | 4700 | goto DISPATCH; |
| 4721 | } | 4701 | } |
| 4722 | } | 4702 | } |
| 4723 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4703 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4764,7 +4744,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4764 | cpu->TFlag = value & 0x1; | 4744 | cpu->TFlag = value & 0x1; |
| 4765 | cpu->Reg[15] &= 0xFFFFFFFE; | 4745 | cpu->Reg[15] &= 0xFFFFFFFE; |
| 4766 | INC_PC(sizeof(ldst_inst)); | 4746 | INC_PC(sizeof(ldst_inst)); |
| 4767 | goto PROFILING; | 4747 | goto DISPATCH; |
| 4768 | } | 4748 | } |
| 4769 | //} | 4749 | //} |
| 4770 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4750 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4794,7 +4774,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4794 | cpu->TFlag = value & 0x1; | 4774 | cpu->TFlag = value & 0x1; |
| 4795 | cpu->Reg[15] &= 0xFFFFFFFE; | 4775 | cpu->Reg[15] &= 0xFFFFFFFE; |
| 4796 | INC_PC(sizeof(ldst_inst)); | 4776 | INC_PC(sizeof(ldst_inst)); |
| 4797 | goto PROFILING; | 4777 | goto DISPATCH; |
| 4798 | } | 4778 | } |
| 4799 | } | 4779 | } |
| 4800 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4780 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4825,7 +4805,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4825 | & 0xffff; | 4805 | & 0xffff; |
| 4826 | RD = RN + operand2; | 4806 | RD = RN + operand2; |
| 4827 | if (inst_cream->Rn == 15 || inst_cream->Rm == 15) { | 4807 | if (inst_cream->Rn == 15 || inst_cream->Rm == 15) { |
| 4828 | DEBUG_LOG(ARM11, "in line %d\n", __LINE__); | 4808 | LOG_ERROR(Core_ARM11, "invalid operands for UXTAH"); |
| 4829 | CITRA_IGNORE_EXIT(-1); | 4809 | CITRA_IGNORE_EXIT(-1); |
| 4830 | } | 4810 | } |
| 4831 | } | 4811 | } |
| @@ -4848,7 +4828,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4848 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 4828 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 4849 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 4829 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 4850 | INC_PC(sizeof(ldst_inst)); | 4830 | INC_PC(sizeof(ldst_inst)); |
| 4851 | goto PROFILING; | 4831 | goto DISPATCH; |
| 4852 | } | 4832 | } |
| 4853 | } | 4833 | } |
| 4854 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4834 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4869,7 +4849,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4869 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 4849 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 4870 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 4850 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 4871 | INC_PC(sizeof(ldst_inst)); | 4851 | INC_PC(sizeof(ldst_inst)); |
| 4872 | goto PROFILING; | 4852 | goto DISPATCH; |
| 4873 | } | 4853 | } |
| 4874 | } | 4854 | } |
| 4875 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4855 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4888,7 +4868,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4888 | uint32_t rear_phys_addr; | 4868 | uint32_t rear_phys_addr; |
| 4889 | fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 1); | 4869 | fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 1); |
| 4890 | if(fault){ | 4870 | if(fault){ |
| 4891 | ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n"); | 4871 | LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n"); |
| 4892 | CITRA_IGNORE_EXIT(-1); | 4872 | CITRA_IGNORE_EXIT(-1); |
| 4893 | goto MMU_EXCEPTION; | 4873 | goto MMU_EXCEPTION; |
| 4894 | } | 4874 | } |
| @@ -4926,7 +4906,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4926 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 4906 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 4927 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 4907 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 4928 | INC_PC(sizeof(ldst_inst)); | 4908 | INC_PC(sizeof(ldst_inst)); |
| 4929 | goto PROFILING; | 4909 | goto DISPATCH; |
| 4930 | } | 4910 | } |
| 4931 | } | 4911 | } |
| 4932 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4912 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4953,7 +4933,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4953 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 4933 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 4954 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 4934 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 4955 | INC_PC(sizeof(ldst_inst)); | 4935 | INC_PC(sizeof(ldst_inst)); |
| 4956 | goto PROFILING; | 4936 | goto DISPATCH; |
| 4957 | } | 4937 | } |
| 4958 | } | 4938 | } |
| 4959 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4939 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -4980,7 +4960,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 4980 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 4960 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 4981 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 4961 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 4982 | INC_PC(sizeof(ldst_inst)); | 4962 | INC_PC(sizeof(ldst_inst)); |
| 4983 | goto PROFILING; | 4963 | goto DISPATCH; |
| 4984 | } | 4964 | } |
| 4985 | } | 4965 | } |
| 4986 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4966 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5006,7 +4986,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5006 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 4986 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 5007 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 4987 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 5008 | INC_PC(sizeof(ldst_inst)); | 4988 | INC_PC(sizeof(ldst_inst)); |
| 5009 | goto PROFILING; | 4989 | goto DISPATCH; |
| 5010 | } | 4990 | } |
| 5011 | } | 4991 | } |
| 5012 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 4992 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5031,7 +5011,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5031 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; | 5011 | cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; |
| 5032 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 5012 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 5033 | INC_PC(sizeof(ldst_inst)); | 5013 | INC_PC(sizeof(ldst_inst)); |
| 5034 | goto PROFILING; | 5014 | goto DISPATCH; |
| 5035 | } | 5015 | } |
| 5036 | } | 5016 | } |
| 5037 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5017 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5058,7 +5038,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5058 | 5038 | ||
| 5059 | if (BITS(inst_cream->inst, 12, 15) == 15) { | 5039 | if (BITS(inst_cream->inst, 12, 15) == 15) { |
| 5060 | INC_PC(sizeof(ldst_inst)); | 5040 | INC_PC(sizeof(ldst_inst)); |
| 5061 | goto PROFILING; | 5041 | goto DISPATCH; |
| 5062 | } | 5042 | } |
| 5063 | } | 5043 | } |
| 5064 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5044 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5111,7 +5091,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5111 | switch(OPCODE_2){ | 5091 | switch(OPCODE_2){ |
| 5112 | case 0: /* invalidate all */ | 5092 | case 0: /* invalidate all */ |
| 5113 | //invalidate_all_tlb(state); | 5093 | //invalidate_all_tlb(state); |
| 5114 | DEBUG_LOG(ARM11, "{TLB} [INSN] invalidate all\n"); | 5094 | LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate all"); |
| 5115 | //remove_tlb(INSN_TLB); | 5095 | //remove_tlb(INSN_TLB); |
| 5116 | //erase_all(core, INSN_TLB); | 5096 | //erase_all(core, INSN_TLB); |
| 5117 | break; | 5097 | break; |
| @@ -5137,7 +5117,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5137 | //invalidate_all_tlb(state); | 5117 | //invalidate_all_tlb(state); |
| 5138 | //remove_tlb(DATA_TLB); | 5118 | //remove_tlb(DATA_TLB); |
| 5139 | //erase_all(core, DATA_TLB); | 5119 | //erase_all(core, DATA_TLB); |
| 5140 | DEBUG_LOG(ARM11, "{TLB} [DATA] invalidate all\n"); | 5120 | LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate all"); |
| 5141 | break; | 5121 | break; |
| 5142 | case 1: /* invalidate by MVA */ | 5122 | case 1: /* invalidate by MVA */ |
| 5143 | //invalidate_by_mva(state, value); | 5123 | //invalidate_by_mva(state, value); |
| @@ -5169,13 +5149,13 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5169 | //invalidate_by_mva(state, value); | 5149 | //invalidate_by_mva(state, value); |
| 5170 | //erase_by_mva(core, RD, DATA_TLB); | 5150 | //erase_by_mva(core, RD, DATA_TLB); |
| 5171 | //erase_by_mva(core, RD, INSN_TLB); | 5151 | //erase_by_mva(core, RD, INSN_TLB); |
| 5172 | DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by mva\n"); | 5152 | LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by mva"); |
| 5173 | break; | 5153 | break; |
| 5174 | case 2: /* invalidate by asid */ | 5154 | case 2: /* invalidate by asid */ |
| 5175 | //invalidate_by_asid(state, value); | 5155 | //invalidate_by_asid(state, value); |
| 5176 | //erase_by_asid(core, RD, DATA_TLB); | 5156 | //erase_by_asid(core, RD, DATA_TLB); |
| 5177 | //erase_by_asid(core, RD, INSN_TLB); | 5157 | //erase_by_asid(core, RD, INSN_TLB); |
| 5178 | DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by asid\n"); | 5158 | LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by asid"); |
| 5179 | break; | 5159 | break; |
| 5180 | default: | 5160 | default: |
| 5181 | break; | 5161 | break; |
| @@ -5197,7 +5177,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5197 | } | 5177 | } |
| 5198 | 5178 | ||
| 5199 | } else { | 5179 | } else { |
| 5200 | DEBUG_LOG(ARM11, "mcr is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); | 5180 | LOG_ERROR(Core_ARM11, "mcr CRn=%d, CRm=%d OP2=%d is not implemented", CRn, CRm, OPCODE_2); |
| 5201 | } | 5181 | } |
| 5202 | } | 5182 | } |
| 5203 | } | 5183 | } |
| @@ -5217,7 +5197,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5217 | uint64_t rs = RS; | 5197 | uint64_t rs = RS; |
| 5218 | uint64_t rn = RN; | 5198 | uint64_t rn = RN; |
| 5219 | if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) { | 5199 | if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) { |
| 5220 | DEBUG_LOG(ARM11, "in __line__\n", __LINE__); | 5200 | LOG_ERROR(Core_ARM11, "invalid operands for MLA"); |
| 5221 | CITRA_IGNORE_EXIT(-1); | 5201 | CITRA_IGNORE_EXIT(-1); |
| 5222 | } | 5202 | } |
| 5223 | // RD = dst = RM * RS + RN; | 5203 | // RD = dst = RM * RS + RN; |
| @@ -5228,7 +5208,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5228 | } | 5208 | } |
| 5229 | if (inst_cream->Rd == 15) { | 5209 | if (inst_cream->Rd == 15) { |
| 5230 | INC_PC(sizeof(mla_inst)); | 5210 | INC_PC(sizeof(mla_inst)); |
| 5231 | goto PROFILING; | 5211 | goto DISPATCH; |
| 5232 | } | 5212 | } |
| 5233 | } | 5213 | } |
| 5234 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5214 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5260,7 +5240,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5260 | } | 5240 | } |
| 5261 | if (inst_cream->Rd == 15) { | 5241 | if (inst_cream->Rd == 15) { |
| 5262 | INC_PC(sizeof(mov_inst)); | 5242 | INC_PC(sizeof(mov_inst)); |
| 5263 | goto PROFILING; | 5243 | goto DISPATCH; |
| 5264 | } | 5244 | } |
| 5265 | // return; | 5245 | // return; |
| 5266 | } | 5246 | } |
| @@ -5331,7 +5311,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5331 | } | 5311 | } |
| 5332 | } | 5312 | } |
| 5333 | else { | 5313 | else { |
| 5334 | DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); | 5314 | LOG_ERROR(Core_ARM11, "mrc CRn=%d, CRm=%d, OP2=%d is not implemented", CRn, CRm, OPCODE_2); |
| 5335 | } | 5315 | } |
| 5336 | } | 5316 | } |
| 5337 | //DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); | 5317 | //DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); |
| @@ -5422,7 +5402,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5422 | } | 5402 | } |
| 5423 | if (inst_cream->Rd == 15) { | 5403 | if (inst_cream->Rd == 15) { |
| 5424 | INC_PC(sizeof(mul_inst)); | 5404 | INC_PC(sizeof(mul_inst)); |
| 5425 | goto PROFILING; | 5405 | goto DISPATCH; |
| 5426 | } | 5406 | } |
| 5427 | } | 5407 | } |
| 5428 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5408 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5451,7 +5431,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5451 | } | 5431 | } |
| 5452 | if (inst_cream->Rd == 15) { | 5432 | if (inst_cream->Rd == 15) { |
| 5453 | INC_PC(sizeof(mvn_inst)); | 5433 | INC_PC(sizeof(mvn_inst)); |
| 5454 | goto PROFILING; | 5434 | goto DISPATCH; |
| 5455 | } | 5435 | } |
| 5456 | } | 5436 | } |
| 5457 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5437 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5483,7 +5463,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5483 | } | 5463 | } |
| 5484 | if (inst_cream->Rd == 15) { | 5464 | if (inst_cream->Rd == 15) { |
| 5485 | INC_PC(sizeof(orr_inst)); | 5465 | INC_PC(sizeof(orr_inst)); |
| 5486 | goto PROFILING; | 5466 | goto DISPATCH; |
| 5487 | } | 5467 | } |
| 5488 | } | 5468 | } |
| 5489 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5469 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5522,7 +5502,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5522 | (((RM >> 16) & 0xff) << 8) | | 5502 | (((RM >> 16) & 0xff) << 8) | |
| 5523 | ((RM >> 24) & 0xff); | 5503 | ((RM >> 24) & 0xff); |
| 5524 | if (inst_cream->Rm == 15) { | 5504 | if (inst_cream->Rm == 15) { |
| 5525 | DEBUG_LOG(ARM11, "in line %d\n", __LINE__); | 5505 | LOG_ERROR(Core_ARM11, "invalid operand for REV"); |
| 5526 | CITRA_IGNORE_EXIT(-1); | 5506 | CITRA_IGNORE_EXIT(-1); |
| 5527 | } | 5507 | } |
| 5528 | } | 5508 | } |
| @@ -5575,7 +5555,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5575 | } | 5555 | } |
| 5576 | if (inst_cream->Rd == 15) { | 5556 | if (inst_cream->Rd == 15) { |
| 5577 | INC_PC(sizeof(rsb_inst)); | 5557 | INC_PC(sizeof(rsb_inst)); |
| 5578 | goto PROFILING; | 5558 | goto DISPATCH; |
| 5579 | } | 5559 | } |
| 5580 | } | 5560 | } |
| 5581 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5561 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5612,7 +5592,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5612 | } | 5592 | } |
| 5613 | if (inst_cream->Rd == 15) { | 5593 | if (inst_cream->Rd == 15) { |
| 5614 | INC_PC(sizeof(rsc_inst)); | 5594 | INC_PC(sizeof(rsc_inst)); |
| 5615 | goto PROFILING; | 5595 | goto DISPATCH; |
| 5616 | } | 5596 | } |
| 5617 | } | 5597 | } |
| 5618 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5598 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5653,7 +5633,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5653 | } | 5633 | } |
| 5654 | if (inst_cream->Rd == 15) { | 5634 | if (inst_cream->Rd == 15) { |
| 5655 | INC_PC(sizeof(sbc_inst)); | 5635 | INC_PC(sizeof(sbc_inst)); |
| 5656 | goto PROFILING; | 5636 | goto DISPATCH; |
| 5657 | } | 5637 | } |
| 5658 | } | 5638 | } |
| 5659 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5639 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -5975,7 +5955,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 5975 | sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; | 5955 | sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; |
| 5976 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 5956 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { |
| 5977 | if (inst_cream->Rm == 15) { | 5957 | if (inst_cream->Rm == 15) { |
| 5978 | DEBUG_LOG(ARM11, "line is %d\n", __LINE__); | 5958 | LOG_ERROR(Core_ARM11, "invalid operand for SXTB"); |
| 5979 | CITRA_IGNORE_EXIT(-1); | 5959 | CITRA_IGNORE_EXIT(-1); |
| 5980 | } | 5960 | } |
| 5981 | unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate); | 5961 | unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate); |
| @@ -6066,7 +6046,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6066 | } | 6046 | } |
| 6067 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 6047 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 6068 | //if (BITS(inst_cream->inst, 12, 15) == 15) | 6048 | //if (BITS(inst_cream->inst, 12, 15) == 15) |
| 6069 | // goto PROFILING; | 6049 | // goto DISPATCH; |
| 6070 | INC_PC(sizeof(ldst_inst)); | 6050 | INC_PC(sizeof(ldst_inst)); |
| 6071 | FETCH_INST; | 6051 | FETCH_INST; |
| 6072 | GOTO_NEXT_INST; | 6052 | GOTO_NEXT_INST; |
| @@ -6081,7 +6061,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6081 | uint32_t rear_phys_addr; | 6061 | uint32_t rear_phys_addr; |
| 6082 | fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 0); | 6062 | fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 0); |
| 6083 | if (fault){ | 6063 | if (fault){ |
| 6084 | ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n"); | 6064 | LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n"); |
| 6085 | CITRA_IGNORE_EXIT(-1); | 6065 | CITRA_IGNORE_EXIT(-1); |
| 6086 | goto MMU_EXCEPTION; | 6066 | goto MMU_EXCEPTION; |
| 6087 | } | 6067 | } |
| @@ -6175,7 +6155,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6175 | } | 6155 | } |
| 6176 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 6156 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 6177 | //if (BITS(inst_cream->inst, 12, 15) == 15) | 6157 | //if (BITS(inst_cream->inst, 12, 15) == 15) |
| 6178 | // goto PROFILING; | 6158 | // goto DISPATCH; |
| 6179 | INC_PC(sizeof(ldst_inst)); | 6159 | INC_PC(sizeof(ldst_inst)); |
| 6180 | FETCH_INST; | 6160 | FETCH_INST; |
| 6181 | GOTO_NEXT_INST; | 6161 | GOTO_NEXT_INST; |
| @@ -6225,7 +6205,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6225 | } | 6205 | } |
| 6226 | if (inst_cream->Rd == 15) { | 6206 | if (inst_cream->Rd == 15) { |
| 6227 | INC_PC(sizeof(sub_inst)); | 6207 | INC_PC(sizeof(sub_inst)); |
| 6228 | goto PROFILING; | 6208 | goto DISPATCH; |
| 6229 | } | 6209 | } |
| 6230 | } | 6210 | } |
| 6231 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 6211 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| @@ -6449,7 +6429,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6449 | cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm; | 6429 | cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm; |
| 6450 | //DEBUG_LOG(ARM11, " BL_1_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]); | 6430 | //DEBUG_LOG(ARM11, " BL_1_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]); |
| 6451 | INC_PC(sizeof(b_2_thumb)); | 6431 | INC_PC(sizeof(b_2_thumb)); |
| 6452 | goto PROFILING; | 6432 | goto DISPATCH; |
| 6453 | } | 6433 | } |
| 6454 | B_COND_THUMB: | 6434 | B_COND_THUMB: |
| 6455 | { | 6435 | { |
| @@ -6461,7 +6441,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6461 | cpu->Reg[15] += 2; | 6441 | cpu->Reg[15] += 2; |
| 6462 | //DEBUG_LOG(ARM11, " B_COND_THUMB: imm=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[15]); | 6442 | //DEBUG_LOG(ARM11, " B_COND_THUMB: imm=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[15]); |
| 6463 | INC_PC(sizeof(b_cond_thumb)); | 6443 | INC_PC(sizeof(b_cond_thumb)); |
| 6464 | goto PROFILING; | 6444 | goto DISPATCH; |
| 6465 | } | 6445 | } |
| 6466 | BL_1_THUMB: | 6446 | BL_1_THUMB: |
| 6467 | { | 6447 | { |
| @@ -6487,7 +6467,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6487 | cpu->Reg[14] = tmp; | 6467 | cpu->Reg[14] = tmp; |
| 6488 | //DEBUG_LOG(ARM11, " BL_2_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]); | 6468 | //DEBUG_LOG(ARM11, " BL_2_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]); |
| 6489 | INC_PC(sizeof(bl_2_thumb)); | 6469 | INC_PC(sizeof(bl_2_thumb)); |
| 6490 | goto PROFILING; | 6470 | goto DISPATCH; |
| 6491 | } | 6471 | } |
| 6492 | BLX_1_THUMB: | 6472 | BLX_1_THUMB: |
| 6493 | { | 6473 | { |
| @@ -6503,7 +6483,7 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6503 | cpu->TFlag = 0; | 6483 | cpu->TFlag = 0; |
| 6504 | //DEBUG_LOG(ARM11, "In BLX_1_THUMB, BLX(1),imm=0x%x,r14=0x%x, r15=0x%x, \n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]); | 6484 | //DEBUG_LOG(ARM11, "In BLX_1_THUMB, BLX(1),imm=0x%x,r14=0x%x, r15=0x%x, \n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]); |
| 6505 | INC_PC(sizeof(blx_1_thumb)); | 6485 | INC_PC(sizeof(blx_1_thumb)); |
| 6506 | goto PROFILING; | 6486 | goto DISPATCH; |
| 6507 | } | 6487 | } |
| 6508 | 6488 | ||
| 6509 | UQADD16_INST: | 6489 | UQADD16_INST: |
| @@ -6532,12 +6512,14 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6532 | cpu->AbortAddr = addr; | 6512 | cpu->AbortAddr = addr; |
| 6533 | cpu->CP15[CP15(CP15_FAULT_STATUS)] = fault & 0xff; | 6513 | cpu->CP15[CP15(CP15_FAULT_STATUS)] = fault & 0xff; |
| 6534 | cpu->CP15[CP15(CP15_FAULT_ADDRESS)] = addr; | 6514 | cpu->CP15[CP15(CP15_FAULT_ADDRESS)] = addr; |
| 6535 | return; | 6515 | cpu->NumInstrsToExecute = 0; |
| 6516 | return num_instrs; | ||
| 6536 | } | 6517 | } |
| 6537 | END: | 6518 | END: |
| 6538 | { | 6519 | { |
| 6539 | SAVE_NZCVT; | 6520 | SAVE_NZCVT; |
| 6540 | return; | 6521 | cpu->NumInstrsToExecute = 0; |
| 6522 | return num_instrs; | ||
| 6541 | } | 6523 | } |
| 6542 | INIT_INST_LENGTH: | 6524 | INIT_INST_LENGTH: |
| 6543 | { | 6525 | { |
| @@ -6557,7 +6539,8 @@ void InterpreterMainLoop(ARMul_State* state) | |||
| 6557 | DEBUG_LOG(ARM11, "%llx\n", InstLabel[1]); | 6539 | DEBUG_LOG(ARM11, "%llx\n", InstLabel[1]); |
| 6558 | DEBUG_LOG(ARM11, "%lld\n", (char *)InstEndLabel[1] - (char *)InstLabel[1]); | 6540 | DEBUG_LOG(ARM11, "%lld\n", (char *)InstEndLabel[1] - (char *)InstLabel[1]); |
| 6559 | #endif | 6541 | #endif |
| 6560 | return; | 6542 | cpu->NumInstrsToExecute = 0; |
| 6543 | return num_instrs; | ||
| 6561 | } | 6544 | } |
| 6562 | } | 6545 | } |
| 6563 | 6546 | ||
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.h b/src/core/arm/dyncom/arm_dyncom_interpreter.h index d73f8f65f..3a2462f55 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.h +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | void InterpreterMainLoop(ARMul_State* state); | 7 | unsigned InterpreterMainLoop(ARMul_State* state); |
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index ed4415082..e2aa5ce92 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/arm/interpreter/arm_interpreter.h" | 5 | #include "core/arm/interpreter/arm_interpreter.h" |
| 6 | 6 | ||
| @@ -24,7 +24,7 @@ ARM_Interpreter::ARM_Interpreter() { | |||
| 24 | state->lateabtSig = LOW; | 24 | state->lateabtSig = LOW; |
| 25 | 25 | ||
| 26 | // Reset the core to initial state | 26 | // Reset the core to initial state |
| 27 | ARMul_CoProInit(state); | 27 | ARMul_CoProInit(state); |
| 28 | ARMul_Reset(state); | 28 | ARMul_Reset(state); |
| 29 | state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext | 29 | state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext |
| 30 | state->Emulate = 3; | 30 | state->Emulate = 3; |
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index ceb1be438..ed53d997c 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -18,7 +18,7 @@ public: | |||
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| 20 | * Set the Program Counter to an address | 20 | * Set the Program Counter to an address |
| 21 | * @param addr Address to set PC to | 21 | * @param pc Address to set PC to |
| 22 | */ | 22 | */ |
| 23 | void SetPC(u32 pc) override; | 23 | void SetPC(u32 pc) override; |
| 24 | 24 | ||
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 73223874e..1a589e39c 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp | |||
| @@ -949,7 +949,7 @@ ARMul_Emulate26 (ARMul_State * state) | |||
| 949 | //printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp); | 949 | //printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp); |
| 950 | 950 | ||
| 951 | if (armOp == 0xDEADC0DE) { | 951 | if (armOp == 0xDEADC0DE) { |
| 952 | DEBUG("Failed to decode thumb opcode %04X at %08X\n", instr, pc); | 952 | LOG_ERROR(Core_ARM11, "Failed to decode thumb opcode %04X at %08X", instr, pc); |
| 953 | } | 953 | } |
| 954 | 954 | ||
| 955 | instr = armOp; | 955 | instr = armOp; |
| @@ -1166,7 +1166,7 @@ mainswitch: | |||
| 1166 | else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) { | 1166 | else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) { |
| 1167 | //(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m)) | 1167 | //(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m)) |
| 1168 | unsigned msb ,tmp_rn, tmp_rd, dst; | 1168 | unsigned msb ,tmp_rn, tmp_rd, dst; |
| 1169 | msb = tmp_rd = tmp_rn = dst = 0; | 1169 | tmp_rd = tmp_rn = dst = 0; |
| 1170 | Rd = BITS(12, 15); | 1170 | Rd = BITS(12, 15); |
| 1171 | Rn = BITS(0, 3); | 1171 | Rn = BITS(0, 3); |
| 1172 | lsb = BITS(7, 11); | 1172 | lsb = BITS(7, 11); |
| @@ -1737,7 +1737,7 @@ mainswitch: | |||
| 1737 | //chy 2006-02-15 if in user mode, can not set cpsr 0:23 | 1737 | //chy 2006-02-15 if in user mode, can not set cpsr 0:23 |
| 1738 | //from p165 of ARMARM book | 1738 | //from p165 of ARMARM book |
| 1739 | state->Cpsr = GETSPSR (state->Bank); | 1739 | state->Cpsr = GETSPSR (state->Bank); |
| 1740 | //ARMul_CPSRAltered (state); | 1740 | ARMul_CPSRAltered (state); |
| 1741 | #else | 1741 | #else |
| 1742 | rhs = DPRegRHS; | 1742 | rhs = DPRegRHS; |
| 1743 | temp = LHS & rhs; | 1743 | temp = LHS & rhs; |
| @@ -1877,7 +1877,7 @@ mainswitch: | |||
| 1877 | /* TEQP reg */ | 1877 | /* TEQP reg */ |
| 1878 | #ifdef MODE32 | 1878 | #ifdef MODE32 |
| 1879 | state->Cpsr = GETSPSR (state->Bank); | 1879 | state->Cpsr = GETSPSR (state->Bank); |
| 1880 | //ARMul_CPSRAltered (state); | 1880 | ARMul_CPSRAltered (state); |
| 1881 | #else | 1881 | #else |
| 1882 | rhs = DPRegRHS; | 1882 | rhs = DPRegRHS; |
| 1883 | temp = LHS ^ rhs; | 1883 | temp = LHS ^ rhs; |
| @@ -1993,7 +1993,7 @@ mainswitch: | |||
| 1993 | /* CMPP reg. */ | 1993 | /* CMPP reg. */ |
| 1994 | #ifdef MODE32 | 1994 | #ifdef MODE32 |
| 1995 | state->Cpsr = GETSPSR (state->Bank); | 1995 | state->Cpsr = GETSPSR (state->Bank); |
| 1996 | //ARMul_CPSRAltered (state); | 1996 | ARMul_CPSRAltered (state); |
| 1997 | #else | 1997 | #else |
| 1998 | rhs = DPRegRHS; | 1998 | rhs = DPRegRHS; |
| 1999 | temp = LHS - rhs; | 1999 | temp = LHS - rhs; |
| @@ -2112,7 +2112,7 @@ mainswitch: | |||
| 2112 | if (DESTReg == 15) { | 2112 | if (DESTReg == 15) { |
| 2113 | #ifdef MODE32 | 2113 | #ifdef MODE32 |
| 2114 | state->Cpsr = GETSPSR (state->Bank); | 2114 | state->Cpsr = GETSPSR (state->Bank); |
| 2115 | //ARMul_CPSRAltered (state); | 2115 | ARMul_CPSRAltered (state); |
| 2116 | #else | 2116 | #else |
| 2117 | rhs = DPRegRHS; | 2117 | rhs = DPRegRHS; |
| 2118 | temp = LHS + rhs; | 2118 | temp = LHS + rhs; |
| @@ -2200,17 +2200,57 @@ mainswitch: | |||
| 2200 | Handle_Store_Double (state, instr); | 2200 | Handle_Store_Double (state, instr); |
| 2201 | break; | 2201 | break; |
| 2202 | } | 2202 | } |
| 2203 | if (BITS(4, 11) == 0xF9) { //strexd | ||
| 2204 | u32 l = LHSReg; | ||
| 2205 | |||
| 2206 | bool enter = false; | ||
| 2207 | |||
| 2208 | if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr)&& | ||
| 2209 | state->currentexvald == (u32)ARMul_ReadWord(state, state->currentexaddr + 4)) | ||
| 2210 | enter = true; | ||
| 2211 | |||
| 2212 | |||
| 2213 | //todo bug this and STREXD and LDREXD http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDGJGGC.html | ||
| 2214 | |||
| 2215 | |||
| 2216 | if (enter) { | ||
| 2217 | ARMul_StoreWordN(state, LHS, state->Reg[RHSReg]); | ||
| 2218 | ARMul_StoreWordN(state,LHS + 4 , state->Reg[RHSReg + 1]); | ||
| 2219 | state->Reg[DESTReg] = 0; | ||
| 2220 | } else { | ||
| 2221 | state->Reg[DESTReg] = 1; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | break; | ||
| 2225 | } | ||
| 2203 | #endif | 2226 | #endif |
| 2204 | dest = DPRegRHS; | 2227 | dest = DPRegRHS; |
| 2205 | WRITEDEST (dest); | 2228 | WRITEDEST (dest); |
| 2206 | break; | 2229 | break; |
| 2207 | 2230 | ||
| 2208 | case 0x1b: /* MOVS reg */ | 2231 | case 0x1B: /* MOVS reg */ |
| 2209 | #ifdef MODET | 2232 | #ifdef MODET |
| 2233 | /* ldrexd ichfly */ | ||
| 2234 | if (BITS(0, 11) == 0xF9F) { //strexd | ||
| 2235 | lhs = LHS; | ||
| 2236 | |||
| 2237 | state->currentexaddr = lhs; | ||
| 2238 | state->currentexval = (u32)ARMul_ReadWord(state, lhs); | ||
| 2239 | state->currentexvald = (u32)ARMul_ReadWord(state, lhs + 4); | ||
| 2240 | |||
| 2241 | state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs); | ||
| 2242 | state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs + 4); | ||
| 2243 | break; | ||
| 2244 | } | ||
| 2245 | |||
| 2210 | if ((BITS (4, 11) & 0xF9) == 0x9) | 2246 | if ((BITS (4, 11) & 0xF9) == 0x9) |
| 2211 | /* LDR register offset, write-back, up, pre indexed. */ | 2247 | /* LDR register offset, write-back, up, pre indexed. */ |
| 2212 | LHPREUPWB (); | 2248 | LHPREUPWB (); |
| 2213 | /* Continue with remaining instruction decoding. */ | 2249 | /* Continue with remaining instruction decoding. */ |
| 2250 | |||
| 2251 | |||
| 2252 | |||
| 2253 | |||
| 2214 | #endif | 2254 | #endif |
| 2215 | dest = DPSRegRHS; | 2255 | dest = DPSRegRHS; |
| 2216 | WRITESDEST (dest); | 2256 | WRITESDEST (dest); |
| @@ -2297,12 +2337,12 @@ mainswitch: | |||
| 2297 | if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true; | 2337 | if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true; |
| 2298 | 2338 | ||
| 2299 | 2339 | ||
| 2300 | ARMul_StoreHalfWord(state, lhs, RHS); | ||
| 2301 | //StoreWord(state, lhs, RHS) | 2340 | //StoreWord(state, lhs, RHS) |
| 2302 | if (state->Aborted) { | 2341 | if (state->Aborted) { |
| 2303 | TAKEABORT; | 2342 | TAKEABORT; |
| 2304 | } | 2343 | } |
| 2305 | if (enter) { | 2344 | if (enter) { |
| 2345 | ARMul_StoreHalfWord(state, lhs, RHS); | ||
| 2306 | state->Reg[DESTReg] = 0; | 2346 | state->Reg[DESTReg] = 0; |
| 2307 | } else { | 2347 | } else { |
| 2308 | state->Reg[DESTReg] = 1; | 2348 | state->Reg[DESTReg] = 1; |
| @@ -2520,7 +2560,7 @@ mainswitch: | |||
| 2520 | /* TSTP immed. */ | 2560 | /* TSTP immed. */ |
| 2521 | #ifdef MODE32 | 2561 | #ifdef MODE32 |
| 2522 | state->Cpsr = GETSPSR (state->Bank); | 2562 | state->Cpsr = GETSPSR (state->Bank); |
| 2523 | //ARMul_CPSRAltered (state); | 2563 | ARMul_CPSRAltered (state); |
| 2524 | #else | 2564 | #else |
| 2525 | temp = LHS & DPImmRHS; | 2565 | temp = LHS & DPImmRHS; |
| 2526 | SETR15PSR (temp); | 2566 | SETR15PSR (temp); |
| @@ -2547,7 +2587,7 @@ mainswitch: | |||
| 2547 | /* TEQP immed. */ | 2587 | /* TEQP immed. */ |
| 2548 | #ifdef MODE32 | 2588 | #ifdef MODE32 |
| 2549 | state->Cpsr = GETSPSR (state->Bank); | 2589 | state->Cpsr = GETSPSR (state->Bank); |
| 2550 | //ARMul_CPSRAltered (state); | 2590 | ARMul_CPSRAltered (state); |
| 2551 | #else | 2591 | #else |
| 2552 | temp = LHS ^ DPImmRHS; | 2592 | temp = LHS ^ DPImmRHS; |
| 2553 | SETR15PSR (temp); | 2593 | SETR15PSR (temp); |
| @@ -2568,7 +2608,7 @@ mainswitch: | |||
| 2568 | /* CMPP immed. */ | 2608 | /* CMPP immed. */ |
| 2569 | #ifdef MODE32 | 2609 | #ifdef MODE32 |
| 2570 | state->Cpsr = GETSPSR (state->Bank); | 2610 | state->Cpsr = GETSPSR (state->Bank); |
| 2571 | //ARMul_CPSRAltered (state); | 2611 | ARMul_CPSRAltered (state); |
| 2572 | #else | 2612 | #else |
| 2573 | temp = LHS - DPImmRHS; | 2613 | temp = LHS - DPImmRHS; |
| 2574 | SETR15PSR (temp); | 2614 | SETR15PSR (temp); |
| @@ -2604,7 +2644,7 @@ mainswitch: | |||
| 2604 | /* CMNP immed. */ | 2644 | /* CMNP immed. */ |
| 2605 | #ifdef MODE32 | 2645 | #ifdef MODE32 |
| 2606 | state->Cpsr = GETSPSR (state->Bank); | 2646 | state->Cpsr = GETSPSR (state->Bank); |
| 2607 | //ARMul_CPSRAltered (state); | 2647 | ARMul_CPSRAltered (state); |
| 2608 | #else | 2648 | #else |
| 2609 | temp = LHS + DPImmRHS; | 2649 | temp = LHS + DPImmRHS; |
| 2610 | SETR15PSR (temp); | 2650 | SETR15PSR (temp); |
| @@ -3055,17 +3095,14 @@ mainswitch: | |||
| 3055 | 3095 | ||
| 3056 | case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */ | 3096 | case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */ |
| 3057 | //ichfly PKHBT PKHTB todo check this | 3097 | //ichfly PKHBT PKHTB todo check this |
| 3058 | if ((instr & 0x70) == 0x10) //pkhbt | 3098 | if ((instr & 0x70) == 0x10) { //pkhbt |
| 3059 | { | ||
| 3060 | u8 idest = BITS(12, 15); | 3099 | u8 idest = BITS(12, 15); |
| 3061 | u8 rfis = BITS(16, 19); | 3100 | u8 rfis = BITS(16, 19); |
| 3062 | u8 rlast = BITS(0, 3); | 3101 | u8 rlast = BITS(0, 3); |
| 3063 | u8 ishi = BITS(7,11); | 3102 | u8 ishi = BITS(7,11); |
| 3064 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); | 3103 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); |
| 3065 | break; | 3104 | break; |
| 3066 | } | 3105 | } else if ((instr & 0x70) == 0x50) { //pkhtb |
| 3067 | else if ((instr & 0x70) == 0x50)//pkhtb | ||
| 3068 | { | ||
| 3069 | u8 idest = BITS(12, 15); | 3106 | u8 idest = BITS(12, 15); |
| 3070 | u8 rfis = BITS(16, 19); | 3107 | u8 rfis = BITS(16, 19); |
| 3071 | u8 rlast = BITS(0, 3); | 3108 | u8 rlast = BITS(0, 3); |
| @@ -3073,8 +3110,7 @@ mainswitch: | |||
| 3073 | if (ishi == 0)ishi = 0x20; | 3110 | if (ishi == 0)ishi = 0x20; |
| 3074 | state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000); | 3111 | state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000); |
| 3075 | break; | 3112 | break; |
| 3076 | } | 3113 | } else if (BIT (4)) { |
| 3077 | else if (BIT (4)) { | ||
| 3078 | #ifdef MODE32 | 3114 | #ifdef MODE32 |
| 3079 | if (state->is_v6 | 3115 | if (state->is_v6 |
| 3080 | && handle_v6_insn (state, instr)) | 3116 | && handle_v6_insn (state, instr)) |
| @@ -3437,7 +3473,7 @@ mainswitch: | |||
| 3437 | 3473 | ||
| 3438 | case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */ | 3474 | case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */ |
| 3439 | if (BIT (4)) { | 3475 | if (BIT (4)) { |
| 3440 | DEBUG("got unhandled special breakpoint\n"); | 3476 | LOG_DEBUG(Core_ARM11, "got unhandled special breakpoint"); |
| 3441 | return 1; | 3477 | return 1; |
| 3442 | } | 3478 | } |
| 3443 | UNDEF_LSRBaseEQOffWb; | 3479 | UNDEF_LSRBaseEQOffWb; |
| @@ -3686,13 +3722,11 @@ mainswitch: | |||
| 3686 | 3722 | ||
| 3687 | /* Co-Processor Data Transfers. */ | 3723 | /* Co-Processor Data Transfers. */ |
| 3688 | case 0xc4: | 3724 | case 0xc4: |
| 3689 | if ((instr & 0x0FF00FF0) == 0xC400B10) //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0 | 3725 | if ((instr & 0x0FF00FF0) == 0xC400B10) { //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0 |
| 3690 | { | ||
| 3691 | state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)]; | 3726 | state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)]; |
| 3692 | state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)]; | 3727 | state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)]; |
| 3693 | break; | 3728 | break; |
| 3694 | } | 3729 | } else if (state->is_v5) { |
| 3695 | else if (state->is_v5) { | ||
| 3696 | /* Reading from R15 is UNPREDICTABLE. */ | 3730 | /* Reading from R15 is UNPREDICTABLE. */ |
| 3697 | if (BITS (12, 15) == 15 || BITS (16, 19) == 15) | 3731 | if (BITS (12, 15) == 15 || BITS (16, 19) == 15) |
| 3698 | ARMul_UndefInstr (state, instr); | 3732 | ARMul_UndefInstr (state, instr); |
| @@ -3712,22 +3746,18 @@ mainswitch: | |||
| 3712 | break; | 3746 | break; |
| 3713 | 3747 | ||
| 3714 | case 0xc5: | 3748 | case 0xc5: |
| 3715 | if ((instr & 0x00000FF0) == 0xB10) //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0 | 3749 | if ((instr & 0x00000FF0) == 0xB10) { //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0 |
| 3716 | { | ||
| 3717 | state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1]; | 3750 | state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1]; |
| 3718 | state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1]; | 3751 | state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1]; |
| 3719 | break; | 3752 | break; |
| 3720 | } | 3753 | } else if (state->is_v5) { |
| 3721 | else if (state->is_v5) { | ||
| 3722 | /* Writes to R15 are UNPREDICATABLE. */ | 3754 | /* Writes to R15 are UNPREDICATABLE. */ |
| 3723 | if (DESTReg == 15 || LHSReg == 15) | 3755 | if (DESTReg == 15 || LHSReg == 15) |
| 3724 | ARMul_UndefInstr (state, instr); | 3756 | ARMul_UndefInstr (state, instr); |
| 3725 | /* Is access to the coprocessor allowed ? */ | 3757 | /* Is access to the coprocessor allowed ? */ |
| 3726 | else if (!CP_ACCESS_ALLOWED(state, CPNum)) | 3758 | else if (!CP_ACCESS_ALLOWED(state, CPNum)) { |
| 3727 | { | ||
| 3728 | ARMul_UndefInstr(state, instr); | 3759 | ARMul_UndefInstr(state, instr); |
| 3729 | } | 3760 | } else { |
| 3730 | else { | ||
| 3731 | /* MRRC, ARMv5TE and up */ | 3761 | /* MRRC, ARMv5TE and up */ |
| 3732 | ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg])); | 3762 | ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg])); |
| 3733 | break; | 3763 | break; |
| @@ -4565,7 +4595,7 @@ out: | |||
| 4565 | #ifdef MODE32 | 4595 | #ifdef MODE32 |
| 4566 | if (state->Bank > 0) { | 4596 | if (state->Bank > 0) { |
| 4567 | state->Cpsr = state->Spsr[state->Bank]; | 4597 | state->Cpsr = state->Spsr[state->Bank]; |
| 4568 | //ARMul_CPSRAltered (state); | 4598 | ARMul_CPSRAltered (state); |
| 4569 | } | 4599 | } |
| 4570 | #ifdef MODET | 4600 | #ifdef MODET |
| 4571 | if (TFLAG) | 4601 | if (TFLAG) |
| @@ -5256,7 +5286,7 @@ L_ldm_s_makeabort: | |||
| 5256 | //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode | 5286 | //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode |
| 5257 | if (state->Mode != USER26MODE && state->Mode != USER32MODE ) { | 5287 | if (state->Mode != USER26MODE && state->Mode != USER32MODE ) { |
| 5258 | state->Cpsr = GETSPSR (state->Bank); | 5288 | state->Cpsr = GETSPSR (state->Bank); |
| 5259 | //ARMul_CPSRAltered (state); | 5289 | ARMul_CPSRAltered (state); |
| 5260 | } | 5290 | } |
| 5261 | 5291 | ||
| 5262 | WriteR15 (state, PC); | 5292 | WriteR15 (state, PC); |
| @@ -5641,30 +5671,9 @@ L_stm_s_takeabort: | |||
| 5641 | 5671 | ||
| 5642 | static int | 5672 | static int |
| 5643 | handle_v6_insn (ARMul_State * state, ARMword instr) { | 5673 | handle_v6_insn (ARMul_State * state, ARMword instr) { |
| 5644 | switch (BITS (20, 27)) { | 5674 | ARMword lhs, temp; |
| 5645 | //ichfly | ||
| 5646 | case 0x66: //UQSUB8 | ||
| 5647 | if ((instr & 0x0FF00FF0) == 0x06600FF0) { | ||
| 5648 | u32 rd = (instr >> 12) & 0xF; | ||
| 5649 | u32 rm = (instr >> 16) & 0xF; | ||
| 5650 | u32 rn = (instr >> 0) & 0xF; | ||
| 5651 | u32 subfrom = state->Reg[rm]; | ||
| 5652 | u32 tosub = state->Reg[rn]; | ||
| 5653 | 5675 | ||
| 5654 | u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub)); | 5676 | switch (BITS (20, 27)) { |
| 5655 | if (b1 > (u8)(subfrom)) b1 = 0; | ||
| 5656 | u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8)); | ||
| 5657 | if (b2 > (u8)(subfrom >> 8)) b2 = 0; | ||
| 5658 | u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16)); | ||
| 5659 | if (b3 > (u8)(subfrom >> 16)) b3 = 0; | ||
| 5660 | u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24)); | ||
| 5661 | if (b4 > (u8)(subfrom >> 24)) b4 = 0; | ||
| 5662 | state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24); | ||
| 5663 | return 1; | ||
| 5664 | } else { | ||
| 5665 | printf("UQSUB8 decoding fail %08X",instr); | ||
| 5666 | } | ||
| 5667 | #if 0 | ||
| 5668 | case 0x03: | 5677 | case 0x03: |
| 5669 | printf ("Unhandled v6 insn: ldr\n"); | 5678 | printf ("Unhandled v6 insn: ldr\n"); |
| 5670 | break; | 5679 | break; |
| @@ -5678,9 +5687,43 @@ L_stm_s_takeabort: | |||
| 5678 | printf ("Unhandled v6 insn: smi\n"); | 5687 | printf ("Unhandled v6 insn: smi\n"); |
| 5679 | break; | 5688 | break; |
| 5680 | case 0x18: | 5689 | case 0x18: |
| 5690 | if (BITS(4, 7) == 0x9) { | ||
| 5691 | /* strex */ | ||
| 5692 | u32 l = LHSReg; | ||
| 5693 | u32 r = RHSReg; | ||
| 5694 | lhs = LHS; | ||
| 5695 | |||
| 5696 | bool enter = false; | ||
| 5697 | |||
| 5698 | if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true; | ||
| 5699 | //StoreWord(state, lhs, RHS) | ||
| 5700 | if (state->Aborted) { | ||
| 5701 | TAKEABORT; | ||
| 5702 | } | ||
| 5703 | |||
| 5704 | if (enter) { | ||
| 5705 | ARMul_StoreWordS(state, lhs, RHS); | ||
| 5706 | state->Reg[DESTReg] = 0; | ||
| 5707 | } | ||
| 5708 | else { | ||
| 5709 | state->Reg[DESTReg] = 1; | ||
| 5710 | } | ||
| 5711 | |||
| 5712 | return 1; | ||
| 5713 | } | ||
| 5681 | printf ("Unhandled v6 insn: strex\n"); | 5714 | printf ("Unhandled v6 insn: strex\n"); |
| 5682 | break; | 5715 | break; |
| 5683 | case 0x19: | 5716 | case 0x19: |
| 5717 | /* ldrex */ | ||
| 5718 | if (BITS(4, 7) == 0x9) { | ||
| 5719 | lhs = LHS; | ||
| 5720 | |||
| 5721 | state->currentexaddr = lhs; | ||
| 5722 | state->currentexval = ARMul_ReadWord(state, lhs); | ||
| 5723 | |||
| 5724 | LoadWord(state, instr, lhs); | ||
| 5725 | return 1; | ||
| 5726 | } | ||
| 5684 | printf ("Unhandled v6 insn: ldrex\n"); | 5727 | printf ("Unhandled v6 insn: ldrex\n"); |
| 5685 | break; | 5728 | break; |
| 5686 | case 0x1a: | 5729 | case 0x1a: |
| @@ -5690,9 +5733,52 @@ L_stm_s_takeabort: | |||
| 5690 | printf ("Unhandled v6 insn: ldrexd\n"); | 5733 | printf ("Unhandled v6 insn: ldrexd\n"); |
| 5691 | break; | 5734 | break; |
| 5692 | case 0x1c: | 5735 | case 0x1c: |
| 5736 | if (BITS(4, 7) == 0x9) { | ||
| 5737 | /* strexb */ | ||
| 5738 | lhs = LHS; | ||
| 5739 | |||
| 5740 | bool enter = false; | ||
| 5741 | |||
| 5742 | if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true; | ||
| 5743 | |||
| 5744 | BUSUSEDINCPCN; | ||
| 5745 | if (state->Aborted) { | ||
| 5746 | TAKEABORT; | ||
| 5747 | } | ||
| 5748 | |||
| 5749 | |||
| 5750 | if (enter) { | ||
| 5751 | ARMul_StoreByte(state, lhs, RHS); | ||
| 5752 | state->Reg[DESTReg] = 0; | ||
| 5753 | } | ||
| 5754 | else { | ||
| 5755 | state->Reg[DESTReg] = 1; | ||
| 5756 | } | ||
| 5757 | |||
| 5758 | //printf("In %s, strexb not implemented\n", __FUNCTION__); | ||
| 5759 | UNDEF_LSRBPC; | ||
| 5760 | /* WRITESDEST (dest); */ | ||
| 5761 | return 1; | ||
| 5762 | } | ||
| 5693 | printf ("Unhandled v6 insn: strexb\n"); | 5763 | printf ("Unhandled v6 insn: strexb\n"); |
| 5694 | break; | 5764 | break; |
| 5695 | case 0x1d: | 5765 | case 0x1d: |
| 5766 | if ((BITS(4, 7)) == 0x9) { | ||
| 5767 | /* ldrexb */ | ||
| 5768 | temp = LHS; | ||
| 5769 | LoadByte(state, instr, temp, LUNSIGNED); | ||
| 5770 | |||
| 5771 | state->currentexaddr = temp; | ||
| 5772 | state->currentexval = (u32)ARMul_ReadByte(state, temp); | ||
| 5773 | |||
| 5774 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); | ||
| 5775 | //printf("ldrexb\n"); | ||
| 5776 | //printf("instr is %x rm is %d\n", instr, BITS(16, 19)); | ||
| 5777 | //exit(-1); | ||
| 5778 | |||
| 5779 | //printf("In %s, ldrexb not implemented\n", __FUNCTION__); | ||
| 5780 | return 1; | ||
| 5781 | } | ||
| 5696 | printf ("Unhandled v6 insn: ldrexb\n"); | 5782 | printf ("Unhandled v6 insn: ldrexb\n"); |
| 5697 | break; | 5783 | break; |
| 5698 | case 0x1e: | 5784 | case 0x1e: |
| @@ -5713,10 +5799,8 @@ L_stm_s_takeabort: | |||
| 5713 | case 0x3f: | 5799 | case 0x3f: |
| 5714 | printf ("Unhandled v6 insn: rbit\n"); | 5800 | printf ("Unhandled v6 insn: rbit\n"); |
| 5715 | break; | 5801 | break; |
| 5716 | #endif | ||
| 5717 | case 0x61: | 5802 | case 0x61: |
| 5718 | if ((instr & 0xFF0) == 0xf70)//ssub16 | 5803 | if ((instr & 0xFF0) == 0xf70) { //ssub16 |
| 5719 | { | ||
| 5720 | u8 tar = BITS(12, 15); | 5804 | u8 tar = BITS(12, 15); |
| 5721 | u8 src1 = BITS(16, 19); | 5805 | u8 src1 = BITS(16, 19); |
| 5722 | u8 src2 = BITS(0, 3); | 5806 | u8 src2 = BITS(0, 3); |
| @@ -5724,23 +5808,20 @@ L_stm_s_takeabort: | |||
| 5724 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5808 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); |
| 5725 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5809 | s16 b1 = (state->Reg[src2] & 0xFFFF); |
| 5726 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5810 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); |
| 5727 | state->Reg[tar] = (a1 - a2)&0xFFFF | (((b1 - b2)&0xFFFF)<< 0x10); | 5811 | state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10); |
| 5728 | return 1; | 5812 | return 1; |
| 5729 | } | 5813 | } else if ((instr & 0xFF0) == 0xf10) { //sadd16 |
| 5730 | else if ((instr & 0xFF0) == 0xf10)//sadd16 | 5814 | const u8 rd_idx = BITS(12, 15); |
| 5731 | { | 5815 | const u8 rm_idx = BITS(0, 3); |
| 5732 | u8 tar = BITS(12, 15); | 5816 | const u8 rn_idx = BITS(16, 19); |
| 5733 | u8 src1 = BITS(16, 19); | 5817 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); |
| 5734 | u8 src2 = BITS(0, 3); | 5818 | const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF); |
| 5735 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5819 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5736 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5820 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); |
| 5737 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5821 | |
| 5738 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5822 | state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16); |
| 5739 | state->Reg[tar] = (a1 + a2)&0xFFFF | (((b1 + b2)&0xFFFF)<< 0x10); | ||
| 5740 | return 1; | 5823 | return 1; |
| 5741 | } | 5824 | } else if ((instr & 0xFF0) == 0xf50) { //ssax |
| 5742 | else if ((instr & 0xFF0) == 0xf50)//ssax | ||
| 5743 | { | ||
| 5744 | u8 tar = BITS(12, 15); | 5825 | u8 tar = BITS(12, 15); |
| 5745 | u8 src1 = BITS(16, 19); | 5826 | u8 src1 = BITS(16, 19); |
| 5746 | u8 src2 = BITS(0, 3); | 5827 | u8 src2 = BITS(0, 3); |
| @@ -5748,11 +5829,9 @@ L_stm_s_takeabort: | |||
| 5748 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5829 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); |
| 5749 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5830 | s16 b1 = (state->Reg[src2] & 0xFFFF); |
| 5750 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5831 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); |
| 5751 | state->Reg[tar] = (a1 - b2) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); | 5832 | state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10); |
| 5752 | return 1; | 5833 | return 1; |
| 5753 | } | 5834 | } else if ((instr & 0xFF0) == 0xf30) { //sasx |
| 5754 | else if ((instr & 0xFF0) == 0xf30)//sasx | ||
| 5755 | { | ||
| 5756 | u8 tar = BITS(12, 15); | 5835 | u8 tar = BITS(12, 15); |
| 5757 | u8 src1 = BITS(16, 19); | 5836 | u8 src1 = BITS(16, 19); |
| 5758 | u8 src2 = BITS(0, 3); | 5837 | u8 src2 = BITS(0, 3); |
| @@ -5760,127 +5839,445 @@ L_stm_s_takeabort: | |||
| 5760 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5839 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); |
| 5761 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5840 | s16 b1 = (state->Reg[src2] & 0xFFFF); |
| 5762 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5841 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); |
| 5763 | state->Reg[tar] = (a2 - b1) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); | 5842 | state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10); |
| 5764 | return 1; | 5843 | return 1; |
| 5765 | } | 5844 | } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n"); |
| 5766 | else printf ("Unhandled v6 insn: sadd/ssub\n"); | ||
| 5767 | break; | 5845 | break; |
| 5768 | case 0x62: | 5846 | case 0x62: // QSUB16 and QADD16 |
| 5769 | if ((instr & 0xFF0) == 0xf70)//QSUB16 | 5847 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) { |
| 5770 | { | 5848 | const u8 rd_idx = BITS(12, 15); |
| 5771 | u8 tar = BITS(12, 15); | 5849 | const u8 rn_idx = BITS(16, 19); |
| 5772 | u8 src1 = BITS(16, 19); | 5850 | const u8 rm_idx = BITS(0, 3); |
| 5773 | u8 src2 = BITS(0, 3); | 5851 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); |
| 5774 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5852 | const s16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF); |
| 5775 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5853 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5776 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5854 | const s16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF); |
| 5777 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5855 | |
| 5778 | s32 res1 = (a1 - b1); | 5856 | s32 lo_result; |
| 5779 | s32 res2 = (a2 - b2); | 5857 | s32 hi_result; |
| 5780 | if (res1 > 0x7FFF) res1 = 0x7FFF; | 5858 | |
| 5781 | if (res2 > 0x7FFF) res2 = 0x7FFF; | 5859 | // QSUB16 |
| 5782 | if (res1 < 0x7FFF) res1 = -0x8000; | 5860 | if ((instr & 0xFF0) == 0xf70) { |
| 5783 | if (res2 < 0x7FFF) res2 = -0x8000; | 5861 | lo_result = (rn_lo - rm_lo); |
| 5784 | state->Reg[tar] = (res1 & 0xFFFF) | ((res2 & 0xFFFF) << 0x10); | 5862 | hi_result = (rn_hi - rm_hi); |
| 5785 | return 1; | 5863 | } |
| 5786 | } | 5864 | else { // QADD16 |
| 5787 | else if ((instr & 0xFF0) == 0xf10)//QADD16 | 5865 | lo_result = (rn_lo + rm_lo); |
| 5788 | { | 5866 | hi_result = (rn_hi + rm_hi); |
| 5789 | u8 tar = BITS(12, 15); | 5867 | } |
| 5790 | u8 src1 = BITS(16, 19); | 5868 | |
| 5791 | u8 src2 = BITS(0, 3); | 5869 | if (lo_result > 0x7FFF) |
| 5792 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5870 | lo_result = 0x7FFF; |
| 5793 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5871 | else if (lo_result < -0x8000) |
| 5794 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5872 | lo_result = -0x8000; |
| 5795 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5873 | |
| 5796 | s32 res1 = (a1 + b1); | 5874 | if (hi_result > 0x7FFF) |
| 5797 | s32 res2 = (a2 + b2); | 5875 | hi_result = 0x7FFF; |
| 5798 | if (res1 > 0x7FFF) res1 = 0x7FFF; | 5876 | else if (hi_result < -0x8000) |
| 5799 | if (res2 > 0x7FFF) res2 = 0x7FFF; | 5877 | hi_result = -0x8000; |
| 5800 | if (res1 < 0x7FFF) res1 = -0x8000; | 5878 | |
| 5801 | if (res2 < 0x7FFF) res2 = -0x8000; | 5879 | state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); |
| 5802 | state->Reg[tar] = ((res1) & 0xFFFF) | (((res2) & 0xFFFF) << 0x10); | ||
| 5803 | return 1; | 5880 | return 1; |
| 5881 | } else { | ||
| 5882 | printf("Unhandled v6 insn: %08x", BITS(20, 27)); | ||
| 5804 | } | 5883 | } |
| 5805 | else printf ("Unhandled v6 insn: qadd/qsub\n"); | ||
| 5806 | break; | 5884 | break; |
| 5807 | #if 0 | ||
| 5808 | case 0x63: | 5885 | case 0x63: |
| 5809 | printf ("Unhandled v6 insn: shadd/shsub\n"); | 5886 | printf ("Unhandled v6 insn: shadd/shsub\n"); |
| 5810 | break; | 5887 | break; |
| 5811 | case 0x65: | 5888 | case 0x65: |
| 5812 | printf ("Unhandled v6 insn: uadd/usub\n"); | 5889 | { |
| 5890 | u32 rd = (instr >> 12) & 0xF; | ||
| 5891 | u32 rn = (instr >> 16) & 0xF; | ||
| 5892 | u32 rm = (instr >> 0) & 0xF; | ||
| 5893 | u32 from = state->Reg[rn]; | ||
| 5894 | u32 to = state->Reg[rm]; | ||
| 5895 | |||
| 5896 | if ((instr & 0xFF0) == 0xF10 || (instr & 0xFF0) == 0xF70) { // UADD16/USUB16 | ||
| 5897 | u32 h1, h2; | ||
| 5898 | state->Cpsr &= 0xfff0ffff; | ||
| 5899 | if ((instr & 0x0F0) == 0x070) { // USUB16 | ||
| 5900 | h1 = ((u16)from - (u16)to); | ||
| 5901 | h2 = ((u16)(from >> 16) - (u16)(to >> 16)); | ||
| 5902 | if (!(h1 & 0xffff0000)) state->Cpsr |= (3 << 16); | ||
| 5903 | if (!(h2 & 0xffff0000)) state->Cpsr |= (3 << 18); | ||
| 5904 | } | ||
| 5905 | else { // UADD16 | ||
| 5906 | h1 = ((u16)from + (u16)to); | ||
| 5907 | h2 = ((u16)(from >> 16) + (u16)(to >> 16)); | ||
| 5908 | if (h1 & 0xffff0000) state->Cpsr |= (3 << 16); | ||
| 5909 | if (h2 & 0xffff0000) state->Cpsr |= (3 << 18); | ||
| 5910 | } | ||
| 5911 | state->Reg[rd] = (u32)((h1 & 0xffff) | ((h2 & 0xffff) << 16)); | ||
| 5912 | return 1; | ||
| 5913 | } | ||
| 5914 | else | ||
| 5915 | if ((instr & 0xFF0) == 0xF90 || (instr & 0xFF0) == 0xFF0) { // UADD8/USUB8 | ||
| 5916 | u32 b1, b2, b3, b4; | ||
| 5917 | state->Cpsr &= 0xfff0ffff; | ||
| 5918 | if ((instr & 0x0F0) == 0x0F0) { // USUB8 | ||
| 5919 | b1 = ((u8)from - (u8)to); | ||
| 5920 | b2 = ((u8)(from >> 8) - (u8)(to >> 8)); | ||
| 5921 | b3 = ((u8)(from >> 16) - (u8)(to >> 16)); | ||
| 5922 | b4 = ((u8)(from >> 24) - (u8)(to >> 24)); | ||
| 5923 | if (!(b1 & 0xffffff00)) state->Cpsr |= (1 << 16); | ||
| 5924 | if (!(b2 & 0xffffff00)) state->Cpsr |= (1 << 17); | ||
| 5925 | if (!(b3 & 0xffffff00)) state->Cpsr |= (1 << 18); | ||
| 5926 | if (!(b4 & 0xffffff00)) state->Cpsr |= (1 << 19); | ||
| 5927 | } | ||
| 5928 | else { // UADD8 | ||
| 5929 | b1 = ((u8)from + (u8)to); | ||
| 5930 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); | ||
| 5931 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); | ||
| 5932 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); | ||
| 5933 | if (b1 & 0xffffff00) state->Cpsr |= (1 << 16); | ||
| 5934 | if (b2 & 0xffffff00) state->Cpsr |= (1 << 17); | ||
| 5935 | if (b3 & 0xffffff00) state->Cpsr |= (1 << 18); | ||
| 5936 | if (b4 & 0xffffff00) state->Cpsr |= (1 << 19); | ||
| 5937 | } | ||
| 5938 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); | ||
| 5939 | return 1; | ||
| 5940 | } | ||
| 5941 | } | ||
| 5942 | printf("Unhandled v6 insn: uasx/usax\n"); | ||
| 5813 | break; | 5943 | break; |
| 5814 | case 0x66: | 5944 | case 0x66: |
| 5815 | printf ("Unhandled v6 insn: uqadd/uqsub\n"); | 5945 | if ((instr & 0x0FF00FF0) == 0x06600FF0) { //uqsub8 |
| 5946 | u32 rd = (instr >> 12) & 0xF; | ||
| 5947 | u32 rm = (instr >> 16) & 0xF; | ||
| 5948 | u32 rn = (instr >> 0) & 0xF; | ||
| 5949 | u32 subfrom = state->Reg[rm]; | ||
| 5950 | u32 tosub = state->Reg[rn]; | ||
| 5951 | |||
| 5952 | u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub)); | ||
| 5953 | if (b1 > (u8)(subfrom)) b1 = 0; | ||
| 5954 | u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8)); | ||
| 5955 | if (b2 > (u8)(subfrom >> 8)) b2 = 0; | ||
| 5956 | u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16)); | ||
| 5957 | if (b3 > (u8)(subfrom >> 16)) b3 = 0; | ||
| 5958 | u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24)); | ||
| 5959 | if (b4 > (u8)(subfrom >> 24)) b4 = 0; | ||
| 5960 | state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24); | ||
| 5961 | return 1; | ||
| 5962 | } else { | ||
| 5963 | printf ("Unhandled v6 insn: uqsub16\n"); | ||
| 5964 | } | ||
| 5816 | break; | 5965 | break; |
| 5817 | case 0x67: | 5966 | case 0x67: |
| 5818 | printf ("Unhandled v6 insn: uhadd/uhsub\n"); | 5967 | printf ("Unhandled v6 insn: uhadd/uhsub\n"); |
| 5819 | break; | 5968 | break; |
| 5820 | case 0x68: | 5969 | case 0x68: |
| 5821 | printf ("Unhandled v6 insn: pkh/sxtab/selsxtb\n"); | 5970 | { |
| 5822 | break; | 5971 | u32 rd = (instr >> 12) & 0xF; |
| 5823 | #endif | 5972 | u32 rn = (instr >> 16) & 0xF; |
| 5824 | case 0x6c: | 5973 | u32 rm = (instr >> 0) & 0xF; |
| 5825 | if ((instr & 0xf03f0) == 0xf0070) //uxtb16 | 5974 | u32 from = state->Reg[rn]; |
| 5826 | { | 5975 | u32 to = state->Reg[rm]; |
| 5827 | u8 src1 = BITS(0, 3); | 5976 | u32 cpsr = state->Cpsr; |
| 5828 | u8 tar = BITS(12, 15); | 5977 | if ((instr & 0xFF0) == 0xFB0) { // SEL |
| 5829 | u32 base = state->Reg[src1]; | 5978 | u32 result; |
| 5830 | u32 shamt = BITS(9,10)* 8; | 5979 | if (cpsr & (1 << 16)) |
| 5831 | u32 in = ((base << (32 - shamt)) | (base >> shamt)); | 5980 | result = from & 0xff; |
| 5832 | state->Reg[tar] = in & 0x00FF00FF; | 5981 | else |
| 5982 | result = to & 0xff; | ||
| 5983 | if (cpsr & (1 << 17)) | ||
| 5984 | result |= from & 0x0000ff00; | ||
| 5985 | else | ||
| 5986 | result |= to & 0x0000ff00; | ||
| 5987 | if (cpsr & (1 << 18)) | ||
| 5988 | result |= from & 0x00ff0000; | ||
| 5989 | else | ||
| 5990 | result |= to & 0x00ff0000; | ||
| 5991 | if (cpsr & (1 << 19)) | ||
| 5992 | result |= from & 0xff000000; | ||
| 5993 | else | ||
| 5994 | result |= to & 0xff000000; | ||
| 5995 | state->Reg[rd] = result; | ||
| 5833 | return 1; | 5996 | return 1; |
| 5834 | } | 5997 | } |
| 5835 | else | 5998 | } |
| 5836 | printf ("Unhandled v6 insn: uxtb16/uxtab16\n"); | 5999 | printf("Unhandled v6 insn: pkh/sxtab/selsxtb\n"); |
| 5837 | break; | 6000 | break; |
| 6001 | case 0x6a: { | ||
| 6002 | ARMword Rm; | ||
| 6003 | int ror = -1; | ||
| 6004 | |||
| 6005 | switch (BITS(4, 11)) { | ||
| 6006 | case 0x07: | ||
| 6007 | ror = 0; | ||
| 6008 | break; | ||
| 6009 | case 0x47: | ||
| 6010 | ror = 8; | ||
| 6011 | break; | ||
| 6012 | case 0x87: | ||
| 6013 | ror = 16; | ||
| 6014 | break; | ||
| 6015 | case 0xc7: | ||
| 6016 | ror = 24; | ||
| 6017 | break; | ||
| 6018 | |||
| 6019 | case 0x01: | ||
| 6020 | case 0xf3: | ||
| 6021 | //ichfly | ||
| 6022 | //SSAT16 | ||
| 6023 | { | ||
| 6024 | u8 tar = BITS(12, 15); | ||
| 6025 | u8 src = BITS(0, 3); | ||
| 6026 | u8 val = BITS(16, 19) + 1; | ||
| 6027 | s16 a1 = (state->Reg[src]); | ||
| 6028 | s16 a2 = (state->Reg[src] >> 0x10); | ||
| 6029 | s16 min = (s16)(0x8000 >> (16 - val)); | ||
| 6030 | s16 max = 0x7FFF >> (16 - val); | ||
| 6031 | if (min > a1) a1 = min; | ||
| 6032 | if (max < a1) a1 = max; | ||
| 6033 | if (min > a2) a2 = min; | ||
| 6034 | if (max < a2) a2 = max; | ||
| 6035 | u32 temp2 = ((u32)(a2)) << 0x10; | ||
| 6036 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | ||
| 6037 | } | ||
| 6038 | |||
| 6039 | return 1; | ||
| 6040 | default: | ||
| 6041 | break; | ||
| 6042 | } | ||
| 6043 | |||
| 6044 | if (ror == -1) { | ||
| 6045 | if (BITS(4, 6) == 0x7) { | ||
| 6046 | printf("Unhandled v6 insn: ssat\n"); | ||
| 6047 | return 0; | ||
| 6048 | } | ||
| 6049 | break; | ||
| 6050 | } | ||
| 6051 | |||
| 6052 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | ||
| 6053 | if (Rm & 0x80) | ||
| 6054 | Rm |= 0xffffff00; | ||
| 6055 | |||
| 6056 | if (BITS(16, 19) == 0xf) | ||
| 6057 | /* SXTB */ | ||
| 6058 | state->Reg[BITS(12, 15)] = Rm; | ||
| 6059 | else | ||
| 6060 | /* SXTAB */ | ||
| 6061 | state->Reg[BITS(12, 15)] += Rm; | ||
| 6062 | |||
| 6063 | return 1; | ||
| 6064 | } | ||
| 6065 | case 0x6b: { | ||
| 6066 | ARMword Rm; | ||
| 6067 | int ror = -1; | ||
| 6068 | |||
| 6069 | switch (BITS(4, 11)) { | ||
| 6070 | case 0x07: | ||
| 6071 | ror = 0; | ||
| 6072 | break; | ||
| 6073 | case 0x47: | ||
| 6074 | ror = 8; | ||
| 6075 | break; | ||
| 6076 | case 0x87: | ||
| 6077 | ror = 16; | ||
| 6078 | break; | ||
| 6079 | case 0xc7: | ||
| 6080 | ror = 24; | ||
| 6081 | break; | ||
| 6082 | |||
| 6083 | case 0xf3: | ||
| 6084 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); | ||
| 6085 | return 1; | ||
| 6086 | case 0xfb: | ||
| 6087 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); | ||
| 6088 | return 1; | ||
| 6089 | default: | ||
| 6090 | break; | ||
| 6091 | } | ||
| 6092 | |||
| 6093 | if (ror == -1) | ||
| 6094 | break; | ||
| 6095 | |||
| 6096 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | ||
| 6097 | if (Rm & 0x8000) | ||
| 6098 | Rm |= 0xffff0000; | ||
| 6099 | |||
| 6100 | if (BITS(16, 19) == 0xf) | ||
| 6101 | /* SXTH */ | ||
| 6102 | state->Reg[BITS(12, 15)] = Rm; | ||
| 6103 | else | ||
| 6104 | /* SXTAH */ | ||
| 6105 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; | ||
| 6106 | |||
| 6107 | return 1; | ||
| 6108 | } | ||
| 6109 | case 0x6c: // UXTB16 and UXTAB16 | ||
| 6110 | { | ||
| 6111 | const u8 rm_idx = BITS(0, 3); | ||
| 6112 | const u8 rn_idx = BITS(16, 19); | ||
| 6113 | const u8 rd_idx = BITS(12, 15); | ||
| 6114 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 6115 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 6116 | const u32 rotation = BITS(10, 11) * 8; | ||
| 6117 | const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation)); | ||
| 6118 | |||
| 6119 | // UXTB16 | ||
| 6120 | if ((instr & 0xf03f0) == 0xf0070) { | ||
| 6121 | state->Reg[rd_idx] = rotated_rm & 0x00FF00FF; | ||
| 6122 | } | ||
| 6123 | else { // UXTAB16 | ||
| 6124 | const u8 lo_rotated = (rotated_rm & 0xFF); | ||
| 6125 | const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated; | ||
| 6126 | |||
| 6127 | const u8 hi_rotated = (rotated_rm >> 16) & 0xFF; | ||
| 6128 | const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated; | ||
| 6129 | |||
| 6130 | state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF)); | ||
| 6131 | } | ||
| 6132 | |||
| 6133 | return 1; | ||
| 6134 | } | ||
| 6135 | break; | ||
| 6136 | case 0x6e: { | ||
| 6137 | ARMword Rm; | ||
| 6138 | int ror = -1; | ||
| 6139 | |||
| 6140 | switch (BITS(4, 11)) { | ||
| 6141 | case 0x07: | ||
| 6142 | ror = 0; | ||
| 6143 | break; | ||
| 6144 | case 0x47: | ||
| 6145 | ror = 8; | ||
| 6146 | break; | ||
| 6147 | case 0x87: | ||
| 6148 | ror = 16; | ||
| 6149 | break; | ||
| 6150 | case 0xc7: | ||
| 6151 | ror = 24; | ||
| 6152 | break; | ||
| 6153 | |||
| 6154 | case 0x01: | ||
| 6155 | case 0xf3: | ||
| 6156 | //ichfly | ||
| 6157 | //USAT16 | ||
| 6158 | { | ||
| 6159 | u8 tar = BITS(12, 15); | ||
| 6160 | u8 src = BITS(0, 3); | ||
| 6161 | u8 val = BITS(16, 19); | ||
| 6162 | s16 a1 = (state->Reg[src]); | ||
| 6163 | s16 a2 = (state->Reg[src] >> 0x10); | ||
| 6164 | s16 max = 0xFFFF >> (16 - val); | ||
| 6165 | if (max < a1) a1 = max; | ||
| 6166 | if (max < a2) a2 = max; | ||
| 6167 | u32 temp2 = ((u32)(a2)) << 0x10; | ||
| 6168 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | ||
| 6169 | } | ||
| 6170 | return 1; | ||
| 6171 | default: | ||
| 6172 | break; | ||
| 6173 | } | ||
| 6174 | |||
| 6175 | if (ror == -1) { | ||
| 6176 | if (BITS(4, 6) == 0x7) { | ||
| 6177 | printf("Unhandled v6 insn: usat\n"); | ||
| 6178 | return 0; | ||
| 6179 | } | ||
| 6180 | break; | ||
| 6181 | } | ||
| 6182 | |||
| 6183 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | ||
| 6184 | |||
| 6185 | if (BITS(16, 19) == 0xf) | ||
| 6186 | /* UXTB */ | ||
| 6187 | state->Reg[BITS(12, 15)] = Rm; | ||
| 6188 | else | ||
| 6189 | /* UXTAB */ | ||
| 6190 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; | ||
| 6191 | |||
| 6192 | return 1; | ||
| 6193 | } | ||
| 6194 | |||
| 6195 | case 0x6f: { | ||
| 6196 | ARMword Rm; | ||
| 6197 | int ror = -1; | ||
| 6198 | |||
| 6199 | switch (BITS(4, 11)) { | ||
| 6200 | case 0x07: | ||
| 6201 | ror = 0; | ||
| 6202 | break; | ||
| 6203 | case 0x47: | ||
| 6204 | ror = 8; | ||
| 6205 | break; | ||
| 6206 | case 0x87: | ||
| 6207 | ror = 16; | ||
| 6208 | break; | ||
| 6209 | case 0xc7: | ||
| 6210 | ror = 24; | ||
| 6211 | break; | ||
| 6212 | |||
| 6213 | case 0xfb: | ||
| 6214 | printf("Unhandled v6 insn: revsh\n"); | ||
| 6215 | return 0; | ||
| 6216 | default: | ||
| 6217 | break; | ||
| 6218 | } | ||
| 6219 | |||
| 6220 | if (ror == -1) | ||
| 6221 | break; | ||
| 6222 | |||
| 6223 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | ||
| 6224 | |||
| 6225 | /* UXT */ | ||
| 6226 | /* state->Reg[BITS (12, 15)] = Rm; */ | ||
| 6227 | /* dyf add */ | ||
| 6228 | if (BITS(16, 19) == 0xf) { | ||
| 6229 | state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF; | ||
| 6230 | } | ||
| 6231 | else { | ||
| 6232 | /* UXTAH */ | ||
| 6233 | /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */ | ||
| 6234 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] | ||
| 6235 | // , Rm, BITS(10, 11)); | ||
| 6236 | // printf("icounter is %lld\n", state->NumInstrs); | ||
| 6237 | state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm; | ||
| 6238 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); | ||
| 6239 | // exit(-1); | ||
| 6240 | } | ||
| 6241 | |||
| 6242 | return 1; | ||
| 6243 | } | ||
| 5838 | case 0x70: | 6244 | case 0x70: |
| 5839 | if ((instr & 0xf0d0) == 0xf010)//smuad //ichfly | 6245 | // ichfly |
| 5840 | { | 6246 | // SMUAD, SMUSD, SMLAD |
| 5841 | u8 tar = BITS(16, 19); | 6247 | if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || (instr & 0xd0) == 0x10) { |
| 5842 | u8 src1 = BITS(0, 3); | 6248 | const u8 rd_idx = BITS(16, 19); |
| 5843 | u8 src2 = BITS(8, 11); | 6249 | const u8 rn_idx = BITS(0, 3); |
| 5844 | u8 swap = BIT(5); | 6250 | const u8 rm_idx = BITS(8, 11); |
| 5845 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6251 | const bool do_swap = (BIT(5) == 1); |
| 5846 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6252 | |
| 5847 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6253 | u32 rm_val = state->Reg[rm_idx]; |
| 5848 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6254 | const u32 rn_val = state->Reg[rn_idx]; |
| 5849 | state->Reg[tar] = a1*a2 + b1*b2; | 6255 | |
| 5850 | return 1; | 6256 | if (do_swap) |
| 5851 | 6257 | rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16)); | |
| 5852 | } | 6258 | |
| 5853 | else if ((instr & 0xf0d0) == 0xf050)//smusd | 6259 | const s16 rm_lo = (rm_val & 0xFFFF); |
| 5854 | { | 6260 | const s16 rm_hi = ((rm_val >> 16) & 0xFFFF); |
| 5855 | u8 tar = BITS(16, 19); | 6261 | const s16 rn_lo = (rn_val & 0xFFFF); |
| 5856 | u8 src1 = BITS(0, 3); | 6262 | const s16 rn_hi = ((rn_val >> 16) & 0xFFFF); |
| 5857 | u8 src2 = BITS(8, 11); | 6263 | |
| 5858 | u8 swap = BIT(5); | 6264 | // SMUAD |
| 5859 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6265 | if ((instr & 0xf0d0) == 0xf010) { |
| 5860 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6266 | state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi); |
| 5861 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6267 | } |
| 5862 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6268 | // SMUSD |
| 5863 | state->Reg[tar] = a1*a2 - b1*b2; | 6269 | else if ((instr & 0xf0d0) == 0xf050) { |
| 5864 | return 1; | 6270 | state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi); |
| 5865 | } | 6271 | } |
| 5866 | else if ((instr & 0xd0) == 0x10)//smlad | 6272 | // SMLAD |
| 5867 | { | 6273 | else { |
| 5868 | u8 tar = BITS(16, 19); | 6274 | const u8 ra_idx = BITS(12, 15); |
| 5869 | u8 src1 = BITS(0, 3); | 6275 | state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx]; |
| 5870 | u8 src2 = BITS(8, 11); | 6276 | } |
| 5871 | u8 src3 = BITS(12, 15); | ||
| 5872 | u8 swap = BIT(5); | ||
| 5873 | |||
| 5874 | u32 a3 = state->Reg[src3]; | ||
| 5875 | |||
| 5876 | s16 a1 = (state->Reg[src1] & 0xFFFF); | ||
| 5877 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | ||
| 5878 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | ||
| 5879 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | ||
| 5880 | state->Reg[tar] = a1*a2 + b1*b2 + a3; | ||
| 5881 | return 1; | 6277 | return 1; |
| 6278 | } else { | ||
| 6279 | printf ("Unhandled v6 insn: smlsd\n"); | ||
| 5882 | } | 6280 | } |
| 5883 | else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n"); | ||
| 5884 | break; | 6281 | break; |
| 5885 | case 0x74: | 6282 | case 0x74: |
| 5886 | printf ("Unhandled v6 insn: smlald/smlsld\n"); | 6283 | printf ("Unhandled v6 insn: smlald/smlsld\n"); |
| @@ -5891,332 +6288,18 @@ L_stm_s_takeabort: | |||
| 5891 | case 0x78: | 6288 | case 0x78: |
| 5892 | printf ("Unhandled v6 insn: usad/usada8\n"); | 6289 | printf ("Unhandled v6 insn: usad/usada8\n"); |
| 5893 | break; | 6290 | break; |
| 5894 | #if 0 | ||
| 5895 | case 0x7a: | 6291 | case 0x7a: |
| 5896 | printf ("Unhandled v6 insn: usbfx\n"); | 6292 | printf ("Unhandled v6 insn: usbfx\n"); |
| 5897 | break; | 6293 | break; |
| 5898 | case 0x7c: | 6294 | case 0x7c: |
| 5899 | printf ("Unhandled v6 insn: bfc/bfi\n"); | 6295 | printf ("Unhandled v6 insn: bfc/bfi\n"); |
| 5900 | break; | 6296 | break; |
| 5901 | #endif | ||
| 5902 | |||
| 5903 | |||
| 5904 | /* add new instr for arm v6. */ | ||
| 5905 | ARMword lhs, temp; | ||
| 5906 | case 0x18: { /* ORR reg */ | ||
| 5907 | /* dyf add armv6 instr strex 2010.9.17 */ | ||
| 5908 | if (BITS (4, 7) == 0x9) { | ||
| 5909 | u32 l = LHSReg; | ||
| 5910 | u32 r = RHSReg; | ||
| 5911 | lhs = LHS; | ||
| 5912 | |||
| 5913 | bool enter = false; | ||
| 5914 | |||
| 5915 | if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true; | ||
| 5916 | ARMul_StoreWordS(state, lhs, RHS); | ||
| 5917 | //StoreWord(state, lhs, RHS) | ||
| 5918 | if (state->Aborted) { | ||
| 5919 | TAKEABORT; | ||
| 5920 | } | ||
| 5921 | |||
| 5922 | if (enter) { | ||
| 5923 | state->Reg[DESTReg] = 0; | ||
| 5924 | } else { | ||
| 5925 | state->Reg[DESTReg] = 1; | ||
| 5926 | } | ||
| 5927 | |||
| 5928 | return 1; | ||
| 5929 | } | ||
| 5930 | break; | ||
| 5931 | } | ||
| 5932 | |||
| 5933 | case 0x19: { /* orrs reg */ | ||
| 5934 | /* dyf add armv6 instr ldrex */ | ||
| 5935 | if (BITS (4, 7) == 0x9) { | ||
| 5936 | lhs = LHS; | ||
| 5937 | |||
| 5938 | state->currentexaddr = lhs; | ||
| 5939 | state->currentexval = ARMul_ReadWord(state, lhs); | ||
| 5940 | |||
| 5941 | LoadWord (state, instr, lhs); | ||
| 5942 | return 1; | ||
| 5943 | } | ||
| 5944 | break; | ||
| 5945 | } | ||
| 5946 | |||
| 5947 | case 0x1c: { /* BIC reg */ | ||
| 5948 | /* dyf add for STREXB */ | ||
| 5949 | if (BITS (4, 7) == 0x9) { | ||
| 5950 | lhs = LHS; | ||
| 5951 | |||
| 5952 | bool enter = false; | ||
| 5953 | |||
| 5954 | if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true; | ||
| 5955 | |||
| 5956 | ARMul_StoreByte (state, lhs, RHS); | ||
| 5957 | BUSUSEDINCPCN; | ||
| 5958 | if (state->Aborted) { | ||
| 5959 | TAKEABORT; | ||
| 5960 | } | ||
| 5961 | |||
| 5962 | |||
| 5963 | if (enter) { | ||
| 5964 | state->Reg[DESTReg] = 0; | ||
| 5965 | } else { | ||
| 5966 | state->Reg[DESTReg] = 1; | ||
| 5967 | } | ||
| 5968 | |||
| 5969 | //printf("In %s, strexb not implemented\n", __FUNCTION__); | ||
| 5970 | UNDEF_LSRBPC; | ||
| 5971 | /* WRITESDEST (dest); */ | ||
| 5972 | return 1; | ||
| 5973 | } | ||
| 5974 | break; | ||
| 5975 | } | ||
| 5976 | |||
| 5977 | case 0x1d: { /* BICS reg */ | ||
| 5978 | if ((BITS (4, 7)) == 0x9) { | ||
| 5979 | /* ldrexb */ | ||
| 5980 | temp = LHS; | ||
| 5981 | LoadByte (state, instr, temp, LUNSIGNED); | ||
| 5982 | |||
| 5983 | state->currentexaddr = temp; | ||
| 5984 | state->currentexval = (u32)ARMul_ReadByte(state, temp); | ||
| 5985 | |||
| 5986 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); | ||
| 5987 | //printf("ldrexb\n"); | ||
| 5988 | //printf("instr is %x rm is %d\n", instr, BITS(16, 19)); | ||
| 5989 | //exit(-1); | ||
| 5990 | |||
| 5991 | //printf("In %s, ldrexb not implemented\n", __FUNCTION__); | ||
| 5992 | return 1; | ||
| 5993 | } | ||
| 5994 | break; | ||
| 5995 | } | ||
| 5996 | /* add end */ | ||
| 5997 | |||
| 5998 | case 0x6a: { | ||
| 5999 | ARMword Rm; | ||
| 6000 | int ror = -1; | ||
| 6001 | |||
| 6002 | switch (BITS (4, 11)) { | ||
| 6003 | case 0x07: | ||
| 6004 | ror = 0; | ||
| 6005 | break; | ||
| 6006 | case 0x47: | ||
| 6007 | ror = 8; | ||
| 6008 | break; | ||
| 6009 | case 0x87: | ||
| 6010 | ror = 16; | ||
| 6011 | break; | ||
| 6012 | case 0xc7: | ||
| 6013 | ror = 24; | ||
| 6014 | break; | ||
| 6015 | |||
| 6016 | case 0x01: | ||
| 6017 | case 0xf3: | ||
| 6018 | //ichfly | ||
| 6019 | //SSAT16 | ||
| 6020 | { | ||
| 6021 | u8 tar = BITS(12,15); | ||
| 6022 | u8 src = BITS(0, 3); | ||
| 6023 | u8 val = BITS(16, 19) + 1; | ||
| 6024 | s16 a1 = (state->Reg[src]); | ||
| 6025 | s16 a2 = (state->Reg[src] >> 0x10); | ||
| 6026 | s16 min = (s16)(0x8000) >> (16 - val); | ||
| 6027 | s16 max = 0x7FFF >> (16 - val); | ||
| 6028 | if (min > a1) a1 = min; | ||
| 6029 | if (max < a1) a1 = max; | ||
| 6030 | if (min > a2) a2 = min; | ||
| 6031 | if (max < a2) a2 = max; | ||
| 6032 | u32 temp2 = ((u32)(a2)) << 0x10; | ||
| 6033 | state->Reg[tar] = (a1&0xFFFF) | (temp2); | ||
| 6034 | } | ||
| 6035 | |||
| 6036 | return 1; | ||
| 6037 | default: | ||
| 6038 | break; | ||
| 6039 | } | ||
| 6040 | |||
| 6041 | if (ror == -1) { | ||
| 6042 | if (BITS (4, 6) == 0x7) { | ||
| 6043 | printf ("Unhandled v6 insn: ssat\n"); | ||
| 6044 | return 0; | ||
| 6045 | } | ||
| 6046 | break; | ||
| 6047 | } | ||
| 6048 | |||
| 6049 | Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF); | ||
| 6050 | if (Rm & 0x80) | ||
| 6051 | Rm |= 0xffffff00; | ||
| 6052 | |||
| 6053 | if (BITS (16, 19) == 0xf) | ||
| 6054 | /* SXTB */ | ||
| 6055 | state->Reg[BITS (12, 15)] = Rm; | ||
| 6056 | else | ||
| 6057 | /* SXTAB */ | ||
| 6058 | state->Reg[BITS (12, 15)] += Rm; | ||
| 6059 | } | ||
| 6060 | return 1; | ||
| 6061 | |||
| 6062 | case 0x6b: { | ||
| 6063 | ARMword Rm; | ||
| 6064 | int ror = -1; | ||
| 6065 | |||
| 6066 | switch (BITS (4, 11)) { | ||
| 6067 | case 0x07: | ||
| 6068 | ror = 0; | ||
| 6069 | break; | ||
| 6070 | case 0x47: | ||
| 6071 | ror = 8; | ||
| 6072 | break; | ||
| 6073 | case 0x87: | ||
| 6074 | ror = 16; | ||
| 6075 | break; | ||
| 6076 | case 0xc7: | ||
| 6077 | ror = 24; | ||
| 6078 | break; | ||
| 6079 | |||
| 6080 | case 0xf3: | ||
| 6081 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); | ||
| 6082 | return 1; | ||
| 6083 | case 0xfb: | ||
| 6084 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); | ||
| 6085 | return 1; | ||
| 6086 | default: | ||
| 6087 | break; | ||
| 6088 | } | ||
| 6089 | |||
| 6090 | if (ror == -1) | ||
| 6091 | break; | ||
| 6092 | |||
| 6093 | Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF); | ||
| 6094 | if (Rm & 0x8000) | ||
| 6095 | Rm |= 0xffff0000; | ||
| 6096 | |||
| 6097 | if (BITS (16, 19) == 0xf) | ||
| 6098 | /* SXTH */ | ||
| 6099 | state->Reg[BITS (12, 15)] = Rm; | ||
| 6100 | else | ||
| 6101 | /* SXTAH */ | ||
| 6102 | state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm; | ||
| 6103 | } | ||
| 6104 | return 1; | ||
| 6105 | |||
| 6106 | case 0x6e: { | ||
| 6107 | ARMword Rm; | ||
| 6108 | int ror = -1; | ||
| 6109 | |||
| 6110 | switch (BITS (4, 11)) { | ||
| 6111 | case 0x07: | ||
| 6112 | ror = 0; | ||
| 6113 | break; | ||
| 6114 | case 0x47: | ||
| 6115 | ror = 8; | ||
| 6116 | break; | ||
| 6117 | case 0x87: | ||
| 6118 | ror = 16; | ||
| 6119 | break; | ||
| 6120 | case 0xc7: | ||
| 6121 | ror = 24; | ||
| 6122 | break; | ||
| 6123 | |||
| 6124 | case 0x01: | ||
| 6125 | case 0xf3: | ||
| 6126 | //ichfly | ||
| 6127 | //USAT16 | ||
| 6128 | { | ||
| 6129 | u8 tar = BITS(12, 15); | ||
| 6130 | u8 src = BITS(0, 3); | ||
| 6131 | u8 val = BITS(16, 19); | ||
| 6132 | s16 a1 = (state->Reg[src]); | ||
| 6133 | s16 a2 = (state->Reg[src] >> 0x10); | ||
| 6134 | s16 max = 0xFFFF >> (16 - val); | ||
| 6135 | if (max < a1) a1 = max; | ||
| 6136 | if (max < a2) a2 = max; | ||
| 6137 | u32 temp2 = ((u32)(a2)) << 0x10; | ||
| 6138 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | ||
| 6139 | } | ||
| 6140 | return 1; | ||
| 6141 | default: | ||
| 6142 | break; | ||
| 6143 | } | ||
| 6144 | |||
| 6145 | if (ror == -1) { | ||
| 6146 | if (BITS (4, 6) == 0x7) { | ||
| 6147 | printf ("Unhandled v6 insn: usat\n"); | ||
| 6148 | return 0; | ||
| 6149 | } | ||
| 6150 | break; | ||
| 6151 | } | ||
| 6152 | |||
| 6153 | Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF); | ||
| 6154 | |||
| 6155 | if (BITS (16, 19) == 0xf) | ||
| 6156 | /* UXTB */ | ||
| 6157 | state->Reg[BITS (12, 15)] = Rm; | ||
| 6158 | else | ||
| 6159 | /* UXTAB */ | ||
| 6160 | state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm; | ||
| 6161 | } | ||
| 6162 | return 1; | ||
| 6163 | |||
| 6164 | case 0x6f: { | ||
| 6165 | ARMword Rm; | ||
| 6166 | int ror = -1; | ||
| 6167 | |||
| 6168 | switch (BITS (4, 11)) { | ||
| 6169 | case 0x07: | ||
| 6170 | ror = 0; | ||
| 6171 | break; | ||
| 6172 | case 0x47: | ||
| 6173 | ror = 8; | ||
| 6174 | break; | ||
| 6175 | case 0x87: | ||
| 6176 | ror = 16; | ||
| 6177 | break; | ||
| 6178 | case 0xc7: | ||
| 6179 | ror = 24; | ||
| 6180 | break; | ||
| 6181 | |||
| 6182 | case 0xfb: | ||
| 6183 | printf ("Unhandled v6 insn: revsh\n"); | ||
| 6184 | return 0; | ||
| 6185 | default: | ||
| 6186 | break; | ||
| 6187 | } | ||
| 6188 | |||
| 6189 | if (ror == -1) | ||
| 6190 | break; | ||
| 6191 | |||
| 6192 | Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF); | ||
| 6193 | |||
| 6194 | /* UXT */ | ||
| 6195 | /* state->Reg[BITS (12, 15)] = Rm; */ | ||
| 6196 | /* dyf add */ | ||
| 6197 | if (BITS (16, 19) == 0xf) { | ||
| 6198 | state->Reg[BITS (12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF; | ||
| 6199 | } else { | ||
| 6200 | /* UXTAH */ | ||
| 6201 | /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */ | ||
| 6202 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] | ||
| 6203 | // , Rm, BITS(10, 11)); | ||
| 6204 | // printf("icounter is %lld\n", state->NumInstrs); | ||
| 6205 | state->Reg[BITS (12, 15)] = (state->Reg[BITS (16, 19)] >> (8 * (BITS(10, 11)))) + Rm; | ||
| 6206 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); | ||
| 6207 | // exit(-1); | ||
| 6208 | } | ||
| 6209 | } | ||
| 6210 | return 1; | ||
| 6211 | |||
| 6212 | #if 0 | ||
| 6213 | case 0x84: | 6297 | case 0x84: |
| 6214 | printf ("Unhandled v6 insn: srs\n"); | 6298 | printf ("Unhandled v6 insn: srs\n"); |
| 6215 | break; | 6299 | break; |
| 6216 | #endif | ||
| 6217 | default: | 6300 | default: |
| 6218 | break; | 6301 | break; |
| 6219 | } | 6302 | } |
| 6220 | printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27)); | 6303 | printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27)); |
| 6221 | return 0; | 6304 | return 0; |
| 6222 | } | 6305 | } \ No newline at end of file |
diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp index 2568b93ef..30519f216 100644 --- a/src/core/arm/interpreter/armsupp.cpp +++ b/src/core/arm/interpreter/armsupp.cpp | |||
| @@ -665,7 +665,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source) | |||
| 665 | //if (!CP_ACCESS_ALLOWED (state, CPNum)) { | 665 | //if (!CP_ACCESS_ALLOWED (state, CPNum)) { |
| 666 | if (!state->MCR[CPNum]) { | 666 | if (!state->MCR[CPNum]) { |
| 667 | //chy 2004-07-19 should fix in the future ????!!!! | 667 | //chy 2004-07-19 should fix in the future ????!!!! |
| 668 | DEBUG("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x\n",CPNum, source); | 668 | LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x",CPNum, source); |
| 669 | ARMul_UndefInstr (state, instr); | 669 | ARMul_UndefInstr (state, instr); |
| 670 | return; | 670 | return; |
| 671 | } | 671 | } |
| @@ -690,7 +690,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source) | |||
| 690 | } | 690 | } |
| 691 | 691 | ||
| 692 | if (cpab == ARMul_CANT) { | 692 | if (cpab == ARMul_CANT) { |
| 693 | DEBUG("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x\n", instr, CPNum, source); //ichfly todo | 693 | LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x", instr, CPNum, source); //ichfly todo |
| 694 | //ARMul_Abort (state, ARMul_UndefinedInstrV); | 694 | //ARMul_Abort (state, ARMul_UndefinedInstrV); |
| 695 | } else { | 695 | } else { |
| 696 | BUSUSEDINCPCN; | 696 | BUSUSEDINCPCN; |
| @@ -762,7 +762,7 @@ ARMword ARMul_MRC (ARMul_State * state, ARMword instr) | |||
| 762 | //if (!CP_ACCESS_ALLOWED (state, CPNum)) { | 762 | //if (!CP_ACCESS_ALLOWED (state, CPNum)) { |
| 763 | if (!state->MRC[CPNum]) { | 763 | if (!state->MRC[CPNum]) { |
| 764 | //chy 2004-07-19 should fix in the future????!!!! | 764 | //chy 2004-07-19 should fix in the future????!!!! |
| 765 | DEBUG("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x\n", CPNum, instr); | 765 | LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x", CPNum, instr); |
| 766 | ARMul_UndefInstr (state, instr); | 766 | ARMul_UndefInstr (state, instr); |
| 767 | return -1; | 767 | return -1; |
| 768 | } | 768 | } |
| @@ -865,7 +865,7 @@ void | |||
| 865 | ARMul_UndefInstr (ARMul_State * state, ARMword instr) | 865 | ARMul_UndefInstr (ARMul_State * state, ARMword instr) |
| 866 | { | 866 | { |
| 867 | std::string disasm = ARM_Disasm::Disassemble(state->pc, instr); | 867 | std::string disasm = ARM_Disasm::Disassemble(state->pc, instr); |
| 868 | ERROR_LOG(ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr); | 868 | LOG_ERROR(Core_ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr); |
| 869 | ARMul_Abort (state, ARMul_UndefinedInstrV); | 869 | ARMul_Abort (state, ARMul_UndefinedInstrV); |
| 870 | } | 870 | } |
| 871 | 871 | ||
diff --git a/src/core/arm/interpreter/thumbemu.cpp b/src/core/arm/interpreter/thumbemu.cpp index f7f11f714..9cf80672d 100644 --- a/src/core/arm/interpreter/thumbemu.cpp +++ b/src/core/arm/interpreter/thumbemu.cpp | |||
| @@ -467,7 +467,7 @@ ARMul_ThumbDecode ( | |||
| 467 | (state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC; | 467 | (state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC; |
| 468 | state->Reg[14] = (tmp | 1); | 468 | state->Reg[14] = (tmp | 1); |
| 469 | CLEART; | 469 | CLEART; |
| 470 | DEBUG_LOG(ARM11, "In %s, After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1); | 470 | LOG_DEBUG(Core_ARM11, "After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x", state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1); |
| 471 | valid = t_branch; | 471 | valid = t_branch; |
| 472 | FLUSHPIPE; | 472 | FLUSHPIPE; |
| 473 | } | 473 | } |
diff --git a/src/core/arm/skyeye_common/armcpu.h b/src/core/arm/skyeye_common/armcpu.h index 3a029f0e7..2b756c5bc 100644 --- a/src/core/arm/skyeye_common/armcpu.h +++ b/src/core/arm/skyeye_common/armcpu.h | |||
| @@ -24,8 +24,6 @@ | |||
| 24 | #include <stddef.h> | 24 | #include <stddef.h> |
| 25 | #include <stdio.h> | 25 | #include <stdio.h> |
| 26 | 26 | ||
| 27 | #include "common/thread.h" | ||
| 28 | |||
| 29 | #include "core/arm/skyeye_common/armdefs.h" | 27 | #include "core/arm/skyeye_common/armdefs.h" |
| 30 | 28 | ||
| 31 | typedef struct ARM_CPU_State_s { | 29 | typedef struct ARM_CPU_State_s { |
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h index 8e71948c6..28a4a0db4 100644 --- a/src/core/arm/skyeye_common/armdefs.h +++ b/src/core/arm/skyeye_common/armdefs.h | |||
| @@ -18,38 +18,26 @@ | |||
| 18 | #ifndef _ARMDEFS_H_ | 18 | #ifndef _ARMDEFS_H_ |
| 19 | #define _ARMDEFS_H_ | 19 | #define _ARMDEFS_H_ |
| 20 | 20 | ||
| 21 | #include <stdio.h> | 21 | #include <cerrno> |
| 22 | #include <stdlib.h> | 22 | #include <csignal> |
| 23 | #include <errno.h> | 23 | #include <cstdio> |
| 24 | 24 | #include <cstdlib> | |
| 25 | #include "common/platform.h" | 25 | #include <cstring> |
| 26 | 26 | #include <fcntl.h> | |
| 27 | //teawater add for arm2x86 2005.02.14------------------------------------------- | 27 | #include <sys/stat.h> |
| 28 | // koodailar remove it for mingw 2005.12.18---------------- | 28 | #include <sys/types.h> |
| 29 | //anthonylee modify it for portable 2007.01.30 | ||
| 30 | //#include "portable/mman.h" | ||
| 31 | 29 | ||
| 32 | #include "arm_regformat.h" | 30 | #include "arm_regformat.h" |
| 31 | #include "common/common_types.h" | ||
| 33 | #include "common/platform.h" | 32 | #include "common/platform.h" |
| 33 | #include "core/arm/skyeye_common/armmmu.h" | ||
| 34 | #include "core/arm/skyeye_common/skyeye_defs.h" | 34 | #include "core/arm/skyeye_common/skyeye_defs.h" |
| 35 | 35 | ||
| 36 | //AJ2D-------------------------------------------------------------------------- | ||
| 37 | |||
| 38 | //teawater add for arm2x86 2005.07.03------------------------------------------- | ||
| 39 | |||
| 40 | #include <sys/types.h> | ||
| 41 | #include <stdio.h> | ||
| 42 | #include <stdlib.h> | ||
| 43 | #include <string.h> | ||
| 44 | #if EMU_PLATFORM == PLATFORM_LINUX | 36 | #if EMU_PLATFORM == PLATFORM_LINUX |
| 37 | #include <sys/time.h> | ||
| 45 | #include <unistd.h> | 38 | #include <unistd.h> |
| 46 | #endif | 39 | #endif |
| 47 | #include <errno.h> | ||
| 48 | #include <sys/stat.h> | ||
| 49 | #include <fcntl.h> | ||
| 50 | 40 | ||
| 51 | //#include <memory_space.h> | ||
| 52 | //AJ2D-------------------------------------------------------------------------- | ||
| 53 | #if 0 | 41 | #if 0 |
| 54 | #if 0 | 42 | #if 0 |
| 55 | #define DIFF_STATE 1 | 43 | #define DIFF_STATE 1 |
| @@ -70,25 +58,8 @@ | |||
| 70 | #define LOWHIGH 1 | 58 | #define LOWHIGH 1 |
| 71 | #define HIGHLOW 2 | 59 | #define HIGHLOW 2 |
| 72 | 60 | ||
| 73 | //teawater add DBCT_TEST_SPEED 2005.10.04--------------------------------------- | ||
| 74 | #include <signal.h> | ||
| 75 | |||
| 76 | #include "common/platform.h" | ||
| 77 | |||
| 78 | #if EMU_PLATFORM == PLATFORM_LINUX | ||
| 79 | #include <sys/time.h> | ||
| 80 | #endif | ||
| 81 | |||
| 82 | //#define DBCT_TEST_SPEED | 61 | //#define DBCT_TEST_SPEED |
| 83 | #define DBCT_TEST_SPEED_SEC 10 | 62 | #define DBCT_TEST_SPEED_SEC 10 |
| 84 | //AJ2D-------------------------------------------------------------------------- | ||
| 85 | |||
| 86 | //teawater add compile switch for DBCT GDB RSP function 2005.10.21-------------- | ||
| 87 | //#define DBCT_GDBRSP | ||
| 88 | //AJ2D-------------------------------------------------------------------------- | ||
| 89 | |||
| 90 | //#include <skyeye_defs.h> | ||
| 91 | //#include <skyeye_types.h> | ||
| 92 | 63 | ||
| 93 | #define ARM_BYTE_TYPE 0 | 64 | #define ARM_BYTE_TYPE 0 |
| 94 | #define ARM_HALFWORD_TYPE 1 | 65 | #define ARM_HALFWORD_TYPE 1 |
| @@ -103,71 +74,34 @@ | |||
| 103 | typedef char *VoidStar; | 74 | typedef char *VoidStar; |
| 104 | #endif | 75 | #endif |
| 105 | 76 | ||
| 106 | typedef unsigned long long ARMdword; /* must be 64 bits wide */ | 77 | typedef u64 ARMdword; // must be 64 bits wide |
| 107 | typedef unsigned int ARMword; /* must be 32 bits wide */ | 78 | typedef u32 ARMword; // must be 32 bits wide |
| 108 | typedef unsigned char ARMbyte; /* must be 8 bits wide */ | 79 | typedef u16 ARMhword; // must be 16 bits wide |
| 109 | typedef unsigned short ARMhword; /* must be 16 bits wide */ | 80 | typedef u8 ARMbyte; // must be 8 bits wide |
| 110 | typedef struct ARMul_State ARMul_State; | 81 | typedef struct ARMul_State ARMul_State; |
| 111 | typedef struct ARMul_io ARMul_io; | 82 | typedef struct ARMul_io ARMul_io; |
| 112 | typedef struct ARMul_Energy ARMul_Energy; | 83 | typedef struct ARMul_Energy ARMul_Energy; |
| 113 | 84 | ||
| 114 | //teawater add for arm2x86 2005.06.24------------------------------------------- | ||
| 115 | #include <stdint.h> | ||
| 116 | //AJ2D-------------------------------------------------------------------------- | ||
| 117 | /* | ||
| 118 | //chy 2005-05-11 | ||
| 119 | #ifndef __CYGWIN__ | ||
| 120 | //teawater add for arm2x86 2005.02.14------------------------------------------- | ||
| 121 | typedef unsigned char uint8_t; | ||
| 122 | typedef unsigned short uint16_t; | ||
| 123 | typedef unsigned int u32; | ||
| 124 | #if defined (__x86_64__) | ||
| 125 | typedef unsigned long uint64_t; | ||
| 126 | #else | ||
| 127 | typedef unsigned long long uint64_t; | ||
| 128 | #endif | ||
| 129 | ////AJ2D-------------------------------------------------------------------------- | ||
| 130 | #endif | ||
| 131 | */ | ||
| 132 | 85 | ||
| 133 | #include "core/arm/skyeye_common/armmmu.h" | 86 | typedef unsigned ARMul_CPInits(ARMul_State* state); |
| 134 | //#include "lcd/skyeye_lcd.h" | 87 | typedef unsigned ARMul_CPExits(ARMul_State* state); |
| 135 | 88 | typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value); | |
| 136 | 89 | typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value); | |
| 137 | //#include "skyeye.h" | 90 | typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value); |
| 138 | //#include "skyeye_device.h" | 91 | typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value); |
| 139 | //#include "net/skyeye_net.h" | 92 | typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2); |
| 140 | //#include "skyeye_config.h" | 93 | typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2); |
| 141 | 94 | typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr); | |
| 142 | 95 | typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value); | |
| 143 | typedef unsigned ARMul_CPInits (ARMul_State * state); | 96 | typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value); |
| 144 | typedef unsigned ARMul_CPExits (ARMul_State * state); | ||
| 145 | typedef unsigned ARMul_LDCs (ARMul_State * state, unsigned type, | ||
| 146 | ARMword instr, ARMword value); | ||
| 147 | typedef unsigned ARMul_STCs (ARMul_State * state, unsigned type, | ||
| 148 | ARMword instr, ARMword * value); | ||
| 149 | typedef unsigned ARMul_MRCs (ARMul_State * state, unsigned type, | ||
| 150 | ARMword instr, ARMword * value); | ||
| 151 | typedef unsigned ARMul_MCRs (ARMul_State * state, unsigned type, | ||
| 152 | ARMword instr, ARMword value); | ||
| 153 | typedef unsigned ARMul_MRRCs (ARMul_State * state, unsigned type, | ||
| 154 | ARMword instr, ARMword * value1, ARMword * value2); | ||
| 155 | typedef unsigned ARMul_MCRRs (ARMul_State * state, unsigned type, | ||
| 156 | ARMword instr, ARMword value1, ARMword value2); | ||
| 157 | typedef unsigned ARMul_CDPs (ARMul_State * state, unsigned type, | ||
| 158 | ARMword instr); | ||
| 159 | typedef unsigned ARMul_CPReads (ARMul_State * state, unsigned reg, | ||
| 160 | ARMword * value); | ||
| 161 | typedef unsigned ARMul_CPWrites (ARMul_State * state, unsigned reg, | ||
| 162 | ARMword value); | ||
| 163 | 97 | ||
| 164 | 98 | ||
| 165 | //added by ksh,2004-3-5 | 99 | //added by ksh,2004-3-5 |
| 166 | struct ARMul_io | 100 | struct ARMul_io |
| 167 | { | 101 | { |
| 168 | ARMword *instr; //to display the current interrupt state | 102 | ARMword *instr; // to display the current interrupt state |
| 169 | ARMword *net_flag; //to judge if network is enabled | 103 | ARMword *net_flag; // to judge if network is enabled |
| 170 | ARMword *net_int; //netcard interrupt | 104 | ARMword *net_int; // netcard interrupt |
| 171 | 105 | ||
| 172 | //ywc,2004-04-01 | 106 | //ywc,2004-04-01 |
| 173 | ARMword *ts_int; | 107 | ARMword *ts_int; |
| @@ -180,17 +114,17 @@ struct ARMul_io | |||
| 180 | /* added by ksh,2004-11-26,some energy profiling */ | 114 | /* added by ksh,2004-11-26,some energy profiling */ |
| 181 | struct ARMul_Energy | 115 | struct ARMul_Energy |
| 182 | { | 116 | { |
| 183 | int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */ | 117 | int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */ |
| 184 | int enable_func_energy; /* <tktan> BUG200105181702 */ | 118 | int enable_func_energy; /* <tktan> BUG200105181702 */ |
| 185 | char *func_energy; | 119 | char *func_energy; |
| 186 | int func_display; /* <tktan> BUG200103311509 : for function call display */ | 120 | int func_display; /* <tktan> BUG200103311509 : for function call display */ |
| 187 | int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */ | 121 | int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */ |
| 188 | char *start_func; /* <tktan> BUG200104191428 */ | 122 | char *start_func; /* <tktan> BUG200104191428 */ |
| 189 | 123 | ||
| 190 | FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */ | 124 | FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */ |
| 191 | long long tcycle, pcycle; | 125 | long long tcycle, pcycle; |
| 192 | float t_energy; | 126 | float t_energy; |
| 193 | void *cur_task; /* <tktan> BUG200103291737 */ | 127 | void *cur_task; /* <tktan> BUG200103291737 */ |
| 194 | long long t_mem_cycle, t_idle_cycle, t_uart_cycle; | 128 | long long t_mem_cycle, t_idle_cycle, t_uart_cycle; |
| 195 | long long p_mem_cycle, p_idle_cycle, p_uart_cycle; | 129 | long long p_mem_cycle, p_idle_cycle, p_uart_cycle; |
| 196 | long long p_io_update_tcycle; | 130 | long long p_io_update_tcycle; |
| @@ -203,13 +137,12 @@ struct ARMul_Energy | |||
| 203 | 137 | ||
| 204 | typedef struct mem_bank | 138 | typedef struct mem_bank |
| 205 | { | 139 | { |
| 206 | ARMword (*read_byte) (ARMul_State * state, ARMword addr); | 140 | ARMword (*read_byte) (ARMul_State* state, ARMword addr); |
| 207 | void (*write_byte) (ARMul_State * state, ARMword addr, ARMword data); | 141 | void (*write_byte) (ARMul_State* state, ARMword addr, ARMword data); |
| 208 | ARMword (*read_halfword) (ARMul_State * state, ARMword addr); | 142 | ARMword (*read_halfword) (ARMul_State* state, ARMword addr); |
| 209 | void (*write_halfword) (ARMul_State * state, ARMword addr, | 143 | void (*write_halfword) (ARMul_State* state, ARMword addr, ARMword data); |
| 210 | ARMword data); | 144 | ARMword (*read_word) (ARMul_State* state, ARMword addr); |
| 211 | ARMword (*read_word) (ARMul_State * state, ARMword addr); | 145 | void (*write_word) (ARMul_State* state, ARMword addr, ARMword data); |
| 212 | void (*write_word) (ARMul_State * state, ARMword addr, ARMword data); | ||
| 213 | unsigned int addr, len; | 146 | unsigned int addr, len; |
| 214 | char filename[MAX_STR]; | 147 | char filename[MAX_STR]; |
| 215 | unsigned type; //chy 2003-09-21: maybe io,ram,rom | 148 | unsigned type; //chy 2003-09-21: maybe io,ram,rom |
| @@ -224,24 +157,24 @@ typedef struct | |||
| 224 | #define VFP_REG_NUM 64 | 157 | #define VFP_REG_NUM 64 |
| 225 | struct ARMul_State | 158 | struct ARMul_State |
| 226 | { | 159 | { |
| 227 | ARMword Emulate; /* to start and stop emulation */ | 160 | ARMword Emulate; /* to start and stop emulation */ |
| 228 | unsigned EndCondition; /* reason for stopping */ | 161 | unsigned EndCondition; /* reason for stopping */ |
| 229 | unsigned ErrorCode; /* type of illegal instruction */ | 162 | unsigned ErrorCode; /* type of illegal instruction */ |
| 230 | 163 | ||
| 231 | /* Order of the following register should not be modified */ | 164 | /* Order of the following register should not be modified */ |
| 232 | ARMword Reg[16]; /* the current register file */ | 165 | ARMword Reg[16]; /* the current register file */ |
| 233 | ARMword Cpsr; /* the current psr */ | 166 | ARMword Cpsr; /* the current psr */ |
| 234 | ARMword Spsr_copy; | 167 | ARMword Spsr_copy; |
| 235 | ARMword phys_pc; | 168 | ARMword phys_pc; |
| 236 | ARMword Reg_usr[2]; | 169 | ARMword Reg_usr[2]; |
| 237 | ARMword Reg_svc[2]; /* R13_SVC R14_SVC */ | 170 | ARMword Reg_svc[2]; /* R13_SVC R14_SVC */ |
| 238 | ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */ | 171 | ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */ |
| 239 | ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */ | 172 | ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */ |
| 240 | ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */ | 173 | ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */ |
| 241 | ARMword Reg_firq[7]; /* R8---R14 FIRQ */ | 174 | ARMword Reg_firq[7]; /* R8---R14 FIRQ */ |
| 242 | ARMword Spsr[7]; /* the exception psr's */ | 175 | ARMword Spsr[7]; /* the exception psr's */ |
| 243 | ARMword Mode; /* the current mode */ | 176 | ARMword Mode; /* the current mode */ |
| 244 | ARMword Bank; /* the current register bank */ | 177 | ARMword Bank; /* the current register bank */ |
| 245 | ARMword exclusive_tag; | 178 | ARMword exclusive_tag; |
| 246 | ARMword exclusive_state; | 179 | ARMword exclusive_state; |
| 247 | ARMword exclusive_result; | 180 | ARMword exclusive_result; |
| @@ -281,38 +214,39 @@ struct ARMul_State | |||
| 281 | 214 | ||
| 282 | ARMword currentexaddr; | 215 | ARMword currentexaddr; |
| 283 | ARMword currentexval; | 216 | ARMword currentexval; |
| 217 | ARMword currentexvald; | ||
| 284 | ARMword servaddr; | 218 | ARMword servaddr; |
| 285 | 219 | ||
| 286 | unsigned NextInstr; | 220 | unsigned NextInstr; |
| 287 | unsigned VectorCatch; /* caught exception mask */ | 221 | unsigned VectorCatch; /* caught exception mask */ |
| 288 | unsigned CallDebug; /* set to call the debugger */ | 222 | unsigned CallDebug; /* set to call the debugger */ |
| 289 | unsigned CanWatch; /* set by memory interface if its willing to suffer the | 223 | unsigned CanWatch; /* set by memory interface if its willing to suffer the |
| 290 | overhead of checking for watchpoints on each memory | 224 | overhead of checking for watchpoints on each memory |
| 291 | access */ | 225 | access */ |
| 292 | unsigned int StopHandle; | 226 | unsigned int StopHandle; |
| 293 | 227 | ||
| 294 | char *CommandLine; /* Command Line from ARMsd */ | 228 | char *CommandLine; /* Command Line from ARMsd */ |
| 295 | 229 | ||
| 296 | ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */ | 230 | ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */ |
| 297 | ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */ | 231 | ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */ |
| 298 | ARMul_LDCs *LDC[16]; /* LDC instruction */ | 232 | ARMul_LDCs *LDC[16]; /* LDC instruction */ |
| 299 | ARMul_STCs *STC[16]; /* STC instruction */ | 233 | ARMul_STCs *STC[16]; /* STC instruction */ |
| 300 | ARMul_MRCs *MRC[16]; /* MRC instruction */ | 234 | ARMul_MRCs *MRC[16]; /* MRC instruction */ |
| 301 | ARMul_MCRs *MCR[16]; /* MCR instruction */ | 235 | ARMul_MCRs *MCR[16]; /* MCR instruction */ |
| 302 | ARMul_MRRCs *MRRC[16]; /* MRRC instruction */ | 236 | ARMul_MRRCs *MRRC[16]; /* MRRC instruction */ |
| 303 | ARMul_MCRRs *MCRR[16]; /* MCRR instruction */ | 237 | ARMul_MCRRs *MCRR[16]; /* MCRR instruction */ |
| 304 | ARMul_CDPs *CDP[16]; /* CDP instruction */ | 238 | ARMul_CDPs *CDP[16]; /* CDP instruction */ |
| 305 | ARMul_CPReads *CPRead[16]; /* Read CP register */ | 239 | ARMul_CPReads *CPRead[16]; /* Read CP register */ |
| 306 | ARMul_CPWrites *CPWrite[16]; /* Write CP register */ | 240 | ARMul_CPWrites *CPWrite[16]; /* Write CP register */ |
| 307 | unsigned char *CPData[16]; /* Coprocessor data */ | 241 | unsigned char *CPData[16]; /* Coprocessor data */ |
| 308 | unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */ | 242 | unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */ |
| 309 | 243 | ||
| 310 | unsigned EventSet; /* the number of events in the queue */ | 244 | unsigned EventSet; /* the number of events in the queue */ |
| 311 | unsigned int Now; /* time to the nearest cycle */ | 245 | unsigned int Now; /* time to the nearest cycle */ |
| 312 | struct EventNode **EventPtr; /* the event list */ | 246 | struct EventNode **EventPtr; /* the event list */ |
| 313 | 247 | ||
| 314 | unsigned Debug; /* show instructions as they are executed */ | 248 | unsigned Debug; /* show instructions as they are executed */ |
| 315 | unsigned NresetSig; /* reset the processor */ | 249 | unsigned NresetSig; /* reset the processor */ |
| 316 | unsigned NfiqSig; | 250 | unsigned NfiqSig; |
| 317 | unsigned NirqSig; | 251 | unsigned NirqSig; |
| 318 | 252 | ||
| @@ -356,12 +290,12 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) | |||
| 356 | */ | 290 | */ |
| 357 | unsigned lateabtSig; | 291 | unsigned lateabtSig; |
| 358 | 292 | ||
| 359 | ARMword Vector; /* synthesize aborts in cycle modes */ | 293 | ARMword Vector; /* synthesize aborts in cycle modes */ |
| 360 | ARMword Aborted; /* sticky flag for aborts */ | 294 | ARMword Aborted; /* sticky flag for aborts */ |
| 361 | ARMword Reseted; /* sticky flag for Reset */ | 295 | ARMword Reseted; /* sticky flag for Reset */ |
| 362 | ARMword Inted, LastInted; /* sticky flags for interrupts */ | 296 | ARMword Inted, LastInted; /* sticky flags for interrupts */ |
| 363 | ARMword Base; /* extra hand for base writeback */ | 297 | ARMword Base; /* extra hand for base writeback */ |
| 364 | ARMword AbortAddr; /* to keep track of Prefetch aborts */ | 298 | ARMword AbortAddr; /* to keep track of Prefetch aborts */ |
| 365 | 299 | ||
| 366 | const struct Dbg_HostosInterface *hostif; | 300 | const struct Dbg_HostosInterface *hostif; |
| 367 | 301 | ||
| @@ -378,7 +312,7 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) | |||
| 378 | //chy: 2003-08-11, for different arm core type | 312 | //chy: 2003-08-11, for different arm core type |
| 379 | unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */ | 313 | unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */ |
| 380 | unsigned is_v5; /* Are we emulating a v5 architecture ? */ | 314 | unsigned is_v5; /* Are we emulating a v5 architecture ? */ |
| 381 | unsigned is_v5e; /* Are we emulating a v5e architecture ? */ | 315 | unsigned is_v5e; /* Are we emulating a v5e architecture ? */ |
| 382 | unsigned is_v6; /* Are we emulating a v6 architecture ? */ | 316 | unsigned is_v6; /* Are we emulating a v6 architecture ? */ |
| 383 | unsigned is_v7; /* Are we emulating a v7 architecture ? */ | 317 | unsigned is_v7; /* Are we emulating a v7 architecture ? */ |
| 384 | unsigned is_XScale; /* Are we emulating an XScale architecture ? */ | 318 | unsigned is_XScale; /* Are we emulating an XScale architecture ? */ |
| @@ -387,51 +321,43 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) | |||
| 387 | //chy 2005-09-19 | 321 | //chy 2005-09-19 |
| 388 | unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */ | 322 | unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */ |
| 389 | //chy: seems only used in xscale's CP14 | 323 | //chy: seems only used in xscale's CP14 |
| 390 | unsigned int LastTime; /* Value of last call to ARMul_Time() */ | 324 | unsigned int LastTime; /* Value of last call to ARMul_Time() */ |
| 391 | ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */ | 325 | ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */ |
| 392 | 326 | ||
| 393 | 327 | ||
| 394 | //added by ksh:for handle different machs io 2004-3-5 | 328 | //added by ksh:for handle different machs io 2004-3-5 |
| 395 | ARMul_io mach_io; | 329 | ARMul_io mach_io; |
| 396 | 330 | ||
| 397 | /*added by ksh,2004-11-26,some energy profiling*/ | 331 | /*added by ksh,2004-11-26,some energy profiling*/ |
| 398 | ARMul_Energy energy; | 332 | ARMul_Energy energy; |
| 399 | 333 | ||
| 400 | //teawater add for next_dis 2004.10.27----------------------- | 334 | //teawater add for next_dis 2004.10.27----------------------- |
| 401 | int disassemble; | 335 | int disassemble; |
| 402 | //AJ2D------------------------------------------ | ||
| 403 | 336 | ||
| 404 | //teawater add for arm2x86 2005.02.15------------------------------------------- | 337 | |
| 338 | //teawater add for arm2x86 2005.02.15------------------------------------------- | ||
| 405 | u32 trap; | 339 | u32 trap; |
| 406 | u32 tea_break_addr; | 340 | u32 tea_break_addr; |
| 407 | u32 tea_break_ok; | 341 | u32 tea_break_ok; |
| 408 | int tea_pc; | 342 | int tea_pc; |
| 409 | //AJ2D-------------------------------------------------------------------------- | ||
| 410 | //teawater add for arm2x86 2005.07.03------------------------------------------- | ||
| 411 | |||
| 412 | /* | ||
| 413 | * 2007-01-24 removed the term-io functions by Anthony Lee, | ||
| 414 | * moved to "device/uart/skyeye_uart_stdio.c". | ||
| 415 | */ | ||
| 416 | 343 | ||
| 417 | //AJ2D-------------------------------------------------------------------------- | 344 | //teawater add for arm2x86 2005.07.05------------------------------------------- |
| 418 | //teawater add for arm2x86 2005.07.05------------------------------------------- | ||
| 419 | //arm_arm A2-18 | 345 | //arm_arm A2-18 |
| 420 | int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model | 346 | int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model |
| 421 | //AJ2D-------------------------------------------------------------------------- | 347 | |
| 422 | //teawater change for return if running tb dirty 2005.07.09--------------------- | 348 | //teawater change for return if running tb dirty 2005.07.09--------------------- |
| 423 | void *tb_now; | 349 | void *tb_now; |
| 424 | //AJ2D-------------------------------------------------------------------------- | ||
| 425 | 350 | ||
| 426 | //teawater add for record reg value to ./reg.txt 2005.07.10--------------------- | 351 | |
| 352 | //teawater add for record reg value to ./reg.txt 2005.07.10--------------------- | ||
| 427 | FILE *tea_reg_fd; | 353 | FILE *tea_reg_fd; |
| 428 | //AJ2D-------------------------------------------------------------------------- | ||
| 429 | 354 | ||
| 430 | /*added by ksh in 2005-10-1*/ | 355 | |
| 356 | /*added by ksh in 2005-10-1*/ | ||
| 431 | cpu_config_t *cpu; | 357 | cpu_config_t *cpu; |
| 432 | //mem_config_t *mem_bank; | 358 | //mem_config_t *mem_bank; |
| 433 | 359 | ||
| 434 | /* added LPC remap function */ | 360 | /* added LPC remap function */ |
| 435 | int vector_remap_flag; | 361 | int vector_remap_flag; |
| 436 | u32 vector_remap_addr; | 362 | u32 vector_remap_addr; |
| 437 | u32 vector_remap_size; | 363 | u32 vector_remap_size; |
| @@ -486,17 +412,14 @@ typedef ARMul_State arm_core_t; | |||
| 486 | #define ARM_Debug_Prop 0x10 | 412 | #define ARM_Debug_Prop 0x10 |
| 487 | #define ARM_Isync_Prop ARM_Debug_Prop | 413 | #define ARM_Isync_Prop ARM_Debug_Prop |
| 488 | #define ARM_Lock_Prop 0x20 | 414 | #define ARM_Lock_Prop 0x20 |
| 489 | //chy 2003-08-11 | ||
| 490 | #define ARM_v4_Prop 0x40 | 415 | #define ARM_v4_Prop 0x40 |
| 491 | #define ARM_v5_Prop 0x80 | 416 | #define ARM_v5_Prop 0x80 |
| 492 | /*jeff.du 2010-08-05 */ | ||
| 493 | #define ARM_v6_Prop 0xc0 | 417 | #define ARM_v6_Prop 0xc0 |
| 494 | 418 | ||
| 495 | #define ARM_v5e_Prop 0x100 | 419 | #define ARM_v5e_Prop 0x100 |
| 496 | #define ARM_XScale_Prop 0x200 | 420 | #define ARM_XScale_Prop 0x200 |
| 497 | #define ARM_ep9312_Prop 0x400 | 421 | #define ARM_ep9312_Prop 0x400 |
| 498 | #define ARM_iWMMXt_Prop 0x800 | 422 | #define ARM_iWMMXt_Prop 0x800 |
| 499 | //chy 2005-09-19 | ||
| 500 | #define ARM_PXA27X_Prop 0x1000 | 423 | #define ARM_PXA27X_Prop 0x1000 |
| 501 | #define ARM_v7_Prop 0x2000 | 424 | #define ARM_v7_Prop 0x2000 |
| 502 | 425 | ||
| @@ -591,47 +514,44 @@ typedef ARMul_State arm_core_t; | |||
| 591 | #ifdef __cplusplus | 514 | #ifdef __cplusplus |
| 592 | extern "C" { | 515 | extern "C" { |
| 593 | #endif | 516 | #endif |
| 594 | extern void ARMul_EmulateInit (void); | 517 | extern void ARMul_EmulateInit(); |
| 595 | extern void ARMul_Reset (ARMul_State * state); | 518 | extern void ARMul_Reset(ARMul_State* state); |
| 596 | #ifdef __cplusplus | 519 | #ifdef __cplusplus |
| 597 | } | 520 | } |
| 598 | #endif | 521 | #endif |
| 599 | extern ARMul_State *ARMul_NewState (ARMul_State * state); | 522 | extern ARMul_State *ARMul_NewState(ARMul_State* state); |
| 600 | extern ARMword ARMul_DoProg (ARMul_State * state); | 523 | extern ARMword ARMul_DoProg(ARMul_State* state); |
| 601 | extern ARMword ARMul_DoInstr (ARMul_State * state); | 524 | extern ARMword ARMul_DoInstr(ARMul_State* state); |
| 602 | /***************************************************************************\ | 525 | /***************************************************************************\ |
| 603 | * Definitons of things for event handling * | 526 | * Definitons of things for event handling * |
| 604 | \***************************************************************************/ | 527 | \***************************************************************************/ |
| 605 | 528 | ||
| 606 | extern void ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay, | 529 | extern void ARMul_ScheduleEvent(ARMul_State* state, unsigned int delay, unsigned(*func) ()); |
| 607 | unsigned (*func) ()); | 530 | extern void ARMul_EnvokeEvent(ARMul_State* state); |
| 608 | extern void ARMul_EnvokeEvent (ARMul_State * state); | 531 | extern unsigned int ARMul_Time(ARMul_State* state); |
| 609 | extern unsigned int ARMul_Time (ARMul_State * state); | ||
| 610 | 532 | ||
| 611 | /***************************************************************************\ | 533 | /***************************************************************************\ |
| 612 | * Useful support routines * | 534 | * Useful support routines * |
| 613 | \***************************************************************************/ | 535 | \***************************************************************************/ |
| 614 | 536 | ||
| 615 | extern ARMword ARMul_GetReg (ARMul_State * state, unsigned mode, | 537 | extern ARMword ARMul_GetReg (ARMul_State* state, unsigned mode, unsigned reg); |
| 616 | unsigned reg); | 538 | extern void ARMul_SetReg (ARMul_State* state, unsigned mode, unsigned reg, ARMword value); |
| 617 | extern void ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg, | 539 | extern ARMword ARMul_GetPC(ARMul_State* state); |
| 618 | ARMword value); | 540 | extern ARMword ARMul_GetNextPC(ARMul_State* state); |
| 619 | extern ARMword ARMul_GetPC (ARMul_State * state); | 541 | extern void ARMul_SetPC(ARMul_State* state, ARMword value); |
| 620 | extern ARMword ARMul_GetNextPC (ARMul_State * state); | 542 | extern ARMword ARMul_GetR15(ARMul_State* state); |
| 621 | extern void ARMul_SetPC (ARMul_State * state, ARMword value); | 543 | extern void ARMul_SetR15(ARMul_State* state, ARMword value); |
| 622 | extern ARMword ARMul_GetR15 (ARMul_State * state); | 544 | |
| 623 | extern void ARMul_SetR15 (ARMul_State * state, ARMword value); | 545 | extern ARMword ARMul_GetCPSR(ARMul_State* state); |
| 624 | 546 | extern void ARMul_SetCPSR(ARMul_State* state, ARMword value); | |
| 625 | extern ARMword ARMul_GetCPSR (ARMul_State * state); | 547 | extern ARMword ARMul_GetSPSR(ARMul_State* state, ARMword mode); |
| 626 | extern void ARMul_SetCPSR (ARMul_State * state, ARMword value); | 548 | extern void ARMul_SetSPSR(ARMul_State* state, ARMword mode, ARMword value); |
| 627 | extern ARMword ARMul_GetSPSR (ARMul_State * state, ARMword mode); | ||
| 628 | extern void ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value); | ||
| 629 | 549 | ||
| 630 | /***************************************************************************\ | 550 | /***************************************************************************\ |
| 631 | * Definitons of things to handle aborts * | 551 | * Definitons of things to handle aborts * |
| 632 | \***************************************************************************/ | 552 | \***************************************************************************/ |
| 633 | 553 | ||
| 634 | extern void ARMul_Abort (ARMul_State * state, ARMword address); | 554 | extern void ARMul_Abort(ARMul_State* state, ARMword address); |
| 635 | #ifdef MODET | 555 | #ifdef MODET |
| 636 | #define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */ | 556 | #define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */ |
| 637 | #define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \ | 557 | #define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \ |
| @@ -649,54 +569,40 @@ extern void ARMul_Abort (ARMul_State * state, ARMword address); | |||
| 649 | * Definitons of things in the memory interface * | 569 | * Definitons of things in the memory interface * |
| 650 | \***************************************************************************/ | 570 | \***************************************************************************/ |
| 651 | 571 | ||
| 652 | extern unsigned ARMul_MemoryInit (ARMul_State * state, | 572 | extern unsigned ARMul_MemoryInit(ARMul_State* state, unsigned int initmemsize); |
| 653 | unsigned int initmemsize); | 573 | extern void ARMul_MemoryExit(ARMul_State* state); |
| 654 | extern void ARMul_MemoryExit (ARMul_State * state); | ||
| 655 | 574 | ||
| 656 | extern ARMword ARMul_LoadInstrS (ARMul_State * state, ARMword address, | 575 | extern ARMword ARMul_LoadInstrS(ARMul_State* state, ARMword address, ARMword isize); |
| 657 | ARMword isize); | 576 | extern ARMword ARMul_LoadInstrN(ARMul_State* state, ARMword address, ARMword isize); |
| 658 | extern ARMword ARMul_LoadInstrN (ARMul_State * state, ARMword address, | ||
| 659 | ARMword isize); | ||
| 660 | #ifdef __cplusplus | 577 | #ifdef __cplusplus |
| 661 | extern "C" { | 578 | extern "C" { |
| 662 | #endif | 579 | #endif |
| 663 | extern ARMword ARMul_ReLoadInstr (ARMul_State * state, ARMword address, | 580 | extern ARMword ARMul_ReLoadInstr(ARMul_State* state, ARMword address, ARMword isize); |
| 664 | ARMword isize); | ||
| 665 | #ifdef __cplusplus | 581 | #ifdef __cplusplus |
| 666 | } | 582 | } |
| 667 | #endif | 583 | #endif |
| 668 | extern ARMword ARMul_LoadWordS (ARMul_State * state, ARMword address); | 584 | extern ARMword ARMul_LoadWordS(ARMul_State* state, ARMword address); |
| 669 | extern ARMword ARMul_LoadWordN (ARMul_State * state, ARMword address); | 585 | extern ARMword ARMul_LoadWordN(ARMul_State* state, ARMword address); |
| 670 | extern ARMword ARMul_LoadHalfWord (ARMul_State * state, ARMword address); | 586 | extern ARMword ARMul_LoadHalfWord(ARMul_State* state, ARMword address); |
| 671 | extern ARMword ARMul_LoadByte (ARMul_State * state, ARMword address); | 587 | extern ARMword ARMul_LoadByte(ARMul_State* state, ARMword address); |
| 672 | 588 | ||
| 673 | extern void ARMul_StoreWordS (ARMul_State * state, ARMword address, | 589 | extern void ARMul_StoreWordS(ARMul_State* state, ARMword address, ARMword data); |
| 674 | ARMword data); | 590 | extern void ARMul_StoreWordN(ARMul_State* state, ARMword address, ARMword data); |
| 675 | extern void ARMul_StoreWordN (ARMul_State * state, ARMword address, | 591 | extern void ARMul_StoreHalfWord(ARMul_State* state, ARMword address, ARMword data); |
| 676 | ARMword data); | 592 | extern void ARMul_StoreByte(ARMul_State* state, ARMword address, ARMword data); |
| 677 | extern void ARMul_StoreHalfWord (ARMul_State * state, ARMword address, | 593 | |
| 678 | ARMword data); | 594 | extern ARMword ARMul_SwapWord(ARMul_State* state, ARMword address, ARMword data); |
| 679 | extern void ARMul_StoreByte (ARMul_State * state, ARMword address, | 595 | extern ARMword ARMul_SwapByte(ARMul_State* state, ARMword address, ARMword data); |
| 680 | ARMword data); | 596 | |
| 681 | 597 | extern void ARMul_Icycles(ARMul_State* state, unsigned number, ARMword address); | |
| 682 | extern ARMword ARMul_SwapWord (ARMul_State * state, ARMword address, | 598 | extern void ARMul_Ccycles(ARMul_State* state, unsigned number, ARMword address); |
| 683 | ARMword data); | 599 | |
| 684 | extern ARMword ARMul_SwapByte (ARMul_State * state, ARMword address, | 600 | extern ARMword ARMul_ReadWord(ARMul_State* state, ARMword address); |
| 685 | ARMword data); | 601 | extern ARMword ARMul_ReadByte(ARMul_State* state, ARMword address); |
| 686 | 602 | extern void ARMul_WriteWord(ARMul_State* state, ARMword address, ARMword data); | |
| 687 | extern void ARMul_Icycles (ARMul_State * state, unsigned number, | 603 | extern void ARMul_WriteByte(ARMul_State* state, ARMword address, ARMword data); |
| 688 | ARMword address); | 604 | |
| 689 | extern void ARMul_Ccycles (ARMul_State * state, unsigned number, | 605 | extern ARMword ARMul_MemAccess(ARMul_State* state, ARMword, ARMword, |
| 690 | ARMword address); | ||
| 691 | |||
| 692 | extern ARMword ARMul_ReadWord (ARMul_State * state, ARMword address); | ||
| 693 | extern ARMword ARMul_ReadByte (ARMul_State * state, ARMword address); | ||
| 694 | extern void ARMul_WriteWord (ARMul_State * state, ARMword address, | ||
| 695 | ARMword data); | ||
| 696 | extern void ARMul_WriteByte (ARMul_State * state, ARMword address, | ||
| 697 | ARMword data); | ||
| 698 | |||
| 699 | extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword, | ||
| 700 | ARMword, ARMword, ARMword, ARMword, ARMword, | 606 | ARMword, ARMword, ARMword, ARMword, ARMword, |
| 701 | ARMword, ARMword, ARMword); | 607 | ARMword, ARMword, ARMword); |
| 702 | 608 | ||
| @@ -739,82 +645,58 @@ extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword, | |||
| 739 | #define ARMul_CP15_DBCON_E1 0x000c | 645 | #define ARMul_CP15_DBCON_E1 0x000c |
| 740 | #define ARMul_CP15_DBCON_E0 0x0003 | 646 | #define ARMul_CP15_DBCON_E0 0x0003 |
| 741 | 647 | ||
| 742 | extern unsigned ARMul_CoProInit (ARMul_State * state); | 648 | extern unsigned ARMul_CoProInit(ARMul_State* state); |
| 743 | extern void ARMul_CoProExit (ARMul_State * state); | 649 | extern void ARMul_CoProExit(ARMul_State* state); |
| 744 | extern void ARMul_CoProAttach (ARMul_State * state, unsigned number, | 650 | extern void ARMul_CoProAttach (ARMul_State* state, unsigned number, |
| 745 | ARMul_CPInits * init, ARMul_CPExits * exit, | 651 | ARMul_CPInits* init, ARMul_CPExits* exit, |
| 746 | ARMul_LDCs * ldc, ARMul_STCs * stc, | 652 | ARMul_LDCs* ldc, ARMul_STCs* stc, |
| 747 | ARMul_MRCs * mrc, ARMul_MCRs * mcr, | 653 | ARMul_MRCs* mrc, ARMul_MCRs* mcr, |
| 748 | ARMul_MRRCs * mrrc, ARMul_MCRRs * mcrr, | 654 | ARMul_MRRCs* mrrc, ARMul_MCRRs* mcrr, |
| 749 | ARMul_CDPs * cdp, | 655 | ARMul_CDPs* cdp, |
| 750 | ARMul_CPReads * read, ARMul_CPWrites * write); | 656 | ARMul_CPReads* read, ARMul_CPWrites* write); |
| 751 | extern void ARMul_CoProDetach (ARMul_State * state, unsigned number); | 657 | extern void ARMul_CoProDetach(ARMul_State* state, unsigned number); |
| 752 | 658 | ||
| 753 | /***************************************************************************\ | 659 | /***************************************************************************\ |
| 754 | * Definitons of things in the host environment * | 660 | * Definitons of things in the host environment * |
| 755 | \***************************************************************************/ | 661 | \***************************************************************************/ |
| 756 | 662 | ||
| 757 | extern unsigned ARMul_OSInit (ARMul_State * state); | 663 | extern unsigned ARMul_OSInit(ARMul_State* state); |
| 758 | extern void ARMul_OSExit (ARMul_State * state); | 664 | extern void ARMul_OSExit(ARMul_State* state); |
| 759 | 665 | ||
| 760 | #ifdef __cplusplus | 666 | #ifdef __cplusplus |
| 761 | extern "C" { | 667 | extern "C" { |
| 762 | #endif | 668 | #endif |
| 763 | 669 | ||
| 764 | extern unsigned ARMul_OSHandleSWI (ARMul_State * state, ARMword number); | 670 | extern unsigned ARMul_OSHandleSWI(ARMul_State* state, ARMword number); |
| 765 | #ifdef __cplusplus | 671 | #ifdef __cplusplus |
| 766 | } | 672 | } |
| 767 | #endif | 673 | #endif |
| 768 | 674 | ||
| 769 | 675 | ||
| 770 | extern ARMword ARMul_OSLastErrorP (ARMul_State * state); | 676 | extern ARMword ARMul_OSLastErrorP(ARMul_State* state); |
| 771 | 677 | ||
| 772 | extern ARMword ARMul_Debug (ARMul_State * state, ARMword pc, ARMword instr); | 678 | extern ARMword ARMul_Debug(ARMul_State* state, ARMword pc, ARMword instr); |
| 773 | extern unsigned ARMul_OSException (ARMul_State * state, ARMword vector, | 679 | extern unsigned ARMul_OSException(ARMul_State* state, ARMword vector, ARMword pc); |
| 774 | ARMword pc); | ||
| 775 | extern int rdi_log; | 680 | extern int rdi_log; |
| 776 | 681 | ||
| 777 | /***************************************************************************\ | 682 | enum ConditionCode { |
| 778 | * Host-dependent stuff * | 683 | EQ = 0, |
| 779 | \***************************************************************************/ | 684 | NE = 1, |
| 780 | 685 | CS = 2, | |
| 781 | #ifdef macintosh | 686 | CC = 3, |
| 782 | pascal void SpinCursor (short increment); /* copied from CursorCtl.h */ | 687 | MI = 4, |
| 783 | # define HOURGLASS SpinCursor( 1 ) | 688 | PL = 5, |
| 784 | # define HOURGLASS_RATE 1023 /* 2^n - 1 */ | 689 | VS = 6, |
| 785 | #endif | 690 | VC = 7, |
| 786 | 691 | HI = 8, | |
| 787 | //teawater add for arm2x86 2005.02.14------------------------------------------- | 692 | LS = 9, |
| 788 | /*ywc 2005-03-31*/ | 693 | GE = 10, |
| 789 | /* | 694 | LT = 11, |
| 790 | #include "arm2x86.h" | 695 | GT = 12, |
| 791 | #include "arm2x86_dp.h" | 696 | LE = 13, |
| 792 | #include "arm2x86_movl.h" | 697 | AL = 14, |
| 793 | #include "arm2x86_psr.h" | 698 | NV = 15, |
| 794 | #include "arm2x86_shift.h" | 699 | }; |
| 795 | #include "arm2x86_mem.h" | ||
| 796 | #include "arm2x86_mul.h" | ||
| 797 | #include "arm2x86_test.h" | ||
| 798 | #include "arm2x86_other.h" | ||
| 799 | #include "list.h" | ||
| 800 | #include "tb.h" | ||
| 801 | */ | ||
| 802 | #define EQ 0 | ||
| 803 | #define NE 1 | ||
| 804 | #define CS 2 | ||
| 805 | #define CC 3 | ||
| 806 | #define MI 4 | ||
| 807 | #define PL 5 | ||
| 808 | #define VS 6 | ||
| 809 | #define VC 7 | ||
| 810 | #define HI 8 | ||
| 811 | #define LS 9 | ||
| 812 | #define GE 10 | ||
| 813 | #define LT 11 | ||
| 814 | #define GT 12 | ||
| 815 | #define LE 13 | ||
| 816 | #define AL 14 | ||
| 817 | #define NV 15 | ||
| 818 | 700 | ||
| 819 | #ifndef NFLAG | 701 | #ifndef NFLAG |
| 820 | #define NFLAG state->NFlag | 702 | #define NFLAG state->NFlag |
| @@ -849,32 +731,16 @@ pascal void SpinCursor (short increment); /* copied from CursorCtl.h */ | |||
| 849 | #define ZBIT_SHIFT 30 | 731 | #define ZBIT_SHIFT 30 |
| 850 | #define CBIT_SHIFT 29 | 732 | #define CBIT_SHIFT 29 |
| 851 | #define VBIT_SHIFT 28 | 733 | #define VBIT_SHIFT 28 |
| 852 | #ifdef DBCT | 734 | |
| 853 | //teawater change for local tb branch directly jump 2005.10.18------------------ | ||
| 854 | #include "dbct/list.h" | ||
| 855 | #include "dbct/arm2x86.h" | ||
| 856 | #include "dbct/arm2x86_dp.h" | ||
| 857 | #include "dbct/arm2x86_movl.h" | ||
| 858 | #include "dbct/arm2x86_psr.h" | ||
| 859 | #include "dbct/arm2x86_shift.h" | ||
| 860 | #include "dbct/arm2x86_mem.h" | ||
| 861 | #include "dbct/arm2x86_mul.h" | ||
| 862 | #include "dbct/arm2x86_test.h" | ||
| 863 | #include "dbct/arm2x86_other.h" | ||
| 864 | #include "dbct/arm2x86_coproc.h" | ||
| 865 | #include "dbct/tb.h" | ||
| 866 | #endif | ||
| 867 | //AJ2D-------------------------------------------------------------------------- | ||
| 868 | //AJ2D-------------------------------------------------------------------------- | ||
| 869 | #define SKYEYE_OUTREGS(fd) { fprintf ((fd), "R %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,C %x,S %x,%x,%x,%x,%x,%x,%x,M %x,B %x,E %x,I %x,P %x,T %x,L %x,D %x,",\ | 735 | #define SKYEYE_OUTREGS(fd) { fprintf ((fd), "R %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,C %x,S %x,%x,%x,%x,%x,%x,%x,M %x,B %x,E %x,I %x,P %x,T %x,L %x,D %x,",\ |
| 870 | state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \ | 736 | state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \ |
| 871 | state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \ | 737 | state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \ |
| 872 | state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \ | 738 | state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \ |
| 873 | state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \ | 739 | state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \ |
| 874 | state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\ | 740 | state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\ |
| 875 | state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\ | 741 | state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\ |
| 876 | state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\ | 742 | state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\ |
| 877 | state->temp,state->loaded,state->decoded);} | 743 | state->temp,state->loaded,state->decoded);} |
| 878 | 744 | ||
| 879 | #define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\ | 745 | #define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\ |
| 880 | RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\ | 746 | RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\ |
| @@ -912,17 +778,17 @@ RUn %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",\ | |||
| 912 | 778 | ||
| 913 | #define SA1110 0x6901b110 | 779 | #define SA1110 0x6901b110 |
| 914 | #define SA1100 0x4401a100 | 780 | #define SA1100 0x4401a100 |
| 915 | #define PXA250 0x69052100 | 781 | #define PXA250 0x69052100 |
| 916 | #define PXA270 0x69054110 | 782 | #define PXA270 0x69054110 |
| 917 | //#define PXA250 0x69052903 | 783 | //#define PXA250 0x69052903 |
| 918 | // 0x69052903; //PXA250 B1 from intel 278522-001.pdf | 784 | // 0x69052903; //PXA250 B1 from intel 278522-001.pdf |
| 919 | 785 | ||
| 920 | 786 | ||
| 921 | extern void ARMul_UndefInstr (ARMul_State *, ARMword); | 787 | extern void ARMul_UndefInstr(ARMul_State*, ARMword); |
| 922 | extern void ARMul_FixCPSR (ARMul_State *, ARMword, ARMword); | 788 | extern void ARMul_FixCPSR(ARMul_State*, ARMword, ARMword); |
| 923 | extern void ARMul_FixSPSR (ARMul_State *, ARMword, ARMword); | 789 | extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword); |
| 924 | extern void ARMul_ConsolePrint (ARMul_State *, const char *, ...); | 790 | extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...); |
| 925 | extern void ARMul_SelectProcessor (ARMul_State *, unsigned); | 791 | extern void ARMul_SelectProcessor(ARMul_State*, unsigned); |
| 926 | 792 | ||
| 927 | #define DIFF_LOG 0 | 793 | #define DIFF_LOG 0 |
| 928 | #define SAVE_LOG 0 | 794 | #define SAVE_LOG 0 |
diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h index c0f0270fe..7f7c0e682 100644 --- a/src/core/arm/skyeye_common/armemu.h +++ b/src/core/arm/skyeye_common/armemu.h | |||
| @@ -23,26 +23,6 @@ | |||
| 23 | 23 | ||
| 24 | //extern ARMword isize; | 24 | //extern ARMword isize; |
| 25 | 25 | ||
| 26 | #define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__) | ||
| 27 | |||
| 28 | /* Condition code values. */ | ||
| 29 | #define EQ 0 | ||
| 30 | #define NE 1 | ||
| 31 | #define CS 2 | ||
| 32 | #define CC 3 | ||
| 33 | #define MI 4 | ||
| 34 | #define PL 5 | ||
| 35 | #define VS 6 | ||
| 36 | #define VC 7 | ||
| 37 | #define HI 8 | ||
| 38 | #define LS 9 | ||
| 39 | #define GE 10 | ||
| 40 | #define LT 11 | ||
| 41 | #define GT 12 | ||
| 42 | #define LE 13 | ||
| 43 | #define AL 14 | ||
| 44 | #define NV 15 | ||
| 45 | |||
| 46 | /* Shift Opcodes. */ | 26 | /* Shift Opcodes. */ |
| 47 | #define LSL 0 | 27 | #define LSL 0 |
| 48 | #define LSR 1 | 28 | #define LSR 1 |
| @@ -503,7 +483,7 @@ tdstate; | |||
| 503 | * out-of-updated with the newer ISA. | 483 | * out-of-updated with the newer ISA. |
| 504 | * -- Michael.Kang | 484 | * -- Michael.Kang |
| 505 | ********************************************************************************/ | 485 | ********************************************************************************/ |
| 506 | #define UNDEF_WARNING WARN_LOG(ARM11, "undefined or unpredicted behavior for arm instruction.\n"); | 486 | #define UNDEF_WARNING LOG_WARNING(Core_ARM11, "undefined or unpredicted behavior for arm instruction."); |
| 507 | 487 | ||
| 508 | /* Macros to scrutinize instructions. */ | 488 | /* Macros to scrutinize instructions. */ |
| 509 | #define UNDEF_Test UNDEF_WARNING | 489 | #define UNDEF_Test UNDEF_WARNING |
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 07d0c1f44..6c33d8b78 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp | |||
| @@ -522,8 +522,7 @@ static s64 vfp_single_to_doubleintern(ARMul_State* state, s32 m, u32 fpscr) //ic | |||
| 522 | if (tm == VFP_QNAN) | 522 | if (tm == VFP_QNAN) |
| 523 | vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; | 523 | vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; |
| 524 | goto pack_nan; | 524 | goto pack_nan; |
| 525 | } | 525 | } else if (tm & VFP_ZERO) |
| 526 | else if (tm & VFP_ZERO) | ||
| 527 | vdd.exponent = 0; | 526 | vdd.exponent = 0; |
| 528 | else | 527 | else |
| 529 | vdd.exponent = vsm.exponent + (1023 - 127); | 528 | vdd.exponent = vsm.exponent + (1023 - 127); |
| @@ -615,12 +614,12 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f | |||
| 615 | exceptions |= FPSCR_IDC; | 614 | exceptions |= FPSCR_IDC; |
| 616 | 615 | ||
| 617 | if (tm & VFP_NAN) | 616 | if (tm & VFP_NAN) |
| 618 | vsm.sign = 0; | 617 | vsm.sign = 1; |
| 619 | 618 | ||
| 620 | if (vsm.exponent >= 127 + 32) { | 619 | if (vsm.exponent >= 127 + 32) { |
| 621 | d = vsm.sign ? 0 : 0xffffffff; | 620 | d = vsm.sign ? 0 : 0xffffffff; |
| 622 | exceptions = FPSCR_IOC; | 621 | exceptions = FPSCR_IOC; |
| 623 | } else if (vsm.exponent >= 127 - 1) { | 622 | } else if (vsm.exponent >= 127) { |
| 624 | int shift = 127 + 31 - vsm.exponent; | 623 | int shift = 127 + 31 - vsm.exponent; |
| 625 | u32 rem, incr = 0; | 624 | u32 rem, incr = 0; |
| 626 | 625 | ||
| @@ -705,7 +704,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f | |||
| 705 | if (vsm.sign) | 704 | if (vsm.sign) |
| 706 | d = ~d; | 705 | d = ~d; |
| 707 | exceptions |= FPSCR_IOC; | 706 | exceptions |= FPSCR_IOC; |
| 708 | } else if (vsm.exponent >= 127 - 1) { | 707 | } else if (vsm.exponent >= 127) { |
| 709 | int shift = 127 + 31 - vsm.exponent; | 708 | int shift = 127 + 31 - vsm.exponent; |
| 710 | u32 rem, incr = 0; | 709 | u32 rem, incr = 0; |
| 711 | 710 | ||
| @@ -1149,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) | |||
| 1149 | /* | 1148 | /* |
| 1150 | * Subtraction is addition with one sign inverted. | 1149 | * Subtraction is addition with one sign inverted. |
| 1151 | */ | 1150 | */ |
| 1152 | return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr); | 1151 | if (m != 0x7FC00000) // Only negate if m isn't NaN. |
| 1152 | m = vfp_single_packed_negate(m); | ||
| 1153 | |||
| 1154 | return vfp_single_fadd(state, sd, sn, m, fpscr); | ||
| 1153 | } | 1155 | } |
| 1154 | 1156 | ||
| 1155 | /* | 1157 | /* |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 25c78d33c..64de0cbba 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -16,10 +16,10 @@ | |||
| 16 | 16 | ||
| 17 | namespace Core { | 17 | namespace Core { |
| 18 | 18 | ||
| 19 | u64 g_last_ticks = 0; ///< Last CPU ticks | 19 | static u64 last_ticks = 0; ///< Last CPU ticks |
| 20 | ARM_Disasm* g_disasm = nullptr; ///< ARM disassembler | 20 | static ARM_Disasm* disasm = nullptr; ///< ARM disassembler |
| 21 | ARM_Interface* g_app_core = nullptr; ///< ARM11 application core | 21 | ARM_Interface* g_app_core = nullptr; ///< ARM11 application core |
| 22 | ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core | 22 | ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core |
| 23 | 23 | ||
| 24 | /// Run the core CPU loop | 24 | /// Run the core CPU loop |
| 25 | void RunLoop(int tight_loop) { | 25 | void RunLoop(int tight_loop) { |
| @@ -47,9 +47,9 @@ void Stop() { | |||
| 47 | 47 | ||
| 48 | /// Initialize the core | 48 | /// Initialize the core |
| 49 | int Init() { | 49 | int Init() { |
| 50 | NOTICE_LOG(MASTER_LOG, "initialized OK"); | 50 | LOG_DEBUG(Core, "initialized OK"); |
| 51 | 51 | ||
| 52 | g_disasm = new ARM_Disasm(); | 52 | disasm = new ARM_Disasm(); |
| 53 | g_sys_core = new ARM_Interpreter(); | 53 | g_sys_core = new ARM_Interpreter(); |
| 54 | 54 | ||
| 55 | switch (Settings::values.cpu_core) { | 55 | switch (Settings::values.cpu_core) { |
| @@ -62,17 +62,17 @@ int Init() { | |||
| 62 | break; | 62 | break; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | g_last_ticks = Core::g_app_core->GetTicks(); | 65 | last_ticks = Core::g_app_core->GetTicks(); |
| 66 | 66 | ||
| 67 | return 0; | 67 | return 0; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | void Shutdown() { | 70 | void Shutdown() { |
| 71 | delete g_disasm; | 71 | delete disasm; |
| 72 | delete g_app_core; | 72 | delete g_app_core; |
| 73 | delete g_sys_core; | 73 | delete g_sys_core; |
| 74 | 74 | ||
| 75 | NOTICE_LOG(MASTER_LOG, "shutdown OK"); | 75 | LOG_DEBUG(Core, "shutdown OK"); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | } // namespace | 78 | } // namespace |
diff --git a/src/core/core.h b/src/core/core.h index 872dc0cd1..850bb0ab4 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -26,13 +26,13 @@ void Start(); | |||
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | * Run the core CPU loop | 28 | * Run the core CPU loop |
| 29 | * This function loops for 100 instructions in the CPU before trying to update hardware. This is a | 29 | * This function runs the core for the specified number of CPU instructions before trying to update |
| 30 | * little bit faster than SingleStep, and should be pretty much equivalent. The number of | 30 | * hardware. This is much faster than SingleStep (and should be equivalent), as the CPU is not |
| 31 | * instructions chosen is fairly arbitrary, however a large number will more drastically affect the | 31 | * required to do a full dispatch with each instruction. NOTE: the number of instructions requested |
| 32 | * frequency of GSP interrupts and likely break things. The point of this is to just loop in the CPU | 32 | * is not guaranteed to run, as this will be interrupted preemptively if a hardware update is |
| 33 | * for more than 1 instruction to reduce overhead and make it a little bit faster... | 33 | * requested (e.g. on a thread switch). |
| 34 | */ | 34 | */ |
| 35 | void RunLoop(int tight_loop=100); | 35 | void RunLoop(int tight_loop=1000); |
| 36 | 36 | ||
| 37 | /// Step the CPU one instruction | 37 | /// Step the CPU one instruction |
| 38 | void SingleStep(); | 38 | void SingleStep(); |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 0116cb376..1a0b2724a 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -41,7 +41,7 @@ struct BaseEvent | |||
| 41 | s64 time; | 41 | s64 time; |
| 42 | u64 userdata; | 42 | u64 userdata; |
| 43 | int type; | 43 | int type; |
| 44 | // Event *next; | 44 | // Event *next; |
| 45 | }; | 45 | }; |
| 46 | 46 | ||
| 47 | typedef LinkedListItem<BaseEvent> Event; | 47 | typedef LinkedListItem<BaseEvent> Event; |
| @@ -67,7 +67,7 @@ s64 idledCycles; | |||
| 67 | static std::recursive_mutex externalEventSection; | 67 | static std::recursive_mutex externalEventSection; |
| 68 | 68 | ||
| 69 | // Warning: not included in save state. | 69 | // Warning: not included in save state. |
| 70 | void(*advanceCallback)(int cyclesExecuted) = NULL; | 70 | void(*advanceCallback)(int cyclesExecuted) = nullptr; |
| 71 | 71 | ||
| 72 | void SetClockFrequencyMHz(int cpuMhz) | 72 | void SetClockFrequencyMHz(int cpuMhz) |
| 73 | { | 73 | { |
| @@ -124,7 +124,7 @@ int RegisterEvent(const char *name, TimedCallback callback) | |||
| 124 | 124 | ||
| 125 | void AntiCrashCallback(u64 userdata, int cyclesLate) | 125 | void AntiCrashCallback(u64 userdata, int cyclesLate) |
| 126 | { | 126 | { |
| 127 | ERROR_LOG(TIME, "Savestate broken: an unregistered event was called."); | 127 | LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called."); |
| 128 | Core::Halt("invalid timing events"); | 128 | Core::Halt("invalid timing events"); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| @@ -176,7 +176,7 @@ void Shutdown() | |||
| 176 | 176 | ||
| 177 | u64 GetTicks() | 177 | u64 GetTicks() |
| 178 | { | 178 | { |
| 179 | ERROR_LOG(TIME, "Unimplemented function!"); | 179 | LOG_ERROR(Core, "Unimplemented function!"); |
| 180 | return 0; | 180 | return 0; |
| 181 | //return (u64)globalTimer + slicelength - currentMIPS->downcount; | 181 | //return (u64)globalTimer + slicelength - currentMIPS->downcount; |
| 182 | } | 182 | } |
| @@ -231,7 +231,7 @@ void ClearPendingEvents() | |||
| 231 | 231 | ||
| 232 | void AddEventToQueue(Event* ne) | 232 | void AddEventToQueue(Event* ne) |
| 233 | { | 233 | { |
| 234 | Event* prev = NULL; | 234 | Event* prev = nullptr; |
| 235 | Event** pNext = &first; | 235 | Event** pNext = &first; |
| 236 | for (;;) | 236 | for (;;) |
| 237 | { | 237 | { |
| @@ -249,7 +249,7 @@ void AddEventToQueue(Event* ne) | |||
| 249 | 249 | ||
| 250 | // This must be run ONLY from within the cpu thread | 250 | // This must be run ONLY from within the cpu thread |
| 251 | // cyclesIntoFuture may be VERY inaccurate if called from anything else | 251 | // cyclesIntoFuture may be VERY inaccurate if called from anything else |
| 252 | // than Advance | 252 | // than Advance |
| 253 | void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) | 253 | void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) |
| 254 | { | 254 | { |
| 255 | Event *ne = GetNewEvent(); | 255 | Event *ne = GetNewEvent(); |
| @@ -327,7 +327,7 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) | |||
| 327 | } | 327 | } |
| 328 | if (!tsFirst) | 328 | if (!tsFirst) |
| 329 | { | 329 | { |
| 330 | tsLast = NULL; | 330 | tsLast = nullptr; |
| 331 | return result; | 331 | return result; |
| 332 | } | 332 | } |
| 333 | 333 | ||
| @@ -433,7 +433,7 @@ void RemoveThreadsafeEvent(int event_type) | |||
| 433 | } | 433 | } |
| 434 | if (!tsFirst) | 434 | if (!tsFirst) |
| 435 | { | 435 | { |
| 436 | tsLast = NULL; | 436 | tsLast = nullptr; |
| 437 | return; | 437 | return; |
| 438 | } | 438 | } |
| 439 | Event *prev = tsFirst; | 439 | Event *prev = tsFirst; |
| @@ -469,8 +469,8 @@ void ProcessFifoWaitEvents() | |||
| 469 | { | 469 | { |
| 470 | if (first->time <= globalTimer) | 470 | if (first->time <= globalTimer) |
| 471 | { | 471 | { |
| 472 | // LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", | 472 | //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", |
| 473 | // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); | 473 | // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); |
| 474 | Event* evt = first; | 474 | Event* evt = first; |
| 475 | first = first->next; | 475 | first = first->next; |
| 476 | event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); | 476 | event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); |
| @@ -495,7 +495,7 @@ void MoveEvents() | |||
| 495 | AddEventToQueue(tsFirst); | 495 | AddEventToQueue(tsFirst); |
| 496 | tsFirst = next; | 496 | tsFirst = next; |
| 497 | } | 497 | } |
| 498 | tsLast = NULL; | 498 | tsLast = nullptr; |
| 499 | 499 | ||
| 500 | // Move free events to threadsafe pool | 500 | // Move free events to threadsafe pool |
| 501 | while (allocatedTsEvents > 0 && eventPool) | 501 | while (allocatedTsEvents > 0 && eventPool) |
| @@ -510,29 +510,29 @@ void MoveEvents() | |||
| 510 | 510 | ||
| 511 | void Advance() | 511 | void Advance() |
| 512 | { | 512 | { |
| 513 | ERROR_LOG(TIME, "Unimplemented function!"); | 513 | LOG_ERROR(Core, "Unimplemented function!"); |
| 514 | //int cyclesExecuted = slicelength - currentMIPS->downcount; | 514 | //int cyclesExecuted = slicelength - currentMIPS->downcount; |
| 515 | //globalTimer += cyclesExecuted; | 515 | //globalTimer += cyclesExecuted; |
| 516 | //currentMIPS->downcount = slicelength; | 516 | //currentMIPS->downcount = slicelength; |
| 517 | 517 | ||
| 518 | //if (Common::AtomicLoadAcquire(hasTsEvents)) | 518 | //if (Common::AtomicLoadAcquire(hasTsEvents)) |
| 519 | // MoveEvents(); | 519 | // MoveEvents(); |
| 520 | //ProcessFifoWaitEvents(); | 520 | //ProcessFifoWaitEvents(); |
| 521 | 521 | ||
| 522 | //if (!first) | 522 | //if (!first) |
| 523 | //{ | 523 | //{ |
| 524 | // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); | 524 | // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); |
| 525 | // currentMIPS->downcount += 10000; | 525 | // currentMIPS->downcount += 10000; |
| 526 | //} | 526 | //} |
| 527 | //else | 527 | //else |
| 528 | //{ | 528 | //{ |
| 529 | // slicelength = (int)(first->time - globalTimer); | 529 | // slicelength = (int)(first->time - globalTimer); |
| 530 | // if (slicelength > MAX_SLICE_LENGTH) | 530 | // if (slicelength > MAX_SLICE_LENGTH) |
| 531 | // slicelength = MAX_SLICE_LENGTH; | 531 | // slicelength = MAX_SLICE_LENGTH; |
| 532 | // currentMIPS->downcount = slicelength; | 532 | // currentMIPS->downcount = slicelength; |
| 533 | //} | 533 | //} |
| 534 | //if (advanceCallback) | 534 | //if (advanceCallback) |
| 535 | // advanceCallback(cyclesExecuted); | 535 | // advanceCallback(cyclesExecuted); |
| 536 | } | 536 | } |
| 537 | 537 | ||
| 538 | void LogPendingEvents() | 538 | void LogPendingEvents() |
| @@ -547,23 +547,23 @@ void LogPendingEvents() | |||
| 547 | 547 | ||
| 548 | void Idle(int maxIdle) | 548 | void Idle(int maxIdle) |
| 549 | { | 549 | { |
| 550 | ERROR_LOG(TIME, "Unimplemented function!"); | 550 | LOG_ERROR(Core, "Unimplemented function!"); |
| 551 | //int cyclesDown = currentMIPS->downcount; | 551 | //int cyclesDown = currentMIPS->downcount; |
| 552 | //if (maxIdle != 0 && cyclesDown > maxIdle) | 552 | //if (maxIdle != 0 && cyclesDown > maxIdle) |
| 553 | // cyclesDown = maxIdle; | 553 | // cyclesDown = maxIdle; |
| 554 | 554 | ||
| 555 | //if (first && cyclesDown > 0) | 555 | //if (first && cyclesDown > 0) |
| 556 | //{ | 556 | //{ |
| 557 | // int cyclesExecuted = slicelength - currentMIPS->downcount; | 557 | // int cyclesExecuted = slicelength - currentMIPS->downcount; |
| 558 | // int cyclesNextEvent = (int) (first->time - globalTimer); | 558 | // int cyclesNextEvent = (int) (first->time - globalTimer); |
| 559 | 559 | ||
| 560 | // if (cyclesNextEvent < cyclesExecuted + cyclesDown) | 560 | // if (cyclesNextEvent < cyclesExecuted + cyclesDown) |
| 561 | // { | 561 | // { |
| 562 | // cyclesDown = cyclesNextEvent - cyclesExecuted; | 562 | // cyclesDown = cyclesNextEvent - cyclesExecuted; |
| 563 | // // Now, now... no time machines, please. | 563 | // // Now, now... no time machines, please. |
| 564 | // if (cyclesDown < 0) | 564 | // if (cyclesDown < 0) |
| 565 | // cyclesDown = 0; | 565 | // cyclesDown = 0; |
| 566 | // } | 566 | // } |
| 567 | //} | 567 | //} |
| 568 | 568 | ||
| 569 | //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); | 569 | //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); |
| @@ -571,7 +571,7 @@ void Idle(int maxIdle) | |||
| 571 | //idledCycles += cyclesDown; | 571 | //idledCycles += cyclesDown; |
| 572 | //currentMIPS->downcount -= cyclesDown; | 572 | //currentMIPS->downcount -= cyclesDown; |
| 573 | //if (currentMIPS->downcount == 0) | 573 | //if (currentMIPS->downcount == 0) |
| 574 | // currentMIPS->downcount = -1; | 574 | // currentMIPS->downcount = -1; |
| 575 | } | 575 | } |
| 576 | 576 | ||
| 577 | std::string GetScheduledEventsSummary() | 577 | std::string GetScheduledEventsSummary() |
| @@ -614,7 +614,7 @@ void DoState(PointerWrap &p) | |||
| 614 | // These (should) be filled in later by the modules. | 614 | // These (should) be filled in later by the modules. |
| 615 | event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); | 615 | event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); |
| 616 | 616 | ||
| 617 | p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL); | 617 | p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr); |
| 618 | p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast); | 618 | p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast); |
| 619 | 619 | ||
| 620 | p.Do(g_clock_rate_arm11); | 620 | p.Do(g_clock_rate_arm11); |
| @@ -623,4 +623,4 @@ void DoState(PointerWrap &p) | |||
| 623 | p.Do(idledCycles); | 623 | p.Do(idledCycles); |
| 624 | } | 624 | } |
| 625 | 625 | ||
| 626 | } // namespace | 626 | } // namespace |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 09fdf7a90..b197cf40c 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -106,4 +106,4 @@ void SetClockFrequencyMHz(int cpuMhz); | |||
| 106 | int GetClockFrequencyMHz(); | 106 | int GetClockFrequencyMHz(); |
| 107 | extern int slicelength; | 107 | extern int slicelength; |
| 108 | 108 | ||
| 109 | }; // namespace | 109 | } // namespace |
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h deleted file mode 100644 index aeabf09ac..000000000 --- a/src/core/file_sys/archive.h +++ /dev/null | |||
| @@ -1,104 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | |||
| 12 | #include "core/file_sys/file.h" | ||
| 13 | #include "core/file_sys/directory.h" | ||
| 14 | |||
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // FileSys namespace | ||
| 19 | |||
| 20 | namespace FileSys { | ||
| 21 | |||
| 22 | union Mode { | ||
| 23 | u32 hex; | ||
| 24 | BitField<0, 1, u32> read_flag; | ||
| 25 | BitField<1, 1, u32> write_flag; | ||
| 26 | BitField<2, 1, u32> create_flag; | ||
| 27 | }; | ||
| 28 | |||
| 29 | class Archive : NonCopyable { | ||
| 30 | public: | ||
| 31 | /// Supported archive types | ||
| 32 | enum class IdCode : u32 { | ||
| 33 | RomFS = 0x00000003, | ||
| 34 | SaveData = 0x00000004, | ||
| 35 | ExtSaveData = 0x00000006, | ||
| 36 | SharedExtSaveData = 0x00000007, | ||
| 37 | SystemSaveData = 0x00000008, | ||
| 38 | SDMC = 0x00000009, | ||
| 39 | SDMCWriteOnly = 0x0000000A, | ||
| 40 | }; | ||
| 41 | |||
| 42 | Archive() { } | ||
| 43 | virtual ~Archive() { } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) | ||
| 47 | * @return IdCode of the archive | ||
| 48 | */ | ||
| 49 | virtual IdCode GetIdCode() const = 0; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Open a file specified by its path, using the specified mode | ||
| 53 | * @param path Path relative to the archive | ||
| 54 | * @param mode Mode to open the file with | ||
| 55 | * @return Opened file, or nullptr | ||
| 56 | */ | ||
| 57 | virtual std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const = 0; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Create a directory specified by its path | ||
| 61 | * @param path Path relative to the archive | ||
| 62 | * @return Whether the directory could be created | ||
| 63 | */ | ||
| 64 | virtual bool CreateDirectory(const std::string& path) const = 0; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Open a directory specified by its path | ||
| 68 | * @param path Path relative to the archive | ||
| 69 | * @return Opened directory, or nullptr | ||
| 70 | */ | ||
| 71 | virtual std::unique_ptr<Directory> OpenDirectory(const std::string& path) const = 0; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Read data from the archive | ||
| 75 | * @param offset Offset in bytes to start reading data from | ||
| 76 | * @param length Length in bytes of data to read from archive | ||
| 77 | * @param buffer Buffer to read data into | ||
| 78 | * @return Number of bytes read | ||
| 79 | */ | ||
| 80 | virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Write data to the archive | ||
| 84 | * @param offset Offset in bytes to start writing data to | ||
| 85 | * @param length Length in bytes of data to write to archive | ||
| 86 | * @param buffer Buffer to write data from | ||
| 87 | * @param flush The flush parameters (0 == do not flush) | ||
| 88 | * @return Number of bytes written | ||
| 89 | */ | ||
| 90 | virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Get the size of the archive in bytes | ||
| 94 | * @return Size of the archive in bytes | ||
| 95 | */ | ||
| 96 | virtual size_t GetSize() const = 0; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Set the size of the archive in bytes | ||
| 100 | */ | ||
| 101 | virtual void SetSize(const u64 size) = 0; | ||
| 102 | }; | ||
| 103 | |||
| 104 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h new file mode 100644 index 000000000..18c314884 --- /dev/null +++ b/src/core/file_sys/archive_backend.h | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/string_util.h" | ||
| 11 | #include "common/bit_field.h" | ||
| 12 | |||
| 13 | #include "core/file_sys/file_backend.h" | ||
| 14 | #include "core/file_sys/directory_backend.h" | ||
| 15 | |||
| 16 | #include "core/mem_map.h" | ||
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 18 | |||
| 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 20 | // FileSys namespace | ||
| 21 | |||
| 22 | namespace FileSys { | ||
| 23 | |||
| 24 | // Path string type | ||
| 25 | enum LowPathType : u32 { | ||
| 26 | Invalid = 0, | ||
| 27 | Empty = 1, | ||
| 28 | Binary = 2, | ||
| 29 | Char = 3, | ||
| 30 | Wchar = 4 | ||
| 31 | }; | ||
| 32 | |||
| 33 | union Mode { | ||
| 34 | u32 hex; | ||
| 35 | BitField<0, 1, u32> read_flag; | ||
| 36 | BitField<1, 1, u32> write_flag; | ||
| 37 | BitField<2, 1, u32> create_flag; | ||
| 38 | }; | ||
| 39 | |||
| 40 | class Path { | ||
| 41 | public: | ||
| 42 | |||
| 43 | Path(): | ||
| 44 | type(Invalid) | ||
| 45 | { | ||
| 46 | } | ||
| 47 | |||
| 48 | Path(LowPathType type, u32 size, u32 pointer): | ||
| 49 | type(type) | ||
| 50 | { | ||
| 51 | switch (type) { | ||
| 52 | case Binary: | ||
| 53 | { | ||
| 54 | u8* data = Memory::GetPointer(pointer); | ||
| 55 | binary = std::vector<u8>(data, data + size); | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | case Char: | ||
| 59 | { | ||
| 60 | const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); | ||
| 61 | string = std::string(data, size - 1); // Data is always null-terminated. | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | case Wchar: | ||
| 65 | { | ||
| 66 | const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer)); | ||
| 67 | u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated. | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | default: | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | LowPathType GetType() const { | ||
| 76 | return type; | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Gets the string representation of the path for debugging | ||
| 81 | * @return String representation of the path for debugging | ||
| 82 | */ | ||
| 83 | const std::string DebugStr() const { | ||
| 84 | switch (GetType()) { | ||
| 85 | case Invalid: | ||
| 86 | return "[Invalid]"; | ||
| 87 | case Empty: | ||
| 88 | return "[Empty]"; | ||
| 89 | case Binary: | ||
| 90 | { | ||
| 91 | std::stringstream res; | ||
| 92 | res << "[Binary: "; | ||
| 93 | for (unsigned byte : binary) | ||
| 94 | res << std::hex << std::setw(2) << std::setfill('0') << byte; | ||
| 95 | res << ']'; | ||
| 96 | return res.str(); | ||
| 97 | } | ||
| 98 | case Char: | ||
| 99 | return "[Char: " + AsString() + ']'; | ||
| 100 | case Wchar: | ||
| 101 | return "[Wchar: " + AsString() + ']'; | ||
| 102 | default: | ||
| 103 | // TODO(yuriks): Add assert | ||
| 104 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); | ||
| 105 | return {}; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | const std::string AsString() const { | ||
| 110 | switch (GetType()) { | ||
| 111 | case Char: | ||
| 112 | return string; | ||
| 113 | case Wchar: | ||
| 114 | return Common::UTF16ToUTF8(u16str); | ||
| 115 | case Empty: | ||
| 116 | return {}; | ||
| 117 | default: | ||
| 118 | // TODO(yuriks): Add assert | ||
| 119 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); | ||
| 120 | return {}; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | const std::u16string AsU16Str() const { | ||
| 125 | switch (GetType()) { | ||
| 126 | case Char: | ||
| 127 | return Common::UTF8ToUTF16(string); | ||
| 128 | case Wchar: | ||
| 129 | return u16str; | ||
| 130 | case Empty: | ||
| 131 | return {}; | ||
| 132 | default: | ||
| 133 | // TODO(yuriks): Add assert | ||
| 134 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); | ||
| 135 | return {}; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | const std::vector<u8> AsBinary() const { | ||
| 140 | switch (GetType()) { | ||
| 141 | case Binary: | ||
| 142 | return binary; | ||
| 143 | case Char: | ||
| 144 | return std::vector<u8>(string.begin(), string.end()); | ||
| 145 | case Wchar: | ||
| 146 | return std::vector<u8>(u16str.begin(), u16str.end()); | ||
| 147 | case Empty: | ||
| 148 | return {}; | ||
| 149 | default: | ||
| 150 | // TODO(yuriks): Add assert | ||
| 151 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); | ||
| 152 | return {}; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | private: | ||
| 157 | LowPathType type; | ||
| 158 | std::vector<u8> binary; | ||
| 159 | std::string string; | ||
| 160 | std::u16string u16str; | ||
| 161 | }; | ||
| 162 | |||
| 163 | class ArchiveBackend : NonCopyable { | ||
| 164 | public: | ||
| 165 | virtual ~ArchiveBackend() { } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 169 | */ | ||
| 170 | virtual std::string GetName() const = 0; | ||
| 171 | |||
| 172 | /** | ||
| 173 | * Open a file specified by its path, using the specified mode | ||
| 174 | * @param path Path relative to the archive | ||
| 175 | * @param mode Mode to open the file with | ||
| 176 | * @return Opened file, or nullptr | ||
| 177 | */ | ||
| 178 | virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0; | ||
| 179 | |||
| 180 | /** | ||
| 181 | * Delete a file specified by its path | ||
| 182 | * @param path Path relative to the archive | ||
| 183 | * @return Whether the file could be deleted | ||
| 184 | */ | ||
| 185 | virtual bool DeleteFile(const FileSys::Path& path) const = 0; | ||
| 186 | |||
| 187 | /** | ||
| 188 | * Rename a File specified by its path | ||
| 189 | * @param src_path Source path relative to the archive | ||
| 190 | * @param dest_path Destination path relative to the archive | ||
| 191 | * @return Whether rename succeeded | ||
| 192 | */ | ||
| 193 | virtual bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0; | ||
| 194 | |||
| 195 | /** | ||
| 196 | * Delete a directory specified by its path | ||
| 197 | * @param path Path relative to the archive | ||
| 198 | * @return Whether the directory could be deleted | ||
| 199 | */ | ||
| 200 | virtual bool DeleteDirectory(const FileSys::Path& path) const = 0; | ||
| 201 | |||
| 202 | /** | ||
| 203 | * Create a directory specified by its path | ||
| 204 | * @param path Path relative to the archive | ||
| 205 | * @return Whether the directory could be created | ||
| 206 | */ | ||
| 207 | virtual bool CreateDirectory(const Path& path) const = 0; | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Rename a Directory specified by its path | ||
| 211 | * @param src_path Source path relative to the archive | ||
| 212 | * @param dest_path Destination path relative to the archive | ||
| 213 | * @return Whether rename succeeded | ||
| 214 | */ | ||
| 215 | virtual bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0; | ||
| 216 | |||
| 217 | /** | ||
| 218 | * Open a directory specified by its path | ||
| 219 | * @param path Path relative to the archive | ||
| 220 | * @return Opened directory, or nullptr | ||
| 221 | */ | ||
| 222 | virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; | ||
| 223 | }; | ||
| 224 | |||
| 225 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index cc759faa8..0709b62a1 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | ||
| 6 | |||
| 5 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 6 | 8 | ||
| 7 | #include "core/file_sys/archive_romfs.h" | 9 | #include "core/file_sys/archive_romfs.h" |
| @@ -16,81 +18,67 @@ namespace FileSys { | |||
| 16 | Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { | 18 | Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { |
| 17 | // Load the RomFS from the app | 19 | // Load the RomFS from the app |
| 18 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) { | 20 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) { |
| 19 | WARN_LOG(FILESYS, "Unable to read RomFS!"); | 21 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| 20 | } | 22 | } |
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | Archive_RomFS::~Archive_RomFS() { | ||
| 24 | } | ||
| 25 | |||
| 26 | /** | 25 | /** |
| 27 | * Open a file specified by its path, using the specified mode | 26 | * Open a file specified by its path, using the specified mode |
| 28 | * @param path Path relative to the archive | 27 | * @param path Path relative to the archive |
| 29 | * @param mode Mode to open the file with | 28 | * @param mode Mode to open the file with |
| 30 | * @return Opened file, or nullptr | 29 | * @return Opened file, or nullptr |
| 31 | */ | 30 | */ |
| 32 | std::unique_ptr<File> Archive_RomFS::OpenFile(const std::string& path, const Mode mode) const { | 31 | std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { |
| 33 | return std::unique_ptr<File>(new File_RomFS); | 32 | return std::make_unique<File_RomFS>(this); |
| 34 | } | 33 | } |
| 35 | 34 | ||
| 36 | /** | 35 | /** |
| 37 | * Create a directory specified by its path | 36 | * Delete a file specified by its path |
| 38 | * @param path Path relative to the archive | 37 | * @param path Path relative to the archive |
| 39 | * @return Whether the directory could be created | 38 | * @return Whether the file could be deleted |
| 40 | */ | 39 | */ |
| 41 | bool Archive_RomFS::CreateDirectory(const std::string& path) const { | 40 | bool Archive_RomFS::DeleteFile(const FileSys::Path& path) const { |
| 42 | ERROR_LOG(FILESYS, "Attempted to create a directory in ROMFS."); | 41 | LOG_WARNING(Service_FS, "Attempted to delete a file from ROMFS."); |
| 43 | return false; | 42 | return false; |
| 44 | }; | 43 | } |
| 45 | 44 | ||
| 46 | /** | 45 | bool Archive_RomFS::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { |
| 47 | * Open a directory specified by its path | 46 | LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS."); |
| 48 | * @param path Path relative to the archive | 47 | return false; |
| 49 | * @return Opened directory, or nullptr | ||
| 50 | */ | ||
| 51 | std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const std::string& path) const { | ||
| 52 | return std::unique_ptr<Directory>(new Directory_RomFS); | ||
| 53 | } | 48 | } |
| 54 | 49 | ||
| 55 | /** | 50 | /** |
| 56 | * Read data from the archive | 51 | * Delete a directory specified by its path |
| 57 | * @param offset Offset in bytes to start reading data from | 52 | * @param path Path relative to the archive |
| 58 | * @param length Length in bytes of data to read from archive | 53 | * @return Whether the directory could be deleted |
| 59 | * @param buffer Buffer to read data into | ||
| 60 | * @return Number of bytes read | ||
| 61 | */ | 54 | */ |
| 62 | size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { | 55 | bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const { |
| 63 | DEBUG_LOG(FILESYS, "called offset=%llu, length=%d", offset, length); | 56 | LOG_WARNING(Service_FS, "Attempted to delete a directory from ROMFS."); |
| 64 | memcpy(buffer, &raw_data[(u32)offset], length); | 57 | return false; |
| 65 | return length; | ||
| 66 | } | 58 | } |
| 67 | 59 | ||
| 68 | /** | 60 | /** |
| 69 | * Write data to the archive | 61 | * Create a directory specified by its path |
| 70 | * @param offset Offset in bytes to start writing data to | 62 | * @param path Path relative to the archive |
| 71 | * @param length Length in bytes of data to write to archive | 63 | * @return Whether the directory could be created |
| 72 | * @param buffer Buffer to write data from | ||
| 73 | * @param flush The flush parameters (0 == do not flush) | ||
| 74 | * @return Number of bytes written | ||
| 75 | */ | 64 | */ |
| 76 | size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { | 65 | bool Archive_RomFS::CreateDirectory(const Path& path) const { |
| 77 | ERROR_LOG(FILESYS, "Attempted to write to ROMFS."); | 66 | LOG_WARNING(Service_FS, "Attempted to create a directory in ROMFS."); |
| 78 | return 0; | 67 | return false; |
| 79 | } | 68 | } |
| 80 | 69 | ||
| 81 | /** | 70 | bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { |
| 82 | * Get the size of the archive in bytes | 71 | LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS."); |
| 83 | * @return Size of the archive in bytes | 72 | return false; |
| 84 | */ | ||
| 85 | size_t Archive_RomFS::GetSize() const { | ||
| 86 | return sizeof(u8) * raw_data.size(); | ||
| 87 | } | 73 | } |
| 88 | 74 | ||
| 89 | /** | 75 | /** |
| 90 | * Set the size of the archive in bytes | 76 | * Open a directory specified by its path |
| 77 | * @param path Path relative to the archive | ||
| 78 | * @return Opened directory, or nullptr | ||
| 91 | */ | 79 | */ |
| 92 | void Archive_RomFS::SetSize(const u64 size) { | 80 | std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const { |
| 93 | ERROR_LOG(FILESYS, "Attempted to set the size of ROMFS"); | 81 | return std::make_unique<Directory_RomFS>(); |
| 94 | } | 82 | } |
| 95 | 83 | ||
| 96 | } // namespace FileSys | 84 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index ae2344e82..5b1ee6332 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | #include "core/file_sys/archive.h" | 11 | #include "core/file_sys/archive_backend.h" |
| 12 | #include "core/loader/loader.h" | 12 | #include "core/loader/loader.h" |
| 13 | 13 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -17,16 +17,11 @@ | |||
| 17 | namespace FileSys { | 17 | namespace FileSys { |
| 18 | 18 | ||
| 19 | /// File system interface to the RomFS archive | 19 | /// File system interface to the RomFS archive |
| 20 | class Archive_RomFS final : public Archive { | 20 | class Archive_RomFS final : public ArchiveBackend { |
| 21 | public: | 21 | public: |
| 22 | Archive_RomFS(const Loader::AppLoader& app_loader); | 22 | Archive_RomFS(const Loader::AppLoader& app_loader); |
| 23 | ~Archive_RomFS() override; | ||
| 24 | 23 | ||
| 25 | /** | 24 | std::string GetName() const override { return "RomFS"; } |
| 26 | * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) | ||
| 27 | * @return IdCode of the archive | ||
| 28 | */ | ||
| 29 | IdCode GetIdCode() const override { return IdCode::RomFS; }; | ||
| 30 | 25 | ||
| 31 | /** | 26 | /** |
| 32 | * Open a file specified by its path, using the specified mode | 27 | * Open a file specified by its path, using the specified mode |
| @@ -34,53 +29,55 @@ public: | |||
| 34 | * @param mode Mode to open the file with | 29 | * @param mode Mode to open the file with |
| 35 | * @return Opened file, or nullptr | 30 | * @return Opened file, or nullptr |
| 36 | */ | 31 | */ |
| 37 | std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override; | 32 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; |
| 38 | 33 | ||
| 39 | /** | 34 | /** |
| 40 | * Create a directory specified by its path | 35 | * Delete a file specified by its path |
| 41 | * @param path Path relative to the archive | 36 | * @param path Path relative to the archive |
| 42 | * @return Whether the directory could be created | 37 | * @return Whether the file could be deleted |
| 43 | */ | 38 | */ |
| 44 | bool CreateDirectory(const std::string& path) const override; | 39 | bool DeleteFile(const FileSys::Path& path) const override; |
| 45 | 40 | ||
| 46 | /** | 41 | /** |
| 47 | * Open a directory specified by its path | 42 | * Rename a File specified by its path |
| 48 | * @param path Path relative to the archive | 43 | * @param src_path Source path relative to the archive |
| 49 | * @return Opened directory, or nullptr | 44 | * @param dest_path Destination path relative to the archive |
| 45 | * @return Whether rename succeeded | ||
| 50 | */ | 46 | */ |
| 51 | std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override; | 47 | bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; |
| 52 | 48 | ||
| 53 | /** | 49 | /** |
| 54 | * Read data from the archive | 50 | * Delete a directory specified by its path |
| 55 | * @param offset Offset in bytes to start reading data from | 51 | * @param path Path relative to the archive |
| 56 | * @param length Length in bytes of data to read from archive | 52 | * @return Whether the directory could be deleted |
| 57 | * @param buffer Buffer to read data into | ||
| 58 | * @return Number of bytes read | ||
| 59 | */ | 53 | */ |
| 60 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | 54 | bool DeleteDirectory(const FileSys::Path& path) const override; |
| 61 | 55 | ||
| 62 | /** | 56 | /** |
| 63 | * Write data to the archive | 57 | * Create a directory specified by its path |
| 64 | * @param offset Offset in bytes to start writing data to | 58 | * @param path Path relative to the archive |
| 65 | * @param length Length in bytes of data to write to archive | 59 | * @return Whether the directory could be created |
| 66 | * @param buffer Buffer to write data from | ||
| 67 | * @param flush The flush parameters (0 == do not flush) | ||
| 68 | * @return Number of bytes written | ||
| 69 | */ | 60 | */ |
| 70 | size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; | 61 | bool CreateDirectory(const Path& path) const override; |
| 71 | 62 | ||
| 72 | /** | 63 | /** |
| 73 | * Get the size of the archive in bytes | 64 | * Rename a Directory specified by its path |
| 74 | * @return Size of the archive in bytes | 65 | * @param src_path Source path relative to the archive |
| 66 | * @param dest_path Destination path relative to the archive | ||
| 67 | * @return Whether rename succeeded | ||
| 75 | */ | 68 | */ |
| 76 | size_t GetSize() const override; | 69 | bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; |
| 77 | 70 | ||
| 78 | /** | 71 | /** |
| 79 | * Set the size of the archive in bytes | 72 | * Open a directory specified by its path |
| 73 | * @param path Path relative to the archive | ||
| 74 | * @return Opened directory, or nullptr | ||
| 80 | */ | 75 | */ |
| 81 | void SetSize(const u64 size) override; | 76 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |
| 82 | 77 | ||
| 83 | private: | 78 | private: |
| 79 | friend class File_RomFS; | ||
| 80 | |||
| 84 | std::vector<u8> raw_data; | 81 | std::vector<u8> raw_data; |
| 85 | }; | 82 | }; |
| 86 | 83 | ||
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp new file mode 100644 index 000000000..2414564e4 --- /dev/null +++ b/src/core/file_sys/archive_savedata.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/archive_savedata.h" | ||
| 11 | #include "core/file_sys/disk_archive.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id) | ||
| 20 | : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) { | ||
| 21 | LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); | ||
| 22 | } | ||
| 23 | |||
| 24 | bool Archive_SaveData::Initialize() { | ||
| 25 | if (!FileUtil::CreateFullPath(mount_point)) { | ||
| 26 | LOG_ERROR(Service_FS, "Unable to create SaveData path."); | ||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h new file mode 100644 index 000000000..b3e561130 --- /dev/null +++ b/src/core/file_sys/archive_savedata.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/disk_archive.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the SaveData archive | ||
| 18 | class Archive_SaveData final : public DiskArchive { | ||
| 19 | public: | ||
| 20 | Archive_SaveData(const std::string& mount_point, u64 program_id); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Initialize the archive. | ||
| 24 | * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists, | ||
| 25 | * Success if it was created properly and Failure if there was any error | ||
| 26 | */ | ||
| 27 | bool Initialize(); | ||
| 28 | |||
| 29 | std::string GetName() const override { return "SaveData"; } | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 66931e93e..dccdf7f67 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp | |||
| @@ -8,8 +8,7 @@ | |||
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | 9 | ||
| 10 | #include "core/file_sys/archive_sdmc.h" | 10 | #include "core/file_sys/archive_sdmc.h" |
| 11 | #include "core/file_sys/directory_sdmc.h" | 11 | #include "core/file_sys/disk_archive.h" |
| 12 | #include "core/file_sys/file_sdmc.h" | ||
| 13 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 14 | 13 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -17,113 +16,22 @@ | |||
| 17 | 16 | ||
| 18 | namespace FileSys { | 17 | namespace FileSys { |
| 19 | 18 | ||
| 20 | Archive_SDMC::Archive_SDMC(const std::string& mount_point) { | 19 | Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) { |
| 21 | this->mount_point = mount_point; | 20 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); |
| 22 | DEBUG_LOG(FILESYS, "Directory %s set as SDMC.", mount_point.c_str()); | ||
| 23 | } | 21 | } |
| 24 | 22 | ||
| 25 | Archive_SDMC::~Archive_SDMC() { | ||
| 26 | } | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Initialize the archive. | ||
| 30 | * @return true if it initialized successfully | ||
| 31 | */ | ||
| 32 | bool Archive_SDMC::Initialize() { | 23 | bool Archive_SDMC::Initialize() { |
| 33 | if (!Settings::values.use_virtual_sd) { | 24 | if (!Settings::values.use_virtual_sd) { |
| 34 | WARN_LOG(FILESYS, "SDMC disabled by config."); | 25 | LOG_WARNING(Service_FS, "SDMC disabled by config."); |
| 35 | return false; | 26 | return false; |
| 36 | } | 27 | } |
| 37 | 28 | ||
| 38 | if (!FileUtil::CreateFullPath(mount_point)) { | 29 | if (!FileUtil::CreateFullPath(mount_point)) { |
| 39 | WARN_LOG(FILESYS, "Unable to create SDMC path."); | 30 | LOG_ERROR(Service_FS, "Unable to create SDMC path."); |
| 40 | return false; | 31 | return false; |
| 41 | } | 32 | } |
| 42 | 33 | ||
| 43 | return true; | 34 | return true; |
| 44 | } | 35 | } |
| 45 | 36 | ||
| 46 | /** | ||
| 47 | * Open a file specified by its path, using the specified mode | ||
| 48 | * @param path Path relative to the archive | ||
| 49 | * @param mode Mode to open the file with | ||
| 50 | * @return Opened file, or nullptr | ||
| 51 | */ | ||
| 52 | std::unique_ptr<File> Archive_SDMC::OpenFile(const std::string& path, const Mode mode) const { | ||
| 53 | DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.c_str(), mode); | ||
| 54 | File_SDMC* file = new File_SDMC(this, path, mode); | ||
| 55 | if (!file->Open()) | ||
| 56 | return nullptr; | ||
| 57 | return std::unique_ptr<File>(file); | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Create a directory specified by its path | ||
| 62 | * @param path Path relative to the archive | ||
| 63 | * @return Whether the directory could be created | ||
| 64 | */ | ||
| 65 | bool Archive_SDMC::CreateDirectory(const std::string& path) const { | ||
| 66 | return FileUtil::CreateDir(GetMountPoint() + path); | ||
| 67 | } | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Open a directory specified by its path | ||
| 71 | * @param path Path relative to the archive | ||
| 72 | * @return Opened directory, or nullptr | ||
| 73 | */ | ||
| 74 | std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const std::string& path) const { | ||
| 75 | DEBUG_LOG(FILESYS, "called path=%s", path.c_str()); | ||
| 76 | Directory_SDMC* directory = new Directory_SDMC(this, path); | ||
| 77 | return std::unique_ptr<Directory>(directory); | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Read data from the archive | ||
| 82 | * @param offset Offset in bytes to start reading archive from | ||
| 83 | * @param length Length in bytes to read data from archive | ||
| 84 | * @param buffer Buffer to read data into | ||
| 85 | * @return Number of bytes read | ||
| 86 | */ | ||
| 87 | size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 88 | ERROR_LOG(FILESYS, "(UNIMPLEMENTED)"); | ||
| 89 | return -1; | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Write data to the archive | ||
| 94 | * @param offset Offset in bytes to start writing data to | ||
| 95 | * @param length Length in bytes of data to write to archive | ||
| 96 | * @param buffer Buffer to write data from | ||
| 97 | * @param flush The flush parameters (0 == do not flush) | ||
| 98 | * @return Number of bytes written | ||
| 99 | */ | ||
| 100 | size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { | ||
| 101 | ERROR_LOG(FILESYS, "(UNIMPLEMENTED)"); | ||
| 102 | return -1; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Get the size of the archive in bytes | ||
| 107 | * @return Size of the archive in bytes | ||
| 108 | */ | ||
| 109 | size_t Archive_SDMC::GetSize() const { | ||
| 110 | ERROR_LOG(FILESYS, "(UNIMPLEMENTED)"); | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Set the size of the archive in bytes | ||
| 116 | */ | ||
| 117 | void Archive_SDMC::SetSize(const u64 size) { | ||
| 118 | ERROR_LOG(FILESYS, "(UNIMPLEMENTED)"); | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Getter for the path used for this Archive | ||
| 123 | * @return Mount point of that passthrough archive | ||
| 124 | */ | ||
| 125 | std::string Archive_SDMC::GetMountPoint() const { | ||
| 126 | return mount_point; | ||
| 127 | } | ||
| 128 | |||
| 129 | } // namespace FileSys | 37 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 0e059b635..c84c6948e 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | #include "core/file_sys/archive.h" | 9 | #include "core/file_sys/disk_archive.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -15,10 +15,9 @@ | |||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the SDMC archive | 17 | /// File system interface to the SDMC archive |
| 18 | class Archive_SDMC final : public Archive { | 18 | class Archive_SDMC final : public DiskArchive { |
| 19 | public: | 19 | public: |
| 20 | Archive_SDMC(const std::string& mount_point); | 20 | Archive_SDMC(const std::string& mount_point); |
| 21 | ~Archive_SDMC() override; | ||
| 22 | 21 | ||
| 23 | /** | 22 | /** |
| 24 | * Initialize the archive. | 23 | * Initialize the archive. |
| @@ -26,72 +25,7 @@ public: | |||
| 26 | */ | 25 | */ |
| 27 | bool Initialize(); | 26 | bool Initialize(); |
| 28 | 27 | ||
| 29 | /** | 28 | std::string GetName() const override { return "SDMC"; } |
| 30 | * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) | ||
| 31 | * @return IdCode of the archive | ||
| 32 | */ | ||
| 33 | IdCode GetIdCode() const override { return IdCode::SDMC; }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Open a file specified by its path, using the specified mode | ||
| 37 | * @param path Path relative to the archive | ||
| 38 | * @param mode Mode to open the file with | ||
| 39 | * @return Opened file, or nullptr | ||
| 40 | */ | ||
| 41 | std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Create a directory specified by its path | ||
| 45 | * @param path Path relative to the archive | ||
| 46 | * @return Whether the directory could be created | ||
| 47 | */ | ||
| 48 | bool CreateDirectory(const std::string& path) const override; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Open a directory specified by its path | ||
| 52 | * @param path Path relative to the archive | ||
| 53 | * @return Opened directory, or nullptr | ||
| 54 | */ | ||
| 55 | std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Read data from the archive | ||
| 59 | * @param offset Offset in bytes to start reading archive from | ||
| 60 | * @param length Length in bytes to read data from archive | ||
| 61 | * @param buffer Buffer to read data into | ||
| 62 | * @return Number of bytes read | ||
| 63 | */ | ||
| 64 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Write data to the archive | ||
| 68 | * @param offset Offset in bytes to start writing data to | ||
| 69 | * @param length Length in bytes of data to write to archive | ||
| 70 | * @param buffer Buffer to write data from | ||
| 71 | * @param flush The flush parameters (0 == do not flush) | ||
| 72 | * @return Number of bytes written | ||
| 73 | */ | ||
| 74 | size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Get the size of the archive in bytes | ||
| 78 | * @return Size of the archive in bytes | ||
| 79 | */ | ||
| 80 | size_t GetSize() const override; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Set the size of the archive in bytes | ||
| 84 | */ | ||
| 85 | void SetSize(const u64 size) override; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Getter for the path used for this Archive | ||
| 89 | * @return Mount point of that passthrough archive | ||
| 90 | */ | ||
| 91 | std::string GetMountPoint() const; | ||
| 92 | |||
| 93 | private: | ||
| 94 | std::string mount_point; | ||
| 95 | }; | 29 | }; |
| 96 | 30 | ||
| 97 | } // namespace FileSys | 31 | } // namespace FileSys |
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h index e10431337..188746a6f 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory_backend.h | |||
| @@ -36,10 +36,16 @@ static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension i | |||
| 36 | static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); | 36 | static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); |
| 37 | static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); | 37 | static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); |
| 38 | 38 | ||
| 39 | class Directory : NonCopyable { | 39 | class DirectoryBackend : NonCopyable { |
| 40 | public: | 40 | public: |
| 41 | Directory() { } | 41 | DirectoryBackend() { } |
| 42 | virtual ~Directory() { } | 42 | virtual ~DirectoryBackend() { } |
| 43 | |||
| 44 | /** | ||
| 45 | * Open the directory | ||
| 46 | * @return true if the directory opened correctly | ||
| 47 | */ | ||
| 48 | virtual bool Open() = 0; | ||
| 43 | 49 | ||
| 44 | /** | 50 | /** |
| 45 | * List files contained in the directory | 51 | * List files contained in the directory |
diff --git a/src/core/file_sys/directory_romfs.cpp b/src/core/file_sys/directory_romfs.cpp index 4e8f4c04d..e6d571391 100644 --- a/src/core/file_sys/directory_romfs.cpp +++ b/src/core/file_sys/directory_romfs.cpp | |||
| @@ -17,6 +17,10 @@ Directory_RomFS::Directory_RomFS() { | |||
| 17 | Directory_RomFS::~Directory_RomFS() { | 17 | Directory_RomFS::~Directory_RomFS() { |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | bool Directory_RomFS::Open() { | ||
| 21 | return false; | ||
| 22 | } | ||
| 23 | |||
| 20 | /** | 24 | /** |
| 21 | * List files contained in the directory | 25 | * List files contained in the directory |
| 22 | * @param count Number of entries to return at once in entries | 26 | * @param count Number of entries to return at once in entries |
diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h index 4b71c4b13..b775f014d 100644 --- a/src/core/file_sys/directory_romfs.h +++ b/src/core/file_sys/directory_romfs.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | #include "core/file_sys/directory.h" | 9 | #include "core/file_sys/directory_backend.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -14,12 +14,18 @@ | |||
| 14 | 14 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | class Directory_RomFS final : public Directory { | 17 | class Directory_RomFS final : public DirectoryBackend { |
| 18 | public: | 18 | public: |
| 19 | Directory_RomFS(); | 19 | Directory_RomFS(); |
| 20 | ~Directory_RomFS() override; | 20 | ~Directory_RomFS() override; |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * Open the directory | ||
| 24 | * @return true if the directory opened correctly | ||
| 25 | */ | ||
| 26 | bool Open() override; | ||
| 27 | |||
| 28 | /** | ||
| 23 | * List files contained in the directory | 29 | * List files contained in the directory |
| 24 | * @param count Number of entries to return at once in entries | 30 | * @param count Number of entries to return at once in entries |
| 25 | * @param entries Buffer to read data into | 31 | * @param entries Buffer to read data into |
diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp deleted file mode 100644 index fd558def9..000000000 --- a/src/core/file_sys/directory_sdmc.cpp +++ /dev/null | |||
| @@ -1,81 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/directory_sdmc.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const std::string& path) { | ||
| 19 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 20 | // the root directory we set while opening the archive. | ||
| 21 | // For example, opening /../../usr/bin can give the emulated program your installed programs. | ||
| 22 | std::string absolute_path = archive->GetMountPoint() + path; | ||
| 23 | FileUtil::ScanDirectoryTree(absolute_path, directory); | ||
| 24 | children_iterator = directory.children.begin(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Directory_SDMC::~Directory_SDMC() { | ||
| 28 | Close(); | ||
| 29 | } | ||
| 30 | |||
| 31 | /** | ||
| 32 | * List files contained in the directory | ||
| 33 | * @param count Number of entries to return at once in entries | ||
| 34 | * @param entries Buffer to read data into | ||
| 35 | * @return Number of entries listed | ||
| 36 | */ | ||
| 37 | u32 Directory_SDMC::Read(const u32 count, Entry* entries) { | ||
| 38 | u32 entries_read = 0; | ||
| 39 | |||
| 40 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 41 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 42 | const std::string& filename = file.virtualName; | ||
| 43 | Entry& entry = entries[entries_read]; | ||
| 44 | |||
| 45 | WARN_LOG(FILESYS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); | ||
| 46 | |||
| 47 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 48 | for (int j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 49 | entry.filename[j] = filename[j]; | ||
| 50 | if (!filename[j]) | ||
| 51 | break; | ||
| 52 | } | ||
| 53 | |||
| 54 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 55 | |||
| 56 | entry.is_directory = file.isDirectory; | ||
| 57 | entry.is_hidden = (filename[0] == '.'); | ||
| 58 | entry.is_read_only = 0; | ||
| 59 | entry.file_size = file.size; | ||
| 60 | |||
| 61 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 62 | // most user SD cards. | ||
| 63 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 64 | // file bit. | ||
| 65 | entry.is_archive = !file.isDirectory; | ||
| 66 | |||
| 67 | ++entries_read; | ||
| 68 | ++children_iterator; | ||
| 69 | } | ||
| 70 | return entries_read; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Close the directory | ||
| 75 | * @return true if the directory closed correctly | ||
| 76 | */ | ||
| 77 | bool Directory_SDMC::Close() const { | ||
| 78 | return true; | ||
| 79 | } | ||
| 80 | |||
| 81 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h deleted file mode 100644 index cb8d32fda..000000000 --- a/src/core/file_sys/directory_sdmc.h +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/directory.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class Directory_SDMC final : public Directory { | ||
| 20 | public: | ||
| 21 | Directory_SDMC(); | ||
| 22 | Directory_SDMC(const Archive_SDMC* archive, const std::string& path); | ||
| 23 | ~Directory_SDMC() override; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * List files contained in the directory | ||
| 27 | * @param count Number of entries to return at once in entries | ||
| 28 | * @param entries Buffer to read data into | ||
| 29 | * @return Number of entries listed | ||
| 30 | */ | ||
| 31 | u32 Read(const u32 count, Entry* entries) override; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Close the directory | ||
| 35 | * @return true if the directory closed correctly | ||
| 36 | */ | ||
| 37 | bool Close() const override; | ||
| 38 | |||
| 39 | private: | ||
| 40 | u32 total_entries_in_directory; | ||
| 41 | FileUtil::FSTEntry directory; | ||
| 42 | |||
| 43 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 44 | // from the next one. This iterator will always point to the next unread entry. | ||
| 45 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 46 | }; | ||
| 47 | |||
| 48 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp new file mode 100644 index 000000000..eabf58057 --- /dev/null +++ b/src/core/file_sys/disk_archive.cpp | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/disk_archive.h" | ||
| 11 | #include "core/settings.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { | ||
| 19 | LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); | ||
| 20 | DiskFile* file = new DiskFile(this, path, mode); | ||
| 21 | if (!file->Open()) | ||
| 22 | return nullptr; | ||
| 23 | return std::unique_ptr<FileBackend>(file); | ||
| 24 | } | ||
| 25 | |||
| 26 | bool DiskArchive::DeleteFile(const FileSys::Path& path) const { | ||
| 27 | return FileUtil::Delete(GetMountPoint() + path.AsString()); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 31 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 32 | } | ||
| 33 | |||
| 34 | bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const { | ||
| 35 | return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | ||
| 36 | } | ||
| 37 | |||
| 38 | bool DiskArchive::CreateDirectory(const Path& path) const { | ||
| 39 | return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 43 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { | ||
| 47 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||
| 48 | DiskDirectory* directory = new DiskDirectory(this, path); | ||
| 49 | if (!directory->Open()) | ||
| 50 | return nullptr; | ||
| 51 | return std::unique_ptr<DirectoryBackend>(directory); | ||
| 52 | } | ||
| 53 | |||
| 54 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 55 | |||
| 56 | DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) { | ||
| 57 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 58 | // the root directory we set while opening the archive. | ||
| 59 | // For example, opening /../../etc/passwd can give the emulated program your users list. | ||
| 60 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 61 | this->mode.hex = mode.hex; | ||
| 62 | this->archive = archive; | ||
| 63 | } | ||
| 64 | |||
| 65 | bool DiskFile::Open() { | ||
| 66 | if (!mode.create_flag && !FileUtil::Exists(path)) { | ||
| 67 | LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||
| 68 | return false; | ||
| 69 | } | ||
| 70 | |||
| 71 | std::string mode_string; | ||
| 72 | if (mode.create_flag) | ||
| 73 | mode_string = "w+"; | ||
| 74 | else if (mode.write_flag) | ||
| 75 | mode_string = "r+"; // Files opened with Write access can be read from | ||
| 76 | else if (mode.read_flag) | ||
| 77 | mode_string = "r"; | ||
| 78 | |||
| 79 | // Open the file in binary mode, to avoid problems with CR/LF on Windows systems | ||
| 80 | mode_string += "b"; | ||
| 81 | |||
| 82 | file = new FileUtil::IOFile(path, mode_string.c_str()); | ||
| 83 | return true; | ||
| 84 | } | ||
| 85 | |||
| 86 | size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 87 | file->Seek(offset, SEEK_SET); | ||
| 88 | return file->ReadBytes(buffer, length); | ||
| 89 | } | ||
| 90 | |||
| 91 | size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||
| 92 | file->Seek(offset, SEEK_SET); | ||
| 93 | size_t written = file->WriteBytes(buffer, length); | ||
| 94 | if (flush) | ||
| 95 | file->Flush(); | ||
| 96 | return written; | ||
| 97 | } | ||
| 98 | |||
| 99 | size_t DiskFile::GetSize() const { | ||
| 100 | return static_cast<size_t>(file->GetSize()); | ||
| 101 | } | ||
| 102 | |||
| 103 | bool DiskFile::SetSize(const u64 size) const { | ||
| 104 | file->Resize(size); | ||
| 105 | file->Flush(); | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | bool DiskFile::Close() const { | ||
| 110 | return file->Close(); | ||
| 111 | } | ||
| 112 | |||
| 113 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 114 | |||
| 115 | DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) { | ||
| 116 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 117 | // the root directory we set while opening the archive. | ||
| 118 | // For example, opening /../../usr/bin can give the emulated program your installed programs. | ||
| 119 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 120 | this->archive = archive; | ||
| 121 | } | ||
| 122 | |||
| 123 | bool DiskDirectory::Open() { | ||
| 124 | if (!FileUtil::IsDirectory(path)) | ||
| 125 | return false; | ||
| 126 | FileUtil::ScanDirectoryTree(path, directory); | ||
| 127 | children_iterator = directory.children.begin(); | ||
| 128 | return true; | ||
| 129 | } | ||
| 130 | |||
| 131 | u32 DiskDirectory::Read(const u32 count, Entry* entries) { | ||
| 132 | u32 entries_read = 0; | ||
| 133 | |||
| 134 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 135 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 136 | const std::string& filename = file.virtualName; | ||
| 137 | Entry& entry = entries[entries_read]; | ||
| 138 | |||
| 139 | LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); | ||
| 140 | |||
| 141 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 142 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 143 | entry.filename[j] = filename[j]; | ||
| 144 | if (!filename[j]) | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | |||
| 148 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 149 | |||
| 150 | entry.is_directory = file.isDirectory; | ||
| 151 | entry.is_hidden = (filename[0] == '.'); | ||
| 152 | entry.is_read_only = 0; | ||
| 153 | entry.file_size = file.size; | ||
| 154 | |||
| 155 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 156 | // most user SD cards. | ||
| 157 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 158 | // file bit. | ||
| 159 | entry.is_archive = !file.isDirectory; | ||
| 160 | |||
| 161 | ++entries_read; | ||
| 162 | ++children_iterator; | ||
| 163 | } | ||
| 164 | return entries_read; | ||
| 165 | } | ||
| 166 | |||
| 167 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h new file mode 100644 index 000000000..778c83953 --- /dev/null +++ b/src/core/file_sys/disk_archive.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive_backend.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Helper which implements a backend accessing the host machine's filesystem. | ||
| 19 | * This should be subclassed by concrete archive types, which will provide the | ||
| 20 | * base directory on the host filesystem and override any required functionality. | ||
| 21 | */ | ||
| 22 | class DiskArchive : public ArchiveBackend { | ||
| 23 | public: | ||
| 24 | DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} | ||
| 25 | |||
| 26 | virtual std::string GetName() const = 0; | ||
| 27 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||
| 28 | bool DeleteFile(const FileSys::Path& path) const override; | ||
| 29 | bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 30 | bool DeleteDirectory(const FileSys::Path& path) const override; | ||
| 31 | bool CreateDirectory(const Path& path) const override; | ||
| 32 | bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 33 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Getter for the path used for this Archive | ||
| 37 | * @return Mount point of that passthrough archive | ||
| 38 | */ | ||
| 39 | const std::string& GetMountPoint() const { | ||
| 40 | return mount_point; | ||
| 41 | } | ||
| 42 | |||
| 43 | protected: | ||
| 44 | std::string mount_point; | ||
| 45 | }; | ||
| 46 | |||
| 47 | class DiskFile : public FileBackend { | ||
| 48 | public: | ||
| 49 | DiskFile(); | ||
| 50 | DiskFile(const DiskArchive* archive, const Path& path, const Mode mode); | ||
| 51 | |||
| 52 | ~DiskFile() override { | ||
| 53 | Close(); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool Open() override; | ||
| 57 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 58 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||
| 59 | size_t GetSize() const override; | ||
| 60 | bool SetSize(const u64 size) const override; | ||
| 61 | bool Close() const override; | ||
| 62 | |||
| 63 | void Flush() const override { | ||
| 64 | file->Flush(); | ||
| 65 | } | ||
| 66 | |||
| 67 | protected: | ||
| 68 | const DiskArchive* archive; | ||
| 69 | std::string path; | ||
| 70 | Mode mode; | ||
| 71 | FileUtil::IOFile* file; | ||
| 72 | }; | ||
| 73 | |||
| 74 | class DiskDirectory : public DirectoryBackend { | ||
| 75 | public: | ||
| 76 | DiskDirectory(); | ||
| 77 | DiskDirectory(const DiskArchive* archive, const Path& path); | ||
| 78 | |||
| 79 | ~DiskDirectory() override { | ||
| 80 | Close(); | ||
| 81 | } | ||
| 82 | |||
| 83 | bool Open() override; | ||
| 84 | u32 Read(const u32 count, Entry* entries) override; | ||
| 85 | |||
| 86 | bool Close() const override { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | protected: | ||
| 91 | const DiskArchive* archive; | ||
| 92 | std::string path; | ||
| 93 | u32 total_entries_in_directory; | ||
| 94 | FileUtil::FSTEntry directory; | ||
| 95 | |||
| 96 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 97 | // from the next one. This iterator will always point to the next unread entry. | ||
| 98 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 99 | }; | ||
| 100 | |||
| 101 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h index 4013b6c3e..539ec7314 100644 --- a/src/core/file_sys/file.h +++ b/src/core/file_sys/file_backend.h | |||
| @@ -13,10 +13,10 @@ | |||
| 13 | 13 | ||
| 14 | namespace FileSys { | 14 | namespace FileSys { |
| 15 | 15 | ||
| 16 | class File : NonCopyable { | 16 | class FileBackend : NonCopyable { |
| 17 | public: | 17 | public: |
| 18 | File() { } | 18 | FileBackend() { } |
| 19 | virtual ~File() { } | 19 | virtual ~FileBackend() { } |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Open the file | 22 | * Open the file |
| @@ -61,6 +61,11 @@ public: | |||
| 61 | * @return true if the file closed correctly | 61 | * @return true if the file closed correctly |
| 62 | */ | 62 | */ |
| 63 | virtual bool Close() const = 0; | 63 | virtual bool Close() const = 0; |
| 64 | |||
| 65 | /** | ||
| 66 | * Flushes the file | ||
| 67 | */ | ||
| 68 | virtual void Flush() const = 0; | ||
| 64 | }; | 69 | }; |
| 65 | 70 | ||
| 66 | } // namespace FileSys | 71 | } // namespace FileSys |
diff --git a/src/core/file_sys/file_romfs.cpp b/src/core/file_sys/file_romfs.cpp index b55708df4..5f38c2704 100644 --- a/src/core/file_sys/file_romfs.cpp +++ b/src/core/file_sys/file_romfs.cpp | |||
| @@ -5,24 +5,19 @@ | |||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | 6 | ||
| 7 | #include "core/file_sys/file_romfs.h" | 7 | #include "core/file_sys/file_romfs.h" |
| 8 | #include "core/file_sys/archive_romfs.h" | ||
| 8 | 9 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // FileSys namespace | 11 | // FileSys namespace |
| 11 | 12 | ||
| 12 | namespace FileSys { | 13 | namespace FileSys { |
| 13 | 14 | ||
| 14 | File_RomFS::File_RomFS() { | ||
| 15 | } | ||
| 16 | |||
| 17 | File_RomFS::~File_RomFS() { | ||
| 18 | } | ||
| 19 | |||
| 20 | /** | 15 | /** |
| 21 | * Open the file | 16 | * Open the file |
| 22 | * @return true if the file opened correctly | 17 | * @return true if the file opened correctly |
| 23 | */ | 18 | */ |
| 24 | bool File_RomFS::Open() { | 19 | bool File_RomFS::Open() { |
| 25 | return false; | 20 | return true; |
| 26 | } | 21 | } |
| 27 | 22 | ||
| 28 | /** | 23 | /** |
| @@ -33,7 +28,9 @@ bool File_RomFS::Open() { | |||
| 33 | * @return Number of bytes read | 28 | * @return Number of bytes read |
| 34 | */ | 29 | */ |
| 35 | size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { | 30 | size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { |
| 36 | return -1; | 31 | LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); |
| 32 | memcpy(buffer, &archive->raw_data[(u32)offset], length); | ||
| 33 | return length; | ||
| 37 | } | 34 | } |
| 38 | 35 | ||
| 39 | /** | 36 | /** |
| @@ -45,7 +42,8 @@ size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { | |||
| 45 | * @return Number of bytes written | 42 | * @return Number of bytes written |
| 46 | */ | 43 | */ |
| 47 | size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | 44 | size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { |
| 48 | return -1; | 45 | LOG_WARNING(Service_FS, "Attempted to write to ROMFS."); |
| 46 | return 0; | ||
| 49 | } | 47 | } |
| 50 | 48 | ||
| 51 | /** | 49 | /** |
| @@ -53,7 +51,7 @@ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, co | |||
| 53 | * @return Size of the file in bytes | 51 | * @return Size of the file in bytes |
| 54 | */ | 52 | */ |
| 55 | size_t File_RomFS::GetSize() const { | 53 | size_t File_RomFS::GetSize() const { |
| 56 | return -1; | 54 | return sizeof(u8) * archive->raw_data.size(); |
| 57 | } | 55 | } |
| 58 | 56 | ||
| 59 | /** | 57 | /** |
| @@ -62,6 +60,7 @@ size_t File_RomFS::GetSize() const { | |||
| 62 | * @return true if successful | 60 | * @return true if successful |
| 63 | */ | 61 | */ |
| 64 | bool File_RomFS::SetSize(const u64 size) const { | 62 | bool File_RomFS::SetSize(const u64 size) const { |
| 63 | LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS"); | ||
| 65 | return false; | 64 | return false; |
| 66 | } | 65 | } |
| 67 | 66 | ||
diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h index 5196701d3..32fa6b6d3 100644 --- a/src/core/file_sys/file_romfs.h +++ b/src/core/file_sys/file_romfs.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | #include "core/file_sys/file.h" | 9 | #include "core/file_sys/file_backend.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -14,10 +14,11 @@ | |||
| 14 | 14 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | class File_RomFS final : public File { | 17 | class Archive_RomFS; |
| 18 | |||
| 19 | class File_RomFS final : public FileBackend { | ||
| 18 | public: | 20 | public: |
| 19 | File_RomFS(); | 21 | File_RomFS(const Archive_RomFS* archive) : archive(archive) {} |
| 20 | ~File_RomFS() override; | ||
| 21 | 22 | ||
| 22 | /** | 23 | /** |
| 23 | * Open the file | 24 | * Open the file |
| @@ -62,6 +63,11 @@ public: | |||
| 62 | * @return true if the file closed correctly | 63 | * @return true if the file closed correctly |
| 63 | */ | 64 | */ |
| 64 | bool Close() const override; | 65 | bool Close() const override; |
| 66 | |||
| 67 | void Flush() const override { } | ||
| 68 | |||
| 69 | private: | ||
| 70 | const Archive_RomFS* archive; | ||
| 65 | }; | 71 | }; |
| 66 | 72 | ||
| 67 | } // namespace FileSys | 73 | } // namespace FileSys |
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp deleted file mode 100644 index 26204392c..000000000 --- a/src/core/file_sys/file_sdmc.cpp +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/file_sdmc.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | File_SDMC::File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode) { | ||
| 19 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 20 | // the root directory we set while opening the archive. | ||
| 21 | // For example, opening /../../etc/passwd can give the emulated program your users list. | ||
| 22 | this->path = archive->GetMountPoint() + path; | ||
| 23 | this->mode.hex = mode.hex; | ||
| 24 | } | ||
| 25 | |||
| 26 | File_SDMC::~File_SDMC() { | ||
| 27 | Close(); | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Open the file | ||
| 32 | * @return true if the file opened correctly | ||
| 33 | */ | ||
| 34 | bool File_SDMC::Open() { | ||
| 35 | if (!mode.create_flag && !FileUtil::Exists(path)) { | ||
| 36 | ERROR_LOG(FILESYS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||
| 37 | return false; | ||
| 38 | } | ||
| 39 | |||
| 40 | std::string mode_string; | ||
| 41 | if (mode.read_flag && mode.write_flag) | ||
| 42 | mode_string = "w+"; | ||
| 43 | else if (mode.read_flag) | ||
| 44 | mode_string = "r"; | ||
| 45 | else if (mode.write_flag) | ||
| 46 | mode_string = "w"; | ||
| 47 | |||
| 48 | file = new FileUtil::IOFile(path, mode_string.c_str()); | ||
| 49 | return true; | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Read data from the file | ||
| 54 | * @param offset Offset in bytes to start reading data from | ||
| 55 | * @param length Length in bytes of data to read from file | ||
| 56 | * @param buffer Buffer to read data into | ||
| 57 | * @return Number of bytes read | ||
| 58 | */ | ||
| 59 | size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 60 | file->Seek(offset, SEEK_SET); | ||
| 61 | return file->ReadBytes(buffer, length); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Write data to the file | ||
| 66 | * @param offset Offset in bytes to start writing data to | ||
| 67 | * @param length Length in bytes of data to write to file | ||
| 68 | * @param flush The flush parameters (0 == do not flush) | ||
| 69 | * @param buffer Buffer to read data from | ||
| 70 | * @return Number of bytes written | ||
| 71 | */ | ||
| 72 | size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||
| 73 | file->Seek(offset, SEEK_SET); | ||
| 74 | size_t written = file->WriteBytes(buffer, length); | ||
| 75 | if (flush) | ||
| 76 | file->Flush(); | ||
| 77 | return written; | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Get the size of the file in bytes | ||
| 82 | * @return Size of the file in bytes | ||
| 83 | */ | ||
| 84 | size_t File_SDMC::GetSize() const { | ||
| 85 | return static_cast<size_t>(file->GetSize()); | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Set the size of the file in bytes | ||
| 90 | * @param size New size of the file | ||
| 91 | * @return true if successful | ||
| 92 | */ | ||
| 93 | bool File_SDMC::SetSize(const u64 size) const { | ||
| 94 | file->Resize(size); | ||
| 95 | file->Flush(); | ||
| 96 | return true; | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Close the file | ||
| 101 | * @return true if the file closed correctly | ||
| 102 | */ | ||
| 103 | bool File_SDMC::Close() const { | ||
| 104 | return file->Close(); | ||
| 105 | } | ||
| 106 | |||
| 107 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/file_sdmc.h b/src/core/file_sys/file_sdmc.h deleted file mode 100644 index df032f7c0..000000000 --- a/src/core/file_sys/file_sdmc.h +++ /dev/null | |||
| @@ -1,75 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/file.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class File_SDMC final : public File { | ||
| 20 | public: | ||
| 21 | File_SDMC(); | ||
| 22 | File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode); | ||
| 23 | ~File_SDMC() override; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Open the file | ||
| 27 | * @return true if the file opened correctly | ||
| 28 | */ | ||
| 29 | bool Open() override; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Read data from the file | ||
| 33 | * @param offset Offset in bytes to start reading data from | ||
| 34 | * @param length Length in bytes of data to read from file | ||
| 35 | * @param buffer Buffer to read data into | ||
| 36 | * @return Number of bytes read | ||
| 37 | */ | ||
| 38 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Write data to the file | ||
| 42 | * @param offset Offset in bytes to start writing data to | ||
| 43 | * @param length Length in bytes of data to write to file | ||
| 44 | * @param flush The flush parameters (0 == do not flush) | ||
| 45 | * @param buffer Buffer to read data from | ||
| 46 | * @return Number of bytes written | ||
| 47 | */ | ||
| 48 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Get the size of the file in bytes | ||
| 52 | * @return Size of the file in bytes | ||
| 53 | */ | ||
| 54 | size_t GetSize() const override; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Set the size of the file in bytes | ||
| 58 | * @param size New size of the file | ||
| 59 | * @return true if successful | ||
| 60 | */ | ||
| 61 | bool SetSize(const u64 size) const override; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Close the file | ||
| 65 | * @return true if the file closed correctly | ||
| 66 | */ | ||
| 67 | bool Close() const override; | ||
| 68 | |||
| 69 | private: | ||
| 70 | std::string path; | ||
| 71 | Mode mode; | ||
| 72 | FileUtil::IOFile* file; | ||
| 73 | }; | ||
| 74 | |||
| 75 | } // namespace FileSys | ||
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index a45e61427..d8ba9e6cf 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | #include "common/log.h" | ||
| 6 | 7 | ||
| 7 | #include "core/hle/config_mem.h" | 8 | #include "core/hle/config_mem.h" |
| 8 | 9 | ||
| @@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) { | |||
| 54 | break; | 55 | break; |
| 55 | 56 | ||
| 56 | default: | 57 | default: |
| 57 | ERROR_LOG(HLE, "unknown addr=0x%08X", addr); | 58 | LOG_ERROR(Kernel, "unknown addr=0x%08X", addr); |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp index 1eb33eb86..e34229a57 100644 --- a/src/core/hle/coprocessor.cpp +++ b/src/core/hle/coprocessor.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/coprocessor.h" | 5 | #include "core/hle/coprocessor.h" |
| 6 | #include "core/hle/hle.h" | 6 | #include "core/hle/hle.h" |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 55eaf0621..b44479b2f 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -50,7 +50,7 @@ template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){ | |||
| 50 | 50 | ||
| 51 | template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() { | 51 | template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() { |
| 52 | s32 param_1 = 0; | 52 | s32 param_1 = 0; |
| 53 | s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), | 53 | s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), |
| 54 | (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))); | 54 | (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))); |
| 55 | Core::g_app_core->SetReg(1, (u32)param_1); | 55 | Core::g_app_core->SetReg(1, (u32)param_1); |
| 56 | FuncReturn(retval); | 56 | FuncReturn(retval); |
| @@ -103,7 +103,7 @@ template<s32 func(void*)> void Wrap() { | |||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | template<s32 func(s64*, u32, void*, s32)> void Wrap(){ | 105 | template<s32 func(s64*, u32, void*, s32)> void Wrap(){ |
| 106 | FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), | 106 | FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), |
| 107 | (s32)PARAM(3))); | 107 | (s32)PARAM(3))); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| @@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() { | |||
| 114 | FuncReturn(retval); | 114 | FuncReturn(retval); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | template<s32 func(u32*, s32, s32)> void Wrap() { | ||
| 118 | u32 param_1 = 0; | ||
| 119 | u32 retval = func(¶m_1, PARAM(1), PARAM(2)); | ||
| 120 | Core::g_app_core->SetReg(1, param_1); | ||
| 121 | FuncReturn(retval); | ||
| 122 | } | ||
| 123 | |||
| 124 | template<s32 func(s32*, u32, s32)> void Wrap() { | ||
| 125 | s32 param_1 = 0; | ||
| 126 | u32 retval = func(¶m_1, PARAM(1), PARAM(2)); | ||
| 127 | Core::g_app_core->SetReg(1, param_1); | ||
| 128 | FuncReturn(retval); | ||
| 129 | } | ||
| 130 | |||
| 117 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 131 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 118 | // Function wrappers that return type u32 | 132 | // Function wrappers that return type u32 |
| 119 | 133 | ||
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index b03894ad7..cc3d5255a 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "core/hle/hle.h" | 8 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/kernel/thread.h" | 9 | #include "core/hle/kernel/thread.h" |
| 10 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 11 | #include "core/hle/service/fs/archive.h" | ||
| 11 | 12 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 13 | 14 | ||
| @@ -20,7 +21,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n | |||
| 20 | const FunctionDef* GetSVCInfo(u32 opcode) { | 21 | const FunctionDef* GetSVCInfo(u32 opcode) { |
| 21 | u32 func_num = opcode & 0xFFFFFF; // 8 bits | 22 | u32 func_num = opcode & 0xFFFFFF; // 8 bits |
| 22 | if (func_num > 0xFF) { | 23 | if (func_num > 0xFF) { |
| 23 | ERROR_LOG(HLE,"unknown svc=0x%02X", func_num); | 24 | LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num); |
| 24 | return nullptr; | 25 | return nullptr; |
| 25 | } | 26 | } |
| 26 | return &g_module_db[0].func_table[func_num]; | 27 | return &g_module_db[0].func_table[func_num]; |
| @@ -35,14 +36,12 @@ void CallSVC(u32 opcode) { | |||
| 35 | if (info->func) { | 36 | if (info->func) { |
| 36 | info->func(); | 37 | info->func(); |
| 37 | } else { | 38 | } else { |
| 38 | ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str()); | 39 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str()); |
| 39 | } | 40 | } |
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | void Reschedule(const char *reason) { | 43 | void Reschedule(const char *reason) { |
| 43 | #ifdef _DEBUG | 44 | _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); |
| 44 | _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); | ||
| 45 | #endif | ||
| 46 | Core::g_app_core->PrepareReschedule(); | 45 | Core::g_app_core->PrepareReschedule(); |
| 47 | g_reschedule = true; | 46 | g_reschedule = true; |
| 48 | } | 47 | } |
| @@ -58,18 +57,20 @@ void RegisterAllModules() { | |||
| 58 | 57 | ||
| 59 | void Init() { | 58 | void Init() { |
| 60 | Service::Init(); | 59 | Service::Init(); |
| 61 | 60 | Service::FS::ArchiveInit(); | |
| 61 | |||
| 62 | RegisterAllModules(); | 62 | RegisterAllModules(); |
| 63 | 63 | ||
| 64 | NOTICE_LOG(HLE, "initialized OK"); | 64 | LOG_DEBUG(Kernel, "initialized OK"); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void Shutdown() { | 67 | void Shutdown() { |
| 68 | Service::FS::ArchiveShutdown(); | ||
| 68 | Service::Shutdown(); | 69 | Service::Shutdown(); |
| 69 | 70 | ||
| 70 | g_module_db.clear(); | 71 | g_module_db.clear(); |
| 71 | 72 | ||
| 72 | NOTICE_LOG(HLE, "shutdown OK"); | 73 | LOG_DEBUG(Kernel, "shutdown OK"); |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | } // namespace | 76 | } // namespace |
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index bf4d84575..4ab258c69 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 2b21657da..9a921108d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -24,23 +24,12 @@ public: | |||
| 24 | Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } | 24 | Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } |
| 25 | 25 | ||
| 26 | std::string name; ///< Name of address arbiter object (optional) | 26 | std::string name; ///< Name of address arbiter object (optional) |
| 27 | |||
| 28 | /** | ||
| 29 | * Wait for kernel object to synchronize | ||
| 30 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 31 | * @return Result of operation, 0 on success, otherwise error code | ||
| 32 | */ | ||
| 33 | Result WaitSynchronization(bool* wait) override { | ||
| 34 | // TODO(bunnei): ImplementMe | ||
| 35 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | }; | 27 | }; |
| 39 | 28 | ||
| 40 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 29 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 41 | 30 | ||
| 42 | /// Arbitrate an address | 31 | /// Arbitrate an address |
| 43 | Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { | 32 | ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { |
| 44 | switch (type) { | 33 | switch (type) { |
| 45 | 34 | ||
| 46 | // Signal thread(s) waiting for arbitrate address... | 35 | // Signal thread(s) waiting for arbitrate address... |
| @@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va | |||
| 58 | // Wait current thread (acquire the arbiter)... | 47 | // Wait current thread (acquire the arbiter)... |
| 59 | case ArbitrationType::WaitIfLessThan: | 48 | case ArbitrationType::WaitIfLessThan: |
| 60 | if ((s32)Memory::Read32(address) <= value) { | 49 | if ((s32)Memory::Read32(address) <= value) { |
| 61 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); | 50 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); |
| 62 | HLE::Reschedule(__func__); | 51 | HLE::Reschedule(__func__); |
| 63 | } | 52 | } |
| 64 | break; | 53 | break; |
| 65 | 54 | ||
| 66 | default: | 55 | default: |
| 67 | ERROR_LOG(KERNEL, "unknown type=%d", type); | 56 | LOG_ERROR(Kernel, "unknown type=%d", type); |
| 68 | return -1; | 57 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); |
| 69 | } | 58 | } |
| 70 | return 0; | 59 | return RESULT_SUCCESS; |
| 71 | } | 60 | } |
| 72 | 61 | ||
| 73 | /// Create an address arbiter | 62 | /// Create an address arbiter |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index a483fe466..8a5fb10b4 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | // Address arbiters are an underlying kernel synchronization object that can be created/used via | 11 | // Address arbiters are an underlying kernel synchronization object that can be created/used via |
| 12 | // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR | 12 | // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR |
| 13 | // applications use them as an underlying mechanism to implement thread-safe barriers, events, and | 13 | // applications use them as an underlying mechanism to implement thread-safe barriers, events, and |
| 14 | // semphores. | 14 | // semphores. |
| 15 | 15 | ||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 17 | // Kernel namespace | 17 | // Kernel namespace |
| @@ -28,7 +28,7 @@ enum class ArbitrationType : u32 { | |||
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | /// Arbitrate an address | 30 | /// Arbitrate an address |
| 31 | Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); | 31 | ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); |
| 32 | 32 | ||
| 33 | /// Create an address arbiter | 33 | /// Create an address arbiter |
| 34 | Handle CreateAddressArbiter(const std::string& name = "Unknown"); | 34 | Handle CreateAddressArbiter(const std::string& name = "Unknown"); |
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp deleted file mode 100644 index 764082d71..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null | |||
| @@ -1,436 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/math_util.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive.h" | ||
| 10 | #include "core/file_sys/archive_sdmc.h" | ||
| 11 | #include "core/file_sys/directory.h" | ||
| 12 | #include "core/hle/service/service.h" | ||
| 13 | #include "core/hle/kernel/archive.h" | ||
| 14 | |||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 16 | // Kernel namespace | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | // Command to access archive file | ||
| 21 | enum class FileCommand : u32 { | ||
| 22 | Dummy1 = 0x000100C6, | ||
| 23 | Control = 0x040100C4, | ||
| 24 | OpenSubFile = 0x08010100, | ||
| 25 | Read = 0x080200C2, | ||
| 26 | Write = 0x08030102, | ||
| 27 | GetSize = 0x08040000, | ||
| 28 | SetSize = 0x08050080, | ||
| 29 | GetAttributes = 0x08060000, | ||
| 30 | SetAttributes = 0x08070040, | ||
| 31 | Close = 0x08080000, | ||
| 32 | Flush = 0x08090000, | ||
| 33 | }; | ||
| 34 | |||
| 35 | // Command to access directory | ||
| 36 | enum class DirectoryCommand : u32 { | ||
| 37 | Dummy1 = 0x000100C6, | ||
| 38 | Control = 0x040100C4, | ||
| 39 | Read = 0x08010042, | ||
| 40 | Close = 0x08020000, | ||
| 41 | }; | ||
| 42 | |||
| 43 | class Archive : public Object { | ||
| 44 | public: | ||
| 45 | std::string GetTypeName() const override { return "Archive"; } | ||
| 46 | std::string GetName() const override { return name; } | ||
| 47 | |||
| 48 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; } | ||
| 49 | Kernel::HandleType GetHandleType() const override { return HandleType::Archive; } | ||
| 50 | |||
| 51 | std::string name; ///< Name of archive (optional) | ||
| 52 | FileSys::Archive* backend; ///< Archive backend interface | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Synchronize kernel object | ||
| 56 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 57 | * @return Result of operation, 0 on success, otherwise error code | ||
| 58 | */ | ||
| 59 | Result SyncRequest(bool* wait) override { | ||
| 60 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 61 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 62 | |||
| 63 | switch (cmd) { | ||
| 64 | // Read from archive... | ||
| 65 | case FileCommand::Read: | ||
| 66 | { | ||
| 67 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 68 | u32 length = cmd_buff[3]; | ||
| 69 | u32 address = cmd_buff[5]; | ||
| 70 | |||
| 71 | // Number of bytes read | ||
| 72 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | // Write to archive... | ||
| 76 | case FileCommand::Write: | ||
| 77 | { | ||
| 78 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 79 | u32 length = cmd_buff[3]; | ||
| 80 | u32 flush = cmd_buff[4]; | ||
| 81 | u32 address = cmd_buff[6]; | ||
| 82 | |||
| 83 | // Number of bytes written | ||
| 84 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | case FileCommand::GetSize: | ||
| 88 | { | ||
| 89 | u64 filesize = (u64) backend->GetSize(); | ||
| 90 | cmd_buff[2] = (u32) filesize; // Lower word | ||
| 91 | cmd_buff[3] = (u32) (filesize >> 32); // Upper word | ||
| 92 | break; | ||
| 93 | } | ||
| 94 | case FileCommand::SetSize: | ||
| 95 | { | ||
| 96 | backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | case FileCommand::Close: | ||
| 100 | { | ||
| 101 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 102 | Kernel::g_object_pool.Destroy<Archive>(GetHandle()); | ||
| 103 | CloseArchive(backend->GetIdCode()); | ||
| 104 | break; | ||
| 105 | } | ||
| 106 | // Unknown command... | ||
| 107 | default: | ||
| 108 | { | ||
| 109 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 110 | return -1; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | cmd_buff[1] = 0; // No error | ||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Wait for kernel object to synchronize | ||
| 119 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 120 | * @return Result of operation, 0 on success, otherwise error code | ||
| 121 | */ | ||
| 122 | Result WaitSynchronization(bool* wait) override { | ||
| 123 | // TODO(bunnei): ImplementMe | ||
| 124 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | }; | ||
| 128 | |||
| 129 | class File : public Object { | ||
| 130 | public: | ||
| 131 | std::string GetTypeName() const override { return "File"; } | ||
| 132 | std::string GetName() const override { return path; } | ||
| 133 | |||
| 134 | static Kernel::HandleType GetStaticHandleType() { return HandleType::File; } | ||
| 135 | Kernel::HandleType GetHandleType() const override { return HandleType::File; } | ||
| 136 | |||
| 137 | std::string path; ///< Path of the file | ||
| 138 | std::unique_ptr<FileSys::File> backend; ///< File backend interface | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Synchronize kernel object | ||
| 142 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 143 | * @return Result of operation, 0 on success, otherwise error code | ||
| 144 | */ | ||
| 145 | Result SyncRequest(bool* wait) override { | ||
| 146 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 147 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 148 | switch (cmd) { | ||
| 149 | |||
| 150 | // Read from file... | ||
| 151 | case FileCommand::Read: | ||
| 152 | { | ||
| 153 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 154 | u32 length = cmd_buff[3]; | ||
| 155 | u32 address = cmd_buff[5]; | ||
| 156 | DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 157 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 158 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 159 | break; | ||
| 160 | } | ||
| 161 | |||
| 162 | // Write to file... | ||
| 163 | case FileCommand::Write: | ||
| 164 | { | ||
| 165 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 166 | u32 length = cmd_buff[3]; | ||
| 167 | u32 flush = cmd_buff[4]; | ||
| 168 | u32 address = cmd_buff[6]; | ||
| 169 | DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 170 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 171 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 172 | break; | ||
| 173 | } | ||
| 174 | |||
| 175 | case FileCommand::GetSize: | ||
| 176 | { | ||
| 177 | DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 178 | u64 size = backend->GetSize(); | ||
| 179 | cmd_buff[2] = (u32)size; | ||
| 180 | cmd_buff[3] = size >> 32; | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | |||
| 184 | case FileCommand::SetSize: | ||
| 185 | { | ||
| 186 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 187 | DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size); | ||
| 188 | backend->SetSize(size); | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | |||
| 192 | case FileCommand::Close: | ||
| 193 | { | ||
| 194 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 195 | Kernel::g_object_pool.Destroy<File>(GetHandle()); | ||
| 196 | break; | ||
| 197 | } | ||
| 198 | |||
| 199 | // Unknown command... | ||
| 200 | default: | ||
| 201 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 202 | cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that. | ||
| 203 | return -1; | ||
| 204 | } | ||
| 205 | cmd_buff[1] = 0; // No error | ||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Wait for kernel object to synchronize | ||
| 211 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 212 | * @return Result of operation, 0 on success, otherwise error code | ||
| 213 | */ | ||
| 214 | Result WaitSynchronization(bool* wait) override { | ||
| 215 | // TODO(bunnei): ImplementMe | ||
| 216 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | }; | ||
| 220 | |||
| 221 | class Directory : public Object { | ||
| 222 | public: | ||
| 223 | std::string GetTypeName() const override { return "Directory"; } | ||
| 224 | std::string GetName() const override { return path; } | ||
| 225 | |||
| 226 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; } | ||
| 227 | Kernel::HandleType GetHandleType() const override { return HandleType::Directory; } | ||
| 228 | |||
| 229 | std::string path; ///< Path of the directory | ||
| 230 | std::unique_ptr<FileSys::Directory> backend; ///< File backend interface | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Synchronize kernel object | ||
| 234 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 235 | * @return Result of operation, 0 on success, otherwise error code | ||
| 236 | */ | ||
| 237 | Result SyncRequest(bool* wait) override { | ||
| 238 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 239 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 240 | switch (cmd) { | ||
| 241 | |||
| 242 | // Read from directory... | ||
| 243 | case DirectoryCommand::Read: | ||
| 244 | { | ||
| 245 | u32 count = cmd_buff[1]; | ||
| 246 | u32 address = cmd_buff[3]; | ||
| 247 | FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 248 | DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count); | ||
| 249 | |||
| 250 | // Number of entries actually read | ||
| 251 | cmd_buff[2] = backend->Read(count, entries); | ||
| 252 | break; | ||
| 253 | } | ||
| 254 | |||
| 255 | case DirectoryCommand::Close: | ||
| 256 | { | ||
| 257 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 258 | Kernel::g_object_pool.Destroy<Directory>(GetHandle()); | ||
| 259 | break; | ||
| 260 | } | ||
| 261 | |||
| 262 | // Unknown command... | ||
| 263 | default: | ||
| 264 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 265 | cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that. | ||
| 266 | return -1; | ||
| 267 | } | ||
| 268 | cmd_buff[1] = 0; // No error | ||
| 269 | return 0; | ||
| 270 | } | ||
| 271 | |||
| 272 | /** | ||
| 273 | * Wait for kernel object to synchronize | ||
| 274 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 275 | * @return Result of operation, 0 on success, otherwise error code | ||
| 276 | */ | ||
| 277 | Result WaitSynchronization(bool* wait) override { | ||
| 278 | // TODO(bunnei): ImplementMe | ||
| 279 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | }; | ||
| 283 | |||
| 284 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 285 | |||
| 286 | std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode | ||
| 287 | |||
| 288 | /** | ||
| 289 | * Opens an archive | ||
| 290 | * @param id_code IdCode of the archive to open | ||
| 291 | * @return Handle to archive if it exists, otherwise a null handle (0) | ||
| 292 | */ | ||
| 293 | Handle OpenArchive(FileSys::Archive::IdCode id_code) { | ||
| 294 | auto itr = g_archive_map.find(id_code); | ||
| 295 | if (itr == g_archive_map.end()) { | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | return itr->second; | ||
| 299 | } | ||
| 300 | |||
| 301 | /** | ||
| 302 | * Closes an archive | ||
| 303 | * @param id_code IdCode of the archive to open | ||
| 304 | * @return Result of operation, 0 on success, otherwise error code | ||
| 305 | */ | ||
| 306 | Result CloseArchive(FileSys::Archive::IdCode id_code) { | ||
| 307 | if (1 != g_archive_map.erase(id_code)) { | ||
| 308 | ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code); | ||
| 309 | return -1; | ||
| 310 | } | ||
| 311 | |||
| 312 | INFO_LOG(KERNEL, "Closed archive %d", (int) id_code); | ||
| 313 | return 0; | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * Mounts an archive | ||
| 318 | * @param archive Pointer to the archive to mount | ||
| 319 | * @return Result of operation, 0 on success, otherwise error code | ||
| 320 | */ | ||
| 321 | Result MountArchive(Archive* archive) { | ||
| 322 | FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); | ||
| 323 | if (0 != OpenArchive(id_code)) { | ||
| 324 | ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); | ||
| 325 | return -1; | ||
| 326 | } | ||
| 327 | g_archive_map[id_code] = archive->GetHandle(); | ||
| 328 | INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str()); | ||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * Creates an Archive | ||
| 334 | * @param handle Handle to newly created archive object | ||
| 335 | * @param backend File system backend interface to the archive | ||
| 336 | * @param name Optional name of Archive | ||
| 337 | * @return Newly created Archive object | ||
| 338 | */ | ||
| 339 | Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) { | ||
| 340 | Archive* archive = new Archive; | ||
| 341 | handle = Kernel::g_object_pool.Create(archive); | ||
| 342 | archive->name = name; | ||
| 343 | archive->backend = backend; | ||
| 344 | |||
| 345 | MountArchive(archive); | ||
| 346 | |||
| 347 | return archive; | ||
| 348 | } | ||
| 349 | |||
| 350 | /** | ||
| 351 | * Creates an Archive | ||
| 352 | * @param backend File system backend interface to the archive | ||
| 353 | * @param name Optional name of Archive | ||
| 354 | * @return Handle to newly created Archive object | ||
| 355 | */ | ||
| 356 | Handle CreateArchive(FileSys::Archive* backend, const std::string& name) { | ||
| 357 | Handle handle; | ||
| 358 | CreateArchive(handle, backend, name); | ||
| 359 | return handle; | ||
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * Open a File from an Archive | ||
| 364 | * @param archive_handle Handle to an open Archive object | ||
| 365 | * @param path Path to the File inside of the Archive | ||
| 366 | * @param mode Mode under which to open the File | ||
| 367 | * @return Opened File object | ||
| 368 | */ | ||
| 369 | Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) { | ||
| 370 | File* file = new File; | ||
| 371 | Handle handle = Kernel::g_object_pool.Create(file); | ||
| 372 | |||
| 373 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 374 | file->path = path; | ||
| 375 | file->backend = archive->backend->OpenFile(path, mode); | ||
| 376 | |||
| 377 | if (!file->backend) | ||
| 378 | return 0; | ||
| 379 | |||
| 380 | return handle; | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Create a Directory from an Archive | ||
| 385 | * @param archive_handle Handle to an open Archive object | ||
| 386 | * @param path Path to the Directory inside of the Archive | ||
| 387 | * @return Opened Directory object | ||
| 388 | */ | ||
| 389 | Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& path) { | ||
| 390 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 391 | if (archive == nullptr) | ||
| 392 | return -1; | ||
| 393 | if (archive->backend->CreateDirectory(path)) | ||
| 394 | return 0; | ||
| 395 | return -1; | ||
| 396 | } | ||
| 397 | |||
| 398 | /** | ||
| 399 | * Open a Directory from an Archive | ||
| 400 | * @param archive_handle Handle to an open Archive object | ||
| 401 | * @param path Path to the Directory inside of the Archive | ||
| 402 | * @return Opened Directory object | ||
| 403 | */ | ||
| 404 | Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) { | ||
| 405 | Directory* directory = new Directory; | ||
| 406 | Handle handle = Kernel::g_object_pool.Create(directory); | ||
| 407 | |||
| 408 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 409 | directory->path = path; | ||
| 410 | directory->backend = archive->backend->OpenDirectory(path); | ||
| 411 | |||
| 412 | return handle; | ||
| 413 | } | ||
| 414 | |||
| 415 | /// Initialize archives | ||
| 416 | void ArchiveInit() { | ||
| 417 | g_archive_map.clear(); | ||
| 418 | |||
| 419 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 420 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 421 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 422 | |||
| 423 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 424 | auto archive = new FileSys::Archive_SDMC(sdmc_directory); | ||
| 425 | if (archive->Initialize()) | ||
| 426 | CreateArchive(archive, "SDMC"); | ||
| 427 | else | ||
| 428 | ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 429 | } | ||
| 430 | |||
| 431 | /// Shutdown archives | ||
| 432 | void ArchiveShutdown() { | ||
| 433 | g_archive_map.clear(); | ||
| 434 | } | ||
| 435 | |||
| 436 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h deleted file mode 100644 index 0230996b6..000000000 --- a/src/core/hle/kernel/archive.h +++ /dev/null | |||
| @@ -1,70 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/file_sys/archive.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // Kernel namespace | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Opens an archive | ||
| 19 | * @param id_code IdCode of the archive to open | ||
| 20 | * @return Handle to archive if it exists, otherwise a null handle (0) | ||
| 21 | */ | ||
| 22 | Handle OpenArchive(FileSys::Archive::IdCode id_code); | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Closes an archive | ||
| 26 | * @param id_code IdCode of the archive to open | ||
| 27 | * @return true if it worked fine | ||
| 28 | */ | ||
| 29 | Result CloseArchive(FileSys::Archive::IdCode id_code); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Creates an Archive | ||
| 33 | * @param backend File system backend interface to the archive | ||
| 34 | * @param name Optional name of Archive | ||
| 35 | * @return Handle to newly created Archive object | ||
| 36 | */ | ||
| 37 | Handle CreateArchive(FileSys::Archive* backend, const std::string& name); | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Open a File from an Archive | ||
| 41 | * @param archive_handle Handle to an open Archive object | ||
| 42 | * @param path Path to the File inside of the Archive | ||
| 43 | * @param mode Mode under which to open the File | ||
| 44 | * @return Opened File object | ||
| 45 | */ | ||
| 46 | Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode); | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Create a Directory from an Archive | ||
| 50 | * @param archive_handle Handle to an open Archive object | ||
| 51 | * @param path Path to the Directory inside of the Archive | ||
| 52 | * @return Whether creation of directory succeeded | ||
| 53 | */ | ||
| 54 | Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& name); | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Open a Directory from an Archive | ||
| 58 | * @param archive_handle Handle to an open Archive object | ||
| 59 | * @param path Path to the Directory inside of the Archive | ||
| 60 | * @return Opened Directory object | ||
| 61 | */ | ||
| 62 | Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name); | ||
| 63 | |||
| 64 | /// Initialize archives | ||
| 65 | void ArchiveInit(); | ||
| 66 | |||
| 67 | /// Shutdown archives | ||
| 68 | void ArchiveShutdown(); | ||
| 69 | |||
| 70 | } // namespace FileSys | ||
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 45ed79be8..288080209 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 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 <algorithm> | 6 | #include <algorithm> |
| @@ -30,13 +30,8 @@ public: | |||
| 30 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event | 30 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event |
| 31 | std::string name; ///< Name of event (optional) | 31 | std::string name; ///< Name of event (optional) |
| 32 | 32 | ||
| 33 | /** | 33 | ResultVal<bool> WaitSynchronization() override { |
| 34 | * Wait for kernel object to synchronize | 34 | bool wait = locked; |
| 35 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 36 | * @return Result of operation, 0 on success, otherwise error code | ||
| 37 | */ | ||
| 38 | Result WaitSynchronization(bool* wait) override { | ||
| 39 | *wait = locked; | ||
| 40 | if (locked) { | 35 | if (locked) { |
| 41 | Handle thread = GetCurrentThreadHandle(); | 36 | Handle thread = GetCurrentThreadHandle(); |
| 42 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | 37 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { |
| @@ -47,7 +42,7 @@ public: | |||
| 47 | if (reset_type != RESETTYPE_STICKY && !permanent_locked) { | 42 | if (reset_type != RESETTYPE_STICKY && !permanent_locked) { |
| 48 | locked = true; | 43 | locked = true; |
| 49 | } | 44 | } |
| 50 | return 0; | 45 | return MakeResult<bool>(wait); |
| 51 | } | 46 | } |
| 52 | }; | 47 | }; |
| 53 | 48 | ||
| @@ -57,12 +52,12 @@ public: | |||
| 57 | * @param permanent_locked Boolean permanent locked value to set event | 52 | * @param permanent_locked Boolean permanent locked value to set event |
| 58 | * @return Result of operation, 0 on success, otherwise error code | 53 | * @return Result of operation, 0 on success, otherwise error code |
| 59 | */ | 54 | */ |
| 60 | Result SetPermanentLock(Handle handle, const bool permanent_locked) { | 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { |
| 61 | Event* evt = g_object_pool.GetFast<Event>(handle); | 56 | Event* evt = g_object_pool.Get<Event>(handle); |
| 62 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 63 | 58 | ||
| 64 | evt->permanent_locked = permanent_locked; | 59 | evt->permanent_locked = permanent_locked; |
| 65 | return 0; | 60 | return RESULT_SUCCESS; |
| 66 | } | 61 | } |
| 67 | 62 | ||
| 68 | /** | 63 | /** |
| @@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) { | |||
| 71 | * @param locked Boolean locked value to set event | 66 | * @param locked Boolean locked value to set event |
| 72 | * @return Result of operation, 0 on success, otherwise error code | 67 | * @return Result of operation, 0 on success, otherwise error code |
| 73 | */ | 68 | */ |
| 74 | Result SetEventLocked(const Handle handle, const bool locked) { | 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { |
| 75 | Event* evt = g_object_pool.GetFast<Event>(handle); | 70 | Event* evt = g_object_pool.Get<Event>(handle); |
| 76 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 77 | 72 | ||
| 78 | if (!evt->permanent_locked) { | 73 | if (!evt->permanent_locked) { |
| 79 | evt->locked = locked; | 74 | evt->locked = locked; |
| 80 | } | 75 | } |
| 81 | return 0; | 76 | return RESULT_SUCCESS; |
| 82 | } | 77 | } |
| 83 | 78 | ||
| 84 | /** | 79 | /** |
| @@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) { | |||
| 86 | * @param handle Handle to event to signal | 81 | * @param handle Handle to event to signal |
| 87 | * @return Result of operation, 0 on success, otherwise error code | 82 | * @return Result of operation, 0 on success, otherwise error code |
| 88 | */ | 83 | */ |
| 89 | Result SignalEvent(const Handle handle) { | 84 | ResultCode SignalEvent(const Handle handle) { |
| 90 | Event* evt = g_object_pool.GetFast<Event>(handle); | 85 | Event* evt = g_object_pool.Get<Event>(handle); |
| 91 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 92 | 87 | ||
| 93 | // Resume threads waiting for event to signal | 88 | // Resume threads waiting for event to signal |
| 94 | bool event_caught = false; | 89 | bool event_caught = false; |
| 95 | for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { | 90 | for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { |
| 96 | ResumeThreadFromWait( evt->waiting_threads[i]); | 91 | ResumeThreadFromWait( evt->waiting_threads[i]); |
| 97 | 92 | ||
| 98 | // If any thread is signalled awake by this event, assume the event was "caught" and reset | 93 | // If any thread is signalled awake by this event, assume the event was "caught" and reset |
| 99 | // the event. This will result in the next thread waiting on the event to block. Otherwise, | 94 | // the event. This will result in the next thread waiting on the event to block. Otherwise, |
| 100 | // the event will not be reset, and the next thread to call WaitSynchronization on it will | 95 | // the event will not be reset, and the next thread to call WaitSynchronization on it will |
| 101 | // not block. Not sure if this is correct behavior, but it seems to work. | 96 | // not block. Not sure if this is correct behavior, but it seems to work. |
| @@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) { | |||
| 106 | if (!evt->permanent_locked) { | 101 | if (!evt->permanent_locked) { |
| 107 | evt->locked = event_caught; | 102 | evt->locked = event_caught; |
| 108 | } | 103 | } |
| 109 | return 0; | 104 | return RESULT_SUCCESS; |
| 110 | } | 105 | } |
| 111 | 106 | ||
| 112 | /** | 107 | /** |
| @@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) { | |||
| 114 | * @param handle Handle to event to clear | 109 | * @param handle Handle to event to clear |
| 115 | * @return Result of operation, 0 on success, otherwise error code | 110 | * @return Result of operation, 0 on success, otherwise error code |
| 116 | */ | 111 | */ |
| 117 | Result ClearEvent(Handle handle) { | 112 | ResultCode ClearEvent(Handle handle) { |
| 118 | Event* evt = g_object_pool.GetFast<Event>(handle); | 113 | Event* evt = g_object_pool.Get<Event>(handle); |
| 119 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | 114 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 120 | 115 | ||
| 121 | if (!evt->permanent_locked) { | 116 | if (!evt->permanent_locked) { |
| 122 | evt->locked = true; | 117 | evt->locked = true; |
| 123 | } | 118 | } |
| 124 | return 0; | 119 | return RESULT_SUCCESS; |
| 125 | } | 120 | } |
| 126 | 121 | ||
| 127 | /** | 122 | /** |
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index c39b33180..73aec4e79 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -15,31 +15,27 @@ namespace Kernel { | |||
| 15 | * Changes whether an event is locked or not | 15 | * Changes whether an event is locked or not |
| 16 | * @param handle Handle to event to change | 16 | * @param handle Handle to event to change |
| 17 | * @param locked Boolean locked value to set event | 17 | * @param locked Boolean locked value to set event |
| 18 | * @return Result of operation, 0 on success, otherwise error code | ||
| 19 | */ | 18 | */ |
| 20 | Result SetEventLocked(const Handle handle, const bool locked); | 19 | ResultCode SetEventLocked(const Handle handle, const bool locked); |
| 21 | 20 | ||
| 22 | /** | 21 | /** |
| 23 | * Hackish function to set an events permanent lock state, used to pass through synch blocks | 22 | * Hackish function to set an events permanent lock state, used to pass through synch blocks |
| 24 | * @param handle Handle to event to change | 23 | * @param handle Handle to event to change |
| 25 | * @param permanent_locked Boolean permanent locked value to set event | 24 | * @param permanent_locked Boolean permanent locked value to set event |
| 26 | * @return Result of operation, 0 on success, otherwise error code | ||
| 27 | */ | 25 | */ |
| 28 | Result SetPermanentLock(Handle handle, const bool permanent_locked); | 26 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked); |
| 29 | 27 | ||
| 30 | /** | 28 | /** |
| 31 | * Signals an event | 29 | * Signals an event |
| 32 | * @param handle Handle to event to signal | 30 | * @param handle Handle to event to signal |
| 33 | * @return Result of operation, 0 on success, otherwise error code | ||
| 34 | */ | 31 | */ |
| 35 | Result SignalEvent(const Handle handle); | 32 | ResultCode SignalEvent(const Handle handle); |
| 36 | 33 | ||
| 37 | /** | 34 | /** |
| 38 | * Clears an event | 35 | * Clears an event |
| 39 | * @param handle Handle to event to clear | 36 | * @param handle Handle to event to clear |
| 40 | * @return Result of operation, 0 on success, otherwise error code | ||
| 41 | */ | 37 | */ |
| 42 | Result ClearEvent(Handle handle); | 38 | ResultCode ClearEvent(Handle handle); |
| 43 | 39 | ||
| 44 | /** | 40 | /** |
| 45 | * Creates an event | 41 | * Creates an event |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 88cbc1af5..6a690e915 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -1,18 +1,20 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | |||
| 5 | #include <algorithm> | ||
| 4 | 6 | ||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | 8 | ||
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/thread.h" | 11 | #include "core/hle/kernel/thread.h" |
| 10 | #include "core/hle/kernel/archive.h" | ||
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| 14 | Handle g_main_thread = 0; | 15 | Handle g_main_thread = 0; |
| 15 | ObjectPool g_object_pool; | 16 | ObjectPool g_object_pool; |
| 17 | u64 g_program_id = 0; | ||
| 16 | 18 | ||
| 17 | ObjectPool::ObjectPool() { | 19 | ObjectPool::ObjectPool() { |
| 18 | next_id = INITIAL_NEXT_ID; | 20 | next_id = INITIAL_NEXT_ID; |
| @@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { | |||
| 33 | return i + HANDLE_OFFSET; | 35 | return i + HANDLE_OFFSET; |
| 34 | } | 36 | } |
| 35 | } | 37 | } |
| 36 | ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); | 38 | LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use."); |
| 37 | return 0; | 39 | return 0; |
| 38 | } | 40 | } |
| 39 | 41 | ||
| 40 | bool ObjectPool::IsValid(Handle handle) { | 42 | bool ObjectPool::IsValid(Handle handle) const { |
| 41 | int index = handle - HANDLE_OFFSET; | 43 | int index = handle - HANDLE_OFFSET; |
| 42 | if (index < 0) | 44 | if (index < 0) |
| 43 | return false; | 45 | return false; |
| @@ -60,7 +62,7 @@ void ObjectPool::Clear() { | |||
| 60 | 62 | ||
| 61 | Object* &ObjectPool::operator [](Handle handle) | 63 | Object* &ObjectPool::operator [](Handle handle) |
| 62 | { | 64 | { |
| 63 | _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); | 65 | _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); |
| 64 | return pool[handle - HANDLE_OFFSET]; | 66 | return pool[handle - HANDLE_OFFSET]; |
| 65 | } | 67 | } |
| 66 | 68 | ||
| @@ -68,37 +70,30 @@ void ObjectPool::List() { | |||
| 68 | for (int i = 0; i < MAX_COUNT; i++) { | 70 | for (int i = 0; i < MAX_COUNT; i++) { |
| 69 | if (occupied[i]) { | 71 | if (occupied[i]) { |
| 70 | if (pool[i]) { | 72 | if (pool[i]) { |
| 71 | INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), | 73 | LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), |
| 72 | pool[i]->GetName().c_str()); | 74 | pool[i]->GetName().c_str()); |
| 73 | } | 75 | } |
| 74 | } | 76 | } |
| 75 | } | 77 | } |
| 76 | } | 78 | } |
| 77 | 79 | ||
| 78 | int ObjectPool::GetCount() { | 80 | int ObjectPool::GetCount() const { |
| 79 | int count = 0; | 81 | return std::count(occupied.begin(), occupied.end(), true); |
| 80 | for (int i = 0; i < MAX_COUNT; i++) { | ||
| 81 | if (occupied[i]) | ||
| 82 | count++; | ||
| 83 | } | ||
| 84 | return count; | ||
| 85 | } | 82 | } |
| 86 | 83 | ||
| 87 | Object* ObjectPool::CreateByIDType(int type) { | 84 | Object* ObjectPool::CreateByIDType(int type) { |
| 88 | ERROR_LOG(COMMON, "Unimplemented: %d.", type); | 85 | LOG_ERROR(Kernel, "Unimplemented: %d.", type); |
| 89 | return nullptr; | 86 | return nullptr; |
| 90 | } | 87 | } |
| 91 | 88 | ||
| 92 | /// Initialize the kernel | 89 | /// Initialize the kernel |
| 93 | void Init() { | 90 | void Init() { |
| 94 | Kernel::ThreadingInit(); | 91 | Kernel::ThreadingInit(); |
| 95 | Kernel::ArchiveInit(); | ||
| 96 | } | 92 | } |
| 97 | 93 | ||
| 98 | /// Shutdown the kernel | 94 | /// Shutdown the kernel |
| 99 | void Shutdown() { | 95 | void Shutdown() { |
| 100 | Kernel::ThreadingShutdown(); | 96 | Kernel::ThreadingShutdown(); |
| 101 | Kernel::ArchiveShutdown(); | ||
| 102 | 97 | ||
| 103 | g_object_pool.Clear(); // Free all kernel objects | 98 | g_object_pool.Clear(); // Free all kernel objects |
| 104 | } | 99 | } |
| @@ -109,8 +104,6 @@ void Shutdown() { | |||
| 109 | * @return True on success, otherwise false | 104 | * @return True on success, otherwise false |
| 110 | */ | 105 | */ |
| 111 | bool LoadExec(u32 entry_point) { | 106 | bool LoadExec(u32 entry_point) { |
| 112 | Init(); | ||
| 113 | |||
| 114 | Core::g_app_core->SetPC(entry_point); | 107 | Core::g_app_core->SetPC(entry_point); |
| 115 | 108 | ||
| 116 | // 0x30 is the typical main thread priority I've seen used so far | 109 | // 0x30 is the typical main thread priority I've seen used so far |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 867d1b89c..7123485be 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -1,12 +1,13 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/common.h" | 9 | #include "common/common.h" |
| 10 | #include "core/hle/result.h" | ||
| 10 | 11 | ||
| 11 | typedef u32 Handle; | 12 | typedef u32 Handle; |
| 12 | typedef s32 Result; | 13 | typedef s32 Result; |
| @@ -21,7 +22,7 @@ enum KernelHandle { | |||
| 21 | enum class HandleType : u32 { | 22 | enum class HandleType : u32 { |
| 22 | Unknown = 0, | 23 | Unknown = 0, |
| 23 | Port = 1, | 24 | Port = 1, |
| 24 | Service = 2, | 25 | Session = 2, |
| 25 | Event = 3, | 26 | Event = 3, |
| 26 | Mutex = 4, | 27 | Mutex = 4, |
| 27 | SharedMemory = 5, | 28 | SharedMemory = 5, |
| @@ -29,12 +30,9 @@ enum class HandleType : u32 { | |||
| 29 | Thread = 7, | 30 | Thread = 7, |
| 30 | Process = 8, | 31 | Process = 8, |
| 31 | AddressArbiter = 9, | 32 | AddressArbiter = 9, |
| 32 | File = 10, | 33 | Semaphore = 10, |
| 33 | Semaphore = 11, | ||
| 34 | Archive = 12, | ||
| 35 | Directory = 13, | ||
| 36 | }; | 34 | }; |
| 37 | 35 | ||
| 38 | enum { | 36 | enum { |
| 39 | DEFAULT_STACK_SIZE = 0x4000, | 37 | DEFAULT_STACK_SIZE = 0x4000, |
| 40 | }; | 38 | }; |
| @@ -52,21 +50,13 @@ public: | |||
| 52 | virtual Kernel::HandleType GetHandleType() const = 0; | 50 | virtual Kernel::HandleType GetHandleType() const = 0; |
| 53 | 51 | ||
| 54 | /** | 52 | /** |
| 55 | * Synchronize kernel object | 53 | * Wait for kernel object to synchronize. |
| 56 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | 54 | * @return True if the current thread should wait as a result of the wait |
| 57 | * @return Result of operation, 0 on success, otherwise error code | ||
| 58 | */ | 55 | */ |
| 59 | virtual Result SyncRequest(bool* wait) { | 56 | virtual ResultVal<bool> WaitSynchronization() { |
| 60 | ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); | 57 | LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); |
| 61 | return -1; | 58 | return UnimplementedFunction(ErrorModule::Kernel); |
| 62 | } | 59 | } |
| 63 | |||
| 64 | /** | ||
| 65 | * Wait for kernel object to synchronize | ||
| 66 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 67 | * @return Result of operation, 0 on success, otherwise error code | ||
| 68 | */ | ||
| 69 | virtual Result WaitSynchronization(bool* wait) = 0; | ||
| 70 | }; | 60 | }; |
| 71 | 61 | ||
| 72 | class ObjectPool : NonCopyable { | 62 | class ObjectPool : NonCopyable { |
| @@ -80,38 +70,29 @@ public: | |||
| 80 | static Object* CreateByIDType(int type); | 70 | static Object* CreateByIDType(int type); |
| 81 | 71 | ||
| 82 | template <class T> | 72 | template <class T> |
| 83 | u32 Destroy(Handle handle) { | 73 | void Destroy(Handle handle) { |
| 84 | u32 error; | 74 | if (Get<T>(handle)) { |
| 85 | if (Get<T>(handle, error)) { | ||
| 86 | occupied[handle - HANDLE_OFFSET] = false; | 75 | occupied[handle - HANDLE_OFFSET] = false; |
| 87 | delete pool[handle - HANDLE_OFFSET]; | 76 | delete pool[handle - HANDLE_OFFSET]; |
| 88 | } | 77 | } |
| 89 | return error; | 78 | } |
| 90 | }; | ||
| 91 | 79 | ||
| 92 | bool IsValid(Handle handle); | 80 | bool IsValid(Handle handle) const; |
| 93 | 81 | ||
| 94 | template <class T> | 82 | template <class T> |
| 95 | T* Get(Handle handle, u32& outError) { | 83 | T* Get(Handle handle) { |
| 96 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { | 84 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { |
| 97 | // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP | 85 | if (handle != 0) { |
| 98 | if (handle != 0 && (u32)handle != 0x80020001) { | 86 | LOG_ERROR(Kernel, "Bad object handle %08x", handle); |
| 99 | WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | ||
| 100 | } | 87 | } |
| 101 | outError = 0;//T::GetMissingErrorCode(); | 88 | return nullptr; |
| 102 | return 0; | ||
| 103 | } else { | 89 | } else { |
| 104 | // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, | 90 | Object* t = pool[handle - HANDLE_OFFSET]; |
| 105 | // it just acted as a static case and everything worked. This means that we will never | 91 | if (t->GetHandleType() != T::GetStaticHandleType()) { |
| 106 | // see the Wrong type object error below, but we'll just have to live with that danger. | 92 | LOG_ERROR(Kernel, "Wrong object type for %08x", handle); |
| 107 | T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]); | 93 | return nullptr; |
| 108 | if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) { | ||
| 109 | WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); | ||
| 110 | outError = 0;//T::GetMissingErrorCode(); | ||
| 111 | return 0; | ||
| 112 | } | 94 | } |
| 113 | outError = 0;//SCE_KERNEL_ERROR_OK; | 95 | return static_cast<T*>(t); |
| 114 | return t; | ||
| 115 | } | 96 | } |
| 116 | } | 97 | } |
| 117 | 98 | ||
| @@ -119,7 +100,7 @@ public: | |||
| 119 | template <class T> | 100 | template <class T> |
| 120 | T *GetFast(Handle handle) { | 101 | T *GetFast(Handle handle) { |
| 121 | const Handle realHandle = handle - HANDLE_OFFSET; | 102 | const Handle realHandle = handle - HANDLE_OFFSET; |
| 122 | _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); | 103 | _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); |
| 123 | return static_cast<T*>(pool[realHandle]); | 104 | return static_cast<T*>(pool[realHandle]); |
| 124 | } | 105 | } |
| 125 | 106 | ||
| @@ -139,9 +120,9 @@ public: | |||
| 139 | } | 120 | } |
| 140 | 121 | ||
| 141 | bool GetIDType(Handle handle, HandleType* type) const { | 122 | bool GetIDType(Handle handle, HandleType* type) const { |
| 142 | if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || | 123 | if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || |
| 143 | !occupied[handle - HANDLE_OFFSET]) { | 124 | !occupied[handle - HANDLE_OFFSET]) { |
| 144 | ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | 125 | LOG_ERROR(Kernel, "Bad object handle %08X", handle); |
| 145 | return false; | 126 | return false; |
| 146 | } | 127 | } |
| 147 | Object* t = pool[handle - HANDLE_OFFSET]; | 128 | Object* t = pool[handle - HANDLE_OFFSET]; |
| @@ -152,10 +133,10 @@ public: | |||
| 152 | Object* &operator [](Handle handle); | 133 | Object* &operator [](Handle handle); |
| 153 | void List(); | 134 | void List(); |
| 154 | void Clear(); | 135 | void Clear(); |
| 155 | int GetCount(); | 136 | int GetCount() const; |
| 156 | 137 | ||
| 157 | private: | 138 | private: |
| 158 | 139 | ||
| 159 | enum { | 140 | enum { |
| 160 | MAX_COUNT = 0x1000, | 141 | MAX_COUNT = 0x1000, |
| 161 | HANDLE_OFFSET = 0x100, | 142 | HANDLE_OFFSET = 0x100, |
| @@ -170,6 +151,12 @@ private: | |||
| 170 | extern ObjectPool g_object_pool; | 151 | extern ObjectPool g_object_pool; |
| 171 | extern Handle g_main_thread; | 152 | extern Handle g_main_thread; |
| 172 | 153 | ||
| 154 | /// The ID code of the currently running game | ||
| 155 | /// TODO(Subv): This variable should not be here, | ||
| 156 | /// we need a way to store information about the currently loaded application | ||
| 157 | /// for later query during runtime, maybe using the LDR service? | ||
| 158 | extern u64 g_program_id; | ||
| 159 | |||
| 173 | /// Initialize the kernel | 160 | /// Initialize the kernel |
| 174 | void Init(); | 161 | void Init(); |
| 175 | 162 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index fcfd061ac..5a173e129 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 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 <vector> | 6 | #include <vector> |
| @@ -27,32 +27,7 @@ public: | |||
| 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex | 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex |
| 28 | std::string name; ///< Name of mutex (optional) | 28 | std::string name; ///< Name of mutex (optional) |
| 29 | 29 | ||
| 30 | /** | 30 | ResultVal<bool> WaitSynchronization() override; |
| 31 | * Synchronize kernel object | ||
| 32 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 33 | * @return Result of operation, 0 on success, otherwise error code | ||
| 34 | */ | ||
| 35 | Result SyncRequest(bool* wait) override { | ||
| 36 | // TODO(bunnei): ImplementMe | ||
| 37 | locked = true; | ||
| 38 | return 0; | ||
| 39 | } | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Wait for kernel object to synchronize | ||
| 43 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 44 | * @return Result of operation, 0 on success, otherwise error code | ||
| 45 | */ | ||
| 46 | Result WaitSynchronization(bool* wait) override { | ||
| 47 | // TODO(bunnei): ImplementMe | ||
| 48 | *wait = locked; | ||
| 49 | |||
| 50 | if (locked) { | ||
| 51 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | ||
| 52 | } | ||
| 53 | |||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | }; | 31 | }; |
| 57 | 32 | ||
| 58 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 33 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -60,21 +35,46 @@ public: | |||
| 60 | typedef std::multimap<Handle, Handle> MutexMap; | 35 | typedef std::multimap<Handle, Handle> MutexMap; |
| 61 | static MutexMap g_mutex_held_locks; | 36 | static MutexMap g_mutex_held_locks; |
| 62 | 37 | ||
| 63 | void MutexAcquireLock(Mutex* mutex, Handle thread) { | 38 | /** |
| 39 | * Acquires the specified mutex for the specified thread | ||
| 40 | * @param mutex Mutex that is to be acquired | ||
| 41 | * @param thread Thread that will acquired | ||
| 42 | */ | ||
| 43 | void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { | ||
| 64 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); | 44 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); |
| 65 | mutex->lock_thread = thread; | 45 | mutex->lock_thread = thread; |
| 66 | } | 46 | } |
| 67 | 47 | ||
| 68 | void MutexAcquireLock(Mutex* mutex) { | 48 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { |
| 69 | Handle thread = GetCurrentThreadHandle(); | ||
| 70 | MutexAcquireLock(mutex, thread); | 49 | MutexAcquireLock(mutex, thread); |
| 50 | Kernel::ResumeThreadFromWait(thread); | ||
| 51 | return true; | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Resumes a thread waiting for the specified mutex | ||
| 56 | * @param mutex The mutex that some thread is waiting on | ||
| 57 | */ | ||
| 58 | void ResumeWaitingThread(Mutex* mutex) { | ||
| 59 | // Find the next waiting thread for the mutex... | ||
| 60 | if (mutex->waiting_threads.empty()) { | ||
| 61 | // Reset mutex lock thread handle, nothing is waiting | ||
| 62 | mutex->locked = false; | ||
| 63 | mutex->lock_thread = -1; | ||
| 64 | } | ||
| 65 | else { | ||
| 66 | // Resume the next waiting thread and re-lock the mutex | ||
| 67 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 68 | ReleaseMutexForThread(mutex, *iter); | ||
| 69 | mutex->waiting_threads.erase(iter); | ||
| 70 | } | ||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void MutexEraseLock(Mutex* mutex) { | 73 | void MutexEraseLock(Mutex* mutex) { |
| 74 | Handle handle = mutex->GetHandle(); | 74 | Handle handle = mutex->GetHandle(); |
| 75 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); | 75 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); |
| 76 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | 76 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { |
| 77 | if ((*iter).second == handle) { | 77 | if (iter->second == handle) { |
| 78 | g_mutex_held_locks.erase(iter); | 78 | g_mutex_held_locks.erase(iter); |
| 79 | break; | 79 | break; |
| 80 | } | 80 | } |
| @@ -82,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) { | |||
| 82 | mutex->lock_thread = -1; | 82 | mutex->lock_thread = -1; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | void ReleaseThreadMutexes(Handle thread) { | ||
| 86 | auto locked = g_mutex_held_locks.equal_range(thread); | ||
| 87 | |||
| 88 | // Release every mutex that the thread holds, and resume execution on the waiting threads | ||
| 89 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | ||
| 90 | Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second); | ||
| 91 | ResumeWaitingThread(mutex); | ||
| 92 | } | ||
| 93 | |||
| 94 | // Erase all the locks that this thread holds | ||
| 95 | g_mutex_held_locks.erase(thread); | ||
| 96 | } | ||
| 97 | |||
| 85 | bool LockMutex(Mutex* mutex) { | 98 | bool LockMutex(Mutex* mutex) { |
| 86 | // Mutex alread locked? | 99 | // Mutex alread locked? |
| 87 | if (mutex->locked) { | 100 | if (mutex->locked) { |
| @@ -91,43 +104,27 @@ bool LockMutex(Mutex* mutex) { | |||
| 91 | return true; | 104 | return true; |
| 92 | } | 105 | } |
| 93 | 106 | ||
| 94 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | ||
| 95 | MutexAcquireLock(mutex, thread); | ||
| 96 | Kernel::ResumeThreadFromWait(thread); | ||
| 97 | return true; | ||
| 98 | } | ||
| 99 | |||
| 100 | bool ReleaseMutex(Mutex* mutex) { | 107 | bool ReleaseMutex(Mutex* mutex) { |
| 101 | MutexEraseLock(mutex); | 108 | MutexEraseLock(mutex); |
| 102 | bool woke_threads = false; | 109 | ResumeWaitingThread(mutex); |
| 103 | 110 | return true; | |
| 104 | // Find the next waiting thread for the mutex... | ||
| 105 | while (!woke_threads && !mutex->waiting_threads.empty()) { | ||
| 106 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 107 | woke_threads |= ReleaseMutexForThread(mutex, *iter); | ||
| 108 | mutex->waiting_threads.erase(iter); | ||
| 109 | } | ||
| 110 | // Reset mutex lock thread handle, nothing is waiting | ||
| 111 | if (!woke_threads) { | ||
| 112 | mutex->locked = false; | ||
| 113 | mutex->lock_thread = -1; | ||
| 114 | } | ||
| 115 | return woke_threads; | ||
| 116 | } | 111 | } |
| 117 | 112 | ||
| 118 | /** | 113 | /** |
| 119 | * Releases a mutex | 114 | * Releases a mutex |
| 120 | * @param handle Handle to mutex to release | 115 | * @param handle Handle to mutex to release |
| 121 | */ | 116 | */ |
| 122 | Result ReleaseMutex(Handle handle) { | 117 | ResultCode ReleaseMutex(Handle handle) { |
| 123 | Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); | 118 | Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); |
| 124 | 119 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); | |
| 125 | _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!"); | ||
| 126 | 120 | ||
| 127 | if (!ReleaseMutex(mutex)) { | 121 | if (!ReleaseMutex(mutex)) { |
| 128 | return -1; | 122 | // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure |
| 123 | // what error condition this is supposed to be signaling. | ||
| 124 | return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel, | ||
| 125 | ErrorSummary::NothingHappened, ErrorLevel::Temporary); | ||
| 129 | } | 126 | } |
| 130 | return 0; | 127 | return RESULT_SUCCESS; |
| 131 | } | 128 | } |
| 132 | 129 | ||
| 133 | /** | 130 | /** |
| @@ -167,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | |||
| 167 | return handle; | 164 | return handle; |
| 168 | } | 165 | } |
| 169 | 166 | ||
| 167 | ResultVal<bool> Mutex::WaitSynchronization() { | ||
| 168 | bool wait = locked; | ||
| 169 | if (locked) { | ||
| 170 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | ||
| 171 | } | ||
| 172 | else { | ||
| 173 | // Lock the mutex when the first thread accesses it | ||
| 174 | locked = true; | ||
| 175 | MutexAcquireLock(this); | ||
| 176 | } | ||
| 177 | |||
| 178 | return MakeResult<bool>(wait); | ||
| 179 | } | ||
| 170 | } // namespace | 180 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 7d7b5137e..7f4909a6e 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -13,9 +13,8 @@ namespace Kernel { | |||
| 13 | /** | 13 | /** |
| 14 | * Releases a mutex | 14 | * Releases a mutex |
| 15 | * @param handle Handle to mutex to release | 15 | * @param handle Handle to mutex to release |
| 16 | * @return Result of operation, 0 on success, otherwise error code | ||
| 17 | */ | 16 | */ |
| 18 | Result ReleaseMutex(Handle handle); | 17 | ResultCode ReleaseMutex(Handle handle); |
| 19 | 18 | ||
| 20 | /** | 19 | /** |
| 21 | * Creates a mutex | 20 | * Creates a mutex |
| @@ -25,4 +24,10 @@ Result ReleaseMutex(Handle handle); | |||
| 25 | */ | 24 | */ |
| 26 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); | 25 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); |
| 27 | 26 | ||
| 27 | /** | ||
| 28 | * Releases all the mutexes held by the specified thread | ||
| 29 | * @param thread Thread that is holding the mutexes | ||
| 30 | */ | ||
| 31 | void ReleaseThreadMutexes(Handle thread); | ||
| 32 | |||
| 28 | } // namespace | 33 | } // namespace |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp new file mode 100644 index 000000000..6f56da8a9 --- /dev/null +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <queue> | ||
| 6 | |||
| 7 | #include "common/common.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/hle/kernel/semaphore.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | class Semaphore : public Object { | ||
| 16 | public: | ||
| 17 | std::string GetTypeName() const override { return "Semaphore"; } | ||
| 18 | std::string GetName() const override { return name; } | ||
| 19 | |||
| 20 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; } | ||
| 21 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; } | ||
| 22 | |||
| 23 | u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have | ||
| 24 | u32 available_count; ///< Number of free slots left in the semaphore | ||
| 25 | std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore | ||
| 26 | std::string name; ///< Name of semaphore (optional) | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Tests whether a semaphore still has free slots | ||
| 30 | * @return Whether the semaphore is available | ||
| 31 | */ | ||
| 32 | bool IsAvailable() const { | ||
| 33 | return available_count > 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultVal<bool> WaitSynchronization() override { | ||
| 37 | bool wait = !IsAvailable(); | ||
| 38 | |||
| 39 | if (wait) { | ||
| 40 | Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); | ||
| 41 | waiting_threads.push(GetCurrentThreadHandle()); | ||
| 42 | } else { | ||
| 43 | --available_count; | ||
| 44 | } | ||
| 45 | |||
| 46 | return MakeResult<bool>(wait); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 51 | |||
| 52 | ResultCode CreateSemaphore(Handle* handle, u32 initial_count, | ||
| 53 | u32 max_count, const std::string& name) { | ||
| 54 | |||
| 55 | if (initial_count > max_count) | ||
| 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, | ||
| 57 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 58 | |||
| 59 | Semaphore* semaphore = new Semaphore; | ||
| 60 | *handle = g_object_pool.Create(semaphore); | ||
| 61 | |||
| 62 | // When the semaphore is created, some slots are reserved for other threads, | ||
| 63 | // and the rest is reserved for the caller thread | ||
| 64 | semaphore->max_count = max_count; | ||
| 65 | semaphore->available_count = initial_count; | ||
| 66 | semaphore->name = name; | ||
| 67 | |||
| 68 | return RESULT_SUCCESS; | ||
| 69 | } | ||
| 70 | |||
| 71 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { | ||
| 72 | Semaphore* semaphore = g_object_pool.Get<Semaphore>(handle); | ||
| 73 | if (semaphore == nullptr) | ||
| 74 | return InvalidHandle(ErrorModule::Kernel); | ||
| 75 | |||
| 76 | if (semaphore->max_count - semaphore->available_count < release_count) | ||
| 77 | return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, | ||
| 78 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 79 | |||
| 80 | *count = semaphore->available_count; | ||
| 81 | semaphore->available_count += release_count; | ||
| 82 | |||
| 83 | // Notify some of the threads that the semaphore has been released | ||
| 84 | // stop once the semaphore is full again or there are no more waiting threads | ||
| 85 | while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { | ||
| 86 | Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); | ||
| 87 | semaphore->waiting_threads.pop(); | ||
| 88 | --semaphore->available_count; | ||
| 89 | } | ||
| 90 | |||
| 91 | return RESULT_SUCCESS; | ||
| 92 | } | ||
| 93 | |||
| 94 | } // namespace | ||
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h new file mode 100644 index 000000000..f0075fdb8 --- /dev/null +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Creates a semaphore. | ||
| 15 | * @param handle Pointer to the handle of the newly created object | ||
| 16 | * @param initial_count Number of slots reserved for other threads | ||
| 17 | * @param max_count Maximum number of slots the semaphore can have | ||
| 18 | * @param name Optional name of semaphore | ||
| 19 | * @return ResultCode of the error | ||
| 20 | */ | ||
| 21 | ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown"); | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Releases a certain number of slots from a semaphore. | ||
| 25 | * @param count The number of free slots the semaphore had before this call | ||
| 26 | * @param handle The handle of the semaphore to release | ||
| 27 | * @param release_count The number of slots to release | ||
| 28 | * @return ResultCode of the error | ||
| 29 | */ | ||
| 30 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); | ||
| 31 | |||
| 32 | } // namespace | ||
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h new file mode 100644 index 000000000..06ae4bc39 --- /dev/null +++ b/src/core/hle/kernel/session.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Returns a pointer to the command buffer in kernel memory | ||
| 15 | * @param offset Optional offset into command buffer | ||
| 16 | * @return Pointer to command buffer | ||
| 17 | */ | ||
| 18 | inline static u32* GetCommandBuffer(const int offset=0) { | ||
| 19 | return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||
| 20 | } | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS | ||
| 24 | * primitive for communication between different processes, and are used to implement service calls | ||
| 25 | * to the various system services. | ||
| 26 | * | ||
| 27 | * To make a service call, the client must write the command header and parameters to the buffer | ||
| 28 | * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest | ||
| 29 | * SVC call with its Session handle. The kernel will read the command header, using it to marshall | ||
| 30 | * the parameters to the process at the server endpoint of the session. After the server replies to | ||
| 31 | * the request, the response is marshalled back to the caller's TLS buffer and control is | ||
| 32 | * transferred back to it. | ||
| 33 | * | ||
| 34 | * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC | ||
| 35 | * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called | ||
| 36 | * with the session handle, this class's SyncRequest method is called, which should read the TLS | ||
| 37 | * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, | ||
| 38 | * no parameter marshalling is done. | ||
| 39 | * | ||
| 40 | * In the long term, this should be turned into the full-fledged IPC mechanism implemented by | ||
| 41 | * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as | ||
| 42 | * opposed to HLE simulations. | ||
| 43 | */ | ||
| 44 | class Session : public Object { | ||
| 45 | public: | ||
| 46 | std::string GetTypeName() const override { return "Session"; } | ||
| 47 | |||
| 48 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; } | ||
| 49 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls | ||
| 53 | * aren't supported yet. | ||
| 54 | */ | ||
| 55 | virtual ResultVal<bool> SyncRequest() = 0; | ||
| 56 | }; | ||
| 57 | |||
| 58 | } | ||
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 6bd5e2728..3c8c502c6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
| 6 | 6 | ||
| @@ -16,17 +16,6 @@ public: | |||
| 16 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } | 16 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } |
| 17 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } | 17 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } |
| 18 | 18 | ||
| 19 | /** | ||
| 20 | * Wait for kernel object to synchronize | ||
| 21 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 22 | * @return Result of operation, 0 on success, otherwise error code | ||
| 23 | */ | ||
| 24 | Result WaitSynchronization(bool* wait) override { | ||
| 25 | // TODO(bunnei): ImplementMe | ||
| 26 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | u32 base_address; ///< Address of shared memory block in RAM | 19 | u32 base_address; ///< Address of shared memory block in RAM |
| 31 | MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) | 20 | MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) |
| 32 | MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) | 21 | MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) |
| @@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { | |||
| 48 | return shared_memory; | 37 | return shared_memory; |
| 49 | } | 38 | } |
| 50 | 39 | ||
| 51 | /** | ||
| 52 | * Creates a shared memory object | ||
| 53 | * @param name Optional name of shared memory object | ||
| 54 | * @return Handle of newly created shared memory object | ||
| 55 | */ | ||
| 56 | Handle CreateSharedMemory(const std::string& name) { | 40 | Handle CreateSharedMemory(const std::string& name) { |
| 57 | Handle handle; | 41 | Handle handle; |
| 58 | CreateSharedMemory(handle, name); | 42 | CreateSharedMemory(handle, name); |
| @@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) { | |||
| 67 | * @param other_permissions Memory block map other permissions (specified by SVC field) | 51 | * @param other_permissions Memory block map other permissions (specified by SVC field) |
| 68 | * @return Result of operation, 0 on success, otherwise error code | 52 | * @return Result of operation, 0 on success, otherwise error code |
| 69 | */ | 53 | */ |
| 70 | Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, | 54 | ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, |
| 71 | MemoryPermission other_permissions) { | 55 | MemoryPermission other_permissions) { |
| 72 | 56 | ||
| 73 | if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { | 57 | if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { |
| 74 | ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", | 58 | LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", |
| 75 | handle); | 59 | handle, address); |
| 76 | return -1; | 60 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| 61 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 77 | } | 62 | } |
| 78 | SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle); | 63 | SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); |
| 79 | _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle); | 64 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 80 | 65 | ||
| 81 | shared_memory->base_address = address; | 66 | shared_memory->base_address = address; |
| 82 | shared_memory->permissions = permissions; | 67 | shared_memory->permissions = permissions; |
| 83 | shared_memory->other_permissions = other_permissions; | 68 | shared_memory->other_permissions = other_permissions; |
| 84 | 69 | ||
| 85 | return 0; | 70 | return RESULT_SUCCESS; |
| 86 | } | 71 | } |
| 87 | 72 | ||
| 88 | /** | 73 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { |
| 89 | * Gets a pointer to the shared memory block | 74 | SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); |
| 90 | * @param handle Shared memory block handle | 75 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 91 | * @param offset Offset from the start of the shared memory block to get pointer | ||
| 92 | * @return Pointer to the shared memory block from the specified offset | ||
| 93 | */ | ||
| 94 | u8* GetSharedMemoryPointer(Handle handle, u32 offset) { | ||
| 95 | SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle); | ||
| 96 | _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle); | ||
| 97 | 76 | ||
| 98 | if (0 != shared_memory->base_address) | 77 | if (0 != shared_memory->base_address) |
| 99 | return Memory::GetPointer(shared_memory->base_address + offset); | 78 | return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); |
| 100 | 79 | ||
| 101 | ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); | 80 | LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); |
| 102 | return nullptr; | 81 | // TODO(yuriks): Verify error code. |
| 82 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | ||
| 83 | ErrorSummary::InvalidState, ErrorLevel::Permanent); | ||
| 103 | } | 84 | } |
| 104 | 85 | ||
| 105 | } // namespace | 86 | } // namespace |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 6204d8a45..bb778ec26 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown"); | |||
| 36 | * @param address Address in system memory to map shared memory block to | 36 | * @param address Address in system memory to map shared memory block to |
| 37 | * @param permissions Memory block map permissions (specified by SVC field) | 37 | * @param permissions Memory block map permissions (specified by SVC field) |
| 38 | * @param other_permissions Memory block map other permissions (specified by SVC field) | 38 | * @param other_permissions Memory block map other permissions (specified by SVC field) |
| 39 | * @return Result of operation, 0 on success, otherwise error code | ||
| 40 | */ | 39 | */ |
| 41 | Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, | 40 | ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions, |
| 42 | MemoryPermission other_permissions); | 41 | MemoryPermission other_permissions); |
| 43 | 42 | ||
| 44 | /** | 43 | /** |
| @@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, | |||
| 47 | * @param offset Offset from the start of the shared memory block to get pointer | 46 | * @param offset Offset from the start of the shared memory block to get pointer |
| 48 | * @return Pointer to the shared memory block from the specified offset | 47 | * @return Pointer to the shared memory block from the specified offset |
| 49 | */ | 48 | */ |
| 50 | u8* GetSharedMemoryPointer(Handle handle, u32 offset); | 49 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset); |
| 51 | 50 | ||
| 52 | } // namespace | 51 | } // namespace |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e15590c49..1c04701de 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 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 <list> | 6 | #include <list> |
| @@ -11,10 +11,12 @@ | |||
| 11 | #include "common/thread_queue_list.h" | 11 | #include "common/thread_queue_list.h" |
| 12 | 12 | ||
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "core/mem_map.h" | ||
| 15 | #include "core/hle/hle.h" | 14 | #include "core/hle/hle.h" |
| 16 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 17 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 17 | #include "core/hle/kernel/mutex.h" | ||
| 18 | #include "core/hle/result.h" | ||
| 19 | #include "core/mem_map.h" | ||
| 18 | 20 | ||
| 19 | namespace Kernel { | 21 | namespace Kernel { |
| 20 | 22 | ||
| @@ -33,25 +35,23 @@ public: | |||
| 33 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | 35 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } |
| 34 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | 36 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } |
| 35 | 37 | ||
| 36 | /** | 38 | ResultVal<bool> WaitSynchronization() override { |
| 37 | * Wait for kernel object to synchronize | 39 | const bool wait = status != THREADSTATUS_DORMANT; |
| 38 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | 40 | if (wait) { |
| 39 | * @return Result of operation, 0 on success, otherwise error code | ||
| 40 | */ | ||
| 41 | Result WaitSynchronization(bool* wait) override { | ||
| 42 | if (status != THREADSTATUS_DORMANT) { | ||
| 43 | Handle thread = GetCurrentThreadHandle(); | 41 | Handle thread = GetCurrentThreadHandle(); |
| 44 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | 42 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { |
| 45 | waiting_threads.push_back(thread); | 43 | waiting_threads.push_back(thread); |
| 46 | } | 44 | } |
| 47 | WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); | 45 | WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); |
| 48 | *wait = true; | ||
| 49 | } | 46 | } |
| 50 | return 0; | 47 | |
| 48 | return MakeResult<bool>(wait); | ||
| 51 | } | 49 | } |
| 52 | 50 | ||
| 53 | ThreadContext context; | 51 | ThreadContext context; |
| 54 | 52 | ||
| 53 | u32 thread_id; | ||
| 54 | |||
| 55 | u32 status; | 55 | u32 status; |
| 56 | u32 entry_point; | 56 | u32 entry_point; |
| 57 | u32 stack_top; | 57 | u32 stack_top; |
| @@ -64,6 +64,7 @@ public: | |||
| 64 | 64 | ||
| 65 | WaitType wait_type; | 65 | WaitType wait_type; |
| 66 | Handle wait_handle; | 66 | Handle wait_handle; |
| 67 | VAddr wait_address; | ||
| 67 | 68 | ||
| 68 | std::vector<Handle> waiting_threads; | 69 | std::vector<Handle> waiting_threads; |
| 69 | 70 | ||
| @@ -71,17 +72,20 @@ public: | |||
| 71 | }; | 72 | }; |
| 72 | 73 | ||
| 73 | // Lists all thread ids that aren't deleted/etc. | 74 | // Lists all thread ids that aren't deleted/etc. |
| 74 | std::vector<Handle> g_thread_queue; | 75 | static std::vector<Handle> thread_queue; |
| 75 | 76 | ||
| 76 | // Lists only ready thread ids. | 77 | // Lists only ready thread ids. |
| 77 | Common::ThreadQueueList<Handle> g_thread_ready_queue; | 78 | static Common::ThreadQueueList<Handle> thread_ready_queue; |
| 79 | |||
| 80 | static Handle current_thread_handle; | ||
| 81 | static Thread* current_thread; | ||
| 78 | 82 | ||
| 79 | Handle g_current_thread_handle; | 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| 80 | Thread* g_current_thread; | 84 | static u32 next_thread_id; ///< The next available thread id |
| 81 | 85 | ||
| 82 | /// Gets the current thread | 86 | /// Gets the current thread |
| 83 | inline Thread* GetCurrentThread() { | 87 | inline Thread* GetCurrentThread() { |
| 84 | return g_current_thread; | 88 | return current_thread; |
| 85 | } | 89 | } |
| 86 | 90 | ||
| 87 | /// Gets the current thread handle | 91 | /// Gets the current thread handle |
| @@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() { | |||
| 91 | 95 | ||
| 92 | /// Sets the current thread | 96 | /// Sets the current thread |
| 93 | inline void SetCurrentThread(Thread* t) { | 97 | inline void SetCurrentThread(Thread* t) { |
| 94 | g_current_thread = t; | 98 | current_thread = t; |
| 95 | g_current_thread_handle = t->GetHandle(); | 99 | current_thread_handle = t->GetHandle(); |
| 96 | } | 100 | } |
| 97 | 101 | ||
| 98 | /// Saves the current CPU context | 102 | /// Saves the current CPU context |
| @@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 113 | t->context.pc = t->context.reg_15 = t->entry_point; | 117 | t->context.pc = t->context.reg_15 = t->entry_point; |
| 114 | t->context.sp = t->stack_top; | 118 | t->context.sp = t->stack_top; |
| 115 | t->context.cpsr = 0x1F; // Usermode | 119 | t->context.cpsr = 0x1F; // Usermode |
| 116 | 120 | ||
| 117 | // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a | 121 | // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a |
| 118 | // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be | 122 | // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be |
| 119 | // agnostic of the CPU core. | 123 | // agnostic of the CPU core. |
| @@ -124,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 124 | } | 128 | } |
| 125 | t->wait_type = WAITTYPE_NONE; | 129 | t->wait_type = WAITTYPE_NONE; |
| 126 | t->wait_handle = 0; | 130 | t->wait_handle = 0; |
| 131 | t->wait_address = 0; | ||
| 127 | } | 132 | } |
| 128 | 133 | ||
| 129 | /// Change a thread to "ready" state | 134 | /// Change a thread to "ready" state |
| @@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) { | |||
| 131 | Handle handle = t->GetHandle(); | 136 | Handle handle = t->GetHandle(); |
| 132 | if (t->IsReady()) { | 137 | if (t->IsReady()) { |
| 133 | if (!ready) { | 138 | if (!ready) { |
| 134 | g_thread_ready_queue.remove(t->current_priority, handle); | 139 | thread_ready_queue.remove(t->current_priority, handle); |
| 135 | } | 140 | } |
| 136 | } else if (ready) { | 141 | } else if (ready) { |
| 137 | if (t->IsRunning()) { | 142 | if (t->IsRunning()) { |
| 138 | g_thread_ready_queue.push_front(t->current_priority, handle); | 143 | thread_ready_queue.push_front(t->current_priority, handle); |
| 139 | } else { | 144 | } else { |
| 140 | g_thread_ready_queue.push_back(t->current_priority, handle); | 145 | thread_ready_queue.push_back(t->current_priority, handle); |
| 141 | } | 146 | } |
| 142 | t->status = THREADSTATUS_READY; | 147 | t->status = THREADSTATUS_READY; |
| 143 | } | 148 | } |
| 144 | } | 149 | } |
| 145 | 150 | ||
| 146 | /// Verify that a thread has not been released from waiting | 151 | /// Verify that a thread has not been released from waiting |
| 147 | inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) { | 152 | static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { |
| 148 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | 153 | _dbg_assert_(Kernel, thread != nullptr); |
| 149 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | 154 | return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); |
| 150 | 155 | } | |
| 151 | if (type != thread->wait_type || wait_handle != thread->wait_handle) | ||
| 152 | return false; | ||
| 153 | 156 | ||
| 154 | return true; | 157 | /// Verify that a thread has not been released from waiting (with wait address) |
| 158 | static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | ||
| 159 | _dbg_assert_(Kernel, thread != nullptr); | ||
| 160 | return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address); | ||
| 155 | } | 161 | } |
| 156 | 162 | ||
| 157 | /// Stops the current thread | 163 | /// Stops the current thread |
| 158 | void StopThread(Handle handle, const char* reason) { | 164 | ResultCode StopThread(Handle handle, const char* reason) { |
| 159 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | 165 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 160 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | 166 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 161 | 167 | ||
| 168 | // Release all the mutexes that this thread holds | ||
| 169 | ReleaseThreadMutexes(handle); | ||
| 170 | |||
| 162 | ChangeReadyState(thread, false); | 171 | ChangeReadyState(thread, false); |
| 163 | thread->status = THREADSTATUS_DORMANT; | 172 | thread->status = THREADSTATUS_DORMANT; |
| 164 | for (size_t i = 0; i < thread->waiting_threads.size(); ++i) { | 173 | for (Handle waiting_handle : thread->waiting_threads) { |
| 165 | const Handle waiting_thread = thread->waiting_threads[i]; | 174 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); |
| 166 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { | 175 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { |
| 167 | ResumeThreadFromWait(waiting_thread); | 176 | ResumeThreadFromWait(waiting_handle); |
| 168 | } | 177 | } |
| 169 | } | 178 | } |
| 170 | thread->waiting_threads.clear(); | 179 | thread->waiting_threads.clear(); |
| @@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) { | |||
| 172 | // Stopped threads are never waiting. | 181 | // Stopped threads are never waiting. |
| 173 | thread->wait_type = WAITTYPE_NONE; | 182 | thread->wait_type = WAITTYPE_NONE; |
| 174 | thread->wait_handle = 0; | 183 | thread->wait_handle = 0; |
| 184 | thread->wait_address = 0; | ||
| 185 | |||
| 186 | return RESULT_SUCCESS; | ||
| 175 | } | 187 | } |
| 176 | 188 | ||
| 177 | /// Changes a threads state | 189 | /// Changes a threads state |
| @@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 181 | } | 193 | } |
| 182 | ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); | 194 | ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); |
| 183 | t->status = new_status; | 195 | t->status = new_status; |
| 184 | 196 | ||
| 185 | if (new_status == THREADSTATUS_WAIT) { | 197 | if (new_status == THREADSTATUS_WAIT) { |
| 186 | if (t->wait_type == WAITTYPE_NONE) { | 198 | if (t->wait_type == WAITTYPE_NONE) { |
| 187 | ERROR_LOG(KERNEL, "Waittype none not allowed"); | 199 | LOG_ERROR(Kernel, "Waittype none not allowed"); |
| 188 | } | 200 | } |
| 189 | } | 201 | } |
| 190 | } | 202 | } |
| @@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 195 | s32 priority = THREADPRIO_LOWEST; | 207 | s32 priority = THREADPRIO_LOWEST; |
| 196 | 208 | ||
| 197 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 209 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 198 | for (const auto& handle : g_thread_queue) { | 210 | for (Handle handle : thread_queue) { |
| 211 | Thread* thread = g_object_pool.Get<Thread>(handle); | ||
| 199 | 212 | ||
| 200 | // TODO(bunnei): Verify arbiter address... | 213 | if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) |
| 201 | if (!VerifyWait(handle, WAITTYPE_ARB, arbiter)) | ||
| 202 | continue; | 214 | continue; |
| 203 | 215 | ||
| 204 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | 216 | if (thread == nullptr) |
| 217 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. | ||
| 218 | |||
| 205 | if(thread->current_priority <= priority) { | 219 | if(thread->current_priority <= priority) { |
| 206 | highest_priority_thread = handle; | 220 | highest_priority_thread = handle; |
| 207 | priority = thread->current_priority; | 221 | priority = thread->current_priority; |
| @@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 216 | 230 | ||
| 217 | /// Arbitrate all threads currently waiting | 231 | /// Arbitrate all threads currently waiting |
| 218 | void ArbitrateAllThreads(u32 arbiter, u32 address) { | 232 | void ArbitrateAllThreads(u32 arbiter, u32 address) { |
| 219 | 233 | ||
| 220 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 234 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 221 | for (const auto& handle : g_thread_queue) { | 235 | for (Handle handle : thread_queue) { |
| 236 | Thread* thread = g_object_pool.Get<Thread>(handle); | ||
| 222 | 237 | ||
| 223 | // TODO(bunnei): Verify arbiter address... | 238 | if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) |
| 224 | if (VerifyWait(handle, WAITTYPE_ARB, arbiter)) | ||
| 225 | ResumeThreadFromWait(handle); | 239 | ResumeThreadFromWait(handle); |
| 226 | } | 240 | } |
| 227 | } | 241 | } |
| @@ -238,11 +252,11 @@ void CallThread(Thread* t) { | |||
| 238 | /// Switches CPU context to that of the specified thread | 252 | /// Switches CPU context to that of the specified thread |
| 239 | void SwitchContext(Thread* t) { | 253 | void SwitchContext(Thread* t) { |
| 240 | Thread* cur = GetCurrentThread(); | 254 | Thread* cur = GetCurrentThread(); |
| 241 | 255 | ||
| 242 | // Save context for current thread | 256 | // Save context for current thread |
| 243 | if (cur) { | 257 | if (cur) { |
| 244 | SaveContext(cur->context); | 258 | SaveContext(cur->context); |
| 245 | 259 | ||
| 246 | if (cur->IsRunning()) { | 260 | if (cur->IsRunning()) { |
| 247 | ChangeReadyState(cur, true); | 261 | ChangeReadyState(cur, true); |
| 248 | } | 262 | } |
| @@ -263,23 +277,18 @@ void SwitchContext(Thread* t) { | |||
| 263 | Thread* NextThread() { | 277 | Thread* NextThread() { |
| 264 | Handle next; | 278 | Handle next; |
| 265 | Thread* cur = GetCurrentThread(); | 279 | Thread* cur = GetCurrentThread(); |
| 266 | 280 | ||
| 267 | if (cur && cur->IsRunning()) { | 281 | if (cur && cur->IsRunning()) { |
| 268 | next = g_thread_ready_queue.pop_first_better(cur->current_priority); | 282 | next = thread_ready_queue.pop_first_better(cur->current_priority); |
| 269 | } else { | 283 | } else { |
| 270 | next = g_thread_ready_queue.pop_first(); | 284 | next = thread_ready_queue.pop_first(); |
| 271 | } | 285 | } |
| 272 | if (next == 0) { | 286 | if (next == 0) { |
| 273 | return nullptr; | 287 | return nullptr; |
| 274 | } | 288 | } |
| 275 | return Kernel::g_object_pool.GetFast<Thread>(next); | 289 | return Kernel::g_object_pool.Get<Thread>(next); |
| 276 | } | 290 | } |
| 277 | 291 | ||
| 278 | /** | ||
| 279 | * Puts the current thread in the wait state for the given type | ||
| 280 | * @param wait_type Type of wait | ||
| 281 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | ||
| 282 | */ | ||
| 283 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | 292 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { |
| 284 | Thread* thread = GetCurrentThread(); | 293 | Thread* thread = GetCurrentThread(); |
| 285 | thread->wait_type = wait_type; | 294 | thread->wait_type = wait_type; |
| @@ -287,10 +296,14 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | |||
| 287 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 296 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |
| 288 | } | 297 | } |
| 289 | 298 | ||
| 299 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { | ||
| 300 | WaitCurrentThread(wait_type, wait_handle); | ||
| 301 | GetCurrentThread()->wait_address = wait_address; | ||
| 302 | } | ||
| 303 | |||
| 290 | /// Resumes a thread from waiting by marking it as "ready" | 304 | /// Resumes a thread from waiting by marking it as "ready" |
| 291 | void ResumeThreadFromWait(Handle handle) { | 305 | void ResumeThreadFromWait(Handle handle) { |
| 292 | u32 error; | 306 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); |
| 293 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error); | ||
| 294 | if (thread) { | 307 | if (thread) { |
| 295 | thread->status &= ~THREADSTATUS_WAIT; | 308 | thread->status &= ~THREADSTATUS_WAIT; |
| 296 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 309 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| @@ -305,12 +318,12 @@ void DebugThreadQueue() { | |||
| 305 | if (!thread) { | 318 | if (!thread) { |
| 306 | return; | 319 | return; |
| 307 | } | 320 | } |
| 308 | INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); | 321 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); |
| 309 | for (u32 i = 0; i < g_thread_queue.size(); i++) { | 322 | for (u32 i = 0; i < thread_queue.size(); i++) { |
| 310 | Handle handle = g_thread_queue[i]; | 323 | Handle handle = thread_queue[i]; |
| 311 | s32 priority = g_thread_ready_queue.contains(handle); | 324 | s32 priority = thread_ready_queue.contains(handle); |
| 312 | if (priority != -1) { | 325 | if (priority != -1) { |
| 313 | INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); | 326 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); |
| 314 | } | 327 | } |
| 315 | } | 328 | } |
| 316 | } | 329 | } |
| @@ -319,16 +332,17 @@ void DebugThreadQueue() { | |||
| 319 | Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, | 332 | Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, |
| 320 | s32 processor_id, u32 stack_top, int stack_size) { | 333 | s32 processor_id, u32 stack_top, int stack_size) { |
| 321 | 334 | ||
| 322 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | 335 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), |
| 323 | "CreateThread priority=%d, outside of allowable range!", priority) | 336 | "priority=%d, outside of allowable range!", priority) |
| 324 | 337 | ||
| 325 | Thread* thread = new Thread; | 338 | Thread* thread = new Thread; |
| 326 | 339 | ||
| 327 | handle = Kernel::g_object_pool.Create(thread); | 340 | handle = Kernel::g_object_pool.Create(thread); |
| 328 | 341 | ||
| 329 | g_thread_queue.push_back(handle); | 342 | thread_queue.push_back(handle); |
| 330 | g_thread_ready_queue.prepare(priority); | 343 | thread_ready_queue.prepare(priority); |
| 331 | 344 | ||
| 345 | thread->thread_id = next_thread_id++; | ||
| 332 | thread->status = THREADSTATUS_DORMANT; | 346 | thread->status = THREADSTATUS_DORMANT; |
| 333 | thread->entry_point = entry_point; | 347 | thread->entry_point = entry_point; |
| 334 | thread->stack_top = stack_top; | 348 | thread->stack_top = stack_top; |
| @@ -337,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 337 | thread->processor_id = processor_id; | 351 | thread->processor_id = processor_id; |
| 338 | thread->wait_type = WAITTYPE_NONE; | 352 | thread->wait_type = WAITTYPE_NONE; |
| 339 | thread->wait_handle = 0; | 353 | thread->wait_handle = 0; |
| 354 | thread->wait_address = 0; | ||
| 340 | thread->name = name; | 355 | thread->name = name; |
| 341 | 356 | ||
| 342 | return thread; | 357 | return thread; |
| @@ -347,28 +362,28 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 347 | u32 stack_top, int stack_size) { | 362 | u32 stack_top, int stack_size) { |
| 348 | 363 | ||
| 349 | if (name == nullptr) { | 364 | if (name == nullptr) { |
| 350 | ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); | 365 | LOG_ERROR(Kernel_SVC, "nullptr name"); |
| 351 | return -1; | 366 | return -1; |
| 352 | } | 367 | } |
| 353 | if ((u32)stack_size < 0x200) { | 368 | if ((u32)stack_size < 0x200) { |
| 354 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, | 369 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, |
| 355 | stack_size); | 370 | stack_size); |
| 356 | return -1; | 371 | return -1; |
| 357 | } | 372 | } |
| 358 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 373 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 359 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 374 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 360 | WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", | 375 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", |
| 361 | name, priority, new_priority); | 376 | name, priority, new_priority); |
| 362 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 377 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 363 | // validity of this | 378 | // validity of this |
| 364 | priority = new_priority; | 379 | priority = new_priority; |
| 365 | } | 380 | } |
| 366 | if (!Memory::GetPointer(entry_point)) { | 381 | if (!Memory::GetPointer(entry_point)) { |
| 367 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); | 382 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); |
| 368 | return -1; | 383 | return -1; |
| 369 | } | 384 | } |
| 370 | Handle handle; | 385 | Handle handle; |
| 371 | Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, | 386 | Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, |
| 372 | stack_size); | 387 | stack_size); |
| 373 | 388 | ||
| 374 | ResetThread(thread, arg, 0); | 389 | ResetThread(thread, arg, 0); |
| @@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 378 | } | 393 | } |
| 379 | 394 | ||
| 380 | /// Get the priority of the thread specified by handle | 395 | /// Get the priority of the thread specified by handle |
| 381 | u32 GetThreadPriority(const Handle handle) { | 396 | ResultVal<u32> GetThreadPriority(const Handle handle) { |
| 382 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | 397 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 383 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | 398 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 384 | return thread->current_priority; | 399 | |
| 400 | return MakeResult<u32>(thread->current_priority); | ||
| 385 | } | 401 | } |
| 386 | 402 | ||
| 387 | /// Set the priority of the thread specified by handle | 403 | /// Set the priority of the thread specified by handle |
| 388 | Result SetThreadPriority(Handle handle, s32 priority) { | 404 | ResultCode SetThreadPriority(Handle handle, s32 priority) { |
| 389 | Thread* thread = nullptr; | 405 | Thread* thread = nullptr; |
| 390 | if (!handle) { | 406 | if (!handle) { |
| 391 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? | 407 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? |
| 392 | } else { | 408 | } else { |
| 393 | thread = g_object_pool.GetFast<Thread>(handle); | 409 | thread = g_object_pool.Get<Thread>(handle); |
| 410 | if (thread == nullptr) { | ||
| 411 | return InvalidHandle(ErrorModule::Kernel); | ||
| 412 | } | ||
| 394 | } | 413 | } |
| 395 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | 414 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); |
| 396 | 415 | ||
| 397 | // If priority is invalid, clamp to valid range | 416 | // If priority is invalid, clamp to valid range |
| 398 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 417 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 399 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 418 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 400 | WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); | 419 | LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); |
| 401 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 420 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 402 | // validity of this | 421 | // validity of this |
| 403 | priority = new_priority; | 422 | priority = new_priority; |
| @@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) { | |||
| 405 | 424 | ||
| 406 | // Change thread priority | 425 | // Change thread priority |
| 407 | s32 old = thread->current_priority; | 426 | s32 old = thread->current_priority; |
| 408 | g_thread_ready_queue.remove(old, handle); | 427 | thread_ready_queue.remove(old, handle); |
| 409 | thread->current_priority = priority; | 428 | thread->current_priority = priority; |
| 410 | g_thread_ready_queue.prepare(thread->current_priority); | 429 | thread_ready_queue.prepare(thread->current_priority); |
| 411 | 430 | ||
| 412 | // Change thread status to "ready" and push to ready queue | 431 | // Change thread status to "ready" and push to ready queue |
| 413 | if (thread->IsRunning()) { | 432 | if (thread->IsRunning()) { |
| 414 | thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; | 433 | thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; |
| 415 | } | 434 | } |
| 416 | if (thread->IsReady()) { | 435 | if (thread->IsReady()) { |
| 417 | g_thread_ready_queue.push_back(thread->current_priority, handle); | 436 | thread_ready_queue.push_back(thread->current_priority, handle); |
| 418 | } | 437 | } |
| 419 | 438 | ||
| 420 | return 0; | 439 | return RESULT_SUCCESS; |
| 421 | } | 440 | } |
| 422 | 441 | ||
| 423 | /// Sets up the primary application thread | 442 | /// Sets up the primary application thread |
| 424 | Handle SetupMainThread(s32 priority, int stack_size) { | 443 | Handle SetupMainThread(s32 priority, int stack_size) { |
| 425 | Handle handle; | 444 | Handle handle; |
| 426 | 445 | ||
| 427 | // Initialize new "main" thread | 446 | // Initialize new "main" thread |
| 428 | Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, | 447 | Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, |
| 429 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | 448 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); |
| 430 | 449 | ||
| 431 | ResetThread(thread, 0, 0); | 450 | ResetThread(thread, 0, 0); |
| 432 | 451 | ||
| 433 | // If running another thread already, set it to "ready" state | 452 | // If running another thread already, set it to "ready" state |
| 434 | Thread* cur = GetCurrentThread(); | 453 | Thread* cur = GetCurrentThread(); |
| 435 | if (cur && cur->IsRunning()) { | 454 | if (cur && cur->IsRunning()) { |
| 436 | ChangeReadyState(cur, true); | 455 | ChangeReadyState(cur, true); |
| 437 | } | 456 | } |
| 438 | 457 | ||
| 439 | // Run new "main" thread | 458 | // Run new "main" thread |
| 440 | SetCurrentThread(thread); | 459 | SetCurrentThread(thread); |
| 441 | thread->status = THREADSTATUS_RUNNING; | 460 | thread->status = THREADSTATUS_RUNNING; |
| @@ -451,13 +470,13 @@ void Reschedule() { | |||
| 451 | Thread* next = NextThread(); | 470 | Thread* next = NextThread(); |
| 452 | HLE::g_reschedule = false; | 471 | HLE::g_reschedule = false; |
| 453 | if (next > 0) { | 472 | if (next > 0) { |
| 454 | INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | 473 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); |
| 455 | 474 | ||
| 456 | SwitchContext(next); | 475 | SwitchContext(next); |
| 457 | 476 | ||
| 458 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep | 477 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep |
| 459 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. | 478 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. |
| 460 | // This results in the current thread yielding on a VBLANK once, and then it will be | 479 | // This results in the current thread yielding on a VBLANK once, and then it will be |
| 461 | // immediately placed back in the queue for execution. | 480 | // immediately placed back in the queue for execution. |
| 462 | if (prev->wait_type == WAITTYPE_VBLANK) { | 481 | if (prev->wait_type == WAITTYPE_VBLANK) { |
| 463 | ResumeThreadFromWait(prev->GetHandle()); | 482 | ResumeThreadFromWait(prev->GetHandle()); |
| @@ -465,9 +484,21 @@ void Reschedule() { | |||
| 465 | } | 484 | } |
| 466 | } | 485 | } |
| 467 | 486 | ||
| 487 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | ||
| 488 | Thread* thread = g_object_pool.Get<Thread>(handle); | ||
| 489 | if (thread == nullptr) | ||
| 490 | return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, | ||
| 491 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 492 | |||
| 493 | *thread_id = thread->thread_id; | ||
| 494 | |||
| 495 | return RESULT_SUCCESS; | ||
| 496 | } | ||
| 497 | |||
| 468 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 498 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 469 | 499 | ||
| 470 | void ThreadingInit() { | 500 | void ThreadingInit() { |
| 501 | next_thread_id = INITIAL_THREAD_ID; | ||
| 471 | } | 502 | } |
| 472 | 503 | ||
| 473 | void ThreadingShutdown() { | 504 | void ThreadingShutdown() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 39fa38b75..be7adface 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -1,11 +1,15 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | |||
| 9 | #include "core/mem_map.h" | ||
| 10 | |||
| 8 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/result.h" | ||
| 9 | 13 | ||
| 10 | enum ThreadPriority { | 14 | enum ThreadPriority { |
| 11 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority | 15 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority |
| @@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); | |||
| 55 | void Reschedule(); | 59 | void Reschedule(); |
| 56 | 60 | ||
| 57 | /// Stops the current thread | 61 | /// Stops the current thread |
| 58 | void StopThread(Handle thread, const char* reason); | 62 | ResultCode StopThread(Handle thread, const char* reason); |
| 63 | |||
| 64 | /** | ||
| 65 | * Retrieves the ID of the specified thread handle | ||
| 66 | * @param thread_id Will contain the output thread id | ||
| 67 | * @param handle Handle to the thread we want | ||
| 68 | * @return Whether the function was successful or not | ||
| 69 | */ | ||
| 70 | ResultCode GetThreadId(u32* thread_id, Handle handle); | ||
| 59 | 71 | ||
| 60 | /// Resumes a thread from waiting by marking it as "ready" | 72 | /// Resumes a thread from waiting by marking it as "ready" |
| 61 | void ResumeThreadFromWait(Handle handle); | 73 | void ResumeThreadFromWait(Handle handle); |
| @@ -76,14 +88,22 @@ Handle GetCurrentThreadHandle(); | |||
| 76 | */ | 88 | */ |
| 77 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); | 89 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); |
| 78 | 90 | ||
| 91 | /** | ||
| 92 | * Puts the current thread in the wait state for the given type | ||
| 93 | * @param wait_type Type of wait | ||
| 94 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | ||
| 95 | * @param wait_address Arbitration address used to resume from wait | ||
| 96 | */ | ||
| 97 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); | ||
| 98 | |||
| 79 | /// Put current thread in a wait state - on WaitSynchronization | 99 | /// Put current thread in a wait state - on WaitSynchronization |
| 80 | void WaitThread_Synchronization(); | 100 | void WaitThread_Synchronization(); |
| 81 | 101 | ||
| 82 | /// Get the priority of the thread specified by handle | 102 | /// Get the priority of the thread specified by handle |
| 83 | u32 GetThreadPriority(const Handle handle); | 103 | ResultVal<u32> GetThreadPriority(const Handle handle); |
| 84 | 104 | ||
| 85 | /// Set the priority of the thread specified by handle | 105 | /// Set the priority of the thread specified by handle |
| 86 | Result SetThreadPriority(Handle handle, s32 priority); | 106 | ResultCode SetThreadPriority(Handle handle, s32 priority); |
| 87 | 107 | ||
| 88 | /// Initialize threading | 108 | /// Initialize threading |
| 89 | void ThreadingInit(); | 109 | void ThreadingInit(); |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h new file mode 100644 index 000000000..14d2be4a2 --- /dev/null +++ b/src/core/hle/result.h | |||
| @@ -0,0 +1,402 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cassert> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <type_traits> | ||
| 10 | #include <utility> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/bit_field.h" | ||
| 14 | |||
| 15 | // All the constants in this file come from http://3dbrew.org/wiki/Error_codes | ||
| 16 | |||
| 17 | /// Detailed description of the error. This listing is likely incomplete. | ||
| 18 | enum class ErrorDescription : u32 { | ||
| 19 | Success = 0, | ||
| 20 | FS_NotFound = 100, | ||
| 21 | FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive | ||
| 22 | InvalidSection = 1000, | ||
| 23 | TooLarge = 1001, | ||
| 24 | NotAuthorized = 1002, | ||
| 25 | AlreadyDone = 1003, | ||
| 26 | InvalidSize = 1004, | ||
| 27 | InvalidEnumValue = 1005, | ||
| 28 | InvalidCombination = 1006, | ||
| 29 | NoData = 1007, | ||
| 30 | Busy = 1008, | ||
| 31 | MisalignedAddress = 1009, | ||
| 32 | MisalignedSize = 1010, | ||
| 33 | OutOfMemory = 1011, | ||
| 34 | NotImplemented = 1012, | ||
| 35 | InvalidAddress = 1013, | ||
| 36 | InvalidPointer = 1014, | ||
| 37 | InvalidHandle = 1015, | ||
| 38 | NotInitialized = 1016, | ||
| 39 | AlreadyInitialized = 1017, | ||
| 40 | NotFound = 1018, | ||
| 41 | CancelRequested = 1019, | ||
| 42 | AlreadyExists = 1020, | ||
| 43 | OutOfRange = 1021, | ||
| 44 | Timeout = 1022, | ||
| 45 | InvalidResultValue = 1023, | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Identifies the module which caused the error. Error codes can be propagated through a call | ||
| 50 | * chain, meaning that this doesn't always correspond to the module where the API call made is | ||
| 51 | * contained. | ||
| 52 | */ | ||
| 53 | enum class ErrorModule : u32 { | ||
| 54 | Common = 0, | ||
| 55 | Kernel = 1, | ||
| 56 | Util = 2, | ||
| 57 | FileServer = 3, | ||
| 58 | LoaderServer = 4, | ||
| 59 | TCB = 5, | ||
| 60 | OS = 6, | ||
| 61 | DBG = 7, | ||
| 62 | DMNT = 8, | ||
| 63 | PDN = 9, | ||
| 64 | GX = 10, | ||
| 65 | I2C = 11, | ||
| 66 | GPIO = 12, | ||
| 67 | DD = 13, | ||
| 68 | CODEC = 14, | ||
| 69 | SPI = 15, | ||
| 70 | PXI = 16, | ||
| 71 | FS = 17, | ||
| 72 | DI = 18, | ||
| 73 | HID = 19, | ||
| 74 | CAM = 20, | ||
| 75 | PI = 21, | ||
| 76 | PM = 22, | ||
| 77 | PM_LOW = 23, | ||
| 78 | FSI = 24, | ||
| 79 | SRV = 25, | ||
| 80 | NDM = 26, | ||
| 81 | NWM = 27, | ||
| 82 | SOC = 28, | ||
| 83 | LDR = 29, | ||
| 84 | ACC = 30, | ||
| 85 | RomFS = 31, | ||
| 86 | AM = 32, | ||
| 87 | HIO = 33, | ||
| 88 | Updater = 34, | ||
| 89 | MIC = 35, | ||
| 90 | FND = 36, | ||
| 91 | MP = 37, | ||
| 92 | MPWL = 38, | ||
| 93 | AC = 39, | ||
| 94 | HTTP = 40, | ||
| 95 | DSP = 41, | ||
| 96 | SND = 42, | ||
| 97 | DLP = 43, | ||
| 98 | HIO_LOW = 44, | ||
| 99 | CSND = 45, | ||
| 100 | SSL = 46, | ||
| 101 | AM_LOW = 47, | ||
| 102 | NEX = 48, | ||
| 103 | Friends = 49, | ||
| 104 | RDT = 50, | ||
| 105 | Applet = 51, | ||
| 106 | NIM = 52, | ||
| 107 | PTM = 53, | ||
| 108 | MIDI = 54, | ||
| 109 | MC = 55, | ||
| 110 | SWC = 56, | ||
| 111 | FatFS = 57, | ||
| 112 | NGC = 58, | ||
| 113 | CARD = 59, | ||
| 114 | CARDNOR = 60, | ||
| 115 | SDMC = 61, | ||
| 116 | BOSS = 62, | ||
| 117 | DBM = 63, | ||
| 118 | Config = 64, | ||
| 119 | PS = 65, | ||
| 120 | CEC = 66, | ||
| 121 | IR = 67, | ||
| 122 | UDS = 68, | ||
| 123 | PL = 69, | ||
| 124 | CUP = 70, | ||
| 125 | Gyroscope = 71, | ||
| 126 | MCU = 72, | ||
| 127 | NS = 73, | ||
| 128 | News = 74, | ||
| 129 | RO_1 = 75, | ||
| 130 | GD = 76, | ||
| 131 | CardSPI = 77, | ||
| 132 | EC = 78, | ||
| 133 | RO_2 = 79, | ||
| 134 | WebBrowser = 80, | ||
| 135 | Test = 81, | ||
| 136 | ENC = 82, | ||
| 137 | PIA = 83, | ||
| 138 | |||
| 139 | Application = 254, | ||
| 140 | InvalidResult = 255 | ||
| 141 | }; | ||
| 142 | |||
| 143 | /// A less specific error cause. | ||
| 144 | enum class ErrorSummary : u32 { | ||
| 145 | Success = 0, | ||
| 146 | NothingHappened = 1, | ||
| 147 | WouldBlock = 2, | ||
| 148 | OutOfResource = 3, ///< There are no more kernel resources (memory, table slots) to | ||
| 149 | ///< execute the operation. | ||
| 150 | NotFound = 4, ///< A file or resource was not found. | ||
| 151 | InvalidState = 5, | ||
| 152 | NotSupported = 6, ///< The operation is not supported or not implemented. | ||
| 153 | InvalidArgument = 7, ///< Returned when a passed argument is invalid in the current runtime | ||
| 154 | ///< context. (Invalid handle, out-of-bounds pointer or size, etc.) | ||
| 155 | WrongArgument = 8, ///< Returned when a passed argument is in an incorrect format for use | ||
| 156 | ///< with the function. (E.g. Invalid enum value) | ||
| 157 | Canceled = 9, | ||
| 158 | StatusChanged = 10, | ||
| 159 | Internal = 11, | ||
| 160 | |||
| 161 | InvalidResult = 63 | ||
| 162 | }; | ||
| 163 | |||
| 164 | /// The severity of the error. | ||
| 165 | enum class ErrorLevel : u32 { | ||
| 166 | Success = 0, | ||
| 167 | Info = 1, | ||
| 168 | |||
| 169 | Status = 25, | ||
| 170 | Temporary = 26, | ||
| 171 | Permanent = 27, | ||
| 172 | Usage = 28, | ||
| 173 | Reinitialize = 29, | ||
| 174 | Reset = 30, | ||
| 175 | Fatal = 31 | ||
| 176 | }; | ||
| 177 | |||
| 178 | /// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields. | ||
| 179 | union ResultCode { | ||
| 180 | u32 raw; | ||
| 181 | |||
| 182 | BitField<0, 10, ErrorDescription> description; | ||
| 183 | BitField<10, 8, ErrorModule> module; | ||
| 184 | |||
| 185 | BitField<21, 6, ErrorSummary> summary; | ||
| 186 | BitField<27, 5, ErrorLevel> level; | ||
| 187 | |||
| 188 | // The last bit of `level` is checked by apps and the kernel to determine if a result code is an error | ||
| 189 | BitField<31, 1, u32> is_error; | ||
| 190 | |||
| 191 | explicit ResultCode(u32 raw) : raw(raw) {} | ||
| 192 | ResultCode(ErrorDescription description_, ErrorModule module_, | ||
| 193 | ErrorSummary summary_, ErrorLevel level_) : raw(0) { | ||
| 194 | description = description_; | ||
| 195 | module = module_; | ||
| 196 | summary = summary_; | ||
| 197 | level = level_; | ||
| 198 | } | ||
| 199 | |||
| 200 | ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; } | ||
| 201 | |||
| 202 | bool IsSuccess() const { | ||
| 203 | return is_error == 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | bool IsError() const { | ||
| 207 | return is_error == 1; | ||
| 208 | } | ||
| 209 | }; | ||
| 210 | |||
| 211 | inline bool operator==(const ResultCode a, const ResultCode b) { | ||
| 212 | return a.raw == b.raw; | ||
| 213 | } | ||
| 214 | |||
| 215 | inline bool operator!=(const ResultCode a, const ResultCode b) { | ||
| 216 | return a.raw != b.raw; | ||
| 217 | } | ||
| 218 | |||
| 219 | // Convenience functions for creating some common kinds of errors: | ||
| 220 | |||
| 221 | /// The default success `ResultCode`. | ||
| 222 | const ResultCode RESULT_SUCCESS(0); | ||
| 223 | |||
| 224 | /// Might be returned instead of a dummy success for unimplemented APIs. | ||
| 225 | inline ResultCode UnimplementedFunction(ErrorModule module) { | ||
| 226 | return ResultCode(ErrorDescription::NotImplemented, module, | ||
| 227 | ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 228 | } | ||
| 229 | /// Returned when a function is passed an invalid handle. | ||
| 230 | inline ResultCode InvalidHandle(ErrorModule module) { | ||
| 231 | return ResultCode(ErrorDescription::InvalidHandle, module, | ||
| 232 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 233 | } | ||
| 234 | |||
| 235 | /** | ||
| 236 | * This is an optional value type. It holds a `ResultCode` and, if that code is a success code, | ||
| 237 | * also holds a result of type `T`. If the code is an error code then trying to access the inner | ||
| 238 | * value fails, thus ensuring that the ResultCode of functions is always checked properly before | ||
| 239 | * their return value is used. It is similar in concept to the `std::optional` type | ||
| 240 | * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in | ||
| 241 | * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html). | ||
| 242 | * | ||
| 243 | * An example of how it could be used: | ||
| 244 | * \code | ||
| 245 | * ResultVal<int> Frobnicate(float strength) { | ||
| 246 | * if (strength < 0.f || strength > 1.0f) { | ||
| 247 | * // Can't frobnicate too weakly or too strongly | ||
| 248 | * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common, | ||
| 249 | * ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 250 | * } else { | ||
| 251 | * // Frobnicated! Give caller a cookie | ||
| 252 | * return MakeResult<int>(42); | ||
| 253 | * } | ||
| 254 | * } | ||
| 255 | * \endcode | ||
| 256 | * | ||
| 257 | * \code | ||
| 258 | * ResultVal<int> frob_result = Frobnicate(0.75f); | ||
| 259 | * if (frob_result) { | ||
| 260 | * // Frobbed ok | ||
| 261 | * printf("My cookie is %d\n", *frob_result); | ||
| 262 | * } else { | ||
| 263 | * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex); | ||
| 264 | * } | ||
| 265 | * \endcode | ||
| 266 | */ | ||
| 267 | template <typename T> | ||
| 268 | class ResultVal { | ||
| 269 | public: | ||
| 270 | /// Constructs an empty `ResultVal` with the given error code. The code must not be a success code. | ||
| 271 | ResultVal(ResultCode error_code = ResultCode(-1)) | ||
| 272 | : result_code(error_code) | ||
| 273 | { | ||
| 274 | assert(error_code.IsError()); | ||
| 275 | UpdateDebugPtr(); | ||
| 276 | } | ||
| 277 | |||
| 278 | /** | ||
| 279 | * Similar to the non-member function `MakeResult`, with the exception that you can manually | ||
| 280 | * specify the success code. `success_code` must not be an error code. | ||
| 281 | */ | ||
| 282 | template <typename... Args> | ||
| 283 | static ResultVal WithCode(ResultCode success_code, Args&&... args) { | ||
| 284 | ResultVal<T> result; | ||
| 285 | result.emplace(success_code, std::forward<Args>(args)...); | ||
| 286 | return result; | ||
| 287 | } | ||
| 288 | |||
| 289 | ResultVal(const ResultVal& o) | ||
| 290 | : result_code(o.result_code) | ||
| 291 | { | ||
| 292 | if (!o.empty()) { | ||
| 293 | new (&storage) T(*o.GetPointer()); | ||
| 294 | } | ||
| 295 | UpdateDebugPtr(); | ||
| 296 | } | ||
| 297 | |||
| 298 | ResultVal(ResultVal&& o) | ||
| 299 | : result_code(o.result_code) | ||
| 300 | { | ||
| 301 | if (!o.empty()) { | ||
| 302 | new (&storage) T(std::move(*o.GetPointer())); | ||
| 303 | } | ||
| 304 | UpdateDebugPtr(); | ||
| 305 | } | ||
| 306 | |||
| 307 | ~ResultVal() { | ||
| 308 | if (!empty()) { | ||
| 309 | GetPointer()->~T(); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | ResultVal& operator=(const ResultVal& o) { | ||
| 314 | if (*this) { | ||
| 315 | if (o) { | ||
| 316 | *GetPointer() = *o.GetPointer(); | ||
| 317 | } else { | ||
| 318 | GetPointer()->~T(); | ||
| 319 | } | ||
| 320 | } else { | ||
| 321 | if (o) { | ||
| 322 | new (&storage) T(*o.GetPointer()); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | result_code = o.result_code; | ||
| 326 | UpdateDebugPtr(); | ||
| 327 | |||
| 328 | return *this; | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Replaces the current result with a new constructed result value in-place. The code must not | ||
| 333 | * be an error code. | ||
| 334 | */ | ||
| 335 | template <typename... Args> | ||
| 336 | void emplace(ResultCode success_code, Args&&... args) { | ||
| 337 | assert(success_code.IsSuccess()); | ||
| 338 | if (!empty()) { | ||
| 339 | GetPointer()->~T(); | ||
| 340 | } | ||
| 341 | new (&storage) T(std::forward<Args>(args)...); | ||
| 342 | result_code = success_code; | ||
| 343 | UpdateDebugPtr(); | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Returns true if the `ResultVal` contains an error code and no value. | ||
| 347 | bool empty() const { return result_code.IsError(); } | ||
| 348 | |||
| 349 | /// Returns true if the `ResultVal` contains a return value. | ||
| 350 | bool Succeeded() const { return result_code.IsSuccess(); } | ||
| 351 | /// Returns true if the `ResultVal` contains an error code and no value. | ||
| 352 | bool Failed() const { return empty(); } | ||
| 353 | |||
| 354 | ResultCode Code() const { return result_code; } | ||
| 355 | |||
| 356 | const T& operator* () const { return *GetPointer(); } | ||
| 357 | T& operator* () { return *GetPointer(); } | ||
| 358 | const T* operator->() const { return GetPointer(); } | ||
| 359 | T* operator->() { return GetPointer(); } | ||
| 360 | |||
| 361 | /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. | ||
| 362 | template <typename U> | ||
| 363 | T ValueOr(U&& value) const { | ||
| 364 | return !empty() ? *GetPointer() : std::move(value); | ||
| 365 | } | ||
| 366 | |||
| 367 | private: | ||
| 368 | typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType; | ||
| 369 | |||
| 370 | StorageType storage; | ||
| 371 | ResultCode result_code; | ||
| 372 | #if _DEBUG | ||
| 373 | // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the | ||
| 374 | // need to cast `storage` to a pointer or pay attention to `result_code`. | ||
| 375 | const T* debug_ptr; | ||
| 376 | #endif | ||
| 377 | |||
| 378 | void UpdateDebugPtr() { | ||
| 379 | #if _DEBUG | ||
| 380 | debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage)); | ||
| 381 | #endif | ||
| 382 | } | ||
| 383 | |||
| 384 | const T* GetPointer() const { | ||
| 385 | assert(!empty()); | ||
| 386 | return static_cast<const T*>(static_cast<const void*>(&storage)); | ||
| 387 | } | ||
| 388 | |||
| 389 | T* GetPointer() { | ||
| 390 | assert(!empty()); | ||
| 391 | return static_cast<T*>(static_cast<void*>(&storage)); | ||
| 392 | } | ||
| 393 | }; | ||
| 394 | |||
| 395 | /** | ||
| 396 | * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct | ||
| 397 | * `T` with and creates a success `ResultVal` contained the constructed value. | ||
| 398 | */ | ||
| 399 | template <typename T, typename... Args> | ||
| 400 | ResultVal<T> MakeResult(Args&&... args) { | ||
| 401 | return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...); | ||
| 402 | } | ||
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index b39603bdf..311682abf 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp | |||
| @@ -11,6 +11,24 @@ | |||
| 11 | 11 | ||
| 12 | namespace AC_U { | 12 | namespace AC_U { |
| 13 | 13 | ||
| 14 | /** | ||
| 15 | * AC_U::GetWifiStatus service function | ||
| 16 | * Outputs: | ||
| 17 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 18 | * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. | ||
| 19 | */ | ||
| 20 | void GetWifiStatus(Service::Interface* self) { | ||
| 21 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 22 | |||
| 23 | // TODO(purpasmart96): This function is only a stub, | ||
| 24 | // it returns a valid result without implementing full functionality. | ||
| 25 | |||
| 26 | cmd_buff[1] = 0; // No error | ||
| 27 | cmd_buff[2] = 0; // Connection type set to none | ||
| 28 | |||
| 29 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 30 | } | ||
| 31 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 32 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010000, nullptr, "CreateDefaultConfig"}, | 33 | {0x00010000, nullptr, "CreateDefaultConfig"}, |
| 16 | {0x00040006, nullptr, "ConnectAsync"}, | 34 | {0x00040006, nullptr, "ConnectAsync"}, |
| @@ -18,16 +36,16 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 18 | {0x00080004, nullptr, "CloseAsync"}, | 36 | {0x00080004, nullptr, "CloseAsync"}, |
| 19 | {0x00090002, nullptr, "GetCloseResult"}, | 37 | {0x00090002, nullptr, "GetCloseResult"}, |
| 20 | {0x000A0000, nullptr, "GetLastErrorCode"}, | 38 | {0x000A0000, nullptr, "GetLastErrorCode"}, |
| 21 | {0x000D0000, nullptr, "GetWifiStatus"}, | 39 | {0x000D0000, GetWifiStatus, "GetWifiStatus"}, |
| 22 | {0x000E0042, nullptr, "GetCurrentAPInfo"}, | 40 | {0x000E0042, nullptr, "GetCurrentAPInfo"}, |
| 23 | {0x00100042, nullptr, "GetCurrentNZoneInfo"}, | 41 | {0x00100042, nullptr, "GetCurrentNZoneInfo"}, |
| 24 | {0x00110042, nullptr, "GetNZoneApNumService"}, | 42 | {0x00110042, nullptr, "GetNZoneApNumService"}, |
| 25 | {0x00240042, nullptr, "AddDenyApType "}, | 43 | {0x00240042, nullptr, "AddDenyApType"}, |
| 26 | {0x00270002, nullptr, "GetInfraPriority "}, | 44 | {0x00270002, nullptr, "GetInfraPriority"}, |
| 27 | {0x002D0082, nullptr, "SetRequestEulaVersion"}, | 45 | {0x002D0082, nullptr, "SetRequestEulaVersion"}, |
| 28 | {0x00300004, nullptr, "RegisterDisconnectEvent"}, | 46 | {0x00300004, nullptr, "RegisterDisconnectEvent"}, |
| 29 | {0x003C0042, nullptr, "GetAPSSIDList"}, | 47 | {0x003C0042, nullptr, "GetAPSSIDList"}, |
| 30 | {0x003E0042, nullptr, "IsConnected "}, | 48 | {0x003E0042, nullptr, "IsConnected"}, |
| 31 | {0x00400042, nullptr, "SetClientVersion"}, | 49 | {0x00400042, nullptr, "SetClientVersion"}, |
| 32 | }; | 50 | }; |
| 33 | 51 | ||
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h index 3c5958d27..c91b28353 100644 --- a/src/core/hle/service/ac_u.h +++ b/src/core/hle/service/ac_u.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // Namespace AC_U | 10 | // Namespace AC_U |
| 11 | 11 | ||
| 12 | // socket service "ac:u" | 12 | // socket service "ac:u" |
| 13 | 13 | ||
| 14 | namespace AC_U { | 14 | namespace AC_U { |
| 15 | 15 | ||
| @@ -21,7 +21,7 @@ public: | |||
| 21 | * Gets the string port name used by CTROS for the service | 21 | * Gets the string port name used by CTROS for the service |
| 22 | * @return Port name of service | 22 | * @return Port name of service |
| 23 | */ | 23 | */ |
| 24 | std::string GetPortName() const { | 24 | std::string GetPortName() const override { |
| 25 | return "ac:u"; | 25 | return "ac:u"; |
| 26 | } | 26 | } |
| 27 | }; | 27 | }; |
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp new file mode 100644 index 000000000..b8b06418c --- /dev/null +++ b/src/core/hle/service/am_app.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/am_app.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace AM_APP | ||
| 11 | |||
| 12 | namespace AM_APP { | ||
| 13 | |||
| 14 | // Empty arrays are illegal -- commented out until an entry is added. | ||
| 15 | //const Interface::FunctionInfo FunctionTable[] = { }; | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // Interface class | ||
| 19 | |||
| 20 | Interface::Interface() { | ||
| 21 | //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 22 | } | ||
| 23 | |||
| 24 | } // namespace | ||
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h new file mode 100644 index 000000000..86a5f5b74 --- /dev/null +++ b/src/core/hle/service/am_app.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace AM_APP | ||
| 11 | |||
| 12 | namespace AM_APP { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "am:app"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp new file mode 100644 index 000000000..403cac353 --- /dev/null +++ b/src/core/hle/service/am_net.cpp | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/am_net.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace AM_NET | ||
| 11 | |||
| 12 | namespace AM_NET { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x08010000, nullptr, "OpenTicket"}, | ||
| 16 | {0x08020002, nullptr, "TicketAbortInstall"}, | ||
| 17 | {0x08030002, nullptr, "TicketFinalizeInstall"}, | ||
| 18 | {0x08040100, nullptr, "InstallTitleBegin"}, | ||
| 19 | {0x08050000, nullptr, "InstallTitleAbort"}, | ||
| 20 | {0x080600C0, nullptr, "InstallTitleResume"}, | ||
| 21 | {0x08070000, nullptr, "InstallTitleAbortTMD"}, | ||
| 22 | {0x08080000, nullptr, "InstallTitleFinish"}, | ||
| 23 | {0x080A0000, nullptr, "OpenTMD"}, | ||
| 24 | {0x080B0002, nullptr, "TMDAbortInstall"}, | ||
| 25 | {0x080C0042, nullptr, "TMDFinalizeInstall"}, | ||
| 26 | {0x080E0040, nullptr, "OpenContentCreate"}, | ||
| 27 | {0x080F0002, nullptr, "ContentAbortInstall"}, | ||
| 28 | {0x08100040, nullptr, "OpenContentResume"}, | ||
| 29 | {0x08120002, nullptr, "ContentFinalizeInstall"}, | ||
| 30 | {0x08130000, nullptr, "GetTotalContents"}, | ||
| 31 | {0x08140042, nullptr, "GetContentIndexes"}, | ||
| 32 | {0x08150044, nullptr, "GetContentsInfo"}, | ||
| 33 | {0x08190108, nullptr, "Unknown"}, | ||
| 34 | {0x081B00C2, nullptr, "InstallTitlesFinish"}, | ||
| 35 | }; | ||
| 36 | |||
| 37 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 38 | // Interface class | ||
| 39 | |||
| 40 | Interface::Interface() { | ||
| 41 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 42 | } | ||
| 43 | |||
| 44 | Interface::~Interface() { | ||
| 45 | } | ||
| 46 | |||
| 47 | } // namespace | ||
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/am_net.h index 005382540..4816e1697 100644 --- a/src/core/hle/service/fs_user.h +++ b/src/core/hle/service/am_net.h | |||
| @@ -7,24 +7,20 @@ | |||
| 7 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | 8 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // Namespace FS_User | 10 | // Namespace AM_NET |
| 11 | 11 | ||
| 12 | namespace FS_User { | 12 | namespace AM_NET { |
| 13 | 13 | ||
| 14 | /// Interface to "fs:USER" service | ||
| 15 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 16 | public: | 15 | public: |
| 17 | |||
| 18 | Interface(); | 16 | Interface(); |
| 19 | |||
| 20 | ~Interface(); | 17 | ~Interface(); |
| 21 | |||
| 22 | /** | 18 | /** |
| 23 | * Gets the string port name used by CTROS for the service | 19 | * Gets the string port name used by CTROS for the service |
| 24 | * @return Port name of service | 20 | * @return Port name of service |
| 25 | */ | 21 | */ |
| 26 | std::string GetPortName() const override { | 22 | std::string GetPortName() const override { |
| 27 | return "fs:USER"; | 23 | return "am:net"; |
| 28 | } | 24 | } |
| 29 | }; | 25 | }; |
| 30 | 26 | ||
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index 617b6add4..ebfba4d8d 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp | |||
| @@ -4,10 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | #include "common/common.h" | 6 | #include "common/common.h" |
| 7 | #include "common/file_util.h" | ||
| 7 | 8 | ||
| 8 | #include "core/hle/hle.h" | 9 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/event.h" |
| 10 | #include "core/hle/kernel/mutex.h" | 11 | #include "core/hle/kernel/mutex.h" |
| 12 | #include "core/hle/kernel/shared_memory.h" | ||
| 11 | #include "apt_u.h" | 13 | #include "apt_u.h" |
| 12 | 14 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -15,6 +17,20 @@ | |||
| 15 | 17 | ||
| 16 | namespace APT_U { | 18 | namespace APT_U { |
| 17 | 19 | ||
| 20 | // Address used for shared font (as observed on HW) | ||
| 21 | // TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via | ||
| 22 | // https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any | ||
| 23 | // address other than 0x18000000 due to internal pointers in the shared font dump that would need to | ||
| 24 | // be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then | ||
| 25 | // correctly mapping it in Citra, however we still do not understand how the mapping is determined. | ||
| 26 | static const VAddr SHARED_FONT_VADDR = 0x18000000; | ||
| 27 | |||
| 28 | // Handle to shared memory region designated to for shared system font | ||
| 29 | static Handle shared_font_mem = 0; | ||
| 30 | |||
| 31 | static Handle lock_handle = 0; | ||
| 32 | static std::vector<u8> shared_font; | ||
| 33 | |||
| 18 | /// Signals used by APT functions | 34 | /// Signals used by APT functions |
| 19 | enum class SignalType : u32 { | 35 | enum class SignalType : u32 { |
| 20 | None = 0x0, | 36 | None = 0x0, |
| @@ -24,83 +40,178 @@ enum class SignalType : u32 { | |||
| 24 | }; | 40 | }; |
| 25 | 41 | ||
| 26 | void Initialize(Service::Interface* self) { | 42 | void Initialize(Service::Interface* self) { |
| 27 | u32* cmd_buff = Service::GetCommandBuffer(); | 43 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 28 | 44 | ||
| 29 | cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle | 45 | cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle |
| 30 | cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle | 46 | cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle |
| 31 | 47 | ||
| 32 | Kernel::SetEventLocked(cmd_buff[3], true); | 48 | Kernel::SetEventLocked(cmd_buff[3], true); |
| 33 | Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event | 49 | Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event |
| 34 | 50 | ||
| 51 | _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock"); | ||
| 52 | Kernel::ReleaseMutex(lock_handle); | ||
| 53 | |||
| 35 | cmd_buff[1] = 0; // No error | 54 | cmd_buff[1] = 0; // No error |
| 36 | DEBUG_LOG(KERNEL, "called"); | 55 | |
| 56 | LOG_DEBUG(Service_APT, "called"); | ||
| 37 | } | 57 | } |
| 38 | 58 | ||
| 39 | void GetLockHandle(Service::Interface* self) { | 59 | void GetLockHandle(Service::Interface* self) { |
| 40 | u32* cmd_buff = Service::GetCommandBuffer(); | 60 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 41 | u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field | 61 | u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field |
| 62 | |||
| 63 | if (0 == lock_handle) { | ||
| 64 | // TODO(bunnei): Verify if this is created here or at application boot? | ||
| 65 | lock_handle = Kernel::CreateMutex(false, "APT_U:Lock"); | ||
| 66 | Kernel::ReleaseMutex(lock_handle); | ||
| 67 | } | ||
| 42 | cmd_buff[1] = 0; // No error | 68 | cmd_buff[1] = 0; // No error |
| 43 | cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock"); | 69 | |
| 44 | DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]); | 70 | // Not sure what these parameters are used for, but retail apps check that they are 0 after |
| 71 | // GetLockHandle has been called. | ||
| 72 | cmd_buff[2] = 0; | ||
| 73 | cmd_buff[3] = 0; | ||
| 74 | cmd_buff[4] = 0; | ||
| 75 | |||
| 76 | cmd_buff[5] = lock_handle; | ||
| 77 | LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]); | ||
| 45 | } | 78 | } |
| 46 | 79 | ||
| 47 | void Enable(Service::Interface* self) { | 80 | void Enable(Service::Interface* self) { |
| 48 | u32* cmd_buff = Service::GetCommandBuffer(); | 81 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 49 | u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? | 82 | u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? |
| 50 | cmd_buff[1] = 0; // No error | 83 | cmd_buff[1] = 0; // No error |
| 51 | WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk); | 84 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); |
| 52 | } | 85 | } |
| 53 | 86 | ||
| 54 | void InquireNotification(Service::Interface* self) { | 87 | void InquireNotification(Service::Interface* self) { |
| 55 | u32* cmd_buff = Service::GetCommandBuffer(); | 88 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 56 | u32 app_id = cmd_buff[2]; | 89 | u32 app_id = cmd_buff[2]; |
| 57 | cmd_buff[1] = 0; // No error | 90 | cmd_buff[1] = 0; // No error |
| 58 | cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type | 91 | cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type |
| 59 | WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id); | 92 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); |
| 60 | } | 93 | } |
| 61 | 94 | ||
| 95 | /** | ||
| 96 | * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state, | ||
| 97 | * from the source process which set the parameters. Once finished, NS will clear a flag in the NS | ||
| 98 | * state so that this command will return an error if this command is used again if parameters were | ||
| 99 | * not set again. This is called when the second Initialize event is triggered. It returns a signal | ||
| 100 | * type indicating why it was triggered. | ||
| 101 | * Inputs: | ||
| 102 | * 1 : AppID | ||
| 103 | * 2 : Parameter buffer size, max size is 0x1000 | ||
| 104 | * Outputs: | ||
| 105 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 106 | * 2 : Unknown, for now assume AppID of the process which sent these parameters | ||
| 107 | * 3 : Unknown, for now assume Signal type | ||
| 108 | * 4 : Actual parameter buffer size, this is <= to the the input size | ||
| 109 | * 5 : Value | ||
| 110 | * 6 : Handle from the source process which set the parameters, likely used for shared memory | ||
| 111 | * 7 : Size | ||
| 112 | * 8 : Output parameter buffer ptr | ||
| 113 | */ | ||
| 62 | void ReceiveParameter(Service::Interface* self) { | 114 | void ReceiveParameter(Service::Interface* self) { |
| 63 | u32* cmd_buff = Service::GetCommandBuffer(); | 115 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 64 | u32 app_id = cmd_buff[1]; | 116 | u32 app_id = cmd_buff[1]; |
| 65 | u32 buffer_size = cmd_buff[2]; | 117 | u32 buffer_size = cmd_buff[2]; |
| 66 | cmd_buff[1] = 0; // No error | 118 | cmd_buff[1] = 0; // No error |
| 67 | cmd_buff[2] = 0; | 119 | cmd_buff[2] = 0; |
| 68 | cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type | 120 | cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type |
| 69 | cmd_buff[4] = 0x10; | 121 | cmd_buff[4] = 0x10; // Parameter buffer size (16) |
| 70 | cmd_buff[5] = 0; | 122 | cmd_buff[5] = 0; |
| 71 | cmd_buff[6] = 0; | 123 | cmd_buff[6] = 0; |
| 72 | cmd_buff[7] = 0; | 124 | cmd_buff[7] = 0; |
| 73 | WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | 125 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); |
| 74 | } | 126 | } |
| 75 | 127 | ||
| 76 | /** | 128 | /** |
| 77 | * APT_U::GlanceParameter service function | 129 | * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter |
| 78 | * Inputs: | 130 | * (except for the word value prior to the output handle), except this will not clear the flag |
| 79 | * 1 : AppID | 131 | * (except when responseword[3]==8 || responseword[3]==9) in NS state. |
| 80 | * 2 : Parameter buffer size, max size is 0x1000 | 132 | * Inputs: |
| 81 | * Outputs: | 133 | * 1 : AppID |
| 82 | * 1 : Result of function, 0 on success, otherwise error code | 134 | * 2 : Parameter buffer size, max size is 0x1000 |
| 83 | * 2 : Unknown, for now assume AppID of the process which sent these parameters | 135 | * Outputs: |
| 84 | * 3 : Unknown, for now assume Signal type | 136 | * 1 : Result of function, 0 on success, otherwise error code |
| 85 | * 4 : Actual parameter buffer size, this is <= to the the input size | 137 | * 2 : Unknown, for now assume AppID of the process which sent these parameters |
| 86 | * 5 : Value | 138 | * 3 : Unknown, for now assume Signal type |
| 87 | * 6 : Handle from the source process which set the parameters, likely used for shared memory | 139 | * 4 : Actual parameter buffer size, this is <= to the the input size |
| 88 | * 7 : Size | 140 | * 5 : Value |
| 89 | * 8 : Output parameter buffer ptr | 141 | * 6 : Handle from the source process which set the parameters, likely used for shared memory |
| 90 | */ | 142 | * 7 : Size |
| 143 | * 8 : Output parameter buffer ptr | ||
| 144 | */ | ||
| 91 | void GlanceParameter(Service::Interface* self) { | 145 | void GlanceParameter(Service::Interface* self) { |
| 92 | u32* cmd_buff = Service::GetCommandBuffer(); | 146 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 93 | u32 app_id = cmd_buff[1]; | 147 | u32 app_id = cmd_buff[1]; |
| 94 | u32 buffer_size = cmd_buff[2]; | 148 | u32 buffer_size = cmd_buff[2]; |
| 149 | |||
| 95 | cmd_buff[1] = 0; // No error | 150 | cmd_buff[1] = 0; // No error |
| 96 | cmd_buff[2] = 0; | 151 | cmd_buff[2] = 0; |
| 97 | cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type | 152 | cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type |
| 98 | cmd_buff[4] = 0; | 153 | cmd_buff[4] = 0x10; // Parameter buffer size (16) |
| 99 | cmd_buff[5] = 0; | 154 | cmd_buff[5] = 0; |
| 100 | cmd_buff[6] = 0; | 155 | cmd_buff[6] = 0; |
| 101 | cmd_buff[7] = 0; | 156 | cmd_buff[7] = 0; |
| 102 | cmd_buff[8] = 0; | 157 | |
| 103 | WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | 158 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); |
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * APT_U::AppletUtility service function | ||
| 163 | * Inputs: | ||
| 164 | * 1 : Unknown, but clearly used for something | ||
| 165 | * 2 : Buffer 1 size (purpose is unknown) | ||
| 166 | * 3 : Buffer 2 size (purpose is unknown) | ||
| 167 | * 5 : Buffer 1 address (purpose is unknown) | ||
| 168 | * 65 : Buffer 2 address (purpose is unknown) | ||
| 169 | * Outputs: | ||
| 170 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 171 | */ | ||
| 172 | void AppletUtility(Service::Interface* self) { | ||
| 173 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 174 | |||
| 175 | // These are from 3dbrew - I'm not really sure what they're used for. | ||
| 176 | u32 unk = cmd_buff[1]; | ||
| 177 | u32 buffer1_size = cmd_buff[2]; | ||
| 178 | u32 buffer2_size = cmd_buff[3]; | ||
| 179 | u32 buffer1_addr = cmd_buff[5]; | ||
| 180 | u32 buffer2_addr = cmd_buff[65]; | ||
| 181 | |||
| 182 | cmd_buff[1] = 0; // No error | ||
| 183 | |||
| 184 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, " | ||
| 185 | "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size, | ||
| 186 | buffer1_addr, buffer2_addr); | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * APT_U::GetSharedFont service function | ||
| 191 | * Outputs: | ||
| 192 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 193 | * 2 : Virtual address of where shared font will be loaded in memory | ||
| 194 | * 4 : Handle to shared font memory | ||
| 195 | */ | ||
| 196 | void GetSharedFont(Service::Interface* self) { | ||
| 197 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 198 | |||
| 199 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 200 | |||
| 201 | if (!shared_font.empty()) { | ||
| 202 | // TODO(bunnei): This function shouldn't copy the shared font every time it's called. | ||
| 203 | // Instead, it should probably map the shared font as RO memory. We don't currently have | ||
| 204 | // an easy way to do this, but the copy should be sufficient for now. | ||
| 205 | memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); | ||
| 206 | |||
| 207 | cmd_buff[0] = 0x00440082; | ||
| 208 | cmd_buff[1] = 0; // No error | ||
| 209 | cmd_buff[2] = SHARED_FONT_VADDR; | ||
| 210 | cmd_buff[4] = shared_font_mem; | ||
| 211 | } else { | ||
| 212 | cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware) | ||
| 213 | LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT); | ||
| 214 | } | ||
| 104 | } | 215 | } |
| 105 | 216 | ||
| 106 | const Interface::FunctionInfo FunctionTable[] = { | 217 | const Interface::FunctionInfo FunctionTable[] = { |
| @@ -171,14 +282,14 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 171 | {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, | 282 | {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, |
| 172 | {0x00420080, nullptr, "SleepSystem"}, | 283 | {0x00420080, nullptr, "SleepSystem"}, |
| 173 | {0x00430040, nullptr, "NotifyToWait"}, | 284 | {0x00430040, nullptr, "NotifyToWait"}, |
| 174 | {0x00440000, nullptr, "GetSharedFont"}, | 285 | {0x00440000, GetSharedFont, "GetSharedFont"}, |
| 175 | {0x00450040, nullptr, "GetWirelessRebootInfo"}, | 286 | {0x00450040, nullptr, "GetWirelessRebootInfo"}, |
| 176 | {0x00460104, nullptr, "Wrap"}, | 287 | {0x00460104, nullptr, "Wrap"}, |
| 177 | {0x00470104, nullptr, "Unwrap"}, | 288 | {0x00470104, nullptr, "Unwrap"}, |
| 178 | {0x00480100, nullptr, "GetProgramInfo"}, | 289 | {0x00480100, nullptr, "GetProgramInfo"}, |
| 179 | {0x00490180, nullptr, "Reboot"}, | 290 | {0x00490180, nullptr, "Reboot"}, |
| 180 | {0x004A0040, nullptr, "GetCaptureInfo"}, | 291 | {0x004A0040, nullptr, "GetCaptureInfo"}, |
| 181 | {0x004B00C2, nullptr, "AppletUtility"}, | 292 | {0x004B00C2, AppletUtility, "AppletUtility"}, |
| 182 | {0x004C0000, nullptr, "SetFatalErrDispMode"}, | 293 | {0x004C0000, nullptr, "SetFatalErrDispMode"}, |
| 183 | {0x004D0080, nullptr, "GetAppletProgramInfo"}, | 294 | {0x004D0080, nullptr, "GetAppletProgramInfo"}, |
| 184 | {0x004E0000, nullptr, "HardwareResetAsync"}, | 295 | {0x004E0000, nullptr, "HardwareResetAsync"}, |
| @@ -190,6 +301,32 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 190 | // Interface class | 301 | // Interface class |
| 191 | 302 | ||
| 192 | Interface::Interface() { | 303 | Interface::Interface() { |
| 304 | // Load the shared system font (if available). | ||
| 305 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | ||
| 306 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | ||
| 307 | // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||
| 308 | // "shared_font.bin" in the Citra "sysdata" directory. | ||
| 309 | |||
| 310 | shared_font.clear(); | ||
| 311 | std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||
| 312 | |||
| 313 | FileUtil::CreateFullPath(filepath); // Create path if not already created | ||
| 314 | FileUtil::IOFile file(filepath, "rb"); | ||
| 315 | |||
| 316 | if (file.IsOpen()) { | ||
| 317 | // Read shared font data | ||
| 318 | shared_font.resize(file.GetSize()); | ||
| 319 | file.ReadBytes(shared_font.data(), file.GetSize()); | ||
| 320 | |||
| 321 | // Create shared font memory object | ||
| 322 | shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem"); | ||
| 323 | } else { | ||
| 324 | LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); | ||
| 325 | shared_font_mem = 0; | ||
| 326 | } | ||
| 327 | |||
| 328 | lock_handle = 0; | ||
| 329 | |||
| 193 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 330 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 194 | } | 331 | } |
| 195 | 332 | ||
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h index 5af39e085..306730400 100644 --- a/src/core/hle/service/apt_u.h +++ b/src/core/hle/service/apt_u.h | |||
| @@ -13,8 +13,8 @@ namespace APT_U { | |||
| 13 | 13 | ||
| 14 | // Application and title launching service. These services handle signaling for home/power button as | 14 | // Application and title launching service. These services handle signaling for home/power button as |
| 15 | // well. Only one session for either APT service can be open at a time, normally processes close the | 15 | // well. Only one session for either APT service can be open at a time, normally processes close the |
| 16 | // service handle immediately once finished using the service. The commands for APT:U and APT:S are | 16 | // service handle immediately once finished using the service. The commands for APT:U and APT:S are |
| 17 | // exactly the same, however certain commands are only accessible with APT:S(NS module will call | 17 | // exactly the same, however certain commands are only accessible with APT:S(NS module will call |
| 18 | // svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. | 18 | // svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. |
| 19 | 19 | ||
| 20 | /// Interface to "APT:U" service | 20 | /// Interface to "APT:U" service |
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp new file mode 100644 index 000000000..b2ff4a756 --- /dev/null +++ b/src/core/hle/service/boss_u.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/boss_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace BOSS_U | ||
| 11 | |||
| 12 | namespace BOSS_U { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00020100, nullptr, "GetStorageInfo"}, | ||
| 16 | }; | ||
| 17 | |||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 19 | // Interface class | ||
| 20 | |||
| 21 | Interface::Interface() { | ||
| 22 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 23 | } | ||
| 24 | |||
| 25 | Interface::~Interface() { | ||
| 26 | } | ||
| 27 | |||
| 28 | } // namespace | ||
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h new file mode 100644 index 000000000..af39b8e65 --- /dev/null +++ b/src/core/hle/service/boss_u.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace BOSS_U | ||
| 11 | |||
| 12 | namespace BOSS_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const { | ||
| 23 | return "boss:U"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp new file mode 100644 index 000000000..25d903516 --- /dev/null +++ b/src/core/hle/service/cecd_u.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/cecd_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CECD_U | ||
| 11 | |||
| 12 | namespace CECD_U { | ||
| 13 | |||
| 14 | // Empty arrays are illegal -- commented out until an entry is added. | ||
| 15 | //const Interface::FunctionInfo FunctionTable[] = { }; | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // Interface class | ||
| 19 | |||
| 20 | Interface::Interface() { | ||
| 21 | //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 22 | } | ||
| 23 | |||
| 24 | } // namespace | ||
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h new file mode 100644 index 000000000..969e1ed1b --- /dev/null +++ b/src/core/hle/service/cecd_u.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CECD_U | ||
| 11 | |||
| 12 | namespace CECD_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "cecd:u"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp new file mode 100644 index 000000000..88d13d459 --- /dev/null +++ b/src/core/hle/service/cfg_i.cpp | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/cfg_i.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CFG_I | ||
| 11 | |||
| 12 | namespace CFG_I { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x04010082, nullptr, "GetConfigInfoBlk8"}, | ||
| 16 | {0x04020082, nullptr, "GetConfigInfoBlk4"}, | ||
| 17 | {0x04030000, nullptr, "UpdateConfigNANDSavegame"}, | ||
| 18 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, | ||
| 19 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, | ||
| 20 | {0x04060000, nullptr, "SecureInfoGetRegion"}, | ||
| 21 | {0x04070000, nullptr, "SecureInfoGetByte101"}, | ||
| 22 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, | ||
| 23 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, | ||
| 24 | {0x08010082, nullptr, "GetConfigInfoBlk8"}, | ||
| 25 | {0x08020082, nullptr, "GetConfigInfoBlk4"}, | ||
| 26 | {0x08030000, nullptr, "UpdateConfigNANDSavegame"}, | ||
| 27 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, | ||
| 28 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, | ||
| 29 | {0x08060000, nullptr, "FormatConfig"}, | ||
| 30 | {0x08070000, nullptr, "Unknown"}, | ||
| 31 | {0x08080000, nullptr, "UpdateConfigBlk1"}, | ||
| 32 | {0x08090000, nullptr, "UpdateConfigBlk2"}, | ||
| 33 | {0x080A0000, nullptr, "UpdateConfigBlk3"}, | ||
| 34 | {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"}, | ||
| 35 | {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"}, | ||
| 36 | {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"}, | ||
| 37 | {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"}, | ||
| 38 | {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"}, | ||
| 39 | {0x08100000, nullptr, "GetLocalFriendCodeSeed"}, | ||
| 40 | {0x08110084, nullptr, "SetSecureInfo"}, | ||
| 41 | {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"}, | ||
| 42 | {0x08130000, nullptr, "VerifySigSecureInfo"}, | ||
| 43 | {0x08140042, nullptr, "SecureInfoGetData"}, | ||
| 44 | {0x08150042, nullptr, "SecureInfoGetSignature"}, | ||
| 45 | {0x08160000, nullptr, "SecureInfoGetRegion"}, | ||
| 46 | {0x08170000, nullptr, "SecureInfoGetByte101"}, | ||
| 47 | {0x08180042, nullptr, "SecureInfoGetSerialNo"}, | ||
| 48 | }; | ||
| 49 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 50 | // Interface class | ||
| 51 | |||
| 52 | Interface::Interface() { | ||
| 53 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 54 | } | ||
| 55 | |||
| 56 | Interface::~Interface() { | ||
| 57 | } | ||
| 58 | |||
| 59 | } // namespace | ||
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg_i.h new file mode 100644 index 000000000..fe343c968 --- /dev/null +++ b/src/core/hle/service/cfg_i.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CFG_I | ||
| 11 | |||
| 12 | namespace CFG_I { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "cfg:i"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp index 822b0e2b8..2e9d7bf21 100644 --- a/src/core/hle/service/cfg_u.cpp +++ b/src/core/hle/service/cfg_u.cpp | |||
| @@ -11,6 +11,94 @@ | |||
| 11 | 11 | ||
| 12 | namespace CFG_U { | 12 | namespace CFG_U { |
| 13 | 13 | ||
| 14 | // TODO(Link Mauve): use a constexpr once MSVC starts supporting it. | ||
| 15 | #define C(code) ((code)[0] | ((code)[1] << 8)) | ||
| 16 | |||
| 17 | static const std::array<u16, 187> country_codes = { | ||
| 18 | 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7 | ||
| 19 | C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15 | ||
| 20 | C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23 | ||
| 21 | C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31 | ||
| 22 | C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39 | ||
| 23 | C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47 | ||
| 24 | C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55 | ||
| 25 | 0, 0, 0, 0, 0, 0, 0, 0, // 56-63 | ||
| 26 | C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71 | ||
| 27 | C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79 | ||
| 28 | C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87 | ||
| 29 | C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95 | ||
| 30 | C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103 | ||
| 31 | C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111 | ||
| 32 | C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119 | ||
| 33 | C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127 | ||
| 34 | C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135 | ||
| 35 | C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143 | ||
| 36 | C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151 | ||
| 37 | C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159 | ||
| 38 | C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167 | ||
| 39 | C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175 | ||
| 40 | C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183 | ||
| 41 | C("SM"), C("VA"), C("BM") // 184-186 | ||
| 42 | }; | ||
| 43 | |||
| 44 | #undef C | ||
| 45 | |||
| 46 | /** | ||
| 47 | * CFG_User::GetCountryCodeString service function | ||
| 48 | * Inputs: | ||
| 49 | * 1 : Country Code ID | ||
| 50 | * Outputs: | ||
| 51 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 52 | * 2 : Country's 2-char string | ||
| 53 | */ | ||
| 54 | static void GetCountryCodeString(Service::Interface* self) { | ||
| 55 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 56 | u32 country_code_id = cmd_buffer[1]; | ||
| 57 | |||
| 58 | if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) { | ||
| 59 | LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id); | ||
| 60 | cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | cmd_buffer[1] = 0; | ||
| 65 | cmd_buffer[2] = country_codes[country_code_id]; | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * CFG_User::GetCountryCodeID service function | ||
| 70 | * Inputs: | ||
| 71 | * 1 : Country Code 2-char string | ||
| 72 | * Outputs: | ||
| 73 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 74 | * 2 : Country Code ID | ||
| 75 | */ | ||
| 76 | static void GetCountryCodeID(Service::Interface* self) { | ||
| 77 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 78 | u16 country_code = cmd_buffer[1]; | ||
| 79 | u16 country_code_id = 0; | ||
| 80 | |||
| 81 | // The following algorithm will fail if the first country code isn't 0. | ||
| 82 | _dbg_assert_(Service_CFG, country_codes[0] == 0); | ||
| 83 | |||
| 84 | for (size_t id = 0; id < country_codes.size(); ++id) { | ||
| 85 | if (country_codes[id] == country_code) { | ||
| 86 | country_code_id = id; | ||
| 87 | break; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | if (0 == country_code_id) { | ||
| 92 | LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8); | ||
| 93 | cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; | ||
| 94 | cmd_buffer[2] = 0xFFFF; | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | |||
| 98 | cmd_buffer[1] = 0; | ||
| 99 | cmd_buffer[2] = country_code_id; | ||
| 100 | } | ||
| 101 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 102 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010082, nullptr, "GetConfigInfoBlk2"}, | 103 | {0x00010082, nullptr, "GetConfigInfoBlk2"}, |
| 16 | {0x00020000, nullptr, "SecureInfoGetRegion"}, | 104 | {0x00020000, nullptr, "SecureInfoGetRegion"}, |
| @@ -20,8 +108,8 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 20 | {0x00060000, nullptr, "GetModelNintendo2DS"}, | 108 | {0x00060000, nullptr, "GetModelNintendo2DS"}, |
| 21 | {0x00070040, nullptr, "unknown"}, | 109 | {0x00070040, nullptr, "unknown"}, |
| 22 | {0x00080080, nullptr, "unknown"}, | 110 | {0x00080080, nullptr, "unknown"}, |
| 23 | {0x00090080, nullptr, "GetCountryCodeString"}, | 111 | {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, |
| 24 | {0x000A0040, nullptr, "GetCountryCodeID"}, | 112 | {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, |
| 25 | }; | 113 | }; |
| 26 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 114 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 27 | // Interface class | 115 | // Interface class |
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg_u.h index 7525bd7c6..8075d19a8 100644 --- a/src/core/hle/service/cfg_u.h +++ b/src/core/hle/service/cfg_u.h | |||
| @@ -19,7 +19,7 @@ public: | |||
| 19 | * Gets the string port name used by CTROS for the service | 19 | * Gets the string port name used by CTROS for the service |
| 20 | * @return Port name of service | 20 | * @return Port name of service |
| 21 | */ | 21 | */ |
| 22 | std::string GetPortName() const { | 22 | std::string GetPortName() const override { |
| 23 | return "cfg:u"; | 23 | return "cfg:u"; |
| 24 | } | 24 | } |
| 25 | }; | 25 | }; |
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp new file mode 100644 index 000000000..6e59a9bf3 --- /dev/null +++ b/src/core/hle/service/csnd_snd.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/csnd_snd.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CSND_SND | ||
| 11 | |||
| 12 | namespace CSND_SND { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010140, nullptr, "Initialize"}, | ||
| 16 | {0x00020000, nullptr, "Shutdown"}, | ||
| 17 | {0x00030040, nullptr, "Unknown"}, | ||
| 18 | {0x00040080, nullptr, "Unknown"}, | ||
| 19 | {0x00050000, nullptr, "Unknown"}, | ||
| 20 | {0x00060000, nullptr, "Unknown"}, | ||
| 21 | {0x00070000, nullptr, "Unknown"}, | ||
| 22 | {0x00080040, nullptr, "Unknown"}, | ||
| 23 | {0x00090082, nullptr, "FlushDCache"}, | ||
| 24 | {0x000A0082, nullptr, "StoreDCache"}, | ||
| 25 | {0x000B0082, nullptr, "InvalidateDCache"}, | ||
| 26 | {0x000C0000, nullptr, "Unknown"}, | ||
| 27 | }; | ||
| 28 | |||
| 29 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 30 | // Interface class | ||
| 31 | |||
| 32 | Interface::Interface() { | ||
| 33 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 34 | } | ||
| 35 | |||
| 36 | Interface::~Interface() { | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace | ||
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h new file mode 100644 index 000000000..31cc85b07 --- /dev/null +++ b/src/core/hle/service/csnd_snd.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CSND_SND | ||
| 11 | |||
| 12 | namespace CSND_SND { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "csnd:SND"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 9e84ac938..bd82063c6 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| 6 | #include "core/hle/hle.h" | 6 | #include "core/hle/hle.h" |
| 7 | #include "core/hle/kernel/event.h" | ||
| 7 | #include "core/hle/service/dsp_dsp.h" | 8 | #include "core/hle/service/dsp_dsp.h" |
| 8 | 9 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -11,38 +12,181 @@ | |||
| 11 | 12 | ||
| 12 | namespace DSP_DSP { | 13 | namespace DSP_DSP { |
| 13 | 14 | ||
| 15 | static u32 read_pipe_count; | ||
| 16 | static Handle semaphore_event; | ||
| 17 | static Handle interrupt_event; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * DSP_DSP::ConvertProcessAddressFromDspDram service function | ||
| 21 | * Inputs: | ||
| 22 | * 1 : Address | ||
| 23 | * Outputs: | ||
| 24 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 25 | * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address) | ||
| 26 | */ | ||
| 27 | void ConvertProcessAddressFromDspDram(Service::Interface* self) { | ||
| 28 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 29 | |||
| 30 | u32 addr = cmd_buff[1]; | ||
| 31 | |||
| 32 | cmd_buff[1] = 0; // No error | ||
| 33 | cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000); | ||
| 34 | |||
| 35 | LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr); | ||
| 36 | } | ||
| 37 | |||
| 38 | /** | ||
| 39 | * DSP_DSP::LoadComponent service function | ||
| 40 | * Inputs: | ||
| 41 | * 1 : Size | ||
| 42 | * 2 : Unknown (observed only half word used) | ||
| 43 | * 3 : Unknown (observed only half word used) | ||
| 44 | * 4 : (size << 4) | 0xA | ||
| 45 | * 5 : Buffer address | ||
| 46 | * Outputs: | ||
| 47 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 48 | * 2 : Component loaded, 0 on not loaded, 1 on loaded | ||
| 49 | */ | ||
| 50 | void LoadComponent(Service::Interface* self) { | ||
| 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 52 | |||
| 53 | cmd_buff[1] = 0; // No error | ||
| 54 | cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware | ||
| 55 | |||
| 56 | // TODO(bunnei): Implement real DSP firmware loading | ||
| 57 | |||
| 58 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * DSP_DSP::GetSemaphoreEventHandle service function | ||
| 63 | * Outputs: | ||
| 64 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 65 | * 3 : Semaphore event handle | ||
| 66 | */ | ||
| 67 | void GetSemaphoreEventHandle(Service::Interface* self) { | ||
| 68 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 69 | |||
| 70 | cmd_buff[1] = 0; // No error | ||
| 71 | cmd_buff[3] = semaphore_event; // Event handle | ||
| 72 | |||
| 73 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * DSP_DSP::RegisterInterruptEvents service function | ||
| 78 | * Inputs: | ||
| 79 | * 1 : Parameter 0 (purpose unknown) | ||
| 80 | * 2 : Parameter 1 (purpose unknown) | ||
| 81 | * 4 : Interrupt event handle | ||
| 82 | * Outputs: | ||
| 83 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 84 | */ | ||
| 85 | void RegisterInterruptEvents(Service::Interface* self) { | ||
| 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 87 | |||
| 88 | interrupt_event = static_cast<Handle>(cmd_buff[4]); | ||
| 89 | |||
| 90 | cmd_buff[1] = 0; // No error | ||
| 91 | |||
| 92 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * DSP_DSP::WriteReg0x10 service function | ||
| 97 | * Inputs: | ||
| 98 | * 1 : Unknown (observed only half word used) | ||
| 99 | * Outputs: | ||
| 100 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 101 | */ | ||
| 102 | void WriteReg0x10(Service::Interface* self) { | ||
| 103 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 104 | |||
| 105 | Kernel::SignalEvent(interrupt_event); | ||
| 106 | |||
| 107 | cmd_buff[1] = 0; // No error | ||
| 108 | |||
| 109 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * DSP_DSP::ReadPipeIfPossible service function | ||
| 114 | * Inputs: | ||
| 115 | * 1 : Unknown | ||
| 116 | * 2 : Unknown | ||
| 117 | * 3 : Size in bytes of read (observed only lower half word used) | ||
| 118 | * 0x41 : Virtual address to read from DSP pipe to in memory | ||
| 119 | * Outputs: | ||
| 120 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 121 | * 2 : Number of bytes read from pipe | ||
| 122 | */ | ||
| 123 | void ReadPipeIfPossible(Service::Interface* self) { | ||
| 124 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 125 | |||
| 126 | u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size | ||
| 127 | VAddr addr = cmd_buff[0x41]; | ||
| 128 | |||
| 129 | // Canned DSP responses that games expect. These were taken from HW by 3dmoo team. | ||
| 130 | // TODO: Remove this hack :) | ||
| 131 | static const std::array<u16, 16> canned_read_pipe = { | ||
| 132 | 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540, | ||
| 133 | 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58 | ||
| 134 | }; | ||
| 135 | |||
| 136 | u32 initial_size = read_pipe_count; | ||
| 137 | |||
| 138 | for (unsigned offset = 0; offset < size; offset += sizeof(u16)) { | ||
| 139 | if (read_pipe_count < canned_read_pipe.size()) { | ||
| 140 | Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]); | ||
| 141 | read_pipe_count++; | ||
| 142 | } else { | ||
| 143 | LOG_ERROR(Service_DSP, "canned read pipe log exceeded!"); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | cmd_buff[1] = 0; // No error | ||
| 149 | cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); | ||
| 150 | |||
| 151 | LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); | ||
| 152 | } | ||
| 153 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 154 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010040, nullptr, "RecvData"}, | 155 | {0x00010040, nullptr, "RecvData"}, |
| 16 | {0x00020040, nullptr, "RecvDataIsReady"}, | 156 | {0x00020040, nullptr, "RecvDataIsReady"}, |
| 17 | {0x00030080, nullptr, "SendData"}, | 157 | {0x00030080, nullptr, "SendData"}, |
| 18 | {0x00040040, nullptr, "SendDataIsEmpty"}, | 158 | {0x00040040, nullptr, "SendDataIsEmpty"}, |
| 19 | {0x00070040, nullptr, "WriteReg0x10"}, | 159 | {0x00070040, WriteReg0x10, "WriteReg0x10"}, |
| 20 | {0x00080000, nullptr, "GetSemaphore"}, | 160 | {0x00080000, nullptr, "GetSemaphore"}, |
| 21 | {0x00090040, nullptr, "ClearSemaphore"}, | 161 | {0x00090040, nullptr, "ClearSemaphore"}, |
| 22 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, | 162 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, |
| 23 | {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"}, | 163 | {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, |
| 24 | {0x000D0082, nullptr, "WriteProcessPipe"}, | 164 | {0x000D0082, nullptr, "WriteProcessPipe"}, |
| 25 | {0x001000C0, nullptr, "ReadPipeIfPossible"}, | 165 | {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, |
| 26 | {0x001100C2, nullptr, "LoadComponent"}, | 166 | {0x001100C2, LoadComponent, "LoadComponent"}, |
| 27 | {0x00120000, nullptr, "UnloadComponent"}, | 167 | {0x00120000, nullptr, "UnloadComponent"}, |
| 28 | {0x00130082, nullptr, "FlushDataCache"}, | 168 | {0x00130082, nullptr, "FlushDataCache"}, |
| 29 | {0x00140082, nullptr, "InvalidateDCache "}, | 169 | {0x00140082, nullptr, "InvalidateDCache"}, |
| 30 | {0x00150082, nullptr, "RegisterInterruptEvents"}, | 170 | {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"}, |
| 31 | {0x00160000, nullptr, "GetSemaphoreEventHandle"}, | 171 | {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"}, |
| 32 | {0x00170040, nullptr, "SetSemaphoreMask"}, | 172 | {0x00170040, nullptr, "SetSemaphoreMask"}, |
| 33 | {0x00180040, nullptr, "GetPhysicalAddress"}, | 173 | {0x00180040, nullptr, "GetPhysicalAddress"}, |
| 34 | {0x00190040, nullptr, "GetVirtualAddress" }, | 174 | {0x00190040, nullptr, "GetVirtualAddress"}, |
| 35 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, | 175 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, |
| 36 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, | 176 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, |
| 37 | {0x001C0082, nullptr, "SetIirFilterEQ"}, | 177 | {0x001C0082, nullptr, "SetIirFilterEQ"}, |
| 38 | {0x001F0000, nullptr, "GetHeadphoneStatus"}, | 178 | {0x001F0000, nullptr, "GetHeadphoneStatus"}, |
| 39 | {0x00210000, nullptr, "GetIsDspOccupied"}, | 179 | {0x00210000, nullptr, "GetIsDspOccupied"}, |
| 40 | }; | 180 | }; |
| 41 | 181 | ||
| 42 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 182 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 43 | // Interface class | 183 | // Interface class |
| 44 | 184 | ||
| 45 | Interface::Interface() { | 185 | Interface::Interface() { |
| 186 | semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event"); | ||
| 187 | interrupt_event = 0; | ||
| 188 | read_pipe_count = 0; | ||
| 189 | |||
| 46 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 190 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 47 | } | 191 | } |
| 48 | 192 | ||
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index c439ed266..9431b62f6 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h | |||
| @@ -19,8 +19,8 @@ public: | |||
| 19 | * Gets the string port name used by CTROS for the service | 19 | * Gets the string port name used by CTROS for the service |
| 20 | * @return Port name of service | 20 | * @return Port name of service |
| 21 | */ | 21 | */ |
| 22 | std::string GetPortName() const { | 22 | std::string GetPortName() const override { |
| 23 | return "dsp:DSP"; | 23 | return "dsp::DSP"; |
| 24 | } | 24 | } |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 917b2f8ca..785c351e9 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp | |||
| @@ -20,8 +20,8 @@ namespace ERR_F { | |||
| 20 | Interface::Interface() { | 20 | Interface::Interface() { |
| 21 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 21 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | Interface::~Interface() { | 24 | Interface::~Interface() { |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | } // namespace | 27 | } // namespace |
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h index 5da663267..6d7141c1b 100644 --- a/src/core/hle/service/err_f.h +++ b/src/core/hle/service/err_f.h | |||
| @@ -19,9 +19,9 @@ namespace ERR_F { | |||
| 19 | * Gets the string port name used by CTROS for the service | 19 | * Gets the string port name used by CTROS for the service |
| 20 | * @return Port name of service | 20 | * @return Port name of service |
| 21 | */ | 21 | */ |
| 22 | std::string GetPortName() const { | 22 | std::string GetPortName() const override { |
| 23 | return "err:f"; | 23 | return "err:f"; |
| 24 | } | 24 | } |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | } // namespace | 27 | } // namespace |
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp new file mode 100644 index 000000000..58023e536 --- /dev/null +++ b/src/core/hle/service/frd_u.cpp | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/frd_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace FRD_U | ||
| 11 | |||
| 12 | namespace FRD_U { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00050000, nullptr, "GetFriendKey"}, | ||
| 16 | {0x00080000, nullptr, "GetMyPresence"}, | ||
| 17 | {0x00100040, nullptr, "GetPassword"}, | ||
| 18 | {0x00190042, nullptr, "GetFriendFavoriteGame"}, | ||
| 19 | {0x001A00C4, nullptr, "GetFriendInfo"}, | ||
| 20 | {0x001B0080, nullptr, "IsOnFriendList"}, | ||
| 21 | {0x001C0042, nullptr, "DecodeLocalFriendCode"}, | ||
| 22 | {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, | ||
| 23 | {0x00320042, nullptr, "SetClientSdkVersion"} | ||
| 24 | }; | ||
| 25 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 26 | // Interface class | ||
| 27 | |||
| 28 | Interface::Interface() { | ||
| 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 30 | } | ||
| 31 | |||
| 32 | Interface::~Interface() { | ||
| 33 | } | ||
| 34 | |||
| 35 | } // namespace | ||
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h new file mode 100644 index 000000000..4020c6664 --- /dev/null +++ b/src/core/hle/service/frd_u.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace FRD_U | ||
| 11 | |||
| 12 | namespace FRD_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "frd:u"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp new file mode 100644 index 000000000..9c3834733 --- /dev/null +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -0,0 +1,431 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <unordered_map> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/math_util.h" | ||
| 11 | |||
| 12 | #include "core/file_sys/archive_savedata.h" | ||
| 13 | #include "core/file_sys/archive_backend.h" | ||
| 14 | #include "core/file_sys/archive_sdmc.h" | ||
| 15 | #include "core/file_sys/directory_backend.h" | ||
| 16 | #include "core/hle/service/fs/archive.h" | ||
| 17 | #include "core/hle/kernel/session.h" | ||
| 18 | #include "core/hle/result.h" | ||
| 19 | |||
| 20 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. | ||
| 21 | // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 | ||
| 22 | namespace std { | ||
| 23 | template <> | ||
| 24 | struct hash<Service::FS::ArchiveIdCode> { | ||
| 25 | typedef Service::FS::ArchiveIdCode argument_type; | ||
| 26 | typedef std::size_t result_type; | ||
| 27 | |||
| 28 | result_type operator()(const argument_type& id_code) const { | ||
| 29 | typedef std::underlying_type<argument_type>::type Type; | ||
| 30 | return std::hash<Type>()(static_cast<Type>(id_code)); | ||
| 31 | } | ||
| 32 | }; | ||
| 33 | } | ||
| 34 | |||
| 35 | namespace Service { | ||
| 36 | namespace FS { | ||
| 37 | |||
| 38 | // Command to access archive file | ||
| 39 | enum class FileCommand : u32 { | ||
| 40 | Dummy1 = 0x000100C6, | ||
| 41 | Control = 0x040100C4, | ||
| 42 | OpenSubFile = 0x08010100, | ||
| 43 | Read = 0x080200C2, | ||
| 44 | Write = 0x08030102, | ||
| 45 | GetSize = 0x08040000, | ||
| 46 | SetSize = 0x08050080, | ||
| 47 | GetAttributes = 0x08060000, | ||
| 48 | SetAttributes = 0x08070040, | ||
| 49 | Close = 0x08080000, | ||
| 50 | Flush = 0x08090000, | ||
| 51 | }; | ||
| 52 | |||
| 53 | // Command to access directory | ||
| 54 | enum class DirectoryCommand : u32 { | ||
| 55 | Dummy1 = 0x000100C6, | ||
| 56 | Control = 0x040100C4, | ||
| 57 | Read = 0x08010042, | ||
| 58 | Close = 0x08020000, | ||
| 59 | }; | ||
| 60 | |||
| 61 | class Archive { | ||
| 62 | public: | ||
| 63 | Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) | ||
| 64 | : backend(std::move(backend)), id_code(id_code) { | ||
| 65 | } | ||
| 66 | |||
| 67 | std::string GetName() const { return "Archive: " + backend->GetName(); } | ||
| 68 | |||
| 69 | ArchiveIdCode id_code; ///< Id code of the archive | ||
| 70 | std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface | ||
| 71 | }; | ||
| 72 | |||
| 73 | class File : public Kernel::Session { | ||
| 74 | public: | ||
| 75 | File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) | ||
| 76 | : backend(std::move(backend)), path(path) { | ||
| 77 | } | ||
| 78 | |||
| 79 | std::string GetName() const override { return "Path: " + path.DebugStr(); } | ||
| 80 | |||
| 81 | FileSys::Path path; ///< Path of the file | ||
| 82 | std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface | ||
| 83 | |||
| 84 | ResultVal<bool> SyncRequest() override { | ||
| 85 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 86 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 87 | switch (cmd) { | ||
| 88 | |||
| 89 | // Read from file... | ||
| 90 | case FileCommand::Read: | ||
| 91 | { | ||
| 92 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 93 | u32 length = cmd_buff[3]; | ||
| 94 | u32 address = cmd_buff[5]; | ||
| 95 | LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 96 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 97 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 98 | break; | ||
| 99 | } | ||
| 100 | |||
| 101 | // Write to file... | ||
| 102 | case FileCommand::Write: | ||
| 103 | { | ||
| 104 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 105 | u32 length = cmd_buff[3]; | ||
| 106 | u32 flush = cmd_buff[4]; | ||
| 107 | u32 address = cmd_buff[6]; | ||
| 108 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 109 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 110 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | |||
| 114 | case FileCommand::GetSize: | ||
| 115 | { | ||
| 116 | LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 117 | u64 size = backend->GetSize(); | ||
| 118 | cmd_buff[2] = (u32)size; | ||
| 119 | cmd_buff[3] = size >> 32; | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | |||
| 123 | case FileCommand::SetSize: | ||
| 124 | { | ||
| 125 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 126 | LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", | ||
| 127 | GetTypeName().c_str(), GetName().c_str(), size); | ||
| 128 | backend->SetSize(size); | ||
| 129 | break; | ||
| 130 | } | ||
| 131 | |||
| 132 | case FileCommand::Close: | ||
| 133 | { | ||
| 134 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 135 | Kernel::g_object_pool.Destroy<File>(GetHandle()); | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | |||
| 139 | case FileCommand::Flush: | ||
| 140 | { | ||
| 141 | LOG_TRACE(Service_FS, "Flush"); | ||
| 142 | backend->Flush(); | ||
| 143 | break; | ||
| 144 | } | ||
| 145 | |||
| 146 | // Unknown command... | ||
| 147 | default: | ||
| 148 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 149 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 150 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 151 | return error; | ||
| 152 | } | ||
| 153 | cmd_buff[1] = 0; // No error | ||
| 154 | return MakeResult<bool>(false); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | |||
| 158 | class Directory : public Kernel::Session { | ||
| 159 | public: | ||
| 160 | Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) | ||
| 161 | : backend(std::move(backend)), path(path) { | ||
| 162 | } | ||
| 163 | |||
| 164 | std::string GetName() const override { return "Directory: " + path.DebugStr(); } | ||
| 165 | |||
| 166 | FileSys::Path path; ///< Path of the directory | ||
| 167 | std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface | ||
| 168 | |||
| 169 | ResultVal<bool> SyncRequest() override { | ||
| 170 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 171 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 172 | switch (cmd) { | ||
| 173 | |||
| 174 | // Read from directory... | ||
| 175 | case DirectoryCommand::Read: | ||
| 176 | { | ||
| 177 | u32 count = cmd_buff[1]; | ||
| 178 | u32 address = cmd_buff[3]; | ||
| 179 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 180 | LOG_TRACE(Service_FS, "Read %s %s: count=%d", | ||
| 181 | GetTypeName().c_str(), GetName().c_str(), count); | ||
| 182 | |||
| 183 | // Number of entries actually read | ||
| 184 | cmd_buff[2] = backend->Read(count, entries); | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | |||
| 188 | case DirectoryCommand::Close: | ||
| 189 | { | ||
| 190 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 191 | Kernel::g_object_pool.Destroy<Directory>(GetHandle()); | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | |||
| 195 | // Unknown command... | ||
| 196 | default: | ||
| 197 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 198 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 199 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 200 | return MakeResult<bool>(false); | ||
| 201 | } | ||
| 202 | cmd_buff[1] = 0; // No error | ||
| 203 | return MakeResult<bool>(false); | ||
| 204 | } | ||
| 205 | }; | ||
| 206 | |||
| 207 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Map of registered archives, identified by id code. Once an archive is registered here, it is | ||
| 211 | * never removed until the FS service is shut down. | ||
| 212 | */ | ||
| 213 | static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map; | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Map of active archive handles. Values are pointers to the archives in `idcode_map`. | ||
| 217 | */ | ||
| 218 | static std::unordered_map<ArchiveHandle, Archive*> handle_map; | ||
| 219 | static ArchiveHandle next_handle; | ||
| 220 | |||
| 221 | static Archive* GetArchive(ArchiveHandle handle) { | ||
| 222 | auto itr = handle_map.find(handle); | ||
| 223 | return (itr == handle_map.end()) ? nullptr : itr->second; | ||
| 224 | } | ||
| 225 | |||
| 226 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) { | ||
| 227 | LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code); | ||
| 228 | |||
| 229 | auto itr = id_code_map.find(id_code); | ||
| 230 | if (itr == id_code_map.end()) { | ||
| 231 | if (id_code == ArchiveIdCode::SaveData) { | ||
| 232 | // When a SaveData archive is created for the first time, it is not yet formatted | ||
| 233 | // and the save file/directory structure expected by the game has not yet been initialized. | ||
| 234 | // Returning the NotFormatted error code will signal the game to provision the SaveData archive | ||
| 235 | // with the files and folders that it expects. | ||
| 236 | // The FormatSaveData service call will create the SaveData archive when it is called. | ||
| 237 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||
| 238 | ErrorSummary::InvalidState, ErrorLevel::Status); | ||
| 239 | } | ||
| 240 | // TODO: Verify error against hardware | ||
| 241 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 242 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 243 | } | ||
| 244 | |||
| 245 | // This should never even happen in the first place with 64-bit handles, | ||
| 246 | while (handle_map.count(next_handle) != 0) { | ||
| 247 | ++next_handle; | ||
| 248 | } | ||
| 249 | handle_map.emplace(next_handle, itr->second.get()); | ||
| 250 | return MakeResult<ArchiveHandle>(next_handle++); | ||
| 251 | } | ||
| 252 | |||
| 253 | ResultCode CloseArchive(ArchiveHandle handle) { | ||
| 254 | if (handle_map.erase(handle) == 0) | ||
| 255 | return InvalidHandle(ErrorModule::FS); | ||
| 256 | else | ||
| 257 | return RESULT_SUCCESS; | ||
| 258 | } | ||
| 259 | |||
| 260 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in | ||
| 261 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 | ||
| 262 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { | ||
| 263 | auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code)); | ||
| 264 | |||
| 265 | bool inserted = result.second; | ||
| 266 | _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); | ||
| 267 | |||
| 268 | auto& archive = result.first->second; | ||
| 269 | LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code); | ||
| 270 | return RESULT_SUCCESS; | ||
| 271 | } | ||
| 272 | |||
| 273 | ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { | ||
| 274 | Archive* archive = GetArchive(archive_handle); | ||
| 275 | if (archive == nullptr) | ||
| 276 | return InvalidHandle(ErrorModule::FS); | ||
| 277 | |||
| 278 | std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); | ||
| 279 | if (backend == nullptr) { | ||
| 280 | return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||
| 281 | ErrorSummary::NotFound, ErrorLevel::Status); | ||
| 282 | } | ||
| 283 | |||
| 284 | auto file = std::make_unique<File>(std::move(backend), path); | ||
| 285 | Handle handle = Kernel::g_object_pool.Create(file.release()); | ||
| 286 | return MakeResult<Handle>(handle); | ||
| 287 | } | ||
| 288 | |||
| 289 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 290 | Archive* archive = GetArchive(archive_handle); | ||
| 291 | if (archive == nullptr) | ||
| 292 | return InvalidHandle(ErrorModule::FS); | ||
| 293 | |||
| 294 | if (archive->backend->DeleteFile(path)) | ||
| 295 | return RESULT_SUCCESS; | ||
| 296 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 297 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 298 | } | ||
| 299 | |||
| 300 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 301 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 302 | Archive* src_archive = GetArchive(src_archive_handle); | ||
| 303 | Archive* dest_archive = GetArchive(dest_archive_handle); | ||
| 304 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 305 | return InvalidHandle(ErrorModule::FS); | ||
| 306 | |||
| 307 | if (src_archive == dest_archive) { | ||
| 308 | if (src_archive->backend->RenameFile(src_path, dest_path)) | ||
| 309 | return RESULT_SUCCESS; | ||
| 310 | } else { | ||
| 311 | // TODO: Implement renaming across archives | ||
| 312 | return UnimplementedFunction(ErrorModule::FS); | ||
| 313 | } | ||
| 314 | |||
| 315 | // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't | ||
| 316 | // exist or similar. Verify. | ||
| 317 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 318 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 319 | } | ||
| 320 | |||
| 321 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 322 | Archive* archive = GetArchive(archive_handle); | ||
| 323 | if (archive == nullptr) | ||
| 324 | return InvalidHandle(ErrorModule::FS); | ||
| 325 | |||
| 326 | if (archive->backend->DeleteDirectory(path)) | ||
| 327 | return RESULT_SUCCESS; | ||
| 328 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 329 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 330 | } | ||
| 331 | |||
| 332 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 333 | Archive* archive = GetArchive(archive_handle); | ||
| 334 | if (archive == nullptr) | ||
| 335 | return InvalidHandle(ErrorModule::FS); | ||
| 336 | |||
| 337 | if (archive->backend->CreateDirectory(path)) | ||
| 338 | return RESULT_SUCCESS; | ||
| 339 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 340 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 341 | } | ||
| 342 | |||
| 343 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 344 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 345 | Archive* src_archive = GetArchive(src_archive_handle); | ||
| 346 | Archive* dest_archive = GetArchive(dest_archive_handle); | ||
| 347 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 348 | return InvalidHandle(ErrorModule::FS); | ||
| 349 | |||
| 350 | if (src_archive == dest_archive) { | ||
| 351 | if (src_archive->backend->RenameDirectory(src_path, dest_path)) | ||
| 352 | return RESULT_SUCCESS; | ||
| 353 | } else { | ||
| 354 | // TODO: Implement renaming across archives | ||
| 355 | return UnimplementedFunction(ErrorModule::FS); | ||
| 356 | } | ||
| 357 | |||
| 358 | // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't | ||
| 359 | // exist or similar. Verify. | ||
| 360 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 361 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 362 | } | ||
| 363 | |||
| 364 | /** | ||
| 365 | * Open a Directory from an Archive | ||
| 366 | * @param archive_handle Handle to an open Archive object | ||
| 367 | * @param path Path to the Directory inside of the Archive | ||
| 368 | * @return Opened Directory object | ||
| 369 | */ | ||
| 370 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 371 | Archive* archive = GetArchive(archive_handle); | ||
| 372 | if (archive == nullptr) | ||
| 373 | return InvalidHandle(ErrorModule::FS); | ||
| 374 | |||
| 375 | std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path); | ||
| 376 | if (backend == nullptr) { | ||
| 377 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 378 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 379 | } | ||
| 380 | |||
| 381 | auto directory = std::make_unique<Directory>(std::move(backend), path); | ||
| 382 | Handle handle = Kernel::g_object_pool.Create(directory.release()); | ||
| 383 | return MakeResult<Handle>(handle); | ||
| 384 | } | ||
| 385 | |||
| 386 | ResultCode FormatSaveData() { | ||
| 387 | // TODO(Subv): Actually wipe the savedata folder after creating or opening it | ||
| 388 | |||
| 389 | // Do not create the archive again if it already exists | ||
| 390 | if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end()) | ||
| 391 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code | ||
| 392 | |||
| 393 | // Create the SaveData archive | ||
| 394 | std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX); | ||
| 395 | auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory, | ||
| 396 | Kernel::g_program_id); | ||
| 397 | |||
| 398 | if (savedata_archive->Initialize()) { | ||
| 399 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | ||
| 400 | return RESULT_SUCCESS; | ||
| 401 | } else { | ||
| 402 | LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s", | ||
| 403 | savedata_archive->GetMountPoint().c_str()); | ||
| 404 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Initialize archives | ||
| 409 | void ArchiveInit() { | ||
| 410 | next_handle = 1; | ||
| 411 | |||
| 412 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 413 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 414 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 415 | |||
| 416 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 417 | auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | ||
| 418 | if (sdmc_archive->Initialize()) | ||
| 419 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | ||
| 420 | else | ||
| 421 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Shutdown archives | ||
| 425 | void ArchiveShutdown() { | ||
| 426 | handle_map.clear(); | ||
| 427 | id_code_map.clear(); | ||
| 428 | } | ||
| 429 | |||
| 430 | } // namespace FS | ||
| 431 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h new file mode 100644 index 000000000..a128276b6 --- /dev/null +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive_backend.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace Service { | ||
| 14 | namespace FS { | ||
| 15 | |||
| 16 | /// Supported archive types | ||
| 17 | enum class ArchiveIdCode : u32 { | ||
| 18 | RomFS = 0x00000003, | ||
| 19 | SaveData = 0x00000004, | ||
| 20 | ExtSaveData = 0x00000006, | ||
| 21 | SharedExtSaveData = 0x00000007, | ||
| 22 | SystemSaveData = 0x00000008, | ||
| 23 | SDMC = 0x00000009, | ||
| 24 | SDMCWriteOnly = 0x0000000A, | ||
| 25 | }; | ||
| 26 | |||
| 27 | typedef u64 ArchiveHandle; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Opens an archive | ||
| 31 | * @param id_code IdCode of the archive to open | ||
| 32 | * @return Handle to the opened archive | ||
| 33 | */ | ||
| 34 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code); | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Closes an archive | ||
| 38 | * @param id_code IdCode of the archive to open | ||
| 39 | */ | ||
| 40 | ResultCode CloseArchive(ArchiveHandle handle); | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Creates an Archive | ||
| 44 | * @param backend File system backend interface to the archive | ||
| 45 | * @param id_code Id code used to access this type of archive | ||
| 46 | */ | ||
| 47 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Open a File from an Archive | ||
| 51 | * @param archive_handle Handle to an open Archive object | ||
| 52 | * @param path Path to the File inside of the Archive | ||
| 53 | * @param mode Mode under which to open the File | ||
| 54 | * @return Handle to the opened File object | ||
| 55 | */ | ||
| 56 | ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Delete a File from an Archive | ||
| 60 | * @param archive_handle Handle to an open Archive object | ||
| 61 | * @param path Path to the File inside of the Archive | ||
| 62 | * @return Whether deletion succeeded | ||
| 63 | */ | ||
| 64 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Rename a File between two Archives | ||
| 68 | * @param src_archive_handle Handle to the source Archive object | ||
| 69 | * @param src_path Path to the File inside of the source Archive | ||
| 70 | * @param dest_archive_handle Handle to the destination Archive object | ||
| 71 | * @param dest_path Path to the File inside of the destination Archive | ||
| 72 | * @return Whether rename succeeded | ||
| 73 | */ | ||
| 74 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 75 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Delete a Directory from an Archive | ||
| 79 | * @param archive_handle Handle to an open Archive object | ||
| 80 | * @param path Path to the Directory inside of the Archive | ||
| 81 | * @return Whether deletion succeeded | ||
| 82 | */ | ||
| 83 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Create a Directory from an Archive | ||
| 87 | * @param archive_handle Handle to an open Archive object | ||
| 88 | * @param path Path to the Directory inside of the Archive | ||
| 89 | * @return Whether creation of directory succeeded | ||
| 90 | */ | ||
| 91 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Rename a Directory between two Archives | ||
| 95 | * @param src_archive_handle Handle to the source Archive object | ||
| 96 | * @param src_path Path to the Directory inside of the source Archive | ||
| 97 | * @param dest_archive_handle Handle to the destination Archive object | ||
| 98 | * @param dest_path Path to the Directory inside of the destination Archive | ||
| 99 | * @return Whether rename succeeded | ||
| 100 | */ | ||
| 101 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 102 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Open a Directory from an Archive | ||
| 106 | * @param archive_handle Handle to an open Archive object | ||
| 107 | * @param path Path to the Directory inside of the Archive | ||
| 108 | * @return Handle to the opened File object | ||
| 109 | */ | ||
| 110 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Creates a blank SaveData archive. | ||
| 114 | * @return ResultCode 0 on success or the corresponding code on error | ||
| 115 | */ | ||
| 116 | ResultCode FormatSaveData(); | ||
| 117 | |||
| 118 | /// Initialize archives | ||
| 119 | void ArchiveInit(); | ||
| 120 | |||
| 121 | /// Shutdown archives | ||
| 122 | void ArchiveShutdown(); | ||
| 123 | |||
| 124 | } // namespace FS | ||
| 125 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp new file mode 100644 index 000000000..f99d84b2f --- /dev/null +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -0,0 +1,529 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common.h" | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/scope_exit.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/fs/archive.h" | ||
| 11 | #include "core/hle/service/fs/fs_user.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // Namespace FS_User | ||
| 16 | |||
| 17 | namespace Service { | ||
| 18 | namespace FS { | ||
| 19 | |||
| 20 | static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) { | ||
| 21 | return (u64)low_word | ((u64)high_word << 32); | ||
| 22 | } | ||
| 23 | |||
| 24 | static void Initialize(Service::Interface* self) { | ||
| 25 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 26 | |||
| 27 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per | ||
| 28 | // http://3dbrew.org/wiki/FS:Initialize#Request | ||
| 29 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 30 | |||
| 31 | LOG_DEBUG(Service_FS, "called"); | ||
| 32 | } | ||
| 33 | |||
| 34 | /** | ||
| 35 | * FS_User::OpenFile service function | ||
| 36 | * Inputs: | ||
| 37 | * 1 : Transaction | ||
| 38 | * 2 : Archive handle lower word | ||
| 39 | * 3 : Archive handle upper word | ||
| 40 | * 4 : Low path type | ||
| 41 | * 5 : Low path size | ||
| 42 | * 6 : Open flags | ||
| 43 | * 7 : Attributes | ||
| 44 | * 8 : (LowPathSize << 14) | 2 | ||
| 45 | * 9 : Low path data pointer | ||
| 46 | * Outputs: | ||
| 47 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 48 | * 3 : File handle | ||
| 49 | */ | ||
| 50 | static void OpenFile(Service::Interface* self) { | ||
| 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 52 | |||
| 53 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 54 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 55 | u32 filename_size = cmd_buff[5]; | ||
| 56 | FileSys::Mode mode; mode.hex = cmd_buff[6]; | ||
| 57 | u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes. | ||
| 58 | u32 filename_ptr = cmd_buff[9]; | ||
| 59 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 60 | |||
| 61 | LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); | ||
| 62 | |||
| 63 | ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode); | ||
| 64 | cmd_buff[1] = handle.Code().raw; | ||
| 65 | if (handle.Succeeded()) { | ||
| 66 | cmd_buff[3] = *handle; | ||
| 67 | } else { | ||
| 68 | cmd_buff[3] = 0; | ||
| 69 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * FS_User::OpenFileDirectly service function | ||
| 75 | * Inputs: | ||
| 76 | * 1 : Transaction | ||
| 77 | * 2 : Archive ID | ||
| 78 | * 3 : Archive low path type | ||
| 79 | * 4 : Archive low path size | ||
| 80 | * 5 : File low path type | ||
| 81 | * 6 : File low path size | ||
| 82 | * 7 : Flags | ||
| 83 | * 8 : Attributes | ||
| 84 | * 9 : (ArchiveLowPathSize << 14) | 0x802 | ||
| 85 | * 10 : Archive low path | ||
| 86 | * 11 : (FileLowPathSize << 14) | 2 | ||
| 87 | * 12 : File low path | ||
| 88 | * Outputs: | ||
| 89 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 90 | * 3 : File handle | ||
| 91 | */ | ||
| 92 | static void OpenFileDirectly(Service::Interface* self) { | ||
| 93 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 94 | |||
| 95 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]); | ||
| 96 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||
| 97 | u32 archivename_size = cmd_buff[4]; | ||
| 98 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); | ||
| 99 | u32 filename_size = cmd_buff[6]; | ||
| 100 | FileSys::Mode mode; mode.hex = cmd_buff[7]; | ||
| 101 | u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes. | ||
| 102 | u32 archivename_ptr = cmd_buff[10]; | ||
| 103 | u32 filename_ptr = cmd_buff[12]; | ||
| 104 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 105 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 106 | |||
| 107 | LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d", | ||
| 108 | archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes); | ||
| 109 | |||
| 110 | if (archive_path.GetType() != FileSys::Empty) { | ||
| 111 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 112 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 113 | cmd_buff[3] = 0; | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id); | ||
| 118 | if (archive_handle.Failed()) { | ||
| 119 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | ||
| 120 | cmd_buff[1] = archive_handle.Code().raw; | ||
| 121 | cmd_buff[3] = 0; | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | SCOPE_EXIT({ CloseArchive(*archive_handle); }); | ||
| 125 | |||
| 126 | ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode); | ||
| 127 | cmd_buff[1] = handle.Code().raw; | ||
| 128 | if (handle.Succeeded()) { | ||
| 129 | cmd_buff[3] = *handle; | ||
| 130 | } else { | ||
| 131 | cmd_buff[3] = 0; | ||
| 132 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | /* | ||
| 137 | * FS_User::DeleteFile service function | ||
| 138 | * Inputs: | ||
| 139 | * 2 : Archive handle lower word | ||
| 140 | * 3 : Archive handle upper word | ||
| 141 | * 4 : File path string type | ||
| 142 | * 5 : File path string size | ||
| 143 | * 7 : File path string data | ||
| 144 | * Outputs: | ||
| 145 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 146 | */ | ||
| 147 | static void DeleteFile(Service::Interface* self) { | ||
| 148 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 149 | |||
| 150 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 151 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 152 | u32 filename_size = cmd_buff[5]; | ||
| 153 | u32 filename_ptr = cmd_buff[7]; | ||
| 154 | |||
| 155 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 156 | |||
| 157 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", | ||
| 158 | filename_type, filename_size, file_path.DebugStr().c_str()); | ||
| 159 | |||
| 160 | cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw; | ||
| 161 | } | ||
| 162 | |||
| 163 | /* | ||
| 164 | * FS_User::RenameFile service function | ||
| 165 | * Inputs: | ||
| 166 | * 2 : Source archive handle lower word | ||
| 167 | * 3 : Source archive handle upper word | ||
| 168 | * 4 : Source file path type | ||
| 169 | * 5 : Source file path size | ||
| 170 | * 6 : Dest archive handle lower word | ||
| 171 | * 7 : Dest archive handle upper word | ||
| 172 | * 8 : Dest file path type | ||
| 173 | * 9 : Dest file path size | ||
| 174 | * 11: Source file path string data | ||
| 175 | * 13: Dest file path string | ||
| 176 | * Outputs: | ||
| 177 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 178 | */ | ||
| 179 | static void RenameFile(Service::Interface* self) { | ||
| 180 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 181 | |||
| 182 | ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 183 | auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 184 | u32 src_filename_size = cmd_buff[5]; | ||
| 185 | ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);; | ||
| 186 | auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | ||
| 187 | u32 dest_filename_size = cmd_buff[9]; | ||
| 188 | u32 src_filename_ptr = cmd_buff[11]; | ||
| 189 | u32 dest_filename_ptr = cmd_buff[13]; | ||
| 190 | |||
| 191 | FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr); | ||
| 192 | FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr); | ||
| 193 | |||
| 194 | LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s", | ||
| 195 | src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(), | ||
| 196 | dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str()); | ||
| 197 | |||
| 198 | cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* | ||
| 202 | * FS_User::DeleteDirectory service function | ||
| 203 | * Inputs: | ||
| 204 | * 2 : Archive handle lower word | ||
| 205 | * 3 : Archive handle upper word | ||
| 206 | * 4 : Directory path string type | ||
| 207 | * 5 : Directory path string size | ||
| 208 | * 7 : Directory path string data | ||
| 209 | * Outputs: | ||
| 210 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 211 | */ | ||
| 212 | static void DeleteDirectory(Service::Interface* self) { | ||
| 213 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 214 | |||
| 215 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 216 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 217 | u32 dirname_size = cmd_buff[5]; | ||
| 218 | u32 dirname_ptr = cmd_buff[7]; | ||
| 219 | |||
| 220 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 221 | |||
| 222 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", | ||
| 223 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 224 | |||
| 225 | cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* | ||
| 229 | * FS_User::CreateDirectory service function | ||
| 230 | * Inputs: | ||
| 231 | * 2 : Archive handle lower word | ||
| 232 | * 3 : Archive handle upper word | ||
| 233 | * 4 : Directory path string type | ||
| 234 | * 5 : Directory path string size | ||
| 235 | * 8 : Directory path string data | ||
| 236 | * Outputs: | ||
| 237 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 238 | */ | ||
| 239 | static void CreateDirectory(Service::Interface* self) { | ||
| 240 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 241 | |||
| 242 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 243 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 244 | u32 dirname_size = cmd_buff[5]; | ||
| 245 | u32 dirname_ptr = cmd_buff[8]; | ||
| 246 | |||
| 247 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 248 | |||
| 249 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 250 | |||
| 251 | cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* | ||
| 255 | * FS_User::RenameDirectory service function | ||
| 256 | * Inputs: | ||
| 257 | * 2 : Source archive handle lower word | ||
| 258 | * 3 : Source archive handle upper word | ||
| 259 | * 4 : Source dir path type | ||
| 260 | * 5 : Source dir path size | ||
| 261 | * 6 : Dest archive handle lower word | ||
| 262 | * 7 : Dest archive handle upper word | ||
| 263 | * 8 : Dest dir path type | ||
| 264 | * 9 : Dest dir path size | ||
| 265 | * 11: Source dir path string data | ||
| 266 | * 13: Dest dir path string | ||
| 267 | * Outputs: | ||
| 268 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 269 | */ | ||
| 270 | static void RenameDirectory(Service::Interface* self) { | ||
| 271 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 272 | |||
| 273 | ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 274 | auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 275 | u32 src_dirname_size = cmd_buff[5]; | ||
| 276 | ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]); | ||
| 277 | auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | ||
| 278 | u32 dest_dirname_size = cmd_buff[9]; | ||
| 279 | u32 src_dirname_ptr = cmd_buff[11]; | ||
| 280 | u32 dest_dirname_ptr = cmd_buff[13]; | ||
| 281 | |||
| 282 | FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr); | ||
| 283 | FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr); | ||
| 284 | |||
| 285 | LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s", | ||
| 286 | src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(), | ||
| 287 | dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str()); | ||
| 288 | |||
| 289 | cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; | ||
| 290 | } | ||
| 291 | |||
| 292 | /** | ||
| 293 | * FS_User::OpenDirectory service function | ||
| 294 | * Inputs: | ||
| 295 | * 1 : Archive handle low word | ||
| 296 | * 2 : Archive handle high word | ||
| 297 | * 3 : Low path type | ||
| 298 | * 4 : Low path size | ||
| 299 | * 7 : (LowPathSize << 14) | 2 | ||
| 300 | * 8 : Low path data pointer | ||
| 301 | * Outputs: | ||
| 302 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 303 | * 3 : Directory handle | ||
| 304 | */ | ||
| 305 | static void OpenDirectory(Service::Interface* self) { | ||
| 306 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 307 | |||
| 308 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); | ||
| 309 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||
| 310 | u32 dirname_size = cmd_buff[4]; | ||
| 311 | u32 dirname_ptr = cmd_buff[6]; | ||
| 312 | |||
| 313 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 314 | |||
| 315 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 316 | |||
| 317 | ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path); | ||
| 318 | cmd_buff[1] = handle.Code().raw; | ||
| 319 | if (handle.Succeeded()) { | ||
| 320 | cmd_buff[3] = *handle; | ||
| 321 | } else { | ||
| 322 | LOG_ERROR(Service_FS, "failed to get a handle for directory"); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * FS_User::OpenArchive service function | ||
| 328 | * Inputs: | ||
| 329 | * 1 : Archive ID | ||
| 330 | * 2 : Archive low path type | ||
| 331 | * 3 : Archive low path size | ||
| 332 | * 4 : (LowPathSize << 14) | 2 | ||
| 333 | * 5 : Archive low path | ||
| 334 | * Outputs: | ||
| 335 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 336 | * 2 : Archive handle lower word (unused) | ||
| 337 | * 3 : Archive handle upper word (same as file handle) | ||
| 338 | */ | ||
| 339 | static void OpenArchive(Service::Interface* self) { | ||
| 340 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 341 | |||
| 342 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||
| 343 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||
| 344 | u32 archivename_size = cmd_buff[3]; | ||
| 345 | u32 archivename_ptr = cmd_buff[5]; | ||
| 346 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 347 | |||
| 348 | LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||
| 349 | |||
| 350 | if (archive_path.GetType() != FileSys::Empty) { | ||
| 351 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 352 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 353 | return; | ||
| 354 | } | ||
| 355 | |||
| 356 | ResultVal<ArchiveHandle> handle = OpenArchive(archive_id); | ||
| 357 | cmd_buff[1] = handle.Code().raw; | ||
| 358 | if (handle.Succeeded()) { | ||
| 359 | cmd_buff[2] = *handle & 0xFFFFFFFF; | ||
| 360 | cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; | ||
| 361 | } else { | ||
| 362 | cmd_buff[2] = cmd_buff[3] = 0; | ||
| 363 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | /** | ||
| 368 | * FS_User::CloseArchive service function | ||
| 369 | * Inputs: | ||
| 370 | * 0 : 0x080E0080 | ||
| 371 | * 1 : Archive handle low word | ||
| 372 | * 2 : Archive handle high word | ||
| 373 | * Outputs: | ||
| 374 | * 0 : ??? TODO(yuriks): Verify return header | ||
| 375 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 376 | */ | ||
| 377 | static void CloseArchive(Service::Interface* self) { | ||
| 378 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 379 | |||
| 380 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); | ||
| 381 | cmd_buff[1] = CloseArchive(archive_handle).raw; | ||
| 382 | } | ||
| 383 | |||
| 384 | /* | ||
| 385 | * FS_User::IsSdmcDetected service function | ||
| 386 | * Outputs: | ||
| 387 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 388 | * 2 : Whether the Sdmc could be detected | ||
| 389 | */ | ||
| 390 | static void IsSdmcDetected(Service::Interface* self) { | ||
| 391 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 392 | |||
| 393 | cmd_buff[1] = 0; | ||
| 394 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | ||
| 395 | |||
| 396 | LOG_DEBUG(Service_FS, "called"); | ||
| 397 | } | ||
| 398 | |||
| 399 | /** | ||
| 400 | * FS_User::FormatSaveData service function | ||
| 401 | * Inputs: | ||
| 402 | * Outputs: | ||
| 403 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 404 | */ | ||
| 405 | static void FormatSaveData(Service::Interface* self) { | ||
| 406 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 407 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 408 | |||
| 409 | // TODO(Subv): Find out what the inputs and outputs of this function are | ||
| 410 | |||
| 411 | cmd_buff[1] = FormatSaveData().raw; | ||
| 412 | } | ||
| 413 | |||
| 414 | /** | ||
| 415 | * FS_User::FormatThisUserSaveData service function | ||
| 416 | * Inputs: | ||
| 417 | * Outputs: | ||
| 418 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 419 | */ | ||
| 420 | static void FormatThisUserSaveData(Service::Interface* self) { | ||
| 421 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 422 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 423 | |||
| 424 | // TODO(Subv): Find out what the inputs and outputs of this function are | ||
| 425 | |||
| 426 | cmd_buff[1] = FormatSaveData().raw; | ||
| 427 | } | ||
| 428 | |||
| 429 | const FSUserInterface::FunctionInfo FunctionTable[] = { | ||
| 430 | {0x000100C6, nullptr, "Dummy1"}, | ||
| 431 | {0x040100C4, nullptr, "Control"}, | ||
| 432 | {0x08010002, Initialize, "Initialize"}, | ||
| 433 | {0x080201C2, OpenFile, "OpenFile"}, | ||
| 434 | {0x08030204, OpenFileDirectly, "OpenFileDirectly"}, | ||
| 435 | {0x08040142, DeleteFile, "DeleteFile"}, | ||
| 436 | {0x08050244, RenameFile, "RenameFile"}, | ||
| 437 | {0x08060142, DeleteDirectory, "DeleteDirectory"}, | ||
| 438 | {0x08070142, nullptr, "DeleteDirectoryRecursively"}, | ||
| 439 | {0x08080202, nullptr, "CreateFile"}, | ||
| 440 | {0x08090182, CreateDirectory, "CreateDirectory"}, | ||
| 441 | {0x080A0244, RenameDirectory, "RenameDirectory"}, | ||
| 442 | {0x080B0102, OpenDirectory, "OpenDirectory"}, | ||
| 443 | {0x080C00C2, OpenArchive, "OpenArchive"}, | ||
| 444 | {0x080D0144, nullptr, "ControlArchive"}, | ||
| 445 | {0x080E0080, CloseArchive, "CloseArchive"}, | ||
| 446 | {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, | ||
| 447 | {0x08100200, nullptr, "CreateSystemSaveData"}, | ||
| 448 | {0x08110040, nullptr, "DeleteSystemSaveData"}, | ||
| 449 | {0x08120080, nullptr, "GetFreeBytes"}, | ||
| 450 | {0x08130000, nullptr, "GetCardType"}, | ||
| 451 | {0x08140000, nullptr, "GetSdmcArchiveResource"}, | ||
| 452 | {0x08150000, nullptr, "GetNandArchiveResource"}, | ||
| 453 | {0x08160000, nullptr, "GetSdmcFatfsErro"}, | ||
| 454 | {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, | ||
| 455 | {0x08180000, nullptr, "IsSdmcWritable"}, | ||
| 456 | {0x08190042, nullptr, "GetSdmcCid"}, | ||
| 457 | {0x081A0042, nullptr, "GetNandCid"}, | ||
| 458 | {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, | ||
| 459 | {0x081C0000, nullptr, "GetNandSpeedInfo"}, | ||
| 460 | {0x081D0042, nullptr, "GetSdmcLog"}, | ||
| 461 | {0x081E0042, nullptr, "GetNandLog"}, | ||
| 462 | {0x081F0000, nullptr, "ClearSdmcLog"}, | ||
| 463 | {0x08200000, nullptr, "ClearNandLog"}, | ||
| 464 | {0x08210000, nullptr, "CardSlotIsInserted"}, | ||
| 465 | {0x08220000, nullptr, "CardSlotPowerOn"}, | ||
| 466 | {0x08230000, nullptr, "CardSlotPowerOff"}, | ||
| 467 | {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, | ||
| 468 | {0x08250040, nullptr, "CardNorDirectCommand"}, | ||
| 469 | {0x08260080, nullptr, "CardNorDirectCommandWithAddress"}, | ||
| 470 | {0x08270082, nullptr, "CardNorDirectRead"}, | ||
| 471 | {0x082800C2, nullptr, "CardNorDirectReadWithAddress"}, | ||
| 472 | {0x08290082, nullptr, "CardNorDirectWrite"}, | ||
| 473 | {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"}, | ||
| 474 | {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"}, | ||
| 475 | {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, | ||
| 476 | {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, | ||
| 477 | {0x082E0040, nullptr, "GetProductInfo"}, | ||
| 478 | {0x082F0040, nullptr, "GetProgramLaunchInfo"}, | ||
| 479 | {0x08300182, nullptr, "CreateExtSaveData"}, | ||
| 480 | {0x08310180, nullptr, "CreateSharedExtSaveData"}, | ||
| 481 | {0x08320102, nullptr, "ReadExtSaveDataIcon"}, | ||
| 482 | {0x08330082, nullptr, "EnumerateExtSaveData"}, | ||
| 483 | {0x08340082, nullptr, "EnumerateSharedExtSaveData"}, | ||
| 484 | {0x08350080, nullptr, "DeleteExtSaveData"}, | ||
| 485 | {0x08360080, nullptr, "DeleteSharedExtSaveData"}, | ||
| 486 | {0x08370040, nullptr, "SetCardSpiBaudRate"}, | ||
| 487 | {0x08380040, nullptr, "SetCardSpiBusMode"}, | ||
| 488 | {0x08390000, nullptr, "SendInitializeInfoTo9"}, | ||
| 489 | {0x083A0100, nullptr, "GetSpecialContentIndex"}, | ||
| 490 | {0x083B00C2, nullptr, "GetLegacyRomHeader"}, | ||
| 491 | {0x083C00C2, nullptr, "GetLegacyBannerData"}, | ||
| 492 | {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"}, | ||
| 493 | {0x083E00C2, nullptr, "QueryTotalQuotaSize"}, | ||
| 494 | {0x083F00C0, nullptr, "GetExtDataBlockSize"}, | ||
| 495 | {0x08400040, nullptr, "AbnegateAccessRight"}, | ||
| 496 | {0x08410000, nullptr, "DeleteSdmcRoot"}, | ||
| 497 | {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, | ||
| 498 | {0x08430000, nullptr, "InitializeCtrFileSystem"}, | ||
| 499 | {0x08440000, nullptr, "CreateSeed"}, | ||
| 500 | {0x084500C2, nullptr, "GetFormatInfo"}, | ||
| 501 | {0x08460102, nullptr, "GetLegacyRomHeader2"}, | ||
| 502 | {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, | ||
| 503 | {0x08480042, nullptr, "GetSdmcCtrRootPath"}, | ||
| 504 | {0x08490040, nullptr, "GetArchiveResource"}, | ||
| 505 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, | ||
| 506 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, | ||
| 507 | {0x084C0242, FormatSaveData, "FormatSaveData"}, | ||
| 508 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, | ||
| 509 | {0x084E0342, nullptr, "UpdateSha256Context"}, | ||
| 510 | {0x084F0102, nullptr, "ReadSpecialFile"}, | ||
| 511 | {0x08500040, nullptr, "GetSpecialFileSize"}, | ||
| 512 | {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, | ||
| 513 | {0x08610042, nullptr, "InitializeWithSdkVersion"}, | ||
| 514 | {0x08620040, nullptr, "SetPriority"}, | ||
| 515 | {0x08630000, nullptr, "GetPriority"}, | ||
| 516 | }; | ||
| 517 | |||
| 518 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 519 | // Interface class | ||
| 520 | |||
| 521 | FSUserInterface::FSUserInterface() { | ||
| 522 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 523 | } | ||
| 524 | |||
| 525 | FSUserInterface::~FSUserInterface() { | ||
| 526 | } | ||
| 527 | |||
| 528 | } // namespace FS | ||
| 529 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h new file mode 100644 index 000000000..80e3804e0 --- /dev/null +++ b/src/core/hle/service/fs/fs_user.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace FS_User | ||
| 11 | |||
| 12 | namespace Service { | ||
| 13 | namespace FS { | ||
| 14 | |||
| 15 | /// Interface to "fs:USER" service | ||
| 16 | class FSUserInterface : public Service::Interface { | ||
| 17 | public: | ||
| 18 | |||
| 19 | FSUserInterface(); | ||
| 20 | |||
| 21 | ~FSUserInterface(); | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Gets the string port name used by CTROS for the service | ||
| 25 | * @return Port name of service | ||
| 26 | */ | ||
| 27 | std::string GetPortName() const override { | ||
| 28 | return "fs:USER"; | ||
| 29 | } | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace FS | ||
| 33 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs_user.cpp deleted file mode 100644 index 48d806e2f..000000000 --- a/src/core/hle/service/fs_user.cpp +++ /dev/null | |||
| @@ -1,354 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common.h" | ||
| 6 | |||
| 7 | #include "fs_user.h" | ||
| 8 | #include "core/settings.h" | ||
| 9 | #include "core/hle/kernel/archive.h" | ||
| 10 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // Namespace FS_User | ||
| 13 | |||
| 14 | namespace FS_User { | ||
| 15 | |||
| 16 | // Command to access archive file | ||
| 17 | enum class LowPathType : u32 { | ||
| 18 | Invalid = 0, | ||
| 19 | Empty = 1, | ||
| 20 | Binary = 2, | ||
| 21 | Char = 3, | ||
| 22 | Wchar = 4 | ||
| 23 | }; | ||
| 24 | |||
| 25 | std::string GetStringFromCmdBuff(const u32 pointer, const u32 size) { | ||
| 26 | auto data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); | ||
| 27 | return std::string(data, size - 1); | ||
| 28 | } | ||
| 29 | |||
| 30 | // We currently return 0 for success and -1 for failure in cmd_buff[1]. -1 was chosen because it | ||
| 31 | // puts all the sections of the http://3dbrew.org/wiki/Error_codes to something non-zero, to make | ||
| 32 | // sure we don't mislead the application into thinking something worked. | ||
| 33 | |||
| 34 | void Initialize(Service::Interface* self) { | ||
| 35 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 36 | |||
| 37 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per | ||
| 38 | // http://3dbrew.org/wiki/FS:Initialize#Request | ||
| 39 | cmd_buff[1] = 0; | ||
| 40 | |||
| 41 | DEBUG_LOG(KERNEL, "called"); | ||
| 42 | } | ||
| 43 | |||
| 44 | void OpenFile(Service::Interface* self) { | ||
| 45 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 46 | |||
| 47 | u32 transaction = cmd_buff[1]; | ||
| 48 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 49 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 50 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 51 | LowPathType type = static_cast<LowPathType>(cmd_buff[4]); | ||
| 52 | u32 size = cmd_buff[5]; | ||
| 53 | FileSys::Mode mode; mode.hex = cmd_buff[6]; | ||
| 54 | u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes. | ||
| 55 | u32 pointer = cmd_buff[9]; | ||
| 56 | |||
| 57 | if (type != LowPathType::Char) { | ||
| 58 | ERROR_LOG(KERNEL, "file LowPath type other than char is currently unsupported"); | ||
| 59 | cmd_buff[1] = -1; | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | std::string file_name = GetStringFromCmdBuff(pointer, size); | ||
| 64 | |||
| 65 | DEBUG_LOG(KERNEL, "type=%d size=%d mode=%d attrs=%d data=%s", type, size, mode, attributes, file_name.c_str()); | ||
| 66 | |||
| 67 | Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode); | ||
| 68 | if (handle) { | ||
| 69 | cmd_buff[1] = 0; | ||
| 70 | cmd_buff[3] = handle; | ||
| 71 | } else { | ||
| 72 | ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str()); | ||
| 73 | // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily. | ||
| 74 | cmd_buff[1] = -1; | ||
| 75 | } | ||
| 76 | |||
| 77 | DEBUG_LOG(KERNEL, "called"); | ||
| 78 | } | ||
| 79 | |||
| 80 | void OpenFileDirectly(Service::Interface* self) { | ||
| 81 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 82 | |||
| 83 | u32 transaction = cmd_buff[1]; | ||
| 84 | auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]); | ||
| 85 | LowPathType archive_type = static_cast<LowPathType>(cmd_buff[3]); | ||
| 86 | u32 archive_size = cmd_buff[4]; | ||
| 87 | LowPathType file_type = static_cast<LowPathType>(cmd_buff[5]); | ||
| 88 | u32 size = cmd_buff[6]; | ||
| 89 | FileSys::Mode mode; mode.hex = cmd_buff[7]; | ||
| 90 | u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes. | ||
| 91 | u32 archive_pointer = cmd_buff[10]; | ||
| 92 | u32 pointer = cmd_buff[12]; | ||
| 93 | |||
| 94 | if (archive_type != LowPathType::Empty) { | ||
| 95 | ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported"); | ||
| 96 | cmd_buff[1] = -1; | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string archive_name = GetStringFromCmdBuff(archive_pointer, archive_size); | ||
| 101 | std::string file_name = GetStringFromCmdBuff(pointer, size); | ||
| 102 | |||
| 103 | DEBUG_LOG(KERNEL, "archive_type=%d archive_size=%d archive_data=%s " | ||
| 104 | "file_type=%d file_size=%d file_mode=%d file_attrs=%d file_data=%s", | ||
| 105 | archive_type, archive_size, archive_name.c_str(), | ||
| 106 | file_type, size, mode, attributes, file_name.c_str()); | ||
| 107 | |||
| 108 | // TODO(Link Mauve): check if we should even get a handle for the archive, and don't leak it. | ||
| 109 | Handle archive_handle = Kernel::OpenArchive(archive_id); | ||
| 110 | if (archive_handle) { | ||
| 111 | cmd_buff[1] = 0; | ||
| 112 | // cmd_buff[2] isn't used according to 3dmoo's implementation. | ||
| 113 | cmd_buff[3] = archive_handle; | ||
| 114 | } else { | ||
| 115 | ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str()); | ||
| 116 | // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily. | ||
| 117 | cmd_buff[1] = -1; | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | |||
| 121 | if (file_type != LowPathType::Char) { | ||
| 122 | WARN_LOG(KERNEL, "file LowPath type other than char is currently unsupported; returning archive handle instead"); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | |||
| 126 | Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode); | ||
| 127 | if (handle) { | ||
| 128 | cmd_buff[1] = 0; | ||
| 129 | cmd_buff[3] = handle; | ||
| 130 | } else { | ||
| 131 | ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str()); | ||
| 132 | // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily. | ||
| 133 | cmd_buff[1] = -1; | ||
| 134 | } | ||
| 135 | |||
| 136 | DEBUG_LOG(KERNEL, "called"); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* | ||
| 140 | * FS_User::CreateDirectory service function | ||
| 141 | * Inputs: | ||
| 142 | * 2 : Archive handle lower word | ||
| 143 | * 3 : Archive handle upper word | ||
| 144 | * 4 : Directory path string type | ||
| 145 | * 5 : Directory path string size | ||
| 146 | * 8 : Directory path string data | ||
| 147 | * Outputs: | ||
| 148 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 149 | */ | ||
| 150 | void CreateDirectory(Service::Interface* self) { | ||
| 151 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 152 | |||
| 153 | // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 154 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 155 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 156 | LowPathType type = static_cast<LowPathType>(cmd_buff[4]); | ||
| 157 | u32 name_size = cmd_buff[5]; | ||
| 158 | u32 name_offset = cmd_buff[8]; | ||
| 159 | |||
| 160 | if (type != LowPathType::Char) { | ||
| 161 | ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported"); | ||
| 162 | cmd_buff[1] = -1; | ||
| 163 | return; | ||
| 164 | } | ||
| 165 | |||
| 166 | std::string dir_name = GetStringFromCmdBuff(name_offset, name_size); | ||
| 167 | |||
| 168 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, name_size, dir_name.c_str()); | ||
| 169 | |||
| 170 | cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_name); | ||
| 171 | |||
| 172 | DEBUG_LOG(KERNEL, "called"); | ||
| 173 | } | ||
| 174 | |||
| 175 | void OpenDirectory(Service::Interface* self) { | ||
| 176 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 177 | |||
| 178 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 179 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 180 | Handle archive_handle = static_cast<Handle>(cmd_buff[2]); | ||
| 181 | LowPathType type = static_cast<LowPathType>(cmd_buff[3]); | ||
| 182 | u32 size = cmd_buff[4]; | ||
| 183 | u32 pointer = cmd_buff[6]; | ||
| 184 | |||
| 185 | if (type != LowPathType::Char) { | ||
| 186 | ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported"); | ||
| 187 | cmd_buff[1] = -1; | ||
| 188 | return; | ||
| 189 | } | ||
| 190 | |||
| 191 | std::string dir_name = GetStringFromCmdBuff(pointer, size); | ||
| 192 | |||
| 193 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, dir_name.c_str()); | ||
| 194 | |||
| 195 | Handle handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_name); | ||
| 196 | if (handle) { | ||
| 197 | cmd_buff[1] = 0; | ||
| 198 | cmd_buff[3] = handle; | ||
| 199 | } else { | ||
| 200 | ERROR_LOG(KERNEL, "failed to get a handle for directory %s", dir_name.c_str()); | ||
| 201 | // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily. | ||
| 202 | cmd_buff[1] = -1; | ||
| 203 | } | ||
| 204 | |||
| 205 | DEBUG_LOG(KERNEL, "called"); | ||
| 206 | } | ||
| 207 | |||
| 208 | void OpenArchive(Service::Interface* self) { | ||
| 209 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 210 | |||
| 211 | auto arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]); | ||
| 212 | LowPathType type = static_cast<LowPathType>(cmd_buff[2]); | ||
| 213 | u32 size = cmd_buff[3]; | ||
| 214 | u32 pointer = cmd_buff[5]; | ||
| 215 | |||
| 216 | if (type != LowPathType::Empty) { | ||
| 217 | ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported"); | ||
| 218 | cmd_buff[1] = -1; | ||
| 219 | return; | ||
| 220 | } | ||
| 221 | |||
| 222 | std::string archive_name = GetStringFromCmdBuff(pointer, size); | ||
| 223 | |||
| 224 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, archive_name.c_str()); | ||
| 225 | |||
| 226 | Handle handle = Kernel::OpenArchive(arch_id); | ||
| 227 | if (handle) { | ||
| 228 | cmd_buff[1] = 0; | ||
| 229 | // cmd_buff[2] isn't used according to 3dmoo's implementation. | ||
| 230 | cmd_buff[3] = handle; | ||
| 231 | } else { | ||
| 232 | ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str()); | ||
| 233 | // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily. | ||
| 234 | cmd_buff[1] = -1; | ||
| 235 | } | ||
| 236 | |||
| 237 | DEBUG_LOG(KERNEL, "called"); | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * FS_User::IsSdmcDetected service function | ||
| 242 | * Outputs: | ||
| 243 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 244 | * 2 : Whether the Sdmc could be detected | ||
| 245 | */ | ||
| 246 | void IsSdmcDetected(Service::Interface* self) { | ||
| 247 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 248 | |||
| 249 | cmd_buff[1] = 0; | ||
| 250 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | ||
| 251 | |||
| 252 | DEBUG_LOG(KERNEL, "called"); | ||
| 253 | } | ||
| 254 | |||
| 255 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 256 | {0x000100C6, nullptr, "Dummy1"}, | ||
| 257 | {0x040100C4, nullptr, "Control"}, | ||
| 258 | {0x08010002, Initialize, "Initialize"}, | ||
| 259 | {0x080201C2, OpenFile, "OpenFile"}, | ||
| 260 | {0x08030204, OpenFileDirectly, "OpenFileDirectly"}, | ||
| 261 | {0x08040142, nullptr, "DeleteFile"}, | ||
| 262 | {0x08050244, nullptr, "RenameFile"}, | ||
| 263 | {0x08060142, nullptr, "DeleteDirectory"}, | ||
| 264 | {0x08070142, nullptr, "DeleteDirectoryRecursively"}, | ||
| 265 | {0x08080202, nullptr, "CreateFile"}, | ||
| 266 | {0x08090182, CreateDirectory, "CreateDirectory"}, | ||
| 267 | {0x080A0244, nullptr, "RenameDirectory"}, | ||
| 268 | {0x080B0102, OpenDirectory, "OpenDirectory"}, | ||
| 269 | {0x080C00C2, OpenArchive, "OpenArchive"}, | ||
| 270 | {0x080D0144, nullptr, "ControlArchive"}, | ||
| 271 | {0x080E0080, nullptr, "CloseArchive"}, | ||
| 272 | {0x080F0180, nullptr, "FormatThisUserSaveData"}, | ||
| 273 | {0x08100200, nullptr, "CreateSystemSaveData"}, | ||
| 274 | {0x08110040, nullptr, "DeleteSystemSaveData"}, | ||
| 275 | {0x08120080, nullptr, "GetFreeBytes"}, | ||
| 276 | {0x08130000, nullptr, "GetCardType"}, | ||
| 277 | {0x08140000, nullptr, "GetSdmcArchiveResource"}, | ||
| 278 | {0x08150000, nullptr, "GetNandArchiveResource"}, | ||
| 279 | {0x08160000, nullptr, "GetSdmcFatfsErro"}, | ||
| 280 | {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, | ||
| 281 | {0x08180000, nullptr, "IsSdmcWritable"}, | ||
| 282 | {0x08190042, nullptr, "GetSdmcCid"}, | ||
| 283 | {0x081A0042, nullptr, "GetNandCid"}, | ||
| 284 | {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, | ||
| 285 | {0x081C0000, nullptr, "GetNandSpeedInfo"}, | ||
| 286 | {0x081D0042, nullptr, "GetSdmcLog"}, | ||
| 287 | {0x081E0042, nullptr, "GetNandLog"}, | ||
| 288 | {0x081F0000, nullptr, "ClearSdmcLog"}, | ||
| 289 | {0x08200000, nullptr, "ClearNandLog"}, | ||
| 290 | {0x08210000, nullptr, "CardSlotIsInserted"}, | ||
| 291 | {0x08220000, nullptr, "CardSlotPowerOn"}, | ||
| 292 | {0x08230000, nullptr, "CardSlotPowerOff"}, | ||
| 293 | {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, | ||
| 294 | {0x08250040, nullptr, "CardNorDirectCommand"}, | ||
| 295 | {0x08260080, nullptr, "CardNorDirectCommandWithAddress"}, | ||
| 296 | {0x08270082, nullptr, "CardNorDirectRead"}, | ||
| 297 | {0x082800C2, nullptr, "CardNorDirectReadWithAddress"}, | ||
| 298 | {0x08290082, nullptr, "CardNorDirectWrite"}, | ||
| 299 | {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"}, | ||
| 300 | {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"}, | ||
| 301 | {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, | ||
| 302 | {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, | ||
| 303 | {0x082E0040, nullptr, "GetProductInfo"}, | ||
| 304 | {0x082F0040, nullptr, "GetProgramLaunchInfo"}, | ||
| 305 | {0x08300182, nullptr, "CreateExtSaveData"}, | ||
| 306 | {0x08310180, nullptr, "CreateSharedExtSaveData"}, | ||
| 307 | {0x08320102, nullptr, "ReadExtSaveDataIcon"}, | ||
| 308 | {0x08330082, nullptr, "EnumerateExtSaveData"}, | ||
| 309 | {0x08340082, nullptr, "EnumerateSharedExtSaveData"}, | ||
| 310 | {0x08350080, nullptr, "DeleteExtSaveData"}, | ||
| 311 | {0x08360080, nullptr, "DeleteSharedExtSaveData"}, | ||
| 312 | {0x08370040, nullptr, "SetCardSpiBaudRate"}, | ||
| 313 | {0x08380040, nullptr, "SetCardSpiBusMode"}, | ||
| 314 | {0x08390000, nullptr, "SendInitializeInfoTo9"}, | ||
| 315 | {0x083A0100, nullptr, "GetSpecialContentIndex"}, | ||
| 316 | {0x083B00C2, nullptr, "GetLegacyRomHeader"}, | ||
| 317 | {0x083C00C2, nullptr, "GetLegacyBannerData"}, | ||
| 318 | {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"}, | ||
| 319 | {0x083E00C2, nullptr, "QueryTotalQuotaSize"}, | ||
| 320 | {0x083F00C0, nullptr, "GetExtDataBlockSize"}, | ||
| 321 | {0x08400040, nullptr, "AbnegateAccessRight"}, | ||
| 322 | {0x08410000, nullptr, "DeleteSdmcRoot"}, | ||
| 323 | {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, | ||
| 324 | {0x08430000, nullptr, "InitializeCtrFileSystem"}, | ||
| 325 | {0x08440000, nullptr, "CreateSeed"}, | ||
| 326 | {0x084500C2, nullptr, "GetFormatInfo"}, | ||
| 327 | {0x08460102, nullptr, "GetLegacyRomHeader2"}, | ||
| 328 | {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, | ||
| 329 | {0x08480042, nullptr, "GetSdmcCtrRootPath"}, | ||
| 330 | {0x08490040, nullptr, "GetArchiveResource"}, | ||
| 331 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, | ||
| 332 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, | ||
| 333 | {0x084C0242, nullptr, "FormatSaveData"}, | ||
| 334 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, | ||
| 335 | {0x084E0342, nullptr, "UpdateSha256Context"}, | ||
| 336 | {0x084F0102, nullptr, "ReadSpecialFile"}, | ||
| 337 | {0x08500040, nullptr, "GetSpecialFileSize"}, | ||
| 338 | {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, | ||
| 339 | {0x08610042, nullptr, "InitializeWithSdkVersion"}, | ||
| 340 | {0x08620040, nullptr, "SetPriority"}, | ||
| 341 | {0x08630000, nullptr, "GetPriority"}, | ||
| 342 | }; | ||
| 343 | |||
| 344 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 345 | // Interface class | ||
| 346 | |||
| 347 | Interface::Interface() { | ||
| 348 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 349 | } | ||
| 350 | |||
| 351 | Interface::~Interface() { | ||
| 352 | } | ||
| 353 | |||
| 354 | } // namespace | ||
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 6119e6300..db8027142 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -28,41 +28,36 @@ u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1 | |||
| 28 | 28 | ||
| 29 | /// Gets a pointer to a thread command buffer in GSP shared memory | 29 | /// Gets a pointer to a thread command buffer in GSP shared memory |
| 30 | static inline u8* GetCommandBuffer(u32 thread_id) { | 30 | static inline u8* GetCommandBuffer(u32 thread_id) { |
| 31 | if (0 == g_shared_memory) | 31 | ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer))); |
| 32 | return nullptr; | 32 | return ptr.ValueOr(nullptr); |
| 33 | |||
| 34 | return Kernel::GetSharedMemoryPointer(g_shared_memory, | ||
| 35 | 0x800 + (thread_id * sizeof(CommandBuffer))); | ||
| 36 | } | 33 | } |
| 37 | 34 | ||
| 38 | static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { | 35 | static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { |
| 39 | if (0 == g_shared_memory) | 36 | _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index"); |
| 40 | return nullptr; | ||
| 41 | |||
| 42 | _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index"); | ||
| 43 | 37 | ||
| 44 | // For each thread there are two FrameBufferUpdate fields | 38 | // For each thread there are two FrameBufferUpdate fields |
| 45 | u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); | 39 | u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); |
| 46 | return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset); | 40 | ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset); |
| 41 | return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr)); | ||
| 47 | } | 42 | } |
| 48 | 43 | ||
| 49 | /// Gets a pointer to the interrupt relay queue for a given thread index | 44 | /// Gets a pointer to the interrupt relay queue for a given thread index |
| 50 | static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { | 45 | static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { |
| 51 | return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory, | 46 | ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id); |
| 52 | sizeof(InterruptRelayQueue) * thread_id); | 47 | return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr)); |
| 53 | } | 48 | } |
| 54 | 49 | ||
| 55 | void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | 50 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { |
| 56 | // TODO: Return proper error codes | 51 | // TODO: Return proper error codes |
| 57 | if (base_address + size_in_bytes >= 0x420000) { | 52 | if (base_address + size_in_bytes >= 0x420000) { |
| 58 | ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", | 53 | LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", |
| 59 | base_address, size_in_bytes); | 54 | base_address, size_in_bytes); |
| 60 | return; | 55 | return; |
| 61 | } | 56 | } |
| 62 | 57 | ||
| 63 | // size should be word-aligned | 58 | // size should be word-aligned |
| 64 | if ((size_in_bytes % 4) != 0) { | 59 | if ((size_in_bytes % 4) != 0) { |
| 65 | ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes); | 60 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); |
| 66 | return; | 61 | return; |
| 67 | } | 62 | } |
| 68 | 63 | ||
| @@ -76,8 +71,8 @@ void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | |||
| 76 | } | 71 | } |
| 77 | 72 | ||
| 78 | /// Write a GSP GPU hardware register | 73 | /// Write a GSP GPU hardware register |
| 79 | void WriteHWRegs(Service::Interface* self) { | 74 | static void WriteHWRegs(Service::Interface* self) { |
| 80 | u32* cmd_buff = Service::GetCommandBuffer(); | 75 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 81 | u32 reg_addr = cmd_buff[1]; | 76 | u32 reg_addr = cmd_buff[1]; |
| 82 | u32 size = cmd_buff[2]; | 77 | u32 size = cmd_buff[2]; |
| 83 | 78 | ||
| @@ -87,20 +82,20 @@ void WriteHWRegs(Service::Interface* self) { | |||
| 87 | } | 82 | } |
| 88 | 83 | ||
| 89 | /// Read a GSP GPU hardware register | 84 | /// Read a GSP GPU hardware register |
| 90 | void ReadHWRegs(Service::Interface* self) { | 85 | static void ReadHWRegs(Service::Interface* self) { |
| 91 | u32* cmd_buff = Service::GetCommandBuffer(); | 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 92 | u32 reg_addr = cmd_buff[1]; | 87 | u32 reg_addr = cmd_buff[1]; |
| 93 | u32 size = cmd_buff[2]; | 88 | u32 size = cmd_buff[2]; |
| 94 | 89 | ||
| 95 | // TODO: Return proper error codes | 90 | // TODO: Return proper error codes |
| 96 | if (reg_addr + size >= 0x420000) { | 91 | if (reg_addr + size >= 0x420000) { |
| 97 | ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); | 92 | LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); |
| 98 | return; | 93 | return; |
| 99 | } | 94 | } |
| 100 | 95 | ||
| 101 | // size should be word-aligned | 96 | // size should be word-aligned |
| 102 | if ((size % 4) != 0) { | 97 | if ((size % 4) != 0) { |
| 103 | ERROR_LOG(GPU, "Invalid size 0x%08x", size); | 98 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size); |
| 104 | return; | 99 | return; |
| 105 | } | 100 | } |
| 106 | 101 | ||
| @@ -115,7 +110,7 @@ void ReadHWRegs(Service::Interface* self) { | |||
| 115 | } | 110 | } |
| 116 | } | 111 | } |
| 117 | 112 | ||
| 118 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | 113 | static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { |
| 119 | u32 base_address = 0x400000; | 114 | u32 base_address = 0x400000; |
| 120 | if (info.active_fb == 0) { | 115 | if (info.active_fb == 0) { |
| 121 | WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left); | 116 | WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left); |
| @@ -140,8 +135,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | |||
| 140 | * Outputs: | 135 | * Outputs: |
| 141 | * 1: Result code | 136 | * 1: Result code |
| 142 | */ | 137 | */ |
| 143 | void SetBufferSwap(Service::Interface* self) { | 138 | static void SetBufferSwap(Service::Interface* self) { |
| 144 | u32* cmd_buff = Service::GetCommandBuffer(); | 139 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 145 | u32 screen_id = cmd_buff[1]; | 140 | u32 screen_id = cmd_buff[1]; |
| 146 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; | 141 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; |
| 147 | SetBufferSwap(screen_id, *fb_info); | 142 | SetBufferSwap(screen_id, *fb_info); |
| @@ -159,15 +154,16 @@ void SetBufferSwap(Service::Interface* self) { | |||
| 159 | * 2 : Thread index into GSP command buffer | 154 | * 2 : Thread index into GSP command buffer |
| 160 | * 4 : Handle to GSP shared memory | 155 | * 4 : Handle to GSP shared memory |
| 161 | */ | 156 | */ |
| 162 | void RegisterInterruptRelayQueue(Service::Interface* self) { | 157 | static void RegisterInterruptRelayQueue(Service::Interface* self) { |
| 163 | u32* cmd_buff = Service::GetCommandBuffer(); | 158 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 164 | u32 flags = cmd_buff[1]; | 159 | u32 flags = cmd_buff[1]; |
| 165 | g_interrupt_event = cmd_buff[3]; | 160 | g_interrupt_event = cmd_buff[3]; |
| 166 | g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); | 161 | g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); |
| 167 | 162 | ||
| 168 | _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); | 163 | _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); |
| 169 | 164 | ||
| 170 | cmd_buff[2] = g_thread_id++; // ThreadID | 165 | cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init |
| 166 | cmd_buff[2] = g_thread_id++; // Thread ID | ||
| 171 | cmd_buff[4] = g_shared_memory; // GSP shared memory | 167 | cmd_buff[4] = g_shared_memory; // GSP shared memory |
| 172 | 168 | ||
| 173 | Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? | 169 | Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? |
| @@ -177,14 +173,15 @@ void RegisterInterruptRelayQueue(Service::Interface* self) { | |||
| 177 | * Signals that the specified interrupt type has occurred to userland code | 173 | * Signals that the specified interrupt type has occurred to userland code |
| 178 | * @param interrupt_id ID of interrupt that is being signalled | 174 | * @param interrupt_id ID of interrupt that is being signalled |
| 179 | * @todo This should probably take a thread_id parameter and only signal this thread? | 175 | * @todo This should probably take a thread_id parameter and only signal this thread? |
| 176 | * @todo This probably does not belong in the GSP module, instead move to video_core | ||
| 180 | */ | 177 | */ |
| 181 | void SignalInterrupt(InterruptId interrupt_id) { | 178 | void SignalInterrupt(InterruptId interrupt_id) { |
| 182 | if (0 == g_interrupt_event) { | 179 | if (0 == g_interrupt_event) { |
| 183 | WARN_LOG(GSP, "cannot synchronize until GSP event has been created!"); | 180 | LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); |
| 184 | return; | 181 | return; |
| 185 | } | 182 | } |
| 186 | if (0 == g_shared_memory) { | 183 | if (0 == g_shared_memory) { |
| 187 | WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!"); | 184 | LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); |
| 188 | return; | 185 | return; |
| 189 | } | 186 | } |
| 190 | for (int thread_id = 0; thread_id < 0x4; ++thread_id) { | 187 | for (int thread_id = 0; thread_id < 0x4; ++thread_id) { |
| @@ -202,7 +199,7 @@ void SignalInterrupt(InterruptId interrupt_id) { | |||
| 202 | } | 199 | } |
| 203 | 200 | ||
| 204 | /// Executes the next GSP command | 201 | /// Executes the next GSP command |
| 205 | void ExecuteCommand(const Command& command, u32 thread_id) { | 202 | static void ExecuteCommand(const Command& command, u32 thread_id) { |
| 206 | // Utility function to convert register ID to address | 203 | // Utility function to convert register ID to address |
| 207 | auto WriteGPURegister = [](u32 id, u32 data) { | 204 | auto WriteGPURegister = [](u32 id, u32 data) { |
| 208 | GPU::Write<u32>(0x1EF00000 + 4 * id, data); | 205 | GPU::Write<u32>(0x1EF00000 + 4 * id, data); |
| @@ -215,6 +212,7 @@ void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 215 | memcpy(Memory::GetPointer(command.dma_request.dest_address), | 212 | memcpy(Memory::GetPointer(command.dma_request.dest_address), |
| 216 | Memory::GetPointer(command.dma_request.source_address), | 213 | Memory::GetPointer(command.dma_request.source_address), |
| 217 | command.dma_request.size); | 214 | command.dma_request.size); |
| 215 | SignalInterrupt(InterruptId::DMA); | ||
| 218 | break; | 216 | break; |
| 219 | 217 | ||
| 220 | // ctrulib homebrew sends all relevant command list data with this command, | 218 | // ctrulib homebrew sends all relevant command list data with this command, |
| @@ -223,13 +221,13 @@ void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 223 | case CommandId::SET_COMMAND_LIST_LAST: | 221 | case CommandId::SET_COMMAND_LIST_LAST: |
| 224 | { | 222 | { |
| 225 | auto& params = command.set_command_list_last; | 223 | auto& params = command.set_command_list_last; |
| 224 | |||
| 226 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); | 225 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); |
| 227 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); | 226 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size); |
| 228 | 227 | ||
| 229 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though | 228 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though |
| 230 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); | 229 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); |
| 231 | 230 | ||
| 232 | SignalInterrupt(InterruptId::P3D); | ||
| 233 | break; | 231 | break; |
| 234 | } | 232 | } |
| 235 | 233 | ||
| @@ -247,6 +245,8 @@ void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 247 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); | 245 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); |
| 248 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); | 246 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); |
| 249 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); | 247 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); |
| 248 | |||
| 249 | SignalInterrupt(InterruptId::PSC0); | ||
| 250 | break; | 250 | break; |
| 251 | } | 251 | } |
| 252 | 252 | ||
| @@ -260,14 +260,9 @@ void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 260 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); | 260 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); |
| 261 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); | 261 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); |
| 262 | 262 | ||
| 263 | // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to | 263 | // TODO(bunnei): Determine if these interrupts should be signalled here. |
| 264 | // work well enough for running demos. Need to figure out how these all work and trigger | ||
| 265 | // them correctly. | ||
| 266 | SignalInterrupt(InterruptId::PSC0); | ||
| 267 | SignalInterrupt(InterruptId::PSC1); | 264 | SignalInterrupt(InterruptId::PSC1); |
| 268 | SignalInterrupt(InterruptId::PPF); | 265 | SignalInterrupt(InterruptId::PPF); |
| 269 | SignalInterrupt(InterruptId::P3D); | ||
| 270 | SignalInterrupt(InterruptId::DMA); | ||
| 271 | 266 | ||
| 272 | // Update framebuffer information if requested | 267 | // Update framebuffer information if requested |
| 273 | for (int screen_id = 0; screen_id < 2; ++screen_id) { | 268 | for (int screen_id = 0; screen_id < 2; ++screen_id) { |
| @@ -303,12 +298,14 @@ void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 303 | } | 298 | } |
| 304 | 299 | ||
| 305 | default: | 300 | default: |
| 306 | ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value()); | 301 | LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); |
| 307 | } | 302 | } |
| 308 | } | 303 | } |
| 309 | 304 | ||
| 310 | /// This triggers handling of the GX command written to the command buffer in shared memory. | 305 | /// This triggers handling of the GX command written to the command buffer in shared memory. |
| 311 | void TriggerCmdReqQueue(Service::Interface* self) { | 306 | static void TriggerCmdReqQueue(Service::Interface* self) { |
| 307 | |||
| 308 | LOG_TRACE(Service_GSP, "called"); | ||
| 312 | 309 | ||
| 313 | // Iterate through each thread's command queue... | 310 | // Iterate through each thread's command queue... |
| 314 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { | 311 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { |
| @@ -325,6 +322,9 @@ void TriggerCmdReqQueue(Service::Interface* self) { | |||
| 325 | command_buffer->number_commands = command_buffer->number_commands - 1; | 322 | command_buffer->number_commands = command_buffer->number_commands - 1; |
| 326 | } | 323 | } |
| 327 | } | 324 | } |
| 325 | |||
| 326 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 327 | cmd_buff[1] = 0; // No error | ||
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | const Interface::FunctionInfo FunctionTable[] = { | 330 | const Interface::FunctionInfo FunctionTable[] = { |
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp index 0eb32ba4a..eb2d35964 100644 --- a/src/core/hle/service/hid_user.cpp +++ b/src/core/hle/service/hid_user.cpp | |||
| @@ -34,10 +34,7 @@ static s16 next_circle_y = 0; | |||
| 34 | * Gets a pointer to the PadData structure inside HID shared memory | 34 | * Gets a pointer to the PadData structure inside HID shared memory |
| 35 | */ | 35 | */ |
| 36 | static inline PadData* GetPadData() { | 36 | static inline PadData* GetPadData() { |
| 37 | if (0 == shared_mem) | 37 | return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr)); |
| 38 | return nullptr; | ||
| 39 | |||
| 40 | return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0)); | ||
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | /** | 40 | /** |
| @@ -47,7 +44,7 @@ static inline PadData* GetPadData() { | |||
| 47 | * | 44 | * |
| 48 | * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions. | 45 | * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions. |
| 49 | */ | 46 | */ |
| 50 | void UpdateNextCirclePadState() { | 47 | static void UpdateNextCirclePadState() { |
| 51 | static const s16 max_value = 0x9C; | 48 | static const s16 max_value = 0x9C; |
| 52 | next_circle_x = next_state.circle_left ? -max_value : 0x0; | 49 | next_circle_x = next_state.circle_left ? -max_value : 0x0; |
| 53 | next_circle_x += next_state.circle_right ? max_value : 0x0; | 50 | next_circle_x += next_state.circle_right ? max_value : 0x0; |
| @@ -58,7 +55,7 @@ void UpdateNextCirclePadState() { | |||
| 58 | /** | 55 | /** |
| 59 | * Sets a Pad state (button or button combo) as pressed | 56 | * Sets a Pad state (button or button combo) as pressed |
| 60 | */ | 57 | */ |
| 61 | void PadButtonPress(PadState pad_state) { | 58 | void PadButtonPress(const PadState& pad_state) { |
| 62 | next_state.hex |= pad_state.hex; | 59 | next_state.hex |= pad_state.hex; |
| 63 | UpdateNextCirclePadState(); | 60 | UpdateNextCirclePadState(); |
| 64 | } | 61 | } |
| @@ -66,7 +63,7 @@ void PadButtonPress(PadState pad_state) { | |||
| 66 | /** | 63 | /** |
| 67 | * Sets a Pad state (button or button combo) as released | 64 | * Sets a Pad state (button or button combo) as released |
| 68 | */ | 65 | */ |
| 69 | void PadButtonRelease(PadState pad_state) { | 66 | void PadButtonRelease(const PadState& pad_state) { |
| 70 | next_state.hex &= ~pad_state.hex; | 67 | next_state.hex &= ~pad_state.hex; |
| 71 | UpdateNextCirclePadState(); | 68 | UpdateNextCirclePadState(); |
| 72 | } | 69 | } |
| @@ -155,8 +152,8 @@ void PadUpdateComplete() { | |||
| 155 | * 7 : Gyroscope event | 152 | * 7 : Gyroscope event |
| 156 | * 8 : Event signaled by HID_User | 153 | * 8 : Event signaled by HID_User |
| 157 | */ | 154 | */ |
| 158 | void GetIPCHandles(Service::Interface* self) { | 155 | static void GetIPCHandles(Service::Interface* self) { |
| 159 | u32* cmd_buff = Service::GetCommandBuffer(); | 156 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 160 | 157 | ||
| 161 | cmd_buff[1] = 0; // No error | 158 | cmd_buff[1] = 0; // No error |
| 162 | cmd_buff[3] = shared_mem; | 159 | cmd_buff[3] = shared_mem; |
| @@ -166,7 +163,7 @@ void GetIPCHandles(Service::Interface* self) { | |||
| 166 | cmd_buff[7] = event_gyroscope; | 163 | cmd_buff[7] = event_gyroscope; |
| 167 | cmd_buff[8] = event_debug_pad; | 164 | cmd_buff[8] = event_debug_pad; |
| 168 | 165 | ||
| 169 | DEBUG_LOG(KERNEL, "called"); | 166 | LOG_TRACE(Service_HID, "called"); |
| 170 | } | 167 | } |
| 171 | 168 | ||
| 172 | const Interface::FunctionInfo FunctionTable[] = { | 169 | const Interface::FunctionInfo FunctionTable[] = { |
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h index 9f6c4d5ed..8f53befdb 100644 --- a/src/core/hle/service/hid_user.h +++ b/src/core/hle/service/hid_user.h | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | namespace HID_User { | 16 | namespace HID_User { |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | * Structure of a Pad controller state. | 19 | * Structure of a Pad controller state. |
| 20 | */ | 20 | */ |
| 21 | struct PadState { | 21 | struct PadState { |
| @@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}}; | |||
| 93 | const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; | 93 | const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; |
| 94 | 94 | ||
| 95 | // Methods for updating the HID module's state | 95 | // Methods for updating the HID module's state |
| 96 | void PadButtonPress(PadState pad_state); | 96 | void PadButtonPress(const PadState& pad_state); |
| 97 | void PadButtonRelease(PadState pad_state); | 97 | void PadButtonRelease(const PadState& pad_state); |
| 98 | void PadUpdateComplete(); | 98 | void PadUpdateComplete(); |
| 99 | 99 | ||
| 100 | /** | 100 | /** |
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp new file mode 100644 index 000000000..be15db231 --- /dev/null +++ b/src/core/hle/service/ir_rst.cpp | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/ir_rst.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace IR_RST | ||
| 11 | |||
| 12 | namespace IR_RST { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010000, nullptr, "GetHandles"}, | ||
| 16 | {0x00020080, nullptr, "Initialize"}, | ||
| 17 | {0x00030000, nullptr, "Shutdown"}, | ||
| 18 | {0x00040000, nullptr, "Unknown"}, | ||
| 19 | {0x00050000, nullptr, "Unknown"}, | ||
| 20 | {0x00060000, nullptr, "Unknown"}, | ||
| 21 | {0x00070080, nullptr, "Unknown"}, | ||
| 22 | {0x00080000, nullptr, "Unknown"}, | ||
| 23 | {0x00090000, nullptr, "Unknown"}, | ||
| 24 | }; | ||
| 25 | |||
| 26 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 27 | // Interface class | ||
| 28 | |||
| 29 | Interface::Interface() { | ||
| 30 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 31 | } | ||
| 32 | |||
| 33 | Interface::~Interface() { | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace | ||
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h new file mode 100644 index 000000000..73effd7e3 --- /dev/null +++ b/src/core/hle/service/ir_rst.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace IR_RST | ||
| 11 | |||
| 12 | namespace IR_RST { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "ir:rst"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp new file mode 100644 index 000000000..aa9db6f6d --- /dev/null +++ b/src/core/hle/service/ir_u.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/ir_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace IR_U | ||
| 11 | |||
| 12 | namespace IR_U { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010000, nullptr, "Initialize"}, | ||
| 16 | {0x00020000, nullptr, "Shutdown"}, | ||
| 17 | {0x00030042, nullptr, "StartSendTransfer"}, | ||
| 18 | {0x00040000, nullptr, "WaitSendTransfer"}, | ||
| 19 | {0x000500C2, nullptr, "StartRecvTransfer"}, | ||
| 20 | {0x00060000, nullptr, "WaitRecvTransfer"}, | ||
| 21 | {0x00070080, nullptr, "GetRecvTransferCount"}, | ||
| 22 | {0x00080000, nullptr, "GetSendState"}, | ||
| 23 | {0x00090040, nullptr, "SetBitRate"}, | ||
| 24 | {0x000A0000, nullptr, "GetBitRate"}, | ||
| 25 | {0x000B0040, nullptr, "SetIRLEDState"}, | ||
| 26 | {0x000C0000, nullptr, "GetIRLEDRecvState"}, | ||
| 27 | {0x000D0000, nullptr, "GetSendFinishedEvent"}, | ||
| 28 | {0x000E0000, nullptr, "GetRecvFinishedEvent"}, | ||
| 29 | {0x000F0000, nullptr, "GetTransferState"}, | ||
| 30 | {0x00100000, nullptr, "GetErrorStatus"}, | ||
| 31 | {0x00110040, nullptr, "SetSleepModeActive"}, | ||
| 32 | {0x00120040, nullptr, "SetSleepModeState"}, | ||
| 33 | }; | ||
| 34 | |||
| 35 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 36 | // Interface class | ||
| 37 | |||
| 38 | Interface::Interface() { | ||
| 39 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 40 | } | ||
| 41 | |||
| 42 | Interface::~Interface() { | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace | ||
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h new file mode 100644 index 000000000..86d98d079 --- /dev/null +++ b/src/core/hle/service/ir_u.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace IR_U | ||
| 11 | |||
| 12 | namespace IR_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "ir:u"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp new file mode 100644 index 000000000..91b1a6fc5 --- /dev/null +++ b/src/core/hle/service/ldr_ro.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/ldr_ro.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace LDR_RO | ||
| 11 | |||
| 12 | namespace LDR_RO { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x000100C2, nullptr, "Initialize"}, | ||
| 16 | {0x00020082, nullptr, "CRR_Load"}, | ||
| 17 | {0x00030042, nullptr, "CRR_Unload"}, | ||
| 18 | {0x000402C2, nullptr, "CRO_LoadAndFix"}, | ||
| 19 | {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"} | ||
| 20 | }; | ||
| 21 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 22 | // Interface class | ||
| 23 | |||
| 24 | Interface::Interface() { | ||
| 25 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 26 | } | ||
| 27 | |||
| 28 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h new file mode 100644 index 000000000..32d7c29cf --- /dev/null +++ b/src/core/hle/service/ldr_ro.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace LDR_RO | ||
| 11 | |||
| 12 | namespace LDR_RO { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "ldr:ro"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 58051f133..d6f30e9ae 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp | |||
| @@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 27 | {0x000D0040, nullptr, "SetClamp"}, | 27 | {0x000D0040, nullptr, "SetClamp"}, |
| 28 | {0x000E0000, nullptr, "GetClamp"}, | 28 | {0x000E0000, nullptr, "GetClamp"}, |
| 29 | {0x000F0040, nullptr, "unknown_input1"}, | 29 | {0x000F0040, nullptr, "unknown_input1"}, |
| 30 | {0x00100040, nullptr, "unknown_input2"}, | 30 | {0x00100040, nullptr, "unknown_input2"}, |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 33 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 72ba048ef..2a495f3a9 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h | |||
| @@ -21,7 +21,7 @@ public: | |||
| 21 | * Gets the string port name used by CTROS for the service | 21 | * Gets the string port name used by CTROS for the service |
| 22 | * @return Port name of service | 22 | * @return Port name of service |
| 23 | */ | 23 | */ |
| 24 | std::string GetPortName() const { | 24 | std::string GetPortName() const override { |
| 25 | return "mic:u"; | 25 | return "mic:u"; |
| 26 | } | 26 | } |
| 27 | }; | 27 | }; |
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp new file mode 100644 index 000000000..04c1e0cf3 --- /dev/null +++ b/src/core/hle/service/nim_aoc.cpp | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/nim_aoc.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace NIM_AOC | ||
| 11 | |||
| 12 | namespace NIM_AOC { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00030042, nullptr, "SetApplicationId"}, | ||
| 16 | {0x00040042, nullptr, "SetTin"}, | ||
| 17 | {0x000902D0, nullptr, "ListContentSetsEx"}, | ||
| 18 | {0x00180000, nullptr, "GetBalance"}, | ||
| 19 | {0x001D0000, nullptr, "GetCustomerSupportCode"}, | ||
| 20 | {0x00210000, nullptr, "Initialize"}, | ||
| 21 | {0x00240282, nullptr, "CalculateContentsRequiredSize"}, | ||
| 22 | {0x00250000, nullptr, "RefreshServerTime"}, | ||
| 23 | }; | ||
| 24 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 25 | // Interface class | ||
| 26 | |||
| 27 | Interface::Interface() { | ||
| 28 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 29 | } | ||
| 30 | |||
| 31 | } // namespace | ||
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h new file mode 100644 index 000000000..2cc673118 --- /dev/null +++ b/src/core/hle/service/nim_aoc.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace NIM_AOC | ||
| 11 | |||
| 12 | namespace NIM_AOC { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "nim:aoc"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index a956ca812..69d2c2002 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm_uds.h | |||
| @@ -21,7 +21,7 @@ public: | |||
| 21 | * Gets the string port name used by CTROS for the service | 21 | * Gets the string port name used by CTROS for the service |
| 22 | * @return Port name of service | 22 | * @return Port name of service |
| 23 | */ | 23 | */ |
| 24 | std::string GetPortName() const { | 24 | std::string GetPortName() const override { |
| 25 | return "nwm:UDS"; | 25 | return "nwm:UDS"; |
| 26 | } | 26 | } |
| 27 | }; | 27 | }; |
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp new file mode 100644 index 000000000..90e9b1bfa --- /dev/null +++ b/src/core/hle/service/pm_app.cpp | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/pm_app.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace PM_APP | ||
| 11 | |||
| 12 | namespace PM_APP { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010140, nullptr, "LaunchTitle"}, | ||
| 16 | {0x00020082, nullptr, "LaunchFIRMSetParams"}, | ||
| 17 | {0x00030080, nullptr, "TerminateProcesse"}, | ||
| 18 | {0x00040100, nullptr, "TerminateProcessTID"}, | ||
| 19 | {0x000500C0, nullptr, "TerminateProcessTID_unknown"}, | ||
| 20 | {0x00070042, nullptr, "GetFIRMLaunchParams"}, | ||
| 21 | {0x00080100, nullptr, "GetTitleExheaderFlags"}, | ||
| 22 | {0x00090042, nullptr, "SetFIRMLaunchParams"}, | ||
| 23 | }; | ||
| 24 | |||
| 25 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 26 | // Interface class | ||
| 27 | |||
| 28 | Interface::Interface() { | ||
| 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 30 | } | ||
| 31 | |||
| 32 | Interface::~Interface() { | ||
| 33 | } | ||
| 34 | |||
| 35 | } // namespace | ||
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h new file mode 100644 index 000000000..28c38f582 --- /dev/null +++ b/src/core/hle/service/pm_app.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace PM_APP | ||
| 11 | |||
| 12 | namespace PM_APP { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | ~Interface(); | ||
| 18 | /** | ||
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const override { | ||
| 23 | return "pm:app"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index f6a14d509..b8c0f6da8 100644 --- a/src/core/hle/service/ptm_u.cpp +++ b/src/core/hle/service/ptm_u.cpp | |||
| @@ -11,19 +11,105 @@ | |||
| 11 | 11 | ||
| 12 | namespace PTM_U { | 12 | namespace PTM_U { |
| 13 | 13 | ||
| 14 | /// Charge levels used by PTM functions | ||
| 15 | enum class ChargeLevels : u32 { | ||
| 16 | CriticalBattery = 1, | ||
| 17 | LowBattery = 2, | ||
| 18 | HalfFull = 3, | ||
| 19 | MostlyFull = 4, | ||
| 20 | CompletelyFull = 5, | ||
| 21 | }; | ||
| 22 | |||
| 23 | static bool shell_open = true; | ||
| 24 | |||
| 25 | static bool battery_is_charging = true; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * It is unknown if GetAdapterState is the same as GetBatteryChargeState, | ||
| 29 | * it is likely to just be a duplicate function of GetBatteryChargeState | ||
| 30 | * that controls another part of the HW. | ||
| 31 | * PTM_U::GetAdapterState service function | ||
| 32 | * Outputs: | ||
| 33 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 34 | * 2 : Output of function, 0 = not charging, 1 = charging. | ||
| 35 | */ | ||
| 36 | static void GetAdapterState(Service::Interface* self) { | ||
| 37 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 38 | |||
| 39 | // TODO(purpasmart96): This function is only a stub, | ||
| 40 | // it returns a valid result without implementing full functionality. | ||
| 41 | |||
| 42 | cmd_buff[1] = 0; // No error | ||
| 43 | cmd_buff[2] = battery_is_charging ? 1 : 0; | ||
| 44 | |||
| 45 | LOG_WARNING(Service_PTM, "(STUBBED) called"); | ||
| 46 | } | ||
| 47 | |||
| 48 | /* | ||
| 49 | * PTM_User::GetShellState service function. | ||
| 50 | * Outputs: | ||
| 51 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 52 | * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) | ||
| 53 | */ | ||
| 54 | static void GetShellState(Service::Interface* self) { | ||
| 55 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 56 | |||
| 57 | cmd_buff[1] = 0; | ||
| 58 | cmd_buff[2] = shell_open ? 1 : 0; | ||
| 59 | |||
| 60 | LOG_TRACE(Service_PTM, "PTM_U::GetShellState called"); | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * PTM_U::GetBatteryLevel service function | ||
| 65 | * Outputs: | ||
| 66 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 67 | * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery, | ||
| 68 | * 3 = half full battery, 2 = low battery, 1 = critical battery. | ||
| 69 | */ | ||
| 70 | static void GetBatteryLevel(Service::Interface* self) { | ||
| 71 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 72 | |||
| 73 | // TODO(purpasmart96): This function is only a stub, | ||
| 74 | // it returns a valid result without implementing full functionality. | ||
| 75 | |||
| 76 | cmd_buff[1] = 0; // No error | ||
| 77 | cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery | ||
| 78 | |||
| 79 | LOG_WARNING(Service_PTM, "(STUBBED) called"); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * PTM_U::GetBatteryChargeState service function | ||
| 84 | * Outputs: | ||
| 85 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 86 | * 2 : Output of function, 0 = not charging, 1 = charging. | ||
| 87 | */ | ||
| 88 | static void GetBatteryChargeState(Service::Interface* self) { | ||
| 89 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 90 | |||
| 91 | // TODO(purpasmart96): This function is only a stub, | ||
| 92 | // it returns a valid result without implementing full functionality. | ||
| 93 | |||
| 94 | cmd_buff[1] = 0; // No error | ||
| 95 | cmd_buff[2] = battery_is_charging ? 1 : 0; | ||
| 96 | |||
| 97 | LOG_WARNING(Service_PTM, "(STUBBED) called"); | ||
| 98 | } | ||
| 99 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 100 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010002, nullptr, "RegisterAlarmClient"}, | 101 | {0x00010002, nullptr, "RegisterAlarmClient"}, |
| 16 | {0x00020080, nullptr, "SetRtcAlarm"}, | 102 | {0x00020080, nullptr, "SetRtcAlarm"}, |
| 17 | {0x00030000, nullptr, "GetRtcAlarm"}, | 103 | {0x00030000, nullptr, "GetRtcAlarm"}, |
| 18 | {0x00040000, nullptr, "CancelRtcAlarm"}, | 104 | {0x00040000, nullptr, "CancelRtcAlarm"}, |
| 19 | {0x00050000, nullptr, "GetAdapterState"}, | 105 | {0x00050000, GetAdapterState, "GetAdapterState"}, |
| 20 | {0x00060000, nullptr, "GetShellState "}, | 106 | {0x00060000, GetShellState, "GetShellState"}, |
| 21 | {0x00070000, nullptr, "GetBatteryLevel"}, | 107 | {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, |
| 22 | {0x00080000, nullptr, "GetBatteryChargeState"}, | 108 | {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, |
| 23 | {0x00090000, nullptr, "GetPedometerState"}, | 109 | {0x00090000, nullptr, "GetPedometerState"}, |
| 24 | {0x000A0042, nullptr, "GetStepHistoryEntry"}, | 110 | {0x000A0042, nullptr, "GetStepHistoryEntry"}, |
| 25 | {0x000B00C2, nullptr, "GetStepHistory "}, | 111 | {0x000B00C2, nullptr, "GetStepHistory"}, |
| 26 | {0x000C0000, nullptr, "GetTotalStepCount "}, | 112 | {0x000C0000, nullptr, "GetTotalStepCount"}, |
| 27 | {0x000D0040, nullptr, "SetPedometerRecordingMode"}, | 113 | {0x000D0040, nullptr, "SetPedometerRecordingMode"}, |
| 28 | {0x000E0000, nullptr, "GetPedometerRecordingMode"}, | 114 | {0x000E0000, nullptr, "GetPedometerRecordingMode"}, |
| 29 | {0x000F0084, nullptr, "GetStepHistoryAll"}, | 115 | {0x000F0084, nullptr, "GetStepHistoryAll"}, |
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h index 82749fa39..f8d9f57be 100644 --- a/src/core/hle/service/ptm_u.h +++ b/src/core/hle/service/ptm_u.h | |||
| @@ -21,7 +21,7 @@ public: | |||
| 21 | * Gets the string port name used by CTROS for the service | 21 | * Gets the string port name used by CTROS for the service |
| 22 | * @return Port name of service | 22 | * @return Port name of service |
| 23 | */ | 23 | */ |
| 24 | std::string GetPortName() const { | 24 | std::string GetPortName() const override { |
| 25 | return "ptm:u"; | 25 | return "ptm:u"; |
| 26 | } | 26 | } |
| 27 | }; | 27 | }; |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index b144a77d4..2230045e3 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -7,16 +7,28 @@ | |||
| 7 | 7 | ||
| 8 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 9 | #include "core/hle/service/ac_u.h" | 9 | #include "core/hle/service/ac_u.h" |
| 10 | #include "core/hle/service/am_app.h" | ||
| 11 | #include "core/hle/service/am_net.h" | ||
| 10 | #include "core/hle/service/apt_u.h" | 12 | #include "core/hle/service/apt_u.h" |
| 13 | #include "core/hle/service/boss_u.h" | ||
| 14 | #include "core/hle/service/cecd_u.h" | ||
| 15 | #include "core/hle/service/cfg_i.h" | ||
| 11 | #include "core/hle/service/cfg_u.h" | 16 | #include "core/hle/service/cfg_u.h" |
| 17 | #include "core/hle/service/csnd_snd.h" | ||
| 12 | #include "core/hle/service/dsp_dsp.h" | 18 | #include "core/hle/service/dsp_dsp.h" |
| 13 | #include "core/hle/service/err_f.h" | 19 | #include "core/hle/service/err_f.h" |
| 14 | #include "core/hle/service/fs_user.h" | 20 | #include "core/hle/service/fs/fs_user.h" |
| 21 | #include "core/hle/service/frd_u.h" | ||
| 15 | #include "core/hle/service/gsp_gpu.h" | 22 | #include "core/hle/service/gsp_gpu.h" |
| 16 | #include "core/hle/service/hid_user.h" | 23 | #include "core/hle/service/hid_user.h" |
| 24 | #include "core/hle/service/ir_rst.h" | ||
| 25 | #include "core/hle/service/ir_u.h" | ||
| 26 | #include "core/hle/service/ldr_ro.h" | ||
| 17 | #include "core/hle/service/mic_u.h" | 27 | #include "core/hle/service/mic_u.h" |
| 28 | #include "core/hle/service/nim_aoc.h" | ||
| 18 | #include "core/hle/service/ndm_u.h" | 29 | #include "core/hle/service/ndm_u.h" |
| 19 | #include "core/hle/service/nwm_uds.h" | 30 | #include "core/hle/service/nwm_uds.h" |
| 31 | #include "core/hle/service/pm_app.h" | ||
| 20 | #include "core/hle/service/ptm_u.h" | 32 | #include "core/hle/service/ptm_u.h" |
| 21 | #include "core/hle/service/soc_u.h" | 33 | #include "core/hle/service/soc_u.h" |
| 22 | #include "core/hle/service/srv.h" | 34 | #include "core/hle/service/srv.h" |
| @@ -54,7 +66,7 @@ void Manager::DeleteService(const std::string& port_name) { | |||
| 54 | 66 | ||
| 55 | /// Get a Service Interface from its Handle | 67 | /// Get a Service Interface from its Handle |
| 56 | Interface* Manager::FetchFromHandle(Handle handle) { | 68 | Interface* Manager::FetchFromHandle(Handle handle) { |
| 57 | return Kernel::g_object_pool.GetFast<Interface>(handle); | 69 | return Kernel::g_object_pool.Get<Interface>(handle); |
| 58 | } | 70 | } |
| 59 | 71 | ||
| 60 | /// Get a Service Interface from its port | 72 | /// Get a Service Interface from its port |
| @@ -73,30 +85,42 @@ Interface* Manager::FetchFromPortName(const std::string& port_name) { | |||
| 73 | /// Initialize ServiceManager | 85 | /// Initialize ServiceManager |
| 74 | void Init() { | 86 | void Init() { |
| 75 | g_manager = new Manager; | 87 | g_manager = new Manager; |
| 76 | 88 | ||
| 77 | g_manager->AddService(new SRV::Interface); | 89 | g_manager->AddService(new SRV::Interface); |
| 78 | g_manager->AddService(new AC_U::Interface); | 90 | g_manager->AddService(new AC_U::Interface); |
| 91 | g_manager->AddService(new AM_APP::Interface); | ||
| 92 | g_manager->AddService(new AM_NET::Interface); | ||
| 79 | g_manager->AddService(new APT_U::Interface); | 93 | g_manager->AddService(new APT_U::Interface); |
| 94 | g_manager->AddService(new BOSS_U::Interface); | ||
| 95 | g_manager->AddService(new CECD_U::Interface); | ||
| 96 | g_manager->AddService(new CFG_I::Interface); | ||
| 80 | g_manager->AddService(new CFG_U::Interface); | 97 | g_manager->AddService(new CFG_U::Interface); |
| 98 | g_manager->AddService(new CSND_SND::Interface); | ||
| 81 | g_manager->AddService(new DSP_DSP::Interface); | 99 | g_manager->AddService(new DSP_DSP::Interface); |
| 82 | g_manager->AddService(new ERR_F::Interface); | 100 | g_manager->AddService(new ERR_F::Interface); |
| 83 | g_manager->AddService(new FS_User::Interface); | 101 | g_manager->AddService(new FRD_U::Interface); |
| 102 | g_manager->AddService(new FS::FSUserInterface); | ||
| 84 | g_manager->AddService(new GSP_GPU::Interface); | 103 | g_manager->AddService(new GSP_GPU::Interface); |
| 85 | g_manager->AddService(new HID_User::Interface); | 104 | g_manager->AddService(new HID_User::Interface); |
| 105 | g_manager->AddService(new IR_RST::Interface); | ||
| 106 | g_manager->AddService(new IR_U::Interface); | ||
| 107 | g_manager->AddService(new LDR_RO::Interface); | ||
| 86 | g_manager->AddService(new MIC_U::Interface); | 108 | g_manager->AddService(new MIC_U::Interface); |
| 109 | g_manager->AddService(new NIM_AOC::Interface); | ||
| 87 | g_manager->AddService(new NDM_U::Interface); | 110 | g_manager->AddService(new NDM_U::Interface); |
| 88 | g_manager->AddService(new NWM_UDS::Interface); | 111 | g_manager->AddService(new NWM_UDS::Interface); |
| 112 | g_manager->AddService(new PM_APP::Interface); | ||
| 89 | g_manager->AddService(new PTM_U::Interface); | 113 | g_manager->AddService(new PTM_U::Interface); |
| 90 | g_manager->AddService(new SOC_U::Interface); | 114 | g_manager->AddService(new SOC_U::Interface); |
| 91 | g_manager->AddService(new SSL_C::Interface); | 115 | g_manager->AddService(new SSL_C::Interface); |
| 92 | 116 | ||
| 93 | NOTICE_LOG(HLE, "initialized OK"); | 117 | LOG_DEBUG(Service, "initialized OK"); |
| 94 | } | 118 | } |
| 95 | 119 | ||
| 96 | /// Shutdown ServiceManager | 120 | /// Shutdown ServiceManager |
| 97 | void Shutdown() { | 121 | void Shutdown() { |
| 98 | delete g_manager; | 122 | delete g_manager; |
| 99 | NOTICE_LOG(HLE, "shutdown OK"); | 123 | LOG_DEBUG(Service, "shutdown OK"); |
| 100 | } | 124 | } |
| 101 | 125 | ||
| 102 | 126 | ||
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 2f5a866c9..9cd906150 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -10,9 +10,11 @@ | |||
| 10 | #include <string> | 10 | #include <string> |
| 11 | 11 | ||
| 12 | #include "common/common.h" | 12 | #include "common/common.h" |
| 13 | #include "common/string_util.h" | ||
| 13 | #include "core/mem_map.h" | 14 | #include "core/mem_map.h" |
| 14 | 15 | ||
| 15 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| 17 | #include "core/hle/kernel/session.h" | ||
| 16 | #include "core/hle/svc.h" | 18 | #include "core/hle/svc.h" |
| 17 | 19 | ||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -20,30 +22,19 @@ | |||
| 20 | 22 | ||
| 21 | namespace Service { | 23 | namespace Service { |
| 22 | 24 | ||
| 23 | static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) | 25 | static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) |
| 24 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Returns a pointer to the command buffer in kernel memory | ||
| 28 | * @param offset Optional offset into command buffer | ||
| 29 | * @return Pointer to command buffer | ||
| 30 | */ | ||
| 31 | inline static u32* GetCommandBuffer(const int offset=0) { | ||
| 32 | return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||
| 33 | } | ||
| 34 | 26 | ||
| 35 | class Manager; | 27 | class Manager; |
| 36 | 28 | ||
| 37 | /// Interface to a CTROS service | 29 | /// Interface to a CTROS service |
| 38 | class Interface : public Kernel::Object { | 30 | class Interface : public Kernel::Session { |
| 31 | // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be | ||
| 32 | // just something that encapsulates a session and acts as a helper to implement service | ||
| 33 | // processes. | ||
| 34 | |||
| 39 | friend class Manager; | 35 | friend class Manager; |
| 40 | public: | 36 | public: |
| 41 | |||
| 42 | std::string GetName() const override { return GetPortName(); } | 37 | std::string GetName() const override { return GetPortName(); } |
| 43 | std::string GetTypeName() const override { return GetPortName(); } | ||
| 44 | |||
| 45 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } | ||
| 46 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; } | ||
| 47 | 38 | ||
| 48 | typedef void (*Function)(Interface*); | 39 | typedef void (*Function)(Interface*); |
| 49 | 40 | ||
| @@ -75,48 +66,31 @@ public: | |||
| 75 | m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); | 66 | m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); |
| 76 | } | 67 | } |
| 77 | 68 | ||
| 78 | /** | 69 | ResultVal<bool> SyncRequest() override { |
| 79 | * Synchronize kernel object | 70 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 80 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 81 | * @return Result of operation, 0 on success, otherwise error code | ||
| 82 | */ | ||
| 83 | Result SyncRequest(bool* wait) override { | ||
| 84 | u32* cmd_buff = GetCommandBuffer(); | ||
| 85 | auto itr = m_functions.find(cmd_buff[0]); | 71 | auto itr = m_functions.find(cmd_buff[0]); |
| 86 | 72 | ||
| 87 | if (itr == m_functions.end()) { | 73 | if (itr == m_functions.end() || itr->second.func == nullptr) { |
| 88 | ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X", | 74 | // Number of params == bits 0-5 + bits 6-11 |
| 89 | GetPortName().c_str(), cmd_buff[0]); | 75 | int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); |
| 90 | 76 | ||
| 91 | // TODO(bunnei): Hack - ignore error | 77 | std::string error = "unknown/unimplemented function '%s': port=%s"; |
| 92 | u32* cmd_buff = Service::GetCommandBuffer(); | 78 | for (int i = 1; i <= num_params; ++i) { |
| 93 | cmd_buff[1] = 0; | 79 | error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); |
| 94 | return 0; | 80 | } |
| 95 | } | 81 | |
| 96 | if (itr->second.func == nullptr) { | 82 | std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; |
| 97 | ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s", | 83 | |
| 98 | GetPortName().c_str(), itr->second.name.c_str()); | 84 | LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str()); |
| 99 | 85 | ||
| 100 | // TODO(bunnei): Hack - ignore error | 86 | // TODO(bunnei): Hack - ignore error |
| 101 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 102 | cmd_buff[1] = 0; | 87 | cmd_buff[1] = 0; |
| 103 | return 0; | 88 | return MakeResult<bool>(false); |
| 104 | } | 89 | } |
| 105 | 90 | ||
| 106 | itr->second.func(this); | 91 | itr->second.func(this); |
| 107 | 92 | ||
| 108 | return 0; // TODO: Implement return from actual function | 93 | return MakeResult<bool>(false); // TODO: Implement return from actual function |
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Wait for kernel object to synchronize | ||
| 113 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 114 | * @return Result of operation, 0 on success, otherwise error code | ||
| 115 | */ | ||
| 116 | Result WaitSynchronization(bool* wait) override { | ||
| 117 | // TODO(bunnei): ImplementMe | ||
| 118 | ERROR_LOG(OSHLE, "unimplemented function"); | ||
| 119 | return 0; | ||
| 120 | } | 94 | } |
| 121 | 95 | ||
| 122 | protected: | 96 | protected: |
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index e27a2b1fe..d5590a683 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h | |||
| @@ -19,7 +19,7 @@ public: | |||
| 19 | * Gets the string port name used by CTROS for the service | 19 | * Gets the string port name used by CTROS for the service |
| 20 | * @return Port name of service | 20 | * @return Port name of service |
| 21 | */ | 21 | */ |
| 22 | std::string GetPortName() const { | 22 | std::string GetPortName() const override { |
| 23 | return "soc:U"; | 23 | return "soc:U"; |
| 24 | } | 24 | } |
| 25 | }; | 25 | }; |
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 6c02a43d9..165fd7aac 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp | |||
| @@ -11,20 +11,20 @@ | |||
| 11 | 11 | ||
| 12 | namespace SRV { | 12 | namespace SRV { |
| 13 | 13 | ||
| 14 | Handle g_event_handle = 0; | 14 | static Handle g_event_handle = 0; |
| 15 | 15 | ||
| 16 | void Initialize(Service::Interface* self) { | 16 | static void Initialize(Service::Interface* self) { |
| 17 | DEBUG_LOG(OSHLE, "called"); | 17 | LOG_DEBUG(Service_SRV, "called"); |
| 18 | 18 | ||
| 19 | u32* cmd_buff = Service::GetCommandBuffer(); | 19 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 20 | 20 | ||
| 21 | cmd_buff[1] = 0; // No error | 21 | cmd_buff[1] = 0; // No error |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | void GetProcSemaphore(Service::Interface* self) { | 24 | static void GetProcSemaphore(Service::Interface* self) { |
| 25 | DEBUG_LOG(OSHLE, "called"); | 25 | LOG_TRACE(Service_SRV, "called"); |
| 26 | 26 | ||
| 27 | u32* cmd_buff = Service::GetCommandBuffer(); | 27 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 28 | 28 | ||
| 29 | // TODO(bunnei): Change to a semaphore once these have been implemented | 29 | // TODO(bunnei): Change to a semaphore once these have been implemented |
| 30 | g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); | 30 | g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); |
| @@ -34,21 +34,21 @@ void GetProcSemaphore(Service::Interface* self) { | |||
| 34 | cmd_buff[3] = g_event_handle; | 34 | cmd_buff[3] = g_event_handle; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | void GetServiceHandle(Service::Interface* self) { | 37 | static void GetServiceHandle(Service::Interface* self) { |
| 38 | Result res = 0; | 38 | ResultCode res = RESULT_SUCCESS; |
| 39 | u32* cmd_buff = Service::GetCommandBuffer(); | 39 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 40 | 40 | ||
| 41 | std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); | 41 | std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); |
| 42 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | 42 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |
| 43 | 43 | ||
| 44 | if (nullptr != service) { | 44 | if (nullptr != service) { |
| 45 | cmd_buff[3] = service->GetHandle(); | 45 | cmd_buff[3] = service->GetHandle(); |
| 46 | DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); | 46 | LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); |
| 47 | } else { | 47 | } else { |
| 48 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); | 48 | LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); |
| 49 | res = -1; | 49 | res = UnimplementedFunction(ErrorModule::SRV); |
| 50 | } | 50 | } |
| 51 | cmd_buff[1] = res; | 51 | cmd_buff[1] = res.raw; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | const Interface::FunctionInfo FunctionTable[] = { | 54 | const Interface::FunctionInfo FunctionTable[] = { |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2aa1303f6..47e9bf77e 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 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 | 6 | ||
| @@ -12,10 +12,12 @@ | |||
| 12 | #include "core/hle/kernel/address_arbiter.h" | 12 | #include "core/hle/kernel/address_arbiter.h" |
| 13 | #include "core/hle/kernel/event.h" | 13 | #include "core/hle/kernel/event.h" |
| 14 | #include "core/hle/kernel/mutex.h" | 14 | #include "core/hle/kernel/mutex.h" |
| 15 | #include "core/hle/kernel/semaphore.h" | ||
| 15 | #include "core/hle/kernel/shared_memory.h" | 16 | #include "core/hle/kernel/shared_memory.h" |
| 16 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 17 | 18 | ||
| 18 | #include "core/hle/function_wrappers.h" | 19 | #include "core/hle/function_wrappers.h" |
| 20 | #include "core/hle/result.h" | ||
| 19 | #include "core/hle/service/service.h" | 21 | #include "core/hle/service/service.h" |
| 20 | 22 | ||
| 21 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 23 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -29,8 +31,8 @@ enum ControlMemoryOperation { | |||
| 29 | }; | 31 | }; |
| 30 | 32 | ||
| 31 | /// Map application or GSP heap memory | 33 | /// Map application or GSP heap memory |
| 32 | Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { | 34 | static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { |
| 33 | DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", | 35 | LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", |
| 34 | operation, addr0, addr1, size, permissions); | 36 | operation, addr0, addr1, size, permissions); |
| 35 | 37 | ||
| 36 | switch (operation) { | 38 | switch (operation) { |
| @@ -42,19 +44,19 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz | |||
| 42 | 44 | ||
| 43 | // Map GSP heap memory | 45 | // Map GSP heap memory |
| 44 | case MEMORY_OPERATION_GSP_HEAP: | 46 | case MEMORY_OPERATION_GSP_HEAP: |
| 45 | *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions); | 47 | *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions); |
| 46 | break; | 48 | break; |
| 47 | 49 | ||
| 48 | // Unknown ControlMemory operation | 50 | // Unknown ControlMemory operation |
| 49 | default: | 51 | default: |
| 50 | ERROR_LOG(SVC, "unknown operation=0x%08X", operation); | 52 | LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); |
| 51 | } | 53 | } |
| 52 | return 0; | 54 | return 0; |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | /// Maps a memory block to specified address | 57 | /// Maps a memory block to specified address |
| 56 | Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { | 58 | static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { |
| 57 | DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", | 59 | LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", |
| 58 | handle, addr, permissions, other_permissions); | 60 | handle, addr, permissions, other_permissions); |
| 59 | 61 | ||
| 60 | Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); | 62 | Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); |
| @@ -67,20 +69,20 @@ Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permis | |||
| 67 | case Kernel::MemoryPermission::WriteExecute: | 69 | case Kernel::MemoryPermission::WriteExecute: |
| 68 | case Kernel::MemoryPermission::ReadWriteExecute: | 70 | case Kernel::MemoryPermission::ReadWriteExecute: |
| 69 | case Kernel::MemoryPermission::DontCare: | 71 | case Kernel::MemoryPermission::DontCare: |
| 70 | Kernel::MapSharedMemory(handle, addr, permissions_type, | 72 | Kernel::MapSharedMemory(handle, addr, permissions_type, |
| 71 | static_cast<Kernel::MemoryPermission>(other_permissions)); | 73 | static_cast<Kernel::MemoryPermission>(other_permissions)); |
| 72 | break; | 74 | break; |
| 73 | default: | 75 | default: |
| 74 | ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions); | 76 | LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); |
| 75 | } | 77 | } |
| 76 | return 0; | 78 | return 0; |
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | /// Connect to an OS service given the port name, returns the handle to the port to out | 81 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 80 | Result ConnectToPort(Handle* out, const char* port_name) { | 82 | static Result ConnectToPort(Handle* out, const char* port_name) { |
| 81 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | 83 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |
| 82 | 84 | ||
| 83 | DEBUG_LOG(SVC, "called port_name=%s", port_name); | 85 | LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); |
| 84 | _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); | 86 | _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); |
| 85 | 87 | ||
| 86 | *out = service->GetHandle(); | 88 | *out = service->GetHandle(); |
| @@ -89,78 +91,80 @@ Result ConnectToPort(Handle* out, const char* port_name) { | |||
| 89 | } | 91 | } |
| 90 | 92 | ||
| 91 | /// Synchronize to an OS service | 93 | /// Synchronize to an OS service |
| 92 | Result SendSyncRequest(Handle handle) { | 94 | static Result SendSyncRequest(Handle handle) { |
| 93 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); | 95 | Kernel::Session* session = Kernel::g_object_pool.Get<Kernel::Session>(handle); |
| 96 | if (session == nullptr) { | ||
| 97 | return InvalidHandle(ErrorModule::Kernel).raw; | ||
| 98 | } | ||
| 94 | 99 | ||
| 95 | _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!"); | 100 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); |
| 96 | DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str()); | ||
| 97 | 101 | ||
| 98 | bool wait = false; | 102 | ResultVal<bool> wait = session->SyncRequest(); |
| 99 | Result res = object->SyncRequest(&wait); | 103 | if (wait.Succeeded() && *wait) { |
| 100 | if (wait) { | ||
| 101 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? | 104 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? |
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | return res; | 107 | return wait.Code().raw; |
| 105 | } | 108 | } |
| 106 | 109 | ||
| 107 | /// Close a handle | 110 | /// Close a handle |
| 108 | Result CloseHandle(Handle handle) { | 111 | static Result CloseHandle(Handle handle) { |
| 109 | // ImplementMe | 112 | // ImplementMe |
| 110 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); | 113 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); |
| 111 | return 0; | 114 | return 0; |
| 112 | } | 115 | } |
| 113 | 116 | ||
| 114 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds | 117 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds |
| 115 | Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | 118 | static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { |
| 116 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this | 119 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this |
| 117 | bool wait = false; | ||
| 118 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated | 120 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated |
| 119 | 121 | ||
| 122 | if (!Kernel::g_object_pool.IsValid(handle)) { | ||
| 123 | return InvalidHandle(ErrorModule::Kernel).raw; | ||
| 124 | } | ||
| 120 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); | 125 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); |
| 126 | _dbg_assert_(Kernel, object != nullptr); | ||
| 121 | 127 | ||
| 122 | DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), | 128 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), |
| 123 | object->GetName().c_str(), nano_seconds); | 129 | object->GetName().c_str(), nano_seconds); |
| 124 | 130 | ||
| 125 | _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!"); | 131 | ResultVal<bool> wait = object->WaitSynchronization(); |
| 126 | |||
| 127 | Result res = object->WaitSynchronization(&wait); | ||
| 128 | 132 | ||
| 129 | // Check for next thread to schedule | 133 | // Check for next thread to schedule |
| 130 | if (wait) { | 134 | if (wait.Succeeded() && *wait) { |
| 131 | HLE::Reschedule(__func__); | 135 | HLE::Reschedule(__func__); |
| 132 | return 0; | ||
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | return res; | 138 | return wait.Code().raw; |
| 136 | } | 139 | } |
| 137 | 140 | ||
| 138 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 141 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 139 | Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, | 142 | static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, |
| 140 | s64 nano_seconds) { | 143 | s64 nano_seconds) { |
| 141 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this | 144 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this |
| 142 | bool unlock_all = true; | 145 | bool unlock_all = true; |
| 143 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated | 146 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated |
| 144 | 147 | ||
| 145 | DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", | 148 | LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", |
| 146 | handle_count, (wait_all ? "true" : "false"), nano_seconds); | 149 | handle_count, (wait_all ? "true" : "false"), nano_seconds); |
| 147 | 150 | ||
| 148 | // Iterate through each handle, synchronize kernel object | 151 | // Iterate through each handle, synchronize kernel object |
| 149 | for (s32 i = 0; i < handle_count; i++) { | 152 | for (s32 i = 0; i < handle_count; i++) { |
| 150 | bool wait = false; | 153 | if (!Kernel::g_object_pool.IsValid(handles[i])) { |
| 154 | return InvalidHandle(ErrorModule::Kernel).raw; | ||
| 155 | } | ||
| 151 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]); | 156 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]); |
| 152 | 157 | ||
| 153 | _assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object " | 158 | LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), |
| 154 | "is nullptr!", handles[i]); | ||
| 155 | |||
| 156 | DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), | ||
| 157 | object->GetName().c_str()); | 159 | object->GetName().c_str()); |
| 158 | 160 | ||
| 159 | Result res = object->WaitSynchronization(&wait); | 161 | // TODO(yuriks): Verify how the real function behaves when an error happens here |
| 162 | ResultVal<bool> wait_result = object->WaitSynchronization(); | ||
| 163 | bool wait = wait_result.Succeeded() && *wait_result; | ||
| 160 | 164 | ||
| 161 | if (!wait && !wait_all) { | 165 | if (!wait && !wait_all) { |
| 162 | *out = i; | 166 | *out = i; |
| 163 | return 0; | 167 | return RESULT_SUCCESS.raw; |
| 164 | } else { | 168 | } else { |
| 165 | unlock_all = false; | 169 | unlock_all = false; |
| 166 | } | 170 | } |
| @@ -168,55 +172,57 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa | |||
| 168 | 172 | ||
| 169 | if (wait_all && unlock_all) { | 173 | if (wait_all && unlock_all) { |
| 170 | *out = handle_count; | 174 | *out = handle_count; |
| 171 | return 0; | 175 | return RESULT_SUCCESS.raw; |
| 172 | } | 176 | } |
| 173 | 177 | ||
| 174 | // Check for next thread to schedule | 178 | // Check for next thread to schedule |
| 175 | HLE::Reschedule(__func__); | 179 | HLE::Reschedule(__func__); |
| 176 | 180 | ||
| 177 | return 0; | 181 | return RESULT_SUCCESS.raw; |
| 178 | } | 182 | } |
| 179 | 183 | ||
| 180 | /// Create an address arbiter (to allocate access to shared resources) | 184 | /// Create an address arbiter (to allocate access to shared resources) |
| 181 | Result CreateAddressArbiter(u32* arbiter) { | 185 | static Result CreateAddressArbiter(u32* arbiter) { |
| 182 | DEBUG_LOG(SVC, "called"); | 186 | LOG_TRACE(Kernel_SVC, "called"); |
| 183 | Handle handle = Kernel::CreateAddressArbiter(); | 187 | Handle handle = Kernel::CreateAddressArbiter(); |
| 184 | *arbiter = handle; | 188 | *arbiter = handle; |
| 185 | return 0; | 189 | return 0; |
| 186 | } | 190 | } |
| 187 | 191 | ||
| 188 | /// Arbitrate address | 192 | /// Arbitrate address |
| 189 | Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { | 193 | static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { |
| 190 | return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address, | 194 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter, |
| 191 | value); | 195 | address, type, value); |
| 196 | return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), | ||
| 197 | address, value).raw; | ||
| 192 | } | 198 | } |
| 193 | 199 | ||
| 194 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 200 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 195 | void OutputDebugString(const char* string) { | 201 | static void OutputDebugString(const char* string) { |
| 196 | OS_LOG(SVC, "%s", string); | 202 | LOG_DEBUG(Debug_Emulated, "%s", string); |
| 197 | } | 203 | } |
| 198 | 204 | ||
| 199 | /// Get resource limit | 205 | /// Get resource limit |
| 200 | Result GetResourceLimit(Handle* resource_limit, Handle process) { | 206 | static Result GetResourceLimit(Handle* resource_limit, Handle process) { |
| 201 | // With regards to proceess values: | 207 | // With regards to proceess values: |
| 202 | // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for | 208 | // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for |
| 203 | // the current KThread. | 209 | // the current KThread. |
| 204 | *resource_limit = 0xDEADBEEF; | 210 | *resource_limit = 0xDEADBEEF; |
| 205 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process); | 211 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process); |
| 206 | return 0; | 212 | return 0; |
| 207 | } | 213 | } |
| 208 | 214 | ||
| 209 | /// Get resource limit current values | 215 | /// Get resource limit current values |
| 210 | Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, | 216 | static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, |
| 211 | s32 name_count) { | 217 | s32 name_count) { |
| 212 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", | 218 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", |
| 213 | resource_limit, names, name_count); | 219 | resource_limit, names, name_count); |
| 214 | Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now | 220 | Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now |
| 215 | return 0; | 221 | return 0; |
| 216 | } | 222 | } |
| 217 | 223 | ||
| 218 | /// Creates a new thread | 224 | /// Creates a new thread |
| 219 | Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { | 225 | static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { |
| 220 | std::string name; | 226 | std::string name; |
| 221 | if (Symbols::HasSymbol(entry_point)) { | 227 | if (Symbols::HasSymbol(entry_point)) { |
| 222 | TSymbol symbol = Symbols::GetSymbol(entry_point); | 228 | TSymbol symbol = Symbols::GetSymbol(entry_point); |
| @@ -230,18 +236,18 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p | |||
| 230 | 236 | ||
| 231 | Core::g_app_core->SetReg(1, thread); | 237 | Core::g_app_core->SetReg(1, thread); |
| 232 | 238 | ||
| 233 | DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 239 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| 234 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, | 240 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, |
| 235 | name.c_str(), arg, stack_top, priority, processor_id, thread); | 241 | name.c_str(), arg, stack_top, priority, processor_id, thread); |
| 236 | 242 | ||
| 237 | return 0; | 243 | return 0; |
| 238 | } | 244 | } |
| 239 | 245 | ||
| 240 | /// Called when a thread exits | 246 | /// Called when a thread exits |
| 241 | u32 ExitThread() { | 247 | static u32 ExitThread() { |
| 242 | Handle thread = Kernel::GetCurrentThreadHandle(); | 248 | Handle thread = Kernel::GetCurrentThreadHandle(); |
| 243 | 249 | ||
| 244 | DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C | 250 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C |
| 245 | 251 | ||
| 246 | Kernel::StopThread(thread, __func__); | 252 | Kernel::StopThread(thread, __func__); |
| 247 | HLE::Reschedule(__func__); | 253 | HLE::Reschedule(__func__); |
| @@ -249,55 +255,73 @@ u32 ExitThread() { | |||
| 249 | } | 255 | } |
| 250 | 256 | ||
| 251 | /// Gets the priority for the specified thread | 257 | /// Gets the priority for the specified thread |
| 252 | Result GetThreadPriority(s32* priority, Handle handle) { | 258 | static Result GetThreadPriority(s32* priority, Handle handle) { |
| 253 | *priority = Kernel::GetThreadPriority(handle); | 259 | ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle); |
| 254 | return 0; | 260 | if (priority_result.Succeeded()) { |
| 261 | *priority = *priority_result; | ||
| 262 | } | ||
| 263 | return priority_result.Code().raw; | ||
| 255 | } | 264 | } |
| 256 | 265 | ||
| 257 | /// Sets the priority for the specified thread | 266 | /// Sets the priority for the specified thread |
| 258 | Result SetThreadPriority(Handle handle, s32 priority) { | 267 | static Result SetThreadPriority(Handle handle, s32 priority) { |
| 259 | return Kernel::SetThreadPriority(handle, priority); | 268 | return Kernel::SetThreadPriority(handle, priority).raw; |
| 260 | } | 269 | } |
| 261 | 270 | ||
| 262 | /// Create a mutex | 271 | /// Create a mutex |
| 263 | Result CreateMutex(Handle* mutex, u32 initial_locked) { | 272 | static Result CreateMutex(Handle* mutex, u32 initial_locked) { |
| 264 | *mutex = Kernel::CreateMutex((initial_locked != 0)); | 273 | *mutex = Kernel::CreateMutex((initial_locked != 0)); |
| 265 | DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X", | 274 | LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", |
| 266 | initial_locked ? "true" : "false", *mutex); | 275 | initial_locked ? "true" : "false", *mutex); |
| 267 | return 0; | 276 | return 0; |
| 268 | } | 277 | } |
| 269 | 278 | ||
| 270 | /// Release a mutex | 279 | /// Release a mutex |
| 271 | Result ReleaseMutex(Handle handle) { | 280 | static Result ReleaseMutex(Handle handle) { |
| 272 | DEBUG_LOG(SVC, "called handle=0x%08X", handle); | 281 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle); |
| 273 | _assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!"); | 282 | ResultCode res = Kernel::ReleaseMutex(handle); |
| 274 | Kernel::ReleaseMutex(handle); | 283 | return res.raw; |
| 275 | return 0; | ||
| 276 | } | 284 | } |
| 277 | 285 | ||
| 278 | /// Get current thread ID | 286 | /// Get the ID for the specified thread. |
| 279 | Result GetThreadId(u32* thread_id, Handle thread) { | 287 | static Result GetThreadId(u32* thread_id, Handle handle) { |
| 280 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread); | 288 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); |
| 281 | return 0; | 289 | ResultCode result = Kernel::GetThreadId(thread_id, handle); |
| 290 | return result.raw; | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Creates a semaphore | ||
| 294 | static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) { | ||
| 295 | ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count); | ||
| 296 | LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X", | ||
| 297 | initial_count, max_count, *semaphore); | ||
| 298 | return res.raw; | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Releases a certain number of slots in a semaphore | ||
| 302 | static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) { | ||
| 303 | LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore); | ||
| 304 | ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count); | ||
| 305 | return res.raw; | ||
| 282 | } | 306 | } |
| 283 | 307 | ||
| 284 | /// Query memory | 308 | /// Query memory |
| 285 | Result QueryMemory(void* info, void* out, u32 addr) { | 309 | static Result QueryMemory(void* info, void* out, u32 addr) { |
| 286 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); | 310 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); |
| 287 | return 0; | 311 | return 0; |
| 288 | } | 312 | } |
| 289 | 313 | ||
| 290 | /// Create an event | 314 | /// Create an event |
| 291 | Result CreateEvent(Handle* evt, u32 reset_type) { | 315 | static Result CreateEvent(Handle* evt, u32 reset_type) { |
| 292 | *evt = Kernel::CreateEvent((ResetType)reset_type); | 316 | *evt = Kernel::CreateEvent((ResetType)reset_type); |
| 293 | DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X", | 317 | LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", |
| 294 | reset_type, *evt); | 318 | reset_type, *evt); |
| 295 | return 0; | 319 | return 0; |
| 296 | } | 320 | } |
| 297 | 321 | ||
| 298 | /// Duplicates a kernel handle | 322 | /// Duplicates a kernel handle |
| 299 | Result DuplicateHandle(Handle* out, Handle handle) { | 323 | static Result DuplicateHandle(Handle* out, Handle handle) { |
| 300 | DEBUG_LOG(SVC, "called handle=0x%08X", handle); | 324 | LOG_WARNING(Kernel_SVC, "(STUBBED) called handle=0x%08X", handle); |
| 301 | 325 | ||
| 302 | // Translate kernel handles -> real handles | 326 | // Translate kernel handles -> real handles |
| 303 | if (handle == Kernel::CurrentThread) { | 327 | if (handle == Kernel::CurrentThread) { |
| @@ -305,7 +329,7 @@ Result DuplicateHandle(Handle* out, Handle handle) { | |||
| 305 | } | 329 | } |
| 306 | _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess), | 330 | _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess), |
| 307 | "(UNIMPLEMENTED) process handle duplication!"); | 331 | "(UNIMPLEMENTED) process handle duplication!"); |
| 308 | 332 | ||
| 309 | // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate. | 333 | // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate. |
| 310 | *out = handle; | 334 | *out = handle; |
| 311 | 335 | ||
| @@ -313,26 +337,27 @@ Result DuplicateHandle(Handle* out, Handle handle) { | |||
| 313 | } | 337 | } |
| 314 | 338 | ||
| 315 | /// Signals an event | 339 | /// Signals an event |
| 316 | Result SignalEvent(Handle evt) { | 340 | static Result SignalEvent(Handle evt) { |
| 317 | Result res = Kernel::SignalEvent(evt); | 341 | LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); |
| 318 | DEBUG_LOG(SVC, "called event=0x%08X", evt); | 342 | return Kernel::SignalEvent(evt).raw; |
| 319 | return res; | ||
| 320 | } | 343 | } |
| 321 | 344 | ||
| 322 | /// Clears an event | 345 | /// Clears an event |
| 323 | Result ClearEvent(Handle evt) { | 346 | static Result ClearEvent(Handle evt) { |
| 324 | Result res = Kernel::ClearEvent(evt); | 347 | LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); |
| 325 | DEBUG_LOG(SVC, "called event=0x%08X", evt); | 348 | return Kernel::ClearEvent(evt).raw; |
| 326 | return res; | ||
| 327 | } | 349 | } |
| 328 | 350 | ||
| 329 | /// Sleep the current thread | 351 | /// Sleep the current thread |
| 330 | void SleepThread(s64 nanoseconds) { | 352 | static void SleepThread(s64 nanoseconds) { |
| 331 | DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds); | 353 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| 354 | |||
| 355 | // Check for next thread to schedule | ||
| 356 | HLE::Reschedule(__func__); | ||
| 332 | } | 357 | } |
| 333 | 358 | ||
| 334 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 359 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| 335 | s64 GetSystemTick() { | 360 | static s64 GetSystemTick() { |
| 336 | return (s64)Core::g_app_core->GetTicks(); | 361 | return (s64)Core::g_app_core->GetTicks(); |
| 337 | } | 362 | } |
| 338 | 363 | ||
| @@ -358,8 +383,8 @@ const HLE::FunctionDef SVC_Table[] = { | |||
| 358 | {0x12, nullptr, "Run"}, | 383 | {0x12, nullptr, "Run"}, |
| 359 | {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, | 384 | {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, |
| 360 | {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, | 385 | {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, |
| 361 | {0x15, nullptr, "CreateSemaphore"}, | 386 | {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"}, |
| 362 | {0x16, nullptr, "ReleaseSemaphore"}, | 387 | {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"}, |
| 363 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, | 388 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, |
| 364 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, | 389 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, |
| 365 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, | 390 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, |
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index 1d125faf6..6be393d0b 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 3ad801c63..da78b85e5 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp | |||
| @@ -35,7 +35,7 @@ inline void Read(T &var, const u32 raw_addr) { | |||
| 35 | 35 | ||
| 36 | // Reads other than u32 are untested, so I'd rather have them abort than silently fail | 36 | // Reads other than u32 are untested, so I'd rather have them abort than silently fail |
| 37 | if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { | 37 | if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { |
| 38 | ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); | 38 | LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); |
| 39 | return; | 39 | return; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| @@ -49,7 +49,7 @@ inline void Write(u32 addr, const T data) { | |||
| 49 | 49 | ||
| 50 | // Writes other than u32 are untested, so I'd rather have them abort than silently fail | 50 | // Writes other than u32 are untested, so I'd rather have them abort than silently fail |
| 51 | if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { | 51 | if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { |
| 52 | ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); | 52 | LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); |
| 53 | return; | 53 | return; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| @@ -73,7 +73,7 @@ inline void Write(u32 addr, const T data) { | |||
| 73 | for (u32* ptr = start; ptr < end; ++ptr) | 73 | for (u32* ptr = start; ptr < end; ++ptr) |
| 74 | *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation | 74 | *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation |
| 75 | 75 | ||
| 76 | DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); | 76 | LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); |
| 77 | } | 77 | } |
| 78 | break; | 78 | break; |
| 79 | } | 79 | } |
| @@ -105,7 +105,7 @@ inline void Write(u32 addr, const T data) { | |||
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | default: | 107 | default: |
| 108 | ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value()); | 108 | LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value()); |
| 109 | break; | 109 | break; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| @@ -132,16 +132,16 @@ inline void Write(u32 addr, const T data) { | |||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | default: | 134 | default: |
| 135 | ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); | 135 | LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); |
| 136 | break; | 136 | break; |
| 137 | } | 137 | } |
| 138 | } | 138 | } |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", | 141 | LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", |
| 142 | config.output_height * config.output_width * 4, | 142 | config.output_height * config.output_width * 4, |
| 143 | config.GetPhysicalInputAddress(), config.input_width, config.input_height, | 143 | config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height, |
| 144 | config.GetPhysicalOutputAddress(), config.output_width, config.output_height, | 144 | config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height, |
| 145 | config.output_format.Value()); | 145 | config.output_format.Value()); |
| 146 | } | 146 | } |
| 147 | break; | 147 | break; |
| @@ -154,8 +154,7 @@ inline void Write(u32 addr, const T data) { | |||
| 154 | if (config.trigger & 1) | 154 | if (config.trigger & 1) |
| 155 | { | 155 | { |
| 156 | u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); | 156 | u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); |
| 157 | u32 size = config.size << 3; | 157 | Pica::CommandProcessor::ProcessCommandList(buffer, config.size); |
| 158 | Pica::CommandProcessor::ProcessCommandList(buffer, size); | ||
| 159 | } | 158 | } |
| 160 | break; | 159 | break; |
| 161 | } | 160 | } |
| @@ -252,12 +251,12 @@ void Init() { | |||
| 252 | framebuffer_sub.color_format = Regs::PixelFormat::RGB8; | 251 | framebuffer_sub.color_format = Regs::PixelFormat::RGB8; |
| 253 | framebuffer_sub.active_fb = 0; | 252 | framebuffer_sub.active_fb = 0; |
| 254 | 253 | ||
| 255 | NOTICE_LOG(GPU, "initialized OK"); | 254 | LOG_DEBUG(HW_GPU, "initialized OK"); |
| 256 | } | 255 | } |
| 257 | 256 | ||
| 258 | /// Shutdown hardware | 257 | /// Shutdown hardware |
| 259 | void Shutdown() { | 258 | void Shutdown() { |
| 260 | NOTICE_LOG(GPU, "shutdown OK"); | 259 | LOG_DEBUG(HW_GPU, "shutdown OK"); |
| 261 | } | 260 | } |
| 262 | 261 | ||
| 263 | } // namespace | 262 | } // namespace |
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 3fa7b9ccf..86cd5e680 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h | |||
| @@ -169,7 +169,7 @@ struct Regs { | |||
| 169 | INSERT_PADDING_WORDS(0x331); | 169 | INSERT_PADDING_WORDS(0x331); |
| 170 | 170 | ||
| 171 | struct { | 171 | struct { |
| 172 | // command list size | 172 | // command list size (in bytes) |
| 173 | u32 size; | 173 | u32 size; |
| 174 | 174 | ||
| 175 | INSERT_PADDING_WORDS(0x1); | 175 | INSERT_PADDING_WORDS(0x1); |
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index 33f75c50a..af42b41fb 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hw/hw.h" | 7 | #include "core/hw/hw.h" |
| 8 | #include "core/hw/gpu.h" | 8 | #include "core/hw/gpu.h" |
| 9 | #include "core/hw/ndma.h" | ||
| 10 | 9 | ||
| 11 | namespace HW { | 10 | namespace HW { |
| 12 | 11 | ||
| @@ -39,36 +38,26 @@ enum { | |||
| 39 | template <typename T> | 38 | template <typename T> |
| 40 | inline void Read(T &var, const u32 addr) { | 39 | inline void Read(T &var, const u32 addr) { |
| 41 | switch (addr & 0xFFFFF000) { | 40 | switch (addr & 0xFFFFF000) { |
| 42 | |||
| 43 | // TODO(bunnei): What is the virtual address of NDMA? | ||
| 44 | // case VADDR_NDMA: | ||
| 45 | // NDMA::Read(var, addr); | ||
| 46 | // break; | ||
| 47 | 41 | ||
| 48 | case VADDR_GPU: | 42 | case VADDR_GPU: |
| 49 | GPU::Read(var, addr); | 43 | GPU::Read(var, addr); |
| 50 | break; | 44 | break; |
| 51 | 45 | ||
| 52 | default: | 46 | default: |
| 53 | ERROR_LOG(HW, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); | 47 | LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); |
| 54 | } | 48 | } |
| 55 | } | 49 | } |
| 56 | 50 | ||
| 57 | template <typename T> | 51 | template <typename T> |
| 58 | inline void Write(u32 addr, const T data) { | 52 | inline void Write(u32 addr, const T data) { |
| 59 | switch (addr & 0xFFFFF000) { | 53 | switch (addr & 0xFFFFF000) { |
| 60 | |||
| 61 | // TODO(bunnei): What is the virtual address of NDMA? | ||
| 62 | // case VADDR_NDMA | ||
| 63 | // NDMA::Write(addr, data); | ||
| 64 | // break; | ||
| 65 | 54 | ||
| 66 | case VADDR_GPU: | 55 | case VADDR_GPU: |
| 67 | GPU::Write(addr, data); | 56 | GPU::Write(addr, data); |
| 68 | break; | 57 | break; |
| 69 | 58 | ||
| 70 | default: | 59 | default: |
| 71 | ERROR_LOG(HW, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); | 60 | LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); |
| 72 | } | 61 | } |
| 73 | } | 62 | } |
| 74 | 63 | ||
| @@ -87,19 +76,17 @@ template void Write<u8>(u32 addr, const u8 data); | |||
| 87 | /// Update hardware | 76 | /// Update hardware |
| 88 | void Update() { | 77 | void Update() { |
| 89 | GPU::Update(); | 78 | GPU::Update(); |
| 90 | NDMA::Update(); | ||
| 91 | } | 79 | } |
| 92 | 80 | ||
| 93 | /// Initialize hardware | 81 | /// Initialize hardware |
| 94 | void Init() { | 82 | void Init() { |
| 95 | GPU::Init(); | 83 | GPU::Init(); |
| 96 | NDMA::Init(); | 84 | LOG_DEBUG(HW, "initialized OK"); |
| 97 | NOTICE_LOG(HW, "initialized OK"); | ||
| 98 | } | 85 | } |
| 99 | 86 | ||
| 100 | /// Shutdown hardware | 87 | /// Shutdown hardware |
| 101 | void Shutdown() { | 88 | void Shutdown() { |
| 102 | NOTICE_LOG(HW, "shutdown OK"); | 89 | LOG_DEBUG(HW, "shutdown OK"); |
| 103 | } | 90 | } |
| 104 | 91 | ||
| 105 | } \ No newline at end of file | 92 | } \ No newline at end of file |
diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp deleted file mode 100644 index e29a773f1..000000000 --- a/src/core/hw/ndma.cpp +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | |||
| 7 | #include "core/hw/ndma.h" | ||
| 8 | |||
| 9 | namespace NDMA { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | inline void Read(T &var, const u32 addr) { | ||
| 13 | ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); | ||
| 14 | } | ||
| 15 | |||
| 16 | template <typename T> | ||
| 17 | inline void Write(u32 addr, const T data) { | ||
| 18 | ERROR_LOG(NDMA, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); | ||
| 19 | } | ||
| 20 | |||
| 21 | // Explicitly instantiate template functions because we aren't defining this in the header: | ||
| 22 | |||
| 23 | template void Read<u64>(u64 &var, const u32 addr); | ||
| 24 | template void Read<u32>(u32 &var, const u32 addr); | ||
| 25 | template void Read<u16>(u16 &var, const u32 addr); | ||
| 26 | template void Read<u8>(u8 &var, const u32 addr); | ||
| 27 | |||
| 28 | template void Write<u64>(u32 addr, const u64 data); | ||
| 29 | template void Write<u32>(u32 addr, const u32 data); | ||
| 30 | template void Write<u16>(u32 addr, const u16 data); | ||
| 31 | template void Write<u8>(u32 addr, const u8 data); | ||
| 32 | |||
| 33 | /// Update hardware | ||
| 34 | void Update() { | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Initialize hardware | ||
| 38 | void Init() { | ||
| 39 | NOTICE_LOG(GPU, "initialized OK"); | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Shutdown hardware | ||
| 43 | void Shutdown() { | ||
| 44 | NOTICE_LOG(GPU, "shutdown OK"); | ||
| 45 | } | ||
| 46 | |||
| 47 | } // namespace | ||
diff --git a/src/core/hw/ndma.h b/src/core/hw/ndma.h deleted file mode 100644 index d8fa3d40b..000000000 --- a/src/core/hw/ndma.h +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace NDMA { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | inline void Read(T &var, const u32 addr); | ||
| 13 | |||
| 14 | template <typename T> | ||
| 15 | inline void Write(u32 addr, const T data); | ||
| 16 | |||
| 17 | /// Update hardware | ||
| 18 | void Update(); | ||
| 19 | |||
| 20 | /// Initialize hardware | ||
| 21 | void Init(); | ||
| 22 | |||
| 23 | /// Shutdown hardware | ||
| 24 | void Shutdown(); | ||
| 25 | |||
| 26 | } // namespace | ||
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp new file mode 100644 index 000000000..0437e5374 --- /dev/null +++ b/src/core/loader/3dsx.cpp | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | #include "core/file_sys/archive_romfs.h" | ||
| 9 | #include "core/loader/elf.h" | ||
| 10 | #include "core/loader/ncch.h" | ||
| 11 | #include "core/hle/service/fs/archive.h" | ||
| 12 | #include "core/mem_map.h" | ||
| 13 | |||
| 14 | #include "3dsx.h" | ||
| 15 | |||
| 16 | |||
| 17 | namespace Loader { | ||
| 18 | |||
| 19 | |||
| 20 | /** | ||
| 21 | * File layout: | ||
| 22 | * - File header | ||
| 23 | * - Code, rodata and data relocation table headers | ||
| 24 | * - Code segment | ||
| 25 | * - Rodata segment | ||
| 26 | * - Loadable (non-BSS) part of the data segment | ||
| 27 | * - Code relocation table | ||
| 28 | * - Rodata relocation table | ||
| 29 | * - Data relocation table | ||
| 30 | * | ||
| 31 | * Memory layout before relocations are applied: | ||
| 32 | * [0..codeSegSize) -> code segment | ||
| 33 | * [codeSegSize..rodataSegSize) -> rodata segment | ||
| 34 | * [rodataSegSize..dataSegSize) -> data segment | ||
| 35 | * | ||
| 36 | * Memory layout after relocations are applied: well, however the loader sets it up :) | ||
| 37 | * The entrypoint is always the start of the code segment. | ||
| 38 | * The BSS section must be cleared manually by the application. | ||
| 39 | */ | ||
| 40 | enum THREEDSX_Error { | ||
| 41 | ERROR_NONE = 0, | ||
| 42 | ERROR_READ = 1, | ||
| 43 | ERROR_FILE = 2, | ||
| 44 | ERROR_ALLOC = 3 | ||
| 45 | }; | ||
| 46 | static const u32 RELOCBUFSIZE = 512; | ||
| 47 | |||
| 48 | // File header | ||
| 49 | static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX' | ||
| 50 | #pragma pack(1) | ||
| 51 | struct THREEDSX_Header | ||
| 52 | { | ||
| 53 | u32 magic; | ||
| 54 | u16 header_size, reloc_hdr_size; | ||
| 55 | u32 format_ver; | ||
| 56 | u32 flags; | ||
| 57 | |||
| 58 | // Sizes of the code, rodata and data segments + | ||
| 59 | // size of the BSS section (uninitialized latter half of the data segment) | ||
| 60 | u32 code_seg_size, rodata_seg_size, data_seg_size, bss_size; | ||
| 61 | }; | ||
| 62 | |||
| 63 | // Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts. | ||
| 64 | struct THREEDSX_RelocHdr | ||
| 65 | { | ||
| 66 | // # of absolute relocations (that is, fix address to post-relocation memory layout) | ||
| 67 | u32 cross_segment_absolute; | ||
| 68 | // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched) | ||
| 69 | u32 cross_segment_relative; | ||
| 70 | // more? | ||
| 71 | |||
| 72 | // Relocations are written in this order: | ||
| 73 | // - Absolute relocations | ||
| 74 | // - Relative relocations | ||
| 75 | }; | ||
| 76 | |||
| 77 | // Relocation entry: from the current pointer, skip X words and patch Y words | ||
| 78 | struct THREEDSX_Reloc | ||
| 79 | { | ||
| 80 | u16 skip, patch; | ||
| 81 | }; | ||
| 82 | #pragma pack() | ||
| 83 | |||
| 84 | struct THREEloadinfo | ||
| 85 | { | ||
| 86 | u8* seg_ptrs[3]; // code, rodata & data | ||
| 87 | u32 seg_addrs[3]; | ||
| 88 | u32 seg_sizes[3]; | ||
| 89 | }; | ||
| 90 | |||
| 91 | class THREEDSXReader { | ||
| 92 | public: | ||
| 93 | static int Load3DSXFile(const std::string& filename, u32 base_addr); | ||
| 94 | }; | ||
| 95 | |||
| 96 | static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets) | ||
| 97 | { | ||
| 98 | if (addr < offsets[0]) | ||
| 99 | return loadinfo->seg_addrs[0] + addr; | ||
| 100 | if (addr < offsets[1]) | ||
| 101 | return loadinfo->seg_addrs[1] + addr - offsets[0]; | ||
| 102 | return loadinfo->seg_addrs[2] + addr - offsets[1]; | ||
| 103 | } | ||
| 104 | |||
| 105 | int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | ||
| 106 | { | ||
| 107 | FileUtil::IOFile file(filename, "rb"); | ||
| 108 | if (!file.IsOpen()) { | ||
| 109 | return ERROR_FILE; | ||
| 110 | } | ||
| 111 | THREEDSX_Header hdr; | ||
| 112 | if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) | ||
| 113 | return ERROR_READ; | ||
| 114 | |||
| 115 | THREEloadinfo loadinfo; | ||
| 116 | //loadinfo segments must be a multiple of 0x1000 | ||
| 117 | loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) &~0xFFF; | ||
| 118 | loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF; | ||
| 119 | loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF; | ||
| 120 | u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] }; | ||
| 121 | u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF; | ||
| 122 | u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size; | ||
| 123 | u32 n_reloc_tables = hdr.reloc_hdr_size / 4; | ||
| 124 | std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables); | ||
| 125 | |||
| 126 | loadinfo.seg_addrs[0] = base_addr; | ||
| 127 | loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; | ||
| 128 | loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; | ||
| 129 | loadinfo.seg_ptrs[0] = &all_mem[0]; | ||
| 130 | loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; | ||
| 131 | loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; | ||
| 132 | |||
| 133 | // Skip header for future compatibility | ||
| 134 | file.Seek(hdr.header_size, SEEK_SET); | ||
| 135 | |||
| 136 | // Read the relocation headers | ||
| 137 | u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); | ||
| 138 | |||
| 139 | for (u32 current_segment = 0; current_segment < 3; current_segment++) { | ||
| 140 | if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4) | ||
| 141 | return ERROR_READ; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Read the segments | ||
| 145 | if (file.ReadBytes(loadinfo.seg_ptrs[0], hdr.code_seg_size) != hdr.code_seg_size) | ||
| 146 | return ERROR_READ; | ||
| 147 | if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size) | ||
| 148 | return ERROR_READ; | ||
| 149 | if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != hdr.data_seg_size - hdr.bss_size) | ||
| 150 | return ERROR_READ; | ||
| 151 | |||
| 152 | // BSS clear | ||
| 153 | memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); | ||
| 154 | |||
| 155 | // Relocate the segments | ||
| 156 | for (u32 current_segment = 0; current_segment < 3; current_segment++) { | ||
| 157 | for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { | ||
| 158 | u32 n_relocs = relocs[current_segment*n_reloc_tables + current_segment_reloc_table]; | ||
| 159 | if (current_segment_reloc_table >= 2) { | ||
| 160 | // We are not using this table - ignore it because we don't know what it dose | ||
| 161 | file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); | ||
| 162 | continue; | ||
| 163 | } | ||
| 164 | static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; | ||
| 165 | |||
| 166 | u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; | ||
| 167 | u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); | ||
| 168 | |||
| 169 | while (n_relocs) { | ||
| 170 | u32 remaining = std::min(RELOCBUFSIZE, n_relocs); | ||
| 171 | n_relocs -= remaining; | ||
| 172 | |||
| 173 | if (file.ReadBytes(reloc_table, remaining*sizeof(THREEDSX_Reloc)) != remaining*sizeof(THREEDSX_Reloc)) | ||
| 174 | return ERROR_READ; | ||
| 175 | |||
| 176 | for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { | ||
| 177 | LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", | ||
| 178 | current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch); | ||
| 179 | pos += reloc_table[current_inprogress].skip; | ||
| 180 | s32 num_patches = reloc_table[current_inprogress].patch; | ||
| 181 | while (0 < num_patches && pos < end_pos) { | ||
| 182 | u32 in_addr = (char*)pos - (char*)&all_mem[0]; | ||
| 183 | u32 addr = TranslateAddr(*pos, &loadinfo, offsets); | ||
| 184 | LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", | ||
| 185 | base_addr + in_addr, addr, current_segment_reloc_table, *pos); | ||
| 186 | switch (current_segment_reloc_table) { | ||
| 187 | case 0: *pos = (addr); break; | ||
| 188 | case 1: *pos = (addr - in_addr); break; | ||
| 189 | default: break; //this should never happen | ||
| 190 | } | ||
| 191 | pos++; | ||
| 192 | num_patches--; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | // Write the data | ||
| 200 | memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); | ||
| 201 | |||
| 202 | LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000); | ||
| 203 | LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000); | ||
| 204 | LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000); | ||
| 205 | LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000); | ||
| 206 | |||
| 207 | return ERROR_NONE; | ||
| 208 | } | ||
| 209 | |||
| 210 | /// AppLoader_DSX constructor | ||
| 211 | AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) { | ||
| 212 | } | ||
| 213 | |||
| 214 | /// AppLoader_DSX destructor | ||
| 215 | AppLoader_THREEDSX::~AppLoader_THREEDSX() { | ||
| 216 | } | ||
| 217 | |||
| 218 | /** | ||
| 219 | * Loads a 3DSX file | ||
| 220 | * @return Success on success, otherwise Error | ||
| 221 | */ | ||
| 222 | ResultStatus AppLoader_THREEDSX::Load() { | ||
| 223 | LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); | ||
| 224 | FileUtil::IOFile file(filename, "rb"); | ||
| 225 | if (file.IsOpen()) { | ||
| 226 | |||
| 227 | THREEDSXReader reader; | ||
| 228 | reader.Load3DSXFile(filename, 0x00100000); | ||
| 229 | Kernel::LoadExec(0x00100000); | ||
| 230 | } else { | ||
| 231 | return ResultStatus::Error; | ||
| 232 | } | ||
| 233 | return ResultStatus::Success; | ||
| 234 | } | ||
| 235 | |||
| 236 | } // namespace Loader | ||
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h new file mode 100644 index 000000000..848d3ef8a --- /dev/null +++ b/src/core/loader/3dsx.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project / Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "core/loader/loader.h" | ||
| 9 | |||
| 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 11 | // Loader namespace | ||
| 12 | |||
| 13 | namespace Loader { | ||
| 14 | |||
| 15 | /// Loads an 3DSX file | ||
| 16 | class AppLoader_THREEDSX final : public AppLoader { | ||
| 17 | public: | ||
| 18 | AppLoader_THREEDSX(const std::string& filename); | ||
| 19 | ~AppLoader_THREEDSX() override; | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Load the bootable file | ||
| 23 | * @return ResultStatus result of function | ||
| 24 | */ | ||
| 25 | ResultStatus Load() override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | std::string filename; | ||
| 29 | bool is_loaded; | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace Loader | ||
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 389d5a8c9..c95882f4a 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -254,18 +254,18 @@ const char *ElfReader::GetSectionName(int section) const { | |||
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | bool ElfReader::LoadInto(u32 vaddr) { | 256 | bool ElfReader::LoadInto(u32 vaddr) { |
| 257 | DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); | 257 | LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); |
| 258 | 258 | ||
| 259 | // Should we relocate? | 259 | // Should we relocate? |
| 260 | relocate = (header->e_type != ET_EXEC); | 260 | relocate = (header->e_type != ET_EXEC); |
| 261 | 261 | ||
| 262 | if (relocate) { | 262 | if (relocate) { |
| 263 | DEBUG_LOG(MASTER_LOG, "Relocatable module"); | 263 | LOG_DEBUG(Loader, "Relocatable module"); |
| 264 | entryPoint += vaddr; | 264 | entryPoint += vaddr; |
| 265 | } else { | 265 | } else { |
| 266 | DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); | 266 | LOG_DEBUG(Loader, "Prerelocated executable"); |
| 267 | } | 267 | } |
| 268 | INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum); | 268 | LOG_DEBUG(Loader, "%i segments:", header->e_phnum); |
| 269 | 269 | ||
| 270 | // First pass : Get the bits into RAM | 270 | // First pass : Get the bits into RAM |
| 271 | u32 segment_addr[32]; | 271 | u32 segment_addr[32]; |
| @@ -273,17 +273,17 @@ bool ElfReader::LoadInto(u32 vaddr) { | |||
| 273 | 273 | ||
| 274 | for (int i = 0; i < header->e_phnum; i++) { | 274 | for (int i = 0; i < header->e_phnum; i++) { |
| 275 | Elf32_Phdr *p = segments + i; | 275 | Elf32_Phdr *p = segments + i; |
| 276 | INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, | 276 | LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, |
| 277 | p->p_filesz, p->p_memsz); | 277 | p->p_filesz, p->p_memsz); |
| 278 | 278 | ||
| 279 | if (p->p_type == PT_LOAD) { | 279 | if (p->p_type == PT_LOAD) { |
| 280 | segment_addr[i] = base_addr + p->p_vaddr; | 280 | segment_addr[i] = base_addr + p->p_vaddr; |
| 281 | memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); | 281 | memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); |
| 282 | INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], | 282 | LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], |
| 283 | p->p_memsz); | 283 | p->p_memsz); |
| 284 | } | 284 | } |
| 285 | } | 285 | } |
| 286 | INFO_LOG(MASTER_LOG, "Done loading."); | 286 | LOG_DEBUG(Loader, "Done loading."); |
| 287 | return true; | 287 | return true; |
| 288 | } | 288 | } |
| 289 | 289 | ||
| @@ -346,7 +346,7 @@ AppLoader_ELF::~AppLoader_ELF() { | |||
| 346 | * @return True on success, otherwise false | 346 | * @return True on success, otherwise false |
| 347 | */ | 347 | */ |
| 348 | ResultStatus AppLoader_ELF::Load() { | 348 | ResultStatus AppLoader_ELF::Load() { |
| 349 | INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); | 349 | LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str()); |
| 350 | 350 | ||
| 351 | if (is_loaded) | 351 | if (is_loaded) |
| 352 | return ResultStatus::ErrorAlreadyLoaded; | 352 | return ResultStatus::ErrorAlreadyLoaded; |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index a268e021a..480274d23 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -5,9 +5,10 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | 6 | ||
| 7 | #include "core/file_sys/archive_romfs.h" | 7 | #include "core/file_sys/archive_romfs.h" |
| 8 | #include "core/loader/3dsx.h" | ||
| 8 | #include "core/loader/elf.h" | 9 | #include "core/loader/elf.h" |
| 9 | #include "core/loader/ncch.h" | 10 | #include "core/loader/ncch.h" |
| 10 | #include "core/hle/kernel/archive.h" | 11 | #include "core/hle/service/fs/archive.h" |
| 11 | #include "core/mem_map.h" | 12 | #include "core/mem_map.h" |
| 12 | 13 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -22,7 +23,7 @@ namespace Loader { | |||
| 22 | */ | 23 | */ |
| 23 | FileType IdentifyFile(const std::string &filename) { | 24 | FileType IdentifyFile(const std::string &filename) { |
| 24 | if (filename.size() == 0) { | 25 | if (filename.size() == 0) { |
| 25 | ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); | 26 | LOG_ERROR(Loader, "invalid filename %s", filename.c_str()); |
| 26 | return FileType::Error; | 27 | return FileType::Error; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| @@ -42,6 +43,8 @@ FileType IdentifyFile(const std::string &filename) { | |||
| 42 | return FileType::CCI; | 43 | return FileType::CCI; |
| 43 | } else if (extension == ".bin") { | 44 | } else if (extension == ".bin") { |
| 44 | return FileType::BIN; | 45 | return FileType::BIN; |
| 46 | } else if (extension == ".3dsx") { | ||
| 47 | return FileType::THREEDSX; | ||
| 45 | } | 48 | } |
| 46 | return FileType::Unknown; | 49 | return FileType::Unknown; |
| 47 | } | 50 | } |
| @@ -52,10 +55,14 @@ FileType IdentifyFile(const std::string &filename) { | |||
| 52 | * @return ResultStatus result of function | 55 | * @return ResultStatus result of function |
| 53 | */ | 56 | */ |
| 54 | ResultStatus LoadFile(const std::string& filename) { | 57 | ResultStatus LoadFile(const std::string& filename) { |
| 55 | INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); | 58 | LOG_INFO(Loader, "Loading file %s...", filename.c_str()); |
| 56 | 59 | ||
| 57 | switch (IdentifyFile(filename)) { | 60 | switch (IdentifyFile(filename)) { |
| 58 | 61 | ||
| 62 | //3DSX file format... | ||
| 63 | case FileType::THREEDSX: | ||
| 64 | return AppLoader_THREEDSX(filename).Load(); | ||
| 65 | |||
| 59 | // Standard ELF file format... | 66 | // Standard ELF file format... |
| 60 | case FileType::ELF: | 67 | case FileType::ELF: |
| 61 | return AppLoader_ELF(filename).Load(); | 68 | return AppLoader_ELF(filename).Load(); |
| @@ -67,7 +74,8 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 67 | 74 | ||
| 68 | // Load application and RomFS | 75 | // Load application and RomFS |
| 69 | if (ResultStatus::Success == app_loader.Load()) { | 76 | if (ResultStatus::Success == app_loader.Load()) { |
| 70 | Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS"); | 77 | Kernel::g_program_id = app_loader.GetProgramId(); |
| 78 | Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); | ||
| 71 | return ResultStatus::Success; | 79 | return ResultStatus::Success; |
| 72 | } | 80 | } |
| 73 | break; | 81 | break; |
| @@ -76,7 +84,7 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 76 | // Raw BIN file format... | 84 | // Raw BIN file format... |
| 77 | case FileType::BIN: | 85 | case FileType::BIN: |
| 78 | { | 86 | { |
| 79 | INFO_LOG(LOADER, "Loading BIN file %s...", filename.c_str()); | 87 | LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str()); |
| 80 | 88 | ||
| 81 | FileUtil::IOFile file(filename, "rb"); | 89 | FileUtil::IOFile file(filename, "rb"); |
| 82 | 90 | ||
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 68f843005..0f836d285 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -22,6 +22,7 @@ enum class FileType { | |||
| 22 | CIA, | 22 | CIA, |
| 23 | ELF, | 23 | ELF, |
| 24 | BIN, | 24 | BIN, |
| 25 | THREEDSX, //3DSX | ||
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 27 | /// Return type for functions in Loader namespace | 28 | /// Return type for functions in Loader namespace |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 1e5501e6d..4d23656ec 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -24,7 +24,7 @@ static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) | |||
| 24 | * @param size Size of compressed buffer | 24 | * @param size Size of compressed buffer |
| 25 | * @return Size of decompressed buffer | 25 | * @return Size of decompressed buffer |
| 26 | */ | 26 | */ |
| 27 | u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | 27 | static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { |
| 28 | u32 offset_size = *(u32*)(buffer + size - 4); | 28 | u32 offset_size = *(u32*)(buffer + size - 4); |
| 29 | return offset_size + size; | 29 | return offset_size + size; |
| 30 | } | 30 | } |
| @@ -37,7 +37,7 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | |||
| 37 | * @param decompressed_size Size of decompressed buffer | 37 | * @param decompressed_size Size of decompressed buffer |
| 38 | * @return True on success, otherwise false | 38 | * @return True on success, otherwise false |
| 39 | */ | 39 | */ |
| 40 | bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { | 40 | static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { |
| 41 | u8* footer = compressed + compressed_size - 8; | 41 | u8* footer = compressed + compressed_size - 8; |
| 42 | u32 buffer_top_and_bottom = *(u32*)footer; | 42 | u32 buffer_top_and_bottom = *(u32*)footer; |
| 43 | u32 out = decompressed_size; | 43 | u32 out = decompressed_size; |
| @@ -118,7 +118,7 @@ AppLoader_NCCH::~AppLoader_NCCH() { | |||
| 118 | * @return ResultStatus result of function | 118 | * @return ResultStatus result of function |
| 119 | */ | 119 | */ |
| 120 | ResultStatus AppLoader_NCCH::LoadExec() const { | 120 | ResultStatus AppLoader_NCCH::LoadExec() const { |
| 121 | if (!is_loaded) | 121 | if (!is_loaded) |
| 122 | return ResultStatus::ErrorNotLoaded; | 122 | return ResultStatus::ErrorNotLoaded; |
| 123 | 123 | ||
| 124 | std::vector<u8> code; | 124 | std::vector<u8> code; |
| @@ -140,13 +140,13 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | |||
| 140 | // Iterate through the ExeFs archive until we find the .code file... | 140 | // Iterate through the ExeFs archive until we find the .code file... |
| 141 | FileUtil::IOFile file(filename, "rb"); | 141 | FileUtil::IOFile file(filename, "rb"); |
| 142 | if (file.IsOpen()) { | 142 | if (file.IsOpen()) { |
| 143 | LOG_DEBUG(Loader, "%d sections:", kMaxSections); | ||
| 143 | for (int i = 0; i < kMaxSections; i++) { | 144 | for (int i = 0; i < kMaxSections; i++) { |
| 144 | // Load the specified section... | 145 | // Load the specified section... |
| 145 | if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { | 146 | if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { |
| 146 | INFO_LOG(LOADER, "ExeFS section %d:", i); | 147 | LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i, |
| 147 | INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); | 148 | exefs_header.section[i].offset, exefs_header.section[i].size, |
| 148 | INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); | 149 | exefs_header.section[i].name); |
| 149 | INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); | ||
| 150 | 150 | ||
| 151 | s64 section_offset = (exefs_header.section[i].offset + exefs_offset + | 151 | s64 section_offset = (exefs_header.section[i].offset + exefs_offset + |
| 152 | sizeof(ExeFs_Header)+ncch_offset); | 152 | sizeof(ExeFs_Header)+ncch_offset); |
| @@ -181,11 +181,11 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | |||
| 181 | } | 181 | } |
| 182 | } | 182 | } |
| 183 | } else { | 183 | } else { |
| 184 | ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str()); | 184 | LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); |
| 185 | return ResultStatus::Error; | 185 | return ResultStatus::Error; |
| 186 | } | 186 | } |
| 187 | return ResultStatus::ErrorNotUsed; | 187 | return ResultStatus::ErrorNotUsed; |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | /** | 190 | /** |
| 191 | * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | 191 | * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) |
| @@ -194,7 +194,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | |||
| 194 | * @return True on success, otherwise false | 194 | * @return True on success, otherwise false |
| 195 | */ | 195 | */ |
| 196 | ResultStatus AppLoader_NCCH::Load() { | 196 | ResultStatus AppLoader_NCCH::Load() { |
| 197 | INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); | 197 | LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str()); |
| 198 | 198 | ||
| 199 | if (is_loaded) | 199 | if (is_loaded) |
| 200 | return ResultStatus::ErrorAlreadyLoaded; | 200 | return ResultStatus::ErrorAlreadyLoaded; |
| @@ -205,12 +205,12 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 205 | 205 | ||
| 206 | // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... | 206 | // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... |
| 207 | if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { | 207 | if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { |
| 208 | WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); | 208 | LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); |
| 209 | ncch_offset = 0x4000; | 209 | ncch_offset = 0x4000; |
| 210 | file.Seek(ncch_offset, 0); | 210 | file.Seek(ncch_offset, 0); |
| 211 | file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | 211 | file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | // Verify we are loading the correct file type... | 214 | // Verify we are loading the correct file type... |
| 215 | if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) | 215 | if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) |
| 216 | return ResultStatus::ErrorInvalidFormat; | 216 | return ResultStatus::ErrorInvalidFormat; |
| @@ -222,17 +222,17 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 222 | is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; | 222 | is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; |
| 223 | entry_point = exheader_header.codeset_info.text.address; | 223 | entry_point = exheader_header.codeset_info.text.address; |
| 224 | 224 | ||
| 225 | INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); | 225 | LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name); |
| 226 | INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); | 226 | LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); |
| 227 | INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point); | 227 | LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); |
| 228 | 228 | ||
| 229 | // Read ExeFS... | 229 | // Read ExeFS... |
| 230 | 230 | ||
| 231 | exefs_offset = ncch_header.exefs_offset * kBlockSize; | 231 | exefs_offset = ncch_header.exefs_offset * kBlockSize; |
| 232 | u32 exefs_size = ncch_header.exefs_size * kBlockSize; | 232 | u32 exefs_size = ncch_header.exefs_size * kBlockSize; |
| 233 | 233 | ||
| 234 | INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); | 234 | LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); |
| 235 | INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); | 235 | LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); |
| 236 | 236 | ||
| 237 | file.Seek(exefs_offset + ncch_offset, 0); | 237 | file.Seek(exefs_offset + ncch_offset, 0); |
| 238 | file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); | 238 | file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); |
| @@ -243,7 +243,7 @@ ResultStatus AppLoader_NCCH::Load() { | |||
| 243 | 243 | ||
| 244 | return ResultStatus::Success; | 244 | return ResultStatus::Success; |
| 245 | } else { | 245 | } else { |
| 246 | ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str()); | 246 | LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); |
| 247 | } | 247 | } |
| 248 | return ResultStatus::Error; | 248 | return ResultStatus::Error; |
| 249 | } | 249 | } |
| @@ -297,8 +297,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | |||
| 297 | u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; | 297 | u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; |
| 298 | u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; | 298 | u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; |
| 299 | 299 | ||
| 300 | INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); | 300 | LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); |
| 301 | INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); | 301 | LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); |
| 302 | 302 | ||
| 303 | buffer.resize(romfs_size); | 303 | buffer.resize(romfs_size); |
| 304 | 304 | ||
| @@ -307,12 +307,16 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | |||
| 307 | 307 | ||
| 308 | return ResultStatus::Success; | 308 | return ResultStatus::Success; |
| 309 | } | 309 | } |
| 310 | NOTICE_LOG(LOADER, "RomFS unused"); | 310 | LOG_DEBUG(Loader, "NCCH has no RomFS"); |
| 311 | return ResultStatus::ErrorNotUsed; | 311 | return ResultStatus::ErrorNotUsed; |
| 312 | } else { | 312 | } else { |
| 313 | ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str()); | 313 | LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str()); |
| 314 | } | 314 | } |
| 315 | return ResultStatus::Error; | 315 | return ResultStatus::Error; |
| 316 | } | 316 | } |
| 317 | 317 | ||
| 318 | u64 AppLoader_NCCH::GetProgramId() const { | ||
| 319 | return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]); | ||
| 320 | } | ||
| 321 | |||
| 318 | } // namespace Loader | 322 | } // namespace Loader |
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index f40a258b7..2fe2a7d82 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h | |||
| @@ -191,6 +191,12 @@ public: | |||
| 191 | */ | 191 | */ |
| 192 | ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; | 192 | ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; |
| 193 | 193 | ||
| 194 | /* | ||
| 195 | * Gets the program id from the NCCH header | ||
| 196 | * @return u64 Program id | ||
| 197 | */ | ||
| 198 | u64 GetProgramId() const; | ||
| 199 | |||
| 194 | private: | 200 | private: |
| 195 | 201 | ||
| 196 | /** | 202 | /** |
| @@ -215,7 +221,7 @@ private: | |||
| 215 | u32 entry_point; | 221 | u32 entry_point; |
| 216 | u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header | 222 | u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header |
| 217 | u32 exefs_offset; | 223 | u32 exefs_offset; |
| 218 | 224 | ||
| 219 | NCCH_Header ncch_header; | 225 | NCCH_Header ncch_header; |
| 220 | ExeFs_Header exefs_header; | 226 | ExeFs_Header exefs_header; |
| 221 | ExHeader_Header exheader_header; | 227 | ExHeader_Header exheader_header; |
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index cf12f24d9..d1c44ed24 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp | |||
| @@ -11,38 +11,38 @@ | |||
| 11 | 11 | ||
| 12 | namespace Memory { | 12 | namespace Memory { |
| 13 | 13 | ||
| 14 | u8* g_base = NULL; ///< The base pointer to the auto-mirrored arena. | 14 | u8* g_base = nullptr; ///< The base pointer to the auto-mirrored arena. |
| 15 | 15 | ||
| 16 | MemArena g_arena; ///< The MemArena class | 16 | static MemArena arena; ///< The MemArena class |
| 17 | 17 | ||
| 18 | u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here | 18 | u8* g_exefs_code = nullptr; ///< ExeFS:/.code is loaded here |
| 19 | u8* g_system_mem = NULL; ///< System memory | 19 | u8* g_system_mem = nullptr; ///< System memory |
| 20 | u8* g_heap = NULL; ///< Application heap (main memory) | 20 | u8* g_heap = nullptr; ///< Application heap (main memory) |
| 21 | u8* g_heap_gsp = NULL; ///< GSP heap (main memory) | 21 | u8* g_heap_linear = nullptr; ///< Linear heap |
| 22 | u8* g_vram = NULL; ///< Video memory (VRAM) pointer | 22 | u8* g_vram = nullptr; ///< Video memory (VRAM) pointer |
| 23 | u8* g_shared_mem = NULL; ///< Shared memory | 23 | u8* g_shared_mem = nullptr; ///< Shared memory |
| 24 | u8* g_kernel_mem; ///< Kernel memory | 24 | u8* g_kernel_mem; ///< Kernel memory |
| 25 | 25 | ||
| 26 | u8* g_physical_bootrom = NULL; ///< Bootrom physical memory | 26 | static u8* physical_bootrom = nullptr; ///< Bootrom physical memory |
| 27 | u8* g_uncached_bootrom = NULL; | 27 | static u8* uncached_bootrom = nullptr; |
| 28 | 28 | ||
| 29 | u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here | 29 | static u8* physical_exefs_code = nullptr; ///< Phsical ExeFS:/.code is loaded here |
| 30 | u8* g_physical_system_mem = NULL; ///< System physical memory | 30 | static u8* physical_system_mem = nullptr; ///< System physical memory |
| 31 | u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM) | 31 | static u8* physical_fcram = nullptr; ///< Main physical memory (FCRAM) |
| 32 | u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory | 32 | static u8* physical_heap_gsp = nullptr; ///< GSP heap physical memory |
| 33 | u8* g_physical_vram = NULL; ///< Video physical memory (VRAM) | 33 | static u8* physical_vram = nullptr; ///< Video physical memory (VRAM) |
| 34 | u8* g_physical_shared_mem = NULL; ///< Physical shared memory | 34 | static u8* physical_shared_mem = nullptr; ///< Physical shared memory |
| 35 | u8* g_physical_kernel_mem; ///< Kernel memory | 35 | static u8* physical_kernel_mem; ///< Kernel memory |
| 36 | 36 | ||
| 37 | // We don't declare the IO region in here since its handled by other means. | 37 | // We don't declare the IO region in here since its handled by other means. |
| 38 | static MemoryView g_views[] = { | 38 | static MemoryView g_views[] = { |
| 39 | {&g_exefs_code, &g_physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0}, | 39 | {&g_exefs_code, &physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0}, |
| 40 | {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, | 40 | {&g_vram, &physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, |
| 41 | {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, | 41 | {&g_heap, &physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, |
| 42 | {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, | 42 | {&g_shared_mem, &physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, |
| 43 | {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0}, | 43 | {&g_system_mem, &physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0}, |
| 44 | {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, | 44 | {&g_kernel_mem, &physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, |
| 45 | {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0}, | 45 | {&g_heap_linear, &physical_heap_gsp, HEAP_LINEAR_VADDR, HEAP_LINEAR_SIZE, 0}, |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | /*static MemoryView views[] = | 48 | /*static MemoryView views[] = |
| @@ -69,20 +69,20 @@ void Init() { | |||
| 69 | g_views[i].size = FCRAM_SIZE; | 69 | g_views[i].size = FCRAM_SIZE; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &g_arena); | 72 | g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena); |
| 73 | 73 | ||
| 74 | NOTICE_LOG(MEMMAP, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap, | 74 | LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap, |
| 75 | g_physical_fcram); | 75 | physical_fcram); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | void Shutdown() { | 78 | void Shutdown() { |
| 79 | u32 flags = 0; | 79 | u32 flags = 0; |
| 80 | MemoryMap_Shutdown(g_views, kNumMemViews, flags, &g_arena); | 80 | MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena); |
| 81 | 81 | ||
| 82 | g_arena.ReleaseSpace(); | 82 | arena.ReleaseSpace(); |
| 83 | g_base = NULL; | 83 | g_base = nullptr; |
| 84 | 84 | ||
| 85 | NOTICE_LOG(MEMMAP, "shutdown OK"); | 85 | LOG_DEBUG(HW_Memory, "shutdown OK"); |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | } // namespace | 88 | } // namespace |
diff --git a/src/core/mem_map.h b/src/core/mem_map.h index eed445046..7b750f848 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h | |||
| @@ -16,10 +16,9 @@ typedef u32 PAddr; ///< Represents a pointer in the physical address space. | |||
| 16 | 16 | ||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 18 | 18 | ||
| 19 | enum { | 19 | enum : u32 { |
| 20 | BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size | 20 | BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size |
| 21 | MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size | 21 | MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size |
| 22 | DSP_SIZE = 0x00080000, ///< DSP memory size | ||
| 23 | AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size | 22 | AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size |
| 24 | 23 | ||
| 25 | FCRAM_SIZE = 0x08000000, ///< FCRAM size | 24 | FCRAM_SIZE = 0x08000000, ///< FCRAM size |
| @@ -27,47 +26,42 @@ enum { | |||
| 27 | FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space | 26 | FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space |
| 28 | FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address | 27 | FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address |
| 29 | FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space | 28 | FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space |
| 30 | FCRAM_MASK = (FCRAM_SIZE - 1), ///< FCRAM mask | ||
| 31 | 29 | ||
| 32 | SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size | 30 | SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size |
| 33 | SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory | 31 | SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory |
| 34 | SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE), | 32 | SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE), |
| 35 | SHARED_MEMORY_MASK = (SHARED_MEMORY_SIZE - 1), | 33 | |
| 34 | DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size | ||
| 35 | DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address | ||
| 36 | 36 | ||
| 37 | CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size | 37 | CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size |
| 38 | CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address | 38 | CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address |
| 39 | CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE), | 39 | CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE), |
| 40 | CONFIG_MEMORY_MASK = (CONFIG_MEMORY_SIZE - 1), | ||
| 41 | 40 | ||
| 42 | KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size | 41 | KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size |
| 43 | KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are | 42 | KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are |
| 44 | KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE), | 43 | KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE), |
| 45 | KERNEL_MEMORY_MASK = (KERNEL_MEMORY_SIZE - 1), | ||
| 46 | 44 | ||
| 47 | EXEFS_CODE_SIZE = 0x03F00000, | 45 | EXEFS_CODE_SIZE = 0x03F00000, |
| 48 | EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here | 46 | EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here |
| 49 | EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), | 47 | EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), |
| 50 | EXEFS_CODE_MASK = 0x03FFFFFF, | ||
| 51 | 48 | ||
| 52 | // Region of FCRAM used by system | 49 | // Region of FCRAM used by system |
| 53 | SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB | 50 | SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB |
| 54 | SYSTEM_MEMORY_VADDR = 0x04000000, | 51 | SYSTEM_MEMORY_VADDR = 0x04000000, |
| 55 | SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), | 52 | SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), |
| 56 | SYSTEM_MEMORY_MASK = 0x03FFFFFF, | ||
| 57 | 53 | ||
| 58 | HEAP_SIZE = FCRAM_SIZE, ///< Application heap size | 54 | HEAP_SIZE = FCRAM_SIZE, ///< Application heap size |
| 59 | //HEAP_PADDR = HEAP_GSP_SIZE, | 55 | //HEAP_PADDR = HEAP_GSP_SIZE, |
| 60 | //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), | 56 | //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), |
| 61 | HEAP_VADDR = 0x08000000, | 57 | HEAP_VADDR = 0x08000000, |
| 62 | HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE), | 58 | HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE), |
| 63 | HEAP_MASK = (HEAP_SIZE - 1), | ||
| 64 | 59 | ||
| 65 | HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly? | 60 | HEAP_LINEAR_SIZE = 0x08000000, ///< Linear heap size... TODO: Define correctly? |
| 66 | HEAP_GSP_VADDR = 0x14000000, | 61 | HEAP_LINEAR_VADDR = 0x14000000, |
| 67 | HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE), | 62 | HEAP_LINEAR_VADDR_END = (HEAP_LINEAR_VADDR + HEAP_LINEAR_SIZE), |
| 68 | HEAP_GSP_PADDR = 0x00000000, | 63 | HEAP_LINEAR_PADDR = 0x00000000, |
| 69 | HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE), | 64 | HEAP_LINEAR_PADDR_END = (HEAP_LINEAR_PADDR + HEAP_LINEAR_SIZE), |
| 70 | HEAP_GSP_MASK = (HEAP_GSP_SIZE - 1), | ||
| 71 | 65 | ||
| 72 | HARDWARE_IO_SIZE = 0x01000000, | 66 | HARDWARE_IO_SIZE = 0x01000000, |
| 73 | HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start | 67 | HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start |
| @@ -80,12 +74,10 @@ enum { | |||
| 80 | VRAM_VADDR = 0x1F000000, | 74 | VRAM_VADDR = 0x1F000000, |
| 81 | VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE), | 75 | VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE), |
| 82 | VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE), | 76 | VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE), |
| 83 | VRAM_MASK = 0x007FFFFF, | ||
| 84 | 77 | ||
| 85 | SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader | 78 | SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader |
| 86 | SCRATCHPAD_VADDR_END = 0x10000000, | 79 | SCRATCHPAD_VADDR_END = 0x10000000, |
| 87 | SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space | 80 | SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space |
| 88 | SCRATCHPAD_MASK = (SCRATCHPAD_SIZE - 1), ///< Scratchpad memory mask | ||
| 89 | }; | 81 | }; |
| 90 | 82 | ||
| 91 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 83 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -120,7 +112,7 @@ extern u8 *g_base; | |||
| 120 | // These are guaranteed to point to "low memory" addresses (sub-32-bit). | 112 | // These are guaranteed to point to "low memory" addresses (sub-32-bit). |
| 121 | // 64-bit: Pointers to low-mem (sub-0x10000000) mirror | 113 | // 64-bit: Pointers to low-mem (sub-0x10000000) mirror |
| 122 | // 32-bit: Same as the corresponding physical/virtual pointers. | 114 | // 32-bit: Same as the corresponding physical/virtual pointers. |
| 123 | extern u8* g_heap_gsp; ///< GSP heap (main memory) | 115 | extern u8* g_heap_linear; ///< Linear heap (main memory) |
| 124 | extern u8* g_heap; ///< Application heap (main memory) | 116 | extern u8* g_heap; ///< Application heap (main memory) |
| 125 | extern u8* g_vram; ///< Video memory (VRAM) | 117 | extern u8* g_vram; ///< Video memory (VRAM) |
| 126 | extern u8* g_shared_mem; ///< Shared memory | 118 | extern u8* g_shared_mem; ///< Shared memory |
| @@ -147,6 +139,7 @@ u32 Read16_ZX(VAddr addr); | |||
| 147 | void Write8(VAddr addr, u8 data); | 139 | void Write8(VAddr addr, u8 data); |
| 148 | void Write16(VAddr addr, u16 data); | 140 | void Write16(VAddr addr, u16 data); |
| 149 | void Write32(VAddr addr, u32 data); | 141 | void Write32(VAddr addr, u32 data); |
| 142 | void Write64(VAddr addr, u64 data); | ||
| 150 | 143 | ||
| 151 | void WriteBlock(VAddr addr, const u8* data, size_t size); | 144 | void WriteBlock(VAddr addr, const u8* data, size_t size); |
| 152 | 145 | ||
| @@ -156,7 +149,7 @@ u8* GetPointer(VAddr virtual_address); | |||
| 156 | * Maps a block of memory on the heap | 149 | * Maps a block of memory on the heap |
| 157 | * @param size Size of block in bytes | 150 | * @param size Size of block in bytes |
| 158 | * @param operation Memory map operation type | 151 | * @param operation Memory map operation type |
| 159 | * @param flags Memory allocation flags | 152 | * @param permissions Memory allocation permissions |
| 160 | */ | 153 | */ |
| 161 | u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions); | 154 | u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions); |
| 162 | 155 | ||
| @@ -166,7 +159,7 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions); | |||
| 166 | * @param operation Memory map operation type | 159 | * @param operation Memory map operation type |
| 167 | * @param permissions Control memory permissions | 160 | * @param permissions Control memory permissions |
| 168 | */ | 161 | */ |
| 169 | u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions); | 162 | u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions); |
| 170 | 163 | ||
| 171 | inline const char* GetCharPointer(const VAddr address) { | 164 | inline const char* GetCharPointer(const VAddr address) { |
| 172 | return (const char *)GetPointer(address); | 165 | return (const char *)GetPointer(address); |
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index 90951812b..7f7e77233 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp | |||
| @@ -12,9 +12,9 @@ | |||
| 12 | 12 | ||
| 13 | namespace Memory { | 13 | namespace Memory { |
| 14 | 14 | ||
| 15 | std::map<u32, MemoryBlock> g_heap_map; | 15 | static std::map<u32, MemoryBlock> heap_map; |
| 16 | std::map<u32, MemoryBlock> g_heap_gsp_map; | 16 | static std::map<u32, MemoryBlock> heap_linear_map; |
| 17 | std::map<u32, MemoryBlock> g_shared_map; | 17 | static std::map<u32, MemoryBlock> shared_map; |
| 18 | 18 | ||
| 19 | /// Convert a physical address to virtual address | 19 | /// Convert a physical address to virtual address |
| 20 | VAddr PhysicalToVirtualAddress(const PAddr addr) { | 20 | VAddr PhysicalToVirtualAddress(const PAddr addr) { |
| @@ -28,7 +28,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { | |||
| 28 | return addr - FCRAM_PADDR + FCRAM_VADDR; | 28 | return addr - FCRAM_PADDR + FCRAM_VADDR; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | ERROR_LOG(MEMMAP, "Unknown physical address @ 0x%08x", addr); | 31 | LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr); |
| 32 | return addr; | 32 | return addr; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| @@ -44,7 +44,7 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) { | |||
| 44 | return addr - FCRAM_VADDR + FCRAM_PADDR; | 44 | return addr - FCRAM_VADDR + FCRAM_PADDR; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | ERROR_LOG(MEMMAP, "Unknown virtual address @ 0x%08x", addr); | 47 | LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr); |
| 48 | return addr; | 48 | return addr; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| @@ -56,7 +56,7 @@ inline void Read(T &var, const VAddr vaddr) { | |||
| 56 | 56 | ||
| 57 | // Kernel memory command buffer | 57 | // Kernel memory command buffer |
| 58 | if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { | 58 | if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { |
| 59 | var = *((const T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK]); | 59 | var = *((const T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR]); |
| 60 | 60 | ||
| 61 | // Hardware I/O register reads | 61 | // Hardware I/O register reads |
| 62 | // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space | 62 | // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space |
| @@ -65,23 +65,23 @@ inline void Read(T &var, const VAddr vaddr) { | |||
| 65 | 65 | ||
| 66 | // ExeFS:/.code is loaded here | 66 | // ExeFS:/.code is loaded here |
| 67 | } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { | 67 | } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { |
| 68 | var = *((const T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK]); | 68 | var = *((const T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR]); |
| 69 | 69 | ||
| 70 | // FCRAM - GSP heap | 70 | // FCRAM - linear heap |
| 71 | } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { | 71 | } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) { |
| 72 | var = *((const T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK]); | 72 | var = *((const T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR]); |
| 73 | 73 | ||
| 74 | // FCRAM - application heap | 74 | // FCRAM - application heap |
| 75 | } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { | 75 | } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { |
| 76 | var = *((const T*)&g_heap[vaddr & HEAP_MASK]); | 76 | var = *((const T*)&g_heap[vaddr - HEAP_VADDR]); |
| 77 | 77 | ||
| 78 | // Shared memory | 78 | // Shared memory |
| 79 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { | 79 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { |
| 80 | var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); | 80 | var = *((const T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR]); |
| 81 | 81 | ||
| 82 | // System memory | 82 | // System memory |
| 83 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { | 83 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { |
| 84 | var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]); | 84 | var = *((const T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR]); |
| 85 | 85 | ||
| 86 | // Config memory | 86 | // Config memory |
| 87 | } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { | 87 | } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { |
| @@ -89,10 +89,10 @@ inline void Read(T &var, const VAddr vaddr) { | |||
| 89 | 89 | ||
| 90 | // VRAM | 90 | // VRAM |
| 91 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { | 91 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { |
| 92 | var = *((const T*)&g_vram[vaddr & VRAM_MASK]); | 92 | var = *((const T*)&g_vram[vaddr - VRAM_VADDR]); |
| 93 | 93 | ||
| 94 | } else { | 94 | } else { |
| 95 | ERROR_LOG(MEMMAP, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr); | 95 | LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr); |
| 96 | } | 96 | } |
| 97 | } | 97 | } |
| 98 | 98 | ||
| @@ -101,7 +101,7 @@ inline void Write(const VAddr vaddr, const T data) { | |||
| 101 | 101 | ||
| 102 | // Kernel memory command buffer | 102 | // Kernel memory command buffer |
| 103 | if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { | 103 | if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { |
| 104 | *(T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK] = data; | 104 | *(T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR] = data; |
| 105 | 105 | ||
| 106 | // Hardware I/O register writes | 106 | // Hardware I/O register writes |
| 107 | // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space | 107 | // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space |
| @@ -110,27 +110,27 @@ inline void Write(const VAddr vaddr, const T data) { | |||
| 110 | 110 | ||
| 111 | // ExeFS:/.code is loaded here | 111 | // ExeFS:/.code is loaded here |
| 112 | } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { | 112 | } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { |
| 113 | *(T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK] = data; | 113 | *(T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR] = data; |
| 114 | 114 | ||
| 115 | // FCRAM - GSP heap | 115 | // FCRAM - linear heap |
| 116 | } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { | 116 | } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) { |
| 117 | *(T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK] = data; | 117 | *(T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR] = data; |
| 118 | 118 | ||
| 119 | // FCRAM - application heap | 119 | // FCRAM - application heap |
| 120 | } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { | 120 | } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { |
| 121 | *(T*)&g_heap[vaddr & HEAP_MASK] = data; | 121 | *(T*)&g_heap[vaddr - HEAP_VADDR] = data; |
| 122 | 122 | ||
| 123 | // Shared memory | 123 | // Shared memory |
| 124 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { | 124 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { |
| 125 | *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; | 125 | *(T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR] = data; |
| 126 | 126 | ||
| 127 | // System memory | 127 | // System memory |
| 128 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { | 128 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { |
| 129 | *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data; | 129 | *(T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR] = data; |
| 130 | 130 | ||
| 131 | // VRAM | 131 | // VRAM |
| 132 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { | 132 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { |
| 133 | *(T*)&g_vram[vaddr & VRAM_MASK] = data; | 133 | *(T*)&g_vram[vaddr - VRAM_VADDR] = data; |
| 134 | 134 | ||
| 135 | //} else if ((vaddr & 0xFFF00000) == 0x1FF00000) { | 135 | //} else if ((vaddr & 0xFFF00000) == 0x1FF00000) { |
| 136 | // _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory"); | 136 | // _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory"); |
| @@ -141,41 +141,41 @@ inline void Write(const VAddr vaddr, const T data) { | |||
| 141 | 141 | ||
| 142 | // Error out... | 142 | // Error out... |
| 143 | } else { | 143 | } else { |
| 144 | ERROR_LOG(MEMMAP, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, vaddr); | 144 | LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, vaddr); |
| 145 | } | 145 | } |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | u8 *GetPointer(const VAddr vaddr) { | 148 | u8 *GetPointer(const VAddr vaddr) { |
| 149 | // Kernel memory command buffer | 149 | // Kernel memory command buffer |
| 150 | if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { | 150 | if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { |
| 151 | return g_kernel_mem + (vaddr & KERNEL_MEMORY_MASK); | 151 | return g_kernel_mem + (vaddr - KERNEL_MEMORY_VADDR); |
| 152 | 152 | ||
| 153 | // ExeFS:/.code is loaded here | 153 | // ExeFS:/.code is loaded here |
| 154 | } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { | 154 | } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { |
| 155 | return g_exefs_code + (vaddr & EXEFS_CODE_MASK); | 155 | return g_exefs_code + (vaddr - EXEFS_CODE_VADDR); |
| 156 | 156 | ||
| 157 | // FCRAM - GSP heap | 157 | // FCRAM - linear heap |
| 158 | } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { | 158 | } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) { |
| 159 | return g_heap_gsp + (vaddr & HEAP_GSP_MASK); | 159 | return g_heap_linear + (vaddr - HEAP_LINEAR_VADDR); |
| 160 | 160 | ||
| 161 | // FCRAM - application heap | 161 | // FCRAM - application heap |
| 162 | } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { | 162 | } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { |
| 163 | return g_heap + (vaddr & HEAP_MASK); | 163 | return g_heap + (vaddr - HEAP_VADDR); |
| 164 | 164 | ||
| 165 | // Shared memory | 165 | // Shared memory |
| 166 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { | 166 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { |
| 167 | return g_shared_mem + (vaddr & SHARED_MEMORY_MASK); | 167 | return g_shared_mem + (vaddr - SHARED_MEMORY_VADDR); |
| 168 | 168 | ||
| 169 | // System memory | 169 | // System memory |
| 170 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { | 170 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { |
| 171 | return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK); | 171 | return g_system_mem + (vaddr - SYSTEM_MEMORY_VADDR); |
| 172 | 172 | ||
| 173 | // VRAM | 173 | // VRAM |
| 174 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { | 174 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { |
| 175 | return g_vram + (vaddr & VRAM_MASK); | 175 | return g_vram + (vaddr - VRAM_VADDR); |
| 176 | 176 | ||
| 177 | } else { | 177 | } else { |
| 178 | ERROR_LOG(MEMMAP, "unknown GetPointer @ 0x%08x", vaddr); | 178 | LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr); |
| 179 | return 0; | 179 | return 0; |
| 180 | } | 180 | } |
| 181 | } | 181 | } |
| @@ -194,34 +194,34 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) { | |||
| 194 | block.operation = operation; | 194 | block.operation = operation; |
| 195 | block.permissions = permissions; | 195 | block.permissions = permissions; |
| 196 | 196 | ||
| 197 | if (g_heap_map.size() > 0) { | 197 | if (heap_map.size() > 0) { |
| 198 | const MemoryBlock last_block = g_heap_map.rbegin()->second; | 198 | const MemoryBlock last_block = heap_map.rbegin()->second; |
| 199 | block.address = last_block.address + last_block.size; | 199 | block.address = last_block.address + last_block.size; |
| 200 | } | 200 | } |
| 201 | g_heap_map[block.GetVirtualAddress()] = block; | 201 | heap_map[block.GetVirtualAddress()] = block; |
| 202 | 202 | ||
| 203 | return block.GetVirtualAddress(); | 203 | return block.GetVirtualAddress(); |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | /** | 206 | /** |
| 207 | * Maps a block of memory on the GSP heap | 207 | * Maps a block of memory on the linear heap |
| 208 | * @param size Size of block in bytes | 208 | * @param size Size of block in bytes |
| 209 | * @param operation Memory map operation type | 209 | * @param operation Memory map operation type |
| 210 | * @param flags Memory allocation flags | 210 | * @param flags Memory allocation flags |
| 211 | */ | 211 | */ |
| 212 | u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions) { | 212 | u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) { |
| 213 | MemoryBlock block; | 213 | MemoryBlock block; |
| 214 | 214 | ||
| 215 | block.base_address = HEAP_GSP_VADDR; | 215 | block.base_address = HEAP_LINEAR_VADDR; |
| 216 | block.size = size; | 216 | block.size = size; |
| 217 | block.operation = operation; | 217 | block.operation = operation; |
| 218 | block.permissions = permissions; | 218 | block.permissions = permissions; |
| 219 | 219 | ||
| 220 | if (g_heap_gsp_map.size() > 0) { | 220 | if (heap_linear_map.size() > 0) { |
| 221 | const MemoryBlock last_block = g_heap_gsp_map.rbegin()->second; | 221 | const MemoryBlock last_block = heap_linear_map.rbegin()->second; |
| 222 | block.address = last_block.address + last_block.size; | 222 | block.address = last_block.address + last_block.size; |
| 223 | } | 223 | } |
| 224 | g_heap_gsp_map[block.GetVirtualAddress()] = block; | 224 | heap_linear_map[block.GetVirtualAddress()] = block; |
| 225 | 225 | ||
| 226 | return block.GetVirtualAddress(); | 226 | return block.GetVirtualAddress(); |
| 227 | } | 227 | } |
| @@ -239,7 +239,7 @@ u16 Read16(const VAddr addr) { | |||
| 239 | // Check for 16-bit unaligned memory reads... | 239 | // Check for 16-bit unaligned memory reads... |
| 240 | if (addr & 1) { | 240 | if (addr & 1) { |
| 241 | // TODO(bunnei): Implement 16-bit unaligned memory reads | 241 | // TODO(bunnei): Implement 16-bit unaligned memory reads |
| 242 | ERROR_LOG(MEMMAP, "16-bit unaligned memory reads are not implemented!"); | 242 | LOG_ERROR(HW_Memory, "16-bit unaligned memory reads are not implemented!"); |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | return (u16)data; | 245 | return (u16)data; |
diff --git a/src/core/settings.h b/src/core/settings.h index 6a6265e18..138ffc615 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 7 | namespace Settings { | 9 | namespace Settings { |
| 8 | 10 | ||
| 9 | struct Values { | 11 | struct Values { |
| @@ -32,6 +34,8 @@ struct Values { | |||
| 32 | 34 | ||
| 33 | // Data Storage | 35 | // Data Storage |
| 34 | bool use_virtual_sd; | 36 | bool use_virtual_sd; |
| 37 | |||
| 38 | std::string log_filter; | ||
| 35 | } extern values; | 39 | } extern values; |
| 36 | 40 | ||
| 37 | } | 41 | } |
diff --git a/src/core/system.cpp b/src/core/system.cpp index 43d0eef2c..2885ff45f 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp | |||
| @@ -23,10 +23,10 @@ void Init(EmuWindow* emu_window) { | |||
| 23 | Core::Init(); | 23 | Core::Init(); |
| 24 | Memory::Init(); | 24 | Memory::Init(); |
| 25 | HW::Init(); | 25 | HW::Init(); |
| 26 | Kernel::Init(); | ||
| 26 | HLE::Init(); | 27 | HLE::Init(); |
| 27 | CoreTiming::Init(); | 28 | CoreTiming::Init(); |
| 28 | VideoCore::Init(emu_window); | 29 | VideoCore::Init(emu_window); |
| 29 | Kernel::Init(); | ||
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void RunLoopFor(int cycles) { | 32 | void RunLoopFor(int cycles) { |
| @@ -37,13 +37,13 @@ void RunLoopUntil(u64 global_cycles) { | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void Shutdown() { | 39 | void Shutdown() { |
| 40 | Core::Shutdown(); | ||
| 41 | Memory::Shutdown(); | ||
| 42 | HW::Shutdown(); | ||
| 43 | HLE::Shutdown(); | ||
| 44 | CoreTiming::Shutdown(); | ||
| 45 | VideoCore::Shutdown(); | 40 | VideoCore::Shutdown(); |
| 41 | CoreTiming::Shutdown(); | ||
| 42 | HLE::Shutdown(); | ||
| 46 | Kernel::Shutdown(); | 43 | Kernel::Shutdown(); |
| 44 | HW::Shutdown(); | ||
| 45 | Memory::Shutdown(); | ||
| 46 | Core::Shutdown(); | ||
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | } // namespace | 49 | } // namespace |
diff --git a/src/core/system.h b/src/core/system.h index 8f8ddf87b..2bc2edc75 100644 --- a/src/core/system.h +++ b/src/core/system.h | |||
| @@ -11,16 +11,16 @@ | |||
| 11 | namespace System { | 11 | namespace System { |
| 12 | 12 | ||
| 13 | // State of the full emulator | 13 | // State of the full emulator |
| 14 | typedef enum { | 14 | enum State { |
| 15 | STATE_NULL = 0, ///< System is in null state, nothing initialized | 15 | STATE_NULL = 0, ///< System is in null state, nothing initialized |
| 16 | STATE_IDLE, ///< System is in an initialized state, but not running | 16 | STATE_IDLE, ///< System is in an initialized state, but not running |
| 17 | STATE_RUNNING, ///< System is running | 17 | STATE_RUNNING, ///< System is running |
| 18 | STATE_LOADING, ///< System is loading a ROM | 18 | STATE_LOADING, ///< System is loading a ROM |
| 19 | STATE_HALTED, ///< System is halted (error) | 19 | STATE_HALTED, ///< System is halted (error) |
| 20 | STATE_STALLED, ///< System is stalled (unused) | 20 | STATE_STALLED, ///< System is stalled (unused) |
| 21 | STATE_DEBUG, ///< System is in a special debug mode (unused) | 21 | STATE_DEBUG, ///< System is in a special debug mode (unused) |
| 22 | STATE_DIE ///< System is shutting down | 22 | STATE_DIE ///< System is shutting down |
| 23 | } State; | 23 | }; |
| 24 | 24 | ||
| 25 | extern volatile State g_state; | 25 | extern volatile State g_state; |
| 26 | 26 | ||
| @@ -30,4 +30,4 @@ void RunLoopFor(int cycles); | |||
| 30 | void RunLoopUntil(u64 global_cycles); | 30 | void RunLoopUntil(u64 global_cycles); |
| 31 | void Shutdown(); | 31 | void Shutdown(); |
| 32 | 32 | ||
| 33 | }; | 33 | } |
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp index 96d3dabe2..632fb959a 100644 --- a/src/video_core/clipper.cpp +++ b/src/video_core/clipper.cpp | |||
| @@ -157,12 +157,12 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { | |||
| 157 | 157 | ||
| 158 | InitScreenCoordinates(vtx2); | 158 | InitScreenCoordinates(vtx2); |
| 159 | 159 | ||
| 160 | DEBUG_LOG(GPU, | 160 | LOG_TRACE(Render_Software, |
| 161 | "Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), " | 161 | "Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), " |
| 162 | "(%.3lu, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " | 162 | "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " |
| 163 | "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", | 163 | "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", |
| 164 | i,output_list.size(), buffer_vertices.size(), | 164 | i,output_list.size(), buffer_vertices.size(), |
| 165 | vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),output_list.size(), | 165 | vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), |
| 166 | vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), | 166 | vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), |
| 167 | vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), | 167 | vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), |
| 168 | vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(), | 168 | vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(), |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 1ec727698..b74cd3261 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "pica.h" | 8 | #include "pica.h" |
| 9 | #include "primitive_assembly.h" | 9 | #include "primitive_assembly.h" |
| 10 | #include "vertex_shader.h" | 10 | #include "vertex_shader.h" |
| 11 | #include "core/hle/service/gsp_gpu.h" | ||
| 11 | 12 | ||
| 12 | #include "debug_utils/debug_utils.h" | 13 | #include "debug_utils/debug_utils.h" |
| 13 | 14 | ||
| @@ -34,15 +35,26 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 34 | u32 old_value = registers[id]; | 35 | u32 old_value = registers[id]; |
| 35 | registers[id] = (old_value & ~mask) | (value & mask); | 36 | registers[id] = (old_value & ~mask) | (value & mask); |
| 36 | 37 | ||
| 38 | if (g_debug_context) | ||
| 39 | g_debug_context->OnEvent(DebugContext::Event::CommandLoaded, reinterpret_cast<void*>(&id)); | ||
| 40 | |||
| 37 | DebugUtils::OnPicaRegWrite(id, registers[id]); | 41 | DebugUtils::OnPicaRegWrite(id, registers[id]); |
| 38 | 42 | ||
| 39 | switch(id) { | 43 | switch(id) { |
| 44 | // Trigger IRQ | ||
| 45 | case PICA_REG_INDEX(trigger_irq): | ||
| 46 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); | ||
| 47 | return; | ||
| 48 | |||
| 40 | // It seems like these trigger vertex rendering | 49 | // It seems like these trigger vertex rendering |
| 41 | case PICA_REG_INDEX(trigger_draw): | 50 | case PICA_REG_INDEX(trigger_draw): |
| 42 | case PICA_REG_INDEX(trigger_draw_indexed): | 51 | case PICA_REG_INDEX(trigger_draw_indexed): |
| 43 | { | 52 | { |
| 44 | DebugUtils::DumpTevStageConfig(registers.GetTevStages()); | 53 | DebugUtils::DumpTevStageConfig(registers.GetTevStages()); |
| 45 | 54 | ||
| 55 | if (g_debug_context) | ||
| 56 | g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); | ||
| 57 | |||
| 46 | const auto& attribute_config = registers.vertex_attributes; | 58 | const auto& attribute_config = registers.vertex_attributes; |
| 47 | const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress()); | 59 | const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress()); |
| 48 | 60 | ||
| @@ -60,7 +72,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 60 | const u8* load_address = base_address + loader_config.data_offset; | 72 | const u8* load_address = base_address + loader_config.data_offset; |
| 61 | 73 | ||
| 62 | // TODO: What happens if a loader overwrites a previous one's data? | 74 | // TODO: What happens if a loader overwrites a previous one's data? |
| 63 | for (int component = 0; component < loader_config.component_count; ++component) { | 75 | for (unsigned component = 0; component < loader_config.component_count; ++component) { |
| 64 | u32 attribute_index = loader_config.GetComponent(component); | 76 | u32 attribute_index = loader_config.GetComponent(component); |
| 65 | vertex_attribute_sources[attribute_index] = load_address; | 77 | vertex_attribute_sources[attribute_index] = load_address; |
| 66 | vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count); | 78 | vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count); |
| @@ -102,7 +114,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 102 | (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : | 114 | (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : |
| 103 | *(float*)srcdata; | 115 | *(float*)srcdata; |
| 104 | input.attr[i][comp] = float24::FromFloat32(srcval); | 116 | input.attr[i][comp] = float24::FromFloat32(srcval); |
| 105 | DEBUG_LOG(GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", | 117 | LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", |
| 106 | comp, i, vertex, index, | 118 | comp, i, vertex, index, |
| 107 | attribute_config.GetBaseAddress(), | 119 | attribute_config.GetBaseAddress(), |
| 108 | vertex_attribute_sources[i] - base_address, | 120 | vertex_attribute_sources[i] - base_address, |
| @@ -132,6 +144,10 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 132 | clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); | 144 | clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); |
| 133 | } | 145 | } |
| 134 | geometry_dumper.Dump(); | 146 | geometry_dumper.Dump(); |
| 147 | |||
| 148 | if (g_debug_context) | ||
| 149 | g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr); | ||
| 150 | |||
| 135 | break; | 151 | break; |
| 136 | } | 152 | } |
| 137 | 153 | ||
| @@ -160,7 +176,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 160 | auto& uniform = VertexShader::GetFloatUniform(uniform_setup.index); | 176 | auto& uniform = VertexShader::GetFloatUniform(uniform_setup.index); |
| 161 | 177 | ||
| 162 | if (uniform_setup.index > 95) { | 178 | if (uniform_setup.index > 95) { |
| 163 | ERROR_LOG(GPU, "Invalid VS uniform index %d", (int)uniform_setup.index); | 179 | LOG_ERROR(HW_GPU, "Invalid VS uniform index %d", (int)uniform_setup.index); |
| 164 | break; | 180 | break; |
| 165 | } | 181 | } |
| 166 | 182 | ||
| @@ -176,7 +192,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 176 | uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF); | 192 | uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF); |
| 177 | } | 193 | } |
| 178 | 194 | ||
| 179 | DEBUG_LOG(GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, | 195 | LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, |
| 180 | uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(), | 196 | uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(), |
| 181 | uniform.w.ToFloat32()); | 197 | uniform.w.ToFloat32()); |
| 182 | 198 | ||
| @@ -229,6 +245,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 229 | default: | 245 | default: |
| 230 | break; | 246 | break; |
| 231 | } | 247 | } |
| 248 | |||
| 249 | if (g_debug_context) | ||
| 250 | g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); | ||
| 232 | } | 251 | } |
| 233 | 252 | ||
| 234 | static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { | 253 | static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { |
| @@ -259,8 +278,9 @@ static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { | |||
| 259 | 278 | ||
| 260 | void ProcessCommandList(const u32* list, u32 size) { | 279 | void ProcessCommandList(const u32* list, u32 size) { |
| 261 | u32* read_pointer = (u32*)list; | 280 | u32* read_pointer = (u32*)list; |
| 281 | u32 list_length = size / sizeof(u32); | ||
| 262 | 282 | ||
| 263 | while (read_pointer < list + size) { | 283 | while (read_pointer < list + list_length) { |
| 264 | read_pointer += ExecuteCommandBlock(read_pointer); | 284 | read_pointer += ExecuteCommandBlock(read_pointer); |
| 265 | } | 285 | } |
| 266 | } | 286 | } |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 22b8e9950..1a20f19ec 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -3,6 +3,8 @@ | |||
| 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 <condition_variable> | ||
| 7 | #include <list> | ||
| 6 | #include <map> | 8 | #include <map> |
| 7 | #include <fstream> | 9 | #include <fstream> |
| 8 | #include <mutex> | 10 | #include <mutex> |
| @@ -12,14 +14,56 @@ | |||
| 12 | #include <png.h> | 14 | #include <png.h> |
| 13 | #endif | 15 | #endif |
| 14 | 16 | ||
| 17 | #include "common/log.h" | ||
| 15 | #include "common/file_util.h" | 18 | #include "common/file_util.h" |
| 16 | 19 | ||
| 20 | #include "video_core/math.h" | ||
| 17 | #include "video_core/pica.h" | 21 | #include "video_core/pica.h" |
| 18 | 22 | ||
| 19 | #include "debug_utils.h" | 23 | #include "debug_utils.h" |
| 20 | 24 | ||
| 21 | namespace Pica { | 25 | namespace Pica { |
| 22 | 26 | ||
| 27 | void DebugContext::OnEvent(Event event, void* data) { | ||
| 28 | if (!breakpoints[event].enabled) | ||
| 29 | return; | ||
| 30 | |||
| 31 | { | ||
| 32 | std::unique_lock<std::mutex> lock(breakpoint_mutex); | ||
| 33 | |||
| 34 | // TODO: Should stop the CPU thread here once we multithread emulation. | ||
| 35 | |||
| 36 | active_breakpoint = event; | ||
| 37 | at_breakpoint = true; | ||
| 38 | |||
| 39 | // Tell all observers that we hit a breakpoint | ||
| 40 | for (auto& breakpoint_observer : breakpoint_observers) { | ||
| 41 | breakpoint_observer->OnPicaBreakPointHit(event, data); | ||
| 42 | } | ||
| 43 | |||
| 44 | // Wait until another thread tells us to Resume() | ||
| 45 | resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; }); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | void DebugContext::Resume() { | ||
| 50 | { | ||
| 51 | std::unique_lock<std::mutex> lock(breakpoint_mutex); | ||
| 52 | |||
| 53 | // Tell all observers that we are about to resume | ||
| 54 | for (auto& breakpoint_observer : breakpoint_observers) { | ||
| 55 | breakpoint_observer->OnPicaResume(); | ||
| 56 | } | ||
| 57 | |||
| 58 | // Resume the waiting thread (i.e. OnEvent()) | ||
| 59 | at_breakpoint = false; | ||
| 60 | } | ||
| 61 | |||
| 62 | resume_from_breakpoint.notify_one(); | ||
| 63 | } | ||
| 64 | |||
| 65 | std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global | ||
| 66 | |||
| 23 | namespace DebugUtils { | 67 | namespace DebugUtils { |
| 24 | 68 | ||
| 25 | void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { | 69 | void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { |
| @@ -155,7 +199,7 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data | |||
| 155 | 199 | ||
| 156 | // This is put into a try-catch block to make sure we notice unknown configurations. | 200 | // This is put into a try-catch block to make sure we notice unknown configurations. |
| 157 | std::vector<OutputRegisterInfo> output_info_table; | 201 | std::vector<OutputRegisterInfo> output_info_table; |
| 158 | for (int i = 0; i < 7; ++i) { | 202 | for (unsigned i = 0; i < 7; ++i) { |
| 159 | using OutputAttributes = Pica::Regs::VSOutputAttributes; | 203 | using OutputAttributes = Pica::Regs::VSOutputAttributes; |
| 160 | 204 | ||
| 161 | // TODO: It's still unclear how the attribute components map to the register! | 205 | // TODO: It's still unclear how the attribute components map to the register! |
| @@ -204,8 +248,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data | |||
| 204 | it->component_mask = it->component_mask | component_mask; | 248 | it->component_mask = it->component_mask | component_mask; |
| 205 | } | 249 | } |
| 206 | } catch (const std::out_of_range& ) { | 250 | } catch (const std::out_of_range& ) { |
| 207 | _dbg_assert_msg_(GPU, 0, "Unknown output attribute mapping"); | 251 | _dbg_assert_msg_(HW_GPU, 0, "Unknown output attribute mapping"); |
| 208 | ERROR_LOG(GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x", | 252 | LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x", |
| 209 | (int)output_attributes[i].map_x.Value(), | 253 | (int)output_attributes[i].map_x.Value(), |
| 210 | (int)output_attributes[i].map_y.Value(), | 254 | (int)output_attributes[i].map_y.Value(), |
| 211 | (int)output_attributes[i].map_z.Value(), | 255 | (int)output_attributes[i].map_z.Value(), |
| @@ -265,7 +309,7 @@ static int is_pica_tracing = false; | |||
| 265 | void StartPicaTracing() | 309 | void StartPicaTracing() |
| 266 | { | 310 | { |
| 267 | if (is_pica_tracing) { | 311 | if (is_pica_tracing) { |
| 268 | ERROR_LOG(GPU, "StartPicaTracing called even though tracing already running!"); | 312 | LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!"); |
| 269 | return; | 313 | return; |
| 270 | } | 314 | } |
| 271 | 315 | ||
| @@ -298,7 +342,7 @@ void OnPicaRegWrite(u32 id, u32 value) | |||
| 298 | std::unique_ptr<PicaTrace> FinishPicaTracing() | 342 | std::unique_ptr<PicaTrace> FinishPicaTracing() |
| 299 | { | 343 | { |
| 300 | if (!is_pica_tracing) { | 344 | if (!is_pica_tracing) { |
| 301 | ERROR_LOG(GPU, "FinishPicaTracing called even though tracing already running!"); | 345 | LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!"); |
| 302 | return {}; | 346 | return {}; |
| 303 | } | 347 | } |
| 304 | 348 | ||
| @@ -312,15 +356,51 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() | |||
| 312 | return std::move(ret); | 356 | return std::move(ret); |
| 313 | } | 357 | } |
| 314 | 358 | ||
| 359 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { | ||
| 360 | _dbg_assert_(Debug_GPU, info.format == Pica::Regs::TextureFormat::RGB8); | ||
| 361 | |||
| 362 | // Cf. rasterizer code for an explanation of this algorithm. | ||
| 363 | int texel_index_within_tile = 0; | ||
| 364 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | ||
| 365 | int sub_tile_width = 1 << block_size_index; | ||
| 366 | int sub_tile_height = 1 << block_size_index; | ||
| 367 | |||
| 368 | int sub_tile_index = (x & sub_tile_width) << block_size_index; | ||
| 369 | sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); | ||
| 370 | texel_index_within_tile += sub_tile_index; | ||
| 371 | } | ||
| 372 | |||
| 373 | const int block_width = 8; | ||
| 374 | const int block_height = 8; | ||
| 375 | |||
| 376 | int coarse_x = (x / block_width) * block_width; | ||
| 377 | int coarse_y = (y / block_height) * block_height; | ||
| 378 | |||
| 379 | const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3; | ||
| 380 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; | ||
| 381 | } | ||
| 382 | |||
| 383 | TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, | ||
| 384 | const Regs::TextureFormat& format) | ||
| 385 | { | ||
| 386 | TextureInfo info; | ||
| 387 | info.address = config.GetPhysicalAddress(); | ||
| 388 | info.width = config.width; | ||
| 389 | info.height = config.height; | ||
| 390 | info.format = format; | ||
| 391 | info.stride = Pica::Regs::BytesPerPixel(info.format) * info.width; | ||
| 392 | return info; | ||
| 393 | } | ||
| 394 | |||
| 315 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | 395 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { |
| 316 | // NOTE: Permanently enabling this just trashes hard disks for no reason. | 396 | // NOTE: Permanently enabling this just trashes hard disks for no reason. |
| 317 | // Hence, this is currently disabled. | 397 | // Hence, this is currently disabled. |
| 318 | return; | 398 | return; |
| 319 | 399 | ||
| 320 | #ifndef HAVE_PNG | 400 | #ifndef HAVE_PNG |
| 321 | return; | 401 | return; |
| 322 | #else | 402 | #else |
| 323 | if (!data) | 403 | if (!data) |
| 324 | return; | 404 | return; |
| 325 | 405 | ||
| 326 | // Write data to file | 406 | // Write data to file |
| @@ -341,7 +421,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | |||
| 341 | // Initialize write structure | 421 | // Initialize write structure |
| 342 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | 422 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); |
| 343 | if (png_ptr == nullptr) { | 423 | if (png_ptr == nullptr) { |
| 344 | ERROR_LOG(GPU, "Could not allocate write struct\n"); | 424 | LOG_ERROR(Debug_GPU, "Could not allocate write struct\n"); |
| 345 | goto finalise; | 425 | goto finalise; |
| 346 | 426 | ||
| 347 | } | 427 | } |
| @@ -349,13 +429,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | |||
| 349 | // Initialize info structure | 429 | // Initialize info structure |
| 350 | info_ptr = png_create_info_struct(png_ptr); | 430 | info_ptr = png_create_info_struct(png_ptr); |
| 351 | if (info_ptr == nullptr) { | 431 | if (info_ptr == nullptr) { |
| 352 | ERROR_LOG(GPU, "Could not allocate info struct\n"); | 432 | LOG_ERROR(Debug_GPU, "Could not allocate info struct\n"); |
| 353 | goto finalise; | 433 | goto finalise; |
| 354 | } | 434 | } |
| 355 | 435 | ||
| 356 | // Setup Exception handling | 436 | // Setup Exception handling |
| 357 | if (setjmp(png_jmpbuf(png_ptr))) { | 437 | if (setjmp(png_jmpbuf(png_ptr))) { |
| 358 | ERROR_LOG(GPU, "Error during png creation\n"); | 438 | LOG_ERROR(Debug_GPU, "Error during png creation\n"); |
| 359 | goto finalise; | 439 | goto finalise; |
| 360 | } | 440 | } |
| 361 | 441 | ||
| @@ -375,34 +455,22 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | |||
| 375 | png_write_info(png_ptr, info_ptr); | 455 | png_write_info(png_ptr, info_ptr); |
| 376 | 456 | ||
| 377 | buf = new u8[row_stride * texture_config.height]; | 457 | buf = new u8[row_stride * texture_config.height]; |
| 378 | for (int y = 0; y < texture_config.height; ++y) { | 458 | for (unsigned y = 0; y < texture_config.height; ++y) { |
| 379 | for (int x = 0; x < texture_config.width; ++x) { | 459 | for (unsigned x = 0; x < texture_config.width; ++x) { |
| 380 | // Cf. rasterizer code for an explanation of this algorithm. | 460 | TextureInfo info; |
| 381 | int texel_index_within_tile = 0; | 461 | info.width = texture_config.width; |
| 382 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | 462 | info.height = texture_config.height; |
| 383 | int sub_tile_width = 1 << block_size_index; | 463 | info.stride = row_stride; |
| 384 | int sub_tile_height = 1 << block_size_index; | 464 | info.format = registers.texture0_format; |
| 385 | 465 | Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info); | |
| 386 | int sub_tile_index = (x & sub_tile_width) << block_size_index; | 466 | buf[3 * x + y * row_stride ] = texture_color.r(); |
| 387 | sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); | 467 | buf[3 * x + y * row_stride + 1] = texture_color.g(); |
| 388 | texel_index_within_tile += sub_tile_index; | 468 | buf[3 * x + y * row_stride + 2] = texture_color.b(); |
| 389 | } | ||
| 390 | |||
| 391 | const int block_width = 8; | ||
| 392 | const int block_height = 8; | ||
| 393 | |||
| 394 | int coarse_x = (x / block_width) * block_width; | ||
| 395 | int coarse_y = (y / block_height) * block_height; | ||
| 396 | |||
| 397 | u8* source_ptr = (u8*)data + coarse_x * block_height * 3 + coarse_y * row_stride + texel_index_within_tile * 3; | ||
| 398 | buf[3 * x + y * row_stride ] = source_ptr[2]; | ||
| 399 | buf[3 * x + y * row_stride + 1] = source_ptr[1]; | ||
| 400 | buf[3 * x + y * row_stride + 2] = source_ptr[0]; | ||
| 401 | } | 469 | } |
| 402 | } | 470 | } |
| 403 | 471 | ||
| 404 | // Write image data | 472 | // Write image data |
| 405 | for (auto y = 0; y < texture_config.height; ++y) | 473 | for (unsigned y = 0; y < texture_config.height; ++y) |
| 406 | { | 474 | { |
| 407 | u8* row_ptr = (u8*)buf + y * row_stride; | 475 | u8* row_ptr = (u8*)buf + y * row_stride; |
| 408 | u8* ptr = row_ptr; | 476 | u8* ptr = row_ptr; |
| @@ -514,7 +582,7 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 514 | stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n"; | 582 | stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n"; |
| 515 | } | 583 | } |
| 516 | 584 | ||
| 517 | DEBUG_LOG(GPU, "%s", stage_info.c_str()); | 585 | LOG_TRACE(HW_GPU, "%s", stage_info.c_str()); |
| 518 | } | 586 | } |
| 519 | 587 | ||
| 520 | } // namespace | 588 | } // namespace |
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 8b1499bf2..51f14f12f 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -5,13 +5,147 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <condition_variable> | ||
| 9 | #include <list> | ||
| 10 | #include <map> | ||
| 8 | #include <memory> | 11 | #include <memory> |
| 12 | #include <mutex> | ||
| 9 | #include <vector> | 13 | #include <vector> |
| 10 | 14 | ||
| 15 | #include "video_core/math.h" | ||
| 11 | #include "video_core/pica.h" | 16 | #include "video_core/pica.h" |
| 12 | 17 | ||
| 13 | namespace Pica { | 18 | namespace Pica { |
| 14 | 19 | ||
| 20 | class DebugContext { | ||
| 21 | public: | ||
| 22 | enum class Event { | ||
| 23 | FirstEvent = 0, | ||
| 24 | |||
| 25 | CommandLoaded = FirstEvent, | ||
| 26 | CommandProcessed, | ||
| 27 | IncomingPrimitiveBatch, | ||
| 28 | FinishedPrimitiveBatch, | ||
| 29 | |||
| 30 | NumEvents | ||
| 31 | }; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Inherit from this class to be notified of events registered to some debug context. | ||
| 35 | * Most importantly this is used for our debugger GUI. | ||
| 36 | * | ||
| 37 | * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods. | ||
| 38 | * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access | ||
| 39 | * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread. | ||
| 40 | */ | ||
| 41 | class BreakPointObserver { | ||
| 42 | public: | ||
| 43 | /// Constructs the object such that it observes events of the given DebugContext. | ||
| 44 | BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) { | ||
| 45 | std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex); | ||
| 46 | debug_context->breakpoint_observers.push_back(this); | ||
| 47 | } | ||
| 48 | |||
| 49 | virtual ~BreakPointObserver() { | ||
| 50 | auto context = context_weak.lock(); | ||
| 51 | if (context) { | ||
| 52 | std::unique_lock<std::mutex> lock(context->breakpoint_mutex); | ||
| 53 | context->breakpoint_observers.remove(this); | ||
| 54 | |||
| 55 | // If we are the last observer to be destroyed, tell the debugger context that | ||
| 56 | // it is free to continue. In particular, this is required for a proper Citra | ||
| 57 | // shutdown, when the emulation thread is waiting at a breakpoint. | ||
| 58 | if (context->breakpoint_observers.empty()) | ||
| 59 | context->Resume(); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Action to perform when a breakpoint was reached. | ||
| 65 | * @param event Type of event which triggered the breakpoint | ||
| 66 | * @param data Optional data pointer (if unused, this is a nullptr) | ||
| 67 | * @note This function will perform nothing unless it is overridden in the child class. | ||
| 68 | */ | ||
| 69 | virtual void OnPicaBreakPointHit(Event, void*) { | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Action to perform when emulation is resumed from a breakpoint. | ||
| 74 | * @note This function will perform nothing unless it is overridden in the child class. | ||
| 75 | */ | ||
| 76 | virtual void OnPicaResume() { | ||
| 77 | } | ||
| 78 | |||
| 79 | protected: | ||
| 80 | /** | ||
| 81 | * Weak context pointer. This need not be valid, so when requesting a shared_ptr via | ||
| 82 | * context_weak.lock(), always compare the result against nullptr. | ||
| 83 | */ | ||
| 84 | std::weak_ptr<DebugContext> context_weak; | ||
| 85 | }; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Simple structure defining a breakpoint state | ||
| 89 | */ | ||
| 90 | struct BreakPoint { | ||
| 91 | bool enabled = false; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Static constructor used to create a shared_ptr of a DebugContext. | ||
| 96 | */ | ||
| 97 | static std::shared_ptr<DebugContext> Construct() { | ||
| 98 | return std::shared_ptr<DebugContext>(new DebugContext); | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Used by the emulation core when a given event has happened. If a breakpoint has been set | ||
| 103 | * for this event, OnEvent calls the event handlers of the registered breakpoint observers. | ||
| 104 | * The current thread then is halted until Resume() is called from another thread (or until | ||
| 105 | * emulation is stopped). | ||
| 106 | * @param event Event which has happened | ||
| 107 | * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called. | ||
| 108 | */ | ||
| 109 | void OnEvent(Event event, void* data); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Resume from the current breakpoint. | ||
| 113 | * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe. | ||
| 114 | */ | ||
| 115 | void Resume(); | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Delete all set breakpoints and resume emulation. | ||
| 119 | */ | ||
| 120 | void ClearBreakpoints() { | ||
| 121 | breakpoints.clear(); | ||
| 122 | Resume(); | ||
| 123 | } | ||
| 124 | |||
| 125 | // TODO: Evaluate if access to these members should be hidden behind a public interface. | ||
| 126 | std::map<Event, BreakPoint> breakpoints; | ||
| 127 | Event active_breakpoint; | ||
| 128 | bool at_breakpoint = false; | ||
| 129 | |||
| 130 | private: | ||
| 131 | /** | ||
| 132 | * Private default constructor to make sure people always construct this through Construct() | ||
| 133 | * instead. | ||
| 134 | */ | ||
| 135 | DebugContext() = default; | ||
| 136 | |||
| 137 | /// Mutex protecting current breakpoint state and the observer list. | ||
| 138 | std::mutex breakpoint_mutex; | ||
| 139 | |||
| 140 | /// Used by OnEvent to wait for resumption. | ||
| 141 | std::condition_variable resume_from_breakpoint; | ||
| 142 | |||
| 143 | /// List of registered observers | ||
| 144 | std::list<BreakPointObserver*> breakpoint_observers; | ||
| 145 | }; | ||
| 146 | |||
| 147 | extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global | ||
| 148 | |||
| 15 | namespace DebugUtils { | 149 | namespace DebugUtils { |
| 16 | 150 | ||
| 17 | // Simple utility class for dumping geometry data to an OBJ file | 151 | // Simple utility class for dumping geometry data to an OBJ file |
| @@ -41,7 +175,7 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data | |||
| 41 | // Utility class to log Pica commands. | 175 | // Utility class to log Pica commands. |
| 42 | struct PicaTrace { | 176 | struct PicaTrace { |
| 43 | struct Write : public std::pair<u32,u32> { | 177 | struct Write : public std::pair<u32,u32> { |
| 44 | Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {} | 178 | Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {} |
| 45 | 179 | ||
| 46 | u32& Id() { return first; } | 180 | u32& Id() { return first; } |
| 47 | const u32& Id() const { return first; } | 181 | const u32& Id() const { return first; } |
| @@ -57,6 +191,18 @@ bool IsPicaTracing(); | |||
| 57 | void OnPicaRegWrite(u32 id, u32 value); | 191 | void OnPicaRegWrite(u32 id, u32 value); |
| 58 | std::unique_ptr<PicaTrace> FinishPicaTracing(); | 192 | std::unique_ptr<PicaTrace> FinishPicaTracing(); |
| 59 | 193 | ||
| 194 | struct TextureInfo { | ||
| 195 | unsigned int address; | ||
| 196 | int width; | ||
| 197 | int height; | ||
| 198 | int stride; | ||
| 199 | Pica::Regs::TextureFormat format; | ||
| 200 | |||
| 201 | static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config, | ||
| 202 | const Pica::Regs::TextureFormat& format); | ||
| 203 | }; | ||
| 204 | |||
| 205 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info); | ||
| 60 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | 206 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); |
| 61 | 207 | ||
| 62 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); | 208 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); |
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 1242eb58f..16b1656bb 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h | |||
| @@ -39,7 +39,7 @@ public: | |||
| 39 | virtual void GXCommandProcessed(int total_command_count) | 39 | virtual void GXCommandProcessed(int total_command_count) |
| 40 | { | 40 | { |
| 41 | const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1); | 41 | const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1); |
| 42 | ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value()); | 42 | LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value()); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | protected: | 45 | protected: |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 5fe15a218..4c3791ad9 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -45,10 +45,16 @@ struct Regs { | |||
| 45 | #define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) | 45 | #define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) |
| 46 | #define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]; | 46 | #define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]; |
| 47 | 47 | ||
| 48 | INSERT_PADDING_WORDS(0x41); | 48 | INSERT_PADDING_WORDS(0x10); |
| 49 | |||
| 50 | u32 trigger_irq; | ||
| 51 | |||
| 52 | INSERT_PADDING_WORDS(0x30); | ||
| 49 | 53 | ||
| 50 | BitField<0, 24, u32> viewport_size_x; | 54 | BitField<0, 24, u32> viewport_size_x; |
| 55 | |||
| 51 | INSERT_PADDING_WORDS(0x1); | 56 | INSERT_PADDING_WORDS(0x1); |
| 57 | |||
| 52 | BitField<0, 24, u32> viewport_size_y; | 58 | BitField<0, 24, u32> viewport_size_y; |
| 53 | 59 | ||
| 54 | INSERT_PADDING_WORDS(0x9); | 60 | INSERT_PADDING_WORDS(0x9); |
| @@ -109,8 +115,8 @@ struct Regs { | |||
| 109 | 115 | ||
| 110 | u32 address; | 116 | u32 address; |
| 111 | 117 | ||
| 112 | u32 GetPhysicalAddress() { | 118 | u32 GetPhysicalAddress() const { |
| 113 | return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; | 119 | return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; |
| 114 | } | 120 | } |
| 115 | 121 | ||
| 116 | // texture1 and texture2 store the texture format directly after the address | 122 | // texture1 and texture2 store the texture format directly after the address |
| @@ -130,7 +136,26 @@ struct Regs { | |||
| 130 | // Seems like they are luminance formats and compressed textures. | 136 | // Seems like they are luminance formats and compressed textures. |
| 131 | }; | 137 | }; |
| 132 | 138 | ||
| 133 | BitField<0, 1, u32> texturing_enable; | 139 | static unsigned BytesPerPixel(TextureFormat format) { |
| 140 | switch (format) { | ||
| 141 | case TextureFormat::RGBA8: | ||
| 142 | return 4; | ||
| 143 | |||
| 144 | case TextureFormat::RGB8: | ||
| 145 | return 3; | ||
| 146 | |||
| 147 | case TextureFormat::RGBA5551: | ||
| 148 | case TextureFormat::RGB565: | ||
| 149 | case TextureFormat::RGBA4: | ||
| 150 | return 2; | ||
| 151 | |||
| 152 | default: | ||
| 153 | // placeholder for yet unknown formats | ||
| 154 | return 1; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | BitField< 0, 1, u32> texturing_enable; | ||
| 134 | TextureConfig texture0; | 159 | TextureConfig texture0; |
| 135 | INSERT_PADDING_WORDS(0x8); | 160 | INSERT_PADDING_WORDS(0x8); |
| 136 | BitField<0, 4, TextureFormat> texture0_format; | 161 | BitField<0, 4, TextureFormat> texture0_format; |
| @@ -287,7 +312,7 @@ struct Regs { | |||
| 287 | 312 | ||
| 288 | inline u32 GetBaseAddress() const { | 313 | inline u32 GetBaseAddress() const { |
| 289 | // TODO: Ugly, should fix PhysicalToVirtualAddress instead | 314 | // TODO: Ugly, should fix PhysicalToVirtualAddress instead |
| 290 | return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; | 315 | return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; |
| 291 | } | 316 | } |
| 292 | 317 | ||
| 293 | // Descriptor for internal vertex attributes | 318 | // Descriptor for internal vertex attributes |
| @@ -517,10 +542,6 @@ struct Regs { | |||
| 517 | static std::string GetCommandName(int index) { | 542 | static std::string GetCommandName(int index) { |
| 518 | std::map<u32, std::string> map; | 543 | std::map<u32, std::string> map; |
| 519 | 544 | ||
| 520 | // TODO: MSVC does not support using offsetof() on non-static data members even though this | ||
| 521 | // is technically allowed since C++11. Hence, this functionality is disabled until | ||
| 522 | // MSVC properly supports it. | ||
| 523 | #ifndef _MSC_VER | ||
| 524 | Regs regs; | 545 | Regs regs; |
| 525 | #define ADD_FIELD(name) \ | 546 | #define ADD_FIELD(name) \ |
| 526 | do { \ | 547 | do { \ |
| @@ -529,6 +550,7 @@ struct Regs { | |||
| 529 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ | 550 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ |
| 530 | } while(false) | 551 | } while(false) |
| 531 | 552 | ||
| 553 | ADD_FIELD(trigger_irq); | ||
| 532 | ADD_FIELD(viewport_size_x); | 554 | ADD_FIELD(viewport_size_x); |
| 533 | ADD_FIELD(viewport_size_y); | 555 | ADD_FIELD(viewport_size_y); |
| 534 | ADD_FIELD(viewport_depth_range); | 556 | ADD_FIELD(viewport_depth_range); |
| @@ -557,7 +579,6 @@ struct Regs { | |||
| 557 | ADD_FIELD(vs_swizzle_patterns); | 579 | ADD_FIELD(vs_swizzle_patterns); |
| 558 | 580 | ||
| 559 | #undef ADD_FIELD | 581 | #undef ADD_FIELD |
| 560 | #endif // _MSC_VER | ||
| 561 | 582 | ||
| 562 | // Return empty string if no match is found | 583 | // Return empty string if no match is found |
| 563 | return map[index]; | 584 | return map[index]; |
| @@ -593,6 +614,7 @@ private: | |||
| 593 | #ifndef _MSC_VER | 614 | #ifndef _MSC_VER |
| 594 | #define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position") | 615 | #define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position") |
| 595 | 616 | ||
| 617 | ASSERT_REG_POSITION(trigger_irq, 0x10); | ||
| 596 | ASSERT_REG_POSITION(viewport_size_x, 0x41); | 618 | ASSERT_REG_POSITION(viewport_size_x, 0x41); |
| 597 | ASSERT_REG_POSITION(viewport_size_y, 0x43); | 619 | ASSERT_REG_POSITION(viewport_size_y, 0x43); |
| 598 | ASSERT_REG_POSITION(viewport_depth_range, 0x4d); | 620 | ASSERT_REG_POSITION(viewport_depth_range, 0x4d); |
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index dabf2d1a3..102693ed9 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp | |||
| @@ -43,7 +43,7 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl | |||
| 43 | break; | 43 | break; |
| 44 | 44 | ||
| 45 | default: | 45 | default: |
| 46 | ERROR_LOG(GPU, "Unknown triangle topology %x:", (int)topology); | 46 | LOG_ERROR(Render_Software, "Unknown triangle topology %x:", (int)topology); |
| 47 | break; | 47 | break; |
| 48 | } | 48 | } |
| 49 | } | 49 | } |
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index a35f0c0d8..b7e04a560 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -252,7 +252,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 252 | return combiner_output.rgb(); | 252 | return combiner_output.rgb(); |
| 253 | 253 | ||
| 254 | default: | 254 | default: |
| 255 | ERROR_LOG(GPU, "Unknown color combiner source %d\n", (int)source); | 255 | LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); |
| 256 | return {}; | 256 | return {}; |
| 257 | } | 257 | } |
| 258 | }; | 258 | }; |
| @@ -272,7 +272,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 272 | return combiner_output.a(); | 272 | return combiner_output.a(); |
| 273 | 273 | ||
| 274 | default: | 274 | default: |
| 275 | ERROR_LOG(GPU, "Unknown alpha combiner source %d\n", (int)source); | 275 | LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); |
| 276 | return 0; | 276 | return 0; |
| 277 | } | 277 | } |
| 278 | }; | 278 | }; |
| @@ -283,7 +283,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 283 | case ColorModifier::SourceColor: | 283 | case ColorModifier::SourceColor: |
| 284 | return values; | 284 | return values; |
| 285 | default: | 285 | default: |
| 286 | ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); | 286 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); |
| 287 | return {}; | 287 | return {}; |
| 288 | } | 288 | } |
| 289 | }; | 289 | }; |
| @@ -293,7 +293,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 293 | case AlphaModifier::SourceAlpha: | 293 | case AlphaModifier::SourceAlpha: |
| 294 | return value; | 294 | return value; |
| 295 | default: | 295 | default: |
| 296 | ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); | 296 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); |
| 297 | return 0; | 297 | return 0; |
| 298 | } | 298 | } |
| 299 | }; | 299 | }; |
| @@ -307,7 +307,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 307 | return ((input[0] * input[1]) / 255).Cast<u8>(); | 307 | return ((input[0] * input[1]) / 255).Cast<u8>(); |
| 308 | 308 | ||
| 309 | default: | 309 | default: |
| 310 | ERROR_LOG(GPU, "Unknown color combiner operation %d\n", (int)op); | 310 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); |
| 311 | return {}; | 311 | return {}; |
| 312 | } | 312 | } |
| 313 | }; | 313 | }; |
| @@ -321,7 +321,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 321 | return input[0] * input[1] / 255; | 321 | return input[0] * input[1] / 255; |
| 322 | 322 | ||
| 323 | default: | 323 | default: |
| 324 | ERROR_LOG(GPU, "Unknown alpha combiner operation %d\n", (int)op); | 324 | LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); |
| 325 | return 0; | 325 | return 0; |
| 326 | } | 326 | } |
| 327 | }; | 327 | }; |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index f1dbc9d17..bce402b88 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | /// Swap buffers (render frame) | 25 | /// Swap buffers (render frame) |
| 26 | virtual void SwapBuffers() = 0; | 26 | virtual void SwapBuffers() = 0; |
| 27 | 27 | ||
| 28 | /** | 28 | /** |
| 29 | * Set the emulator window to use for renderer | 29 | * Set the emulator window to use for renderer |
| 30 | * @param window EmuWindow handle to emulator window to use for rendering | 30 | * @param window EmuWindow handle to emulator window to use for rendering |
| 31 | */ | 31 | */ |
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index a0eb0418c..d0f82e6cd 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp | |||
| @@ -20,9 +20,9 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | |||
| 20 | int info_log_length; | 20 | int info_log_length; |
| 21 | 21 | ||
| 22 | // Compile Vertex Shader | 22 | // Compile Vertex Shader |
| 23 | DEBUG_LOG(GPU, "Compiling vertex shader."); | 23 | LOG_DEBUG(Render_OpenGL, "Compiling vertex shader..."); |
| 24 | 24 | ||
| 25 | glShaderSource(vertex_shader_id, 1, &vertex_shader, NULL); | 25 | glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr); |
| 26 | glCompileShader(vertex_shader_id); | 26 | glCompileShader(vertex_shader_id); |
| 27 | 27 | ||
| 28 | // Check Vertex Shader | 28 | // Check Vertex Shader |
| @@ -31,14 +31,18 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | |||
| 31 | 31 | ||
| 32 | if (info_log_length > 1) { | 32 | if (info_log_length > 1) { |
| 33 | std::vector<char> vertex_shader_error(info_log_length); | 33 | std::vector<char> vertex_shader_error(info_log_length); |
| 34 | glGetShaderInfoLog(vertex_shader_id, info_log_length, NULL, &vertex_shader_error[0]); | 34 | glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]); |
| 35 | DEBUG_LOG(GPU, "%s", &vertex_shader_error[0]); | 35 | if (result) { |
| 36 | LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]); | ||
| 37 | } else { | ||
| 38 | LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]); | ||
| 39 | } | ||
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | // Compile Fragment Shader | 42 | // Compile Fragment Shader |
| 39 | DEBUG_LOG(GPU, "Compiling fragment shader."); | 43 | LOG_DEBUG(Render_OpenGL, "Compiling fragment shader..."); |
| 40 | 44 | ||
| 41 | glShaderSource(fragment_shader_id, 1, &fragment_shader, NULL); | 45 | glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr); |
| 42 | glCompileShader(fragment_shader_id); | 46 | glCompileShader(fragment_shader_id); |
| 43 | 47 | ||
| 44 | // Check Fragment Shader | 48 | // Check Fragment Shader |
| @@ -47,12 +51,16 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | |||
| 47 | 51 | ||
| 48 | if (info_log_length > 1) { | 52 | if (info_log_length > 1) { |
| 49 | std::vector<char> fragment_shader_error(info_log_length); | 53 | std::vector<char> fragment_shader_error(info_log_length); |
| 50 | glGetShaderInfoLog(fragment_shader_id, info_log_length, NULL, &fragment_shader_error[0]); | 54 | glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]); |
| 51 | DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]); | 55 | if (result) { |
| 56 | LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]); | ||
| 57 | } else { | ||
| 58 | LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s", &fragment_shader_error[0]); | ||
| 59 | } | ||
| 52 | } | 60 | } |
| 53 | 61 | ||
| 54 | // Link the program | 62 | // Link the program |
| 55 | DEBUG_LOG(GPU, "Linking program."); | 63 | LOG_DEBUG(Render_OpenGL, "Linking program..."); |
| 56 | 64 | ||
| 57 | GLuint program_id = glCreateProgram(); | 65 | GLuint program_id = glCreateProgram(); |
| 58 | glAttachShader(program_id, vertex_shader_id); | 66 | glAttachShader(program_id, vertex_shader_id); |
| @@ -65,8 +73,12 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | |||
| 65 | 73 | ||
| 66 | if (info_log_length > 1) { | 74 | if (info_log_length > 1) { |
| 67 | std::vector<char> program_error(info_log_length); | 75 | std::vector<char> program_error(info_log_length); |
| 68 | glGetProgramInfoLog(program_id, info_log_length, NULL, &program_error[0]); | 76 | glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); |
| 69 | DEBUG_LOG(GPU, "%s", &program_error[0]); | 77 | if (result) { |
| 78 | LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]); | ||
| 79 | } else { | ||
| 80 | LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]); | ||
| 81 | } | ||
| 70 | } | 82 | } |
| 71 | 83 | ||
| 72 | glDeleteShader(vertex_shader_id); | 84 | glDeleteShader(vertex_shader_id); |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 8483f79be..e2caeeb8f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -61,7 +61,7 @@ void RendererOpenGL::SwapBuffers() { | |||
| 61 | for(int i : {0, 1}) { | 61 | for(int i : {0, 1}) { |
| 62 | const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; | 62 | const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; |
| 63 | 63 | ||
| 64 | if (textures[i].width != framebuffer.width || textures[i].height != framebuffer.height) { | 64 | if (textures[i].width != (GLsizei)framebuffer.width || textures[i].height != (GLsizei)framebuffer.height) { |
| 65 | // Reallocate texture if the framebuffer size has changed. | 65 | // Reallocate texture if the framebuffer size has changed. |
| 66 | // This is expected to not happen very often and hence should not be a | 66 | // This is expected to not happen very often and hence should not be a |
| 67 | // performance problem. | 67 | // performance problem. |
| @@ -90,7 +90,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||
| 90 | const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( | 90 | const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( |
| 91 | framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1); | 91 | framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1); |
| 92 | 92 | ||
| 93 | DEBUG_LOG(GPU, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", | 93 | LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", |
| 94 | framebuffer.stride * framebuffer.height, | 94 | framebuffer.stride * framebuffer.height, |
| 95 | framebuffer_vaddr, (int)framebuffer.width, | 95 | framebuffer_vaddr, (int)framebuffer.width, |
| 96 | (int)framebuffer.height, (int)framebuffer.format); | 96 | (int)framebuffer.height, (int)framebuffer.format); |
| @@ -98,15 +98,15 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||
| 98 | const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); | 98 | const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); |
| 99 | 99 | ||
| 100 | // TODO: Handle other pixel formats | 100 | // TODO: Handle other pixel formats |
| 101 | _dbg_assert_msg_(RENDER, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, | 101 | _dbg_assert_msg_(Render_OpenGL, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, |
| 102 | "Unsupported 3DS pixel format."); | 102 | "Unsupported 3DS pixel format."); |
| 103 | 103 | ||
| 104 | size_t pixel_stride = framebuffer.stride / 3; | 104 | size_t pixel_stride = framebuffer.stride / 3; |
| 105 | // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately | 105 | // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately |
| 106 | _dbg_assert_(RENDER, pixel_stride * 3 == framebuffer.stride); | 106 | _dbg_assert_(Render_OpenGL, pixel_stride * 3 == framebuffer.stride); |
| 107 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default | 107 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default |
| 108 | // only allows rows to have a memory alignement of 4. | 108 | // only allows rows to have a memory alignement of 4. |
| 109 | _dbg_assert_(RENDER, pixel_stride % 4 == 0); | 109 | _dbg_assert_(Render_OpenGL, pixel_stride % 4 == 0); |
| 110 | 110 | ||
| 111 | glBindTexture(GL_TEXTURE_2D, texture.handle); | 111 | glBindTexture(GL_TEXTURE_2D, texture.handle); |
| 112 | glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); | 112 | glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); |
| @@ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x | |||
| 191 | * Draws the emulated screens to the emulator window. | 191 | * Draws the emulated screens to the emulator window. |
| 192 | */ | 192 | */ |
| 193 | void RendererOpenGL::DrawScreens() { | 193 | void RendererOpenGL::DrawScreens() { |
| 194 | glViewport(0, 0, resolution_width, resolution_height); | 194 | auto viewport_extent = GetViewportExtent(); |
| 195 | glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom? | ||
| 195 | glClear(GL_COLOR_BUFFER_BIT); | 196 | glClear(GL_COLOR_BUFFER_BIT); |
| 196 | 197 | ||
| 197 | glUseProgram(program_id); | 198 | glUseProgram(program_id); |
| @@ -228,17 +229,45 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { | |||
| 228 | render_window = window; | 229 | render_window = window; |
| 229 | } | 230 | } |
| 230 | 231 | ||
| 232 | MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() { | ||
| 233 | unsigned framebuffer_width; | ||
| 234 | unsigned framebuffer_height; | ||
| 235 | std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize(); | ||
| 236 | |||
| 237 | float window_aspect_ratio = static_cast<float>(framebuffer_height) / framebuffer_width; | ||
| 238 | float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width; | ||
| 239 | |||
| 240 | MathUtil::Rectangle<unsigned> viewport_extent; | ||
| 241 | if (window_aspect_ratio > emulation_aspect_ratio) { | ||
| 242 | // Window is narrower than the emulation content => apply borders to the top and bottom | ||
| 243 | unsigned viewport_height = std::round(emulation_aspect_ratio * framebuffer_width); | ||
| 244 | viewport_extent.left = 0; | ||
| 245 | viewport_extent.top = (framebuffer_height - viewport_height) / 2; | ||
| 246 | viewport_extent.right = viewport_extent.left + framebuffer_width; | ||
| 247 | viewport_extent.bottom = viewport_extent.top + viewport_height; | ||
| 248 | } else { | ||
| 249 | // Otherwise, apply borders to the left and right sides of the window. | ||
| 250 | unsigned viewport_width = std::round(framebuffer_height / emulation_aspect_ratio); | ||
| 251 | viewport_extent.left = (framebuffer_width - viewport_width) / 2; | ||
| 252 | viewport_extent.top = 0; | ||
| 253 | viewport_extent.right = viewport_extent.left + viewport_width; | ||
| 254 | viewport_extent.bottom = viewport_extent.top + framebuffer_height; | ||
| 255 | } | ||
| 256 | |||
| 257 | return viewport_extent; | ||
| 258 | } | ||
| 259 | |||
| 231 | /// Initialize the renderer | 260 | /// Initialize the renderer |
| 232 | void RendererOpenGL::Init() { | 261 | void RendererOpenGL::Init() { |
| 233 | render_window->MakeCurrent(); | 262 | render_window->MakeCurrent(); |
| 234 | 263 | ||
| 235 | int err = ogl_LoadFunctions(); | 264 | int err = ogl_LoadFunctions(); |
| 236 | if (ogl_LOAD_SUCCEEDED != err) { | 265 | if (ogl_LOAD_SUCCEEDED != err) { |
| 237 | ERROR_LOG(RENDER, "Failed to initialize GL functions! Exiting..."); | 266 | LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting..."); |
| 238 | exit(-1); | 267 | exit(-1); |
| 239 | } | 268 | } |
| 240 | 269 | ||
| 241 | NOTICE_LOG(RENDER, "GL_VERSION: %s\n", glGetString(GL_VERSION)); | 270 | LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION)); |
| 242 | InitOpenGLObjects(); | 271 | InitOpenGLObjects(); |
| 243 | } | 272 | } |
| 244 | 273 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index eed201a95..7fdcec731 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -4,13 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | |||
| 7 | #include "generated/gl_3_2_core.h" | 9 | #include "generated/gl_3_2_core.h" |
| 8 | 10 | ||
| 9 | #include "common/common.h" | 11 | #include "common/math_util.h" |
| 12 | |||
| 10 | #include "core/hw/gpu.h" | 13 | #include "core/hw/gpu.h" |
| 11 | #include "video_core/renderer_base.h" | ||
| 12 | 14 | ||
| 13 | #include <array> | 15 | #include "video_core/renderer_base.h" |
| 14 | 16 | ||
| 15 | class EmuWindow; | 17 | class EmuWindow; |
| 16 | 18 | ||
| @@ -52,6 +54,9 @@ private: | |||
| 52 | static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, | 54 | static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, |
| 53 | const TextureInfo& texture); | 55 | const TextureInfo& texture); |
| 54 | 56 | ||
| 57 | /// Computes the viewport rectangle | ||
| 58 | MathUtil::Rectangle<unsigned> GetViewportExtent(); | ||
| 59 | |||
| 55 | EmuWindow* render_window; ///< Handle to render window | 60 | EmuWindow* render_window; ///< Handle to render window |
| 56 | u32 last_mode; ///< Last render mode | 61 | u32 last_mode; ///< Last render mode |
| 57 | 62 | ||
diff --git a/src/video_core/utils.cpp b/src/video_core/utils.cpp index c1848f923..f1156a493 100644 --- a/src/video_core/utils.cpp +++ b/src/video_core/utils.cpp | |||
| @@ -20,7 +20,7 @@ namespace VideoCore { | |||
| 20 | void DumpTGA(std::string filename, short width, short height, u8* raw_data) { | 20 | void DumpTGA(std::string filename, short width, short height, u8* raw_data) { |
| 21 | TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0}; | 21 | TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0}; |
| 22 | FILE* fout = fopen(filename.c_str(), "wb"); | 22 | FILE* fout = fopen(filename.c_str(), "wb"); |
| 23 | 23 | ||
| 24 | fwrite(&hdr, sizeof(TGAHeader), 1, fout); | 24 | fwrite(&hdr, sizeof(TGAHeader), 1, fout); |
| 25 | 25 | ||
| 26 | for (int y = 0; y < height; y++) { | 26 | for (int y = 0; y < height; y++) { |
| @@ -30,7 +30,7 @@ void DumpTGA(std::string filename, short width, short height, u8* raw_data) { | |||
| 30 | putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r | 30 | putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r |
| 31 | } | 31 | } |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | fclose(fout); | 34 | fclose(fout); |
| 35 | } | 35 | } |
| 36 | } // namespace | 36 | } // namespace |
diff --git a/src/video_core/utils.h b/src/video_core/utils.h index 9cb3d4d43..21380a908 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h | |||
| @@ -12,24 +12,24 @@ namespace FormatPrecision { | |||
| 12 | 12 | ||
| 13 | /// Adjust RGBA8 color with RGBA6 precision | 13 | /// Adjust RGBA8 color with RGBA6 precision |
| 14 | static inline u32 rgba8_with_rgba6(u32 src) { | 14 | static inline u32 rgba8_with_rgba6(u32 src) { |
| 15 | u32 color = src; | 15 | u32 color = src; |
| 16 | color &= 0xFCFCFCFC; | 16 | color &= 0xFCFCFCFC; |
| 17 | color |= (color >> 6) & 0x03030303; | 17 | color |= (color >> 6) & 0x03030303; |
| 18 | return color; | 18 | return color; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | /// Adjust RGBA8 color with RGB565 precision | 21 | /// Adjust RGBA8 color with RGB565 precision |
| 22 | static inline u32 rgba8_with_rgb565(u32 src) { | 22 | static inline u32 rgba8_with_rgb565(u32 src) { |
| 23 | u32 color = (src & 0xF8FCF8); | 23 | u32 color = (src & 0xF8FCF8); |
| 24 | color |= (color >> 5) & 0x070007; | 24 | color |= (color >> 5) & 0x070007; |
| 25 | color |= (color >> 6) & 0x000300; | 25 | color |= (color >> 6) & 0x000300; |
| 26 | color |= 0xFF000000; | 26 | color |= 0xFF000000; |
| 27 | return color; | 27 | return color; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /// Adjust Z24 depth value with Z16 precision | 30 | /// Adjust Z24 depth value with Z16 precision |
| 31 | static inline u32 z24_with_z16(u32 src) { | 31 | static inline u32 z24_with_z16(u32 src) { |
| 32 | return (src & 0xFFFF00) | (src >> 16); | 32 | return (src & 0xFFFF00) | (src >> 16); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | } // namespace | 35 | } // namespace |
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index 96625791c..477e78cfe 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp | |||
| @@ -2,11 +2,16 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <boost/range/algorithm.hpp> | ||
| 6 | |||
| 7 | #include <common/file_util.h> | ||
| 8 | |||
| 9 | #include <core/mem_map.h> | ||
| 10 | |||
| 11 | #include "debug_utils/debug_utils.h" | ||
| 12 | |||
| 5 | #include "pica.h" | 13 | #include "pica.h" |
| 6 | #include "vertex_shader.h" | 14 | #include "vertex_shader.h" |
| 7 | #include "debug_utils/debug_utils.h" | ||
| 8 | #include <core/mem_map.h> | ||
| 9 | #include <common/file_util.h> | ||
| 10 | 15 | ||
| 11 | namespace Pica { | 16 | namespace Pica { |
| 12 | 17 | ||
| @@ -201,7 +206,7 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 201 | case Instruction::OpCode::CALL: | 206 | case Instruction::OpCode::CALL: |
| 202 | increment_pc = false; | 207 | increment_pc = false; |
| 203 | 208 | ||
| 204 | _dbg_assert_(GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); | 209 | _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); |
| 205 | 210 | ||
| 206 | *++state.call_stack_pointer = state.program_counter - shader_memory; | 211 | *++state.call_stack_pointer = state.program_counter - shader_memory; |
| 207 | // TODO: Does this offset refer to the beginning of shader memory? | 212 | // TODO: Does this offset refer to the beginning of shader memory? |
| @@ -213,7 +218,7 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 213 | break; | 218 | break; |
| 214 | 219 | ||
| 215 | default: | 220 | default: |
| 216 | ERROR_LOG(GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", | 221 | LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", |
| 217 | (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex); | 222 | (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex); |
| 218 | break; | 223 | break; |
| 219 | } | 224 | } |
| @@ -238,7 +243,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | |||
| 238 | // Setup input register table | 243 | // Setup input register table |
| 239 | const auto& attribute_register_map = registers.vs_input_register_map; | 244 | const auto& attribute_register_map = registers.vs_input_register_map; |
| 240 | float24 dummy_register; | 245 | float24 dummy_register; |
| 241 | std::fill(&state.input_register_table[0], &state.input_register_table[16], &dummy_register); | 246 | boost::fill(state.input_register_table, &dummy_register); |
| 242 | if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x; | 247 | if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x; |
| 243 | if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x; | 248 | if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x; |
| 244 | if(num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x; | 249 | if(num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x; |
| @@ -272,8 +277,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | |||
| 272 | 277 | ||
| 273 | state.status_registers[0] = false; | 278 | state.status_registers[0] = false; |
| 274 | state.status_registers[1] = false; | 279 | state.status_registers[1] = false; |
| 275 | std::fill(state.call_stack, state.call_stack + sizeof(state.call_stack) / sizeof(state.call_stack[0]), | 280 | boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS); |
| 276 | VertexShaderState::INVALID_ADDRESS); | ||
| 277 | state.call_stack_pointer = &state.call_stack[0]; | 281 | state.call_stack_pointer = &state.call_stack[0]; |
| 278 | 282 | ||
| 279 | ProcessShaderCode(state); | 283 | ProcessShaderCode(state); |
| @@ -281,7 +285,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | |||
| 281 | state.debug.max_opdesc_id, registers.vs_main_offset, | 285 | state.debug.max_opdesc_id, registers.vs_main_offset, |
| 282 | registers.vs_output_attributes); | 286 | registers.vs_output_attributes); |
| 283 | 287 | ||
| 284 | DEBUG_LOG(GPU, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", | 288 | LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", |
| 285 | ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(), | 289 | ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(), |
| 286 | ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(), | 290 | ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(), |
| 287 | ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32()); | 291 | ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32()); |
diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index 607a8e803..bfb6fb6e3 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h | |||
| @@ -141,7 +141,7 @@ union Instruction { | |||
| 141 | return BitFieldType::Value(); | 141 | return BitFieldType::Value(); |
| 142 | else if (GetRegisterType() == Temporary) | 142 | else if (GetRegisterType() == Temporary) |
| 143 | return BitFieldType::Value() - 0x10; | 143 | return BitFieldType::Value() - 0x10; |
| 144 | else if (GetRegisterType() == FloatUniform) | 144 | else // if (GetRegisterType() == FloatUniform) |
| 145 | return BitFieldType::Value() - 0x20; | 145 | return BitFieldType::Value() - 0x20; |
| 146 | } | 146 | } |
| 147 | 147 | ||
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index c779771c5..6791e4007 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -17,8 +17,8 @@ | |||
| 17 | 17 | ||
| 18 | namespace VideoCore { | 18 | namespace VideoCore { |
| 19 | 19 | ||
| 20 | EmuWindow* g_emu_window = NULL; ///< Frontend emulator window | 20 | EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window |
| 21 | RendererBase* g_renderer = NULL; ///< Renderer plugin | 21 | RendererBase* g_renderer = nullptr; ///< Renderer plugin |
| 22 | int g_current_frame = 0; | 22 | int g_current_frame = 0; |
| 23 | 23 | ||
| 24 | /// Initialize the video core | 24 | /// Initialize the video core |
| @@ -30,13 +30,13 @@ void Init(EmuWindow* emu_window) { | |||
| 30 | 30 | ||
| 31 | g_current_frame = 0; | 31 | g_current_frame = 0; |
| 32 | 32 | ||
| 33 | NOTICE_LOG(VIDEO, "initialized OK"); | 33 | LOG_DEBUG(Render, "initialized OK"); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | /// Shutdown the video core | 36 | /// Shutdown the video core |
| 37 | void Shutdown() { | 37 | void Shutdown() { |
| 38 | delete g_renderer; | 38 | delete g_renderer; |
| 39 | NOTICE_LOG(VIDEO, "shutdown OK"); | 39 | LOG_DEBUG(Render, "shutdown OK"); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | } // namespace | 42 | } // namespace |