diff options
244 files changed, 3975 insertions, 2366 deletions
diff --git a/.gitmodules b/.gitmodules index 54714e5cd..a9e0a5c1a 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -4,3 +4,6 @@ | |||
| 4 | [submodule "externals/boost"] | 4 | [submodule "externals/boost"] |
| 5 | path = externals/boost | 5 | path = externals/boost |
| 6 | url = https://github.com/citra-emu/ext-boost.git | 6 | url = https://github.com/citra-emu/ext-boost.git |
| 7 | [submodule "externals/nihstro"] | ||
| 8 | path = externals/nihstro | ||
| 9 | url = https://github.com/neobrain/nihstro.git | ||
diff --git a/.travis-build.sh b/.travis-build.sh index 78e7583a8..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 |
diff --git a/.travis-deps.sh b/.travis-deps.sh index 2ac3a79ce..2a0f6b284 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 |
| @@ -23,6 +24,8 @@ if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then | |||
| 23 | curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ | 24 | curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ |
| 24 | | sudo tar -xz -C /usr/local --strip-components=1 | 25 | | sudo tar -xz -C /usr/local --strip-components=1 |
| 25 | elif [ "$TRAVIS_OS_NAME" = osx ]; then | 26 | elif [ "$TRAVIS_OS_NAME" = osx ]; then |
| 27 | export HOMEBREW_CACHE="$PWD/.homebrew-cache" | ||
| 28 | mkdir -p "$HOMEBREW_CACHE" | ||
| 26 | brew tap homebrew/versions | 29 | brew tap homebrew/versions |
| 27 | brew install qt5 glfw3 pkgconfig | 30 | brew install qt5 glfw3 pkgconfig |
| 28 | fi | 31 | fi |
diff --git a/.travis.yml b/.travis.yml index 1cb369d5b..8c5aceb7c 100644 --- a/.travis.yml +++ b/.travis.yml | |||
| @@ -4,6 +4,11 @@ os: | |||
| 4 | 4 | ||
| 5 | language: cpp | 5 | language: cpp |
| 6 | 6 | ||
| 7 | cache: | ||
| 8 | apt: true | ||
| 9 | directories: | ||
| 10 | - .homebrew-cache | ||
| 11 | |||
| 7 | before_install: | 12 | before_install: |
| 8 | - sh .travis-deps.sh | 13 | - sh .travis-deps.sh |
| 9 | 14 | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e48645d6..5bb87d50d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -12,13 +12,34 @@ else() | |||
| 12 | 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) | 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) | 14 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) |
| 15 | |||
| 16 | # Tweak optimization settings | ||
| 17 | # As far as I can tell, there's no way to override the CMake defaults while leaving user | ||
| 18 | # changes intact, so we'll just clobber everything and say sorry. | ||
| 19 | message(STATUS "Cache compiler flags ignored, please edit CMakeFiles.txt to change the flags.") | ||
| 20 | # /MD - Multi-threaded runtime | ||
| 21 | # /Ox - Full optimization | ||
| 22 | # /Oi - Use intrinsic functions | ||
| 23 | # /Oy- - Don't omit frame pointer | ||
| 24 | # /GR- - Disable RTTI | ||
| 25 | # /GS- - No stack buffer overflow checks | ||
| 26 | # /EHsc - C++-only exception handling semantics | ||
| 27 | set(optimization_flags "/MD /Ox /Oi /Oy- /DNDEBUG /GR- /GS- /EHsc") | ||
| 28 | # /Zi - Output debugging information | ||
| 29 | # /Zo - enahnced debug info for optimized builds | ||
| 30 | set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE) | ||
| 31 | set(CMAKE_CXX_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE) | ||
| 32 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE) | ||
| 33 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE) | ||
| 15 | endif() | 34 | endif() |
| 16 | add_definitions(-DSINGLETHREADED) | 35 | add_definitions(-DSINGLETHREADED) |
| 17 | 36 | ||
| 18 | find_package(PNG) | 37 | find_package(PNG QUIET) |
| 19 | if (PNG_FOUND) | 38 | if (PNG_FOUND) |
| 20 | add_definitions(-DHAVE_PNG) | 39 | add_definitions(-DHAVE_PNG) |
| 21 | endif () | 40 | else() |
| 41 | message(STATUS "libpng not found. Some debugging features have been disabled.") | ||
| 42 | endif() | ||
| 22 | 43 | ||
| 23 | find_package(Boost) | 44 | find_package(Boost) |
| 24 | if (Boost_FOUND) | 45 | if (Boost_FOUND) |
| @@ -138,6 +159,8 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") | |||
| 138 | include_directories(${INI_PREFIX}) | 159 | include_directories(${INI_PREFIX}) |
| 139 | add_subdirectory(${INI_PREFIX}) | 160 | add_subdirectory(${INI_PREFIX}) |
| 140 | 161 | ||
| 162 | include_directories(externals/nihstro/include) | ||
| 163 | |||
| 141 | # process subdirectories | 164 | # process subdirectories |
| 142 | if(ENABLE_QT) | 165 | if(ENABLE_QT) |
| 143 | include_directories(externals/qhexedit) | 166 | include_directories(externals/qhexedit) |
| @@ -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. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! None of these run to a playable state, but we are working every day to advance the project forward. |
| 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/nihstro b/externals/nihstro new file mode 160000 | |||
| Subproject fc71f8684d26ccf277ad68809c8bd7273141fe8 | |||
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index d6e8a4ec7..f6a52758b 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <thread> | 5 | #include <thread> |
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 92764809e..b9d6441be 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <GLFW/glfw3.h> | 5 | #include <GLFW/glfw3.h> |
diff --git a/src/citra/config.h b/src/citra/config.h index 2b46fa8aa..0eb176c7d 100644 --- a/src/citra/config.h +++ b/src/citra/config.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 7cf543e07..a281c536f 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 929e09f43..a6282809b 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <GLFW/glfw3.h> | 5 | #include <GLFW/glfw3.h> |
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 5b04e87bb..5252fccc8 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 0ae6b8b2d..0fea8e4f9 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QString> | 5 | #include <QString> |
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 4c95d0cb9..4485cae73 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index 895851be3..a9ec2f7fe 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp | |||
| @@ -27,10 +27,10 @@ void CallstackWidget::OnCPUStepped() | |||
| 27 | ARM_Interface* app_core = Core::g_app_core; | 27 | ARM_Interface* app_core = Core::g_app_core; |
| 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 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 (u32 addr = 0x10000000; addr >= sp; addr -= 4) |
| 34 | { | 34 | { |
| 35 | ret_addr = Memory::Read32(addr); | 35 | ret_addr = Memory::Read32(addr); |
| 36 | call_addr = ret_addr - 4; //get call address??? | 36 | call_addr = ret_addr - 4; //get call address??? |
diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics.cpp index a86a55404..6ff4c290d 100644 --- a/src/citra_qt/debugger/graphics.cpp +++ b/src/citra_qt/debugger/graphics.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "graphics.hxx" | 5 | #include "graphics.hxx" |
diff --git a/src/citra_qt/debugger/graphics.hxx b/src/citra_qt/debugger/graphics.hxx index 72656f93c..8119b4c87 100644 --- a/src/citra_qt/debugger/graphics.hxx +++ b/src/citra_qt/debugger/graphics.hxx | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 53394b6e6..9486f06cc 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QMetaType> | 5 | #include <QMetaType> |
| @@ -39,15 +39,17 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | |||
| 39 | switch (index.column()) { | 39 | switch (index.column()) { |
| 40 | case 0: | 40 | case 0: |
| 41 | { | 41 | { |
| 42 | std::map<Pica::DebugContext::Event, QString> map; | 42 | static const std::map<Pica::DebugContext::Event, QString> map = { |
| 43 | map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")}); | 43 | { Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") }, |
| 44 | map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")}); | 44 | { Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") }, |
| 45 | map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")}); | 45 | { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, |
| 46 | map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}); | 46 | { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, |
| 47 | { Pica::DebugContext::Event::VertexLoaded, tr("Vertex Loaded") } | ||
| 48 | }; | ||
| 47 | 49 | ||
| 48 | _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | 50 | _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); |
| 49 | 51 | ||
| 50 | return map[event]; | 52 | return (map.find(event) != map.end()) ? map.at(event) : QString(); |
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | case 1: | 55 | case 1: |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.hxx b/src/citra_qt/debugger/graphics_breakpoints.hxx index 2142c6fa1..5b9ba324e 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.hxx +++ b/src/citra_qt/debugger/graphics_breakpoints.hxx | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.hxx b/src/citra_qt/debugger/graphics_breakpoints_p.hxx index bf5daf73d..232bfc863 100644 --- a/src/citra_qt/debugger/graphics_breakpoints_p.hxx +++ b/src/citra_qt/debugger/graphics_breakpoints_p.hxx | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 7f97cf143..753cc25da 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QLabel> | 5 | #include <QLabel> |
| @@ -24,7 +24,7 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | |||
| 24 | QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); | 24 | QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); |
| 25 | for (int y = 0; y < info.height; ++y) { | 25 | for (int y = 0; y < info.height; ++y) { |
| 26 | for (int x = 0; x < info.width; ++x) { | 26 | for (int x = 0; x < info.width; ++x) { |
| 27 | Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info); | 27 | Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info, true); |
| 28 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | 28 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); |
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| @@ -47,7 +47,7 @@ public: | |||
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) | 49 | TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) |
| 50 | : QDockWidget(tr("Texture 0x%1").arg(info.address, 8, 16, QLatin1Char('0'))), | 50 | : QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))), |
| 51 | info(info) { | 51 | info(info) { |
| 52 | 52 | ||
| 53 | QWidget* main_widget = new QWidget; | 53 | QWidget* main_widget = new QWidget; |
| @@ -60,7 +60,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 60 | phys_address_spinbox->SetBase(16); | 60 | phys_address_spinbox->SetBase(16); |
| 61 | phys_address_spinbox->SetRange(0, 0xFFFFFFFF); | 61 | phys_address_spinbox->SetRange(0, 0xFFFFFFFF); |
| 62 | phys_address_spinbox->SetPrefix("0x"); | 62 | phys_address_spinbox->SetPrefix("0x"); |
| 63 | phys_address_spinbox->SetValue(info.address); | 63 | phys_address_spinbox->SetValue(info.physical_address); |
| 64 | connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64))); | 64 | connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64))); |
| 65 | 65 | ||
| 66 | QComboBox* format_choice = new QComboBox; | 66 | QComboBox* format_choice = new QComboBox; |
| @@ -69,6 +69,13 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 69 | format_choice->addItem(tr("RGBA5551")); | 69 | format_choice->addItem(tr("RGBA5551")); |
| 70 | format_choice->addItem(tr("RGB565")); | 70 | format_choice->addItem(tr("RGB565")); |
| 71 | format_choice->addItem(tr("RGBA4")); | 71 | format_choice->addItem(tr("RGBA4")); |
| 72 | format_choice->addItem(tr("IA8")); | ||
| 73 | format_choice->addItem(tr("UNK6")); | ||
| 74 | format_choice->addItem(tr("I8")); | ||
| 75 | format_choice->addItem(tr("A8")); | ||
| 76 | format_choice->addItem(tr("IA4")); | ||
| 77 | format_choice->addItem(tr("UNK10")); | ||
| 78 | format_choice->addItem(tr("A4")); | ||
| 72 | format_choice->setCurrentIndex(static_cast<int>(info.format)); | 79 | format_choice->setCurrentIndex(static_cast<int>(info.format)); |
| 73 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); | 80 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); |
| 74 | 81 | ||
| @@ -125,7 +132,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 125 | } | 132 | } |
| 126 | 133 | ||
| 127 | void TextureInfoDockWidget::OnAddressChanged(qint64 value) { | 134 | void TextureInfoDockWidget::OnAddressChanged(qint64 value) { |
| 128 | info.address = value; | 135 | info.physical_address = value; |
| 129 | emit UpdatePixmap(ReloadPixmap()); | 136 | emit UpdatePixmap(ReloadPixmap()); |
| 130 | } | 137 | } |
| 131 | 138 | ||
| @@ -150,7 +157,7 @@ void TextureInfoDockWidget::OnStrideChanged(int value) { | |||
| 150 | } | 157 | } |
| 151 | 158 | ||
| 152 | QPixmap TextureInfoDockWidget::ReloadPixmap() const { | 159 | QPixmap TextureInfoDockWidget::ReloadPixmap() const { |
| 153 | u8* src = Memory::GetPointer(info.address); | 160 | u8* src = Memory::GetPointer(Pica::PAddrToVAddr(info.physical_address)); |
| 154 | return QPixmap::fromImage(LoadTexture(src, info)); | 161 | return QPixmap::fromImage(LoadTexture(src, info)); |
| 155 | } | 162 | } |
| 156 | 163 | ||
| @@ -223,9 +230,21 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | |||
| 223 | 230 | ||
| 224 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | 231 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { |
| 225 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | 232 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); |
| 226 | if (COMMAND_IN_RANGE(command_id, texture0)) { | 233 | if (COMMAND_IN_RANGE(command_id, texture0) || |
| 227 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | 234 | COMMAND_IN_RANGE(command_id, texture1) || |
| 228 | Pica::registers.texture0_format); | 235 | COMMAND_IN_RANGE(command_id, texture2)) { |
| 236 | |||
| 237 | unsigned index; | ||
| 238 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 239 | index = 0; | ||
| 240 | } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||
| 241 | index = 1; | ||
| 242 | } else { | ||
| 243 | index = 2; | ||
| 244 | } | ||
| 245 | auto config = Pica::registers.GetTextures()[index].config; | ||
| 246 | auto format = Pica::registers.GetTextures()[index].format; | ||
| 247 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 229 | 248 | ||
| 230 | // TODO: Instead, emit a signal here to be caught by the main window widget. | 249 | // TODO: Instead, emit a signal here to be caught by the main window widget. |
| 231 | auto main_window = static_cast<QMainWindow*>(parent()); | 250 | auto main_window = static_cast<QMainWindow*>(parent()); |
| @@ -237,10 +256,23 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | |||
| 237 | QWidget* new_info_widget; | 256 | QWidget* new_info_widget; |
| 238 | 257 | ||
| 239 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | 258 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); |
| 240 | if (COMMAND_IN_RANGE(command_id, texture0)) { | 259 | if (COMMAND_IN_RANGE(command_id, texture0) || |
| 241 | u8* src = Memory::GetPointer(Pica::registers.texture0.GetPhysicalAddress()); | 260 | COMMAND_IN_RANGE(command_id, texture1) || |
| 242 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | 261 | COMMAND_IN_RANGE(command_id, texture2)) { |
| 243 | Pica::registers.texture0_format); | 262 | |
| 263 | unsigned index; | ||
| 264 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 265 | index = 0; | ||
| 266 | } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||
| 267 | index = 1; | ||
| 268 | } else { | ||
| 269 | index = 2; | ||
| 270 | } | ||
| 271 | auto config = Pica::registers.GetTextures()[index].config; | ||
| 272 | auto format = Pica::registers.GetTextures()[index].format; | ||
| 273 | |||
| 274 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 275 | u8* src = Memory::GetPointer(Pica::PAddrToVAddr(config.GetPhysicalAddress())); | ||
| 244 | new_info_widget = new TextureInfoWidget(src, info); | 276 | new_info_widget = new TextureInfoWidget(src, info); |
| 245 | } else { | 277 | } else { |
| 246 | new_info_widget = new QWidget; | 278 | new_info_widget = new QWidget; |
diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx index a459bba64..a465d044c 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.hxx +++ b/src/citra_qt/debugger/graphics_cmdlists.hxx | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index ac47f298d..dd41c3880 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QBoxLayout> | 5 | #include <QBoxLayout> |
| @@ -125,7 +125,8 @@ GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::Debug | |||
| 125 | setWidget(main_widget); | 125 | setWidget(main_widget); |
| 126 | 126 | ||
| 127 | // Load current data - TODO: Make sure this works when emulation is not running | 127 | // Load current data - TODO: Make sure this works when emulation is not running |
| 128 | emit Update(); | 128 | if (debug_context && debug_context->at_breakpoint) |
| 129 | emit Update(); | ||
| 129 | widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint | 130 | widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint |
| 130 | } | 131 | } |
| 131 | 132 | ||
| @@ -198,7 +199,7 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 198 | auto framebuffer = Pica::registers.framebuffer; | 199 | auto framebuffer = Pica::registers.framebuffer; |
| 199 | using Framebuffer = decltype(framebuffer); | 200 | using Framebuffer = decltype(framebuffer); |
| 200 | 201 | ||
| 201 | framebuffer_address = framebuffer.GetColorBufferAddress(); | 202 | framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); |
| 202 | framebuffer_width = framebuffer.GetWidth(); | 203 | framebuffer_width = framebuffer.GetWidth(); |
| 203 | framebuffer_height = framebuffer.GetHeight(); | 204 | framebuffer_height = framebuffer.GetHeight(); |
| 204 | framebuffer_format = static_cast<Format>(framebuffer.color_format); | 205 | framebuffer_format = static_cast<Format>(framebuffer.color_format); |
| @@ -223,9 +224,9 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 223 | case Format::RGBA8: | 224 | case Format::RGBA8: |
| 224 | { | 225 | { |
| 225 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | 226 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); |
| 226 | u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address); | 227 | u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); |
| 227 | for (int y = 0; y < framebuffer_height; ++y) { | 228 | for (unsigned y = 0; y < framebuffer_height; ++y) { |
| 228 | for (int x = 0; x < framebuffer_width; ++x) { | 229 | for (unsigned x = 0; x < framebuffer_width; ++x) { |
| 229 | u32 value = *(color_buffer + x + y * framebuffer_width); | 230 | u32 value = *(color_buffer + x + y * framebuffer_width); |
| 230 | 231 | ||
| 231 | decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/)); | 232 | decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/)); |
| @@ -238,9 +239,9 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 238 | case Format::RGB8: | 239 | case Format::RGB8: |
| 239 | { | 240 | { |
| 240 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | 241 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); |
| 241 | u8* color_buffer = Memory::GetPointer(framebuffer_address); | 242 | u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); |
| 242 | for (int y = 0; y < framebuffer_height; ++y) { | 243 | for (unsigned y = 0; y < framebuffer_height; ++y) { |
| 243 | for (int x = 0; x < framebuffer_width; ++x) { | 244 | for (unsigned x = 0; x < framebuffer_width; ++x) { |
| 244 | u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width; | 245 | u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width; |
| 245 | 246 | ||
| 246 | decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/)); | 247 | decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/)); |
| @@ -253,9 +254,9 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 253 | case Format::RGBA5551: | 254 | case Format::RGBA5551: |
| 254 | { | 255 | { |
| 255 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | 256 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); |
| 256 | u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address); | 257 | u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); |
| 257 | for (int y = 0; y < framebuffer_height; ++y) { | 258 | for (unsigned y = 0; y < framebuffer_height; ++y) { |
| 258 | for (int x = 0; x < framebuffer_width; ++x) { | 259 | for (unsigned x = 0; x < framebuffer_width; ++x) { |
| 259 | u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2); | 260 | u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2); |
| 260 | u8 r = (value >> 11) & 0x1F; | 261 | u8 r = (value >> 11) & 0x1F; |
| 261 | u8 g = (value >> 6) & 0x1F; | 262 | u8 g = (value >> 6) & 0x1F; |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.hxx b/src/citra_qt/debugger/graphics_framebuffer.hxx index 1151ee7a1..56215761e 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.hxx +++ b/src/citra_qt/debugger/graphics_framebuffer.hxx | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 1299338ac..23d4925b8 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -157,12 +157,6 @@ void GMainWindow::BootGame(std::string filename) | |||
| 157 | LOG_INFO(Frontend, "Citra starting...\n"); | 157 | LOG_INFO(Frontend, "Citra starting...\n"); |
| 158 | System::Init(render_window); | 158 | System::Init(render_window); |
| 159 | 159 | ||
| 160 | if (Core::Init()) { | ||
| 161 | LOG_CRITICAL(Frontend, "Core initialization failed, exiting..."); | ||
| 162 | Core::Stop(); | ||
| 163 | exit(1); | ||
| 164 | } | ||
| 165 | |||
| 166 | // Load a game or die... | 160 | // Load a game or die... |
| 167 | if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { | 161 | if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { |
| 168 | LOG_CRITICAL(Frontend, "Failed to load ROM!"); | 162 | LOG_CRITICAL(Frontend, "Failed to load ROM!"); |
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp index 9672168f5..f9988409f 100644 --- a/src/citra_qt/util/spinbox.cpp +++ b/src/citra_qt/util/spinbox.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Licensed under GPLv2+ | 1 | // Licensed under GPLv2 or any later version |
| 2 | // Refer to the license.txt file included. | 2 | // Refer to the license.txt file included. |
| 3 | 3 | ||
| 4 | 4 | ||
| @@ -238,7 +238,7 @@ QValidator::State CSpinBox::validate(QString& input, int& pos) const | |||
| 238 | if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) | 238 | if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) |
| 239 | return QValidator::Invalid; | 239 | return QValidator::Invalid; |
| 240 | 240 | ||
| 241 | unsigned strpos = prefix.length(); | 241 | int strpos = prefix.length(); |
| 242 | 242 | ||
| 243 | // Empty "numbers" allowed as intermediate values | 243 | // Empty "numbers" allowed as intermediate values |
| 244 | if (strpos >= input.length() - HasSign() - suffix.length()) | 244 | if (strpos >= input.length() - HasSign() - suffix.length()) |
diff --git a/src/citra_qt/util/spinbox.hxx b/src/citra_qt/util/spinbox.hxx index 68f5b9894..ee7f08ec2 100644 --- a/src/citra_qt/util/spinbox.hxx +++ b/src/citra_qt/util/spinbox.hxx | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Licensed under GPLv2+ | 1 | // Licensed under GPLv2 or any later version |
| 2 | // Refer to the license.txt file included. | 2 | // Refer to the license.txt file included. |
| 3 | 3 | ||
| 4 | 4 | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 15989708d..3c3419bbc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -49,6 +49,7 @@ set(HEADERS | |||
| 49 | logging/filter.h | 49 | logging/filter.h |
| 50 | logging/log.h | 50 | logging/log.h |
| 51 | logging/backend.h | 51 | logging/backend.h |
| 52 | make_unique.h | ||
| 52 | math_util.h | 53 | math_util.h |
| 53 | mem_arena.h | 54 | mem_arena.h |
| 54 | memory_util.h | 55 | memory_util.h |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 9e02210f9..8eab054b8 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Licensed under GPLv2 | 1 | // Licensed under GPLv2 or any later version |
| 2 | // Refer to the license.txt file included. | 2 | // Refer to the license.txt file included. |
| 3 | 3 | ||
| 4 | 4 | ||
| @@ -142,7 +142,7 @@ public: | |||
| 142 | 142 | ||
| 143 | __forceinline BitField& operator=(T val) | 143 | __forceinline BitField& operator=(T val) |
| 144 | { | 144 | { |
| 145 | storage = (storage & ~GetMask()) | (((StorageType)val << position) & GetMask()); | 145 | Assign(val); |
| 146 | return *this; | 146 | return *this; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| @@ -151,6 +151,10 @@ public: | |||
| 151 | return Value(); | 151 | return Value(); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | __forceinline void Assign(const T& value) { | ||
| 155 | storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); | ||
| 156 | } | ||
| 157 | |||
| 154 | __forceinline T Value() const | 158 | __forceinline T Value() const |
| 155 | { | 159 | { |
| 156 | if (std::numeric_limits<T>::is_signed) | 160 | if (std::numeric_limits<T>::is_signed) |
| @@ -164,6 +168,12 @@ public: | |||
| 164 | } | 168 | } |
| 165 | } | 169 | } |
| 166 | 170 | ||
| 171 | // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015 | ||
| 172 | __forceinline bool ToBool() const | ||
| 173 | { | ||
| 174 | return Value() != 0; | ||
| 175 | } | ||
| 176 | |||
| 167 | private: | 177 | private: |
| 168 | // StorageType is T for non-enum types and the underlying type of T if | 178 | // StorageType is T for non-enum types and the underlying type of T if |
| 169 | // T is an enumeration. Note that T is wrapped within an enable_if in the | 179 | // T is an enumeration. Note that T is wrapped within an enable_if in the |
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp index 587dbf40a..6696935fa 100644 --- a/src/common/break_points.cpp +++ b/src/common/break_points.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
diff --git a/src/common/break_points.h b/src/common/break_points.h index cf3884fbc..5557cd50e 100644 --- a/src/common/break_points.h +++ b/src/common/break_points.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/common.h b/src/common/common.h index 9f3016d34..66f0ccd0c 100644 --- a/src/common/common.h +++ b/src/common/common.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 67b3679b0..ca7abbea6 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 42e1a29c1..9d62a8368 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -40,7 +40,9 @@ | |||
| 40 | #define MAPS_DIR "maps" | 40 | #define MAPS_DIR "maps" |
| 41 | #define CACHE_DIR "cache" | 41 | #define CACHE_DIR "cache" |
| 42 | #define SDMC_DIR "sdmc" | 42 | #define SDMC_DIR "sdmc" |
| 43 | #define SAVEDATA_DIR "savedata" | ||
| 43 | #define SYSDATA_DIR "sysdata" | 44 | #define SYSDATA_DIR "sysdata" |
| 45 | #define SYSSAVEDATA_DIR "syssavedata" | ||
| 44 | #define SHADERCACHE_DIR "shader_cache" | 46 | #define SHADERCACHE_DIR "shader_cache" |
| 45 | #define STATESAVES_DIR "state_saves" | 47 | #define STATESAVES_DIR "state_saves" |
| 46 | #define SCREENSHOTS_DIR "screenShots" | 48 | #define SCREENSHOTS_DIR "screenShots" |
diff --git a/src/common/concurrent_ring_buffer.h b/src/common/concurrent_ring_buffer.h index 2951d93db..311bb01f4 100644 --- a/src/common/concurrent_ring_buffer.h +++ b/src/common/concurrent_ring_buffer.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/cpu_detect.h b/src/common/cpu_detect.h index def6afdee..b585f9608 100644 --- a/src/common/cpu_detect.h +++ b/src/common/cpu_detect.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 7a2c50ac8..4ec7b263a 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "emu_window.h" | 5 | #include "emu_window.h" |
diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 4cb94fed1..1ad4b82a3 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/file_search.cpp b/src/common/file_search.cpp index bfb54ce72..b3a0a84fb 100644 --- a/src/common/file_search.cpp +++ b/src/common/file_search.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
diff --git a/src/common/file_search.h b/src/common/file_search.h index f966a008d..55ca02638 100644 --- a/src/common/file_search.h +++ b/src/common/file_search.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 88c46c117..bba830c70 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
| @@ -676,7 +676,9 @@ 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; | ||
| 679 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | 680 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; |
| 681 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 680 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 682 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 681 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 683 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 682 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 684 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
| @@ -718,6 +720,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 718 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 720 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 719 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 721 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 720 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 722 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 723 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 724 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 721 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 725 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 722 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 726 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 723 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 727 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
diff --git a/src/common/file_util.h b/src/common/file_util.h index a9d48cfe8..293c30941 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -27,7 +27,9 @@ enum { | |||
| 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_SAVEDATA_IDX, | ||
| 30 | D_SYSDATA_IDX, | 31 | D_SYSDATA_IDX, |
| 32 | D_SYSSAVEDATA_IDX, | ||
| 31 | D_HIRESTEXTURES_IDX, | 33 | D_HIRESTEXTURES_IDX, |
| 32 | D_DUMP_IDX, | 34 | D_DUMP_IDX, |
| 33 | D_DUMPFRAMES_IDX, | 35 | D_DUMPFRAMES_IDX, |
diff --git a/src/common/hash.cpp b/src/common/hash.cpp index 2ddcfe6b7..fe2c9e636 100644 --- a/src/common/hash.cpp +++ b/src/common/hash.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
diff --git a/src/common/hash.h b/src/common/hash.h index 29f699d7f..3ac42bc44 100644 --- a/src/common/hash.h +++ b/src/common/hash.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index 309caab98..d8945bb13 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "key_map.h" | 5 | #include "key_map.h" |
diff --git a/src/common/key_map.h b/src/common/key_map.h index bf72362c0..8d949b852 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h index bb1b5174f..74ce74aba 100644 --- a/src/common/linear_disk_cache.h +++ b/src/common/linear_disk_cache.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/log.h b/src/common/log.h index 663eda9ad..96d97249f 100644 --- a/src/common/log.h +++ b/src/common/log.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index e79b84604..816d1bb55 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index ae270efe8..1c44c929e 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 0cf9b05e7..50f2e13f4 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 32b14b159..c3da9989f 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 1eec34fbb..d1c391862 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index f6b02fd47..ef5739d84 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 1f73ca44a..2f05794f0 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/make_unique.h b/src/common/make_unique.h new file mode 100644 index 000000000..2a7b76412 --- /dev/null +++ b/src/common/make_unique.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | template <typename T, typename... Args> | ||
| 12 | std::unique_ptr<T> make_unique(Args&&... args) { | ||
| 13 | return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | ||
| 14 | } | ||
| 15 | |||
| 16 | } // namespace | ||
diff --git a/src/common/math_util.cpp b/src/common/math_util.cpp index 3613e82a6..a83592dd2 100644 --- a/src/common/math_util.cpp +++ b/src/common/math_util.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
diff --git a/src/common/math_util.h b/src/common/math_util.h index b10a25c13..43b0e0dc3 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index ca8a2a9e4..8f982da89 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
diff --git a/src/common/memory_util.h b/src/common/memory_util.h index 922bd44b2..9fdbf1f12 100644 --- a/src/common/memory_util.h +++ b/src/common/memory_util.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/misc.cpp b/src/common/misc.cpp index bc9d26188..e33055d10 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp index 7ffedc45a..4a47b518e 100644 --- a/src/common/msg_handler.cpp +++ b/src/common/msg_handler.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstdio> | 5 | #include <cstdio> |
diff --git a/src/common/msg_handler.h b/src/common/msg_handler.h index 9bfdf950e..7bb216e98 100644 --- a/src/common/msg_handler.h +++ b/src/common/msg_handler.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index d34664614..0ef190afa 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index 1d3e59319..263beaf0e 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 6d9612fb5..d919b7a4c 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <boost/range/algorithm.hpp> | 5 | #include <boost/range/algorithm.hpp> |
diff --git a/src/common/string_util.h b/src/common/string_util.h index 7d75691b1..74974263f 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/symbols.cpp b/src/common/symbols.cpp index 63ad6218b..9e4dccfb3 100644 --- a/src/common/symbols.cpp +++ b/src/common/symbols.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/symbols.h" | 5 | #include "common/symbols.h" |
diff --git a/src/common/symbols.h b/src/common/symbols.h index 4560f5240..f76cb6b1e 100644 --- a/src/common/symbols.h +++ b/src/common/symbols.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index dc153ba71..8c83d67b5 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/thread.h" | 5 | #include "common/thread.h" |
diff --git a/src/common/thread.h b/src/common/thread.h index 8c36d3f07..eaf1ba00c 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 7e3b620c7..4e1c0a215 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/thunk.h b/src/common/thunk.h index 90c8be888..4fb7c98e1 100644 --- a/src/common/thunk.h +++ b/src/common/thunk.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/common/timer.cpp b/src/common/timer.cpp index 4a797f751..a6682ea19 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <time.h> | 5 | #include <time.h> |
diff --git a/src/common/timer.h b/src/common/timer.h index 86418e7a7..4b44c33a0 100644 --- a/src/common/timer.h +++ b/src/common/timer.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 567d7454e..c00fc3493 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -18,13 +18,13 @@ 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/archive_systemsavedata.cpp | ||
| 24 | file_sys/disk_archive.cpp | ||
| 22 | file_sys/file_romfs.cpp | 25 | file_sys/file_romfs.cpp |
| 23 | file_sys/file_sdmc.cpp | ||
| 24 | file_sys/directory_romfs.cpp | 26 | file_sys/directory_romfs.cpp |
| 25 | file_sys/directory_sdmc.cpp | ||
| 26 | hle/kernel/address_arbiter.cpp | 27 | hle/kernel/address_arbiter.cpp |
| 27 | hle/kernel/archive.cpp | ||
| 28 | hle/kernel/event.cpp | 28 | hle/kernel/event.cpp |
| 29 | hle/kernel/kernel.cpp | 29 | hle/kernel/kernel.cpp |
| 30 | hle/kernel/mutex.cpp | 30 | hle/kernel/mutex.cpp |
| @@ -32,21 +32,27 @@ set(SRCS | |||
| 32 | hle/kernel/shared_memory.cpp | 32 | hle/kernel/shared_memory.cpp |
| 33 | hle/kernel/thread.cpp | 33 | hle/kernel/thread.cpp |
| 34 | hle/service/ac_u.cpp | 34 | hle/service/ac_u.cpp |
| 35 | hle/service/am_app.cpp | ||
| 35 | hle/service/am_net.cpp | 36 | hle/service/am_net.cpp |
| 36 | hle/service/apt_u.cpp | 37 | hle/service/apt_u.cpp |
| 37 | hle/service/boss_u.cpp | 38 | hle/service/boss_u.cpp |
| 38 | hle/service/cfg_i.cpp | 39 | hle/service/cecd_u.cpp |
| 39 | hle/service/cfg_u.cpp | 40 | hle/service/cfg/cfg.cpp |
| 41 | hle/service/cfg/cfg_i.cpp | ||
| 42 | hle/service/cfg/cfg_u.cpp | ||
| 40 | hle/service/csnd_snd.cpp | 43 | hle/service/csnd_snd.cpp |
| 41 | hle/service/dsp_dsp.cpp | 44 | hle/service/dsp_dsp.cpp |
| 42 | hle/service/err_f.cpp | 45 | hle/service/err_f.cpp |
| 43 | hle/service/fs_user.cpp | 46 | hle/service/fs/archive.cpp |
| 47 | hle/service/fs/fs_user.cpp | ||
| 44 | hle/service/frd_u.cpp | 48 | hle/service/frd_u.cpp |
| 45 | hle/service/gsp_gpu.cpp | 49 | hle/service/gsp_gpu.cpp |
| 46 | hle/service/hid_user.cpp | 50 | hle/service/hid_user.cpp |
| 47 | hle/service/ir_rst.cpp | 51 | hle/service/ir_rst.cpp |
| 48 | hle/service/ir_u.cpp | 52 | hle/service/ir_u.cpp |
| 53 | hle/service/ldr_ro.cpp | ||
| 49 | hle/service/mic_u.cpp | 54 | hle/service/mic_u.cpp |
| 55 | hle/service/nim_aoc.cpp | ||
| 50 | hle/service/ndm_u.cpp | 56 | hle/service/ndm_u.cpp |
| 51 | hle/service/nwm_uds.cpp | 57 | hle/service/nwm_uds.cpp |
| 52 | hle/service/pm_app.cpp | 58 | hle/service/pm_app.cpp |
| @@ -93,39 +99,46 @@ set(HEADERS | |||
| 93 | arm/skyeye_common/vfp/vfp.h | 99 | arm/skyeye_common/vfp/vfp.h |
| 94 | arm/skyeye_common/vfp/vfp_helper.h | 100 | arm/skyeye_common/vfp/vfp_helper.h |
| 95 | arm/arm_interface.h | 101 | arm/arm_interface.h |
| 96 | file_sys/archive.h | 102 | file_sys/archive_backend.h |
| 97 | file_sys/archive_romfs.h | 103 | file_sys/archive_romfs.h |
| 104 | file_sys/archive_savedata.h | ||
| 98 | file_sys/archive_sdmc.h | 105 | file_sys/archive_sdmc.h |
| 99 | file_sys/file.h | 106 | file_sys/archive_systemsavedata.h |
| 107 | file_sys/disk_archive.h | ||
| 108 | file_sys/file_backend.h | ||
| 100 | file_sys/file_romfs.h | 109 | file_sys/file_romfs.h |
| 101 | file_sys/file_sdmc.h | 110 | file_sys/directory_backend.h |
| 102 | file_sys/directory.h | ||
| 103 | file_sys/directory_romfs.h | 111 | file_sys/directory_romfs.h |
| 104 | file_sys/directory_sdmc.h | ||
| 105 | hle/kernel/address_arbiter.h | 112 | hle/kernel/address_arbiter.h |
| 106 | hle/kernel/archive.h | ||
| 107 | hle/kernel/event.h | 113 | hle/kernel/event.h |
| 108 | hle/kernel/kernel.h | 114 | hle/kernel/kernel.h |
| 109 | hle/kernel/mutex.h | 115 | hle/kernel/mutex.h |
| 110 | hle/kernel/semaphore.h | 116 | hle/kernel/semaphore.h |
| 117 | hle/kernel/session.h | ||
| 111 | hle/kernel/shared_memory.h | 118 | hle/kernel/shared_memory.h |
| 112 | hle/kernel/thread.h | 119 | hle/kernel/thread.h |
| 113 | hle/service/ac_u.h | 120 | hle/service/ac_u.h |
| 121 | hle/service/am_app.h | ||
| 114 | hle/service/am_net.h | 122 | hle/service/am_net.h |
| 115 | hle/service/apt_u.h | 123 | hle/service/apt_u.h |
| 116 | hle/service/boss_u.h | 124 | hle/service/boss_u.h |
| 117 | hle/service/cfg_i.h | 125 | hle/service/cecd_u.h |
| 118 | hle/service/cfg_u.h | 126 | hle/service/cfg/cfg.h |
| 127 | hle/service/cfg/cfg_i.h | ||
| 128 | hle/service/cfg/cfg_u.h | ||
| 119 | hle/service/csnd_snd.h | 129 | hle/service/csnd_snd.h |
| 120 | hle/service/dsp_dsp.h | 130 | hle/service/dsp_dsp.h |
| 121 | hle/service/err_f.h | 131 | hle/service/err_f.h |
| 122 | hle/service/fs_user.h | 132 | hle/service/fs/archive.h |
| 133 | hle/service/fs/fs_user.h | ||
| 123 | hle/service/frd_u.h | 134 | hle/service/frd_u.h |
| 124 | hle/service/gsp_gpu.h | 135 | hle/service/gsp_gpu.h |
| 125 | hle/service/hid_user.h | 136 | hle/service/hid_user.h |
| 126 | hle/service/ir_rst.h | 137 | hle/service/ir_rst.h |
| 127 | hle/service/ir_u.h | 138 | hle/service/ir_u.h |
| 139 | hle/service/ldr_ro.h | ||
| 128 | hle/service/mic_u.h | 140 | hle/service/mic_u.h |
| 141 | hle/service/nim_aoc.h | ||
| 129 | hle/service/ndm_u.h | 142 | hle/service/ndm_u.h |
| 130 | hle/service/nwm_uds.h | 143 | hle/service/nwm_uds.h |
| 131 | hle/service/pm_app.h | 144 | hle/service/pm_app.h |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 3ae528562..c59355339 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp index 55278474b..13d26d170 100644 --- a/src/core/arm/disassembler/load_symbol_map.cpp +++ b/src/core/arm/disassembler/load_symbol_map.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <string> | 5 | #include <string> |
diff --git a/src/core/arm/disassembler/load_symbol_map.h b/src/core/arm/disassembler/load_symbol_map.h index 837cca99b..d28c551c3 100644 --- a/src/core/arm/disassembler/load_symbol_map.h +++ b/src/core/arm/disassembler/load_symbol_map.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 6c8ea211e..6d4fb1b48 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/arm/skyeye_common/armcpu.h" | 5 | #include "core/arm/skyeye_common/armcpu.h" |
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 51eea41ed..6fa2a0ba7 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/arm/dyncom/arm_dyncom_dec.h b/src/core/arm/dyncom/arm_dyncom_dec.h index 19d94f369..70eb96e93 100644 --- a/src/core/arm/dyncom/arm_dyncom_dec.h +++ b/src/core/arm/dyncom/arm_dyncom_dec.h | |||
| @@ -56,8 +56,6 @@ | |||
| 56 | #define RN ((instr >> 16) & 0xF) | 56 | #define RN ((instr >> 16) & 0xF) |
| 57 | /*xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 */ | 57 | /*xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 */ |
| 58 | #define RM (instr & 0xF) | 58 | #define RM (instr & 0xF) |
| 59 | #define BIT(n) ((instr >> (n)) & 1) | ||
| 60 | #define BITS(a,b) ((instr >> (a)) & ((1 << (1+(b)-(a)))-1)) | ||
| 61 | 59 | ||
| 62 | /* CP15 registers */ | 60 | /* CP15 registers */ |
| 63 | #define OPCODE_1 BITS(21, 23) | 61 | #define OPCODE_1 BITS(21, 23) |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 68012bffd..460001b1a 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -1019,6 +1019,15 @@ typedef struct _arm_inst { | |||
| 1019 | char component[0]; | 1019 | char component[0]; |
| 1020 | } arm_inst; | 1020 | } arm_inst; |
| 1021 | 1021 | ||
| 1022 | typedef struct generic_arm_inst { | ||
| 1023 | u32 Ra; | ||
| 1024 | u32 Rm; | ||
| 1025 | u32 Rn; | ||
| 1026 | u32 Rd; | ||
| 1027 | u8 op1; | ||
| 1028 | u8 op2; | ||
| 1029 | } generic_arm_inst; | ||
| 1030 | |||
| 1022 | typedef struct _adc_inst { | 1031 | typedef struct _adc_inst { |
| 1023 | unsigned int I; | 1032 | unsigned int I; |
| 1024 | unsigned int S; | 1033 | unsigned int S; |
| @@ -1266,6 +1275,13 @@ typedef struct _smla_inst { | |||
| 1266 | unsigned int Rn; | 1275 | unsigned int Rn; |
| 1267 | } smla_inst; | 1276 | } smla_inst; |
| 1268 | 1277 | ||
| 1278 | typedef struct umaal_inst { | ||
| 1279 | unsigned int Rn; | ||
| 1280 | unsigned int Rm; | ||
| 1281 | unsigned int RdHi; | ||
| 1282 | unsigned int RdLo; | ||
| 1283 | } umaal_inst; | ||
| 1284 | |||
| 1269 | typedef struct _umlal_inst { | 1285 | typedef struct _umlal_inst { |
| 1270 | unsigned int S; | 1286 | unsigned int S; |
| 1271 | unsigned int Rm; | 1287 | unsigned int Rm; |
| @@ -2374,15 +2390,41 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) | |||
| 2374 | return inst_base; | 2390 | return inst_base; |
| 2375 | } | 2391 | } |
| 2376 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); } | 2392 | 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) { UNIMPLEMENTED_INSTRUCTION("QADD16"); } | ||
| 2378 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); } | 2393 | 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) { UNIMPLEMENTED_INSTRUCTION("QADDSUBX"); } | 2394 | ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) |
| 2395 | { | ||
| 2396 | arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); | ||
| 2397 | generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; | ||
| 2398 | |||
| 2399 | inst_base->cond = BITS(inst, 28, 31); | ||
| 2400 | inst_base->idx = index; | ||
| 2401 | inst_base->br = NON_BRANCH; | ||
| 2402 | inst_base->load_r15 = 0; | ||
| 2403 | |||
| 2404 | inst_cream->Rm = BITS(inst, 0, 3); | ||
| 2405 | inst_cream->Rn = BITS(inst, 16, 19); | ||
| 2406 | inst_cream->Rd = BITS(inst, 12, 15); | ||
| 2407 | inst_cream->op1 = BITS(inst, 20, 21); | ||
| 2408 | inst_cream->op2 = BITS(inst, 5, 7); | ||
| 2409 | |||
| 2410 | return inst_base; | ||
| 2411 | } | ||
| 2412 | ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) | ||
| 2413 | { | ||
| 2414 | return INTERPRETER_TRANSLATE(qadd16)(inst, index); | ||
| 2415 | } | ||
| 2380 | ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); } | 2416 | 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) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); } | 2417 | 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) { UNIMPLEMENTED_INSTRUCTION("QSUB"); } | 2418 | 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) { UNIMPLEMENTED_INSTRUCTION("QSUB16"); } | ||
| 2384 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); } | 2419 | 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) { UNIMPLEMENTED_INSTRUCTION("QSUBADDX"); } | 2420 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) |
| 2421 | { | ||
| 2422 | return INTERPRETER_TRANSLATE(qadd16)(inst, index); | ||
| 2423 | } | ||
| 2424 | ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) | ||
| 2425 | { | ||
| 2426 | return INTERPRETER_TRANSLATE(qadd16)(inst, index); | ||
| 2427 | } | ||
| 2386 | ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) | 2428 | ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) |
| 2387 | { | 2429 | { |
| 2388 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); | 2430 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); |
| @@ -2462,9 +2504,29 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index) | |||
| 2462 | } | 2504 | } |
| 2463 | return inst_base; | 2505 | return inst_base; |
| 2464 | } | 2506 | } |
| 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) { UNIMPLEMENTED_INSTRUCTION("SADD8"); } | 2507 | 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) { UNIMPLEMENTED_INSTRUCTION("SADDSUBX"); } | 2508 | ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) |
| 2509 | { | ||
| 2510 | arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); | ||
| 2511 | generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; | ||
| 2512 | |||
| 2513 | inst_base->cond = BITS(inst, 28, 31); | ||
| 2514 | inst_base->idx = index; | ||
| 2515 | inst_base->br = NON_BRANCH; | ||
| 2516 | inst_base->load_r15 = 0; | ||
| 2517 | |||
| 2518 | inst_cream->Rm = BITS(inst, 0, 3); | ||
| 2519 | inst_cream->Rn = BITS(inst, 16, 19); | ||
| 2520 | inst_cream->Rd = BITS(inst, 12, 15); | ||
| 2521 | inst_cream->op1 = BITS(inst, 20, 21); | ||
| 2522 | inst_cream->op2 = BITS(inst, 5, 7); | ||
| 2523 | |||
| 2524 | return inst_base; | ||
| 2525 | } | ||
| 2526 | ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) | ||
| 2527 | { | ||
| 2528 | return INTERPRETER_TRANSLATE(sadd16)(inst, index); | ||
| 2529 | } | ||
| 2468 | ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) | 2530 | ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) |
| 2469 | { | 2531 | { |
| 2470 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); | 2532 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); |
| @@ -2489,7 +2551,24 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) | |||
| 2489 | } | 2551 | } |
| 2490 | return inst_base; | 2552 | return inst_base; |
| 2491 | } | 2553 | } |
| 2492 | ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SEL"); } | 2554 | ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) |
| 2555 | { | ||
| 2556 | arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst)); | ||
| 2557 | generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; | ||
| 2558 | |||
| 2559 | inst_base->cond = BITS(inst, 28, 31); | ||
| 2560 | inst_base->idx = index; | ||
| 2561 | inst_base->br = NON_BRANCH; | ||
| 2562 | inst_base->load_r15 = 0; | ||
| 2563 | |||
| 2564 | inst_cream->Rm = BITS(inst, 0, 3); | ||
| 2565 | inst_cream->Rn = BITS(inst, 16, 19); | ||
| 2566 | inst_cream->Rd = BITS(inst, 12, 15); | ||
| 2567 | inst_cream->op1 = BITS(inst, 20, 22); | ||
| 2568 | inst_cream->op2 = BITS(inst, 5, 7); | ||
| 2569 | |||
| 2570 | return inst_base; | ||
| 2571 | } | ||
| 2493 | ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); } | 2572 | 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) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); } | 2573 | 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) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); } | 2574 | ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); } |
| @@ -2630,9 +2709,15 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) { UNI | |||
| 2630 | ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); } | 2709 | 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) { UNIMPLEMENTED_INSTRUCTION("SSAT"); } | 2710 | 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) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); } | 2711 | 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) { UNIMPLEMENTED_INSTRUCTION("SSUB16"); } | ||
| 2634 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); } | 2712 | 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) { UNIMPLEMENTED_INSTRUCTION("SSUBADDX"); } | 2713 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) |
| 2714 | { | ||
| 2715 | return INTERPRETER_TRANSLATE(sadd16)(inst, index); | ||
| 2716 | } | ||
| 2717 | ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) | ||
| 2718 | { | ||
| 2719 | return INTERPRETER_TRANSLATE(sadd16)(inst, index); | ||
| 2720 | } | ||
| 2636 | ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) | 2721 | ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) |
| 2637 | { | 2722 | { |
| 2638 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); | 2723 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); |
| @@ -3010,7 +3095,26 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) { UN | |||
| 3010 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); } | 3095 | 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) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); } | 3096 | 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) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); } | 3097 | 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) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); } | 3098 | ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) |
| 3099 | { | ||
| 3100 | arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(umaal_inst)); | ||
| 3101 | umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; | ||
| 3102 | |||
| 3103 | inst_base->cond = BITS(inst, 28, 31); | ||
| 3104 | inst_base->idx = index; | ||
| 3105 | inst_base->br = NON_BRANCH; | ||
| 3106 | inst_base->load_r15 = 0; | ||
| 3107 | |||
| 3108 | inst_cream->Rm = BITS(inst, 8, 11); | ||
| 3109 | inst_cream->Rn = BITS(inst, 0, 3); | ||
| 3110 | inst_cream->RdLo = BITS(inst, 12, 15); | ||
| 3111 | inst_cream->RdHi = BITS(inst, 16, 19); | ||
| 3112 | |||
| 3113 | if (CHECK_RM || CHECK_RN) | ||
| 3114 | inst_base->load_r15 = 1; | ||
| 3115 | |||
| 3116 | return inst_base; | ||
| 3117 | } | ||
| 3014 | ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) | 3118 | ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) |
| 3015 | { | 3119 | { |
| 3016 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); | 3120 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); |
| @@ -3720,9 +3824,9 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 3720 | #define INC_ICOUNTER cpu->icounter++; \ | 3824 | #define INC_ICOUNTER cpu->icounter++; \ |
| 3721 | if(cpu->Reg[15] > 0xc0000000) \ | 3825 | if(cpu->Reg[15] > 0xc0000000) \ |
| 3722 | cpu->kernel_icounter++; | 3826 | cpu->kernel_icounter++; |
| 3723 | //if (debug_function(core)) \ | 3827 | /*if (debug_function(core)) \ |
| 3724 | if (core->check_int_flag) \ | 3828 | if (core->check_int_flag) \ |
| 3725 | goto END | 3829 | goto END*/ |
| 3726 | //LOG_TRACE(Core_ARM11, "icounter is %llx pc is %x\n", cpu->icounter, cpu->Reg[15]) | 3830 | //LOG_TRACE(Core_ARM11, "icounter is %llx pc is %x\n", cpu->icounter, cpu->Reg[15]) |
| 3727 | #else | 3831 | #else |
| 3728 | #define INC_ICOUNTER ; | 3832 | #define INC_ICOUNTER ; |
| @@ -3943,18 +4047,18 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 3943 | 4047 | ||
| 3944 | #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0) | 4048 | #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0) |
| 3945 | #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1) | 4049 | #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1) |
| 3946 | // #define UPDATE_CFLAG(dst, lop, rop) (cpu->CFlag = ((ISNEG(lop) && ISPOS(rop)) || \ | 4050 | /* #define UPDATE_CFLAG(dst, lop, rop) (cpu->CFlag = ((ISNEG(lop) && ISPOS(rop)) || \ |
| 3947 | (ISNEG(lop) && ISPOS(dst)) || \ | 4051 | (ISNEG(lop) && ISPOS(dst)) || \ |
| 3948 | (ISPOS(rop) && ISPOS(dst)))) | 4052 | (ISPOS(rop) && ISPOS(dst)))) */ |
| 3949 | #define UPDATE_CFLAG(dst, lop, rop) (cpu->CFlag = ((dst < lop) || (dst < rop))) | 4053 | #define UPDATE_CFLAG(dst, lop, rop) (cpu->CFlag = ((dst < lop) || (dst < rop))) |
| 3950 | #define UPDATE_CFLAG_CARRY_FROM_ADD(lop, rop, flag) (cpu->CFlag = (((uint64_t) lop + (uint64_t) rop + (uint64_t) flag) > 0xffffffff) ) | 4054 | #define UPDATE_CFLAG_CARRY_FROM_ADD(lop, rop, flag) (cpu->CFlag = (((uint64_t) lop + (uint64_t) rop + (uint64_t) flag) > 0xffffffff) ) |
| 3951 | #define UPDATE_CFLAG_NOT_BORROW_FROM_FLAG(lop, rop, flag) (cpu->CFlag = ((uint64_t) lop >= ((uint64_t) rop + (uint64_t) flag))) | 4055 | #define UPDATE_CFLAG_NOT_BORROW_FROM_FLAG(lop, rop, flag) (cpu->CFlag = ((uint64_t) lop >= ((uint64_t) rop + (uint64_t) flag))) |
| 3952 | #define UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop) (cpu->CFlag = (lop >= rop)) | 4056 | #define UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop) (cpu->CFlag = (lop >= rop)) |
| 3953 | #define UPDATE_CFLAG_WITH_NOT(dst, lop, rop) (cpu->CFlag = !(dst < lop)) | 4057 | #define UPDATE_CFLAG_WITH_NOT(dst, lop, rop) (cpu->CFlag = !(dst < lop)) |
| 3954 | #define UPDATE_CFLAG_WITH_SC cpu->CFlag = cpu->shifter_carry_out | 4058 | #define UPDATE_CFLAG_WITH_SC cpu->CFlag = cpu->shifter_carry_out |
| 3955 | // #define UPDATE_CFLAG_WITH_NOT(dst, lop, rop) cpu->CFlag = !((ISNEG(lop) && ISPOS(rop)) || \ | 4059 | /* #define UPDATE_CFLAG_WITH_NOT(dst, lop, rop) cpu->CFlag = !((ISNEG(lop) && ISPOS(rop)) || \ |
| 3956 | (ISNEG(lop) && ISPOS(dst)) || \ | 4060 | (ISNEG(lop) && ISPOS(dst)) || \ |
| 3957 | (ISPOS(rop) && ISPOS(dst))) | 4061 | (ISPOS(rop) && ISPOS(dst))) */ |
| 3958 | #define UPDATE_VFLAG(dst, lop, rop) (cpu->VFlag = (((lop < 0) && (rop < 0) && (dst >= 0)) || \ | 4062 | #define UPDATE_VFLAG(dst, lop, rop) (cpu->VFlag = (((lop < 0) && (rop < 0) && (dst >= 0)) || \ |
| 3959 | ((lop >= 0) && (rop) >= 0 && (dst < 0)))) | 4063 | ((lop >= 0) && (rop) >= 0 && (dst < 0)))) |
| 3960 | #define UPDATE_VFLAG_WITH_NOT(dst, lop, rop) (cpu->VFlag = !(((lop < 0) && (rop < 0) && (dst >= 0)) || \ | 4064 | #define UPDATE_VFLAG_WITH_NOT(dst, lop, rop) (cpu->VFlag = !(((lop < 0) && (rop < 0) && (dst >= 0)) || \ |
| @@ -5483,15 +5587,69 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 5483 | GOTO_NEXT_INST; | 5587 | GOTO_NEXT_INST; |
| 5484 | } | 5588 | } |
| 5485 | QADD_INST: | 5589 | QADD_INST: |
| 5486 | QADD16_INST: | ||
| 5487 | QADD8_INST: | 5590 | QADD8_INST: |
| 5591 | |||
| 5592 | QADD16_INST: | ||
| 5488 | QADDSUBX_INST: | 5593 | QADDSUBX_INST: |
| 5594 | QSUB16_INST: | ||
| 5595 | QSUBADDX_INST: | ||
| 5596 | { | ||
| 5597 | INC_ICOUNTER; | ||
| 5598 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | ||
| 5599 | generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; | ||
| 5600 | const s16 rm_lo = (RM & 0xFFFF); | ||
| 5601 | const s16 rm_hi = ((RM >> 16) & 0xFFFF); | ||
| 5602 | const s16 rn_lo = (RN & 0xFFFF); | ||
| 5603 | const s16 rn_hi = ((RN >> 16) & 0xFFFF); | ||
| 5604 | const u8 op2 = inst_cream->op2; | ||
| 5605 | |||
| 5606 | s32 lo_result = 0; | ||
| 5607 | s32 hi_result = 0; | ||
| 5608 | |||
| 5609 | // QADD16 | ||
| 5610 | if (op2 == 0x00) { | ||
| 5611 | lo_result = (rn_lo + rm_lo); | ||
| 5612 | hi_result = (rn_hi + rm_hi); | ||
| 5613 | } | ||
| 5614 | // QASX | ||
| 5615 | else if (op2 == 0x01) { | ||
| 5616 | lo_result = (rn_lo - rm_hi); | ||
| 5617 | hi_result = (rn_hi + rm_lo); | ||
| 5618 | } | ||
| 5619 | // QSAX | ||
| 5620 | else if (op2 == 0x02) { | ||
| 5621 | lo_result = (rn_lo + rm_hi); | ||
| 5622 | hi_result = (rn_hi - rm_lo); | ||
| 5623 | } | ||
| 5624 | // QSUB16 | ||
| 5625 | else if (op2 == 0x03) { | ||
| 5626 | lo_result = (rn_lo - rm_lo); | ||
| 5627 | hi_result = (rn_hi - rm_hi); | ||
| 5628 | } | ||
| 5629 | |||
| 5630 | if (lo_result > 0x7FFF) | ||
| 5631 | lo_result = 0x7FFF; | ||
| 5632 | else if (lo_result < -0x8000) | ||
| 5633 | lo_result = -0x8000; | ||
| 5634 | |||
| 5635 | if (hi_result > 0x7FFF) | ||
| 5636 | hi_result = 0x7FFF; | ||
| 5637 | else if (hi_result < -0x8000) | ||
| 5638 | hi_result = -0x8000; | ||
| 5639 | |||
| 5640 | RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); | ||
| 5641 | } | ||
| 5642 | |||
| 5643 | cpu->Reg[15] += GET_INST_SIZE(cpu); | ||
| 5644 | INC_PC(sizeof(generic_arm_inst)); | ||
| 5645 | FETCH_INST; | ||
| 5646 | GOTO_NEXT_INST; | ||
| 5647 | } | ||
| 5648 | |||
| 5489 | QDADD_INST: | 5649 | QDADD_INST: |
| 5490 | QDSUB_INST: | 5650 | QDSUB_INST: |
| 5491 | QSUB_INST: | 5651 | QSUB_INST: |
| 5492 | QSUB16_INST: | ||
| 5493 | QSUB8_INST: | 5652 | QSUB8_INST: |
| 5494 | QSUBADDX_INST: | ||
| 5495 | REV_INST: | 5653 | REV_INST: |
| 5496 | { | 5654 | { |
| 5497 | INC_ICOUNTER; | 5655 | INC_ICOUNTER; |
| @@ -5600,9 +5758,71 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 5600 | FETCH_INST; | 5758 | FETCH_INST; |
| 5601 | GOTO_NEXT_INST; | 5759 | GOTO_NEXT_INST; |
| 5602 | } | 5760 | } |
| 5603 | SADD16_INST: | ||
| 5604 | SADD8_INST: | 5761 | SADD8_INST: |
| 5762 | |||
| 5763 | SADD16_INST: | ||
| 5605 | SADDSUBX_INST: | 5764 | SADDSUBX_INST: |
| 5765 | SSUBADDX_INST: | ||
| 5766 | SSUB16_INST: | ||
| 5767 | { | ||
| 5768 | INC_ICOUNTER; | ||
| 5769 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | ||
| 5770 | generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; | ||
| 5771 | |||
| 5772 | const s16 rn_lo = (RN & 0xFFFF); | ||
| 5773 | const s16 rn_hi = ((RN >> 16) & 0xFFFF); | ||
| 5774 | const s16 rm_lo = (RM & 0xFFFF); | ||
| 5775 | const s16 rm_hi = ((RM >> 16) & 0xFFFF); | ||
| 5776 | |||
| 5777 | s32 lo_result = 0; | ||
| 5778 | s32 hi_result = 0; | ||
| 5779 | |||
| 5780 | // SADD16 | ||
| 5781 | if (inst_cream->op2 == 0x00) { | ||
| 5782 | lo_result = (rn_lo + rm_lo); | ||
| 5783 | hi_result = (rn_hi + rm_hi); | ||
| 5784 | } | ||
| 5785 | // SASX | ||
| 5786 | else if (inst_cream->op2 == 0x01) { | ||
| 5787 | lo_result = (rn_lo - rm_hi); | ||
| 5788 | hi_result = (rn_hi + rm_lo); | ||
| 5789 | } | ||
| 5790 | // SSAX | ||
| 5791 | else if (inst_cream->op2 == 0x02) { | ||
| 5792 | lo_result = (rn_lo + rm_hi); | ||
| 5793 | hi_result = (rn_hi - rm_lo); | ||
| 5794 | } | ||
| 5795 | // SSUB16 | ||
| 5796 | else if (inst_cream->op2 == 0x03) { | ||
| 5797 | lo_result = (rn_lo - rm_lo); | ||
| 5798 | hi_result = (rn_hi - rm_hi); | ||
| 5799 | } | ||
| 5800 | |||
| 5801 | RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); | ||
| 5802 | |||
| 5803 | if (lo_result >= 0) { | ||
| 5804 | cpu->Cpsr |= (1 << 16); | ||
| 5805 | cpu->Cpsr |= (1 << 17); | ||
| 5806 | } else { | ||
| 5807 | cpu->Cpsr &= ~(1 << 16); | ||
| 5808 | cpu->Cpsr &= ~(1 << 17); | ||
| 5809 | } | ||
| 5810 | |||
| 5811 | if (hi_result >= 0) { | ||
| 5812 | cpu->Cpsr |= (1 << 18); | ||
| 5813 | cpu->Cpsr |= (1 << 19); | ||
| 5814 | } else { | ||
| 5815 | cpu->Cpsr &= ~(1 << 18); | ||
| 5816 | cpu->Cpsr &= ~(1 << 19); | ||
| 5817 | } | ||
| 5818 | } | ||
| 5819 | |||
| 5820 | cpu->Reg[15] += GET_INST_SIZE(cpu); | ||
| 5821 | INC_PC(sizeof(generic_arm_inst)); | ||
| 5822 | FETCH_INST; | ||
| 5823 | GOTO_NEXT_INST; | ||
| 5824 | } | ||
| 5825 | |||
| 5606 | SBC_INST: | 5826 | SBC_INST: |
| 5607 | { | 5827 | { |
| 5608 | INC_ICOUNTER; | 5828 | INC_ICOUNTER; |
| @@ -5641,7 +5861,47 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 5641 | FETCH_INST; | 5861 | FETCH_INST; |
| 5642 | GOTO_NEXT_INST; | 5862 | GOTO_NEXT_INST; |
| 5643 | } | 5863 | } |
| 5864 | |||
| 5644 | SEL_INST: | 5865 | SEL_INST: |
| 5866 | { | ||
| 5867 | INC_ICOUNTER; | ||
| 5868 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | ||
| 5869 | generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component; | ||
| 5870 | |||
| 5871 | const u32 to = RM; | ||
| 5872 | const u32 from = RN; | ||
| 5873 | const u32 cpsr = cpu->Cpsr; | ||
| 5874 | |||
| 5875 | u32 result; | ||
| 5876 | if (cpsr & (1 << 16)) | ||
| 5877 | result = from & 0xff; | ||
| 5878 | else | ||
| 5879 | result = to & 0xff; | ||
| 5880 | |||
| 5881 | if (cpsr & (1 << 17)) | ||
| 5882 | result |= from & 0x0000ff00; | ||
| 5883 | else | ||
| 5884 | result |= to & 0x0000ff00; | ||
| 5885 | |||
| 5886 | if (cpsr & (1 << 18)) | ||
| 5887 | result |= from & 0x00ff0000; | ||
| 5888 | else | ||
| 5889 | result |= to & 0x00ff0000; | ||
| 5890 | |||
| 5891 | if (cpsr & (1 << 19)) | ||
| 5892 | result |= from & 0xff000000; | ||
| 5893 | else | ||
| 5894 | result |= to & 0xff000000; | ||
| 5895 | |||
| 5896 | RD = result; | ||
| 5897 | } | ||
| 5898 | |||
| 5899 | cpu->Reg[15] += GET_INST_SIZE(cpu); | ||
| 5900 | INC_PC(sizeof(generic_arm_inst)); | ||
| 5901 | FETCH_INST; | ||
| 5902 | GOTO_NEXT_INST; | ||
| 5903 | } | ||
| 5904 | |||
| 5645 | SETEND_INST: | 5905 | SETEND_INST: |
| 5646 | SHADD16_INST: | 5906 | SHADD16_INST: |
| 5647 | SHADD8_INST: | 5907 | SHADD8_INST: |
| @@ -5825,9 +6085,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 5825 | SRS_INST: | 6085 | SRS_INST: |
| 5826 | SSAT_INST: | 6086 | SSAT_INST: |
| 5827 | SSAT16_INST: | 6087 | SSAT16_INST: |
| 5828 | SSUB16_INST: | ||
| 5829 | SSUB8_INST: | 6088 | SSUB8_INST: |
| 5830 | SSUBADDX_INST: | ||
| 5831 | STC_INST: | 6089 | STC_INST: |
| 5832 | { | 6090 | { |
| 5833 | INC_ICOUNTER; | 6091 | INC_ICOUNTER; |
| @@ -6374,6 +6632,26 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 6374 | UHSUB8_INST: | 6632 | UHSUB8_INST: |
| 6375 | UHSUBADDX_INST: | 6633 | UHSUBADDX_INST: |
| 6376 | UMAAL_INST: | 6634 | UMAAL_INST: |
| 6635 | { | ||
| 6636 | INC_ICOUNTER; | ||
| 6637 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | ||
| 6638 | umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; | ||
| 6639 | |||
| 6640 | const u32 rm = RM; | ||
| 6641 | const u32 rn = RN; | ||
| 6642 | const u32 rd_lo = RDLO; | ||
| 6643 | const u32 rd_hi = RDHI; | ||
| 6644 | |||
| 6645 | const u64 result = (rm * rn) + rd_lo + rd_hi; | ||
| 6646 | |||
| 6647 | RDLO = (result & 0xFFFFFFFF); | ||
| 6648 | RDHI = ((result >> 32) & 0xFFFFFFFF); | ||
| 6649 | } | ||
| 6650 | cpu->Reg[15] += GET_INST_SIZE(cpu); | ||
| 6651 | INC_PC(sizeof(umaal_inst)); | ||
| 6652 | FETCH_INST; | ||
| 6653 | GOTO_NEXT_INST; | ||
| 6654 | } | ||
| 6377 | UMLAL_INST: | 6655 | UMLAL_INST: |
| 6378 | { | 6656 | { |
| 6379 | INC_ICOUNTER; | 6657 | INC_ICOUNTER; |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.h b/src/core/arm/dyncom/arm_dyncom_interpreter.h index 3a2462f55..4791ea25f 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.h +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/arm/dyncom/arm_dyncom_run.cpp b/src/core/arm/dyncom/arm_dyncom_run.cpp index a2026cbf3..b66b92cf5 100644 --- a/src/core/arm/dyncom/arm_dyncom_run.cpp +++ b/src/core/arm/dyncom/arm_dyncom_run.cpp | |||
| @@ -29,7 +29,6 @@ | |||
| 29 | 29 | ||
| 30 | void switch_mode(arm_core_t *core, uint32_t mode) | 30 | void switch_mode(arm_core_t *core, uint32_t mode) |
| 31 | { | 31 | { |
| 32 | uint32_t tmp1, tmp2; | ||
| 33 | if (core->Mode == mode) { | 32 | if (core->Mode == mode) { |
| 34 | //Mode not changed. | 33 | //Mode not changed. |
| 35 | //printf("mode not changed\n"); | 34 | //printf("mode not changed\n"); |
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index e2aa5ce92..be04fc1a1 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/arm/interpreter/arm_interpreter.h" | 5 | #include "core/arm/interpreter/arm_interpreter.h" |
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index ed53d997c..b685215a0 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index ec40881f8..610e04f10 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp | |||
| @@ -1356,7 +1356,13 @@ mainswitch: | |||
| 1356 | } | 1356 | } |
| 1357 | break; | 1357 | break; |
| 1358 | 1358 | ||
| 1359 | case 0x04: /* SUB reg */ | 1359 | case 0x04: /* SUB reg */ |
| 1360 | // Signifies UMAAL | ||
| 1361 | if (state->is_v6 && BITS(4, 7) == 0x09) { | ||
| 1362 | if (handle_v6_insn(state, instr)) | ||
| 1363 | break; | ||
| 1364 | } | ||
| 1365 | |||
| 1360 | #ifdef MODET | 1366 | #ifdef MODET |
| 1361 | if (BITS (4, 7) == 0xB) { | 1367 | if (BITS (4, 7) == 0xB) { |
| 1362 | /* STRH immediate offset, no write-back, down, post indexed. */ | 1368 | /* STRH immediate offset, no write-back, down, post indexed. */ |
| @@ -3103,12 +3109,18 @@ mainswitch: | |||
| 3103 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); | 3109 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); |
| 3104 | break; | 3110 | break; |
| 3105 | } else if ((instr & 0x70) == 0x50) { //pkhtb | 3111 | } else if ((instr & 0x70) == 0x50) { //pkhtb |
| 3106 | u8 idest = BITS(12, 15); | 3112 | const u8 rd_idx = BITS(12, 15); |
| 3107 | u8 rfis = BITS(16, 19); | 3113 | const u8 rn_idx = BITS(16, 19); |
| 3108 | u8 rlast = BITS(0, 3); | 3114 | const u8 rm_idx = BITS(0, 3); |
| 3109 | u8 ishi = BITS(7, 11); | 3115 | const u8 imm5 = BITS(7, 11); |
| 3110 | if (ishi == 0)ishi = 0x20; | 3116 | |
| 3111 | state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000); | 3117 | ARMword val; |
| 3118 | if (imm5 >= 32) | ||
| 3119 | val = (state->Reg[rm_idx] >> 31); | ||
| 3120 | else | ||
| 3121 | val = (state->Reg[rm_idx] >> imm5); | ||
| 3122 | |||
| 3123 | state->Reg[rd_idx] = (val & 0xFFFF) | ((state->Reg[rn_idx]) & 0xFFFF0000); | ||
| 3112 | break; | 3124 | break; |
| 3113 | } else if (BIT (4)) { | 3125 | } else if (BIT (4)) { |
| 3114 | #ifdef MODE32 | 3126 | #ifdef MODE32 |
| @@ -5669,16 +5681,29 @@ L_stm_s_takeabort: | |||
| 5669 | /* Attempt to emulate an ARMv6 instruction. | 5681 | /* Attempt to emulate an ARMv6 instruction. |
| 5670 | Returns non-zero upon success. */ | 5682 | Returns non-zero upon success. */ |
| 5671 | 5683 | ||
| 5672 | static int | 5684 | static int handle_v6_insn(ARMul_State* state, ARMword instr) { |
| 5673 | handle_v6_insn (ARMul_State * state, ARMword instr) { | 5685 | switch (BITS(20, 27)) { |
| 5674 | ARMword lhs, temp; | ||
| 5675 | |||
| 5676 | switch (BITS (20, 27)) { | ||
| 5677 | case 0x03: | 5686 | case 0x03: |
| 5678 | printf ("Unhandled v6 insn: ldr\n"); | 5687 | printf ("Unhandled v6 insn: ldr\n"); |
| 5679 | break; | 5688 | break; |
| 5680 | case 0x04: | 5689 | case 0x04: // UMAAL |
| 5681 | printf ("Unhandled v6 insn: umaal\n"); | 5690 | { |
| 5691 | const u8 rm_idx = BITS(8, 11); | ||
| 5692 | const u8 rn_idx = BITS(0, 3); | ||
| 5693 | const u8 rd_lo_idx = BITS(12, 15); | ||
| 5694 | const u8 rd_hi_idx = BITS(16, 19); | ||
| 5695 | |||
| 5696 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 5697 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 5698 | const u32 rd_lo_val = state->Reg[rd_lo_idx]; | ||
| 5699 | const u32 rd_hi_val = state->Reg[rd_hi_idx]; | ||
| 5700 | |||
| 5701 | const u64 result = (rn_val * rm_val) + rd_lo_val + rd_hi_val; | ||
| 5702 | |||
| 5703 | state->Reg[rd_lo_idx] = (result & 0xFFFFFFFF); | ||
| 5704 | state->Reg[rd_hi_idx] = ((result >> 32) & 0xFFFFFFFF); | ||
| 5705 | return 1; | ||
| 5706 | } | ||
| 5682 | break; | 5707 | break; |
| 5683 | case 0x06: | 5708 | case 0x06: |
| 5684 | printf ("Unhandled v6 insn: mls/str\n"); | 5709 | printf ("Unhandled v6 insn: mls/str\n"); |
| @@ -5691,7 +5716,7 @@ L_stm_s_takeabort: | |||
| 5691 | /* strex */ | 5716 | /* strex */ |
| 5692 | u32 l = LHSReg; | 5717 | u32 l = LHSReg; |
| 5693 | u32 r = RHSReg; | 5718 | u32 r = RHSReg; |
| 5694 | lhs = LHS; | 5719 | u32 lhs = LHS; |
| 5695 | 5720 | ||
| 5696 | bool enter = false; | 5721 | bool enter = false; |
| 5697 | 5722 | ||
| @@ -5716,7 +5741,7 @@ L_stm_s_takeabort: | |||
| 5716 | case 0x19: | 5741 | case 0x19: |
| 5717 | /* ldrex */ | 5742 | /* ldrex */ |
| 5718 | if (BITS(4, 7) == 0x9) { | 5743 | if (BITS(4, 7) == 0x9) { |
| 5719 | lhs = LHS; | 5744 | u32 lhs = LHS; |
| 5720 | 5745 | ||
| 5721 | state->currentexaddr = lhs; | 5746 | state->currentexaddr = lhs; |
| 5722 | state->currentexval = ARMul_ReadWord(state, lhs); | 5747 | state->currentexval = ARMul_ReadWord(state, lhs); |
| @@ -5735,7 +5760,7 @@ L_stm_s_takeabort: | |||
| 5735 | case 0x1c: | 5760 | case 0x1c: |
| 5736 | if (BITS(4, 7) == 0x9) { | 5761 | if (BITS(4, 7) == 0x9) { |
| 5737 | /* strexb */ | 5762 | /* strexb */ |
| 5738 | lhs = LHS; | 5763 | u32 lhs = LHS; |
| 5739 | 5764 | ||
| 5740 | bool enter = false; | 5765 | bool enter = false; |
| 5741 | 5766 | ||
| @@ -5765,11 +5790,11 @@ L_stm_s_takeabort: | |||
| 5765 | case 0x1d: | 5790 | case 0x1d: |
| 5766 | if ((BITS(4, 7)) == 0x9) { | 5791 | if ((BITS(4, 7)) == 0x9) { |
| 5767 | /* ldrexb */ | 5792 | /* ldrexb */ |
| 5768 | temp = LHS; | 5793 | u32 lhs = LHS; |
| 5769 | LoadByte(state, instr, temp, LUNSIGNED); | 5794 | LoadByte(state, instr, lhs, LUNSIGNED); |
| 5770 | 5795 | ||
| 5771 | state->currentexaddr = temp; | 5796 | state->currentexaddr = lhs; |
| 5772 | state->currentexval = (u32)ARMul_ReadByte(state, temp); | 5797 | state->currentexval = (u32)ARMul_ReadByte(state, lhs); |
| 5773 | 5798 | ||
| 5774 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); | 5799 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); |
| 5775 | //printf("ldrexb\n"); | 5800 | //printf("ldrexb\n"); |
| @@ -5799,83 +5824,188 @@ L_stm_s_takeabort: | |||
| 5799 | case 0x3f: | 5824 | case 0x3f: |
| 5800 | printf ("Unhandled v6 insn: rbit\n"); | 5825 | printf ("Unhandled v6 insn: rbit\n"); |
| 5801 | break; | 5826 | break; |
| 5802 | case 0x61: | 5827 | case 0x61: // SADD16, SASX, SSAX, and SSUB16 |
| 5803 | if ((instr & 0xFF0) == 0xf70) { //ssub16 | 5828 | if ((instr & 0xFF0) == 0xf10 || (instr & 0xFF0) == 0xf30 || |
| 5804 | u8 tar = BITS(12, 15); | 5829 | (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf70) |
| 5805 | u8 src1 = BITS(16, 19); | 5830 | { |
| 5806 | u8 src2 = BITS(0, 3); | 5831 | const u8 rd_idx = BITS(12, 15); |
| 5807 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5832 | const u8 rm_idx = BITS(0, 3); |
| 5808 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5833 | const u8 rn_idx = BITS(16, 19); |
| 5809 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5834 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5810 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5835 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); |
| 5811 | state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10); | 5836 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); |
| 5812 | return 1; | 5837 | const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF); |
| 5813 | } else if ((instr & 0xFF0) == 0xf10) { //sadd16 | 5838 | |
| 5814 | u8 tar = BITS(12, 15); | 5839 | s32 lo_result; |
| 5815 | u8 src1 = BITS(16, 19); | 5840 | s32 hi_result; |
| 5816 | u8 src2 = BITS(0, 3); | 5841 | |
| 5817 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5842 | // SADD16 |
| 5818 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5843 | if ((instr & 0xFF0) == 0xf10) { |
| 5819 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5844 | lo_result = (rn_lo + rm_lo); |
| 5820 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5845 | hi_result = (rn_hi + rm_hi); |
| 5821 | state->Reg[tar] = ((a1 + a2) & 0xFFFF) | (((b1 + b2) & 0xFFFF) << 0x10); | 5846 | } |
| 5822 | return 1; | 5847 | // SASX |
| 5823 | } else if ((instr & 0xFF0) == 0xf50) { //ssax | 5848 | else if ((instr & 0xFF0) == 0xf30) { |
| 5824 | u8 tar = BITS(12, 15); | 5849 | lo_result = (rn_lo - rm_hi); |
| 5825 | u8 src1 = BITS(16, 19); | 5850 | hi_result = (rn_hi + rm_lo); |
| 5826 | u8 src2 = BITS(0, 3); | 5851 | } |
| 5827 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5852 | // SSAX |
| 5828 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5853 | else if ((instr & 0xFF0) == 0xf50) { |
| 5829 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5854 | lo_result = (rn_lo + rm_hi); |
| 5830 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5855 | hi_result = (rn_hi - rm_lo); |
| 5831 | state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10); | 5856 | } |
| 5857 | // SSUB16 | ||
| 5858 | else { | ||
| 5859 | lo_result = (rn_lo - rm_lo); | ||
| 5860 | hi_result = (rn_hi - rm_hi); | ||
| 5861 | } | ||
| 5862 | |||
| 5863 | state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); | ||
| 5864 | |||
| 5865 | if (lo_result >= 0) { | ||
| 5866 | state->Cpsr |= (1 << 16); | ||
| 5867 | state->Cpsr |= (1 << 17); | ||
| 5868 | } else { | ||
| 5869 | state->Cpsr &= ~(1 << 16); | ||
| 5870 | state->Cpsr &= ~(1 << 17); | ||
| 5871 | } | ||
| 5872 | |||
| 5873 | if (hi_result >= 0) { | ||
| 5874 | state->Cpsr |= (1 << 18); | ||
| 5875 | state->Cpsr |= (1 << 19); | ||
| 5876 | } else { | ||
| 5877 | state->Cpsr &= ~(1 << 18); | ||
| 5878 | state->Cpsr &= ~(1 << 19); | ||
| 5879 | } | ||
| 5832 | return 1; | 5880 | return 1; |
| 5833 | } else if ((instr & 0xFF0) == 0xf30) { //sasx | 5881 | } |
| 5834 | u8 tar = BITS(12, 15); | 5882 | // SADD8/SSUB8 |
| 5835 | u8 src1 = BITS(16, 19); | 5883 | else if ((instr & 0xFF0) == 0xf90 || (instr & 0xFF0) == 0xff0) |
| 5836 | u8 src2 = BITS(0, 3); | 5884 | { |
| 5837 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5885 | const u8 rd_idx = BITS(12, 15); |
| 5838 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5886 | const u8 rm_idx = BITS(0, 3); |
| 5839 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5887 | const u8 rn_idx = BITS(16, 19); |
| 5840 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5888 | const u32 rm_val = state->Reg[rm_idx]; |
| 5841 | state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10); | 5889 | const u32 rn_val = state->Reg[rn_idx]; |
| 5890 | |||
| 5891 | u8 lo_val1; | ||
| 5892 | u8 lo_val2; | ||
| 5893 | u8 hi_val1; | ||
| 5894 | u8 hi_val2; | ||
| 5895 | |||
| 5896 | // SADD8 | ||
| 5897 | if ((instr & 0xFF0) == 0xf90) { | ||
| 5898 | lo_val1 = (u8)((rn_val & 0xFF) + (rm_val & 0xFF)); | ||
| 5899 | lo_val2 = (u8)(((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF)); | ||
| 5900 | hi_val1 = (u8)(((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF)); | ||
| 5901 | hi_val2 = (u8)(((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF)); | ||
| 5902 | |||
| 5903 | if (lo_val1 & 0x80) | ||
| 5904 | state->Cpsr |= (1 << 16); | ||
| 5905 | else | ||
| 5906 | state->Cpsr &= ~(1 << 16); | ||
| 5907 | |||
| 5908 | if (lo_val2 & 0x80) | ||
| 5909 | state->Cpsr |= (1 << 17); | ||
| 5910 | else | ||
| 5911 | state->Cpsr &= ~(1 << 17); | ||
| 5912 | |||
| 5913 | if (hi_val1 & 0x80) | ||
| 5914 | state->Cpsr |= (1 << 18); | ||
| 5915 | else | ||
| 5916 | state->Cpsr &= ~(1 << 18); | ||
| 5917 | |||
| 5918 | if (hi_val2 & 0x80) | ||
| 5919 | state->Cpsr |= (1 << 19); | ||
| 5920 | else | ||
| 5921 | state->Cpsr &= ~(1 << 19); | ||
| 5922 | } | ||
| 5923 | // SSUB8 | ||
| 5924 | else { | ||
| 5925 | lo_val1 = (u8)((rn_val & 0xFF) - (rm_val & 0xFF)); | ||
| 5926 | lo_val2 = (u8)(((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF)); | ||
| 5927 | hi_val1 = (u8)(((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF)); | ||
| 5928 | hi_val2 = (u8)(((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF)); | ||
| 5929 | |||
| 5930 | if (!(lo_val1 & 0x80)) | ||
| 5931 | state->Cpsr |= (1 << 16); | ||
| 5932 | else | ||
| 5933 | state->Cpsr &= ~(1 << 16); | ||
| 5934 | |||
| 5935 | if (!(lo_val2 & 0x80)) | ||
| 5936 | state->Cpsr |= (1 << 17); | ||
| 5937 | else | ||
| 5938 | state->Cpsr &= ~(1 << 17); | ||
| 5939 | |||
| 5940 | if (!(hi_val1 & 0x80)) | ||
| 5941 | state->Cpsr |= (1 << 18); | ||
| 5942 | else | ||
| 5943 | state->Cpsr &= ~(1 << 18); | ||
| 5944 | |||
| 5945 | if (!(hi_val2 & 0x80)) | ||
| 5946 | state->Cpsr |= (1 << 19); | ||
| 5947 | else | ||
| 5948 | state->Cpsr &= ~(1 << 19); | ||
| 5949 | } | ||
| 5950 | |||
| 5951 | state->Reg[rd_idx] = (lo_val1 | lo_val2 << 8 | hi_val1 << 16 | hi_val2 << 24); | ||
| 5842 | return 1; | 5952 | return 1; |
| 5843 | } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n"); | 5953 | } |
| 5954 | else { | ||
| 5955 | printf("Unhandled v6 insn: %08x", instr); | ||
| 5956 | } | ||
| 5844 | break; | 5957 | break; |
| 5845 | case 0x62: | 5958 | case 0x62: // QADD16, QASX, QSAX, and QSUB16 |
| 5846 | if ((instr & 0xFF0) == 0xf70) { //QSUB16 | 5959 | if ((instr & 0xFF0) == 0xf10 || (instr & 0xFF0) == 0xf30 || |
| 5847 | u8 tar = BITS(12, 15); | 5960 | (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf70) |
| 5848 | u8 src1 = BITS(16, 19); | 5961 | { |
| 5849 | u8 src2 = BITS(0, 3); | 5962 | const u8 rd_idx = BITS(12, 15); |
| 5850 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5963 | const u8 rn_idx = BITS(16, 19); |
| 5851 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5964 | const u8 rm_idx = BITS(0, 3); |
| 5852 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5965 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); |
| 5853 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5966 | const s16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF); |
| 5854 | s32 res1 = (a1 - b1); | 5967 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5855 | s32 res2 = (a2 - b2); | 5968 | const s16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF); |
| 5856 | if (res1 > 0x7FFF) res1 = 0x7FFF; | 5969 | |
| 5857 | if (res2 > 0x7FFF) res2 = 0x7FFF; | 5970 | s32 lo_result; |
| 5858 | if (res1 < 0x7FFF) res1 = -0x8000; | 5971 | s32 hi_result; |
| 5859 | if (res2 < 0x7FFF) res2 = -0x8000; | 5972 | |
| 5860 | state->Reg[tar] = (res1 & 0xFFFF) | ((res2 & 0xFFFF) << 0x10); | 5973 | // QADD16 |
| 5861 | return 1; | 5974 | if ((instr & 0xFF0) == 0xf10) { |
| 5862 | } else if ((instr & 0xFF0) == 0xf10) { //QADD16 | 5975 | lo_result = (rn_lo + rm_lo); |
| 5863 | u8 tar = BITS(12, 15); | 5976 | hi_result = (rn_hi + rm_hi); |
| 5864 | u8 src1 = BITS(16, 19); | 5977 | } |
| 5865 | u8 src2 = BITS(0, 3); | 5978 | // QASX |
| 5866 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5979 | else if ((instr & 0xFF0) == 0xf30) { |
| 5867 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5980 | lo_result = (rn_lo - rm_hi); |
| 5868 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5981 | hi_result = (rn_hi + rm_lo); |
| 5869 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5982 | } |
| 5870 | s32 res1 = (a1 + b1); | 5983 | // QSAX |
| 5871 | s32 res2 = (a2 + b2); | 5984 | else if ((instr & 0xFF0) == 0xf50) { |
| 5872 | if (res1 > 0x7FFF) res1 = 0x7FFF; | 5985 | lo_result = (rn_lo + rm_hi); |
| 5873 | if (res2 > 0x7FFF) res2 = 0x7FFF; | 5986 | hi_result = (rn_hi - rm_lo); |
| 5874 | if (res1 < 0x7FFF) res1 = -0x8000; | 5987 | } |
| 5875 | if (res2 < 0x7FFF) res2 = -0x8000; | 5988 | // QSUB16 |
| 5876 | state->Reg[tar] = ((res1) & 0xFFFF) | (((res2) & 0xFFFF) << 0x10); | 5989 | else { |
| 5990 | lo_result = (rn_lo - rm_lo); | ||
| 5991 | hi_result = (rn_hi - rm_hi); | ||
| 5992 | } | ||
| 5993 | |||
| 5994 | if (lo_result > 0x7FFF) | ||
| 5995 | lo_result = 0x7FFF; | ||
| 5996 | else if (lo_result < -0x8000) | ||
| 5997 | lo_result = -0x8000; | ||
| 5998 | |||
| 5999 | if (hi_result > 0x7FFF) | ||
| 6000 | hi_result = 0x7FFF; | ||
| 6001 | else if (hi_result < -0x8000) | ||
| 6002 | hi_result = -0x8000; | ||
| 6003 | |||
| 6004 | state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); | ||
| 5877 | return 1; | 6005 | return 1; |
| 5878 | } else printf ("Unhandled v6 insn: qadd16/qsub16\n"); | 6006 | } else { |
| 6007 | printf("Unhandled v6 insn: %08x", BITS(20, 27)); | ||
| 6008 | } | ||
| 5879 | break; | 6009 | break; |
| 5880 | case 0x63: | 6010 | case 0x63: |
| 5881 | printf ("Unhandled v6 insn: shadd/shsub\n"); | 6011 | printf ("Unhandled v6 insn: shadd/shsub\n"); |
| @@ -5925,11 +6055,29 @@ L_stm_s_takeabort: | |||
| 5925 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); | 6055 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); |
| 5926 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); | 6056 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); |
| 5927 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); | 6057 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); |
| 5928 | if (b1 & 0xffffff00) state->Cpsr |= (1 << 16); | 6058 | |
| 5929 | if (b2 & 0xffffff00) state->Cpsr |= (1 << 17); | 6059 | if (b1 & 0xffffff00) |
| 5930 | if (b3 & 0xffffff00) state->Cpsr |= (1 << 18); | 6060 | state->Cpsr |= (1 << 16); |
| 5931 | if (b4 & 0xffffff00) state->Cpsr |= (1 << 19); | 6061 | else |
| 6062 | state->Cpsr &= ~(1 << 16); | ||
| 6063 | |||
| 6064 | if (b2 & 0xffffff00) | ||
| 6065 | state->Cpsr |= (1 << 17); | ||
| 6066 | else | ||
| 6067 | state->Cpsr &= ~(1 << 17); | ||
| 6068 | |||
| 6069 | if (b3 & 0xffffff00) | ||
| 6070 | state->Cpsr |= (1 << 18); | ||
| 6071 | else | ||
| 6072 | state->Cpsr &= ~(1 << 18); | ||
| 6073 | |||
| 6074 | |||
| 6075 | if (b4 & 0xffffff00) | ||
| 6076 | state->Cpsr |= (1 << 19); | ||
| 6077 | else | ||
| 6078 | state->Cpsr &= ~(1 << 19); | ||
| 5932 | } | 6079 | } |
| 6080 | |||
| 5933 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); | 6081 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); |
| 5934 | return 1; | 6082 | return 1; |
| 5935 | } | 6083 | } |
| @@ -6016,22 +6164,28 @@ L_stm_s_takeabort: | |||
| 6016 | //ichfly | 6164 | //ichfly |
| 6017 | //SSAT16 | 6165 | //SSAT16 |
| 6018 | { | 6166 | { |
| 6019 | u8 tar = BITS(12, 15); | 6167 | const u8 rd_idx = BITS(12, 15); |
| 6020 | u8 src = BITS(0, 3); | 6168 | const u8 rn_idx = BITS(0, 3); |
| 6021 | u8 val = BITS(16, 19) + 1; | 6169 | const u8 num_bits = BITS(16, 19) + 1; |
| 6022 | s16 a1 = (state->Reg[src]); | 6170 | const s16 min = -(0x8000 >> (16 - num_bits)); |
| 6023 | s16 a2 = (state->Reg[src] >> 0x10); | 6171 | const s16 max = (0x7FFF >> (16 - num_bits)); |
| 6024 | s16 min = (s16)(0x8000 >> (16 - val)); | 6172 | s16 rn_lo = (state->Reg[rn_idx]); |
| 6025 | s16 max = 0x7FFF >> (16 - val); | 6173 | s16 rn_hi = (state->Reg[rn_idx] >> 16); |
| 6026 | if (min > a1) a1 = min; | 6174 | |
| 6027 | if (max < a1) a1 = max; | 6175 | if (rn_lo > max) |
| 6028 | if (min > a2) a2 = min; | 6176 | rn_lo = max; |
| 6029 | if (max < a2) a2 = max; | 6177 | else if (rn_lo < min) |
| 6030 | u32 temp2 = ((u32)(a2)) << 0x10; | 6178 | rn_lo = min; |
| 6031 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | 6179 | |
| 6180 | if (rn_hi > max) | ||
| 6181 | rn_hi = max; | ||
| 6182 | else if (rn_hi < min) | ||
| 6183 | rn_hi = min; | ||
| 6184 | |||
| 6185 | state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi & 0xFFFF) << 16); | ||
| 6186 | return 1; | ||
| 6032 | } | 6187 | } |
| 6033 | 6188 | ||
| 6034 | return 1; | ||
| 6035 | default: | 6189 | default: |
| 6036 | break; | 6190 | break; |
| 6037 | } | 6191 | } |
| @@ -6044,7 +6198,7 @@ L_stm_s_takeabort: | |||
| 6044 | break; | 6198 | break; |
| 6045 | } | 6199 | } |
| 6046 | 6200 | ||
| 6047 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6201 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF); |
| 6048 | if (Rm & 0x80) | 6202 | if (Rm & 0x80) |
| 6049 | Rm |= 0xffffff00; | 6203 | Rm |= 0xffffff00; |
| 6050 | 6204 | ||
| @@ -6053,11 +6207,12 @@ L_stm_s_takeabort: | |||
| 6053 | state->Reg[BITS(12, 15)] = Rm; | 6207 | state->Reg[BITS(12, 15)] = Rm; |
| 6054 | else | 6208 | else |
| 6055 | /* SXTAB */ | 6209 | /* SXTAB */ |
| 6056 | state->Reg[BITS(12, 15)] += Rm; | 6210 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6057 | 6211 | ||
| 6058 | return 1; | 6212 | return 1; |
| 6059 | } | 6213 | } |
| 6060 | case 0x6b: { | 6214 | case 0x6b: |
| 6215 | { | ||
| 6061 | ARMword Rm; | 6216 | ARMword Rm; |
| 6062 | int ror = -1; | 6217 | int ror = -1; |
| 6063 | 6218 | ||
| @@ -6075,10 +6230,10 @@ L_stm_s_takeabort: | |||
| 6075 | ror = 24; | 6230 | ror = 24; |
| 6076 | break; | 6231 | break; |
| 6077 | 6232 | ||
| 6078 | case 0xf3: | 6233 | case 0xf3: // REV |
| 6079 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); | 6234 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); |
| 6080 | return 1; | 6235 | return 1; |
| 6081 | case 0xfb: | 6236 | case 0xfb: // REV16 |
| 6082 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); | 6237 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); |
| 6083 | return 1; | 6238 | return 1; |
| 6084 | default: | 6239 | default: |
| @@ -6088,7 +6243,7 @@ L_stm_s_takeabort: | |||
| 6088 | if (ror == -1) | 6243 | if (ror == -1) |
| 6089 | break; | 6244 | break; |
| 6090 | 6245 | ||
| 6091 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6246 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF); |
| 6092 | if (Rm & 0x8000) | 6247 | if (Rm & 0x8000) |
| 6093 | Rm |= 0xffff0000; | 6248 | Rm |= 0xffff0000; |
| 6094 | 6249 | ||
| @@ -6101,18 +6256,33 @@ L_stm_s_takeabort: | |||
| 6101 | 6256 | ||
| 6102 | return 1; | 6257 | return 1; |
| 6103 | } | 6258 | } |
| 6104 | case 0x6c: | 6259 | case 0x6c: // UXTB16 and UXTAB16 |
| 6105 | if ((instr & 0xf03f0) == 0xf0070) { //uxtb16 | 6260 | { |
| 6106 | u8 src1 = BITS(0, 3); | 6261 | const u8 rm_idx = BITS(0, 3); |
| 6107 | u8 tar = BITS(12, 15); | 6262 | const u8 rn_idx = BITS(16, 19); |
| 6108 | u32 base = state->Reg[src1]; | 6263 | const u8 rd_idx = BITS(12, 15); |
| 6109 | u32 shamt = BITS(9,10)* 8; | 6264 | const u32 rm_val = state->Reg[rm_idx]; |
| 6110 | u32 in = ((base << (32 - shamt)) | (base >> shamt)); | 6265 | const u32 rn_val = state->Reg[rn_idx]; |
| 6111 | state->Reg[tar] = in & 0x00FF00FF; | 6266 | const u32 rotation = BITS(10, 11) * 8; |
| 6112 | return 1; | 6267 | const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation)); |
| 6113 | } else | 6268 | |
| 6114 | printf ("Unhandled v6 insn: uxtab16\n"); | 6269 | // UXTB16 |
| 6115 | break; | 6270 | if ((instr & 0xf03f0) == 0xf0070) { |
| 6271 | state->Reg[rd_idx] = rotated_rm & 0x00FF00FF; | ||
| 6272 | } | ||
| 6273 | else { // UXTAB16 | ||
| 6274 | const u8 lo_rotated = (rotated_rm & 0xFF); | ||
| 6275 | const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated; | ||
| 6276 | |||
| 6277 | const u8 hi_rotated = (rotated_rm >> 16) & 0xFF; | ||
| 6278 | const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated; | ||
| 6279 | |||
| 6280 | state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF)); | ||
| 6281 | } | ||
| 6282 | |||
| 6283 | return 1; | ||
| 6284 | } | ||
| 6285 | break; | ||
| 6116 | case 0x6e: { | 6286 | case 0x6e: { |
| 6117 | ARMword Rm; | 6287 | ARMword Rm; |
| 6118 | int ror = -1; | 6288 | int ror = -1; |
| @@ -6136,18 +6306,27 @@ L_stm_s_takeabort: | |||
| 6136 | //ichfly | 6306 | //ichfly |
| 6137 | //USAT16 | 6307 | //USAT16 |
| 6138 | { | 6308 | { |
| 6139 | u8 tar = BITS(12, 15); | 6309 | const u8 rd_idx = BITS(12, 15); |
| 6140 | u8 src = BITS(0, 3); | 6310 | const u8 rn_idx = BITS(0, 3); |
| 6141 | u8 val = BITS(16, 19); | 6311 | const u8 num_bits = BITS(16, 19); |
| 6142 | s16 a1 = (state->Reg[src]); | 6312 | const s16 max = 0xFFFF >> (16 - num_bits); |
| 6143 | s16 a2 = (state->Reg[src] >> 0x10); | 6313 | s16 rn_lo = (state->Reg[rn_idx]); |
| 6144 | s16 max = 0xFFFF >> (16 - val); | 6314 | s16 rn_hi = (state->Reg[rn_idx] >> 16); |
| 6145 | if (max < a1) a1 = max; | 6315 | |
| 6146 | if (max < a2) a2 = max; | 6316 | if (max < rn_lo) |
| 6147 | u32 temp2 = ((u32)(a2)) << 0x10; | 6317 | rn_lo = max; |
| 6148 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | 6318 | else if (rn_lo < 0) |
| 6319 | rn_lo = 0; | ||
| 6320 | |||
| 6321 | if (max < rn_hi) | ||
| 6322 | rn_hi = max; | ||
| 6323 | else if (rn_hi < 0) | ||
| 6324 | rn_hi = 0; | ||
| 6325 | |||
| 6326 | state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi << 16) & 0xFFFF); | ||
| 6327 | return 1; | ||
| 6149 | } | 6328 | } |
| 6150 | return 1; | 6329 | |
| 6151 | default: | 6330 | default: |
| 6152 | break; | 6331 | break; |
| 6153 | } | 6332 | } |
| @@ -6160,7 +6339,7 @@ L_stm_s_takeabort: | |||
| 6160 | break; | 6339 | break; |
| 6161 | } | 6340 | } |
| 6162 | 6341 | ||
| 6163 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6342 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF); |
| 6164 | 6343 | ||
| 6165 | if (BITS(16, 19) == 0xf) | 6344 | if (BITS(16, 19) == 0xf) |
| 6166 | /* UXTB */ | 6345 | /* UXTB */ |
| @@ -6190,9 +6369,13 @@ L_stm_s_takeabort: | |||
| 6190 | ror = 24; | 6369 | ror = 24; |
| 6191 | break; | 6370 | break; |
| 6192 | 6371 | ||
| 6193 | case 0xfb: | 6372 | case 0xfb: // REVSH |
| 6194 | printf("Unhandled v6 insn: revsh\n"); | 6373 | { |
| 6195 | return 0; | 6374 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00) >> 8); |
| 6375 | if (DEST & 0x8000) | ||
| 6376 | DEST |= 0xffff0000; | ||
| 6377 | return 1; | ||
| 6378 | } | ||
| 6196 | default: | 6379 | default: |
| 6197 | break; | 6380 | break; |
| 6198 | } | 6381 | } |
| @@ -6200,13 +6383,13 @@ L_stm_s_takeabort: | |||
| 6200 | if (ror == -1) | 6383 | if (ror == -1) |
| 6201 | break; | 6384 | break; |
| 6202 | 6385 | ||
| 6203 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6386 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF); |
| 6204 | 6387 | ||
| 6205 | /* UXT */ | 6388 | /* UXT */ |
| 6206 | /* state->Reg[BITS (12, 15)] = Rm; */ | 6389 | /* state->Reg[BITS (12, 15)] = Rm; */ |
| 6207 | /* dyf add */ | 6390 | /* dyf add */ |
| 6208 | if (BITS(16, 19) == 0xf) { | 6391 | if (BITS(16, 19) == 0xf) { |
| 6209 | state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF; | 6392 | state->Reg[BITS(12, 15)] = Rm; |
| 6210 | } | 6393 | } |
| 6211 | else { | 6394 | else { |
| 6212 | /* UXTAH */ | 6395 | /* UXTAH */ |
| @@ -6214,7 +6397,7 @@ L_stm_s_takeabort: | |||
| 6214 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] | 6397 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] |
| 6215 | // , Rm, BITS(10, 11)); | 6398 | // , Rm, BITS(10, 11)); |
| 6216 | // printf("icounter is %lld\n", state->NumInstrs); | 6399 | // printf("icounter is %lld\n", state->NumInstrs); |
| 6217 | state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm; | 6400 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6218 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); | 6401 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); |
| 6219 | // exit(-1); | 6402 | // exit(-1); |
| 6220 | } | 6403 | } |
| @@ -6222,45 +6405,46 @@ L_stm_s_takeabort: | |||
| 6222 | return 1; | 6405 | return 1; |
| 6223 | } | 6406 | } |
| 6224 | case 0x70: | 6407 | case 0x70: |
| 6225 | if ((instr & 0xf0d0) == 0xf010) { //smuad //ichfly | 6408 | // ichfly |
| 6226 | u8 tar = BITS(16, 19); | 6409 | // SMUAD, SMUSD, SMLAD, and SMLSD |
| 6227 | u8 src1 = BITS(0, 3); | 6410 | if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || |
| 6228 | u8 src2 = BITS(8, 11); | 6411 | (instr & 0xd0) == 0x10 || (instr & 0xd0) == 0x50) |
| 6229 | u8 swap = BIT(5); | 6412 | { |
| 6230 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6413 | const u8 rd_idx = BITS(16, 19); |
| 6231 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6414 | const u8 rn_idx = BITS(0, 3); |
| 6232 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6415 | const u8 rm_idx = BITS(8, 11); |
| 6233 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6416 | const u8 ra_idx = BITS(12, 15); |
| 6234 | state->Reg[tar] = a1*a2 + b1*b2; | 6417 | const bool do_swap = (BIT(5) == 1); |
| 6235 | return 1; | 6418 | |
| 6236 | 6419 | u32 rm_val = state->Reg[rm_idx]; | |
| 6237 | } else if ((instr & 0xf0d0) == 0xf050) { //smusd | 6420 | const u32 rn_val = state->Reg[rn_idx]; |
| 6238 | u8 tar = BITS(16, 19); | 6421 | |
| 6239 | u8 src1 = BITS(0, 3); | 6422 | if (do_swap) |
| 6240 | u8 src2 = BITS(8, 11); | 6423 | rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16)); |
| 6241 | u8 swap = BIT(5); | 6424 | |
| 6242 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6425 | const s16 rm_lo = (rm_val & 0xFFFF); |
| 6243 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6426 | const s16 rm_hi = ((rm_val >> 16) & 0xFFFF); |
| 6244 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6427 | const s16 rn_lo = (rn_val & 0xFFFF); |
| 6245 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6428 | const s16 rn_hi = ((rn_val >> 16) & 0xFFFF); |
| 6246 | state->Reg[tar] = a1*a2 - b1*b2; | 6429 | |
| 6247 | return 1; | 6430 | // SMUAD |
| 6248 | } else if ((instr & 0xd0) == 0x10) { //smlad | 6431 | if ((instr & 0xf0d0) == 0xf010) { |
| 6249 | u8 tar = BITS(16, 19); | 6432 | state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi); |
| 6250 | u8 src1 = BITS(0, 3); | 6433 | } |
| 6251 | u8 src2 = BITS(8, 11); | 6434 | // SMUSD |
| 6252 | u8 src3 = BITS(12, 15); | 6435 | else if ((instr & 0xf0d0) == 0xf050) { |
| 6253 | u8 swap = BIT(5); | 6436 | state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi); |
| 6254 | 6437 | } | |
| 6255 | u32 a3 = state->Reg[src3]; | 6438 | // SMLAD |
| 6256 | 6439 | else if ((instr & 0xd0) == 0x10) { | |
| 6257 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6440 | state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx]; |
| 6258 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6441 | } |
| 6259 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6442 | // SMLSD |
| 6260 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6443 | else { |
| 6261 | state->Reg[tar] = a1*a2 + b1*b2 + a3; | 6444 | state->Reg[rd_idx] = ((rn_lo * rm_lo) - (rn_hi * rm_hi)) + (s32)state->Reg[ra_idx]; |
| 6445 | } | ||
| 6262 | return 1; | 6446 | return 1; |
| 6263 | } else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n"); | 6447 | } |
| 6264 | break; | 6448 | break; |
| 6265 | case 0x74: | 6449 | case 0x74: |
| 6266 | printf ("Unhandled v6 insn: smlald/smlsld\n"); | 6450 | printf ("Unhandled v6 insn: smlald/smlsld\n"); |
| @@ -6269,7 +6453,30 @@ L_stm_s_takeabort: | |||
| 6269 | printf ("Unhandled v6 insn: smmla/smmls/smmul\n"); | 6453 | printf ("Unhandled v6 insn: smmla/smmls/smmul\n"); |
| 6270 | break; | 6454 | break; |
| 6271 | case 0x78: | 6455 | case 0x78: |
| 6272 | printf ("Unhandled v6 insn: usad/usada8\n"); | 6456 | if (BITS(20, 24) == 0x18) |
| 6457 | { | ||
| 6458 | const u8 rm_idx = BITS(8, 11); | ||
| 6459 | const u8 rn_idx = BITS(0, 3); | ||
| 6460 | const u8 rd_idx = BITS(16, 19); | ||
| 6461 | |||
| 6462 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 6463 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 6464 | |||
| 6465 | const u8 diff1 = (u8)std::labs((rn_val & 0xFF) - (rm_val & 0xFF)); | ||
| 6466 | const u8 diff2 = (u8)std::labs(((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF)); | ||
| 6467 | const u8 diff3 = (u8)std::labs(((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF)); | ||
| 6468 | const u8 diff4 = (u8)std::labs(((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF)); | ||
| 6469 | |||
| 6470 | u32 finalDif = (diff1 + diff2 + diff3 + diff4); | ||
| 6471 | |||
| 6472 | // Op is USADA8 if true. | ||
| 6473 | const u8 ra_idx = BITS(12, 15); | ||
| 6474 | if (ra_idx != 15) | ||
| 6475 | finalDif += state->Reg[ra_idx]; | ||
| 6476 | |||
| 6477 | state->Reg[rd_idx] = finalDif; | ||
| 6478 | return 1; | ||
| 6479 | } | ||
| 6273 | break; | 6480 | break; |
| 6274 | case 0x7a: | 6481 | case 0x7a: |
| 6275 | printf ("Unhandled v6 insn: usbfx\n"); | 6482 | printf ("Unhandled v6 insn: usbfx\n"); |
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 871900497..6c33d8b78 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp | |||
| @@ -614,7 +614,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f | |||
| 614 | exceptions |= FPSCR_IDC; | 614 | exceptions |= FPSCR_IDC; |
| 615 | 615 | ||
| 616 | if (tm & VFP_NAN) | 616 | if (tm & VFP_NAN) |
| 617 | vsm.sign = 0; | 617 | vsm.sign = 1; |
| 618 | 618 | ||
| 619 | if (vsm.exponent >= 127 + 32) { | 619 | if (vsm.exponent >= 127 + 32) { |
| 620 | d = vsm.sign ? 0 : 0xffffffff; | 620 | d = vsm.sign ? 0 : 0xffffffff; |
| @@ -1148,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) | |||
| 1148 | /* | 1148 | /* |
| 1149 | * Subtraction is addition with one sign inverted. | 1149 | * Subtraction is addition with one sign inverted. |
| 1150 | */ | 1150 | */ |
| 1151 | 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); | ||
| 1152 | } | 1155 | } |
| 1153 | 1156 | ||
| 1154 | /* | 1157 | /* |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 64de0cbba..22213d647 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/core/core.h b/src/core/core.h index 850bb0ab4..05dbe0ae5 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 1a0b2724a..321648b37 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index b197cf40c..496234538 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive_backend.h index 27ed23cd0..e2979be17 100644 --- a/src/core/file_sys/archive.h +++ b/src/core/file_sys/archive_backend.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -10,8 +10,8 @@ | |||
| 10 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 11 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 12 | 12 | ||
| 13 | #include "core/file_sys/file.h" | 13 | #include "core/file_sys/file_backend.h" |
| 14 | #include "core/file_sys/directory.h" | 14 | #include "core/file_sys/directory_backend.h" |
| 15 | 15 | ||
| 16 | #include "core/mem_map.h" | 16 | #include "core/mem_map.h" |
| 17 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| @@ -45,6 +45,11 @@ public: | |||
| 45 | { | 45 | { |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | Path(const char* path): | ||
| 49 | type(Char), string(path) | ||
| 50 | { | ||
| 51 | } | ||
| 52 | |||
| 48 | Path(LowPathType type, u32 size, u32 pointer): | 53 | Path(LowPathType type, u32 size, u32 pointer): |
| 49 | type(type) | 54 | type(type) |
| 50 | { | 55 | { |
| @@ -143,7 +148,16 @@ public: | |||
| 143 | case Char: | 148 | case Char: |
| 144 | return std::vector<u8>(string.begin(), string.end()); | 149 | return std::vector<u8>(string.begin(), string.end()); |
| 145 | case Wchar: | 150 | case Wchar: |
| 146 | return std::vector<u8>(u16str.begin(), u16str.end()); | 151 | { |
| 152 | // use two u8 for each character of u16str | ||
| 153 | std::vector<u8> to_return(u16str.size() * 2); | ||
| 154 | for (size_t i = 0; i < u16str.size(); ++i) { | ||
| 155 | u16 tmp_char = u16str.at(i); | ||
| 156 | to_return[i*2] = (tmp_char & 0xFF00) >> 8; | ||
| 157 | to_return[i*2 + 1] = (tmp_char & 0x00FF); | ||
| 158 | } | ||
| 159 | return to_return; | ||
| 160 | } | ||
| 147 | case Empty: | 161 | case Empty: |
| 148 | return {}; | 162 | return {}; |
| 149 | default: | 163 | default: |
| @@ -160,27 +174,14 @@ private: | |||
| 160 | std::u16string u16str; | 174 | std::u16string u16str; |
| 161 | }; | 175 | }; |
| 162 | 176 | ||
| 163 | class Archive : NonCopyable { | 177 | class ArchiveBackend : NonCopyable { |
| 164 | public: | 178 | public: |
| 165 | /// Supported archive types | 179 | virtual ~ArchiveBackend() { } |
| 166 | enum class IdCode : u32 { | ||
| 167 | RomFS = 0x00000003, | ||
| 168 | SaveData = 0x00000004, | ||
| 169 | ExtSaveData = 0x00000006, | ||
| 170 | SharedExtSaveData = 0x00000007, | ||
| 171 | SystemSaveData = 0x00000008, | ||
| 172 | SDMC = 0x00000009, | ||
| 173 | SDMCWriteOnly = 0x0000000A, | ||
| 174 | }; | ||
| 175 | |||
| 176 | Archive() { } | ||
| 177 | virtual ~Archive() { } | ||
| 178 | 180 | ||
| 179 | /** | 181 | /** |
| 180 | * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) | 182 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |
| 181 | * @return IdCode of the archive | ||
| 182 | */ | 183 | */ |
| 183 | virtual IdCode GetIdCode() const = 0; | 184 | virtual std::string GetName() const = 0; |
| 184 | 185 | ||
| 185 | /** | 186 | /** |
| 186 | * Open a file specified by its path, using the specified mode | 187 | * Open a file specified by its path, using the specified mode |
| @@ -188,7 +189,7 @@ public: | |||
| 188 | * @param mode Mode to open the file with | 189 | * @param mode Mode to open the file with |
| 189 | * @return Opened file, or nullptr | 190 | * @return Opened file, or nullptr |
| 190 | */ | 191 | */ |
| 191 | virtual std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const = 0; | 192 | virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0; |
| 192 | 193 | ||
| 193 | /** | 194 | /** |
| 194 | * Delete a file specified by its path | 195 | * Delete a file specified by its path |
| @@ -213,6 +214,14 @@ public: | |||
| 213 | virtual bool DeleteDirectory(const FileSys::Path& path) const = 0; | 214 | virtual bool DeleteDirectory(const FileSys::Path& path) const = 0; |
| 214 | 215 | ||
| 215 | /** | 216 | /** |
| 217 | * Create a file specified by its path | ||
| 218 | * @param path Path relative to the Archive | ||
| 219 | * @param size The size of the new file, filled with zeroes | ||
| 220 | * @return File creation result code | ||
| 221 | */ | ||
| 222 | virtual ResultCode CreateFile(const Path& path, u32 size) const = 0; | ||
| 223 | |||
| 224 | /** | ||
| 216 | * Create a directory specified by its path | 225 | * Create a directory specified by its path |
| 217 | * @param path Path relative to the archive | 226 | * @param path Path relative to the archive |
| 218 | * @return Whether the directory could be created | 227 | * @return Whether the directory could be created |
| @@ -232,37 +241,7 @@ public: | |||
| 232 | * @param path Path relative to the archive | 241 | * @param path Path relative to the archive |
| 233 | * @return Opened directory, or nullptr | 242 | * @return Opened directory, or nullptr |
| 234 | */ | 243 | */ |
| 235 | virtual std::unique_ptr<Directory> OpenDirectory(const Path& path) const = 0; | 244 | virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; |
| 236 | |||
| 237 | /** | ||
| 238 | * Read data from the archive | ||
| 239 | * @param offset Offset in bytes to start reading data from | ||
| 240 | * @param length Length in bytes of data to read from archive | ||
| 241 | * @param buffer Buffer to read data into | ||
| 242 | * @return Number of bytes read | ||
| 243 | */ | ||
| 244 | virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; | ||
| 245 | |||
| 246 | /** | ||
| 247 | * Write data to the archive | ||
| 248 | * @param offset Offset in bytes to start writing data to | ||
| 249 | * @param length Length in bytes of data to write to archive | ||
| 250 | * @param buffer Buffer to write data from | ||
| 251 | * @param flush The flush parameters (0 == do not flush) | ||
| 252 | * @return Number of bytes written | ||
| 253 | */ | ||
| 254 | virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0; | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Get the size of the archive in bytes | ||
| 258 | * @return Size of the archive in bytes | ||
| 259 | */ | ||
| 260 | virtual size_t GetSize() const = 0; | ||
| 261 | |||
| 262 | /** | ||
| 263 | * Set the size of the archive in bytes | ||
| 264 | */ | ||
| 265 | virtual void SetSize(const u64 size) = 0; | ||
| 266 | }; | 245 | }; |
| 267 | 246 | ||
| 268 | } // namespace FileSys | 247 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index 74974c2df..ced0794ef 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp | |||
| @@ -1,8 +1,11 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | ||
| 6 | |||
| 5 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/make_unique.h" | ||
| 6 | 9 | ||
| 7 | #include "core/file_sys/archive_romfs.h" | 10 | #include "core/file_sys/archive_romfs.h" |
| 8 | #include "core/file_sys/directory_romfs.h" | 11 | #include "core/file_sys/directory_romfs.h" |
| @@ -20,17 +23,14 @@ Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { | |||
| 20 | } | 23 | } |
| 21 | } | 24 | } |
| 22 | 25 | ||
| 23 | Archive_RomFS::~Archive_RomFS() { | ||
| 24 | } | ||
| 25 | |||
| 26 | /** | 26 | /** |
| 27 | * Open a file specified by its path, using the specified mode | 27 | * Open a file specified by its path, using the specified mode |
| 28 | * @param path Path relative to the archive | 28 | * @param path Path relative to the archive |
| 29 | * @param mode Mode to open the file with | 29 | * @param mode Mode to open the file with |
| 30 | * @return Opened file, or nullptr | 30 | * @return Opened file, or nullptr |
| 31 | */ | 31 | */ |
| 32 | std::unique_ptr<File> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { | 32 | std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { |
| 33 | return std::unique_ptr<File>(new File_RomFS); | 33 | return Common::make_unique<File_RomFS>(this); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | /** | 36 | /** |
| @@ -58,6 +58,12 @@ bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const { | |||
| 58 | return false; | 58 | return false; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | ResultCode Archive_RomFS::CreateFile(const Path& path, u32 size) const { | ||
| 62 | LOG_WARNING(Service_FS, "Attempted to create a file in ROMFS."); | ||
| 63 | // TODO: Verify error code | ||
| 64 | return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 65 | } | ||
| 66 | |||
| 61 | /** | 67 | /** |
| 62 | * Create a directory specified by its path | 68 | * Create a directory specified by its path |
| 63 | * @param path Path relative to the archive | 69 | * @param path Path relative to the archive |
| @@ -78,49 +84,8 @@ bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys | |||
| 78 | * @param path Path relative to the archive | 84 | * @param path Path relative to the archive |
| 79 | * @return Opened directory, or nullptr | 85 | * @return Opened directory, or nullptr |
| 80 | */ | 86 | */ |
| 81 | std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const Path& path) const { | 87 | std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const { |
| 82 | return std::unique_ptr<Directory>(new Directory_RomFS); | 88 | return Common::make_unique<Directory_RomFS>(); |
| 83 | } | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Read data from the archive | ||
| 87 | * @param offset Offset in bytes to start reading data from | ||
| 88 | * @param length Length in bytes of data to read from archive | ||
| 89 | * @param buffer Buffer to read data into | ||
| 90 | * @return Number of bytes read | ||
| 91 | */ | ||
| 92 | size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 93 | LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); | ||
| 94 | memcpy(buffer, &raw_data[(u32)offset], length); | ||
| 95 | return length; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Write data to the archive | ||
| 100 | * @param offset Offset in bytes to start writing data to | ||
| 101 | * @param length Length in bytes of data to write to archive | ||
| 102 | * @param buffer Buffer to write data from | ||
| 103 | * @param flush The flush parameters (0 == do not flush) | ||
| 104 | * @return Number of bytes written | ||
| 105 | */ | ||
| 106 | size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { | ||
| 107 | LOG_WARNING(Service_FS, "Attempted to write to ROMFS."); | ||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Get the size of the archive in bytes | ||
| 113 | * @return Size of the archive in bytes | ||
| 114 | */ | ||
| 115 | size_t Archive_RomFS::GetSize() const { | ||
| 116 | return sizeof(u8) * raw_data.size(); | ||
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Set the size of the archive in bytes | ||
| 121 | */ | ||
| 122 | void Archive_RomFS::SetSize(const u64 size) { | ||
| 123 | LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS"); | ||
| 124 | } | 89 | } |
| 125 | 90 | ||
| 126 | } // namespace FileSys | 91 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 222bdc356..2fafd0d2a 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -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,7 +29,7 @@ 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 Path& 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 | * Delete a file specified by its path | 35 | * Delete a file specified by its path |
| @@ -59,6 +54,14 @@ public: | |||
| 59 | bool DeleteDirectory(const FileSys::Path& path) const override; | 54 | bool DeleteDirectory(const FileSys::Path& path) const override; |
| 60 | 55 | ||
| 61 | /** | 56 | /** |
| 57 | * Create a file specified by its path | ||
| 58 | * @param path Path relative to the Archive | ||
| 59 | * @param size The size of the new file, filled with zeroes | ||
| 60 | * @return File creation result code | ||
| 61 | */ | ||
| 62 | ResultCode CreateFile(const Path& path, u32 size) const override; | ||
| 63 | |||
| 64 | /** | ||
| 62 | * Create a directory specified by its path | 65 | * Create a directory specified by its path |
| 63 | * @param path Path relative to the archive | 66 | * @param path Path relative to the archive |
| 64 | * @return Whether the directory could be created | 67 | * @return Whether the directory could be created |
| @@ -78,39 +81,11 @@ public: | |||
| 78 | * @param path Path relative to the archive | 81 | * @param path Path relative to the archive |
| 79 | * @return Opened directory, or nullptr | 82 | * @return Opened directory, or nullptr |
| 80 | */ | 83 | */ |
| 81 | std::unique_ptr<Directory> OpenDirectory(const Path& path) const override; | 84 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |
| 82 | |||
| 83 | /** | ||
| 84 | * Read data from the archive | ||
| 85 | * @param offset Offset in bytes to start reading data from | ||
| 86 | * @param length Length in bytes of data to read from archive | ||
| 87 | * @param buffer Buffer to read data into | ||
| 88 | * @return Number of bytes read | ||
| 89 | */ | ||
| 90 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 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 Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Get the size of the archive in bytes | ||
| 104 | * @return Size of the archive in bytes | ||
| 105 | */ | ||
| 106 | size_t GetSize() const override; | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Set the size of the archive in bytes | ||
| 110 | */ | ||
| 111 | void SetSize(const u64 size) override; | ||
| 112 | 85 | ||
| 113 | private: | 86 | private: |
| 87 | friend class File_RomFS; | ||
| 88 | |||
| 114 | std::vector<u8> raw_data; | 89 | std::vector<u8> raw_data; |
| 115 | }; | 90 | }; |
| 116 | 91 | ||
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp new file mode 100644 index 000000000..cb4a80f5b --- /dev/null +++ b/src/core/file_sys/archive_savedata.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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..5b0ce29e6 --- /dev/null +++ b/src/core/file_sys/archive_savedata.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "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 true if it initialized successfully | ||
| 25 | */ | ||
| 26 | bool Initialize(); | ||
| 27 | |||
| 28 | std::string GetName() const override { return "SaveData"; } | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 9e524b60e..1c1c170b6 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sys/stat.h> | 5 | #include <sys/stat.h> |
| @@ -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,18 +16,10 @@ | |||
| 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; | ||
| 22 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); | 20 | LOG_INFO(Service_FS, "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 | LOG_WARNING(Service_FS, "SDMC disabled by config."); | 25 | LOG_WARNING(Service_FS, "SDMC disabled by config."); |
| @@ -43,115 +34,4 @@ bool Archive_SDMC::Initialize() { | |||
| 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 Path& path, const Mode mode) const { | ||
| 53 | LOG_DEBUG(Service_FS, "called path=%s mode=%u", path.DebugStr().c_str(), mode.hex); | ||
| 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 | * Delete a file specified by its path | ||
| 62 | * @param path Path relative to the archive | ||
| 63 | * @return Whether the file could be deleted | ||
| 64 | */ | ||
| 65 | bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const { | ||
| 66 | return FileUtil::Delete(GetMountPoint() + path.AsString()); | ||
| 67 | } | ||
| 68 | |||
| 69 | bool Archive_SDMC::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 70 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Delete a directory specified by its path | ||
| 75 | * @param path Path relative to the archive | ||
| 76 | * @return Whether the directory could be deleted | ||
| 77 | */ | ||
| 78 | bool Archive_SDMC::DeleteDirectory(const FileSys::Path& path) const { | ||
| 79 | return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Create a directory specified by its path | ||
| 84 | * @param path Path relative to the archive | ||
| 85 | * @return Whether the directory could be created | ||
| 86 | */ | ||
| 87 | bool Archive_SDMC::CreateDirectory(const Path& path) const { | ||
| 88 | return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||
| 89 | } | ||
| 90 | |||
| 91 | bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 92 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Open a directory specified by its path | ||
| 97 | * @param path Path relative to the archive | ||
| 98 | * @return Opened directory, or nullptr | ||
| 99 | */ | ||
| 100 | std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const { | ||
| 101 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||
| 102 | Directory_SDMC* directory = new Directory_SDMC(this, path); | ||
| 103 | if (!directory->Open()) | ||
| 104 | return nullptr; | ||
| 105 | return std::unique_ptr<Directory>(directory); | ||
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Read data from the archive | ||
| 110 | * @param offset Offset in bytes to start reading archive from | ||
| 111 | * @param length Length in bytes to read data from archive | ||
| 112 | * @param buffer Buffer to read data into | ||
| 113 | * @return Number of bytes read | ||
| 114 | */ | ||
| 115 | size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 116 | LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); | ||
| 117 | return -1; | ||
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * Write data to the archive | ||
| 122 | * @param offset Offset in bytes to start writing data to | ||
| 123 | * @param length Length in bytes of data to write to archive | ||
| 124 | * @param buffer Buffer to write data from | ||
| 125 | * @param flush The flush parameters (0 == do not flush) | ||
| 126 | * @return Number of bytes written | ||
| 127 | */ | ||
| 128 | size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { | ||
| 129 | LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); | ||
| 130 | return -1; | ||
| 131 | } | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Get the size of the archive in bytes | ||
| 135 | * @return Size of the archive in bytes | ||
| 136 | */ | ||
| 137 | size_t Archive_SDMC::GetSize() const { | ||
| 138 | LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Set the size of the archive in bytes | ||
| 144 | */ | ||
| 145 | void Archive_SDMC::SetSize(const u64 size) { | ||
| 146 | LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Getter for the path used for this Archive | ||
| 151 | * @return Mount point of that passthrough archive | ||
| 152 | */ | ||
| 153 | std::string Archive_SDMC::GetMountPoint() const { | ||
| 154 | return mount_point; | ||
| 155 | } | ||
| 156 | |||
| 157 | } // namespace FileSys | 37 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 19f563a62..1b801f217 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 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,102 +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 Path& path, const Mode mode) const override; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Delete a file specified by its path | ||
| 45 | * @param path Path relative to the archive | ||
| 46 | * @return Whether the file could be deleted | ||
| 47 | */ | ||
| 48 | bool DeleteFile(const FileSys::Path& path) const override; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Rename a File specified by its path | ||
| 52 | * @param src_path Source path relative to the archive | ||
| 53 | * @param dest_path Destination path relative to the archive | ||
| 54 | * @return Whether rename succeeded | ||
| 55 | */ | ||
| 56 | bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Delete a directory specified by its path | ||
| 60 | * @param path Path relative to the archive | ||
| 61 | * @return Whether the directory could be deleted | ||
| 62 | */ | ||
| 63 | bool DeleteDirectory(const FileSys::Path& path) const override; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Create a directory specified by its path | ||
| 67 | * @param path Path relative to the archive | ||
| 68 | * @return Whether the directory could be created | ||
| 69 | */ | ||
| 70 | bool CreateDirectory(const Path& path) const override; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Rename a Directory specified by its path | ||
| 74 | * @param src_path Source path relative to the archive | ||
| 75 | * @param dest_path Destination path relative to the archive | ||
| 76 | * @return Whether rename succeeded | ||
| 77 | */ | ||
| 78 | bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Open a directory specified by its path | ||
| 82 | * @param path Path relative to the archive | ||
| 83 | * @return Opened directory, or nullptr | ||
| 84 | */ | ||
| 85 | std::unique_ptr<Directory> OpenDirectory(const Path& path) const override; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Read data from the archive | ||
| 89 | * @param offset Offset in bytes to start reading archive from | ||
| 90 | * @param length Length in bytes to read data from archive | ||
| 91 | * @param buffer Buffer to read data into | ||
| 92 | * @return Number of bytes read | ||
| 93 | */ | ||
| 94 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Write data to the archive | ||
| 98 | * @param offset Offset in bytes to start writing data to | ||
| 99 | * @param length Length in bytes of data to write to archive | ||
| 100 | * @param buffer Buffer to write data from | ||
| 101 | * @param flush The flush parameters (0 == do not flush) | ||
| 102 | * @return Number of bytes written | ||
| 103 | */ | ||
| 104 | size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Get the size of the archive in bytes | ||
| 108 | * @return Size of the archive in bytes | ||
| 109 | */ | ||
| 110 | size_t GetSize() const override; | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Set the size of the archive in bytes | ||
| 114 | */ | ||
| 115 | void SetSize(const u64 size) override; | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Getter for the path used for this Archive | ||
| 119 | * @return Mount point of that passthrough archive | ||
| 120 | */ | ||
| 121 | std::string GetMountPoint() const; | ||
| 122 | |||
| 123 | private: | ||
| 124 | std::string mount_point; | ||
| 125 | }; | 29 | }; |
| 126 | 30 | ||
| 127 | } // namespace FileSys | 31 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp new file mode 100644 index 000000000..0da32d510 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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_systemsavedata.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 | static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 save_id) { | ||
| 20 | u32 save_high = static_cast<u32>((save_id >> 32) & 0xFFFFFFFF); | ||
| 21 | u32 save_low = static_cast<u32>(save_id & 0xFFFFFFFF); | ||
| 22 | return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high); | ||
| 23 | } | ||
| 24 | |||
| 25 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) | ||
| 26 | : DiskArchive(GetSystemSaveDataPath(mount_point, save_id)) { | ||
| 27 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool Archive_SystemSaveData::Initialize() { | ||
| 31 | if (!FileUtil::CreateFullPath(mount_point)) { | ||
| 32 | LOG_ERROR(Service_FS, "Unable to create SystemSaveData path."); | ||
| 33 | return false; | ||
| 34 | } | ||
| 35 | |||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h new file mode 100644 index 000000000..443e27091 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "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 SystemSaveData archive | ||
| 18 | /// TODO(Subv): This archive should point to a location in the NAND, | ||
| 19 | /// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High> | ||
| 20 | class Archive_SystemSaveData final : public DiskArchive { | ||
| 21 | public: | ||
| 22 | Archive_SystemSaveData(const std::string& mount_point, u64 save_id); | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Initialize the archive. | ||
| 26 | * @return true if it initialized successfully | ||
| 27 | */ | ||
| 28 | bool Initialize(); | ||
| 29 | |||
| 30 | std::string GetName() const override { return "SystemSaveData"; } | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h index 1bb4101d6..7f327dc42 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory_backend.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -36,10 +36,10 @@ 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 | 43 | ||
| 44 | /** | 44 | /** |
| 45 | * Open the directory | 45 | * Open the directory |
diff --git a/src/core/file_sys/directory_romfs.cpp b/src/core/file_sys/directory_romfs.cpp index e6d571391..0b95f9b65 100644 --- a/src/core/file_sys/directory_romfs.cpp +++ b/src/core/file_sys/directory_romfs.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h index e2944099e..2297f1645 100644 --- a/src/core/file_sys/directory_romfs.h +++ b/src/core/file_sys/directory_romfs.h | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 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,7 +14,7 @@ | |||
| 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; |
diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp deleted file mode 100644 index 519787641..000000000 --- a/src/core/file_sys/directory_sdmc.cpp +++ /dev/null | |||
| @@ -1,88 +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 Path& 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 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 23 | |||
| 24 | } | ||
| 25 | |||
| 26 | Directory_SDMC::~Directory_SDMC() { | ||
| 27 | Close(); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool Directory_SDMC::Open() { | ||
| 31 | if (!FileUtil::IsDirectory(path)) | ||
| 32 | return false; | ||
| 33 | FileUtil::ScanDirectoryTree(path, directory); | ||
| 34 | children_iterator = directory.children.begin(); | ||
| 35 | return true; | ||
| 36 | } | ||
| 37 | |||
| 38 | /** | ||
| 39 | * List files contained in the directory | ||
| 40 | * @param count Number of entries to return at once in entries | ||
| 41 | * @param entries Buffer to read data into | ||
| 42 | * @return Number of entries listed | ||
| 43 | */ | ||
| 44 | u32 Directory_SDMC::Read(const u32 count, Entry* entries) { | ||
| 45 | u32 entries_read = 0; | ||
| 46 | |||
| 47 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 48 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 49 | const std::string& filename = file.virtualName; | ||
| 50 | Entry& entry = entries[entries_read]; | ||
| 51 | |||
| 52 | LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); | ||
| 53 | |||
| 54 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 55 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 56 | entry.filename[j] = filename[j]; | ||
| 57 | if (!filename[j]) | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | |||
| 61 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 62 | |||
| 63 | entry.is_directory = file.isDirectory; | ||
| 64 | entry.is_hidden = (filename[0] == '.'); | ||
| 65 | entry.is_read_only = 0; | ||
| 66 | entry.file_size = file.size; | ||
| 67 | |||
| 68 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 69 | // most user SD cards. | ||
| 70 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 71 | // file bit. | ||
| 72 | entry.is_archive = !file.isDirectory; | ||
| 73 | |||
| 74 | ++entries_read; | ||
| 75 | ++children_iterator; | ||
| 76 | } | ||
| 77 | return entries_read; | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Close the directory | ||
| 82 | * @return true if the directory closed correctly | ||
| 83 | */ | ||
| 84 | bool Directory_SDMC::Close() const { | ||
| 85 | return true; | ||
| 86 | } | ||
| 87 | |||
| 88 | } // 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 4c08b0d61..000000000 --- a/src/core/file_sys/directory_sdmc.h +++ /dev/null | |||
| @@ -1,55 +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 Path& path); | ||
| 23 | ~Directory_SDMC() override; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Open the directory | ||
| 27 | * @return true if the directory opened correctly | ||
| 28 | */ | ||
| 29 | bool Open() override; | ||
| 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 Read(const u32 count, Entry* entries) override; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Close the directory | ||
| 41 | * @return true if the directory closed correctly | ||
| 42 | */ | ||
| 43 | bool Close() const override; | ||
| 44 | |||
| 45 | private: | ||
| 46 | std::string path; | ||
| 47 | u32 total_entries_in_directory; | ||
| 48 | FileUtil::FSTEntry directory; | ||
| 49 | |||
| 50 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 51 | // from the next one. This iterator will always point to the next unread entry. | ||
| 52 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 53 | }; | ||
| 54 | |||
| 55 | } // 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..1689a1a91 --- /dev/null +++ b/src/core/file_sys/disk_archive.cpp | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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 | ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { | ||
| 39 | std::string full_path = GetMountPoint() + path.AsString(); | ||
| 40 | |||
| 41 | if (FileUtil::Exists(full_path)) | ||
| 42 | return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info); | ||
| 43 | |||
| 44 | if (size == 0) { | ||
| 45 | FileUtil::CreateEmptyFile(full_path); | ||
| 46 | return RESULT_SUCCESS; | ||
| 47 | } | ||
| 48 | |||
| 49 | FileUtil::IOFile file(full_path, "wb"); | ||
| 50 | // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) | ||
| 51 | // We do this by seeking to the right size, then writing a single null byte. | ||
| 52 | if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) | ||
| 53 | return RESULT_SUCCESS; | ||
| 54 | |||
| 55 | return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource, ErrorLevel::Info); | ||
| 56 | } | ||
| 57 | |||
| 58 | |||
| 59 | bool DiskArchive::CreateDirectory(const Path& path) const { | ||
| 60 | return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||
| 61 | } | ||
| 62 | |||
| 63 | bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 64 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 65 | } | ||
| 66 | |||
| 67 | std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { | ||
| 68 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||
| 69 | DiskDirectory* directory = new DiskDirectory(this, path); | ||
| 70 | if (!directory->Open()) | ||
| 71 | return nullptr; | ||
| 72 | return std::unique_ptr<DirectoryBackend>(directory); | ||
| 73 | } | ||
| 74 | |||
| 75 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 76 | |||
| 77 | DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) { | ||
| 78 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 79 | // the root directory we set while opening the archive. | ||
| 80 | // For example, opening /../../etc/passwd can give the emulated program your users list. | ||
| 81 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 82 | this->mode.hex = mode.hex; | ||
| 83 | this->archive = archive; | ||
| 84 | } | ||
| 85 | |||
| 86 | bool DiskFile::Open() { | ||
| 87 | if (!mode.create_flag && !FileUtil::Exists(path)) { | ||
| 88 | LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||
| 89 | return false; | ||
| 90 | } | ||
| 91 | |||
| 92 | std::string mode_string; | ||
| 93 | if (mode.create_flag) | ||
| 94 | mode_string = "w+"; | ||
| 95 | else if (mode.write_flag) | ||
| 96 | mode_string = "r+"; // Files opened with Write access can be read from | ||
| 97 | else if (mode.read_flag) | ||
| 98 | mode_string = "r"; | ||
| 99 | |||
| 100 | // Open the file in binary mode, to avoid problems with CR/LF on Windows systems | ||
| 101 | mode_string += "b"; | ||
| 102 | |||
| 103 | file = new FileUtil::IOFile(path, mode_string.c_str()); | ||
| 104 | return true; | ||
| 105 | } | ||
| 106 | |||
| 107 | size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 108 | file->Seek(offset, SEEK_SET); | ||
| 109 | return file->ReadBytes(buffer, length); | ||
| 110 | } | ||
| 111 | |||
| 112 | size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||
| 113 | file->Seek(offset, SEEK_SET); | ||
| 114 | size_t written = file->WriteBytes(buffer, length); | ||
| 115 | if (flush) | ||
| 116 | file->Flush(); | ||
| 117 | return written; | ||
| 118 | } | ||
| 119 | |||
| 120 | size_t DiskFile::GetSize() const { | ||
| 121 | return static_cast<size_t>(file->GetSize()); | ||
| 122 | } | ||
| 123 | |||
| 124 | bool DiskFile::SetSize(const u64 size) const { | ||
| 125 | file->Resize(size); | ||
| 126 | file->Flush(); | ||
| 127 | return true; | ||
| 128 | } | ||
| 129 | |||
| 130 | bool DiskFile::Close() const { | ||
| 131 | return file->Close(); | ||
| 132 | } | ||
| 133 | |||
| 134 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 135 | |||
| 136 | DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) { | ||
| 137 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 138 | // the root directory we set while opening the archive. | ||
| 139 | // For example, opening /../../usr/bin can give the emulated program your installed programs. | ||
| 140 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 141 | this->archive = archive; | ||
| 142 | } | ||
| 143 | |||
| 144 | bool DiskDirectory::Open() { | ||
| 145 | if (!FileUtil::IsDirectory(path)) | ||
| 146 | return false; | ||
| 147 | FileUtil::ScanDirectoryTree(path, directory); | ||
| 148 | children_iterator = directory.children.begin(); | ||
| 149 | return true; | ||
| 150 | } | ||
| 151 | |||
| 152 | u32 DiskDirectory::Read(const u32 count, Entry* entries) { | ||
| 153 | u32 entries_read = 0; | ||
| 154 | |||
| 155 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 156 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 157 | const std::string& filename = file.virtualName; | ||
| 158 | Entry& entry = entries[entries_read]; | ||
| 159 | |||
| 160 | LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); | ||
| 161 | |||
| 162 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 163 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 164 | entry.filename[j] = filename[j]; | ||
| 165 | if (!filename[j]) | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | |||
| 169 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 170 | |||
| 171 | entry.is_directory = file.isDirectory; | ||
| 172 | entry.is_hidden = (filename[0] == '.'); | ||
| 173 | entry.is_read_only = 0; | ||
| 174 | entry.file_size = file.size; | ||
| 175 | |||
| 176 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 177 | // most user SD cards. | ||
| 178 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 179 | // file bit. | ||
| 180 | entry.is_archive = !file.isDirectory; | ||
| 181 | |||
| 182 | ++entries_read; | ||
| 183 | ++children_iterator; | ||
| 184 | } | ||
| 185 | return entries_read; | ||
| 186 | } | ||
| 187 | |||
| 188 | } // 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..6c9b689e0 --- /dev/null +++ b/src/core/file_sys/disk_archive.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/archive_backend.h" | ||
| 11 | #include "core/loader/loader.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Helper which implements a backend accessing the host machine's filesystem. | ||
| 20 | * This should be subclassed by concrete archive types, which will provide the | ||
| 21 | * base directory on the host filesystem and override any required functionality. | ||
| 22 | */ | ||
| 23 | class DiskArchive : public ArchiveBackend { | ||
| 24 | public: | ||
| 25 | DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} | ||
| 26 | |||
| 27 | virtual std::string GetName() const = 0; | ||
| 28 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||
| 29 | bool DeleteFile(const FileSys::Path& path) const override; | ||
| 30 | bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 31 | bool DeleteDirectory(const FileSys::Path& path) const override; | ||
| 32 | ResultCode CreateFile(const Path& path, u32 size) const override; | ||
| 33 | bool CreateDirectory(const Path& path) const override; | ||
| 34 | bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 35 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Getter for the path used for this Archive | ||
| 39 | * @return Mount point of that passthrough archive | ||
| 40 | */ | ||
| 41 | const std::string& GetMountPoint() const { | ||
| 42 | return mount_point; | ||
| 43 | } | ||
| 44 | |||
| 45 | protected: | ||
| 46 | std::string mount_point; | ||
| 47 | }; | ||
| 48 | |||
| 49 | class DiskFile : public FileBackend { | ||
| 50 | public: | ||
| 51 | DiskFile(); | ||
| 52 | DiskFile(const DiskArchive* archive, const Path& path, const Mode mode); | ||
| 53 | |||
| 54 | ~DiskFile() override { | ||
| 55 | Close(); | ||
| 56 | } | ||
| 57 | |||
| 58 | bool Open() override; | ||
| 59 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 60 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||
| 61 | size_t GetSize() const override; | ||
| 62 | bool SetSize(const u64 size) const override; | ||
| 63 | bool Close() const override; | ||
| 64 | |||
| 65 | void Flush() const override { | ||
| 66 | file->Flush(); | ||
| 67 | } | ||
| 68 | |||
| 69 | protected: | ||
| 70 | const DiskArchive* archive; | ||
| 71 | std::string path; | ||
| 72 | Mode mode; | ||
| 73 | FileUtil::IOFile* file; | ||
| 74 | }; | ||
| 75 | |||
| 76 | class DiskDirectory : public DirectoryBackend { | ||
| 77 | public: | ||
| 78 | DiskDirectory(); | ||
| 79 | DiskDirectory(const DiskArchive* archive, const Path& path); | ||
| 80 | |||
| 81 | ~DiskDirectory() override { | ||
| 82 | Close(); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool Open() override; | ||
| 86 | u32 Read(const u32 count, Entry* entries) override; | ||
| 87 | |||
| 88 | bool Close() const override { | ||
| 89 | return true; | ||
| 90 | } | ||
| 91 | |||
| 92 | protected: | ||
| 93 | const DiskArchive* archive; | ||
| 94 | std::string path; | ||
| 95 | u32 total_entries_in_directory; | ||
| 96 | FileUtil::FSTEntry directory; | ||
| 97 | |||
| 98 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 99 | // from the next one. This iterator will always point to the next unread entry. | ||
| 100 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 101 | }; | ||
| 102 | |||
| 103 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h index 4013b6c3e..35890af1f 100644 --- a/src/core/file_sys/file.h +++ b/src/core/file_sys/file_backend.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -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..e79936407 100644 --- a/src/core/file_sys/file_romfs.cpp +++ b/src/core/file_sys/file_romfs.cpp | |||
| @@ -1,28 +1,23 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 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..04d8a16a2 100644 --- a/src/core/file_sys/file_romfs.h +++ b/src/core/file_sys/file_romfs.h | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 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 46c29900b..000000000 --- a/src/core/file_sys/file_sdmc.cpp +++ /dev/null | |||
| @@ -1,110 +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 Path& 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.AsString(); | ||
| 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 | LOG_ERROR(Service_FS, "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.create_flag) | ||
| 42 | mode_string = "w+"; | ||
| 43 | else if (mode.write_flag) | ||
| 44 | mode_string = "r+"; // Files opened with Write access can be read from | ||
| 45 | else if (mode.read_flag) | ||
| 46 | mode_string = "r"; | ||
| 47 | |||
| 48 | // Open the file in binary mode, to avoid problems with CR/LF on Windows systems | ||
| 49 | mode_string += "b"; | ||
| 50 | |||
| 51 | file = new FileUtil::IOFile(path, mode_string.c_str()); | ||
| 52 | return true; | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Read data from the file | ||
| 57 | * @param offset Offset in bytes to start reading data from | ||
| 58 | * @param length Length in bytes of data to read from file | ||
| 59 | * @param buffer Buffer to read data into | ||
| 60 | * @return Number of bytes read | ||
| 61 | */ | ||
| 62 | size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 63 | file->Seek(offset, SEEK_SET); | ||
| 64 | return file->ReadBytes(buffer, length); | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Write data to the file | ||
| 69 | * @param offset Offset in bytes to start writing data to | ||
| 70 | * @param length Length in bytes of data to write to file | ||
| 71 | * @param flush The flush parameters (0 == do not flush) | ||
| 72 | * @param buffer Buffer to read data from | ||
| 73 | * @return Number of bytes written | ||
| 74 | */ | ||
| 75 | size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||
| 76 | file->Seek(offset, SEEK_SET); | ||
| 77 | size_t written = file->WriteBytes(buffer, length); | ||
| 78 | if (flush) | ||
| 79 | file->Flush(); | ||
| 80 | return written; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Get the size of the file in bytes | ||
| 85 | * @return Size of the file in bytes | ||
| 86 | */ | ||
| 87 | size_t File_SDMC::GetSize() const { | ||
| 88 | return static_cast<size_t>(file->GetSize()); | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Set the size of the file in bytes | ||
| 93 | * @param size New size of the file | ||
| 94 | * @return true if successful | ||
| 95 | */ | ||
| 96 | bool File_SDMC::SetSize(const u64 size) const { | ||
| 97 | file->Resize(size); | ||
| 98 | file->Flush(); | ||
| 99 | return true; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Close the file | ||
| 104 | * @return true if the file closed correctly | ||
| 105 | */ | ||
| 106 | bool File_SDMC::Close() const { | ||
| 107 | return file->Close(); | ||
| 108 | } | ||
| 109 | |||
| 110 | } // 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 80b445968..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 Path& 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 d8ba9e6cf..721a600b5 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h index fa01b5cdb..3975af18f 100644 --- a/src/core/hle/config_mem.h +++ b/src/core/hle/config_mem.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp index e34229a57..425959be4 100644 --- a/src/core/hle/coprocessor.cpp +++ b/src/core/hle/coprocessor.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/coprocessor.h" | 5 | #include "core/hle/coprocessor.h" |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index b44479b2f..3259ce9eb 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 3f73b5538..2d314a4cf 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
| @@ -8,6 +8,8 @@ | |||
| 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" | ||
| 12 | #include "core/hle/service/cfg/cfg.h" | ||
| 11 | 13 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 13 | 15 | ||
| @@ -56,6 +58,8 @@ void RegisterAllModules() { | |||
| 56 | 58 | ||
| 57 | void Init() { | 59 | void Init() { |
| 58 | Service::Init(); | 60 | Service::Init(); |
| 61 | Service::FS::ArchiveInit(); | ||
| 62 | Service::CFG::CFGInit(); | ||
| 59 | 63 | ||
| 60 | RegisterAllModules(); | 64 | RegisterAllModules(); |
| 61 | 65 | ||
| @@ -63,6 +67,8 @@ void Init() { | |||
| 63 | } | 67 | } |
| 64 | 68 | ||
| 65 | void Shutdown() { | 69 | void Shutdown() { |
| 70 | Service::CFG::CFGShutdown(); | ||
| 71 | Service::FS::ArchiveShutdown(); | ||
| 66 | Service::Shutdown(); | 72 | Service::Shutdown(); |
| 67 | 73 | ||
| 68 | g_module_db.clear(); | 74 | g_module_db.clear(); |
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 4ab258c69..59b770f02 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 9a921108d..77491900a 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 8a5fb10b4..030e7ad7b 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp deleted file mode 100644 index ddc09e13b..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null | |||
| @@ -1,435 +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/kernel/archive.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/service.h" | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // Kernel namespace | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | // Command to access archive file | ||
| 22 | enum class FileCommand : u32 { | ||
| 23 | Dummy1 = 0x000100C6, | ||
| 24 | Control = 0x040100C4, | ||
| 25 | OpenSubFile = 0x08010100, | ||
| 26 | Read = 0x080200C2, | ||
| 27 | Write = 0x08030102, | ||
| 28 | GetSize = 0x08040000, | ||
| 29 | SetSize = 0x08050080, | ||
| 30 | GetAttributes = 0x08060000, | ||
| 31 | SetAttributes = 0x08070040, | ||
| 32 | Close = 0x08080000, | ||
| 33 | Flush = 0x08090000, | ||
| 34 | }; | ||
| 35 | |||
| 36 | // Command to access directory | ||
| 37 | enum class DirectoryCommand : u32 { | ||
| 38 | Dummy1 = 0x000100C6, | ||
| 39 | Control = 0x040100C4, | ||
| 40 | Read = 0x08010042, | ||
| 41 | Close = 0x08020000, | ||
| 42 | }; | ||
| 43 | |||
| 44 | class Archive : public Object { | ||
| 45 | public: | ||
| 46 | std::string GetTypeName() const override { return "Archive"; } | ||
| 47 | std::string GetName() const override { return name; } | ||
| 48 | |||
| 49 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; } | ||
| 50 | Kernel::HandleType GetHandleType() const override { return HandleType::Archive; } | ||
| 51 | |||
| 52 | std::string name; ///< Name of archive (optional) | ||
| 53 | FileSys::Archive* backend; ///< Archive backend interface | ||
| 54 | |||
| 55 | ResultVal<bool> SyncRequest() override { | ||
| 56 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 57 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 58 | |||
| 59 | switch (cmd) { | ||
| 60 | // Read from archive... | ||
| 61 | case FileCommand::Read: | ||
| 62 | { | ||
| 63 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 64 | u32 length = cmd_buff[3]; | ||
| 65 | u32 address = cmd_buff[5]; | ||
| 66 | |||
| 67 | // Number of bytes read | ||
| 68 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | // Write to archive... | ||
| 72 | case FileCommand::Write: | ||
| 73 | { | ||
| 74 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 75 | u32 length = cmd_buff[3]; | ||
| 76 | u32 flush = cmd_buff[4]; | ||
| 77 | u32 address = cmd_buff[6]; | ||
| 78 | |||
| 79 | // Number of bytes written | ||
| 80 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | case FileCommand::GetSize: | ||
| 84 | { | ||
| 85 | u64 filesize = (u64) backend->GetSize(); | ||
| 86 | cmd_buff[2] = (u32) filesize; // Lower word | ||
| 87 | cmd_buff[3] = (u32) (filesize >> 32); // Upper word | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | case FileCommand::SetSize: | ||
| 91 | { | ||
| 92 | backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | case FileCommand::Close: | ||
| 96 | { | ||
| 97 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 98 | CloseArchive(backend->GetIdCode()); | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | // Unknown command... | ||
| 102 | default: | ||
| 103 | { | ||
| 104 | LOG_ERROR(Service_FS, "Unknown command=0x%08X", cmd); | ||
| 105 | return UnimplementedFunction(ErrorModule::FS); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | cmd_buff[1] = 0; // No error | ||
| 109 | return MakeResult<bool>(false); | ||
| 110 | } | ||
| 111 | }; | ||
| 112 | |||
| 113 | class File : public Object { | ||
| 114 | public: | ||
| 115 | std::string GetTypeName() const override { return "File"; } | ||
| 116 | std::string GetName() const override { return path.DebugStr(); } | ||
| 117 | |||
| 118 | static Kernel::HandleType GetStaticHandleType() { return HandleType::File; } | ||
| 119 | Kernel::HandleType GetHandleType() const override { return HandleType::File; } | ||
| 120 | |||
| 121 | FileSys::Path path; ///< Path of the file | ||
| 122 | std::unique_ptr<FileSys::File> backend; ///< File backend interface | ||
| 123 | |||
| 124 | ResultVal<bool> SyncRequest() override { | ||
| 125 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 126 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 127 | switch (cmd) { | ||
| 128 | |||
| 129 | // Read from file... | ||
| 130 | case FileCommand::Read: | ||
| 131 | { | ||
| 132 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 133 | u32 length = cmd_buff[3]; | ||
| 134 | u32 address = cmd_buff[5]; | ||
| 135 | LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 136 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 137 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | |||
| 141 | // Write to file... | ||
| 142 | case FileCommand::Write: | ||
| 143 | { | ||
| 144 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 145 | u32 length = cmd_buff[3]; | ||
| 146 | u32 flush = cmd_buff[4]; | ||
| 147 | u32 address = cmd_buff[6]; | ||
| 148 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 149 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 150 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 151 | break; | ||
| 152 | } | ||
| 153 | |||
| 154 | case FileCommand::GetSize: | ||
| 155 | { | ||
| 156 | LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 157 | u64 size = backend->GetSize(); | ||
| 158 | cmd_buff[2] = (u32)size; | ||
| 159 | cmd_buff[3] = size >> 32; | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | |||
| 163 | case FileCommand::SetSize: | ||
| 164 | { | ||
| 165 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 166 | LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", | ||
| 167 | GetTypeName().c_str(), GetName().c_str(), size); | ||
| 168 | backend->SetSize(size); | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | |||
| 172 | case FileCommand::Close: | ||
| 173 | { | ||
| 174 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 175 | Kernel::g_object_pool.Destroy<File>(GetHandle()); | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | |||
| 179 | // Unknown command... | ||
| 180 | default: | ||
| 181 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 182 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 183 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 184 | return error; | ||
| 185 | } | ||
| 186 | cmd_buff[1] = 0; // No error | ||
| 187 | return MakeResult<bool>(false); | ||
| 188 | } | ||
| 189 | }; | ||
| 190 | |||
| 191 | class Directory : public Object { | ||
| 192 | public: | ||
| 193 | std::string GetTypeName() const override { return "Directory"; } | ||
| 194 | std::string GetName() const override { return path.DebugStr(); } | ||
| 195 | |||
| 196 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; } | ||
| 197 | Kernel::HandleType GetHandleType() const override { return HandleType::Directory; } | ||
| 198 | |||
| 199 | FileSys::Path path; ///< Path of the directory | ||
| 200 | std::unique_ptr<FileSys::Directory> backend; ///< File backend interface | ||
| 201 | |||
| 202 | ResultVal<bool> SyncRequest() override { | ||
| 203 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 204 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 205 | switch (cmd) { | ||
| 206 | |||
| 207 | // Read from directory... | ||
| 208 | case DirectoryCommand::Read: | ||
| 209 | { | ||
| 210 | u32 count = cmd_buff[1]; | ||
| 211 | u32 address = cmd_buff[3]; | ||
| 212 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 213 | LOG_TRACE(Service_FS, "Read %s %s: count=%d", | ||
| 214 | GetTypeName().c_str(), GetName().c_str(), count); | ||
| 215 | |||
| 216 | // Number of entries actually read | ||
| 217 | cmd_buff[2] = backend->Read(count, entries); | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | |||
| 221 | case DirectoryCommand::Close: | ||
| 222 | { | ||
| 223 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 224 | Kernel::g_object_pool.Destroy<Directory>(GetHandle()); | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | |||
| 228 | // Unknown command... | ||
| 229 | default: | ||
| 230 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 231 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 232 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 233 | return error; | ||
| 234 | } | ||
| 235 | cmd_buff[1] = 0; // No error | ||
| 236 | return MakeResult<bool>(false); | ||
| 237 | } | ||
| 238 | }; | ||
| 239 | |||
| 240 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 241 | |||
| 242 | std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode | ||
| 243 | |||
| 244 | ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) { | ||
| 245 | auto itr = g_archive_map.find(id_code); | ||
| 246 | if (itr == g_archive_map.end()) { | ||
| 247 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 248 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 249 | } | ||
| 250 | |||
| 251 | return MakeResult<Handle>(itr->second); | ||
| 252 | } | ||
| 253 | |||
| 254 | ResultCode CloseArchive(FileSys::Archive::IdCode id_code) { | ||
| 255 | auto itr = g_archive_map.find(id_code); | ||
| 256 | if (itr == g_archive_map.end()) { | ||
| 257 | LOG_ERROR(Service_FS, "Cannot close archive %d, does not exist!", (int)id_code); | ||
| 258 | return InvalidHandle(ErrorModule::FS); | ||
| 259 | } | ||
| 260 | |||
| 261 | LOG_TRACE(Service_FS, "Closed archive %d", (int) id_code); | ||
| 262 | return RESULT_SUCCESS; | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * Mounts an archive | ||
| 267 | * @param archive Pointer to the archive to mount | ||
| 268 | */ | ||
| 269 | ResultCode MountArchive(Archive* archive) { | ||
| 270 | FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); | ||
| 271 | ResultVal<Handle> archive_handle = OpenArchive(id_code); | ||
| 272 | if (archive_handle.Succeeded()) { | ||
| 273 | LOG_ERROR(Service_FS, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); | ||
| 274 | return archive_handle.Code(); | ||
| 275 | } | ||
| 276 | g_archive_map[id_code] = archive->GetHandle(); | ||
| 277 | LOG_TRACE(Service_FS, "Mounted archive %s", archive->GetName().c_str()); | ||
| 278 | return RESULT_SUCCESS; | ||
| 279 | } | ||
| 280 | |||
| 281 | ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) { | ||
| 282 | Archive* archive = new Archive; | ||
| 283 | Handle handle = Kernel::g_object_pool.Create(archive); | ||
| 284 | archive->name = name; | ||
| 285 | archive->backend = backend; | ||
| 286 | |||
| 287 | ResultCode result = MountArchive(archive); | ||
| 288 | if (result.IsError()) { | ||
| 289 | return result; | ||
| 290 | } | ||
| 291 | |||
| 292 | return RESULT_SUCCESS; | ||
| 293 | } | ||
| 294 | |||
| 295 | ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { | ||
| 296 | // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create | ||
| 297 | // the archive file handles at app loading, and then keep them persistent throughout execution. | ||
| 298 | // Archives file handles are just reused and not actually freed until emulation shut down. | ||
| 299 | // Verify if real hardware works this way, or if new handles are created each time | ||
| 300 | if (path.GetType() == FileSys::Binary) | ||
| 301 | // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend | ||
| 302 | // design. While the functionally of this is OK, our implementation decision to separate | ||
| 303 | // normal files from archive file pointers is very likely wrong. | ||
| 304 | // See https://github.com/citra-emu/citra/issues/205 | ||
| 305 | return MakeResult<Handle>(archive_handle); | ||
| 306 | |||
| 307 | File* file = new File; | ||
| 308 | Handle handle = Kernel::g_object_pool.Create(file); | ||
| 309 | |||
| 310 | Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); | ||
| 311 | if (archive == nullptr) { | ||
| 312 | return InvalidHandle(ErrorModule::FS); | ||
| 313 | } | ||
| 314 | file->path = path; | ||
| 315 | file->backend = archive->backend->OpenFile(path, mode); | ||
| 316 | |||
| 317 | if (!file->backend) { | ||
| 318 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 319 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 320 | } | ||
| 321 | |||
| 322 | return MakeResult<Handle>(handle); | ||
| 323 | } | ||
| 324 | |||
| 325 | ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 326 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 327 | if (archive == nullptr) | ||
| 328 | return InvalidHandle(ErrorModule::FS); | ||
| 329 | if (archive->backend->DeleteFile(path)) | ||
| 330 | return RESULT_SUCCESS; | ||
| 331 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 332 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 333 | } | ||
| 334 | |||
| 335 | ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, | ||
| 336 | Handle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 337 | Archive* src_archive = Kernel::g_object_pool.GetFast<Archive>(src_archive_handle); | ||
| 338 | Archive* dest_archive = Kernel::g_object_pool.GetFast<Archive>(dest_archive_handle); | ||
| 339 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 340 | return InvalidHandle(ErrorModule::FS); | ||
| 341 | if (src_archive == dest_archive) { | ||
| 342 | if (src_archive->backend->RenameFile(src_path, dest_path)) | ||
| 343 | return RESULT_SUCCESS; | ||
| 344 | } else { | ||
| 345 | // TODO: Implement renaming across archives | ||
| 346 | return UnimplementedFunction(ErrorModule::FS); | ||
| 347 | } | ||
| 348 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 349 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 350 | } | ||
| 351 | |||
| 352 | ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 353 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 354 | if (archive == nullptr) | ||
| 355 | return InvalidHandle(ErrorModule::FS); | ||
| 356 | if (archive->backend->DeleteDirectory(path)) | ||
| 357 | return RESULT_SUCCESS; | ||
| 358 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 359 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 360 | } | ||
| 361 | |||
| 362 | ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 363 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 364 | if (archive == nullptr) | ||
| 365 | return InvalidHandle(ErrorModule::FS); | ||
| 366 | if (archive->backend->CreateDirectory(path)) | ||
| 367 | return RESULT_SUCCESS; | ||
| 368 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 369 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 370 | } | ||
| 371 | |||
| 372 | ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, | ||
| 373 | Handle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 374 | Archive* src_archive = Kernel::g_object_pool.GetFast<Archive>(src_archive_handle); | ||
| 375 | Archive* dest_archive = Kernel::g_object_pool.GetFast<Archive>(dest_archive_handle); | ||
| 376 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 377 | return InvalidHandle(ErrorModule::FS); | ||
| 378 | if (src_archive == dest_archive) { | ||
| 379 | if (src_archive->backend->RenameDirectory(src_path, dest_path)) | ||
| 380 | return RESULT_SUCCESS; | ||
| 381 | } else { | ||
| 382 | // TODO: Implement renaming across archives | ||
| 383 | return UnimplementedFunction(ErrorModule::FS); | ||
| 384 | } | ||
| 385 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 386 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 387 | } | ||
| 388 | |||
| 389 | /** | ||
| 390 | * Open a Directory from an Archive | ||
| 391 | * @param archive_handle Handle to an open Archive object | ||
| 392 | * @param path Path to the Directory inside of the Archive | ||
| 393 | * @return Opened Directory object | ||
| 394 | */ | ||
| 395 | ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 396 | Directory* directory = new Directory; | ||
| 397 | Handle handle = Kernel::g_object_pool.Create(directory); | ||
| 398 | |||
| 399 | Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); | ||
| 400 | if (archive == nullptr) { | ||
| 401 | return InvalidHandle(ErrorModule::FS); | ||
| 402 | } | ||
| 403 | directory->path = path; | ||
| 404 | directory->backend = archive->backend->OpenDirectory(path); | ||
| 405 | |||
| 406 | if (!directory->backend) { | ||
| 407 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 408 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 409 | } | ||
| 410 | |||
| 411 | return MakeResult<Handle>(handle); | ||
| 412 | } | ||
| 413 | |||
| 414 | /// Initialize archives | ||
| 415 | void ArchiveInit() { | ||
| 416 | g_archive_map.clear(); | ||
| 417 | |||
| 418 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 419 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 420 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 421 | |||
| 422 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 423 | auto archive = new FileSys::Archive_SDMC(sdmc_directory); | ||
| 424 | if (archive->Initialize()) | ||
| 425 | CreateArchive(archive, "SDMC"); | ||
| 426 | else | ||
| 427 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 428 | } | ||
| 429 | |||
| 430 | /// Shutdown archives | ||
| 431 | void ArchiveShutdown() { | ||
| 432 | g_archive_map.clear(); | ||
| 433 | } | ||
| 434 | |||
| 435 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 288080209..4de3fab3c 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | 5 | #include <map> |
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 73aec4e79..da793df1a 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b38be0a49..5fd06046e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| @@ -9,12 +9,12 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/thread.h" | 11 | #include "core/hle/kernel/thread.h" |
| 12 | #include "core/hle/kernel/archive.h" | ||
| 13 | 12 | ||
| 14 | namespace Kernel { | 13 | namespace Kernel { |
| 15 | 14 | ||
| 16 | Handle g_main_thread = 0; | 15 | Handle g_main_thread = 0; |
| 17 | ObjectPool g_object_pool; | 16 | ObjectPool g_object_pool; |
| 17 | u64 g_program_id = 0; | ||
| 18 | 18 | ||
| 19 | ObjectPool::ObjectPool() { | 19 | ObjectPool::ObjectPool() { |
| 20 | next_id = INITIAL_NEXT_ID; | 20 | next_id = INITIAL_NEXT_ID; |
| @@ -89,13 +89,11 @@ Object* ObjectPool::CreateByIDType(int type) { | |||
| 89 | /// Initialize the kernel | 89 | /// Initialize the kernel |
| 90 | void Init() { | 90 | void Init() { |
| 91 | Kernel::ThreadingInit(); | 91 | Kernel::ThreadingInit(); |
| 92 | Kernel::ArchiveInit(); | ||
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | /// Shutdown the kernel | 94 | /// Shutdown the kernel |
| 96 | void Shutdown() { | 95 | void Shutdown() { |
| 97 | Kernel::ThreadingShutdown(); | 96 | Kernel::ThreadingShutdown(); |
| 98 | Kernel::ArchiveShutdown(); | ||
| 99 | 97 | ||
| 100 | g_object_pool.Clear(); // Free all kernel objects | 98 | g_object_pool.Clear(); // Free all kernel objects |
| 101 | } | 99 | } |
| @@ -106,8 +104,6 @@ void Shutdown() { | |||
| 106 | * @return True on success, otherwise false | 104 | * @return True on success, otherwise false |
| 107 | */ | 105 | */ |
| 108 | bool LoadExec(u32 entry_point) { | 106 | bool LoadExec(u32 entry_point) { |
| 109 | Init(); | ||
| 110 | |||
| 111 | Core::g_app_core->SetPC(entry_point); | 107 | Core::g_app_core->SetPC(entry_point); |
| 112 | 108 | ||
| 113 | // 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 85e3264b9..32258d5a0 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -14,6 +14,10 @@ typedef s32 Result; | |||
| 14 | 14 | ||
| 15 | namespace Kernel { | 15 | namespace Kernel { |
| 16 | 16 | ||
| 17 | // From kernel.h. Declarations duplicated here to avoid a circular header dependency. | ||
| 18 | class Thread; | ||
| 19 | Thread* GetCurrentThread(); | ||
| 20 | |||
| 17 | enum KernelHandle { | 21 | enum KernelHandle { |
| 18 | CurrentThread = 0xFFFF8000, | 22 | CurrentThread = 0xFFFF8000, |
| 19 | CurrentProcess = 0xFFFF8001, | 23 | CurrentProcess = 0xFFFF8001, |
| @@ -22,7 +26,7 @@ enum KernelHandle { | |||
| 22 | enum class HandleType : u32 { | 26 | enum class HandleType : u32 { |
| 23 | Unknown = 0, | 27 | Unknown = 0, |
| 24 | Port = 1, | 28 | Port = 1, |
| 25 | Service = 2, | 29 | Session = 2, |
| 26 | Event = 3, | 30 | Event = 3, |
| 27 | Mutex = 4, | 31 | Mutex = 4, |
| 28 | SharedMemory = 5, | 32 | SharedMemory = 5, |
| @@ -30,10 +34,7 @@ enum class HandleType : u32 { | |||
| 30 | Thread = 7, | 34 | Thread = 7, |
| 31 | Process = 8, | 35 | Process = 8, |
| 32 | AddressArbiter = 9, | 36 | AddressArbiter = 9, |
| 33 | File = 10, | 37 | Semaphore = 10, |
| 34 | Semaphore = 11, | ||
| 35 | Archive = 12, | ||
| 36 | Directory = 13, | ||
| 37 | }; | 38 | }; |
| 38 | 39 | ||
| 39 | enum { | 40 | enum { |
| @@ -53,15 +54,6 @@ public: | |||
| 53 | virtual Kernel::HandleType GetHandleType() const = 0; | 54 | virtual Kernel::HandleType GetHandleType() const = 0; |
| 54 | 55 | ||
| 55 | /** | 56 | /** |
| 56 | * Synchronize kernel object. | ||
| 57 | * @return True if the current thread should wait as a result of the sync | ||
| 58 | */ | ||
| 59 | virtual ResultVal<bool> SyncRequest() { | ||
| 60 | LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); | ||
| 61 | return UnimplementedFunction(ErrorModule::Kernel); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Wait for kernel object to synchronize. | 57 | * Wait for kernel object to synchronize. |
| 66 | * @return True if the current thread should wait as a result of the wait | 58 | * @return True if the current thread should wait as a result of the wait |
| 67 | */ | 59 | */ |
| @@ -93,6 +85,10 @@ public: | |||
| 93 | 85 | ||
| 94 | template <class T> | 86 | template <class T> |
| 95 | T* Get(Handle handle) { | 87 | T* Get(Handle handle) { |
| 88 | if (handle == CurrentThread) { | ||
| 89 | return reinterpret_cast<T*>(GetCurrentThread()); | ||
| 90 | } | ||
| 91 | |||
| 96 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { | 92 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { |
| 97 | if (handle != 0) { | 93 | if (handle != 0) { |
| 98 | LOG_ERROR(Kernel, "Bad object handle %08x", handle); | 94 | LOG_ERROR(Kernel, "Bad object handle %08x", handle); |
| @@ -111,6 +107,10 @@ public: | |||
| 111 | // ONLY use this when you know the handle is valid. | 107 | // ONLY use this when you know the handle is valid. |
| 112 | template <class T> | 108 | template <class T> |
| 113 | T *GetFast(Handle handle) { | 109 | T *GetFast(Handle handle) { |
| 110 | if (handle == CurrentThread) { | ||
| 111 | return reinterpret_cast<T*>(GetCurrentThread()); | ||
| 112 | } | ||
| 113 | |||
| 114 | const Handle realHandle = handle - HANDLE_OFFSET; | 114 | const Handle realHandle = handle - HANDLE_OFFSET; |
| 115 | _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); | 115 | _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); |
| 116 | return static_cast<T*>(pool[realHandle]); | 116 | return static_cast<T*>(pool[realHandle]); |
| @@ -163,6 +163,12 @@ private: | |||
| 163 | extern ObjectPool g_object_pool; | 163 | extern ObjectPool g_object_pool; |
| 164 | extern Handle g_main_thread; | 164 | extern Handle g_main_thread; |
| 165 | 165 | ||
| 166 | /// The ID code of the currently running game | ||
| 167 | /// TODO(Subv): This variable should not be here, | ||
| 168 | /// we need a way to store information about the currently loaded application | ||
| 169 | /// for later query during runtime, maybe using the LDR service? | ||
| 170 | extern u64 g_program_id; | ||
| 171 | |||
| 166 | /// Initialize the kernel | 172 | /// Initialize the kernel |
| 167 | void Init(); | 173 | void Init(); |
| 168 | 174 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 5a173e129..5a18af114 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | 5 | #include <map> |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 7f4909a6e..a8ca97014 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6f56da8a9..b81d0b26a 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <queue> | 5 | #include <queue> |
| @@ -20,8 +20,8 @@ public: | |||
| 20 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; } | 20 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; } |
| 21 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; } | 21 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; } |
| 22 | 22 | ||
| 23 | u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have | 23 | s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have |
| 24 | u32 available_count; ///< Number of free slots left in the semaphore | 24 | s32 available_count; ///< Number of free slots left in the semaphore |
| 25 | std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore | 25 | std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore |
| 26 | std::string name; ///< Name of semaphore (optional) | 26 | std::string name; ///< Name of semaphore (optional) |
| 27 | 27 | ||
| @@ -49,8 +49,8 @@ public: | |||
| 49 | 49 | ||
| 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 51 | 51 | ||
| 52 | ResultCode CreateSemaphore(Handle* handle, u32 initial_count, | 52 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, |
| 53 | u32 max_count, const std::string& name) { | 53 | s32 max_count, const std::string& name) { |
| 54 | 54 | ||
| 55 | if (initial_count > max_count) | 55 | if (initial_count > max_count) |
| 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, | 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, |
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index f0075fdb8..8644ecf0c 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -18,7 +18,7 @@ namespace Kernel { | |||
| 18 | * @param name Optional name of semaphore | 18 | * @param name Optional name of semaphore |
| 19 | * @return ResultCode of the error | 19 | * @return ResultCode of the error |
| 20 | */ | 20 | */ |
| 21 | ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown"); | 21 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | * Releases a certain number of slots from a semaphore. | 24 | * Releases a certain number of slots from a semaphore. |
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h new file mode 100644 index 000000000..6760f346e --- /dev/null +++ b/src/core/hle/kernel/session.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "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 3c8c502c6..2840f13bb 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 304cf5b67..bb65c7ccd 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -12,11 +12,15 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | /// Permissions for mapped shared memory blocks | 13 | /// Permissions for mapped shared memory blocks |
| 14 | enum class MemoryPermission : u32 { | 14 | enum class MemoryPermission : u32 { |
| 15 | None = 0, | 15 | None = 0, |
| 16 | Read = (1u << 0), | 16 | Read = (1u << 0), |
| 17 | Write = (1u << 1), | 17 | Write = (1u << 1), |
| 18 | ReadWrite = (Read | Write), | 18 | ReadWrite = (Read | Write), |
| 19 | DontCare = (1u << 28) | 19 | Execute = (1u << 2), |
| 20 | ReadExecute = (Read | Execute), | ||
| 21 | WriteExecute = (Write | Execute), | ||
| 22 | ReadWriteExecute = (Read | Write | Execute), | ||
| 23 | DontCare = (1u << 28) | ||
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | /** | 26 | /** |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1c04701de..c6a8dc7b9 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| @@ -83,8 +83,7 @@ static Thread* current_thread; | |||
| 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup | 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| 84 | static u32 next_thread_id; ///< The next available thread id | 84 | static u32 next_thread_id; ///< The next available thread id |
| 85 | 85 | ||
| 86 | /// Gets the current thread | 86 | Thread* GetCurrentThread() { |
| 87 | inline Thread* GetCurrentThread() { | ||
| 88 | return current_thread; | 87 | return current_thread; |
| 89 | } | 88 | } |
| 90 | 89 | ||
| @@ -148,16 +147,19 @@ void ChangeReadyState(Thread* t, bool ready) { | |||
| 148 | } | 147 | } |
| 149 | } | 148 | } |
| 150 | 149 | ||
| 151 | /// Verify that a thread has not been released from waiting | 150 | /// Check if a thread is blocking on a specified wait type |
| 152 | static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { | 151 | static bool CheckWaitType(const Thread* thread, WaitType type) { |
| 153 | _dbg_assert_(Kernel, thread != nullptr); | 152 | return (type == thread->wait_type) && (thread->IsWaiting()); |
| 154 | return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); | ||
| 155 | } | 153 | } |
| 156 | 154 | ||
| 157 | /// Verify that a thread has not been released from waiting (with wait address) | 155 | /// Check if a thread is blocking on a specified wait type with a specified handle |
| 158 | static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | 156 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { |
| 159 | _dbg_assert_(Kernel, thread != nullptr); | 157 | return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); |
| 160 | return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address); | 158 | } |
| 159 | |||
| 160 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | ||
| 161 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | ||
| 162 | return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); | ||
| 161 | } | 163 | } |
| 162 | 164 | ||
| 163 | /// Stops the current thread | 165 | /// Stops the current thread |
| @@ -172,9 +174,9 @@ ResultCode StopThread(Handle handle, const char* reason) { | |||
| 172 | thread->status = THREADSTATUS_DORMANT; | 174 | thread->status = THREADSTATUS_DORMANT; |
| 173 | for (Handle waiting_handle : thread->waiting_threads) { | 175 | for (Handle waiting_handle : thread->waiting_threads) { |
| 174 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); | 176 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); |
| 175 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { | 177 | |
| 178 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) | ||
| 176 | ResumeThreadFromWait(waiting_handle); | 179 | ResumeThreadFromWait(waiting_handle); |
| 177 | } | ||
| 178 | } | 180 | } |
| 179 | thread->waiting_threads.clear(); | 181 | thread->waiting_threads.clear(); |
| 180 | 182 | ||
| @@ -210,7 +212,7 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 210 | for (Handle handle : thread_queue) { | 212 | for (Handle handle : thread_queue) { |
| 211 | Thread* thread = g_object_pool.Get<Thread>(handle); | 213 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 212 | 214 | ||
| 213 | if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) | 215 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 214 | continue; | 216 | continue; |
| 215 | 217 | ||
| 216 | if (thread == nullptr) | 218 | if (thread == nullptr) |
| @@ -235,7 +237,7 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) { | |||
| 235 | for (Handle handle : thread_queue) { | 237 | for (Handle handle : thread_queue) { |
| 236 | Thread* thread = g_object_pool.Get<Thread>(handle); | 238 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 237 | 239 | ||
| 238 | if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) | 240 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 239 | ResumeThreadFromWait(handle); | 241 | ResumeThreadFromWait(handle); |
| 240 | } | 242 | } |
| 241 | } | 243 | } |
| @@ -306,6 +308,8 @@ void ResumeThreadFromWait(Handle handle) { | |||
| 306 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); | 308 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); |
| 307 | if (thread) { | 309 | if (thread) { |
| 308 | thread->status &= ~THREADSTATUS_WAIT; | 310 | thread->status &= ~THREADSTATUS_WAIT; |
| 311 | thread->wait_handle = 0; | ||
| 312 | thread->wait_type = WAITTYPE_NONE; | ||
| 309 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 313 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 310 | ChangeReadyState(thread, true); | 314 | ChangeReadyState(thread, true); |
| 311 | } | 315 | } |
| @@ -469,19 +473,27 @@ void Reschedule() { | |||
| 469 | Thread* prev = GetCurrentThread(); | 473 | Thread* prev = GetCurrentThread(); |
| 470 | Thread* next = NextThread(); | 474 | Thread* next = NextThread(); |
| 471 | HLE::g_reschedule = false; | 475 | HLE::g_reschedule = false; |
| 472 | if (next > 0) { | ||
| 473 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 474 | 476 | ||
| 477 | if (next != nullptr) { | ||
| 478 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 475 | SwitchContext(next); | 479 | SwitchContext(next); |
| 480 | } else { | ||
| 481 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | ||
| 476 | 482 | ||
| 477 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep | 483 | for (Handle handle : thread_queue) { |
| 478 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. | 484 | Thread* thread = g_object_pool.Get<Thread>(handle); |
| 479 | // This results in the current thread yielding on a VBLANK once, and then it will be | 485 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", |
| 480 | // immediately placed back in the queue for execution. | 486 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); |
| 481 | if (prev->wait_type == WAITTYPE_VBLANK) { | ||
| 482 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 483 | } | 487 | } |
| 484 | } | 488 | } |
| 489 | |||
| 490 | // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put | ||
| 491 | // to sleep. So, we'll just immediately set it to "ready" again after an attempted context | ||
| 492 | // switch has occurred. This results in the current thread yielding on a sleep once, and then it | ||
| 493 | // will immediately be placed back in the queue for execution. | ||
| 494 | |||
| 495 | if (CheckWaitType(prev, WAITTYPE_SLEEP)) | ||
| 496 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 485 | } | 497 | } |
| 486 | 498 | ||
| 487 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | 499 | ResultCode GetThreadId(u32* thread_id, Handle handle) { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index be7adface..9396b6b26 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -40,7 +40,6 @@ enum WaitType { | |||
| 40 | WAITTYPE_SEMA, | 40 | WAITTYPE_SEMA, |
| 41 | WAITTYPE_EVENT, | 41 | WAITTYPE_EVENT, |
| 42 | WAITTYPE_THREADEND, | 42 | WAITTYPE_THREADEND, |
| 43 | WAITTYPE_VBLANK, | ||
| 44 | WAITTYPE_MUTEX, | 43 | WAITTYPE_MUTEX, |
| 45 | WAITTYPE_SYNCH, | 44 | WAITTYPE_SYNCH, |
| 46 | WAITTYPE_ARB, | 45 | WAITTYPE_ARB, |
| @@ -78,6 +77,9 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address); | |||
| 78 | /// Arbitrate all threads currently waiting... | 77 | /// Arbitrate all threads currently waiting... |
| 79 | void ArbitrateAllThreads(u32 arbiter, u32 address); | 78 | void ArbitrateAllThreads(u32 arbiter, u32 address); |
| 80 | 79 | ||
| 80 | /// Gets the current thread | ||
| 81 | Thread* GetCurrentThread(); | ||
| 82 | |||
| 81 | /// Gets the current thread handle | 83 | /// Gets the current thread handle |
| 82 | Handle GetCurrentThreadHandle(); | 84 | Handle GetCurrentThreadHandle(); |
| 83 | 85 | ||
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 15c4a2677..0e9c213e0 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -17,6 +17,8 @@ | |||
| 17 | /// Detailed description of the error. This listing is likely incomplete. | 17 | /// Detailed description of the error. This listing is likely incomplete. |
| 18 | enum class ErrorDescription : u32 { | 18 | enum class ErrorDescription : u32 { |
| 19 | Success = 0, | 19 | Success = 0, |
| 20 | FS_NotFound = 100, | ||
| 21 | FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive | ||
| 20 | InvalidSection = 1000, | 22 | InvalidSection = 1000, |
| 21 | TooLarge = 1001, | 23 | TooLarge = 1001, |
| 22 | NotAuthorized = 1002, | 24 | NotAuthorized = 1002, |
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index 4130feb9d..d180bb4ec 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| @@ -18,7 +18,7 @@ namespace AC_U { | |||
| 18 | * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. | 18 | * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. |
| 19 | */ | 19 | */ |
| 20 | void GetWifiStatus(Service::Interface* self) { | 20 | void GetWifiStatus(Service::Interface* self) { |
| 21 | u32* cmd_buff = Service::GetCommandBuffer(); | 21 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 22 | 22 | ||
| 23 | // TODO(purpasmart96): This function is only a stub, | 23 | // TODO(purpasmart96): This function is only a stub, |
| 24 | // it returns a valid result without implementing full functionality. | 24 | // it returns a valid result without implementing full functionality. |
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h index c91b28353..097b18c4e 100644 --- a/src/core/hle/service/ac_u.h +++ b/src/core/hle/service/ac_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp new file mode 100644 index 000000000..0b396b6d3 --- /dev/null +++ b/src/core/hle/service/am_app.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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/fs_user.h b/src/core/hle/service/am_app.h index 005382540..30a0be4c5 100644 --- a/src/core/hle/service/fs_user.h +++ b/src/core/hle/service/am_app.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -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_APP |
| 11 | 11 | ||
| 12 | namespace FS_User { | 12 | namespace AM_APP { |
| 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 | 17 | ||
| 20 | ~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:app"; |
| 28 | } | 24 | } |
| 29 | }; | 25 | }; |
| 30 | 26 | ||
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp index 403cac353..943205e9e 100644 --- a/src/core/hle/service/am_net.cpp +++ b/src/core/hle/service/am_net.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/am_net.h b/src/core/hle/service/am_net.h index 4816e1697..c0dbfb444 100644 --- a/src/core/hle/service/am_net.h +++ b/src/core/hle/service/am_net.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index b6d5d101f..fecc6e6f9 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
| @@ -40,7 +40,7 @@ enum class SignalType : u32 { | |||
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | void Initialize(Service::Interface* self) { | 42 | void Initialize(Service::Interface* self) { |
| 43 | u32* cmd_buff = Service::GetCommandBuffer(); | 43 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 44 | 44 | ||
| 45 | 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 |
| 46 | 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 |
| @@ -57,7 +57,7 @@ void Initialize(Service::Interface* self) { | |||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | void GetLockHandle(Service::Interface* self) { | 59 | void GetLockHandle(Service::Interface* self) { |
| 60 | u32* cmd_buff = Service::GetCommandBuffer(); | 60 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 61 | 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 | 62 | ||
| 63 | if (0 == lock_handle) { | 63 | if (0 == lock_handle) { |
| @@ -78,14 +78,14 @@ void GetLockHandle(Service::Interface* self) { | |||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void Enable(Service::Interface* self) { | 80 | void Enable(Service::Interface* self) { |
| 81 | u32* cmd_buff = Service::GetCommandBuffer(); | 81 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 82 | 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? |
| 83 | cmd_buff[1] = 0; // No error | 83 | cmd_buff[1] = 0; // No error |
| 84 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); | 84 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | void InquireNotification(Service::Interface* self) { | 87 | void InquireNotification(Service::Interface* self) { |
| 88 | u32* cmd_buff = Service::GetCommandBuffer(); | 88 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 89 | u32 app_id = cmd_buff[2]; | 89 | u32 app_id = cmd_buff[2]; |
| 90 | cmd_buff[1] = 0; // No error | 90 | cmd_buff[1] = 0; // No error |
| 91 | cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type | 91 | cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type |
| @@ -112,7 +112,7 @@ void InquireNotification(Service::Interface* self) { | |||
| 112 | * 8 : Output parameter buffer ptr | 112 | * 8 : Output parameter buffer ptr |
| 113 | */ | 113 | */ |
| 114 | void ReceiveParameter(Service::Interface* self) { | 114 | void ReceiveParameter(Service::Interface* self) { |
| 115 | u32* cmd_buff = Service::GetCommandBuffer(); | 115 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 116 | u32 app_id = cmd_buff[1]; | 116 | u32 app_id = cmd_buff[1]; |
| 117 | u32 buffer_size = cmd_buff[2]; | 117 | u32 buffer_size = cmd_buff[2]; |
| 118 | cmd_buff[1] = 0; // No error | 118 | cmd_buff[1] = 0; // No error |
| @@ -143,7 +143,7 @@ void ReceiveParameter(Service::Interface* self) { | |||
| 143 | * 8 : Output parameter buffer ptr | 143 | * 8 : Output parameter buffer ptr |
| 144 | */ | 144 | */ |
| 145 | void GlanceParameter(Service::Interface* self) { | 145 | void GlanceParameter(Service::Interface* self) { |
| 146 | u32* cmd_buff = Service::GetCommandBuffer(); | 146 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 147 | u32 app_id = cmd_buff[1]; | 147 | u32 app_id = cmd_buff[1]; |
| 148 | u32 buffer_size = cmd_buff[2]; | 148 | u32 buffer_size = cmd_buff[2]; |
| 149 | 149 | ||
| @@ -170,7 +170,7 @@ void GlanceParameter(Service::Interface* self) { | |||
| 170 | * 1 : Result of function, 0 on success, otherwise error code | 170 | * 1 : Result of function, 0 on success, otherwise error code |
| 171 | */ | 171 | */ |
| 172 | void AppletUtility(Service::Interface* self) { | 172 | void AppletUtility(Service::Interface* self) { |
| 173 | u32* cmd_buff = Service::GetCommandBuffer(); | 173 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 174 | 174 | ||
| 175 | // These are from 3dbrew - I'm not really sure what they're used for. | 175 | // These are from 3dbrew - I'm not really sure what they're used for. |
| 176 | u32 unk = cmd_buff[1]; | 176 | u32 unk = cmd_buff[1]; |
| @@ -196,7 +196,7 @@ void AppletUtility(Service::Interface* self) { | |||
| 196 | void GetSharedFont(Service::Interface* self) { | 196 | void GetSharedFont(Service::Interface* self) { |
| 197 | LOG_TRACE(Kernel_SVC, "called"); | 197 | LOG_TRACE(Kernel_SVC, "called"); |
| 198 | 198 | ||
| 199 | u32* cmd_buff = Service::GetCommandBuffer(); | 199 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 200 | 200 | ||
| 201 | if (!shared_font.empty()) { | 201 | if (!shared_font.empty()) { |
| 202 | // TODO(bunnei): This function shouldn't copy the shared font every time it's called. | 202 | // TODO(bunnei): This function shouldn't copy the shared font every time it's called. |
| @@ -315,8 +315,8 @@ Interface::Interface() { | |||
| 315 | 315 | ||
| 316 | if (file.IsOpen()) { | 316 | if (file.IsOpen()) { |
| 317 | // Read shared font data | 317 | // Read shared font data |
| 318 | shared_font.resize(file.GetSize()); | 318 | shared_font.resize((size_t)file.GetSize()); |
| 319 | file.ReadBytes(shared_font.data(), file.GetSize()); | 319 | file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); |
| 320 | 320 | ||
| 321 | // Create shared font memory object | 321 | // Create shared font memory object |
| 322 | shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem"); | 322 | shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem"); |
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h index 306730400..3807cbecc 100644 --- a/src/core/hle/service/apt_u.h +++ b/src/core/hle/service/apt_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp index b2ff4a756..24cd413da 100644 --- a/src/core/hle/service/boss_u.cpp +++ b/src/core/hle/service/boss_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h index af39b8e65..31e4d0c3a 100644 --- a/src/core/hle/service/boss_u.h +++ b/src/core/hle/service/boss_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp new file mode 100644 index 000000000..b7655ef0b --- /dev/null +++ b/src/core/hle/service/cecd_u.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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..0c9968bfe --- /dev/null +++ b/src/core/hle/service/cecd_u.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "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/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp new file mode 100644 index 000000000..161aa8531 --- /dev/null +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include "common/log.h" | ||
| 7 | #include "common/make_unique.h" | ||
| 8 | #include "core/file_sys/archive_systemsavedata.h" | ||
| 9 | #include "core/hle/service/cfg/cfg.h" | ||
| 10 | |||
| 11 | namespace Service { | ||
| 12 | namespace CFG { | ||
| 13 | |||
| 14 | const u64 CFG_SAVE_ID = 0x00010017; | ||
| 15 | const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; | ||
| 16 | const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } }; | ||
| 17 | const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; | ||
| 18 | const char CONSOLE_USERNAME[0x14] = "CITRA"; | ||
| 19 | /// This will be initialized in CFGInit, and will be used when creating the block | ||
| 20 | UsernameBlock CONSOLE_USERNAME_BLOCK; | ||
| 21 | /// TODO(Subv): Find out what this actually is | ||
| 22 | const u8 SOUND_OUTPUT_MODE = 2; | ||
| 23 | const u8 UNITED_STATES_COUNTRY_ID = 49; | ||
| 24 | /// TODO(Subv): Find what the other bytes are | ||
| 25 | const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games, | ||
| 29 | * for example Nintendo Zone | ||
| 30 | * Thanks Normmatt for providing this information | ||
| 31 | */ | ||
| 32 | const std::array<float, 8> STEREO_CAMERA_SETTINGS = { | ||
| 33 | 62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f, | ||
| 34 | 10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f | ||
| 35 | }; | ||
| 36 | |||
| 37 | static const u32 CONFIG_SAVEFILE_SIZE = 0x8000; | ||
| 38 | static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer; | ||
| 39 | |||
| 40 | static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data; | ||
| 41 | |||
| 42 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | ||
| 43 | // Read the header | ||
| 44 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 45 | |||
| 46 | auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), | ||
| 47 | [&](const SaveConfigBlockEntry& entry) { | ||
| 48 | return entry.block_id == block_id && entry.size == size && (entry.flags & flag); | ||
| 49 | }); | ||
| 50 | |||
| 51 | if (itr == std::end(config->block_entries)) { | ||
| 52 | LOG_ERROR(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag); | ||
| 53 | return ResultCode(-1); // TODO(Subv): Find the correct error code | ||
| 54 | } | ||
| 55 | |||
| 56 | // The data is located in the block header itself if the size is less than 4 bytes | ||
| 57 | if (itr->size <= 4) | ||
| 58 | memcpy(output, &itr->offset_or_data, itr->size); | ||
| 59 | else | ||
| 60 | memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size); | ||
| 61 | |||
| 62 | return RESULT_SUCCESS; | ||
| 63 | } | ||
| 64 | |||
| 65 | ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data) { | ||
| 66 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 67 | if (config->total_entries >= CONFIG_FILE_MAX_BLOCK_ENTRIES) | ||
| 68 | return ResultCode(-1); // TODO(Subv): Find the right error code | ||
| 69 | |||
| 70 | // Insert the block header with offset 0 for now | ||
| 71 | config->block_entries[config->total_entries] = { block_id, 0, size, flags }; | ||
| 72 | if (size > 4) { | ||
| 73 | u32 offset = config->data_entries_offset; | ||
| 74 | // Perform a search to locate the next offset for the new data | ||
| 75 | // use the offset and size of the previous block to determine the new position | ||
| 76 | for (int i = config->total_entries - 1; i >= 0; --i) { | ||
| 77 | // Ignore the blocks that don't have a separate data offset | ||
| 78 | if (config->block_entries[i].size > 4) { | ||
| 79 | offset = config->block_entries[i].offset_or_data + | ||
| 80 | config->block_entries[i].size; | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | config->block_entries[config->total_entries].offset_or_data = offset; | ||
| 86 | |||
| 87 | // Write the data at the new offset | ||
| 88 | memcpy(&cfg_config_file_buffer[offset], data, size); | ||
| 89 | } | ||
| 90 | else { | ||
| 91 | // The offset_or_data field in the header contains the data itself if it's 4 bytes or less | ||
| 92 | memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size); | ||
| 93 | } | ||
| 94 | |||
| 95 | ++config->total_entries; | ||
| 96 | return RESULT_SUCCESS; | ||
| 97 | } | ||
| 98 | |||
| 99 | ResultCode DeleteConfigNANDSaveFile() { | ||
| 100 | FileSys::Path path("config"); | ||
| 101 | if (cfg_system_save_data->DeleteFile(path)) | ||
| 102 | return RESULT_SUCCESS; | ||
| 103 | return ResultCode(-1); // TODO(Subv): Find the right error code | ||
| 104 | } | ||
| 105 | |||
| 106 | ResultCode UpdateConfigNANDSavegame() { | ||
| 107 | FileSys::Mode mode = {}; | ||
| 108 | mode.write_flag = 1; | ||
| 109 | mode.create_flag = 1; | ||
| 110 | FileSys::Path path("config"); | ||
| 111 | auto file = cfg_system_save_data->OpenFile(path, mode); | ||
| 112 | _assert_msg_(Service_CFG, file != nullptr, "could not open file"); | ||
| 113 | file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); | ||
| 114 | return RESULT_SUCCESS; | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultCode FormatConfig() { | ||
| 118 | ResultCode res = DeleteConfigNANDSaveFile(); | ||
| 119 | if (!res.IsSuccess()) | ||
| 120 | return res; | ||
| 121 | // Delete the old data | ||
| 122 | cfg_config_file_buffer.fill(0); | ||
| 123 | // Create the header | ||
| 124 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 125 | // This value is hardcoded, taken from 3dbrew, verified by hardware, it's always the same value | ||
| 126 | config->data_entries_offset = 0x455C; | ||
| 127 | // Insert the default blocks | ||
| 128 | res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, | ||
| 129 | reinterpret_cast<const u8*>(STEREO_CAMERA_SETTINGS.data())); | ||
| 130 | if (!res.IsSuccess()) | ||
| 131 | return res; | ||
| 132 | res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, | ||
| 133 | reinterpret_cast<const u8*>(&CONSOLE_UNIQUE_ID)); | ||
| 134 | if (!res.IsSuccess()) | ||
| 135 | return res; | ||
| 136 | res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0x8, | ||
| 137 | reinterpret_cast<const u8*>(&CONSOLE_MODEL)); | ||
| 138 | if (!res.IsSuccess()) | ||
| 139 | return res; | ||
| 140 | res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xA, &CONSOLE_LANGUAGE); | ||
| 141 | if (!res.IsSuccess()) | ||
| 142 | return res; | ||
| 143 | res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); | ||
| 144 | if (!res.IsSuccess()) | ||
| 145 | return res; | ||
| 146 | res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, | ||
| 147 | reinterpret_cast<const u8*>(&COUNTRY_INFO)); | ||
| 148 | if (!res.IsSuccess()) | ||
| 149 | return res; | ||
| 150 | res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, | ||
| 151 | reinterpret_cast<const u8*>(&CONSOLE_USERNAME_BLOCK)); | ||
| 152 | if (!res.IsSuccess()) | ||
| 153 | return res; | ||
| 154 | // Save the buffer to the file | ||
| 155 | res = UpdateConfigNANDSavegame(); | ||
| 156 | if (!res.IsSuccess()) | ||
| 157 | return res; | ||
| 158 | return RESULT_SUCCESS; | ||
| 159 | } | ||
| 160 | |||
| 161 | void CFGInit() { | ||
| 162 | // TODO(Subv): In the future we should use the FS service to query this archive, | ||
| 163 | // currently it is not possible because you can only have one open archive of the same type at any time | ||
| 164 | std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX); | ||
| 165 | cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>( | ||
| 166 | syssavedata_directory, CFG_SAVE_ID); | ||
| 167 | if (!cfg_system_save_data->Initialize()) { | ||
| 168 | LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service"); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | |||
| 172 | // TODO(Subv): All this code should be moved to cfg:i, | ||
| 173 | // it's only here because we do not currently emulate the lower level code that uses that service | ||
| 174 | // Try to open the file in read-only mode to check its existence | ||
| 175 | FileSys::Mode mode = {}; | ||
| 176 | mode.read_flag = 1; | ||
| 177 | FileSys::Path path("config"); | ||
| 178 | auto file = cfg_system_save_data->OpenFile(path, mode); | ||
| 179 | |||
| 180 | // Load the config if it already exists | ||
| 181 | if (file != nullptr) { | ||
| 182 | file->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | |||
| 186 | // Initialize the Username block | ||
| 187 | // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals | ||
| 188 | CONSOLE_USERNAME_BLOCK.ng_word = 0; | ||
| 189 | CONSOLE_USERNAME_BLOCK.zero = 0; | ||
| 190 | // Copy string to buffer and pad with zeros at the end | ||
| 191 | auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14); | ||
| 192 | std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size, | ||
| 193 | std::end(CONSOLE_USERNAME_BLOCK.username), 0); | ||
| 194 | FormatConfig(); | ||
| 195 | } | ||
| 196 | |||
| 197 | void CFGShutdown() { | ||
| 198 | |||
| 199 | } | ||
| 200 | |||
| 201 | } // namespace CFG | ||
| 202 | } // namespace Service | ||
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h new file mode 100644 index 000000000..c74527ca4 --- /dev/null +++ b/src/core/hle/service/cfg/cfg.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include "core/hle/result.h" | ||
| 9 | |||
| 10 | namespace Service { | ||
| 11 | namespace CFG { | ||
| 12 | |||
| 13 | enum SystemModel { | ||
| 14 | NINTENDO_3DS = 0, | ||
| 15 | NINTENDO_3DS_XL = 1, | ||
| 16 | NEW_NINTENDO_3DS = 2, | ||
| 17 | NINTENDO_2DS = 3, | ||
| 18 | NEW_NINTENDO_3DS_XL = 4 | ||
| 19 | }; | ||
| 20 | |||
| 21 | enum SystemLanguage { | ||
| 22 | LANGUAGE_JP = 0, | ||
| 23 | LANGUAGE_EN = 1, | ||
| 24 | LANGUAGE_FR = 2, | ||
| 25 | LANGUAGE_DE = 3, | ||
| 26 | LANGUAGE_IT = 4, | ||
| 27 | LANGUAGE_ES = 5, | ||
| 28 | LANGUAGE_ZH = 6, | ||
| 29 | LANGUAGE_KO = 7, | ||
| 30 | LANGUAGE_NL = 8, | ||
| 31 | LANGUAGE_PT = 9, | ||
| 32 | LANGUAGE_RU = 10 | ||
| 33 | }; | ||
| 34 | |||
| 35 | /// Block header in the config savedata file | ||
| 36 | struct SaveConfigBlockEntry { | ||
| 37 | u32 block_id; ///< The id of the current block | ||
| 38 | u32 offset_or_data; ///< This is the absolute offset to the block data if the size is greater than 4 bytes, otherwise it contains the data itself | ||
| 39 | u16 size; ///< The size of the block | ||
| 40 | u16 flags; ///< The flags of the block, possibly used for access control | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// The maximum number of block entries that can exist in the config file | ||
| 44 | static const u32 CONFIG_FILE_MAX_BLOCK_ENTRIES = 1479; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * The header of the config savedata file, | ||
| 48 | * contains information about the blocks in the file | ||
| 49 | */ | ||
| 50 | struct SaveFileConfig { | ||
| 51 | u16 total_entries; ///< The total number of set entries in the config file | ||
| 52 | u16 data_entries_offset; ///< The offset where the data for the blocks start, this is hardcoded to 0x455C as per hardware | ||
| 53 | SaveConfigBlockEntry block_entries[CONFIG_FILE_MAX_BLOCK_ENTRIES]; ///< The block headers, the maximum possible value is 1479 as per hardware | ||
| 54 | u32 unknown; ///< This field is unknown, possibly padding, 0 has been observed in hardware | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(SaveFileConfig) == 0x455C, "The SaveFileConfig header must be exactly 0x455C bytes"); | ||
| 57 | |||
| 58 | struct UsernameBlock { | ||
| 59 | char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary | ||
| 60 | u32 zero; | ||
| 61 | u32 ng_word; | ||
| 62 | }; | ||
| 63 | static_assert(sizeof(UsernameBlock) == 0x1C, "Size of UsernameBlock must be 0x1C"); | ||
| 64 | |||
| 65 | struct ConsoleModelInfo { | ||
| 66 | u8 model; ///< The console model (3DS, 2DS, etc) | ||
| 67 | u8 unknown[3]; ///< Unknown data | ||
| 68 | }; | ||
| 69 | static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes"); | ||
| 70 | |||
| 71 | struct ConsoleCountryInfo { | ||
| 72 | u8 unknown[3]; ///< Unknown data | ||
| 73 | u8 country_code; ///< The country code of the console | ||
| 74 | }; | ||
| 75 | static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes"); | ||
| 76 | |||
| 77 | extern const u64 CFG_SAVE_ID; | ||
| 78 | extern const u64 CONSOLE_UNIQUE_ID; | ||
| 79 | extern const ConsoleModelInfo CONSOLE_MODEL; | ||
| 80 | extern const u8 CONSOLE_LANGUAGE; | ||
| 81 | extern const char CONSOLE_USERNAME[0x14]; | ||
| 82 | /// This will be initialized in the Interface constructor, and will be used when creating the block | ||
| 83 | extern UsernameBlock CONSOLE_USERNAME_BLOCK; | ||
| 84 | /// TODO(Subv): Find out what this actually is | ||
| 85 | extern const u8 SOUND_OUTPUT_MODE; | ||
| 86 | extern const u8 UNITED_STATES_COUNTRY_ID; | ||
| 87 | /// TODO(Subv): Find what the other bytes are | ||
| 88 | extern const ConsoleCountryInfo COUNTRY_INFO; | ||
| 89 | extern const std::array<float, 8> STEREO_CAMERA_SETTINGS; | ||
| 90 | |||
| 91 | static_assert(sizeof(STEREO_CAMERA_SETTINGS) == 0x20, "STEREO_CAMERA_SETTINGS must be exactly 0x20 bytes"); | ||
| 92 | static_assert(sizeof(CONSOLE_UNIQUE_ID) == 8, "CONSOLE_UNIQUE_ID must be exactly 8 bytes"); | ||
| 93 | static_assert(sizeof(CONSOLE_LANGUAGE) == 1, "CONSOLE_LANGUAGE must be exactly 1 byte"); | ||
| 94 | static_assert(sizeof(SOUND_OUTPUT_MODE) == 1, "SOUND_OUTPUT_MODE must be exactly 1 byte"); | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Reads a block with the specified id and flag from the Config savegame buffer | ||
| 98 | * and writes the output to output. | ||
| 99 | * The input size must match exactly the size of the requested block | ||
| 100 | * @param block_id The id of the block we want to read | ||
| 101 | * @param size The size of the block we want to read | ||
| 102 | * @param flag The requested block must have this flag set | ||
| 103 | * @param output A pointer where we will write the read data | ||
| 104 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 105 | */ | ||
| 106 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. | ||
| 110 | * The config savegame file in the filesystem is not updated. | ||
| 111 | * @param block_id The id of the block we want to create | ||
| 112 | * @param size The size of the block we want to create | ||
| 113 | * @param flag The flags of the new block | ||
| 114 | * @param data A pointer containing the data we will write to the new block | ||
| 115 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 116 | */ | ||
| 117 | ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data); | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Deletes the config savegame file from the filesystem, the buffer in memory is not affected | ||
| 121 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 122 | */ | ||
| 123 | ResultCode DeleteConfigNANDSaveFile(); | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Writes the config savegame memory buffer to the config savegame file in the filesystem | ||
| 127 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 128 | */ | ||
| 129 | ResultCode UpdateConfigNANDSavegame(); | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Re-creates the config savegame file in memory and the filesystem with the default blocks | ||
| 133 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 134 | */ | ||
| 135 | ResultCode FormatConfig(); | ||
| 136 | |||
| 137 | /// Initialize the config service | ||
| 138 | void CFGInit(); | ||
| 139 | |||
| 140 | /// Shutdown the config service | ||
| 141 | void CFGShutdown(); | ||
| 142 | |||
| 143 | } // namespace CFG | ||
| 144 | } // namespace Service | ||
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 88d13d459..3254cc10e 100644 --- a/src/core/hle/service/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp | |||
| @@ -1,32 +1,86 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| 6 | #include "core/hle/hle.h" | 6 | #include "core/hle/hle.h" |
| 7 | #include "core/hle/service/cfg_i.h" | 7 | #include "core/hle/service/cfg/cfg.h" |
| 8 | #include "core/hle/service/cfg/cfg_i.h" | ||
| 8 | 9 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // Namespace CFG_I | 11 | // Namespace CFG_I |
| 11 | 12 | ||
| 12 | namespace CFG_I { | 13 | namespace CFG_I { |
| 14 | |||
| 15 | /** | ||
| 16 | * CFG_I::GetConfigInfoBlk8 service function | ||
| 17 | * This function is called by two command headers, | ||
| 18 | * there appears to be no difference between them according to 3dbrew | ||
| 19 | * Inputs: | ||
| 20 | * 0 : 0x04010082 / 0x08010082 | ||
| 21 | * 1 : Size | ||
| 22 | * 2 : Block ID | ||
| 23 | * 3 : Descriptor for the output buffer | ||
| 24 | * 4 : Output buffer pointer | ||
| 25 | * Outputs: | ||
| 26 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 27 | */ | ||
| 28 | static void GetConfigInfoBlk8(Service::Interface* self) { | ||
| 29 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 30 | u32 size = cmd_buffer[1]; | ||
| 31 | u32 block_id = cmd_buffer[2]; | ||
| 32 | u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); | ||
| 33 | |||
| 34 | if (data_pointer == nullptr) { | ||
| 35 | cmd_buffer[1] = -1; // TODO(Subv): Find the right error code | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | |||
| 39 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw; | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * CFG_I::UpdateConfigNANDSavegame service function | ||
| 44 | * This function is called by two command headers, | ||
| 45 | * there appears to be no difference between them according to 3dbrew | ||
| 46 | * Inputs: | ||
| 47 | * 0 : 0x04030000 / 0x08030000 | ||
| 48 | * Outputs: | ||
| 49 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 50 | */ | ||
| 51 | static void UpdateConfigNANDSavegame(Service::Interface* self) { | ||
| 52 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 53 | cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * CFG_I::FormatConfig service function | ||
| 58 | * Inputs: | ||
| 59 | * 0 : 0x08060000 | ||
| 60 | * Outputs: | ||
| 61 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 62 | */ | ||
| 63 | static void FormatConfig(Service::Interface* self) { | ||
| 64 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 65 | cmd_buffer[1] = Service::CFG::FormatConfig().raw; | ||
| 66 | } | ||
| 13 | 67 | ||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 68 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x04010082, nullptr, "GetConfigInfoBlk8"}, | 69 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, |
| 16 | {0x04020082, nullptr, "GetConfigInfoBlk4"}, | 70 | {0x04020082, nullptr, "SetConfigInfoBlk4"}, |
| 17 | {0x04030000, nullptr, "UpdateConfigNANDSavegame"}, | 71 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, |
| 18 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, | 72 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, |
| 19 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, | 73 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, |
| 20 | {0x04060000, nullptr, "SecureInfoGetRegion"}, | 74 | {0x04060000, nullptr, "SecureInfoGetRegion"}, |
| 21 | {0x04070000, nullptr, "SecureInfoGetByte101"}, | 75 | {0x04070000, nullptr, "SecureInfoGetByte101"}, |
| 22 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, | 76 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, |
| 23 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, | 77 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, |
| 24 | {0x08010082, nullptr, "GetConfigInfoBlk8"}, | 78 | {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, |
| 25 | {0x08020082, nullptr, "GetConfigInfoBlk4"}, | 79 | {0x08020082, nullptr, "SetConfigInfoBlk4"}, |
| 26 | {0x08030000, nullptr, "UpdateConfigNANDSavegame"}, | 80 | {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, |
| 27 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, | 81 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, |
| 28 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, | 82 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, |
| 29 | {0x08060000, nullptr, "FormatConfig"}, | 83 | {0x08060000, FormatConfig, "FormatConfig"}, |
| 30 | {0x08070000, nullptr, "Unknown"}, | 84 | {0x08070000, nullptr, "Unknown"}, |
| 31 | {0x08080000, nullptr, "UpdateConfigBlk1"}, | 85 | {0x08080000, nullptr, "UpdateConfigBlk1"}, |
| 32 | {0x08090000, nullptr, "UpdateConfigBlk2"}, | 86 | {0x08090000, nullptr, "UpdateConfigBlk2"}, |
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h index fe343c968..577aad236 100644 --- a/src/core/hle/service/cfg_i.h +++ b/src/core/hle/service/cfg/cfg_i.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 972cc0534..59934ea46 100644 --- a/src/core/hle/service/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp | |||
| @@ -1,10 +1,14 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/file_util.h" | ||
| 5 | #include "common/log.h" | 6 | #include "common/log.h" |
| 7 | #include "common/string_util.h" | ||
| 8 | #include "core/file_sys/archive_systemsavedata.h" | ||
| 6 | #include "core/hle/hle.h" | 9 | #include "core/hle/hle.h" |
| 7 | #include "core/hle/service/cfg_u.h" | 10 | #include "core/hle/service/cfg/cfg.h" |
| 11 | #include "core/hle/service/cfg/cfg_u.h" | ||
| 8 | 12 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // Namespace CFG_U | 14 | // Namespace CFG_U |
| @@ -52,7 +56,7 @@ static const std::array<u16, 187> country_codes = { | |||
| 52 | * 2 : Country's 2-char string | 56 | * 2 : Country's 2-char string |
| 53 | */ | 57 | */ |
| 54 | static void GetCountryCodeString(Service::Interface* self) { | 58 | static void GetCountryCodeString(Service::Interface* self) { |
| 55 | u32* cmd_buffer = Service::GetCommandBuffer(); | 59 | u32* cmd_buffer = Kernel::GetCommandBuffer(); |
| 56 | u32 country_code_id = cmd_buffer[1]; | 60 | u32 country_code_id = cmd_buffer[1]; |
| 57 | 61 | ||
| 58 | if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) { | 62 | if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) { |
| @@ -74,7 +78,7 @@ static void GetCountryCodeString(Service::Interface* self) { | |||
| 74 | * 2 : Country Code ID | 78 | * 2 : Country Code ID |
| 75 | */ | 79 | */ |
| 76 | static void GetCountryCodeID(Service::Interface* self) { | 80 | static void GetCountryCodeID(Service::Interface* self) { |
| 77 | u32* cmd_buffer = Service::GetCommandBuffer(); | 81 | u32* cmd_buffer = Kernel::GetCommandBuffer(); |
| 78 | u16 country_code = cmd_buffer[1]; | 82 | u16 country_code = cmd_buffer[1]; |
| 79 | u16 country_code_id = 0; | 83 | u16 country_code_id = 0; |
| 80 | 84 | ||
| @@ -99,13 +103,79 @@ static void GetCountryCodeID(Service::Interface* self) { | |||
| 99 | cmd_buffer[2] = country_code_id; | 103 | cmd_buffer[2] = country_code_id; |
| 100 | } | 104 | } |
| 101 | 105 | ||
| 106 | /** | ||
| 107 | * CFG_User::GetConfigInfoBlk2 service function | ||
| 108 | * Inputs: | ||
| 109 | * 0 : 0x00010082 | ||
| 110 | * 1 : Size | ||
| 111 | * 2 : Block ID | ||
| 112 | * 3 : Descriptor for the output buffer | ||
| 113 | * 4 : Output buffer pointer | ||
| 114 | * Outputs: | ||
| 115 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 116 | */ | ||
| 117 | static void GetConfigInfoBlk2(Service::Interface* self) { | ||
| 118 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 119 | u32 size = cmd_buffer[1]; | ||
| 120 | u32 block_id = cmd_buffer[2]; | ||
| 121 | u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); | ||
| 122 | |||
| 123 | if (data_pointer == nullptr) { | ||
| 124 | cmd_buffer[1] = -1; // TODO(Subv): Find the right error code | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw; | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * CFG_User::GetSystemModel service function | ||
| 133 | * Inputs: | ||
| 134 | * 0 : 0x00050000 | ||
| 135 | * Outputs: | ||
| 136 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 137 | * 2 : Model of the console | ||
| 138 | */ | ||
| 139 | static void GetSystemModel(Service::Interface* self) { | ||
| 140 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 141 | u32 data; | ||
| 142 | |||
| 143 | // TODO(Subv): Find out the correct error codes | ||
| 144 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8, | ||
| 145 | reinterpret_cast<u8*>(&data)).raw; | ||
| 146 | cmd_buffer[2] = data & 0xFF; | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * CFG_User::GetModelNintendo2DS service function | ||
| 151 | * Inputs: | ||
| 152 | * 0 : 0x00060000 | ||
| 153 | * Outputs: | ||
| 154 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 155 | * 2 : 0 if the system is a Nintendo 2DS, 1 otherwise | ||
| 156 | */ | ||
| 157 | static void GetModelNintendo2DS(Service::Interface* self) { | ||
| 158 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 159 | u32 data; | ||
| 160 | |||
| 161 | // TODO(Subv): Find out the correct error codes | ||
| 162 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8, | ||
| 163 | reinterpret_cast<u8*>(&data)).raw; | ||
| 164 | |||
| 165 | u8 model = data & 0xFF; | ||
| 166 | if (model == Service::CFG::NINTENDO_2DS) | ||
| 167 | cmd_buffer[2] = 0; | ||
| 168 | else | ||
| 169 | cmd_buffer[2] = 1; | ||
| 170 | } | ||
| 171 | |||
| 102 | const Interface::FunctionInfo FunctionTable[] = { | 172 | const Interface::FunctionInfo FunctionTable[] = { |
| 103 | {0x00010082, nullptr, "GetConfigInfoBlk2"}, | 173 | {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, |
| 104 | {0x00020000, nullptr, "SecureInfoGetRegion"}, | 174 | {0x00020000, nullptr, "SecureInfoGetRegion"}, |
| 105 | {0x00030000, nullptr, "GenHashConsoleUnique"}, | 175 | {0x00030000, nullptr, "GenHashConsoleUnique"}, |
| 106 | {0x00040000, nullptr, "GetRegionCanadaUSA"}, | 176 | {0x00040000, nullptr, "GetRegionCanadaUSA"}, |
| 107 | {0x00050000, nullptr, "GetSystemModel"}, | 177 | {0x00050000, GetSystemModel, "GetSystemModel"}, |
| 108 | {0x00060000, nullptr, "GetModelNintendo2DS"}, | 178 | {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, |
| 109 | {0x00070040, nullptr, "unknown"}, | 179 | {0x00070040, nullptr, "unknown"}, |
| 110 | {0x00080080, nullptr, "unknown"}, | 180 | {0x00080080, nullptr, "unknown"}, |
| 111 | {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, | 181 | {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, |
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h index 8075d19a8..0136bff53 100644 --- a/src/core/hle/service/cfg_u.h +++ b/src/core/hle/service/cfg/cfg_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 6e59a9bf3..3f62c7e9c 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h index 31cc85b07..85aab1dd3 100644 --- a/src/core/hle/service/csnd_snd.h +++ b/src/core/hle/service/csnd_snd.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index ce1c9938d..4c1c5b70b 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| @@ -25,7 +25,7 @@ static Handle interrupt_event; | |||
| 25 | * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address) | 25 | * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address) |
| 26 | */ | 26 | */ |
| 27 | void ConvertProcessAddressFromDspDram(Service::Interface* self) { | 27 | void ConvertProcessAddressFromDspDram(Service::Interface* self) { |
| 28 | u32* cmd_buff = Service::GetCommandBuffer(); | 28 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 29 | 29 | ||
| 30 | u32 addr = cmd_buff[1]; | 30 | u32 addr = cmd_buff[1]; |
| 31 | 31 | ||
| @@ -48,7 +48,7 @@ void ConvertProcessAddressFromDspDram(Service::Interface* self) { | |||
| 48 | * 2 : Component loaded, 0 on not loaded, 1 on loaded | 48 | * 2 : Component loaded, 0 on not loaded, 1 on loaded |
| 49 | */ | 49 | */ |
| 50 | void LoadComponent(Service::Interface* self) { | 50 | void LoadComponent(Service::Interface* self) { |
| 51 | u32* cmd_buff = Service::GetCommandBuffer(); | 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 52 | 52 | ||
| 53 | cmd_buff[1] = 0; // No error | 53 | cmd_buff[1] = 0; // No error |
| 54 | cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware | 54 | cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware |
| @@ -65,7 +65,7 @@ void LoadComponent(Service::Interface* self) { | |||
| 65 | * 3 : Semaphore event handle | 65 | * 3 : Semaphore event handle |
| 66 | */ | 66 | */ |
| 67 | void GetSemaphoreEventHandle(Service::Interface* self) { | 67 | void GetSemaphoreEventHandle(Service::Interface* self) { |
| 68 | u32* cmd_buff = Service::GetCommandBuffer(); | 68 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 69 | 69 | ||
| 70 | cmd_buff[1] = 0; // No error | 70 | cmd_buff[1] = 0; // No error |
| 71 | cmd_buff[3] = semaphore_event; // Event handle | 71 | cmd_buff[3] = semaphore_event; // Event handle |
| @@ -83,7 +83,7 @@ void GetSemaphoreEventHandle(Service::Interface* self) { | |||
| 83 | * 1 : Result of function, 0 on success, otherwise error code | 83 | * 1 : Result of function, 0 on success, otherwise error code |
| 84 | */ | 84 | */ |
| 85 | void RegisterInterruptEvents(Service::Interface* self) { | 85 | void RegisterInterruptEvents(Service::Interface* self) { |
| 86 | u32* cmd_buff = Service::GetCommandBuffer(); | 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 87 | 87 | ||
| 88 | interrupt_event = static_cast<Handle>(cmd_buff[4]); | 88 | interrupt_event = static_cast<Handle>(cmd_buff[4]); |
| 89 | 89 | ||
| @@ -100,7 +100,7 @@ void RegisterInterruptEvents(Service::Interface* self) { | |||
| 100 | * 1 : Result of function, 0 on success, otherwise error code | 100 | * 1 : Result of function, 0 on success, otherwise error code |
| 101 | */ | 101 | */ |
| 102 | void WriteReg0x10(Service::Interface* self) { | 102 | void WriteReg0x10(Service::Interface* self) { |
| 103 | u32* cmd_buff = Service::GetCommandBuffer(); | 103 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 104 | 104 | ||
| 105 | Kernel::SignalEvent(interrupt_event); | 105 | Kernel::SignalEvent(interrupt_event); |
| 106 | 106 | ||
| @@ -121,7 +121,7 @@ void WriteReg0x10(Service::Interface* self) { | |||
| 121 | * 2 : Number of bytes read from pipe | 121 | * 2 : Number of bytes read from pipe |
| 122 | */ | 122 | */ |
| 123 | void ReadPipeIfPossible(Service::Interface* self) { | 123 | void ReadPipeIfPossible(Service::Interface* self) { |
| 124 | u32* cmd_buff = Service::GetCommandBuffer(); | 124 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 125 | 125 | ||
| 126 | u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size | 126 | u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size |
| 127 | VAddr addr = cmd_buff[0x41]; | 127 | VAddr addr = cmd_buff[0x41]; |
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 9431b62f6..7bf27fe0f 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 785c351e9..5c7cce841 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h index 6d7141c1b..2c61c3651 100644 --- a/src/core/hle/service/err_f.h +++ b/src/core/hle/service/err_f.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp index 58023e536..c2ecef5bb 100644 --- a/src/core/hle/service/frd_u.cpp +++ b/src/core/hle/service/frd_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h index 4020c6664..e030f8b3b 100644 --- a/src/core/hle/service/frd_u.h +++ b/src/core/hle/service/frd_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp new file mode 100644 index 000000000..98db02f15 --- /dev/null +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -0,0 +1,440 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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/make_unique.h" | ||
| 11 | #include "common/math_util.h" | ||
| 12 | |||
| 13 | #include "core/file_sys/archive_savedata.h" | ||
| 14 | #include "core/file_sys/archive_backend.h" | ||
| 15 | #include "core/file_sys/archive_sdmc.h" | ||
| 16 | #include "core/file_sys/directory_backend.h" | ||
| 17 | #include "core/hle/service/fs/archive.h" | ||
| 18 | #include "core/hle/kernel/session.h" | ||
| 19 | #include "core/hle/result.h" | ||
| 20 | |||
| 21 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. | ||
| 22 | // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 | ||
| 23 | namespace std { | ||
| 24 | template <> | ||
| 25 | struct hash<Service::FS::ArchiveIdCode> { | ||
| 26 | typedef Service::FS::ArchiveIdCode argument_type; | ||
| 27 | typedef std::size_t result_type; | ||
| 28 | |||
| 29 | result_type operator()(const argument_type& id_code) const { | ||
| 30 | typedef std::underlying_type<argument_type>::type Type; | ||
| 31 | return std::hash<Type>()(static_cast<Type>(id_code)); | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | namespace Service { | ||
| 37 | namespace FS { | ||
| 38 | |||
| 39 | // Command to access archive file | ||
| 40 | enum class FileCommand : u32 { | ||
| 41 | Dummy1 = 0x000100C6, | ||
| 42 | Control = 0x040100C4, | ||
| 43 | OpenSubFile = 0x08010100, | ||
| 44 | Read = 0x080200C2, | ||
| 45 | Write = 0x08030102, | ||
| 46 | GetSize = 0x08040000, | ||
| 47 | SetSize = 0x08050080, | ||
| 48 | GetAttributes = 0x08060000, | ||
| 49 | SetAttributes = 0x08070040, | ||
| 50 | Close = 0x08080000, | ||
| 51 | Flush = 0x08090000, | ||
| 52 | }; | ||
| 53 | |||
| 54 | // Command to access directory | ||
| 55 | enum class DirectoryCommand : u32 { | ||
| 56 | Dummy1 = 0x000100C6, | ||
| 57 | Control = 0x040100C4, | ||
| 58 | Read = 0x08010042, | ||
| 59 | Close = 0x08020000, | ||
| 60 | }; | ||
| 61 | |||
| 62 | class Archive { | ||
| 63 | public: | ||
| 64 | Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) | ||
| 65 | : backend(std::move(backend)), id_code(id_code) { | ||
| 66 | } | ||
| 67 | |||
| 68 | std::string GetName() const { return "Archive: " + backend->GetName(); } | ||
| 69 | |||
| 70 | ArchiveIdCode id_code; ///< Id code of the archive | ||
| 71 | std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface | ||
| 72 | }; | ||
| 73 | |||
| 74 | class File : public Kernel::Session { | ||
| 75 | public: | ||
| 76 | File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) | ||
| 77 | : backend(std::move(backend)), path(path) { | ||
| 78 | } | ||
| 79 | |||
| 80 | std::string GetName() const override { return "Path: " + path.DebugStr(); } | ||
| 81 | |||
| 82 | FileSys::Path path; ///< Path of the file | ||
| 83 | std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface | ||
| 84 | |||
| 85 | ResultVal<bool> SyncRequest() override { | ||
| 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 87 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 88 | switch (cmd) { | ||
| 89 | |||
| 90 | // Read from file... | ||
| 91 | case FileCommand::Read: | ||
| 92 | { | ||
| 93 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 94 | u32 length = cmd_buff[3]; | ||
| 95 | u32 address = cmd_buff[5]; | ||
| 96 | LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 97 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 98 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | |||
| 102 | // Write to file... | ||
| 103 | case FileCommand::Write: | ||
| 104 | { | ||
| 105 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 106 | u32 length = cmd_buff[3]; | ||
| 107 | u32 flush = cmd_buff[4]; | ||
| 108 | u32 address = cmd_buff[6]; | ||
| 109 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 110 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 111 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | |||
| 115 | case FileCommand::GetSize: | ||
| 116 | { | ||
| 117 | LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 118 | u64 size = backend->GetSize(); | ||
| 119 | cmd_buff[2] = (u32)size; | ||
| 120 | cmd_buff[3] = size >> 32; | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | |||
| 124 | case FileCommand::SetSize: | ||
| 125 | { | ||
| 126 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 127 | LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", | ||
| 128 | GetTypeName().c_str(), GetName().c_str(), size); | ||
| 129 | backend->SetSize(size); | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | |||
| 133 | case FileCommand::Close: | ||
| 134 | { | ||
| 135 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 136 | Kernel::g_object_pool.Destroy<File>(GetHandle()); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | |||
| 140 | case FileCommand::Flush: | ||
| 141 | { | ||
| 142 | LOG_TRACE(Service_FS, "Flush"); | ||
| 143 | backend->Flush(); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | |||
| 147 | // Unknown command... | ||
| 148 | default: | ||
| 149 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 150 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 151 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 152 | return error; | ||
| 153 | } | ||
| 154 | cmd_buff[1] = 0; // No error | ||
| 155 | return MakeResult<bool>(false); | ||
| 156 | } | ||
| 157 | }; | ||
| 158 | |||
| 159 | class Directory : public Kernel::Session { | ||
| 160 | public: | ||
| 161 | Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) | ||
| 162 | : backend(std::move(backend)), path(path) { | ||
| 163 | } | ||
| 164 | |||
| 165 | std::string GetName() const override { return "Directory: " + path.DebugStr(); } | ||
| 166 | |||
| 167 | FileSys::Path path; ///< Path of the directory | ||
| 168 | std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface | ||
| 169 | |||
| 170 | ResultVal<bool> SyncRequest() override { | ||
| 171 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 172 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 173 | switch (cmd) { | ||
| 174 | |||
| 175 | // Read from directory... | ||
| 176 | case DirectoryCommand::Read: | ||
| 177 | { | ||
| 178 | u32 count = cmd_buff[1]; | ||
| 179 | u32 address = cmd_buff[3]; | ||
| 180 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 181 | LOG_TRACE(Service_FS, "Read %s %s: count=%d", | ||
| 182 | GetTypeName().c_str(), GetName().c_str(), count); | ||
| 183 | |||
| 184 | // Number of entries actually read | ||
| 185 | cmd_buff[2] = backend->Read(count, entries); | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | |||
| 189 | case DirectoryCommand::Close: | ||
| 190 | { | ||
| 191 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 192 | Kernel::g_object_pool.Destroy<Directory>(GetHandle()); | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | |||
| 196 | // Unknown command... | ||
| 197 | default: | ||
| 198 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 199 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 200 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 201 | return MakeResult<bool>(false); | ||
| 202 | } | ||
| 203 | cmd_buff[1] = 0; // No error | ||
| 204 | return MakeResult<bool>(false); | ||
| 205 | } | ||
| 206 | }; | ||
| 207 | |||
| 208 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Map of registered archives, identified by id code. Once an archive is registered here, it is | ||
| 212 | * never removed until the FS service is shut down. | ||
| 213 | */ | ||
| 214 | static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map; | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Map of active archive handles. Values are pointers to the archives in `idcode_map`. | ||
| 218 | */ | ||
| 219 | static std::unordered_map<ArchiveHandle, Archive*> handle_map; | ||
| 220 | static ArchiveHandle next_handle; | ||
| 221 | |||
| 222 | static Archive* GetArchive(ArchiveHandle handle) { | ||
| 223 | auto itr = handle_map.find(handle); | ||
| 224 | return (itr == handle_map.end()) ? nullptr : itr->second; | ||
| 225 | } | ||
| 226 | |||
| 227 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) { | ||
| 228 | LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code); | ||
| 229 | |||
| 230 | auto itr = id_code_map.find(id_code); | ||
| 231 | if (itr == id_code_map.end()) { | ||
| 232 | if (id_code == ArchiveIdCode::SaveData) { | ||
| 233 | // When a SaveData archive is created for the first time, it is not yet formatted | ||
| 234 | // and the save file/directory structure expected by the game has not yet been initialized. | ||
| 235 | // Returning the NotFormatted error code will signal the game to provision the SaveData archive | ||
| 236 | // with the files and folders that it expects. | ||
| 237 | // The FormatSaveData service call will create the SaveData archive when it is called. | ||
| 238 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||
| 239 | ErrorSummary::InvalidState, ErrorLevel::Status); | ||
| 240 | } | ||
| 241 | // TODO: Verify error against hardware | ||
| 242 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 243 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 244 | } | ||
| 245 | |||
| 246 | // This should never even happen in the first place with 64-bit handles, | ||
| 247 | while (handle_map.count(next_handle) != 0) { | ||
| 248 | ++next_handle; | ||
| 249 | } | ||
| 250 | handle_map.emplace(next_handle, itr->second.get()); | ||
| 251 | return MakeResult<ArchiveHandle>(next_handle++); | ||
| 252 | } | ||
| 253 | |||
| 254 | ResultCode CloseArchive(ArchiveHandle handle) { | ||
| 255 | if (handle_map.erase(handle) == 0) | ||
| 256 | return InvalidHandle(ErrorModule::FS); | ||
| 257 | else | ||
| 258 | return RESULT_SUCCESS; | ||
| 259 | } | ||
| 260 | |||
| 261 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in | ||
| 262 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 | ||
| 263 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { | ||
| 264 | auto result = id_code_map.emplace(id_code, Common::make_unique<Archive>(std::move(backend), id_code)); | ||
| 265 | |||
| 266 | bool inserted = result.second; | ||
| 267 | _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); | ||
| 268 | |||
| 269 | auto& archive = result.first->second; | ||
| 270 | LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code); | ||
| 271 | return RESULT_SUCCESS; | ||
| 272 | } | ||
| 273 | |||
| 274 | ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { | ||
| 275 | Archive* archive = GetArchive(archive_handle); | ||
| 276 | if (archive == nullptr) | ||
| 277 | return InvalidHandle(ErrorModule::FS); | ||
| 278 | |||
| 279 | std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); | ||
| 280 | if (backend == nullptr) { | ||
| 281 | return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||
| 282 | ErrorSummary::NotFound, ErrorLevel::Status); | ||
| 283 | } | ||
| 284 | |||
| 285 | auto file = Common::make_unique<File>(std::move(backend), path); | ||
| 286 | Handle handle = Kernel::g_object_pool.Create(file.release()); | ||
| 287 | return MakeResult<Handle>(handle); | ||
| 288 | } | ||
| 289 | |||
| 290 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 291 | Archive* archive = GetArchive(archive_handle); | ||
| 292 | if (archive == nullptr) | ||
| 293 | return InvalidHandle(ErrorModule::FS); | ||
| 294 | |||
| 295 | if (archive->backend->DeleteFile(path)) | ||
| 296 | return RESULT_SUCCESS; | ||
| 297 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 298 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 299 | } | ||
| 300 | |||
| 301 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 302 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 303 | Archive* src_archive = GetArchive(src_archive_handle); | ||
| 304 | Archive* dest_archive = GetArchive(dest_archive_handle); | ||
| 305 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 306 | return InvalidHandle(ErrorModule::FS); | ||
| 307 | |||
| 308 | if (src_archive == dest_archive) { | ||
| 309 | if (src_archive->backend->RenameFile(src_path, dest_path)) | ||
| 310 | return RESULT_SUCCESS; | ||
| 311 | } else { | ||
| 312 | // TODO: Implement renaming across archives | ||
| 313 | return UnimplementedFunction(ErrorModule::FS); | ||
| 314 | } | ||
| 315 | |||
| 316 | // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't | ||
| 317 | // exist or similar. Verify. | ||
| 318 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 319 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 320 | } | ||
| 321 | |||
| 322 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 323 | Archive* archive = GetArchive(archive_handle); | ||
| 324 | if (archive == nullptr) | ||
| 325 | return InvalidHandle(ErrorModule::FS); | ||
| 326 | |||
| 327 | if (archive->backend->DeleteDirectory(path)) | ||
| 328 | return RESULT_SUCCESS; | ||
| 329 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 330 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 331 | } | ||
| 332 | |||
| 333 | ResultCode CreateFileInArchive(Handle archive_handle, const FileSys::Path& path, u32 file_size) { | ||
| 334 | Archive* archive = GetArchive(archive_handle); | ||
| 335 | if (archive == nullptr) | ||
| 336 | return InvalidHandle(ErrorModule::FS); | ||
| 337 | |||
| 338 | return archive->backend->CreateFile(path, file_size); | ||
| 339 | } | ||
| 340 | |||
| 341 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 342 | Archive* archive = GetArchive(archive_handle); | ||
| 343 | if (archive == nullptr) | ||
| 344 | return InvalidHandle(ErrorModule::FS); | ||
| 345 | |||
| 346 | if (archive->backend->CreateDirectory(path)) | ||
| 347 | return RESULT_SUCCESS; | ||
| 348 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 349 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 350 | } | ||
| 351 | |||
| 352 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 353 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 354 | Archive* src_archive = GetArchive(src_archive_handle); | ||
| 355 | Archive* dest_archive = GetArchive(dest_archive_handle); | ||
| 356 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 357 | return InvalidHandle(ErrorModule::FS); | ||
| 358 | |||
| 359 | if (src_archive == dest_archive) { | ||
| 360 | if (src_archive->backend->RenameDirectory(src_path, dest_path)) | ||
| 361 | return RESULT_SUCCESS; | ||
| 362 | } else { | ||
| 363 | // TODO: Implement renaming across archives | ||
| 364 | return UnimplementedFunction(ErrorModule::FS); | ||
| 365 | } | ||
| 366 | |||
| 367 | // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't | ||
| 368 | // exist or similar. Verify. | ||
| 369 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 370 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Open a Directory from an Archive | ||
| 375 | * @param archive_handle Handle to an open Archive object | ||
| 376 | * @param path Path to the Directory inside of the Archive | ||
| 377 | * @return Opened Directory object | ||
| 378 | */ | ||
| 379 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 380 | Archive* archive = GetArchive(archive_handle); | ||
| 381 | if (archive == nullptr) | ||
| 382 | return InvalidHandle(ErrorModule::FS); | ||
| 383 | |||
| 384 | std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path); | ||
| 385 | if (backend == nullptr) { | ||
| 386 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 387 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 388 | } | ||
| 389 | |||
| 390 | auto directory = Common::make_unique<Directory>(std::move(backend), path); | ||
| 391 | Handle handle = Kernel::g_object_pool.Create(directory.release()); | ||
| 392 | return MakeResult<Handle>(handle); | ||
| 393 | } | ||
| 394 | |||
| 395 | ResultCode FormatSaveData() { | ||
| 396 | // TODO(Subv): Actually wipe the savedata folder after creating or opening it | ||
| 397 | |||
| 398 | // Do not create the archive again if it already exists | ||
| 399 | if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end()) | ||
| 400 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code | ||
| 401 | |||
| 402 | // Create the SaveData archive | ||
| 403 | std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX); | ||
| 404 | auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory, | ||
| 405 | Kernel::g_program_id); | ||
| 406 | |||
| 407 | if (savedata_archive->Initialize()) { | ||
| 408 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | ||
| 409 | return RESULT_SUCCESS; | ||
| 410 | } else { | ||
| 411 | LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s", | ||
| 412 | savedata_archive->GetMountPoint().c_str()); | ||
| 413 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | /// Initialize archives | ||
| 418 | void ArchiveInit() { | ||
| 419 | next_handle = 1; | ||
| 420 | |||
| 421 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 422 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 423 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 424 | |||
| 425 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 426 | auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | ||
| 427 | if (sdmc_archive->Initialize()) | ||
| 428 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | ||
| 429 | else | ||
| 430 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 431 | } | ||
| 432 | |||
| 433 | /// Shutdown archives | ||
| 434 | void ArchiveShutdown() { | ||
| 435 | handle_map.clear(); | ||
| 436 | id_code_map.clear(); | ||
| 437 | } | ||
| 438 | |||
| 439 | } // namespace FS | ||
| 440 | } // namespace Service | ||
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/service/fs/archive.h index b50833a2b..b39bc41b6 100644 --- a/src/core/hle/kernel/archive.h +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -1,39 +1,50 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 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/archive_backend.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | 12 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | namespace Service { |
| 14 | // Kernel namespace | 14 | namespace FS { |
| 15 | 15 | ||
| 16 | namespace Kernel { | 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; | ||
| 17 | 28 | ||
| 18 | /** | 29 | /** |
| 19 | * Opens an archive | 30 | * Opens an archive |
| 20 | * @param id_code IdCode of the archive to open | 31 | * @param id_code IdCode of the archive to open |
| 21 | * @return Handle to the opened archive | 32 | * @return Handle to the opened archive |
| 22 | */ | 33 | */ |
| 23 | ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); | 34 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code); |
| 24 | 35 | ||
| 25 | /** | 36 | /** |
| 26 | * Closes an archive | 37 | * Closes an archive |
| 27 | * @param id_code IdCode of the archive to open | 38 | * @param id_code IdCode of the archive to open |
| 28 | */ | 39 | */ |
| 29 | ResultCode CloseArchive(FileSys::Archive::IdCode id_code); | 40 | ResultCode CloseArchive(ArchiveHandle handle); |
| 30 | 41 | ||
| 31 | /** | 42 | /** |
| 32 | * Creates an Archive | 43 | * Creates an Archive |
| 33 | * @param backend File system backend interface to the archive | 44 | * @param backend File system backend interface to the archive |
| 34 | * @param name Name of Archive | 45 | * @param id_code Id code used to access this type of archive |
| 35 | */ | 46 | */ |
| 36 | ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); | 47 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); |
| 37 | 48 | ||
| 38 | /** | 49 | /** |
| 39 | * Open a File from an Archive | 50 | * Open a File from an Archive |
| @@ -42,7 +53,7 @@ ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); | |||
| 42 | * @param mode Mode under which to open the File | 53 | * @param mode Mode under which to open the File |
| 43 | * @return Handle to the opened File object | 54 | * @return Handle to the opened File object |
| 44 | */ | 55 | */ |
| 45 | ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); | 56 | ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); |
| 46 | 57 | ||
| 47 | /** | 58 | /** |
| 48 | * Delete a File from an Archive | 59 | * Delete a File from an Archive |
| @@ -50,7 +61,7 @@ ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path | |||
| 50 | * @param path Path to the File inside of the Archive | 61 | * @param path Path to the File inside of the Archive |
| 51 | * @return Whether deletion succeeded | 62 | * @return Whether deletion succeeded |
| 52 | */ | 63 | */ |
| 53 | ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); | 64 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); |
| 54 | 65 | ||
| 55 | /** | 66 | /** |
| 56 | * Rename a File between two Archives | 67 | * Rename a File between two Archives |
| @@ -60,8 +71,8 @@ ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& pat | |||
| 60 | * @param dest_path Path to the File inside of the destination Archive | 71 | * @param dest_path Path to the File inside of the destination Archive |
| 61 | * @return Whether rename succeeded | 72 | * @return Whether rename succeeded |
| 62 | */ | 73 | */ |
| 63 | ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, | 74 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, |
| 64 | Handle dest_archive_handle, const FileSys::Path& dest_path); | 75 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); |
| 65 | 76 | ||
| 66 | /** | 77 | /** |
| 67 | * Delete a Directory from an Archive | 78 | * Delete a Directory from an Archive |
| @@ -69,7 +80,16 @@ ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::P | |||
| 69 | * @param path Path to the Directory inside of the Archive | 80 | * @param path Path to the Directory inside of the Archive |
| 70 | * @return Whether deletion succeeded | 81 | * @return Whether deletion succeeded |
| 71 | */ | 82 | */ |
| 72 | ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | 83 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); |
| 84 | |||
| 85 | /** | ||
| 86 | * Create a File in an Archive | ||
| 87 | * @param archive_handle Handle to an open Archive object | ||
| 88 | * @param path Path to the File inside of the Archive | ||
| 89 | * @param file_size The size of the new file, filled with zeroes | ||
| 90 | * @return File creation result code | ||
| 91 | */ | ||
| 92 | ResultCode CreateFileInArchive(Handle archive_handle, const FileSys::Path& path, u32 file_size); | ||
| 73 | 93 | ||
| 74 | /** | 94 | /** |
| 75 | * Create a Directory from an Archive | 95 | * Create a Directory from an Archive |
| @@ -77,7 +97,7 @@ ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path | |||
| 77 | * @param path Path to the Directory inside of the Archive | 97 | * @param path Path to the Directory inside of the Archive |
| 78 | * @return Whether creation of directory succeeded | 98 | * @return Whether creation of directory succeeded |
| 79 | */ | 99 | */ |
| 80 | ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | 100 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); |
| 81 | 101 | ||
| 82 | /** | 102 | /** |
| 83 | * Rename a Directory between two Archives | 103 | * Rename a Directory between two Archives |
| @@ -87,8 +107,8 @@ ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path | |||
| 87 | * @param dest_path Path to the Directory inside of the destination Archive | 107 | * @param dest_path Path to the Directory inside of the destination Archive |
| 88 | * @return Whether rename succeeded | 108 | * @return Whether rename succeeded |
| 89 | */ | 109 | */ |
| 90 | ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, | 110 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, |
| 91 | Handle dest_archive_handle, const FileSys::Path& dest_path); | 111 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); |
| 92 | 112 | ||
| 93 | /** | 113 | /** |
| 94 | * Open a Directory from an Archive | 114 | * Open a Directory from an Archive |
| @@ -96,7 +116,13 @@ ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileS | |||
| 96 | * @param path Path to the Directory inside of the Archive | 116 | * @param path Path to the Directory inside of the Archive |
| 97 | * @return Handle to the opened File object | 117 | * @return Handle to the opened File object |
| 98 | */ | 118 | */ |
| 99 | ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | 119 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); |
| 120 | |||
| 121 | /** | ||
| 122 | * Creates a blank SaveData archive. | ||
| 123 | * @return ResultCode 0 on success or the corresponding code on error | ||
| 124 | */ | ||
| 125 | ResultCode FormatSaveData(); | ||
| 100 | 126 | ||
| 101 | /// Initialize archives | 127 | /// Initialize archives |
| 102 | void ArchiveInit(); | 128 | void ArchiveInit(); |
| @@ -104,4 +130,5 @@ void ArchiveInit(); | |||
| 104 | /// Shutdown archives | 130 | /// Shutdown archives |
| 105 | void ArchiveShutdown(); | 131 | void ArchiveShutdown(); |
| 106 | 132 | ||
| 107 | } // namespace FileSys | 133 | } // namespace FS |
| 134 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 9bda4fe8a..5e9b85cc7 100644 --- a/src/core/hle/service/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -1,23 +1,28 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
| 6 | 6 | #include "common/file_util.h" | |
| 7 | #include "common/scope_exit.h" | ||
| 7 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 8 | #include "core/hle/kernel/archive.h" | ||
| 9 | #include "core/hle/kernel/archive.h" | ||
| 10 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 11 | #include "core/hle/service/fs_user.h" | 10 | #include "core/hle/service/fs/archive.h" |
| 11 | #include "core/hle/service/fs/fs_user.h" | ||
| 12 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 13 | 13 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 15 | // Namespace FS_User | 15 | // Namespace FS_User |
| 16 | 16 | ||
| 17 | namespace FS_User { | 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 | } | ||
| 18 | 23 | ||
| 19 | static void Initialize(Service::Interface* self) { | 24 | static void Initialize(Service::Interface* self) { |
| 20 | u32* cmd_buff = Service::GetCommandBuffer(); | 25 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 21 | 26 | ||
| 22 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per | 27 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per |
| 23 | // http://3dbrew.org/wiki/FS:Initialize#Request | 28 | // http://3dbrew.org/wiki/FS:Initialize#Request |
| @@ -43,11 +48,9 @@ static void Initialize(Service::Interface* self) { | |||
| 43 | * 3 : File handle | 48 | * 3 : File handle |
| 44 | */ | 49 | */ |
| 45 | static void OpenFile(Service::Interface* self) { | 50 | static void OpenFile(Service::Interface* self) { |
| 46 | u32* cmd_buff = Service::GetCommandBuffer(); | 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 47 | 52 | ||
| 48 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | 53 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 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 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 54 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 52 | u32 filename_size = cmd_buff[5]; | 55 | u32 filename_size = cmd_buff[5]; |
| 53 | FileSys::Mode mode; mode.hex = cmd_buff[6]; | 56 | FileSys::Mode mode; mode.hex = cmd_buff[6]; |
| @@ -57,11 +60,12 @@ static void OpenFile(Service::Interface* self) { | |||
| 57 | 60 | ||
| 58 | LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); | 61 | LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); |
| 59 | 62 | ||
| 60 | ResultVal<Handle> handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode); | 63 | ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode); |
| 61 | cmd_buff[1] = handle.Code().raw; | 64 | cmd_buff[1] = handle.Code().raw; |
| 62 | if (handle.Succeeded()) { | 65 | if (handle.Succeeded()) { |
| 63 | cmd_buff[3] = *handle; | 66 | cmd_buff[3] = *handle; |
| 64 | } else { | 67 | } else { |
| 68 | cmd_buff[3] = 0; | ||
| 65 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | 69 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); |
| 66 | } | 70 | } |
| 67 | } | 71 | } |
| @@ -86,9 +90,9 @@ static void OpenFile(Service::Interface* self) { | |||
| 86 | * 3 : File handle | 90 | * 3 : File handle |
| 87 | */ | 91 | */ |
| 88 | static void OpenFileDirectly(Service::Interface* self) { | 92 | static void OpenFileDirectly(Service::Interface* self) { |
| 89 | u32* cmd_buff = Service::GetCommandBuffer(); | 93 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 90 | 94 | ||
| 91 | auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]); | 95 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]); |
| 92 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | 96 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); |
| 93 | u32 archivename_size = cmd_buff[4]; | 97 | u32 archivename_size = cmd_buff[4]; |
| 94 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); | 98 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); |
| @@ -106,25 +110,25 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 106 | if (archive_path.GetType() != FileSys::Empty) { | 110 | if (archive_path.GetType() != FileSys::Empty) { |
| 107 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | 111 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); |
| 108 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | 112 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; |
| 113 | cmd_buff[3] = 0; | ||
| 109 | return; | 114 | return; |
| 110 | } | 115 | } |
| 111 | 116 | ||
| 112 | // TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it | 117 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id); |
| 113 | // TODO(yuriks): Why is there all this duplicate (and seemingly useless) code up here? | ||
| 114 | ResultVal<Handle> archive_handle = Kernel::OpenArchive(archive_id); | ||
| 115 | cmd_buff[1] = archive_handle.Code().raw; | ||
| 116 | if (archive_handle.Failed()) { | 118 | if (archive_handle.Failed()) { |
| 117 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | 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; | ||
| 118 | return; | 122 | return; |
| 119 | } | 123 | } |
| 120 | // cmd_buff[2] isn't used according to 3dmoo's implementation. | 124 | SCOPE_EXIT({ CloseArchive(*archive_handle); }); |
| 121 | cmd_buff[3] = *archive_handle; | ||
| 122 | 125 | ||
| 123 | ResultVal<Handle> handle = Kernel::OpenFileFromArchive(*archive_handle, file_path, mode); | 126 | ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode); |
| 124 | cmd_buff[1] = handle.Code().raw; | 127 | cmd_buff[1] = handle.Code().raw; |
| 125 | if (handle.Succeeded()) { | 128 | if (handle.Succeeded()) { |
| 126 | cmd_buff[3] = *handle; | 129 | cmd_buff[3] = *handle; |
| 127 | } else { | 130 | } else { |
| 131 | cmd_buff[3] = 0; | ||
| 128 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | 132 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); |
| 129 | } | 133 | } |
| 130 | } | 134 | } |
| @@ -140,12 +144,10 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 140 | * Outputs: | 144 | * Outputs: |
| 141 | * 1 : Result of function, 0 on success, otherwise error code | 145 | * 1 : Result of function, 0 on success, otherwise error code |
| 142 | */ | 146 | */ |
| 143 | void DeleteFile(Service::Interface* self) { | 147 | static void DeleteFile(Service::Interface* self) { |
| 144 | u32* cmd_buff = Service::GetCommandBuffer(); | 148 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 145 | 149 | ||
| 146 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | 150 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 147 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 148 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 149 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 151 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 150 | u32 filename_size = cmd_buff[5]; | 152 | u32 filename_size = cmd_buff[5]; |
| 151 | u32 filename_ptr = cmd_buff[7]; | 153 | u32 filename_ptr = cmd_buff[7]; |
| @@ -155,7 +157,7 @@ void DeleteFile(Service::Interface* self) { | |||
| 155 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", | 157 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", |
| 156 | filename_type, filename_size, file_path.DebugStr().c_str()); | 158 | filename_type, filename_size, file_path.DebugStr().c_str()); |
| 157 | 159 | ||
| 158 | cmd_buff[1] = Kernel::DeleteFileFromArchive(archive_handle, file_path).raw; | 160 | cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw; |
| 159 | } | 161 | } |
| 160 | 162 | ||
| 161 | /* | 163 | /* |
| @@ -174,15 +176,13 @@ void DeleteFile(Service::Interface* self) { | |||
| 174 | * Outputs: | 176 | * Outputs: |
| 175 | * 1 : Result of function, 0 on success, otherwise error code | 177 | * 1 : Result of function, 0 on success, otherwise error code |
| 176 | */ | 178 | */ |
| 177 | void RenameFile(Service::Interface* self) { | 179 | static void RenameFile(Service::Interface* self) { |
| 178 | u32* cmd_buff = Service::GetCommandBuffer(); | 180 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 179 | 181 | ||
| 180 | // TODO(Link Mauve): cmd_buff[2] and cmd_buff[6], aka archive handle lower word, aren't used according to | 182 | ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 181 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 182 | Handle src_archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 183 | auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 183 | auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 184 | u32 src_filename_size = cmd_buff[5]; | 184 | u32 src_filename_size = cmd_buff[5]; |
| 185 | Handle dest_archive_handle = static_cast<Handle>(cmd_buff[7]); | 185 | ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);; |
| 186 | auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | 186 | auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); |
| 187 | u32 dest_filename_size = cmd_buff[9]; | 187 | u32 dest_filename_size = cmd_buff[9]; |
| 188 | u32 src_filename_ptr = cmd_buff[11]; | 188 | u32 src_filename_ptr = cmd_buff[11]; |
| @@ -195,7 +195,7 @@ void RenameFile(Service::Interface* self) { | |||
| 195 | src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(), | 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()); | 196 | dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str()); |
| 197 | 197 | ||
| 198 | cmd_buff[1] = Kernel::RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; | 198 | cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | /* | 201 | /* |
| @@ -209,12 +209,10 @@ void RenameFile(Service::Interface* self) { | |||
| 209 | * Outputs: | 209 | * Outputs: |
| 210 | * 1 : Result of function, 0 on success, otherwise error code | 210 | * 1 : Result of function, 0 on success, otherwise error code |
| 211 | */ | 211 | */ |
| 212 | void DeleteDirectory(Service::Interface* self) { | 212 | static void DeleteDirectory(Service::Interface* self) { |
| 213 | u32* cmd_buff = Service::GetCommandBuffer(); | 213 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 214 | 214 | ||
| 215 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | 215 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 216 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 217 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 218 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 216 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 219 | u32 dirname_size = cmd_buff[5]; | 217 | u32 dirname_size = cmd_buff[5]; |
| 220 | u32 dirname_ptr = cmd_buff[7]; | 218 | u32 dirname_ptr = cmd_buff[7]; |
| @@ -224,7 +222,36 @@ void DeleteDirectory(Service::Interface* self) { | |||
| 224 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", | 222 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", |
| 225 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); | 223 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); |
| 226 | 224 | ||
| 227 | cmd_buff[1] = Kernel::DeleteDirectoryFromArchive(archive_handle, dir_path).raw; | 225 | cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; |
| 226 | } | ||
| 227 | |||
| 228 | /* | ||
| 229 | * FS_User::CreateFile service function | ||
| 230 | * Inputs: | ||
| 231 | * 0 : Command header 0x08080202 | ||
| 232 | * 2 : Archive handle lower word | ||
| 233 | * 3 : Archive handle upper word | ||
| 234 | * 4 : File path string type | ||
| 235 | * 5 : File path string size | ||
| 236 | * 7 : File size (filled with zeroes) | ||
| 237 | * 10: File path string data | ||
| 238 | * Outputs: | ||
| 239 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 240 | */ | ||
| 241 | static void CreateFile(Service::Interface* self) { | ||
| 242 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 243 | |||
| 244 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 245 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 246 | u32 filename_size = cmd_buff[5]; | ||
| 247 | u32 file_size = cmd_buff[7]; | ||
| 248 | u32 filename_ptr = cmd_buff[10]; | ||
| 249 | |||
| 250 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 251 | |||
| 252 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); | ||
| 253 | |||
| 254 | cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; | ||
| 228 | } | 255 | } |
| 229 | 256 | ||
| 230 | /* | 257 | /* |
| @@ -239,11 +266,9 @@ void DeleteDirectory(Service::Interface* self) { | |||
| 239 | * 1 : Result of function, 0 on success, otherwise error code | 266 | * 1 : Result of function, 0 on success, otherwise error code |
| 240 | */ | 267 | */ |
| 241 | static void CreateDirectory(Service::Interface* self) { | 268 | static void CreateDirectory(Service::Interface* self) { |
| 242 | u32* cmd_buff = Service::GetCommandBuffer(); | 269 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 243 | 270 | ||
| 244 | // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to | 271 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 245 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 246 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 247 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 272 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 248 | u32 dirname_size = cmd_buff[5]; | 273 | u32 dirname_size = cmd_buff[5]; |
| 249 | u32 dirname_ptr = cmd_buff[8]; | 274 | u32 dirname_ptr = cmd_buff[8]; |
| @@ -252,7 +277,7 @@ static void CreateDirectory(Service::Interface* self) { | |||
| 252 | 277 | ||
| 253 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | 278 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); |
| 254 | 279 | ||
| 255 | cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_path).raw; | 280 | cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw; |
| 256 | } | 281 | } |
| 257 | 282 | ||
| 258 | /* | 283 | /* |
| @@ -271,15 +296,13 @@ static void CreateDirectory(Service::Interface* self) { | |||
| 271 | * Outputs: | 296 | * Outputs: |
| 272 | * 1 : Result of function, 0 on success, otherwise error code | 297 | * 1 : Result of function, 0 on success, otherwise error code |
| 273 | */ | 298 | */ |
| 274 | void RenameDirectory(Service::Interface* self) { | 299 | static void RenameDirectory(Service::Interface* self) { |
| 275 | u32* cmd_buff = Service::GetCommandBuffer(); | 300 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 276 | 301 | ||
| 277 | // TODO(Link Mauve): cmd_buff[2] and cmd_buff[6], aka archive handle lower word, aren't used according to | 302 | ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 278 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 279 | Handle src_archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 280 | auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 303 | auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 281 | u32 src_dirname_size = cmd_buff[5]; | 304 | u32 src_dirname_size = cmd_buff[5]; |
| 282 | Handle dest_archive_handle = static_cast<Handle>(cmd_buff[7]); | 305 | ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]); |
| 283 | auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | 306 | auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); |
| 284 | u32 dest_dirname_size = cmd_buff[9]; | 307 | u32 dest_dirname_size = cmd_buff[9]; |
| 285 | u32 src_dirname_ptr = cmd_buff[11]; | 308 | u32 src_dirname_ptr = cmd_buff[11]; |
| @@ -292,15 +315,26 @@ void RenameDirectory(Service::Interface* self) { | |||
| 292 | src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(), | 315 | src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(), |
| 293 | dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str()); | 316 | dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str()); |
| 294 | 317 | ||
| 295 | cmd_buff[1] = Kernel::RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; | 318 | cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; |
| 296 | } | 319 | } |
| 297 | 320 | ||
| 321 | /** | ||
| 322 | * FS_User::OpenDirectory service function | ||
| 323 | * Inputs: | ||
| 324 | * 1 : Archive handle low word | ||
| 325 | * 2 : Archive handle high word | ||
| 326 | * 3 : Low path type | ||
| 327 | * 4 : Low path size | ||
| 328 | * 7 : (LowPathSize << 14) | 2 | ||
| 329 | * 8 : Low path data pointer | ||
| 330 | * Outputs: | ||
| 331 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 332 | * 3 : Directory handle | ||
| 333 | */ | ||
| 298 | static void OpenDirectory(Service::Interface* self) { | 334 | static void OpenDirectory(Service::Interface* self) { |
| 299 | u32* cmd_buff = Service::GetCommandBuffer(); | 335 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 300 | 336 | ||
| 301 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | 337 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); |
| 302 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 303 | Handle archive_handle = static_cast<Handle>(cmd_buff[2]); | ||
| 304 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | 338 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); |
| 305 | u32 dirname_size = cmd_buff[4]; | 339 | u32 dirname_size = cmd_buff[4]; |
| 306 | u32 dirname_ptr = cmd_buff[6]; | 340 | u32 dirname_ptr = cmd_buff[6]; |
| @@ -309,7 +343,7 @@ static void OpenDirectory(Service::Interface* self) { | |||
| 309 | 343 | ||
| 310 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | 344 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); |
| 311 | 345 | ||
| 312 | ResultVal<Handle> handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path); | 346 | ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path); |
| 313 | cmd_buff[1] = handle.Code().raw; | 347 | cmd_buff[1] = handle.Code().raw; |
| 314 | if (handle.Succeeded()) { | 348 | if (handle.Succeeded()) { |
| 315 | cmd_buff[3] = *handle; | 349 | cmd_buff[3] = *handle; |
| @@ -332,9 +366,9 @@ static void OpenDirectory(Service::Interface* self) { | |||
| 332 | * 3 : Archive handle upper word (same as file handle) | 366 | * 3 : Archive handle upper word (same as file handle) |
| 333 | */ | 367 | */ |
| 334 | static void OpenArchive(Service::Interface* self) { | 368 | static void OpenArchive(Service::Interface* self) { |
| 335 | u32* cmd_buff = Service::GetCommandBuffer(); | 369 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 336 | 370 | ||
| 337 | auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]); | 371 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); |
| 338 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | 372 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); |
| 339 | u32 archivename_size = cmd_buff[3]; | 373 | u32 archivename_size = cmd_buff[3]; |
| 340 | u32 archivename_ptr = cmd_buff[5]; | 374 | u32 archivename_ptr = cmd_buff[5]; |
| @@ -348,16 +382,34 @@ static void OpenArchive(Service::Interface* self) { | |||
| 348 | return; | 382 | return; |
| 349 | } | 383 | } |
| 350 | 384 | ||
| 351 | ResultVal<Handle> handle = Kernel::OpenArchive(archive_id); | 385 | ResultVal<ArchiveHandle> handle = OpenArchive(archive_id); |
| 352 | cmd_buff[1] = handle.Code().raw; | 386 | cmd_buff[1] = handle.Code().raw; |
| 353 | if (handle.Succeeded()) { | 387 | if (handle.Succeeded()) { |
| 354 | // cmd_buff[2] isn't used according to 3dmoo's implementation. | 388 | cmd_buff[2] = *handle & 0xFFFFFFFF; |
| 355 | cmd_buff[3] = *handle; | 389 | cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; |
| 356 | } else { | 390 | } else { |
| 391 | cmd_buff[2] = cmd_buff[3] = 0; | ||
| 357 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | 392 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); |
| 358 | } | 393 | } |
| 359 | } | 394 | } |
| 360 | 395 | ||
| 396 | /** | ||
| 397 | * FS_User::CloseArchive service function | ||
| 398 | * Inputs: | ||
| 399 | * 0 : 0x080E0080 | ||
| 400 | * 1 : Archive handle low word | ||
| 401 | * 2 : Archive handle high word | ||
| 402 | * Outputs: | ||
| 403 | * 0 : ??? TODO(yuriks): Verify return header | ||
| 404 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 405 | */ | ||
| 406 | static void CloseArchive(Service::Interface* self) { | ||
| 407 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 408 | |||
| 409 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); | ||
| 410 | cmd_buff[1] = CloseArchive(archive_handle).raw; | ||
| 411 | } | ||
| 412 | |||
| 361 | /* | 413 | /* |
| 362 | * FS_User::IsSdmcDetected service function | 414 | * FS_User::IsSdmcDetected service function |
| 363 | * Outputs: | 415 | * Outputs: |
| @@ -365,7 +417,7 @@ static void OpenArchive(Service::Interface* self) { | |||
| 365 | * 2 : Whether the Sdmc could be detected | 417 | * 2 : Whether the Sdmc could be detected |
| 366 | */ | 418 | */ |
| 367 | static void IsSdmcDetected(Service::Interface* self) { | 419 | static void IsSdmcDetected(Service::Interface* self) { |
| 368 | u32* cmd_buff = Service::GetCommandBuffer(); | 420 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 369 | 421 | ||
| 370 | cmd_buff[1] = 0; | 422 | cmd_buff[1] = 0; |
| 371 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | 423 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; |
| @@ -373,7 +425,66 @@ static void IsSdmcDetected(Service::Interface* self) { | |||
| 373 | LOG_DEBUG(Service_FS, "called"); | 425 | LOG_DEBUG(Service_FS, "called"); |
| 374 | } | 426 | } |
| 375 | 427 | ||
| 376 | const Interface::FunctionInfo FunctionTable[] = { | 428 | /** |
| 429 | * FS_User::FormatSaveData service function, | ||
| 430 | * formats the SaveData specified by the input path. | ||
| 431 | * Inputs: | ||
| 432 | * 0 : 0x084C0242 | ||
| 433 | * 1 : Archive ID | ||
| 434 | * 2 : Archive low path type | ||
| 435 | * 3 : Archive low path size | ||
| 436 | * 10 : (LowPathSize << 14) | 2 | ||
| 437 | * 11 : Archive low path | ||
| 438 | * Outputs: | ||
| 439 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 440 | */ | ||
| 441 | static void FormatSaveData(Service::Interface* self) { | ||
| 442 | // TODO(Subv): Find out what the other inputs and outputs of this function are | ||
| 443 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 444 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 445 | |||
| 446 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||
| 447 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||
| 448 | u32 archivename_size = cmd_buff[3]; | ||
| 449 | u32 archivename_ptr = cmd_buff[11]; | ||
| 450 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 451 | |||
| 452 | LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||
| 453 | |||
| 454 | if (archive_id != FS::ArchiveIdCode::SaveData) { | ||
| 455 | // TODO(Subv): What should happen if somebody attempts to format a different archive? | ||
| 456 | LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]); | ||
| 457 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 458 | return; | ||
| 459 | } | ||
| 460 | |||
| 461 | if (archive_path.GetType() != FileSys::LowPathType::Empty) { | ||
| 462 | // TODO(Subv): Implement formatting the SaveData of other games | ||
| 463 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 464 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 465 | return; | ||
| 466 | } | ||
| 467 | |||
| 468 | cmd_buff[1] = FormatSaveData().raw; | ||
| 469 | } | ||
| 470 | |||
| 471 | /** | ||
| 472 | * FS_User::FormatThisUserSaveData service function | ||
| 473 | * Inputs: | ||
| 474 | * 0: 0x080F0180 | ||
| 475 | * Outputs: | ||
| 476 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 477 | */ | ||
| 478 | static void FormatThisUserSaveData(Service::Interface* self) { | ||
| 479 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 480 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 481 | |||
| 482 | // TODO(Subv): Find out what the inputs and outputs of this function are | ||
| 483 | |||
| 484 | cmd_buff[1] = FormatSaveData().raw; | ||
| 485 | } | ||
| 486 | |||
| 487 | const FSUserInterface::FunctionInfo FunctionTable[] = { | ||
| 377 | {0x000100C6, nullptr, "Dummy1"}, | 488 | {0x000100C6, nullptr, "Dummy1"}, |
| 378 | {0x040100C4, nullptr, "Control"}, | 489 | {0x040100C4, nullptr, "Control"}, |
| 379 | {0x08010002, Initialize, "Initialize"}, | 490 | {0x08010002, Initialize, "Initialize"}, |
| @@ -383,14 +494,14 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 383 | {0x08050244, RenameFile, "RenameFile"}, | 494 | {0x08050244, RenameFile, "RenameFile"}, |
| 384 | {0x08060142, DeleteDirectory, "DeleteDirectory"}, | 495 | {0x08060142, DeleteDirectory, "DeleteDirectory"}, |
| 385 | {0x08070142, nullptr, "DeleteDirectoryRecursively"}, | 496 | {0x08070142, nullptr, "DeleteDirectoryRecursively"}, |
| 386 | {0x08080202, nullptr, "CreateFile"}, | 497 | {0x08080202, CreateFile, "CreateFile"}, |
| 387 | {0x08090182, CreateDirectory, "CreateDirectory"}, | 498 | {0x08090182, CreateDirectory, "CreateDirectory"}, |
| 388 | {0x080A0244, RenameDirectory, "RenameDirectory"}, | 499 | {0x080A0244, RenameDirectory, "RenameDirectory"}, |
| 389 | {0x080B0102, OpenDirectory, "OpenDirectory"}, | 500 | {0x080B0102, OpenDirectory, "OpenDirectory"}, |
| 390 | {0x080C00C2, OpenArchive, "OpenArchive"}, | 501 | {0x080C00C2, OpenArchive, "OpenArchive"}, |
| 391 | {0x080D0144, nullptr, "ControlArchive"}, | 502 | {0x080D0144, nullptr, "ControlArchive"}, |
| 392 | {0x080E0080, nullptr, "CloseArchive"}, | 503 | {0x080E0080, CloseArchive, "CloseArchive"}, |
| 393 | {0x080F0180, nullptr, "FormatThisUserSaveData"}, | 504 | {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, |
| 394 | {0x08100200, nullptr, "CreateSystemSaveData"}, | 505 | {0x08100200, nullptr, "CreateSystemSaveData"}, |
| 395 | {0x08110040, nullptr, "DeleteSystemSaveData"}, | 506 | {0x08110040, nullptr, "DeleteSystemSaveData"}, |
| 396 | {0x08120080, nullptr, "GetFreeBytes"}, | 507 | {0x08120080, nullptr, "GetFreeBytes"}, |
| @@ -451,7 +562,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 451 | {0x08490040, nullptr, "GetArchiveResource"}, | 562 | {0x08490040, nullptr, "GetArchiveResource"}, |
| 452 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, | 563 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, |
| 453 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, | 564 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, |
| 454 | {0x084C0242, nullptr, "FormatSaveData"}, | 565 | {0x084C0242, FormatSaveData, "FormatSaveData"}, |
| 455 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, | 566 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, |
| 456 | {0x084E0342, nullptr, "UpdateSha256Context"}, | 567 | {0x084E0342, nullptr, "UpdateSha256Context"}, |
| 457 | {0x084F0102, nullptr, "ReadSpecialFile"}, | 568 | {0x084F0102, nullptr, "ReadSpecialFile"}, |
| @@ -465,11 +576,12 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 465 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 576 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 466 | // Interface class | 577 | // Interface class |
| 467 | 578 | ||
| 468 | Interface::Interface() { | 579 | FSUserInterface::FSUserInterface() { |
| 469 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 580 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 470 | } | 581 | } |
| 471 | 582 | ||
| 472 | Interface::~Interface() { | 583 | FSUserInterface::~FSUserInterface() { |
| 473 | } | 584 | } |
| 474 | 585 | ||
| 475 | } // namespace | 586 | } // namespace FS |
| 587 | } // 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..af4da269b --- /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 or any later version | ||
| 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/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 223800560..1f841078a 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
| @@ -72,7 +72,7 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | |||
| 72 | 72 | ||
| 73 | /// Write a GSP GPU hardware register | 73 | /// Write a GSP GPU hardware register |
| 74 | static void WriteHWRegs(Service::Interface* self) { | 74 | static void WriteHWRegs(Service::Interface* self) { |
| 75 | u32* cmd_buff = Service::GetCommandBuffer(); | 75 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 76 | u32 reg_addr = cmd_buff[1]; | 76 | u32 reg_addr = cmd_buff[1]; |
| 77 | u32 size = cmd_buff[2]; | 77 | u32 size = cmd_buff[2]; |
| 78 | 78 | ||
| @@ -83,7 +83,7 @@ static void WriteHWRegs(Service::Interface* self) { | |||
| 83 | 83 | ||
| 84 | /// Read a GSP GPU hardware register | 84 | /// Read a GSP GPU hardware register |
| 85 | static void ReadHWRegs(Service::Interface* self) { | 85 | static void ReadHWRegs(Service::Interface* self) { |
| 86 | u32* cmd_buff = Service::GetCommandBuffer(); | 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 87 | u32 reg_addr = cmd_buff[1]; | 87 | u32 reg_addr = cmd_buff[1]; |
| 88 | u32 size = cmd_buff[2]; | 88 | u32 size = cmd_buff[2]; |
| 89 | 89 | ||
| @@ -136,7 +136,7 @@ static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | |||
| 136 | * 1: Result code | 136 | * 1: Result code |
| 137 | */ | 137 | */ |
| 138 | static void SetBufferSwap(Service::Interface* self) { | 138 | static void SetBufferSwap(Service::Interface* self) { |
| 139 | u32* cmd_buff = Service::GetCommandBuffer(); | 139 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 140 | u32 screen_id = cmd_buff[1]; | 140 | u32 screen_id = cmd_buff[1]; |
| 141 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; | 141 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; |
| 142 | SetBufferSwap(screen_id, *fb_info); | 142 | SetBufferSwap(screen_id, *fb_info); |
| @@ -145,6 +145,30 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | /** | 147 | /** |
| 148 | * GSP_GPU::FlushDataCache service function | ||
| 149 | * | ||
| 150 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 151 | * | ||
| 152 | * Inputs: | ||
| 153 | * 1 : Address | ||
| 154 | * 2 : Size | ||
| 155 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 156 | * 4 : KProcess handle | ||
| 157 | * Outputs: | ||
| 158 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 159 | */ | ||
| 160 | static void FlushDataCache(Service::Interface* self) { | ||
| 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 162 | u32 address = cmd_buff[1]; | ||
| 163 | u32 size = cmd_buff[2]; | ||
| 164 | u32 process = cmd_buff[4]; | ||
| 165 | |||
| 166 | // TODO(purpasmart96): Verify return header on HW | ||
| 167 | |||
| 168 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 148 | * GSP_GPU::RegisterInterruptRelayQueue service function | 172 | * GSP_GPU::RegisterInterruptRelayQueue service function |
| 149 | * Inputs: | 173 | * Inputs: |
| 150 | * 1 : "Flags" field, purpose is unknown | 174 | * 1 : "Flags" field, purpose is unknown |
| @@ -155,7 +179,7 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 155 | * 4 : Handle to GSP shared memory | 179 | * 4 : Handle to GSP shared memory |
| 156 | */ | 180 | */ |
| 157 | static void RegisterInterruptRelayQueue(Service::Interface* self) { | 181 | static void RegisterInterruptRelayQueue(Service::Interface* self) { |
| 158 | u32* cmd_buff = Service::GetCommandBuffer(); | 182 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 159 | u32 flags = cmd_buff[1]; | 183 | u32 flags = cmd_buff[1]; |
| 160 | g_interrupt_event = cmd_buff[3]; | 184 | g_interrupt_event = cmd_buff[3]; |
| 161 | g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); | 185 | g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); |
| @@ -323,7 +347,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) { | |||
| 323 | } | 347 | } |
| 324 | } | 348 | } |
| 325 | 349 | ||
| 326 | u32* cmd_buff = Service::GetCommandBuffer(); | 350 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 327 | cmd_buff[1] = 0; // No error | 351 | cmd_buff[1] = 0; // No error |
| 328 | } | 352 | } |
| 329 | 353 | ||
| @@ -335,7 +359,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 335 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, | 359 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, |
| 336 | {0x00060082, nullptr, "SetCommandList"}, | 360 | {0x00060082, nullptr, "SetCommandList"}, |
| 337 | {0x000700C2, nullptr, "RequestDma"}, | 361 | {0x000700C2, nullptr, "RequestDma"}, |
| 338 | {0x00080082, nullptr, "FlushDataCache"}, | 362 | {0x00080082, FlushDataCache, "FlushDataCache"}, |
| 339 | {0x00090082, nullptr, "InvalidateDataCache"}, | 363 | {0x00090082, nullptr, "InvalidateDataCache"}, |
| 340 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, | 364 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, |
| 341 | {0x000B0040, nullptr, "SetLcdForceBlack"}, | 365 | {0x000B0040, nullptr, "SetLcdForceBlack"}, |
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 177ce8da6..56b5a16c9 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp index 5772199d4..cec9b1bfb 100644 --- a/src/core/hle/service/hid_user.cpp +++ b/src/core/hle/service/hid_user.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| @@ -153,7 +153,7 @@ void PadUpdateComplete() { | |||
| 153 | * 8 : Event signaled by HID_User | 153 | * 8 : Event signaled by HID_User |
| 154 | */ | 154 | */ |
| 155 | static void GetIPCHandles(Service::Interface* self) { | 155 | static void GetIPCHandles(Service::Interface* self) { |
| 156 | u32* cmd_buff = Service::GetCommandBuffer(); | 156 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 157 | 157 | ||
| 158 | cmd_buff[1] = 0; // No error | 158 | cmd_buff[1] = 0; // No error |
| 159 | cmd_buff[3] = shared_mem; | 159 | cmd_buff[3] = shared_mem; |
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h index 8f53befdb..2164ad896 100644 --- a/src/core/hle/service/hid_user.h +++ b/src/core/hle/service/hid_user.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp index be15db231..6145b8b2c 100644 --- a/src/core/hle/service/ir_rst.cpp +++ b/src/core/hle/service/ir_rst.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h index 73effd7e3..2fdab9f02 100644 --- a/src/core/hle/service/ir_rst.h +++ b/src/core/hle/service/ir_rst.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 or any later version |
| 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/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp index aa9db6f6d..db62a9c98 100644 --- a/src/core/hle/service/ir_u.cpp +++ b/src/core/hle/service/ir_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h index 86d98d079..cf1c73f52 100644 --- a/src/core/hle/service/ir_u.h +++ b/src/core/hle/service/ir_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp new file mode 100644 index 000000000..c08313f9a --- /dev/null +++ b/src/core/hle/service/ldr_ro.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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..7716ae74e --- /dev/null +++ b/src/core/hle/service/ldr_ro.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "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 d6f30e9ae..399548d4d 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 2a495f3a9..26842e5f1 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp index 37c0661bf..141c311fd 100644 --- a/src/core/hle/service/ndm_u.cpp +++ b/src/core/hle/service/ndm_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/hle.h" | 5 | #include "core/hle/hle.h" |
diff --git a/src/core/hle/service/ndm_u.h b/src/core/hle/service/ndm_u.h index 2ca9fcf22..62ed901c2 100644 --- a/src/core/hle/service/ndm_u.h +++ b/src/core/hle/service/ndm_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp new file mode 100644 index 000000000..17d1c4ff5 --- /dev/null +++ b/src/core/hle/service/nim_aoc.cpp | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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..33aa25c91 --- /dev/null +++ b/src/core/hle/service/nim_aoc.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "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.cpp b/src/core/hle/service/nwm_uds.cpp index 14df86d85..2491d14d6 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index 69d2c2002..cd27f78fc 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm_uds.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp index 90e9b1bfa..729255348 100644 --- a/src/core/hle/service/pm_app.cpp +++ b/src/core/hle/service/pm_app.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h index 28c38f582..7ed617e5e 100644 --- a/src/core/hle/service/pm_app.h +++ b/src/core/hle/service/pm_app.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index 559f148dd..da48729da 100644 --- a/src/core/hle/service/ptm_u.cpp +++ b/src/core/hle/service/ptm_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| @@ -34,7 +34,7 @@ static bool battery_is_charging = true; | |||
| 34 | * 2 : Output of function, 0 = not charging, 1 = charging. | 34 | * 2 : Output of function, 0 = not charging, 1 = charging. |
| 35 | */ | 35 | */ |
| 36 | static void GetAdapterState(Service::Interface* self) { | 36 | static void GetAdapterState(Service::Interface* self) { |
| 37 | u32* cmd_buff = Service::GetCommandBuffer(); | 37 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 38 | 38 | ||
| 39 | // TODO(purpasmart96): This function is only a stub, | 39 | // TODO(purpasmart96): This function is only a stub, |
| 40 | // it returns a valid result without implementing full functionality. | 40 | // it returns a valid result without implementing full functionality. |
| @@ -52,7 +52,7 @@ static void GetAdapterState(Service::Interface* self) { | |||
| 52 | * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) | 52 | * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) |
| 53 | */ | 53 | */ |
| 54 | static void GetShellState(Service::Interface* self) { | 54 | static void GetShellState(Service::Interface* self) { |
| 55 | u32* cmd_buff = Service::GetCommandBuffer(); | 55 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 56 | 56 | ||
| 57 | cmd_buff[1] = 0; | 57 | cmd_buff[1] = 0; |
| 58 | cmd_buff[2] = shell_open ? 1 : 0; | 58 | cmd_buff[2] = shell_open ? 1 : 0; |
| @@ -68,7 +68,7 @@ static void GetShellState(Service::Interface* self) { | |||
| 68 | * 3 = half full battery, 2 = low battery, 1 = critical battery. | 68 | * 3 = half full battery, 2 = low battery, 1 = critical battery. |
| 69 | */ | 69 | */ |
| 70 | static void GetBatteryLevel(Service::Interface* self) { | 70 | static void GetBatteryLevel(Service::Interface* self) { |
| 71 | u32* cmd_buff = Service::GetCommandBuffer(); | 71 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 72 | 72 | ||
| 73 | // TODO(purpasmart96): This function is only a stub, | 73 | // TODO(purpasmart96): This function is only a stub, |
| 74 | // it returns a valid result without implementing full functionality. | 74 | // it returns a valid result without implementing full functionality. |
| @@ -86,7 +86,7 @@ static void GetBatteryLevel(Service::Interface* self) { | |||
| 86 | * 2 : Output of function, 0 = not charging, 1 = charging. | 86 | * 2 : Output of function, 0 = not charging, 1 = charging. |
| 87 | */ | 87 | */ |
| 88 | static void GetBatteryChargeState(Service::Interface* self) { | 88 | static void GetBatteryChargeState(Service::Interface* self) { |
| 89 | u32* cmd_buff = Service::GetCommandBuffer(); | 89 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 90 | 90 | ||
| 91 | // TODO(purpasmart96): This function is only a stub, | 91 | // TODO(purpasmart96): This function is only a stub, |
| 92 | // it returns a valid result without implementing full functionality. | 92 | // it returns a valid result without implementing full functionality. |
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h index f8d9f57be..c9e0c519f 100644 --- a/src/core/hle/service/ptm_u.h +++ b/src/core/hle/service/ptm_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e6973572b..664f914d6 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
| @@ -7,21 +7,25 @@ | |||
| 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" | ||
| 10 | #include "core/hle/service/am_net.h" | 11 | #include "core/hle/service/am_net.h" |
| 11 | #include "core/hle/service/apt_u.h" | 12 | #include "core/hle/service/apt_u.h" |
| 12 | #include "core/hle/service/boss_u.h" | 13 | #include "core/hle/service/boss_u.h" |
| 13 | #include "core/hle/service/cfg_i.h" | 14 | #include "core/hle/service/cecd_u.h" |
| 14 | #include "core/hle/service/cfg_u.h" | 15 | #include "core/hle/service/cfg/cfg_i.h" |
| 16 | #include "core/hle/service/cfg/cfg_u.h" | ||
| 15 | #include "core/hle/service/csnd_snd.h" | 17 | #include "core/hle/service/csnd_snd.h" |
| 16 | #include "core/hle/service/dsp_dsp.h" | 18 | #include "core/hle/service/dsp_dsp.h" |
| 17 | #include "core/hle/service/err_f.h" | 19 | #include "core/hle/service/err_f.h" |
| 18 | #include "core/hle/service/fs_user.h" | 20 | #include "core/hle/service/fs/fs_user.h" |
| 19 | #include "core/hle/service/frd_u.h" | 21 | #include "core/hle/service/frd_u.h" |
| 20 | #include "core/hle/service/gsp_gpu.h" | 22 | #include "core/hle/service/gsp_gpu.h" |
| 21 | #include "core/hle/service/hid_user.h" | 23 | #include "core/hle/service/hid_user.h" |
| 22 | #include "core/hle/service/ir_rst.h" | 24 | #include "core/hle/service/ir_rst.h" |
| 23 | #include "core/hle/service/ir_u.h" | 25 | #include "core/hle/service/ir_u.h" |
| 26 | #include "core/hle/service/ldr_ro.h" | ||
| 24 | #include "core/hle/service/mic_u.h" | 27 | #include "core/hle/service/mic_u.h" |
| 28 | #include "core/hle/service/nim_aoc.h" | ||
| 25 | #include "core/hle/service/ndm_u.h" | 29 | #include "core/hle/service/ndm_u.h" |
| 26 | #include "core/hle/service/nwm_uds.h" | 30 | #include "core/hle/service/nwm_uds.h" |
| 27 | #include "core/hle/service/pm_app.h" | 31 | #include "core/hle/service/pm_app.h" |
| @@ -84,21 +88,25 @@ void Init() { | |||
| 84 | 88 | ||
| 85 | g_manager->AddService(new SRV::Interface); | 89 | g_manager->AddService(new SRV::Interface); |
| 86 | g_manager->AddService(new AC_U::Interface); | 90 | g_manager->AddService(new AC_U::Interface); |
| 91 | g_manager->AddService(new AM_APP::Interface); | ||
| 87 | g_manager->AddService(new AM_NET::Interface); | 92 | g_manager->AddService(new AM_NET::Interface); |
| 88 | g_manager->AddService(new APT_U::Interface); | 93 | g_manager->AddService(new APT_U::Interface); |
| 89 | g_manager->AddService(new BOSS_U::Interface); | 94 | g_manager->AddService(new BOSS_U::Interface); |
| 95 | g_manager->AddService(new CECD_U::Interface); | ||
| 90 | g_manager->AddService(new CFG_I::Interface); | 96 | g_manager->AddService(new CFG_I::Interface); |
| 91 | g_manager->AddService(new CFG_U::Interface); | 97 | g_manager->AddService(new CFG_U::Interface); |
| 92 | g_manager->AddService(new CSND_SND::Interface); | 98 | g_manager->AddService(new CSND_SND::Interface); |
| 93 | g_manager->AddService(new DSP_DSP::Interface); | 99 | g_manager->AddService(new DSP_DSP::Interface); |
| 94 | g_manager->AddService(new ERR_F::Interface); | 100 | g_manager->AddService(new ERR_F::Interface); |
| 95 | g_manager->AddService(new FRD_U::Interface); | 101 | g_manager->AddService(new FRD_U::Interface); |
| 96 | g_manager->AddService(new FS_User::Interface); | 102 | g_manager->AddService(new FS::FSUserInterface); |
| 97 | g_manager->AddService(new GSP_GPU::Interface); | 103 | g_manager->AddService(new GSP_GPU::Interface); |
| 98 | g_manager->AddService(new HID_User::Interface); | 104 | g_manager->AddService(new HID_User::Interface); |
| 99 | g_manager->AddService(new IR_RST::Interface); | 105 | g_manager->AddService(new IR_RST::Interface); |
| 100 | g_manager->AddService(new IR_U::Interface); | 106 | g_manager->AddService(new IR_U::Interface); |
| 107 | g_manager->AddService(new LDR_RO::Interface); | ||
| 101 | g_manager->AddService(new MIC_U::Interface); | 108 | g_manager->AddService(new MIC_U::Interface); |
| 109 | g_manager->AddService(new NIM_AOC::Interface); | ||
| 102 | g_manager->AddService(new NDM_U::Interface); | 110 | g_manager->AddService(new NDM_U::Interface); |
| 103 | g_manager->AddService(new NWM_UDS::Interface); | 111 | g_manager->AddService(new NWM_UDS::Interface); |
| 104 | g_manager->AddService(new PM_APP::Interface); | 112 | g_manager->AddService(new PM_APP::Interface); |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index baae910a1..0616822fa 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/mem_map.h" | 14 | #include "core/mem_map.h" |
| 15 | 15 | ||
| 16 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| 17 | #include "core/hle/kernel/session.h" | ||
| 17 | #include "core/hle/svc.h" | 18 | #include "core/hle/svc.h" |
| 18 | 19 | ||
| 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -21,30 +22,19 @@ | |||
| 21 | 22 | ||
| 22 | namespace Service { | 23 | namespace Service { |
| 23 | 24 | ||
| 24 | 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) |
| 25 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Returns a pointer to the command buffer in kernel memory | ||
| 29 | * @param offset Optional offset into command buffer | ||
| 30 | * @return Pointer to command buffer | ||
| 31 | */ | ||
| 32 | inline static u32* GetCommandBuffer(const int offset=0) { | ||
| 33 | return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||
| 34 | } | ||
| 35 | 26 | ||
| 36 | class Manager; | 27 | class Manager; |
| 37 | 28 | ||
| 38 | /// Interface to a CTROS service | 29 | /// Interface to a CTROS service |
| 39 | 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 | |||
| 40 | friend class Manager; | 35 | friend class Manager; |
| 41 | public: | 36 | public: |
| 42 | |||
| 43 | std::string GetName() const override { return GetPortName(); } | 37 | std::string GetName() const override { return GetPortName(); } |
| 44 | std::string GetTypeName() const override { return GetPortName(); } | ||
| 45 | |||
| 46 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } | ||
| 47 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; } | ||
| 48 | 38 | ||
| 49 | typedef void (*Function)(Interface*); | 39 | typedef void (*Function)(Interface*); |
| 50 | 40 | ||
| @@ -77,7 +67,7 @@ public: | |||
| 77 | } | 67 | } |
| 78 | 68 | ||
| 79 | ResultVal<bool> SyncRequest() override { | 69 | ResultVal<bool> SyncRequest() override { |
| 80 | u32* cmd_buff = GetCommandBuffer(); | 70 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 81 | auto itr = m_functions.find(cmd_buff[0]); | 71 | auto itr = m_functions.find(cmd_buff[0]); |
| 82 | 72 | ||
| 83 | if (itr == m_functions.end() || itr->second.func == nullptr) { | 73 | if (itr == m_functions.end() || itr->second.func == nullptr) { |
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 2f8910468..03deabe43 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index d5590a683..5c9623730 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 24a846533..05ff1846b 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/hle.h" | 5 | #include "core/hle/hle.h" |
| @@ -16,7 +16,7 @@ static Handle g_event_handle = 0; | |||
| 16 | static void Initialize(Service::Interface* self) { | 16 | static void Initialize(Service::Interface* self) { |
| 17 | LOG_DEBUG(Service_SRV, "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 | } |
| @@ -24,7 +24,7 @@ static void Initialize(Service::Interface* self) { | |||
| 24 | static void GetProcSemaphore(Service::Interface* self) { | 24 | static void GetProcSemaphore(Service::Interface* self) { |
| 25 | LOG_TRACE(Service_SRV, "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"); |
| @@ -36,7 +36,7 @@ static void GetProcSemaphore(Service::Interface* self) { | |||
| 36 | 36 | ||
| 37 | static void GetServiceHandle(Service::Interface* self) { | 37 | static void GetServiceHandle(Service::Interface* self) { |
| 38 | ResultCode res = RESULT_SUCCESS; | 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); |
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 6d5fe5048..4f3e01aca 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/service/service.h" | 5 | #include "core/hle/service/service.h" |
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index 4aa660ecc..d5b0c4b06 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
diff --git a/src/core/hle/service/ssl_c.h b/src/core/hle/service/ssl_c.h index 7b4e7fd8a..6281503a5 100644 --- a/src/core/hle/service/ssl_c.h +++ b/src/core/hle/service/ssl_c.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index f3595096e..c98168e51 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | 5 | #include <map> |
| @@ -64,6 +64,10 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other | |||
| 64 | case Kernel::MemoryPermission::Read: | 64 | case Kernel::MemoryPermission::Read: |
| 65 | case Kernel::MemoryPermission::Write: | 65 | case Kernel::MemoryPermission::Write: |
| 66 | case Kernel::MemoryPermission::ReadWrite: | 66 | case Kernel::MemoryPermission::ReadWrite: |
| 67 | case Kernel::MemoryPermission::Execute: | ||
| 68 | case Kernel::MemoryPermission::ReadExecute: | ||
| 69 | case Kernel::MemoryPermission::WriteExecute: | ||
| 70 | case Kernel::MemoryPermission::ReadWriteExecute: | ||
| 67 | case Kernel::MemoryPermission::DontCare: | 71 | case Kernel::MemoryPermission::DontCare: |
| 68 | Kernel::MapSharedMemory(handle, addr, permissions_type, | 72 | Kernel::MapSharedMemory(handle, addr, permissions_type, |
| 69 | static_cast<Kernel::MemoryPermission>(other_permissions)); | 73 | static_cast<Kernel::MemoryPermission>(other_permissions)); |
| @@ -88,17 +92,14 @@ static Result ConnectToPort(Handle* out, const char* port_name) { | |||
| 88 | 92 | ||
| 89 | /// Synchronize to an OS service | 93 | /// Synchronize to an OS service |
| 90 | static Result SendSyncRequest(Handle handle) { | 94 | static Result SendSyncRequest(Handle handle) { |
| 91 | // TODO(yuriks): ObjectPool::Get tries to check the Object type, which fails since this is a generic base Object, | 95 | Kernel::Session* session = Kernel::g_object_pool.Get<Kernel::Session>(handle); |
| 92 | // so we are forced to use GetFast and manually verify the handle. | 96 | if (session == nullptr) { |
| 93 | if (!Kernel::g_object_pool.IsValid(handle)) { | ||
| 94 | return InvalidHandle(ErrorModule::Kernel).raw; | 97 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 95 | } | 98 | } |
| 96 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); | ||
| 97 | 99 | ||
| 98 | _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()); |
| 99 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str()); | ||
| 100 | 101 | ||
| 101 | ResultVal<bool> wait = object->SyncRequest(); | 102 | ResultVal<bool> wait = session->SyncRequest(); |
| 102 | if (wait.Succeeded() && *wait) { | 103 | if (wait.Succeeded() && *wait) { |
| 103 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? | 104 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? |
| 104 | } | 105 | } |
| @@ -351,7 +352,8 @@ static Result ClearEvent(Handle evt) { | |||
| 351 | static void SleepThread(s64 nanoseconds) { | 352 | static void SleepThread(s64 nanoseconds) { |
| 352 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | 353 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| 353 | 354 | ||
| 354 | // Check for next thread to schedule | 355 | // Sleep current thread and check for next thread to schedule |
| 356 | Kernel::WaitCurrentThread(WAITTYPE_SLEEP); | ||
| 355 | HLE::Reschedule(__func__); | 357 | HLE::Reschedule(__func__); |
| 356 | } | 358 | } |
| 357 | 359 | ||
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index 6be393d0b..ad780818e 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index da78b85e5..67a8bc324 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 86cd5e680..68f11bfcb 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index af42b41fb..848ab5348 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h index 1055ed94f..991c0a07d 100644 --- a/src/core/hw/hw.h +++ b/src/core/hw/hw.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index f48d13530..4d072871a 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "core/file_sys/archive_romfs.h" | 8 | #include "core/file_sys/archive_romfs.h" |
| 9 | #include "core/loader/elf.h" | 9 | #include "core/loader/elf.h" |
| 10 | #include "core/loader/ncch.h" | 10 | #include "core/loader/ncch.h" |
| 11 | #include "core/hle/kernel/archive.h" | 11 | #include "core/hle/service/fs/archive.h" |
| 12 | #include "core/mem_map.h" | 12 | #include "core/mem_map.h" |
| 13 | 13 | ||
| 14 | #include "3dsx.h" | 14 | #include "3dsx.h" |
| @@ -223,9 +223,7 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | |||
| 223 | LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); | 223 | LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); |
| 224 | FileUtil::IOFile file(filename, "rb"); | 224 | FileUtil::IOFile file(filename, "rb"); |
| 225 | if (file.IsOpen()) { | 225 | if (file.IsOpen()) { |
| 226 | 226 | THREEDSXReader::Load3DSXFile(filename, 0x00100000); | |
| 227 | THREEDSXReader reader; | ||
| 228 | reader.Load3DSXFile(filename, 0x00100000); | ||
| 229 | Kernel::LoadExec(0x00100000); | 227 | Kernel::LoadExec(0x00100000); |
| 230 | } else { | 228 | } else { |
| 231 | return ResultStatus::Error; | 229 | return ResultStatus::Error; |
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 848d3ef8a..da8836662 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project / Citra Emulator Project | 1 | // Copyright 2014 Dolphin Emulator Project / Citra Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index c95882f4a..354335014 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <string> | 5 | #include <string> |
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 5ae88439a..c221cce6d 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / Citra Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 3883e1307..87580cb2a 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -1,14 +1,16 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <string> |
| 6 | |||
| 7 | #include "common/make_unique.h" | ||
| 6 | 8 | ||
| 7 | #include "core/file_sys/archive_romfs.h" | 9 | #include "core/file_sys/archive_romfs.h" |
| 8 | #include "core/loader/3dsx.h" | 10 | #include "core/loader/3dsx.h" |
| 9 | #include "core/loader/elf.h" | 11 | #include "core/loader/elf.h" |
| 10 | #include "core/loader/ncch.h" | 12 | #include "core/loader/ncch.h" |
| 11 | #include "core/hle/kernel/archive.h" | 13 | #include "core/hle/service/fs/archive.h" |
| 12 | #include "core/mem_map.h" | 14 | #include "core/mem_map.h" |
| 13 | 15 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -74,7 +76,8 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 74 | 76 | ||
| 75 | // Load application and RomFS | 77 | // Load application and RomFS |
| 76 | if (ResultStatus::Success == app_loader.Load()) { | 78 | if (ResultStatus::Success == app_loader.Load()) { |
| 77 | Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS"); | 79 | Kernel::g_program_id = app_loader.GetProgramId(); |
| 80 | Service::FS::CreateArchive(Common::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); | ||
| 78 | return ResultStatus::Success; | 81 | return ResultStatus::Success; |
| 79 | } | 82 | } |
| 80 | break; | 83 | break; |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 0f836d285..ec5534d41 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index ba9ba00c0..0dc21699e 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| @@ -315,4 +315,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | |||
| 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 03116add8..fd9258970 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -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 | /** |
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index d1c44ed24..eea6c5bf1 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
diff --git a/src/core/mem_map.h b/src/core/mem_map.h index 7b750f848..e63e81a4b 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index 7f7e77233..0f378eaee 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | 5 | #include <map> |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index c486f6274..8a14f75aa 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "settings.h" | 5 | #include "settings.h" |
diff --git a/src/core/settings.h b/src/core/settings.h index 138ffc615..4808872ae 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/core/system.cpp b/src/core/system.cpp index 43d0eef2c..d6188f055 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| @@ -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 2bc2edc75..05d836530 100644 --- a/src/core/system.h +++ b/src/core/system.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp index 632fb959a..0bcd0b895 100644 --- a/src/video_core/clipper.cpp +++ b/src/video_core/clipper.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
diff --git a/src/video_core/clipper.h b/src/video_core/clipper.h index 14d31ca1e..19ce8e140 100644 --- a/src/video_core/clipper.h +++ b/src/video_core/clipper.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index b74cd3261..2083357fe 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "clipper.h" | 5 | #include "clipper.h" |
| @@ -56,10 +56,11 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 56 | g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); | 56 | g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); |
| 57 | 57 | ||
| 58 | const auto& attribute_config = registers.vertex_attributes; | 58 | const auto& attribute_config = registers.vertex_attributes; |
| 59 | const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress()); | 59 | const u32 base_address = attribute_config.GetPhysicalBaseAddress(); |
| 60 | 60 | ||
| 61 | // Information about internal vertex attributes | 61 | // Information about internal vertex attributes |
| 62 | const u8* vertex_attribute_sources[16]; | 62 | u32 vertex_attribute_sources[16]; |
| 63 | std::fill(vertex_attribute_sources, &vertex_attribute_sources[16], 0xdeadbeef); | ||
| 63 | u32 vertex_attribute_strides[16]; | 64 | u32 vertex_attribute_strides[16]; |
| 64 | u32 vertex_attribute_formats[16]; | 65 | u32 vertex_attribute_formats[16]; |
| 65 | u32 vertex_attribute_elements[16]; | 66 | u32 vertex_attribute_elements[16]; |
| @@ -69,7 +70,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 69 | for (int loader = 0; loader < 12; ++loader) { | 70 | for (int loader = 0; loader < 12; ++loader) { |
| 70 | const auto& loader_config = attribute_config.attribute_loaders[loader]; | 71 | const auto& loader_config = attribute_config.attribute_loaders[loader]; |
| 71 | 72 | ||
| 72 | const u8* load_address = base_address + loader_config.data_offset; | 73 | u32 load_address = base_address + loader_config.data_offset; |
| 73 | 74 | ||
| 74 | // TODO: What happens if a loader overwrites a previous one's data? | 75 | // TODO: What happens if a loader overwrites a previous one's data? |
| 75 | for (unsigned component = 0; component < loader_config.component_count; ++component) { | 76 | for (unsigned component = 0; component < loader_config.component_count; ++component) { |
| @@ -87,9 +88,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 87 | bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed)); | 88 | bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed)); |
| 88 | 89 | ||
| 89 | const auto& index_info = registers.index_array; | 90 | const auto& index_info = registers.index_array; |
| 90 | const u8* index_address_8 = (u8*)base_address + index_info.offset; | 91 | const u8* index_address_8 = Memory::GetPointer(PAddrToVAddr(base_address + index_info.offset)); |
| 91 | const u16* index_address_16 = (u16*)index_address_8; | 92 | const u16* index_address_16 = (u16*)index_address_8; |
| 92 | bool index_u16 = (bool)index_info.format; | 93 | bool index_u16 = index_info.format != 0; |
| 93 | 94 | ||
| 94 | DebugUtils::GeometryDumper geometry_dumper; | 95 | DebugUtils::GeometryDumper geometry_dumper; |
| 95 | PrimitiveAssembler<VertexShader::OutputVertex> clipper_primitive_assembler(registers.triangle_topology.Value()); | 96 | PrimitiveAssembler<VertexShader::OutputVertex> clipper_primitive_assembler(registers.triangle_topology.Value()); |
| @@ -108,7 +109,14 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 108 | 109 | ||
| 109 | for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) { | 110 | for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) { |
| 110 | for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { | 111 | for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { |
| 111 | const u8* srcdata = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]; | 112 | const u8* srcdata = Memory::GetPointer(PAddrToVAddr(vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i])); |
| 113 | |||
| 114 | // TODO(neobrain): Ocarina of Time 3D has GetNumTotalAttributes return 8, | ||
| 115 | // yet only provides 2 valid source data addresses. Need to figure out | ||
| 116 | // what's wrong there, until then we just continue when address lookup fails | ||
| 117 | if (srcdata == nullptr) | ||
| 118 | continue; | ||
| 119 | |||
| 112 | const float srcval = (vertex_attribute_formats[i] == 0) ? *(s8*)srcdata : | 120 | const float srcval = (vertex_attribute_formats[i] == 0) ? *(s8*)srcdata : |
| 113 | (vertex_attribute_formats[i] == 1) ? *(u8*)srcdata : | 121 | (vertex_attribute_formats[i] == 1) ? *(u8*)srcdata : |
| 114 | (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : | 122 | (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : |
| @@ -116,13 +124,16 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 116 | input.attr[i][comp] = float24::FromFloat32(srcval); | 124 | input.attr[i][comp] = float24::FromFloat32(srcval); |
| 117 | LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", | 125 | LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", |
| 118 | comp, i, vertex, index, | 126 | comp, i, vertex, index, |
| 119 | attribute_config.GetBaseAddress(), | 127 | attribute_config.GetPhysicalBaseAddress(), |
| 120 | vertex_attribute_sources[i] - base_address, | 128 | vertex_attribute_sources[i] - base_address, |
| 121 | srcdata - vertex_attribute_sources[i], | 129 | vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i], |
| 122 | input.attr[i][comp].ToFloat32()); | 130 | input.attr[i][comp].ToFloat32()); |
| 123 | } | 131 | } |
| 124 | } | 132 | } |
| 125 | 133 | ||
| 134 | if (g_debug_context) | ||
| 135 | g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input); | ||
| 136 | |||
| 126 | // NOTE: When dumping geometry, we simply assume that the first input attribute | 137 | // NOTE: When dumping geometry, we simply assume that the first input attribute |
| 127 | // corresponds to the position for now. | 138 | // corresponds to the position for now. |
| 128 | DebugUtils::GeometryDumper::Vertex dumped_vertex = { | 139 | DebugUtils::GeometryDumper::Vertex dumped_vertex = { |
| @@ -151,6 +162,12 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 151 | break; | 162 | break; |
| 152 | } | 163 | } |
| 153 | 164 | ||
| 165 | case PICA_REG_INDEX(vs_bool_uniforms): | ||
| 166 | for (unsigned i = 0; i < 16; ++i) | ||
| 167 | VertexShader::GetBoolUniform(i) = (registers.vs_bool_uniforms.Value() & (1 << i)) != 0; | ||
| 168 | |||
| 169 | break; | ||
| 170 | |||
| 154 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[0], 0x2c1): | 171 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[0], 0x2c1): |
| 155 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[1], 0x2c2): | 172 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[1], 0x2c2): |
| 156 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[2], 0x2c3): | 173 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[2], 0x2c3): |
diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h index 955f9daec..bb3d4150f 100644 --- a/src/video_core/command_processor.h +++ b/src/video_core/command_processor.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 1a20f19ec..328386b7e 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include <png.h> | 14 | #include <png.h> |
| 15 | #endif | 15 | #endif |
| 16 | 16 | ||
| 17 | #include <nihstro/shader_binary.h> | ||
| 18 | |||
| 17 | #include "common/log.h" | 19 | #include "common/log.h" |
| 18 | #include "common/file_util.h" | 20 | #include "common/file_util.h" |
| 19 | 21 | ||
| @@ -22,6 +24,10 @@ | |||
| 22 | 24 | ||
| 23 | #include "debug_utils.h" | 25 | #include "debug_utils.h" |
| 24 | 26 | ||
| 27 | using nihstro::DVLBHeader; | ||
| 28 | using nihstro::DVLEHeader; | ||
| 29 | using nihstro::DVLPHeader; | ||
| 30 | |||
| 25 | namespace Pica { | 31 | namespace Pica { |
| 26 | 32 | ||
| 27 | void DebugContext::OnEvent(Event event, void* data) { | 33 | void DebugContext::OnEvent(Event event, void* data) { |
| @@ -98,65 +104,6 @@ void GeometryDumper::Dump() { | |||
| 98 | } | 104 | } |
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | #pragma pack(1) | ||
| 102 | struct DVLBHeader { | ||
| 103 | enum : u32 { | ||
| 104 | MAGIC_WORD = 0x424C5644, // "DVLB" | ||
| 105 | }; | ||
| 106 | |||
| 107 | u32 magic_word; | ||
| 108 | u32 num_programs; | ||
| 109 | // u32 dvle_offset_table[]; | ||
| 110 | }; | ||
| 111 | static_assert(sizeof(DVLBHeader) == 0x8, "Incorrect structure size"); | ||
| 112 | |||
| 113 | struct DVLPHeader { | ||
| 114 | enum : u32 { | ||
| 115 | MAGIC_WORD = 0x504C5644, // "DVLP" | ||
| 116 | }; | ||
| 117 | |||
| 118 | u32 magic_word; | ||
| 119 | u32 version; | ||
| 120 | u32 binary_offset; // relative to DVLP start | ||
| 121 | u32 binary_size_words; | ||
| 122 | u32 swizzle_patterns_offset; | ||
| 123 | u32 swizzle_patterns_num_entries; | ||
| 124 | u32 unk2; | ||
| 125 | }; | ||
| 126 | static_assert(sizeof(DVLPHeader) == 0x1C, "Incorrect structure size"); | ||
| 127 | |||
| 128 | struct DVLEHeader { | ||
| 129 | enum : u32 { | ||
| 130 | MAGIC_WORD = 0x454c5644, // "DVLE" | ||
| 131 | }; | ||
| 132 | |||
| 133 | enum class ShaderType : u8 { | ||
| 134 | VERTEX = 0, | ||
| 135 | GEOMETRY = 1, | ||
| 136 | }; | ||
| 137 | |||
| 138 | u32 magic_word; | ||
| 139 | u16 pad1; | ||
| 140 | ShaderType type; | ||
| 141 | u8 pad2; | ||
| 142 | u32 main_offset_words; // offset within binary blob | ||
| 143 | u32 endmain_offset_words; | ||
| 144 | u32 pad3; | ||
| 145 | u32 pad4; | ||
| 146 | u32 constant_table_offset; | ||
| 147 | u32 constant_table_size; // number of entries | ||
| 148 | u32 label_table_offset; | ||
| 149 | u32 label_table_size; | ||
| 150 | u32 output_register_table_offset; | ||
| 151 | u32 output_register_table_size; | ||
| 152 | u32 uniform_table_offset; | ||
| 153 | u32 uniform_table_size; | ||
| 154 | u32 symbol_table_offset; | ||
| 155 | u32 symbol_table_size; | ||
| 156 | |||
| 157 | }; | ||
| 158 | static_assert(sizeof(DVLEHeader) == 0x40, "Incorrect structure size"); | ||
| 159 | #pragma pack() | ||
| 160 | 107 | ||
| 161 | void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, | 108 | void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, |
| 162 | u32 main_offset, const Regs::VSOutputAttributes* output_attributes) | 109 | u32 main_offset, const Regs::VSOutputAttributes* output_attributes) |
| @@ -276,8 +223,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data | |||
| 276 | dvlp.binary_size_words = binary_size; | 223 | dvlp.binary_size_words = binary_size; |
| 277 | QueueForWriting((u8*)binary_data, binary_size * sizeof(u32)); | 224 | QueueForWriting((u8*)binary_data, binary_size * sizeof(u32)); |
| 278 | 225 | ||
| 279 | dvlp.swizzle_patterns_offset = write_offset - dvlp_offset; | 226 | dvlp.swizzle_info_offset = write_offset - dvlp_offset; |
| 280 | dvlp.swizzle_patterns_num_entries = swizzle_size; | 227 | dvlp.swizzle_info_num_entries = swizzle_size; |
| 281 | u32 dummy = 0; | 228 | u32 dummy = 0; |
| 282 | for (unsigned int i = 0; i < swizzle_size; ++i) { | 229 | for (unsigned int i = 0; i < swizzle_size; ++i) { |
| 283 | QueueForWriting((u8*)&swizzle_data[i], sizeof(swizzle_data[i])); | 230 | QueueForWriting((u8*)&swizzle_data[i], sizeof(swizzle_data[i])); |
| @@ -356,10 +303,29 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() | |||
| 356 | return std::move(ret); | 303 | return std::move(ret); |
| 357 | } | 304 | } |
| 358 | 305 | ||
| 359 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { | 306 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha) { |
| 360 | _dbg_assert_(Debug_GPU, info.format == Pica::Regs::TextureFormat::RGB8); | 307 | |
| 361 | 308 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each | |
| 362 | // Cf. rasterizer code for an explanation of this algorithm. | 309 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. |
| 310 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. | ||
| 311 | // texels are laid out in a 2x2 subtile like this: | ||
| 312 | // 2 3 | ||
| 313 | // 0 1 | ||
| 314 | // | ||
| 315 | // The full 8x8 tile has the texels arranged like this: | ||
| 316 | // | ||
| 317 | // 42 43 46 47 58 59 62 63 | ||
| 318 | // 40 41 44 45 56 57 60 61 | ||
| 319 | // 34 35 38 39 50 51 54 55 | ||
| 320 | // 32 33 36 37 48 49 52 53 | ||
| 321 | // 10 11 14 15 26 27 30 31 | ||
| 322 | // 08 09 12 13 24 25 28 29 | ||
| 323 | // 02 03 06 07 18 19 22 23 | ||
| 324 | // 00 01 04 05 16 17 20 21 | ||
| 325 | |||
| 326 | // TODO(neobrain): Not sure if this swizzling pattern is used for all textures. | ||
| 327 | // To be flexible in case different but similar patterns are used, we keep this | ||
| 328 | // somewhat inefficient code around for now. | ||
| 363 | int texel_index_within_tile = 0; | 329 | int texel_index_within_tile = 0; |
| 364 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | 330 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { |
| 365 | int sub_tile_width = 1 << block_size_index; | 331 | int sub_tile_width = 1 << block_size_index; |
| @@ -376,19 +342,134 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 376 | int coarse_x = (x / block_width) * block_width; | 342 | int coarse_x = (x / block_width) * block_width; |
| 377 | int coarse_y = (y / block_height) * block_height; | 343 | int coarse_y = (y / block_height) * block_height; |
| 378 | 344 | ||
| 379 | const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3; | 345 | switch (info.format) { |
| 380 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; | 346 | case Regs::TextureFormat::RGBA8: |
| 347 | { | ||
| 348 | const u8* source_ptr = source + coarse_x * block_height * 4 + coarse_y * info.stride + texel_index_within_tile * 4; | ||
| 349 | return { source_ptr[3], source_ptr[2], source_ptr[1], disable_alpha ? (u8)255 : source_ptr[0] }; | ||
| 350 | } | ||
| 351 | |||
| 352 | case Regs::TextureFormat::RGB8: | ||
| 353 | { | ||
| 354 | const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3; | ||
| 355 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; | ||
| 356 | } | ||
| 357 | |||
| 358 | case Regs::TextureFormat::RGBA5551: | ||
| 359 | { | ||
| 360 | const u16 source_ptr = *(const u16*)(source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2); | ||
| 361 | u8 r = (source_ptr >> 11) & 0x1F; | ||
| 362 | u8 g = ((source_ptr) >> 6) & 0x1F; | ||
| 363 | u8 b = (source_ptr >> 1) & 0x1F; | ||
| 364 | u8 a = source_ptr & 1; | ||
| 365 | return Math::MakeVec<u8>((r << 3) | (r >> 2), (g << 3) | (g >> 2), (b << 3) | (b >> 2), disable_alpha ? 255 : (a * 255)); | ||
| 366 | } | ||
| 367 | |||
| 368 | case Regs::TextureFormat::RGB565: | ||
| 369 | { | ||
| 370 | const u16 source_ptr = *(const u16*)(source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2); | ||
| 371 | u8 r = (source_ptr >> 11) & 0x1F; | ||
| 372 | u8 g = ((source_ptr) >> 5) & 0x3F; | ||
| 373 | u8 b = (source_ptr) & 0x1F; | ||
| 374 | return Math::MakeVec<u8>((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2), 255); | ||
| 375 | } | ||
| 376 | |||
| 377 | case Regs::TextureFormat::RGBA4: | ||
| 378 | { | ||
| 379 | const u8* source_ptr = source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2; | ||
| 380 | u8 r = source_ptr[1] >> 4; | ||
| 381 | u8 g = source_ptr[1] & 0xFF; | ||
| 382 | u8 b = source_ptr[0] >> 4; | ||
| 383 | u8 a = source_ptr[0] & 0xFF; | ||
| 384 | r = (r << 4) | r; | ||
| 385 | g = (g << 4) | g; | ||
| 386 | b = (b << 4) | b; | ||
| 387 | a = (a << 4) | a; | ||
| 388 | return { r, g, b, disable_alpha ? (u8)255 : a }; | ||
| 389 | } | ||
| 390 | |||
| 391 | case Regs::TextureFormat::IA8: | ||
| 392 | { | ||
| 393 | const u8* source_ptr = source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2; | ||
| 394 | |||
| 395 | // TODO: component order not verified | ||
| 396 | |||
| 397 | if (disable_alpha) { | ||
| 398 | // Show intensity as red, alpha as green | ||
| 399 | return { source_ptr[0], source_ptr[1], 0, 255 }; | ||
| 400 | } else { | ||
| 401 | return { source_ptr[0], source_ptr[0], source_ptr[0], source_ptr[1]}; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | case Regs::TextureFormat::I8: | ||
| 406 | { | ||
| 407 | const u8* source_ptr = source + coarse_x * block_height + coarse_y * info.stride + texel_index_within_tile; | ||
| 408 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | ||
| 409 | } | ||
| 410 | |||
| 411 | case Regs::TextureFormat::A8: | ||
| 412 | { | ||
| 413 | const u8* source_ptr = source + coarse_x * block_height + coarse_y * info.stride + texel_index_within_tile; | ||
| 414 | |||
| 415 | if (disable_alpha) { | ||
| 416 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | ||
| 417 | } else { | ||
| 418 | return { 0, 0, 0, *source_ptr }; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | case Regs::TextureFormat::IA4: | ||
| 423 | { | ||
| 424 | const u8* source_ptr = source + coarse_x * block_height / 2 + coarse_y * info.stride + texel_index_within_tile / 2; | ||
| 425 | |||
| 426 | // TODO: component order not verified | ||
| 427 | |||
| 428 | u8 i = (*source_ptr) & 0xF; | ||
| 429 | u8 a = ((*source_ptr) & 0xF0) >> 4; | ||
| 430 | a |= a << 4; | ||
| 431 | i |= i << 4; | ||
| 432 | |||
| 433 | if (disable_alpha) { | ||
| 434 | // Show intensity as red, alpha as green | ||
| 435 | return { i, a, 0, 255 }; | ||
| 436 | } else { | ||
| 437 | return { i, i, i, a }; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | case Regs::TextureFormat::A4: | ||
| 442 | { | ||
| 443 | const u8* source_ptr = source + coarse_x * block_height / 2 + coarse_y * info.stride + texel_index_within_tile / 2; | ||
| 444 | |||
| 445 | // TODO: component order not verified | ||
| 446 | |||
| 447 | u8 a = (coarse_x % 2) ? ((*source_ptr)&0xF) : (((*source_ptr) & 0xF0) >> 4); | ||
| 448 | a |= a << 4; | ||
| 449 | |||
| 450 | if (disable_alpha) { | ||
| 451 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | ||
| 452 | } else { | ||
| 453 | return { 0, 0, 0, *source_ptr }; | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | default: | ||
| 458 | LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format); | ||
| 459 | _dbg_assert_(HW_GPU, 0); | ||
| 460 | return {}; | ||
| 461 | } | ||
| 381 | } | 462 | } |
| 382 | 463 | ||
| 383 | TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, | 464 | TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, |
| 384 | const Regs::TextureFormat& format) | 465 | const Regs::TextureFormat& format) |
| 385 | { | 466 | { |
| 386 | TextureInfo info; | 467 | TextureInfo info; |
| 387 | info.address = config.GetPhysicalAddress(); | 468 | info.physical_address = config.GetPhysicalAddress(); |
| 388 | info.width = config.width; | 469 | info.width = config.width; |
| 389 | info.height = config.height; | 470 | info.height = config.height; |
| 390 | info.format = format; | 471 | info.format = format; |
| 391 | info.stride = Pica::Regs::BytesPerPixel(info.format) * info.width; | 472 | info.stride = Pica::Regs::NibblesPerPixel(info.format) * info.width / 2; |
| 392 | return info; | 473 | return info; |
| 393 | } | 474 | } |
| 394 | 475 | ||
| @@ -499,26 +580,32 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 499 | for (size_t index = 0; index < stages.size(); ++index) { | 580 | for (size_t index = 0; index < stages.size(); ++index) { |
| 500 | const auto& tev_stage = stages[index]; | 581 | const auto& tev_stage = stages[index]; |
| 501 | 582 | ||
| 502 | const std::map<Source, std::string> source_map = { | 583 | static const std::map<Source, std::string> source_map = { |
| 503 | { Source::PrimaryColor, "PrimaryColor" }, | 584 | { Source::PrimaryColor, "PrimaryColor" }, |
| 504 | { Source::Texture0, "Texture0" }, | 585 | { Source::Texture0, "Texture0" }, |
| 586 | { Source::Texture1, "Texture1" }, | ||
| 587 | { Source::Texture2, "Texture2" }, | ||
| 505 | { Source::Constant, "Constant" }, | 588 | { Source::Constant, "Constant" }, |
| 506 | { Source::Previous, "Previous" }, | 589 | { Source::Previous, "Previous" }, |
| 507 | }; | 590 | }; |
| 508 | 591 | ||
| 509 | const std::map<ColorModifier, std::string> color_modifier_map = { | 592 | static const std::map<ColorModifier, std::string> color_modifier_map = { |
| 510 | { ColorModifier::SourceColor, { "%source.rgb" } } | 593 | { ColorModifier::SourceColor, { "%source.rgb" } }, |
| 594 | { ColorModifier::SourceAlpha, { "%source.aaa" } }, | ||
| 511 | }; | 595 | }; |
| 512 | const std::map<AlphaModifier, std::string> alpha_modifier_map = { | 596 | static const std::map<AlphaModifier, std::string> alpha_modifier_map = { |
| 513 | { AlphaModifier::SourceAlpha, "%source.a" } | 597 | { AlphaModifier::SourceAlpha, "%source.a" }, |
| 598 | { AlphaModifier::OneMinusSourceAlpha, "(255 - %source.a)" }, | ||
| 514 | }; | 599 | }; |
| 515 | 600 | ||
| 516 | std::map<Operation, std::string> combiner_map = { | 601 | static const std::map<Operation, std::string> combiner_map = { |
| 517 | { Operation::Replace, "%source1" }, | 602 | { Operation::Replace, "%source1" }, |
| 518 | { Operation::Modulate, "(%source1 * %source2) / 255" }, | 603 | { Operation::Modulate, "(%source1 * %source2) / 255" }, |
| 604 | { Operation::Add, "(%source1 + %source2)" }, | ||
| 605 | { Operation::Lerp, "lerp(%source1, %source2, %source3)" }, | ||
| 519 | }; | 606 | }; |
| 520 | 607 | ||
| 521 | auto ReplacePattern = | 608 | static auto ReplacePattern = |
| 522 | [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string { | 609 | [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string { |
| 523 | size_t start = input.find(pattern); | 610 | size_t start = input.find(pattern); |
| 524 | if (start == std::string::npos) | 611 | if (start == std::string::npos) |
| @@ -528,8 +615,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 528 | ret.replace(start, pattern.length(), replacement); | 615 | ret.replace(start, pattern.length(), replacement); |
| 529 | return ret; | 616 | return ret; |
| 530 | }; | 617 | }; |
| 531 | auto GetColorSourceStr = | 618 | static auto GetColorSourceStr = |
| 532 | [&source_map,&color_modifier_map,&ReplacePattern](const Source& src, const ColorModifier& modifier) { | 619 | [](const Source& src, const ColorModifier& modifier) { |
| 533 | auto src_it = source_map.find(src); | 620 | auto src_it = source_map.find(src); |
| 534 | std::string src_str = "Unknown"; | 621 | std::string src_str = "Unknown"; |
| 535 | if (src_it != source_map.end()) | 622 | if (src_it != source_map.end()) |
| @@ -542,8 +629,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 542 | 629 | ||
| 543 | return ReplacePattern(modifier_str, "%source", src_str); | 630 | return ReplacePattern(modifier_str, "%source", src_str); |
| 544 | }; | 631 | }; |
| 545 | auto GetColorCombinerStr = | 632 | static auto GetColorCombinerStr = |
| 546 | [&](const Regs::TevStageConfig& tev_stage) { | 633 | [](const Regs::TevStageConfig& tev_stage) { |
| 547 | auto op_it = combiner_map.find(tev_stage.color_op); | 634 | auto op_it = combiner_map.find(tev_stage.color_op); |
| 548 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; | 635 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; |
| 549 | if (op_it != combiner_map.end()) | 636 | if (op_it != combiner_map.end()) |
| @@ -553,8 +640,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 553 | op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2)); | 640 | op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2)); |
| 554 | return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3)); | 641 | return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3)); |
| 555 | }; | 642 | }; |
| 556 | auto GetAlphaSourceStr = | 643 | static auto GetAlphaSourceStr = |
| 557 | [&source_map,&alpha_modifier_map,&ReplacePattern](const Source& src, const AlphaModifier& modifier) { | 644 | [](const Source& src, const AlphaModifier& modifier) { |
| 558 | auto src_it = source_map.find(src); | 645 | auto src_it = source_map.find(src); |
| 559 | std::string src_str = "Unknown"; | 646 | std::string src_str = "Unknown"; |
| 560 | if (src_it != source_map.end()) | 647 | if (src_it != source_map.end()) |
| @@ -567,8 +654,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 567 | 654 | ||
| 568 | return ReplacePattern(modifier_str, "%source", src_str); | 655 | return ReplacePattern(modifier_str, "%source", src_str); |
| 569 | }; | 656 | }; |
| 570 | auto GetAlphaCombinerStr = | 657 | static auto GetAlphaCombinerStr = |
| 571 | [&](const Regs::TevStageConfig& tev_stage) { | 658 | [](const Regs::TevStageConfig& tev_stage) { |
| 572 | auto op_it = combiner_map.find(tev_stage.alpha_op); | 659 | auto op_it = combiner_map.find(tev_stage.alpha_op); |
| 573 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; | 660 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; |
| 574 | if (op_it != combiner_map.end()) | 661 | if (op_it != combiner_map.end()) |
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 51f14f12f..f361a5385 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -26,6 +26,7 @@ public: | |||
| 26 | CommandProcessed, | 26 | CommandProcessed, |
| 27 | IncomingPrimitiveBatch, | 27 | IncomingPrimitiveBatch, |
| 28 | FinishedPrimitiveBatch, | 28 | FinishedPrimitiveBatch, |
| 29 | VertexLoaded, | ||
| 29 | 30 | ||
| 30 | NumEvents | 31 | NumEvents |
| 31 | }; | 32 | }; |
| @@ -192,7 +193,7 @@ void OnPicaRegWrite(u32 id, u32 value); | |||
| 192 | std::unique_ptr<PicaTrace> FinishPicaTracing(); | 193 | std::unique_ptr<PicaTrace> FinishPicaTracing(); |
| 193 | 194 | ||
| 194 | struct TextureInfo { | 195 | struct TextureInfo { |
| 195 | unsigned int address; | 196 | PAddr physical_address; |
| 196 | int width; | 197 | int width; |
| 197 | int height; | 198 | int height; |
| 198 | int stride; | 199 | int stride; |
| @@ -202,7 +203,17 @@ struct TextureInfo { | |||
| 202 | const Pica::Regs::TextureFormat& format); | 203 | const Pica::Regs::TextureFormat& format); |
| 203 | }; | 204 | }; |
| 204 | 205 | ||
| 205 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info); | 206 | /** |
| 207 | * Lookup texel located at the given coordinates and return an RGBA vector of its color. | ||
| 208 | * @param source Source pointer to read data from | ||
| 209 | * @param s,t Texture coordinates to read from | ||
| 210 | * @param info TextureInfo object describing the texture setup | ||
| 211 | * @param disable_alpha This is used for debug widgets which use this method to display textures without providing a good way to visualize alpha by themselves. If true, this will return 255 for the alpha component, and either drop the information entirely or store it in an "unused" color channel. | ||
| 212 | * @todo Eventually we should get rid of the disable_alpha parameter. | ||
| 213 | */ | ||
| 214 | const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info, | ||
| 215 | bool disable_alpha = false); | ||
| 216 | |||
| 206 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | 217 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); |
| 207 | 218 | ||
| 208 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); | 219 | 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 16b1656bb..a51d49c92 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -85,7 +85,7 @@ public: | |||
| 85 | 85 | ||
| 86 | void UnregisterObserver(DebuggerObserver* observer) | 86 | void UnregisterObserver(DebuggerObserver* observer) |
| 87 | { | 87 | { |
| 88 | std::remove(observers.begin(), observers.end(), observer); | 88 | observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end()); |
| 89 | observer->observed = nullptr; | 89 | observer->observed = nullptr; |
| 90 | } | 90 | } |
| 91 | 91 | ||
diff --git a/src/video_core/math.h b/src/video_core/math.h index 83ba81235..9622e7614 100644 --- a/src/video_core/math.h +++ b/src/video_core/math.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Licensed under GPLv2 | 1 | // Licensed under GPLv2 or any later version |
| 2 | // Refer to the license.txt file included. | 2 | // Refer to the license.txt file included. |
| 3 | 3 | ||
| 4 | 4 | ||
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 4c3791ad9..89d97e4e9 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <initializer_list> | 9 | #include <initializer_list> |
| 10 | #include <map> | 10 | #include <map> |
| 11 | #include <vector> | ||
| 11 | 12 | ||
| 12 | #include "common/bit_field.h" | 13 | #include "common/bit_field.h" |
| 13 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| @@ -104,6 +105,11 @@ struct Regs { | |||
| 104 | INSERT_PADDING_WORDS(0x17); | 105 | INSERT_PADDING_WORDS(0x17); |
| 105 | 106 | ||
| 106 | struct TextureConfig { | 107 | struct TextureConfig { |
| 108 | enum WrapMode : u32 { | ||
| 109 | ClampToEdge = 0, | ||
| 110 | Repeat = 2, | ||
| 111 | }; | ||
| 112 | |||
| 107 | INSERT_PADDING_WORDS(0x1); | 113 | INSERT_PADDING_WORDS(0x1); |
| 108 | 114 | ||
| 109 | union { | 115 | union { |
| @@ -111,12 +117,17 @@ struct Regs { | |||
| 111 | BitField<16, 16, u32> width; | 117 | BitField<16, 16, u32> width; |
| 112 | }; | 118 | }; |
| 113 | 119 | ||
| 114 | INSERT_PADDING_WORDS(0x2); | 120 | union { |
| 121 | BitField< 8, 2, WrapMode> wrap_s; | ||
| 122 | BitField<11, 2, WrapMode> wrap_t; | ||
| 123 | }; | ||
| 124 | |||
| 125 | INSERT_PADDING_WORDS(0x1); | ||
| 115 | 126 | ||
| 116 | u32 address; | 127 | u32 address; |
| 117 | 128 | ||
| 118 | u32 GetPhysicalAddress() const { | 129 | u32 GetPhysicalAddress() const { |
| 119 | return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; | 130 | return DecodeAddressRegister(address); |
| 120 | } | 131 | } |
| 121 | 132 | ||
| 122 | // texture1 and texture2 store the texture format directly after the address | 133 | // texture1 and texture2 store the texture format directly after the address |
| @@ -131,36 +142,70 @@ struct Regs { | |||
| 131 | RGBA5551 = 2, | 142 | RGBA5551 = 2, |
| 132 | RGB565 = 3, | 143 | RGB565 = 3, |
| 133 | RGBA4 = 4, | 144 | RGBA4 = 4, |
| 145 | IA8 = 5, | ||
| 146 | |||
| 147 | I8 = 7, | ||
| 148 | A8 = 8, | ||
| 149 | IA4 = 9, | ||
| 134 | 150 | ||
| 151 | A4 = 11, | ||
| 135 | // TODO: Support for the other formats is not implemented, yet. | 152 | // TODO: Support for the other formats is not implemented, yet. |
| 136 | // Seems like they are luminance formats and compressed textures. | 153 | // Seems like they are luminance formats and compressed textures. |
| 137 | }; | 154 | }; |
| 138 | 155 | ||
| 139 | static unsigned BytesPerPixel(TextureFormat format) { | 156 | static unsigned NibblesPerPixel(TextureFormat format) { |
| 140 | switch (format) { | 157 | switch (format) { |
| 141 | case TextureFormat::RGBA8: | 158 | case TextureFormat::RGBA8: |
| 142 | return 4; | 159 | return 8; |
| 143 | 160 | ||
| 144 | case TextureFormat::RGB8: | 161 | case TextureFormat::RGB8: |
| 145 | return 3; | 162 | return 6; |
| 146 | 163 | ||
| 147 | case TextureFormat::RGBA5551: | 164 | case TextureFormat::RGBA5551: |
| 148 | case TextureFormat::RGB565: | 165 | case TextureFormat::RGB565: |
| 149 | case TextureFormat::RGBA4: | 166 | case TextureFormat::RGBA4: |
| 150 | return 2; | 167 | case TextureFormat::IA8: |
| 168 | return 4; | ||
| 151 | 169 | ||
| 152 | default: | 170 | case TextureFormat::A4: |
| 153 | // placeholder for yet unknown formats | ||
| 154 | return 1; | 171 | return 1; |
| 172 | |||
| 173 | case TextureFormat::I8: | ||
| 174 | case TextureFormat::A8: | ||
| 175 | case TextureFormat::IA4: | ||
| 176 | default: // placeholder for yet unknown formats | ||
| 177 | return 2; | ||
| 155 | } | 178 | } |
| 156 | } | 179 | } |
| 157 | 180 | ||
| 158 | BitField< 0, 1, u32> texturing_enable; | 181 | union { |
| 182 | BitField< 0, 1, u32> texture0_enable; | ||
| 183 | BitField< 1, 1, u32> texture1_enable; | ||
| 184 | BitField< 2, 1, u32> texture2_enable; | ||
| 185 | }; | ||
| 159 | TextureConfig texture0; | 186 | TextureConfig texture0; |
| 160 | INSERT_PADDING_WORDS(0x8); | 187 | INSERT_PADDING_WORDS(0x8); |
| 161 | BitField<0, 4, TextureFormat> texture0_format; | 188 | BitField<0, 4, TextureFormat> texture0_format; |
| 162 | 189 | INSERT_PADDING_WORDS(0x2); | |
| 163 | INSERT_PADDING_WORDS(0x31); | 190 | TextureConfig texture1; |
| 191 | BitField<0, 4, TextureFormat> texture1_format; | ||
| 192 | INSERT_PADDING_WORDS(0x2); | ||
| 193 | TextureConfig texture2; | ||
| 194 | BitField<0, 4, TextureFormat> texture2_format; | ||
| 195 | INSERT_PADDING_WORDS(0x21); | ||
| 196 | |||
| 197 | struct FullTextureConfig { | ||
| 198 | const bool enabled; | ||
| 199 | const TextureConfig config; | ||
| 200 | const TextureFormat format; | ||
| 201 | }; | ||
| 202 | const std::array<FullTextureConfig, 3> GetTextures() const { | ||
| 203 | return {{ | ||
| 204 | { texture0_enable.ToBool(), texture0, texture0_format }, | ||
| 205 | { texture1_enable.ToBool(), texture1, texture1_format }, | ||
| 206 | { texture2_enable.ToBool(), texture2, texture2_format } | ||
| 207 | }}; | ||
| 208 | } | ||
| 164 | 209 | ||
| 165 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) | 210 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) |
| 166 | struct TevStageConfig { | 211 | struct TevStageConfig { |
| @@ -282,11 +327,11 @@ struct Regs { | |||
| 282 | 327 | ||
| 283 | INSERT_PADDING_WORDS(0x1); | 328 | INSERT_PADDING_WORDS(0x1); |
| 284 | 329 | ||
| 285 | inline u32 GetColorBufferAddress() const { | 330 | inline u32 GetColorBufferPhysicalAddress() const { |
| 286 | return Memory::PhysicalToVirtualAddress(DecodeAddressRegister(color_buffer_address)); | 331 | return DecodeAddressRegister(color_buffer_address); |
| 287 | } | 332 | } |
| 288 | inline u32 GetDepthBufferAddress() const { | 333 | inline u32 GetDepthBufferPhysicalAddress() const { |
| 289 | return Memory::PhysicalToVirtualAddress(DecodeAddressRegister(depth_buffer_address)); | 334 | return DecodeAddressRegister(depth_buffer_address); |
| 290 | } | 335 | } |
| 291 | 336 | ||
| 292 | inline u32 GetWidth() const { | 337 | inline u32 GetWidth() const { |
| @@ -310,9 +355,8 @@ struct Regs { | |||
| 310 | 355 | ||
| 311 | BitField<0, 29, u32> base_address; | 356 | BitField<0, 29, u32> base_address; |
| 312 | 357 | ||
| 313 | inline u32 GetBaseAddress() const { | 358 | u32 GetPhysicalBaseAddress() const { |
| 314 | // TODO: Ugly, should fix PhysicalToVirtualAddress instead | 359 | return DecodeAddressRegister(base_address); |
| 315 | return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; | ||
| 316 | } | 360 | } |
| 317 | 361 | ||
| 318 | // Descriptor for internal vertex attributes | 362 | // Descriptor for internal vertex attributes |
| @@ -448,7 +492,11 @@ struct Regs { | |||
| 448 | 492 | ||
| 449 | BitField<8, 2, TriangleTopology> triangle_topology; | 493 | BitField<8, 2, TriangleTopology> triangle_topology; |
| 450 | 494 | ||
| 451 | INSERT_PADDING_WORDS(0x5b); | 495 | INSERT_PADDING_WORDS(0x51); |
| 496 | |||
| 497 | BitField<0, 16, u32> vs_bool_uniforms; | ||
| 498 | |||
| 499 | INSERT_PADDING_WORDS(0x9); | ||
| 452 | 500 | ||
| 453 | // Offset to shader program entry point (in words) | 501 | // Offset to shader program entry point (in words) |
| 454 | BitField<0, 16, u32> vs_main_offset; | 502 | BitField<0, 16, u32> vs_main_offset; |
| @@ -542,11 +590,11 @@ struct Regs { | |||
| 542 | static std::string GetCommandName(int index) { | 590 | static std::string GetCommandName(int index) { |
| 543 | std::map<u32, std::string> map; | 591 | std::map<u32, std::string> map; |
| 544 | 592 | ||
| 545 | Regs regs; | ||
| 546 | #define ADD_FIELD(name) \ | 593 | #define ADD_FIELD(name) \ |
| 547 | do { \ | 594 | do { \ |
| 548 | map.insert({PICA_REG_INDEX(name), #name}); \ | 595 | map.insert({PICA_REG_INDEX(name), #name}); \ |
| 549 | for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(regs.name) / 4; ++i) \ | 596 | /* TODO: change to Regs::name when VS2015 and other compilers support it */ \ |
| 597 | for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \ | ||
| 550 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ | 598 | map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ |
| 551 | } while(false) | 599 | } while(false) |
| 552 | 600 | ||
| @@ -556,9 +604,13 @@ struct Regs { | |||
| 556 | ADD_FIELD(viewport_depth_range); | 604 | ADD_FIELD(viewport_depth_range); |
| 557 | ADD_FIELD(viewport_depth_far_plane); | 605 | ADD_FIELD(viewport_depth_far_plane); |
| 558 | ADD_FIELD(viewport_corner); | 606 | ADD_FIELD(viewport_corner); |
| 559 | ADD_FIELD(texturing_enable); | 607 | ADD_FIELD(texture0_enable); |
| 560 | ADD_FIELD(texture0); | 608 | ADD_FIELD(texture0); |
| 561 | ADD_FIELD(texture0_format); | 609 | ADD_FIELD(texture0_format); |
| 610 | ADD_FIELD(texture1); | ||
| 611 | ADD_FIELD(texture1_format); | ||
| 612 | ADD_FIELD(texture2); | ||
| 613 | ADD_FIELD(texture2_format); | ||
| 562 | ADD_FIELD(tev_stage0); | 614 | ADD_FIELD(tev_stage0); |
| 563 | ADD_FIELD(tev_stage1); | 615 | ADD_FIELD(tev_stage1); |
| 564 | ADD_FIELD(tev_stage2); | 616 | ADD_FIELD(tev_stage2); |
| @@ -572,6 +624,7 @@ struct Regs { | |||
| 572 | ADD_FIELD(trigger_draw); | 624 | ADD_FIELD(trigger_draw); |
| 573 | ADD_FIELD(trigger_draw_indexed); | 625 | ADD_FIELD(trigger_draw_indexed); |
| 574 | ADD_FIELD(triangle_topology); | 626 | ADD_FIELD(triangle_topology); |
| 627 | ADD_FIELD(vs_bool_uniforms); | ||
| 575 | ADD_FIELD(vs_main_offset); | 628 | ADD_FIELD(vs_main_offset); |
| 576 | ADD_FIELD(vs_input_register_map); | 629 | ADD_FIELD(vs_input_register_map); |
| 577 | ADD_FIELD(vs_uniform_setup); | 630 | ADD_FIELD(vs_uniform_setup); |
| @@ -622,9 +675,13 @@ ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e); | |||
| 622 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); | 675 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); |
| 623 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); | 676 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); |
| 624 | ASSERT_REG_POSITION(viewport_corner, 0x68); | 677 | ASSERT_REG_POSITION(viewport_corner, 0x68); |
| 625 | ASSERT_REG_POSITION(texturing_enable, 0x80); | 678 | ASSERT_REG_POSITION(texture0_enable, 0x80); |
| 626 | ASSERT_REG_POSITION(texture0, 0x81); | 679 | ASSERT_REG_POSITION(texture0, 0x81); |
| 627 | ASSERT_REG_POSITION(texture0_format, 0x8e); | 680 | ASSERT_REG_POSITION(texture0_format, 0x8e); |
| 681 | ASSERT_REG_POSITION(texture1, 0x91); | ||
| 682 | ASSERT_REG_POSITION(texture1_format, 0x96); | ||
| 683 | ASSERT_REG_POSITION(texture2, 0x99); | ||
| 684 | ASSERT_REG_POSITION(texture2_format, 0x9e); | ||
| 628 | ASSERT_REG_POSITION(tev_stage0, 0xc0); | 685 | ASSERT_REG_POSITION(tev_stage0, 0xc0); |
| 629 | ASSERT_REG_POSITION(tev_stage1, 0xc8); | 686 | ASSERT_REG_POSITION(tev_stage1, 0xc8); |
| 630 | ASSERT_REG_POSITION(tev_stage2, 0xd0); | 687 | ASSERT_REG_POSITION(tev_stage2, 0xd0); |
| @@ -638,6 +695,7 @@ ASSERT_REG_POSITION(num_vertices, 0x228); | |||
| 638 | ASSERT_REG_POSITION(trigger_draw, 0x22e); | 695 | ASSERT_REG_POSITION(trigger_draw, 0x22e); |
| 639 | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); | 696 | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); |
| 640 | ASSERT_REG_POSITION(triangle_topology, 0x25e); | 697 | ASSERT_REG_POSITION(triangle_topology, 0x25e); |
| 698 | ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | ||
| 641 | ASSERT_REG_POSITION(vs_main_offset, 0x2ba); | 699 | ASSERT_REG_POSITION(vs_main_offset, 0x2ba); |
| 642 | ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); | 700 | ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); |
| 643 | ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); | 701 | ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); |
| @@ -719,6 +777,14 @@ struct float24 { | |||
| 719 | return ToFloat32() <= flt.ToFloat32(); | 777 | return ToFloat32() <= flt.ToFloat32(); |
| 720 | } | 778 | } |
| 721 | 779 | ||
| 780 | bool operator == (const float24& flt) const { | ||
| 781 | return ToFloat32() == flt.ToFloat32(); | ||
| 782 | } | ||
| 783 | |||
| 784 | bool operator != (const float24& flt) const { | ||
| 785 | return ToFloat32() != flt.ToFloat32(); | ||
| 786 | } | ||
| 787 | |||
| 722 | private: | 788 | private: |
| 723 | // Stored as a regular float, merely for convenience | 789 | // Stored as a regular float, merely for convenience |
| 724 | // TODO: Perform proper arithmetic on this! | 790 | // TODO: Perform proper arithmetic on this! |
| @@ -736,5 +802,15 @@ union CommandHeader { | |||
| 736 | BitField<31, 1, u32> group_commands; | 802 | BitField<31, 1, u32> group_commands; |
| 737 | }; | 803 | }; |
| 738 | 804 | ||
| 805 | // TODO: Ugly, should fix PhysicalToVirtualAddress instead | ||
| 806 | inline static u32 PAddrToVAddr(u32 addr) { | ||
| 807 | if (addr >= Memory::VRAM_PADDR && addr < Memory::VRAM_PADDR + Memory::VRAM_SIZE) { | ||
| 808 | return addr - Memory::VRAM_PADDR + Memory::VRAM_VADDR; | ||
| 809 | } else if (addr >= Memory::FCRAM_PADDR && addr < Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||
| 810 | return addr - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; | ||
| 811 | } else { | ||
| 812 | return 0; | ||
| 813 | } | ||
| 814 | } | ||
| 739 | 815 | ||
| 740 | } // namespace | 816 | } // namespace |
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index 102693ed9..242a07e26 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "pica.h" | 5 | #include "pica.h" |
| @@ -30,20 +30,27 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl | |||
| 30 | } | 30 | } |
| 31 | break; | 31 | break; |
| 32 | 32 | ||
| 33 | case Regs::TriangleTopology::Strip: | ||
| 33 | case Regs::TriangleTopology::Fan: | 34 | case Regs::TriangleTopology::Fan: |
| 34 | if (buffer_index == 2) { | 35 | if (strip_ready) { |
| 35 | buffer_index = 0; | 36 | // TODO: Should be "buffer[0], buffer[1], vtx" instead! |
| 36 | 37 | // Not quite sure why we need this order for things to show up properly. | |
| 37 | triangle_handler(buffer[0], buffer[1], vtx); | 38 | // Maybe a bug in the rasterizer? |
| 39 | triangle_handler(buffer[1], buffer[0], vtx); | ||
| 40 | } | ||
| 41 | buffer[buffer_index] = vtx; | ||
| 38 | 42 | ||
| 39 | buffer[1] = vtx; | 43 | if (topology == Regs::TriangleTopology::Strip) { |
| 40 | } else { | 44 | strip_ready |= (buffer_index == 1); |
| 41 | buffer[buffer_index++] = vtx; | 45 | buffer_index = !buffer_index; |
| 46 | } else if (topology == Regs::TriangleTopology::Fan) { | ||
| 47 | buffer_index = 1; | ||
| 48 | strip_ready = true; | ||
| 42 | } | 49 | } |
| 43 | break; | 50 | break; |
| 44 | 51 | ||
| 45 | default: | 52 | default: |
| 46 | LOG_ERROR(Render_Software, "Unknown triangle topology %x:", (int)topology); | 53 | LOG_ERROR(HW_GPU, "Unknown triangle topology %x:", (int)topology); |
| 47 | break; | 54 | break; |
| 48 | } | 55 | } |
| 49 | } | 56 | } |
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h index ea2e2f61e..52ff4cd89 100644 --- a/src/video_core/primitive_assembly.h +++ b/src/video_core/primitive_assembly.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -37,6 +37,7 @@ private: | |||
| 37 | 37 | ||
| 38 | int buffer_index; | 38 | int buffer_index; |
| 39 | VertexType buffer[2]; | 39 | VertexType buffer[2]; |
| 40 | bool strip_ready = false; | ||
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 42 | 43 | ||
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index b7e04a560..df1f88c79 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| @@ -18,7 +18,7 @@ namespace Pica { | |||
| 18 | namespace Rasterizer { | 18 | namespace Rasterizer { |
| 19 | 19 | ||
| 20 | static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { | 20 | static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { |
| 21 | u32* color_buffer = (u32*)Memory::GetPointer(registers.framebuffer.GetColorBufferAddress()); | 21 | u32* color_buffer = reinterpret_cast<u32*>(Memory::GetPointer(PAddrToVAddr(registers.framebuffer.GetColorBufferPhysicalAddress()))); |
| 22 | u32 value = (color.a() << 24) | (color.r() << 16) | (color.g() << 8) | color.b(); | 22 | u32 value = (color.a() << 24) | (color.r() << 16) | (color.g() << 8) | color.b(); |
| 23 | 23 | ||
| 24 | // Assuming RGBA8 format until actual framebuffer format handling is implemented | 24 | // Assuming RGBA8 format until actual framebuffer format handling is implemented |
| @@ -26,14 +26,14 @@ static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | static u32 GetDepth(int x, int y) { | 28 | static u32 GetDepth(int x, int y) { |
| 29 | u16* depth_buffer = (u16*)Memory::GetPointer(registers.framebuffer.GetDepthBufferAddress()); | 29 | u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(registers.framebuffer.GetDepthBufferPhysicalAddress()))); |
| 30 | 30 | ||
| 31 | // Assuming 16-bit depth buffer format until actual format handling is implemented | 31 | // Assuming 16-bit depth buffer format until actual format handling is implemented |
| 32 | return *(depth_buffer + x + y * registers.framebuffer.GetWidth()); | 32 | return *(depth_buffer + x + y * registers.framebuffer.GetWidth()); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | static void SetDepth(int x, int y, u16 value) { | 35 | static void SetDepth(int x, int y, u16 value) { |
| 36 | u16* depth_buffer = (u16*)Memory::GetPointer(registers.framebuffer.GetDepthBufferAddress()); | 36 | u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(registers.framebuffer.GetDepthBufferPhysicalAddress()))); |
| 37 | 37 | ||
| 38 | // Assuming 16-bit depth buffer format until actual format handling is implemented | 38 | // Assuming 16-bit depth buffer format until actual format handling is implemented |
| 39 | *(depth_buffer + x + y * registers.framebuffer.GetWidth()) = value; | 39 | *(depth_buffer + x + y * registers.framebuffer.GetWidth()) = value; |
| @@ -167,60 +167,48 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 167 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) | 167 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) |
| 168 | }; | 168 | }; |
| 169 | 169 | ||
| 170 | Math::Vec4<u8> texture_color{}; | 170 | Math::Vec2<float24> uv[3]; |
| 171 | float24 u = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u()); | 171 | uv[0].u() = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u()); |
| 172 | float24 v = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v()); | 172 | uv[0].v() = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v()); |
| 173 | if (registers.texturing_enable) { | 173 | uv[1].u() = GetInterpolatedAttribute(v0.tc1.u(), v1.tc1.u(), v2.tc1.u()); |
| 174 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each | 174 | uv[1].v() = GetInterpolatedAttribute(v0.tc1.v(), v1.tc1.v(), v2.tc1.v()); |
| 175 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. | 175 | uv[2].u() = GetInterpolatedAttribute(v0.tc2.u(), v1.tc2.u(), v2.tc2.u()); |
| 176 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. | 176 | uv[2].v() = GetInterpolatedAttribute(v0.tc2.v(), v1.tc2.v(), v2.tc2.v()); |
| 177 | // texels are laid out in a 2x2 subtile like this: | 177 | |
| 178 | // 2 3 | 178 | Math::Vec4<u8> texture_color[3]{}; |
| 179 | // 0 1 | 179 | for (int i = 0; i < 3; ++i) { |
| 180 | // | 180 | auto texture = registers.GetTextures()[i]; |
| 181 | // The full 8x8 tile has the texels arranged like this: | 181 | if (!texture.enabled) |
| 182 | // | 182 | continue; |
| 183 | // 42 43 46 47 58 59 62 63 | 183 | |
| 184 | // 40 41 44 45 56 57 60 61 | 184 | _dbg_assert_(HW_GPU, 0 != texture.config.address); |
| 185 | // 34 35 38 39 50 51 54 55 | 185 | |
| 186 | // 32 33 36 37 48 49 52 53 | 186 | int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32(); |
| 187 | // 10 11 14 15 26 27 30 31 | 187 | int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32(); |
| 188 | // 08 09 12 13 24 25 28 29 | 188 | auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) { |
| 189 | // 02 03 06 07 18 19 22 23 | 189 | switch (mode) { |
| 190 | // 00 01 04 05 16 17 20 21 | 190 | case Regs::TextureConfig::ClampToEdge: |
| 191 | 191 | val = std::max(val, 0); | |
| 192 | // TODO: This is currently hardcoded for RGB8 | 192 | val = std::min(val, (int)size - 1); |
| 193 | u32* texture_data = (u32*)Memory::GetPointer(registers.texture0.GetPhysicalAddress()); | 193 | return val; |
| 194 | 194 | ||
| 195 | // TODO(neobrain): Not sure if this swizzling pattern is used for all textures. | 195 | case Regs::TextureConfig::Repeat: |
| 196 | // To be flexible in case different but similar patterns are used, we keep this | 196 | return (int)(((unsigned)val) % size); |
| 197 | // somewhat inefficient code around for now. | 197 | |
| 198 | int s = (int)(u * float24::FromFloat32(static_cast<float>(registers.texture0.width))).ToFloat32(); | 198 | default: |
| 199 | int t = (int)(v * float24::FromFloat32(static_cast<float>(registers.texture0.height))).ToFloat32(); | 199 | LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode); |
| 200 | int texel_index_within_tile = 0; | 200 | _dbg_assert_(HW_GPU, 0); |
| 201 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | 201 | return 0; |
| 202 | int sub_tile_width = 1 << block_size_index; | 202 | } |
| 203 | int sub_tile_height = 1 << block_size_index; | 203 | }; |
| 204 | 204 | s = GetWrappedTexCoord(registers.texture0.wrap_s, s, registers.texture0.width); | |
| 205 | int sub_tile_index = (s & sub_tile_width) << block_size_index; | 205 | t = GetWrappedTexCoord(registers.texture0.wrap_t, t, registers.texture0.height); |
| 206 | sub_tile_index += 2 * ((t & sub_tile_height) << block_size_index); | 206 | |
| 207 | texel_index_within_tile += sub_tile_index; | 207 | u8* texture_data = Memory::GetPointer(PAddrToVAddr(texture.config.GetPhysicalAddress())); |
| 208 | } | 208 | auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); |
| 209 | 209 | ||
| 210 | const int block_width = 8; | 210 | texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); |
| 211 | const int block_height = 8; | 211 | DebugUtils::DumpTexture(texture.config, texture_data); |
| 212 | |||
| 213 | int coarse_s = (s / block_width) * block_width; | ||
| 214 | int coarse_t = (t / block_height) * block_height; | ||
| 215 | |||
| 216 | const int row_stride = registers.texture0.width * 3; | ||
| 217 | u8* source_ptr = (u8*)texture_data + coarse_s * block_height * 3 + coarse_t * row_stride + texel_index_within_tile * 3; | ||
| 218 | texture_color.r() = source_ptr[2]; | ||
| 219 | texture_color.g() = source_ptr[1]; | ||
| 220 | texture_color.b() = source_ptr[0]; | ||
| 221 | texture_color.a() = 0xFF; | ||
| 222 | |||
| 223 | DebugUtils::DumpTexture(registers.texture0, (u8*)texture_data); | ||
| 224 | } | 212 | } |
| 225 | 213 | ||
| 226 | // Texture environment - consists of 6 stages of color and alpha combining. | 214 | // Texture environment - consists of 6 stages of color and alpha combining. |
| @@ -237,22 +225,29 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 237 | using AlphaModifier = Regs::TevStageConfig::AlphaModifier; | 225 | using AlphaModifier = Regs::TevStageConfig::AlphaModifier; |
| 238 | using Operation = Regs::TevStageConfig::Operation; | 226 | using Operation = Regs::TevStageConfig::Operation; |
| 239 | 227 | ||
| 240 | auto GetColorSource = [&](Source source) -> Math::Vec3<u8> { | 228 | auto GetColorSource = [&](Source source) -> Math::Vec4<u8> { |
| 241 | switch (source) { | 229 | switch (source) { |
| 242 | case Source::PrimaryColor: | 230 | case Source::PrimaryColor: |
| 243 | return primary_color.rgb(); | 231 | return primary_color; |
| 244 | 232 | ||
| 245 | case Source::Texture0: | 233 | case Source::Texture0: |
| 246 | return texture_color.rgb(); | 234 | return texture_color[0]; |
| 235 | |||
| 236 | case Source::Texture1: | ||
| 237 | return texture_color[1]; | ||
| 238 | |||
| 239 | case Source::Texture2: | ||
| 240 | return texture_color[2]; | ||
| 247 | 241 | ||
| 248 | case Source::Constant: | 242 | case Source::Constant: |
| 249 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b}; | 243 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b, tev_stage.const_a}; |
| 250 | 244 | ||
| 251 | case Source::Previous: | 245 | case Source::Previous: |
| 252 | return combiner_output.rgb(); | 246 | return combiner_output; |
| 253 | 247 | ||
| 254 | default: | 248 | default: |
| 255 | LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); | 249 | LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); |
| 250 | _dbg_assert_(HW_GPU, 0); | ||
| 256 | return {}; | 251 | return {}; |
| 257 | } | 252 | } |
| 258 | }; | 253 | }; |
| @@ -263,7 +258,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 263 | return primary_color.a(); | 258 | return primary_color.a(); |
| 264 | 259 | ||
| 265 | case Source::Texture0: | 260 | case Source::Texture0: |
| 266 | return texture_color.a(); | 261 | return texture_color[0].a(); |
| 262 | |||
| 263 | case Source::Texture1: | ||
| 264 | return texture_color[1].a(); | ||
| 265 | |||
| 266 | case Source::Texture2: | ||
| 267 | return texture_color[2].a(); | ||
| 267 | 268 | ||
| 268 | case Source::Constant: | 269 | case Source::Constant: |
| 269 | return tev_stage.const_a; | 270 | return tev_stage.const_a; |
| @@ -273,17 +274,23 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 273 | 274 | ||
| 274 | default: | 275 | default: |
| 275 | LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); | 276 | LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); |
| 277 | _dbg_assert_(HW_GPU, 0); | ||
| 276 | return 0; | 278 | return 0; |
| 277 | } | 279 | } |
| 278 | }; | 280 | }; |
| 279 | 281 | ||
| 280 | auto GetColorModifier = [](ColorModifier factor, const Math::Vec3<u8>& values) -> Math::Vec3<u8> { | 282 | auto GetColorModifier = [](ColorModifier factor, const Math::Vec4<u8>& values) -> Math::Vec3<u8> { |
| 281 | switch (factor) | 283 | switch (factor) |
| 282 | { | 284 | { |
| 283 | case ColorModifier::SourceColor: | 285 | case ColorModifier::SourceColor: |
| 284 | return values; | 286 | return values.rgb(); |
| 287 | |||
| 288 | case ColorModifier::SourceAlpha: | ||
| 289 | return { values.a(), values.a(), values.a() }; | ||
| 290 | |||
| 285 | default: | 291 | default: |
| 286 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); | 292 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); |
| 293 | _dbg_assert_(HW_GPU, 0); | ||
| 287 | return {}; | 294 | return {}; |
| 288 | } | 295 | } |
| 289 | }; | 296 | }; |
| @@ -292,8 +299,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 292 | switch (factor) { | 299 | switch (factor) { |
| 293 | case AlphaModifier::SourceAlpha: | 300 | case AlphaModifier::SourceAlpha: |
| 294 | return value; | 301 | return value; |
| 302 | |||
| 303 | case AlphaModifier::OneMinusSourceAlpha: | ||
| 304 | return 255 - value; | ||
| 305 | |||
| 295 | default: | 306 | default: |
| 296 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); | 307 | LOG_ERROR(HW_GPU, "Unknown alpha factor %d\n", (int)factor); |
| 308 | _dbg_assert_(HW_GPU, 0); | ||
| 297 | return 0; | 309 | return 0; |
| 298 | } | 310 | } |
| 299 | }; | 311 | }; |
| @@ -306,8 +318,21 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 306 | case Operation::Modulate: | 318 | case Operation::Modulate: |
| 307 | return ((input[0] * input[1]) / 255).Cast<u8>(); | 319 | return ((input[0] * input[1]) / 255).Cast<u8>(); |
| 308 | 320 | ||
| 321 | case Operation::Add: | ||
| 322 | { | ||
| 323 | auto result = input[0] + input[1]; | ||
| 324 | result.r() = std::min(255, result.r()); | ||
| 325 | result.g() = std::min(255, result.g()); | ||
| 326 | result.b() = std::min(255, result.b()); | ||
| 327 | return result.Cast<u8>(); | ||
| 328 | } | ||
| 329 | |||
| 330 | case Operation::Lerp: | ||
| 331 | return ((input[0] * input[2] + input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) / 255).Cast<u8>(); | ||
| 332 | |||
| 309 | default: | 333 | default: |
| 310 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); | 334 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); |
| 335 | _dbg_assert_(HW_GPU, 0); | ||
| 311 | return {}; | 336 | return {}; |
| 312 | } | 337 | } |
| 313 | }; | 338 | }; |
| @@ -320,8 +345,15 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 320 | case Operation::Modulate: | 345 | case Operation::Modulate: |
| 321 | return input[0] * input[1] / 255; | 346 | return input[0] * input[1] / 255; |
| 322 | 347 | ||
| 348 | case Operation::Add: | ||
| 349 | return std::min(255, input[0] + input[1]); | ||
| 350 | |||
| 351 | case Operation::Lerp: | ||
| 352 | return (input[0] * input[2] + input[1] * (255 - input[2])) / 255; | ||
| 353 | |||
| 323 | default: | 354 | default: |
| 324 | LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); | 355 | LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); |
| 356 | _dbg_assert_(HW_GPU, 0); | ||
| 325 | return 0; | 357 | return 0; |
| 326 | } | 358 | } |
| 327 | }; | 359 | }; |
diff --git a/src/video_core/rasterizer.h b/src/video_core/rasterizer.h index 500be9462..42148f8b1 100644 --- a/src/video_core/rasterizer.h +++ b/src/video_core/rasterizer.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index bce402b88..b77f29c11 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index d0f82e6cd..e982e3746 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "gl_shader_util.h" | 5 | #include "gl_shader_util.h" |
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 986cbabc0..9b93a8a0c 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h index 0f88ab802..746a37afe 100644 --- a/src/video_core/renderer_opengl/gl_shaders.h +++ b/src/video_core/renderer_opengl/gl_shaders.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e2caeeb8f..4df3a5e25 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hw/gpu.h" | 5 | #include "core/hw/gpu.h" |
| @@ -240,14 +240,14 @@ MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() { | |||
| 240 | MathUtil::Rectangle<unsigned> viewport_extent; | 240 | MathUtil::Rectangle<unsigned> viewport_extent; |
| 241 | if (window_aspect_ratio > emulation_aspect_ratio) { | 241 | if (window_aspect_ratio > emulation_aspect_ratio) { |
| 242 | // Window is narrower than the emulation content => apply borders to the top and bottom | 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); | 243 | unsigned viewport_height = static_cast<unsigned>(std::round(emulation_aspect_ratio * framebuffer_width)); |
| 244 | viewport_extent.left = 0; | 244 | viewport_extent.left = 0; |
| 245 | viewport_extent.top = (framebuffer_height - viewport_height) / 2; | 245 | viewport_extent.top = (framebuffer_height - viewport_height) / 2; |
| 246 | viewport_extent.right = viewport_extent.left + framebuffer_width; | 246 | viewport_extent.right = viewport_extent.left + framebuffer_width; |
| 247 | viewport_extent.bottom = viewport_extent.top + viewport_height; | 247 | viewport_extent.bottom = viewport_extent.top + viewport_height; |
| 248 | } else { | 248 | } else { |
| 249 | // Otherwise, apply borders to the left and right sides of the window. | 249 | // Otherwise, apply borders to the left and right sides of the window. |
| 250 | unsigned viewport_width = std::round(framebuffer_height / emulation_aspect_ratio); | 250 | unsigned viewport_width = static_cast<unsigned>(std::round(framebuffer_height / emulation_aspect_ratio)); |
| 251 | viewport_extent.left = (framebuffer_width - viewport_width) / 2; | 251 | viewport_extent.left = (framebuffer_width - viewport_width) / 2; |
| 252 | viewport_extent.top = 0; | 252 | viewport_extent.top = 0; |
| 253 | viewport_extent.right = viewport_extent.left + viewport_width; | 253 | viewport_extent.right = viewport_extent.left + viewport_width; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 7fdcec731..cf78c1e77 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/utils.cpp b/src/video_core/utils.cpp index f1156a493..c7cc93cea 100644 --- a/src/video_core/utils.cpp +++ b/src/video_core/utils.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <stdio.h> | 5 | #include <stdio.h> |
diff --git a/src/video_core/utils.h b/src/video_core/utils.h index 21380a908..63ebccbde 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index 477e78cfe..935fe66f0 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp | |||
| @@ -1,17 +1,26 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <stack> | ||
| 6 | |||
| 5 | #include <boost/range/algorithm.hpp> | 7 | #include <boost/range/algorithm.hpp> |
| 6 | 8 | ||
| 7 | #include <common/file_util.h> | 9 | #include <common/file_util.h> |
| 8 | 10 | ||
| 9 | #include <core/mem_map.h> | 11 | #include <core/mem_map.h> |
| 10 | 12 | ||
| 11 | #include "debug_utils/debug_utils.h" | 13 | #include <nihstro/shader_bytecode.h> |
| 14 | |||
| 12 | 15 | ||
| 13 | #include "pica.h" | 16 | #include "pica.h" |
| 14 | #include "vertex_shader.h" | 17 | #include "vertex_shader.h" |
| 18 | #include "debug_utils/debug_utils.h" | ||
| 19 | |||
| 20 | using nihstro::Instruction; | ||
| 21 | using nihstro::RegisterType; | ||
| 22 | using nihstro::SourceRegister; | ||
| 23 | using nihstro::SwizzlePattern; | ||
| 15 | 24 | ||
| 16 | namespace Pica { | 25 | namespace Pica { |
| 17 | 26 | ||
| @@ -19,13 +28,14 @@ namespace VertexShader { | |||
| 19 | 28 | ||
| 20 | static struct { | 29 | static struct { |
| 21 | Math::Vec4<float24> f[96]; | 30 | Math::Vec4<float24> f[96]; |
| 22 | } shader_uniforms; | ||
| 23 | 31 | ||
| 32 | std::array<bool,16> b; | ||
| 33 | } shader_uniforms; | ||
| 24 | 34 | ||
| 25 | // TODO: Not sure where the shader binary and swizzle patterns are supposed to be loaded to! | 35 | // TODO: Not sure where the shader binary and swizzle patterns are supposed to be loaded to! |
| 26 | // For now, we just keep these local arrays around. | 36 | // For now, we just keep these local arrays around. |
| 27 | static u32 shader_memory[1024]; | 37 | static std::array<u32, 1024> shader_memory; |
| 28 | static u32 swizzle_data[1024]; | 38 | static std::array<u32, 1024> swizzle_data; |
| 29 | 39 | ||
| 30 | void SubmitShaderMemoryChange(u32 addr, u32 value) | 40 | void SubmitShaderMemoryChange(u32 addr, u32 value) |
| 31 | { | 41 | { |
| @@ -42,6 +52,21 @@ Math::Vec4<float24>& GetFloatUniform(u32 index) | |||
| 42 | return shader_uniforms.f[index]; | 52 | return shader_uniforms.f[index]; |
| 43 | } | 53 | } |
| 44 | 54 | ||
| 55 | bool& GetBoolUniform(u32 index) | ||
| 56 | { | ||
| 57 | return shader_uniforms.b[index]; | ||
| 58 | } | ||
| 59 | |||
| 60 | const std::array<u32, 1024>& GetShaderBinary() | ||
| 61 | { | ||
| 62 | return shader_memory; | ||
| 63 | } | ||
| 64 | |||
| 65 | const std::array<u32, 1024>& GetSwizzlePatterns() | ||
| 66 | { | ||
| 67 | return swizzle_data; | ||
| 68 | } | ||
| 69 | |||
| 45 | struct VertexShaderState { | 70 | struct VertexShaderState { |
| 46 | u32* program_counter; | 71 | u32* program_counter; |
| 47 | 72 | ||
| @@ -49,13 +74,23 @@ struct VertexShaderState { | |||
| 49 | float24* output_register_table[7*4]; | 74 | float24* output_register_table[7*4]; |
| 50 | 75 | ||
| 51 | Math::Vec4<float24> temporary_registers[16]; | 76 | Math::Vec4<float24> temporary_registers[16]; |
| 52 | bool status_registers[2]; | 77 | bool conditional_code[2]; |
| 78 | |||
| 79 | // Two Address registers and one loop counter | ||
| 80 | // TODO: How many bits do these actually have? | ||
| 81 | s32 address_registers[3]; | ||
| 53 | 82 | ||
| 54 | enum { | 83 | enum { |
| 55 | INVALID_ADDRESS = 0xFFFFFFFF | 84 | INVALID_ADDRESS = 0xFFFFFFFF |
| 56 | }; | 85 | }; |
| 57 | u32 call_stack[8]; // TODO: What is the maximal call stack depth? | 86 | |
| 58 | u32* call_stack_pointer; | 87 | struct CallStackElement { |
| 88 | u32 final_address; | ||
| 89 | u32 return_address; | ||
| 90 | }; | ||
| 91 | |||
| 92 | // TODO: Is there a maximal size for this? | ||
| 93 | std::stack<CallStackElement> call_stack; | ||
| 59 | 94 | ||
| 60 | struct { | 95 | struct { |
| 61 | u32 max_offset; // maximum program counter ever reached | 96 | u32 max_offset; // maximum program counter ever reached |
| @@ -64,49 +99,105 @@ struct VertexShaderState { | |||
| 64 | }; | 99 | }; |
| 65 | 100 | ||
| 66 | static void ProcessShaderCode(VertexShaderState& state) { | 101 | static void ProcessShaderCode(VertexShaderState& state) { |
| 102 | |||
| 103 | // Placeholder for invalid inputs | ||
| 104 | static float24 dummy_vec4_float24[4]; | ||
| 105 | |||
| 67 | while (true) { | 106 | while (true) { |
| 68 | bool increment_pc = true; | 107 | if (!state.call_stack.empty()) { |
| 108 | if (state.program_counter - shader_memory.data() == state.call_stack.top().final_address) { | ||
| 109 | state.program_counter = &shader_memory[state.call_stack.top().return_address]; | ||
| 110 | state.call_stack.pop(); | ||
| 111 | |||
| 112 | // TODO: Is "trying again" accurate to hardware? | ||
| 113 | continue; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 69 | bool exit_loop = false; | 117 | bool exit_loop = false; |
| 70 | const Instruction& instr = *(const Instruction*)state.program_counter; | 118 | const Instruction& instr = *(const Instruction*)state.program_counter; |
| 71 | state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + (state.program_counter - shader_memory)); | ||
| 72 | |||
| 73 | const float24* src1_ = (instr.common.src1 < 0x10) ? state.input_register_table[instr.common.src1.GetIndex()] | ||
| 74 | : (instr.common.src1 < 0x20) ? &state.temporary_registers[instr.common.src1.GetIndex()].x | ||
| 75 | : (instr.common.src1 < 0x80) ? &shader_uniforms.f[instr.common.src1.GetIndex()].x | ||
| 76 | : nullptr; | ||
| 77 | const float24* src2_ = (instr.common.src2 < 0x10) ? state.input_register_table[instr.common.src2.GetIndex()] | ||
| 78 | : &state.temporary_registers[instr.common.src2.GetIndex()].x; | ||
| 79 | float24* dest = (instr.common.dest < 0x08) ? state.output_register_table[4*instr.common.dest.GetIndex()] | ||
| 80 | : (instr.common.dest < 0x10) ? nullptr | ||
| 81 | : (instr.common.dest < 0x20) ? &state.temporary_registers[instr.common.dest.GetIndex()][0] | ||
| 82 | : nullptr; | ||
| 83 | |||
| 84 | const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; | 119 | const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; |
| 85 | const bool negate_src1 = (swizzle.negate != 0); | ||
| 86 | 120 | ||
| 87 | float24 src1[4] = { | 121 | auto call = [&](VertexShaderState& state, u32 offset, u32 num_instructions, u32 return_offset) { |
| 88 | src1_[(int)swizzle.GetSelectorSrc1(0)], | 122 | state.program_counter = &shader_memory[offset] - 1; // -1 to make sure when incrementing the PC we end up at the correct offset |
| 89 | src1_[(int)swizzle.GetSelectorSrc1(1)], | 123 | state.call_stack.push({ offset + num_instructions, return_offset }); |
| 90 | src1_[(int)swizzle.GetSelectorSrc1(2)], | ||
| 91 | src1_[(int)swizzle.GetSelectorSrc1(3)], | ||
| 92 | }; | 124 | }; |
| 93 | if (negate_src1) { | 125 | u32 binary_offset = state.program_counter - shader_memory.data(); |
| 94 | src1[0] = src1[0] * float24::FromFloat32(-1); | 126 | |
| 95 | src1[1] = src1[1] * float24::FromFloat32(-1); | 127 | state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + binary_offset); |
| 96 | src1[2] = src1[2] * float24::FromFloat32(-1); | 128 | |
| 97 | src1[3] = src1[3] * float24::FromFloat32(-1); | 129 | auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { |
| 98 | } | 130 | switch (source_reg.GetRegisterType()) { |
| 99 | const float24 src2[4] = { | 131 | case RegisterType::Input: |
| 100 | src2_[(int)swizzle.GetSelectorSrc2(0)], | 132 | return state.input_register_table[source_reg.GetIndex()]; |
| 101 | src2_[(int)swizzle.GetSelectorSrc2(1)], | 133 | |
| 102 | src2_[(int)swizzle.GetSelectorSrc2(2)], | 134 | case RegisterType::Temporary: |
| 103 | src2_[(int)swizzle.GetSelectorSrc2(3)], | 135 | return &state.temporary_registers[source_reg.GetIndex()].x; |
| 136 | |||
| 137 | case RegisterType::FloatUniform: | ||
| 138 | return &shader_uniforms.f[source_reg.GetIndex()].x; | ||
| 139 | |||
| 140 | default: | ||
| 141 | return dummy_vec4_float24; | ||
| 142 | } | ||
| 104 | }; | 143 | }; |
| 105 | 144 | ||
| 106 | switch (instr.opcode) { | 145 | switch (instr.opcode.GetInfo().type) { |
| 146 | case Instruction::OpCodeType::Arithmetic: | ||
| 147 | { | ||
| 148 | bool is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed); | ||
| 149 | if (is_inverted) { | ||
| 150 | // TODO: We don't really support this properly: For instance, the address register | ||
| 151 | // offset needs to be applied to SRC2 instead, etc. | ||
| 152 | // For now, we just abort in this situation. | ||
| 153 | LOG_CRITICAL(HW_GPU, "Bad condition..."); | ||
| 154 | exit(0); | ||
| 155 | } | ||
| 156 | |||
| 157 | const int address_offset = (instr.common.address_register_index == 0) | ||
| 158 | ? 0 : state.address_registers[instr.common.address_register_index - 1]; | ||
| 159 | |||
| 160 | const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + address_offset); | ||
| 161 | const float24* src2_ = LookupSourceRegister(instr.common.GetSrc2(is_inverted)); | ||
| 162 | |||
| 163 | const bool negate_src1 = (swizzle.negate_src1 != false); | ||
| 164 | const bool negate_src2 = (swizzle.negate_src2 != false); | ||
| 165 | |||
| 166 | float24 src1[4] = { | ||
| 167 | src1_[(int)swizzle.GetSelectorSrc1(0)], | ||
| 168 | src1_[(int)swizzle.GetSelectorSrc1(1)], | ||
| 169 | src1_[(int)swizzle.GetSelectorSrc1(2)], | ||
| 170 | src1_[(int)swizzle.GetSelectorSrc1(3)], | ||
| 171 | }; | ||
| 172 | if (negate_src1) { | ||
| 173 | src1[0] = src1[0] * float24::FromFloat32(-1); | ||
| 174 | src1[1] = src1[1] * float24::FromFloat32(-1); | ||
| 175 | src1[2] = src1[2] * float24::FromFloat32(-1); | ||
| 176 | src1[3] = src1[3] * float24::FromFloat32(-1); | ||
| 177 | } | ||
| 178 | float24 src2[4] = { | ||
| 179 | src2_[(int)swizzle.GetSelectorSrc2(0)], | ||
| 180 | src2_[(int)swizzle.GetSelectorSrc2(1)], | ||
| 181 | src2_[(int)swizzle.GetSelectorSrc2(2)], | ||
| 182 | src2_[(int)swizzle.GetSelectorSrc2(3)], | ||
| 183 | }; | ||
| 184 | if (negate_src2) { | ||
| 185 | src2[0] = src2[0] * float24::FromFloat32(-1); | ||
| 186 | src2[1] = src2[1] * float24::FromFloat32(-1); | ||
| 187 | src2[2] = src2[2] * float24::FromFloat32(-1); | ||
| 188 | src2[3] = src2[3] * float24::FromFloat32(-1); | ||
| 189 | } | ||
| 190 | |||
| 191 | float24* dest = (instr.common.dest < 0x08) ? state.output_register_table[4*instr.common.dest.GetIndex()] | ||
| 192 | : (instr.common.dest < 0x10) ? dummy_vec4_float24 | ||
| 193 | : (instr.common.dest < 0x20) ? &state.temporary_registers[instr.common.dest.GetIndex()][0] | ||
| 194 | : dummy_vec4_float24; | ||
| 195 | |||
| 196 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 197 | |||
| 198 | switch (instr.opcode.EffectiveOpCode()) { | ||
| 107 | case Instruction::OpCode::ADD: | 199 | case Instruction::OpCode::ADD: |
| 108 | { | 200 | { |
| 109 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 110 | for (int i = 0; i < 4; ++i) { | 201 | for (int i = 0; i < 4; ++i) { |
| 111 | if (!swizzle.DestComponentEnabled(i)) | 202 | if (!swizzle.DestComponentEnabled(i)) |
| 112 | continue; | 203 | continue; |
| @@ -119,7 +210,6 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 119 | 210 | ||
| 120 | case Instruction::OpCode::MUL: | 211 | case Instruction::OpCode::MUL: |
| 121 | { | 212 | { |
| 122 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 123 | for (int i = 0; i < 4; ++i) { | 213 | for (int i = 0; i < 4; ++i) { |
| 124 | if (!swizzle.DestComponentEnabled(i)) | 214 | if (!swizzle.DestComponentEnabled(i)) |
| 125 | continue; | 215 | continue; |
| @@ -130,10 +220,18 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 130 | break; | 220 | break; |
| 131 | } | 221 | } |
| 132 | 222 | ||
| 223 | case Instruction::OpCode::MAX: | ||
| 224 | for (int i = 0; i < 4; ++i) { | ||
| 225 | if (!swizzle.DestComponentEnabled(i)) | ||
| 226 | continue; | ||
| 227 | |||
| 228 | dest[i] = std::max(src1[i], src2[i]); | ||
| 229 | } | ||
| 230 | break; | ||
| 231 | |||
| 133 | case Instruction::OpCode::DP3: | 232 | case Instruction::OpCode::DP3: |
| 134 | case Instruction::OpCode::DP4: | 233 | case Instruction::OpCode::DP4: |
| 135 | { | 234 | { |
| 136 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 137 | float24 dot = float24::FromFloat32(0.f); | 235 | float24 dot = float24::FromFloat32(0.f); |
| 138 | int num_components = (instr.opcode == Instruction::OpCode::DP3) ? 3 : 4; | 236 | int num_components = (instr.opcode == Instruction::OpCode::DP3) ? 3 : 4; |
| 139 | for (int i = 0; i < num_components; ++i) | 237 | for (int i = 0; i < num_components; ++i) |
| @@ -151,7 +249,6 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 151 | // Reciprocal | 249 | // Reciprocal |
| 152 | case Instruction::OpCode::RCP: | 250 | case Instruction::OpCode::RCP: |
| 153 | { | 251 | { |
| 154 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 155 | for (int i = 0; i < 4; ++i) { | 252 | for (int i = 0; i < 4; ++i) { |
| 156 | if (!swizzle.DestComponentEnabled(i)) | 253 | if (!swizzle.DestComponentEnabled(i)) |
| 157 | continue; | 254 | continue; |
| @@ -167,7 +264,6 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 167 | // Reciprocal Square Root | 264 | // Reciprocal Square Root |
| 168 | case Instruction::OpCode::RSQ: | 265 | case Instruction::OpCode::RSQ: |
| 169 | { | 266 | { |
| 170 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 171 | for (int i = 0; i < 4; ++i) { | 267 | for (int i = 0; i < 4; ++i) { |
| 172 | if (!swizzle.DestComponentEnabled(i)) | 268 | if (!swizzle.DestComponentEnabled(i)) |
| 173 | continue; | 269 | continue; |
| @@ -180,9 +276,21 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 180 | break; | 276 | break; |
| 181 | } | 277 | } |
| 182 | 278 | ||
| 279 | case Instruction::OpCode::MOVA: | ||
| 280 | { | ||
| 281 | for (int i = 0; i < 2; ++i) { | ||
| 282 | if (!swizzle.DestComponentEnabled(i)) | ||
| 283 | continue; | ||
| 284 | |||
| 285 | // TODO: Figure out how the rounding is done on hardware | ||
| 286 | state.address_registers[i] = static_cast<s32>(src1[i].ToFloat32()); | ||
| 287 | } | ||
| 288 | |||
| 289 | break; | ||
| 290 | } | ||
| 291 | |||
| 183 | case Instruction::OpCode::MOV: | 292 | case Instruction::OpCode::MOV: |
| 184 | { | 293 | { |
| 185 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 186 | for (int i = 0; i < 4; ++i) { | 294 | for (int i = 0; i < 4; ++i) { |
| 187 | if (!swizzle.DestComponentEnabled(i)) | 295 | if (!swizzle.DestComponentEnabled(i)) |
| 188 | continue; | 296 | continue; |
| @@ -192,39 +300,137 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 192 | break; | 300 | break; |
| 193 | } | 301 | } |
| 194 | 302 | ||
| 195 | case Instruction::OpCode::RET: | 303 | case Instruction::OpCode::CMP: |
| 196 | if (*state.call_stack_pointer == VertexShaderState::INVALID_ADDRESS) { | 304 | for (int i = 0; i < 2; ++i) { |
| 197 | exit_loop = true; | 305 | // TODO: Can you restrict to one compare via dest masking? |
| 198 | } else { | 306 | |
| 199 | // Jump back to call stack position, invalidate call stack entry, move up call stack pointer | 307 | auto compare_op = instr.common.compare_op; |
| 200 | state.program_counter = &shader_memory[*state.call_stack_pointer]; | 308 | auto op = (i == 0) ? compare_op.x.Value() : compare_op.y.Value(); |
| 201 | *state.call_stack_pointer-- = VertexShaderState::INVALID_ADDRESS; | 309 | |
| 310 | switch (op) { | ||
| 311 | case compare_op.Equal: | ||
| 312 | state.conditional_code[i] = (src1[i] == src2[i]); | ||
| 313 | break; | ||
| 314 | |||
| 315 | case compare_op.NotEqual: | ||
| 316 | state.conditional_code[i] = (src1[i] != src2[i]); | ||
| 317 | break; | ||
| 318 | |||
| 319 | case compare_op.LessThan: | ||
| 320 | state.conditional_code[i] = (src1[i] < src2[i]); | ||
| 321 | break; | ||
| 322 | |||
| 323 | case compare_op.LessEqual: | ||
| 324 | state.conditional_code[i] = (src1[i] <= src2[i]); | ||
| 325 | break; | ||
| 326 | |||
| 327 | case compare_op.GreaterThan: | ||
| 328 | state.conditional_code[i] = (src1[i] > src2[i]); | ||
| 329 | break; | ||
| 330 | |||
| 331 | case compare_op.GreaterEqual: | ||
| 332 | state.conditional_code[i] = (src1[i] >= src2[i]); | ||
| 333 | break; | ||
| 334 | |||
| 335 | default: | ||
| 336 | LOG_ERROR(HW_GPU, "Unknown compare mode %x", static_cast<int>(op)); | ||
| 337 | break; | ||
| 338 | } | ||
| 202 | } | 339 | } |
| 340 | break; | ||
| 203 | 341 | ||
| 342 | default: | ||
| 343 | LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x", | ||
| 344 | (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex); | ||
| 345 | _dbg_assert_(HW_GPU, 0); | ||
| 346 | break; | ||
| 347 | } | ||
| 348 | |||
| 349 | break; | ||
| 350 | } | ||
| 351 | default: | ||
| 352 | // Handle each instruction on its own | ||
| 353 | switch (instr.opcode) { | ||
| 354 | case Instruction::OpCode::END: | ||
| 355 | exit_loop = true; | ||
| 204 | break; | 356 | break; |
| 205 | 357 | ||
| 206 | case Instruction::OpCode::CALL: | 358 | case Instruction::OpCode::CALL: |
| 207 | increment_pc = false; | 359 | call(state, |
| 360 | instr.flow_control.dest_offset, | ||
| 361 | instr.flow_control.num_instructions, | ||
| 362 | binary_offset + 1); | ||
| 363 | break; | ||
| 364 | |||
| 365 | case Instruction::OpCode::NOP: | ||
| 366 | break; | ||
| 208 | 367 | ||
| 209 | _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); | 368 | case Instruction::OpCode::IFU: |
| 369 | if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) { | ||
| 370 | call(state, | ||
| 371 | binary_offset + 1, | ||
| 372 | instr.flow_control.dest_offset - binary_offset - 1, | ||
| 373 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 374 | } else { | ||
| 375 | call(state, | ||
| 376 | instr.flow_control.dest_offset, | ||
| 377 | instr.flow_control.num_instructions, | ||
| 378 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 379 | } | ||
| 210 | 380 | ||
| 211 | *++state.call_stack_pointer = state.program_counter - shader_memory; | ||
| 212 | // TODO: Does this offset refer to the beginning of shader memory? | ||
| 213 | state.program_counter = &shader_memory[instr.flow_control.offset_words]; | ||
| 214 | break; | 381 | break; |
| 215 | 382 | ||
| 216 | case Instruction::OpCode::FLS: | 383 | case Instruction::OpCode::IFC: |
| 217 | // TODO: Do whatever needs to be done here? | 384 | { |
| 385 | // TODO: Do we need to consider swizzlers here? | ||
| 386 | |||
| 387 | auto flow_control = instr.flow_control; | ||
| 388 | bool results[3] = { flow_control.refx == state.conditional_code[0], | ||
| 389 | flow_control.refy == state.conditional_code[1] }; | ||
| 390 | |||
| 391 | switch (flow_control.op) { | ||
| 392 | case flow_control.Or: | ||
| 393 | results[2] = results[0] || results[1]; | ||
| 394 | break; | ||
| 395 | |||
| 396 | case flow_control.And: | ||
| 397 | results[2] = results[0] && results[1]; | ||
| 398 | break; | ||
| 399 | |||
| 400 | case flow_control.JustX: | ||
| 401 | results[2] = results[0]; | ||
| 402 | break; | ||
| 403 | |||
| 404 | case flow_control.JustY: | ||
| 405 | results[2] = results[1]; | ||
| 406 | break; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (results[2]) { | ||
| 410 | call(state, | ||
| 411 | binary_offset + 1, | ||
| 412 | instr.flow_control.dest_offset - binary_offset - 1, | ||
| 413 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 414 | } else { | ||
| 415 | call(state, | ||
| 416 | instr.flow_control.dest_offset, | ||
| 417 | instr.flow_control.num_instructions, | ||
| 418 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 419 | } | ||
| 420 | |||
| 218 | break; | 421 | break; |
| 422 | } | ||
| 219 | 423 | ||
| 220 | default: | 424 | default: |
| 221 | LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", | 425 | LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", |
| 222 | (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex); | 426 | (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex); |
| 223 | break; | 427 | break; |
| 428 | } | ||
| 429 | |||
| 430 | break; | ||
| 224 | } | 431 | } |
| 225 | 432 | ||
| 226 | if (increment_pc) | 433 | ++state.program_counter; |
| 227 | ++state.program_counter; | ||
| 228 | 434 | ||
| 229 | if (exit_loop) | 435 | if (exit_loop) |
| 230 | break; | 436 | break; |
| @@ -275,13 +481,11 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | |||
| 275 | state.output_register_table[4*i+comp] = ((float24*)&ret) + semantics[comp]; | 481 | state.output_register_table[4*i+comp] = ((float24*)&ret) + semantics[comp]; |
| 276 | } | 482 | } |
| 277 | 483 | ||
| 278 | state.status_registers[0] = false; | 484 | state.conditional_code[0] = false; |
| 279 | state.status_registers[1] = false; | 485 | state.conditional_code[1] = false; |
| 280 | boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS); | ||
| 281 | state.call_stack_pointer = &state.call_stack[0]; | ||
| 282 | 486 | ||
| 283 | ProcessShaderCode(state); | 487 | ProcessShaderCode(state); |
| 284 | DebugUtils::DumpShader(shader_memory, state.debug.max_offset, swizzle_data, | 488 | DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(), |
| 285 | state.debug.max_opdesc_id, registers.vs_main_offset, | 489 | state.debug.max_opdesc_id, registers.vs_main_offset, |
| 286 | registers.vs_output_attributes); | 490 | registers.vs_output_attributes); |
| 287 | 491 | ||
diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index bfb6fb6e3..af3fb2a2f 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -27,15 +27,18 @@ struct OutputVertex { | |||
| 27 | Math::Vec4<float24> dummy; // quaternions (not implemented, yet) | 27 | Math::Vec4<float24> dummy; // quaternions (not implemented, yet) |
| 28 | Math::Vec4<float24> color; | 28 | Math::Vec4<float24> color; |
| 29 | Math::Vec2<float24> tc0; | 29 | Math::Vec2<float24> tc0; |
| 30 | Math::Vec2<float24> tc1; | ||
| 31 | float24 pad[6]; | ||
| 32 | Math::Vec2<float24> tc2; | ||
| 30 | 33 | ||
| 31 | // Padding for optimal alignment | 34 | // Padding for optimal alignment |
| 32 | float24 pad[14]; | 35 | float24 pad2[4]; |
| 33 | 36 | ||
| 34 | // Attributes used to store intermediate results | 37 | // Attributes used to store intermediate results |
| 35 | 38 | ||
| 36 | // position after perspective divide | 39 | // position after perspective divide |
| 37 | Math::Vec3<float24> screenpos; | 40 | Math::Vec3<float24> screenpos; |
| 38 | float24 pad2; | 41 | float24 pad3; |
| 39 | 42 | ||
| 40 | // Linear interpolation | 43 | // Linear interpolation |
| 41 | // factor: 0=this, 1=vtx | 44 | // factor: 0=this, 1=vtx |
| @@ -44,6 +47,8 @@ struct OutputVertex { | |||
| 44 | 47 | ||
| 45 | // TODO: Should perform perspective correct interpolation here... | 48 | // TODO: Should perform perspective correct interpolation here... |
| 46 | tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); | 49 | tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); |
| 50 | tc1 = tc1 * factor + vtx.tc1 * (float24::FromFloat32(1) - factor); | ||
| 51 | tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor); | ||
| 47 | 52 | ||
| 48 | screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor); | 53 | screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor); |
| 49 | 54 | ||
| @@ -61,222 +66,16 @@ struct OutputVertex { | |||
| 61 | static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); | 66 | static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); |
| 62 | static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); | 67 | static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); |
| 63 | 68 | ||
| 64 | union Instruction { | ||
| 65 | enum class OpCode : u32 { | ||
| 66 | ADD = 0x0, | ||
| 67 | DP3 = 0x1, | ||
| 68 | DP4 = 0x2, | ||
| 69 | |||
| 70 | MUL = 0x8, | ||
| 71 | |||
| 72 | MAX = 0xC, | ||
| 73 | MIN = 0xD, | ||
| 74 | RCP = 0xE, | ||
| 75 | RSQ = 0xF, | ||
| 76 | |||
| 77 | MOV = 0x13, | ||
| 78 | |||
| 79 | RET = 0x21, | ||
| 80 | FLS = 0x22, // Flush | ||
| 81 | CALL = 0x24, | ||
| 82 | }; | ||
| 83 | |||
| 84 | std::string GetOpCodeName() const { | ||
| 85 | std::map<OpCode, std::string> map = { | ||
| 86 | { OpCode::ADD, "ADD" }, | ||
| 87 | { OpCode::DP3, "DP3" }, | ||
| 88 | { OpCode::DP4, "DP4" }, | ||
| 89 | { OpCode::MUL, "MUL" }, | ||
| 90 | { OpCode::MAX, "MAX" }, | ||
| 91 | { OpCode::MIN, "MIN" }, | ||
| 92 | { OpCode::RCP, "RCP" }, | ||
| 93 | { OpCode::RSQ, "RSQ" }, | ||
| 94 | { OpCode::MOV, "MOV" }, | ||
| 95 | { OpCode::RET, "RET" }, | ||
| 96 | { OpCode::FLS, "FLS" }, | ||
| 97 | }; | ||
| 98 | auto it = map.find(opcode); | ||
| 99 | if (it == map.end()) | ||
| 100 | return "UNK"; | ||
| 101 | else | ||
| 102 | return it->second; | ||
| 103 | } | ||
| 104 | |||
| 105 | u32 hex; | ||
| 106 | |||
| 107 | BitField<0x1a, 0x6, OpCode> opcode; | ||
| 108 | |||
| 109 | // General notes: | ||
| 110 | // | ||
| 111 | // When two input registers are used, one of them uses a 5-bit index while the other | ||
| 112 | // one uses a 7-bit index. This is because at most one floating point uniform may be used | ||
| 113 | // as an input. | ||
| 114 | |||
| 115 | |||
| 116 | // Format used e.g. by arithmetic instructions and comparisons | ||
| 117 | // "src1" and "src2" specify register indices (i.e. indices referring to groups of 4 floats), | ||
| 118 | // while "dest" addresses individual floats. | ||
| 119 | union { | ||
| 120 | BitField<0x00, 0x5, u32> operand_desc_id; | ||
| 121 | |||
| 122 | template<class BitFieldType> | ||
| 123 | struct SourceRegister : BitFieldType { | ||
| 124 | enum RegisterType { | ||
| 125 | Input, | ||
| 126 | Temporary, | ||
| 127 | FloatUniform | ||
| 128 | }; | ||
| 129 | |||
| 130 | RegisterType GetRegisterType() const { | ||
| 131 | if (BitFieldType::Value() < 0x10) | ||
| 132 | return Input; | ||
| 133 | else if (BitFieldType::Value() < 0x20) | ||
| 134 | return Temporary; | ||
| 135 | else | ||
| 136 | return FloatUniform; | ||
| 137 | } | ||
| 138 | |||
| 139 | int GetIndex() const { | ||
| 140 | if (GetRegisterType() == Input) | ||
| 141 | return BitFieldType::Value(); | ||
| 142 | else if (GetRegisterType() == Temporary) | ||
| 143 | return BitFieldType::Value() - 0x10; | ||
| 144 | else // if (GetRegisterType() == FloatUniform) | ||
| 145 | return BitFieldType::Value() - 0x20; | ||
| 146 | } | ||
| 147 | |||
| 148 | std::string GetRegisterName() const { | ||
| 149 | std::map<RegisterType, std::string> type = { | ||
| 150 | { Input, "i" }, | ||
| 151 | { Temporary, "t" }, | ||
| 152 | { FloatUniform, "f" }, | ||
| 153 | }; | ||
| 154 | return type[GetRegisterType()] + std::to_string(GetIndex()); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | |||
| 158 | SourceRegister<BitField<0x07, 0x5, u32>> src2; | ||
| 159 | SourceRegister<BitField<0x0c, 0x7, u32>> src1; | ||
| 160 | |||
| 161 | struct : BitField<0x15, 0x5, u32> | ||
| 162 | { | ||
| 163 | enum RegisterType { | ||
| 164 | Output, | ||
| 165 | Temporary, | ||
| 166 | Unknown | ||
| 167 | }; | ||
| 168 | RegisterType GetRegisterType() const { | ||
| 169 | if (Value() < 0x8) | ||
| 170 | return Output; | ||
| 171 | else if (Value() < 0x10) | ||
| 172 | return Unknown; | ||
| 173 | else | ||
| 174 | return Temporary; | ||
| 175 | } | ||
| 176 | int GetIndex() const { | ||
| 177 | if (GetRegisterType() == Output) | ||
| 178 | return Value(); | ||
| 179 | else if (GetRegisterType() == Temporary) | ||
| 180 | return Value() - 0x10; | ||
| 181 | else | ||
| 182 | return Value(); | ||
| 183 | } | ||
| 184 | std::string GetRegisterName() const { | ||
| 185 | std::map<RegisterType, std::string> type = { | ||
| 186 | { Output, "o" }, | ||
| 187 | { Temporary, "t" }, | ||
| 188 | { Unknown, "u" } | ||
| 189 | }; | ||
| 190 | return type[GetRegisterType()] + std::to_string(GetIndex()); | ||
| 191 | } | ||
| 192 | } dest; | ||
| 193 | } common; | ||
| 194 | |||
| 195 | // Format used for flow control instructions ("if") | ||
| 196 | union { | ||
| 197 | BitField<0x00, 0x8, u32> num_instructions; | ||
| 198 | BitField<0x0a, 0xc, u32> offset_words; | ||
| 199 | } flow_control; | ||
| 200 | }; | ||
| 201 | static_assert(std::is_standard_layout<Instruction>::value, "Structure is not using standard layout!"); | ||
| 202 | |||
| 203 | union SwizzlePattern { | ||
| 204 | u32 hex; | ||
| 205 | |||
| 206 | enum class Selector : u32 { | ||
| 207 | x = 0, | ||
| 208 | y = 1, | ||
| 209 | z = 2, | ||
| 210 | w = 3 | ||
| 211 | }; | ||
| 212 | |||
| 213 | Selector GetSelectorSrc1(int comp) const { | ||
| 214 | Selector selectors[] = { | ||
| 215 | src1_selector_0, src1_selector_1, src1_selector_2, src1_selector_3 | ||
| 216 | }; | ||
| 217 | return selectors[comp]; | ||
| 218 | } | ||
| 219 | |||
| 220 | Selector GetSelectorSrc2(int comp) const { | ||
| 221 | Selector selectors[] = { | ||
| 222 | src2_selector_0, src2_selector_1, src2_selector_2, src2_selector_3 | ||
| 223 | }; | ||
| 224 | return selectors[comp]; | ||
| 225 | } | ||
| 226 | |||
| 227 | bool DestComponentEnabled(int i) const { | ||
| 228 | return (dest_mask & (0x8 >> i)) != 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | std::string SelectorToString(bool src2) const { | ||
| 232 | std::map<Selector, std::string> map = { | ||
| 233 | { Selector::x, "x" }, | ||
| 234 | { Selector::y, "y" }, | ||
| 235 | { Selector::z, "z" }, | ||
| 236 | { Selector::w, "w" } | ||
| 237 | }; | ||
| 238 | std::string ret; | ||
| 239 | for (int i = 0; i < 4; ++i) { | ||
| 240 | ret += map.at(src2 ? GetSelectorSrc2(i) : GetSelectorSrc1(i)); | ||
| 241 | } | ||
| 242 | return ret; | ||
| 243 | } | ||
| 244 | |||
| 245 | std::string DestMaskToString() const { | ||
| 246 | std::string ret; | ||
| 247 | for (int i = 0; i < 4; ++i) { | ||
| 248 | if (!DestComponentEnabled(i)) | ||
| 249 | ret += "_"; | ||
| 250 | else | ||
| 251 | ret += "xyzw"[i]; | ||
| 252 | } | ||
| 253 | return ret; | ||
| 254 | } | ||
| 255 | |||
| 256 | // Components of "dest" that should be written to: LSB=dest.w, MSB=dest.x | ||
| 257 | BitField< 0, 4, u32> dest_mask; | ||
| 258 | |||
| 259 | BitField< 4, 1, u32> negate; // negates src1 | ||
| 260 | |||
| 261 | BitField< 5, 2, Selector> src1_selector_3; | ||
| 262 | BitField< 7, 2, Selector> src1_selector_2; | ||
| 263 | BitField< 9, 2, Selector> src1_selector_1; | ||
| 264 | BitField<11, 2, Selector> src1_selector_0; | ||
| 265 | |||
| 266 | BitField<14, 2, Selector> src2_selector_3; | ||
| 267 | BitField<16, 2, Selector> src2_selector_2; | ||
| 268 | BitField<18, 2, Selector> src2_selector_1; | ||
| 269 | BitField<20, 2, Selector> src2_selector_0; | ||
| 270 | |||
| 271 | BitField<31, 1, u32> flag; // not sure what this means, maybe it's the sign? | ||
| 272 | }; | ||
| 273 | |||
| 274 | void SubmitShaderMemoryChange(u32 addr, u32 value); | 69 | void SubmitShaderMemoryChange(u32 addr, u32 value); |
| 275 | void SubmitSwizzleDataChange(u32 addr, u32 value); | 70 | void SubmitSwizzleDataChange(u32 addr, u32 value); |
| 276 | 71 | ||
| 277 | OutputVertex RunShader(const InputVertex& input, int num_attributes); | 72 | OutputVertex RunShader(const InputVertex& input, int num_attributes); |
| 278 | 73 | ||
| 279 | Math::Vec4<float24>& GetFloatUniform(u32 index); | 74 | Math::Vec4<float24>& GetFloatUniform(u32 index); |
| 75 | bool& GetBoolUniform(u32 index); | ||
| 76 | |||
| 77 | const std::array<u32, 1024>& GetShaderBinary(); | ||
| 78 | const std::array<u32, 1024>& GetSwizzlePatterns(); | ||
| 280 | 79 | ||
| 281 | } // namespace | 80 | } // namespace |
| 282 | 81 | ||
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 6791e4007..c9707e5f1 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 609aac513..b782f17bd 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |