summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--.travis-build.sh1
-rw-r--r--.travis-deps.sh7
-rw-r--r--.travis.yml5
-rw-r--r--CMakeLists.txt27
-rw-r--r--README.md20
m---------externals/nihstro0
-rw-r--r--src/citra/citra.cpp2
-rw-r--r--src/citra/config.cpp2
-rw-r--r--src/citra/config.h2
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp2
-rw-r--r--src/citra/emu_window/emu_window_glfw.h2
-rw-r--r--src/citra_qt/config.cpp2
-rw-r--r--src/citra_qt/config.h2
-rw-r--r--src/citra_qt/debugger/callstack.cpp4
-rw-r--r--src/citra_qt/debugger/graphics.cpp2
-rw-r--r--src/citra_qt/debugger/graphics.hxx2
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.cpp16
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.hxx2
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints_p.hxx2
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp58
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.hxx2
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp25
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.hxx2
-rw-r--r--src/citra_qt/main.cpp6
-rw-r--r--src/citra_qt/util/spinbox.cpp4
-rw-r--r--src/citra_qt/util/spinbox.hxx2
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/bit_field.h14
-rw-r--r--src/common/break_points.cpp4
-rw-r--r--src/common/break_points.h4
-rw-r--r--src/common/common.h4
-rw-r--r--src/common/common_funcs.h4
-rw-r--r--src/common/common_paths.h6
-rw-r--r--src/common/concurrent_ring_buffer.h2
-rw-r--r--src/common/cpu_detect.h4
-rw-r--r--src/common/emu_window.cpp2
-rw-r--r--src/common/emu_window.h2
-rw-r--r--src/common/file_search.cpp4
-rw-r--r--src/common/file_search.h4
-rw-r--r--src/common/file_util.cpp8
-rw-r--r--src/common/file_util.h6
-rw-r--r--src/common/hash.cpp4
-rw-r--r--src/common/hash.h4
-rw-r--r--src/common/key_map.cpp2
-rw-r--r--src/common/key_map.h2
-rw-r--r--src/common/linear_disk_cache.h4
-rw-r--r--src/common/log.h4
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/backend.h2
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/filter.h2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/common/logging/text_formatter.cpp2
-rw-r--r--src/common/logging/text_formatter.h2
-rw-r--r--src/common/make_unique.h16
-rw-r--r--src/common/math_util.cpp4
-rw-r--r--src/common/math_util.h4
-rw-r--r--src/common/memory_util.cpp4
-rw-r--r--src/common/memory_util.h4
-rw-r--r--src/common/misc.cpp4
-rw-r--r--src/common/msg_handler.cpp4
-rw-r--r--src/common/msg_handler.h4
-rw-r--r--src/common/scm_rev.h2
-rw-r--r--src/common/scope_exit.h2
-rw-r--r--src/common/string_util.cpp4
-rw-r--r--src/common/string_util.h4
-rw-r--r--src/common/symbols.cpp2
-rw-r--r--src/common/symbols.h2
-rw-r--r--src/common/thread.cpp4
-rw-r--r--src/common/thread.h4
-rw-r--r--src/common/thread_queue_list.h2
-rw-r--r--src/common/thunk.h4
-rw-r--r--src/common/timer.cpp4
-rw-r--r--src/common/timer.h4
-rw-r--r--src/core/CMakeLists.txt43
-rw-r--r--src/core/arm/arm_interface.h2
-rw-r--r--src/core/arm/disassembler/load_symbol_map.cpp2
-rw-r--r--src/core/arm/disassembler/load_symbol_map.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp2
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_dec.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp322
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_run.cpp1
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp2
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h2
-rw-r--r--src/core/arm/interpreter/armemu.cpp581
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp7
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core.h2
-rw-r--r--src/core/core_timing.cpp4
-rw-r--r--src/core/core_timing.h4
-rw-r--r--src/core/file_sys/archive_backend.h (renamed from src/core/file_sys/archive.h)85
-rw-r--r--src/core/file_sys/archive_romfs.cpp63
-rw-r--r--src/core/file_sys/archive_romfs.h57
-rw-r--r--src/core/file_sys/archive_savedata.cpp33
-rw-r--r--src/core/file_sys/archive_savedata.h31
-rw-r--r--src/core/file_sys/archive_sdmc.cpp126
-rw-r--r--src/core/file_sys/archive_sdmc.h104
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp39
-rw-r--r--src/core/file_sys/archive_systemsavedata.h33
-rw-r--r--src/core/file_sys/directory_backend.h (renamed from src/core/file_sys/directory.h)8
-rw-r--r--src/core/file_sys/directory_romfs.cpp2
-rw-r--r--src/core/file_sys/directory_romfs.h6
-rw-r--r--src/core/file_sys/directory_sdmc.cpp88
-rw-r--r--src/core/file_sys/directory_sdmc.h55
-rw-r--r--src/core/file_sys/disk_archive.cpp188
-rw-r--r--src/core/file_sys/disk_archive.h103
-rw-r--r--src/core/file_sys/file_backend.h (renamed from src/core/file_sys/file.h)13
-rw-r--r--src/core/file_sys/file_romfs.cpp21
-rw-r--r--src/core/file_sys/file_romfs.h16
-rw-r--r--src/core/file_sys/file_sdmc.cpp110
-rw-r--r--src/core/file_sys/file_sdmc.h75
-rw-r--r--src/core/hle/config_mem.cpp2
-rw-r--r--src/core/hle/config_mem.h2
-rw-r--r--src/core/hle/coprocessor.cpp2
-rw-r--r--src/core/hle/function_wrappers.h2
-rw-r--r--src/core/hle/hle.cpp8
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/address_arbiter.h2
-rw-r--r--src/core/hle/kernel/archive.cpp435
-rw-r--r--src/core/hle/kernel/event.cpp2
-rw-r--r--src/core/hle/kernel/event.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp10
-rw-r--r--src/core/hle/kernel/kernel.h36
-rw-r--r--src/core/hle/kernel/mutex.cpp2
-rw-r--r--src/core/hle/kernel/mutex.h2
-rw-r--r--src/core/hle/kernel/semaphore.cpp10
-rw-r--r--src/core/hle/kernel/semaphore.h4
-rw-r--r--src/core/hle/kernel/session.h58
-rw-r--r--src/core/hle/kernel/shared_memory.cpp2
-rw-r--r--src/core/hle/kernel/shared_memory.h16
-rw-r--r--src/core/hle/kernel/thread.cpp58
-rw-r--r--src/core/hle/kernel/thread.h6
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/ac_u.cpp4
-rw-r--r--src/core/hle/service/ac_u.h2
-rw-r--r--src/core/hle/service/am_app.cpp24
-rw-r--r--src/core/hle/service/am_app.h (renamed from src/core/hle/service/fs_user.h)12
-rw-r--r--src/core/hle/service/am_net.cpp2
-rw-r--r--src/core/hle/service/am_net.h2
-rw-r--r--src/core/hle/service/apt_u.cpp22
-rw-r--r--src/core/hle/service/apt_u.h2
-rw-r--r--src/core/hle/service/boss_u.cpp2
-rw-r--r--src/core/hle/service/boss_u.h2
-rw-r--r--src/core/hle/service/cecd_u.cpp24
-rw-r--r--src/core/hle/service/cecd_u.h27
-rw-r--r--src/core/hle/service/cfg/cfg.cpp202
-rw-r--r--src/core/hle/service/cfg/cfg.h144
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp (renamed from src/core/hle/service/cfg_i.cpp)72
-rw-r--r--src/core/hle/service/cfg/cfg_i.h (renamed from src/core/hle/service/cfg_i.h)2
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp (renamed from src/core/hle/service/cfg_u.cpp)84
-rw-r--r--src/core/hle/service/cfg/cfg_u.h (renamed from src/core/hle/service/cfg_u.h)2
-rw-r--r--src/core/hle/service/csnd_snd.cpp2
-rw-r--r--src/core/hle/service/csnd_snd.h2
-rw-r--r--src/core/hle/service/dsp_dsp.cpp14
-rw-r--r--src/core/hle/service/dsp_dsp.h2
-rw-r--r--src/core/hle/service/err_f.cpp2
-rw-r--r--src/core/hle/service/err_f.h2
-rw-r--r--src/core/hle/service/frd_u.cpp2
-rw-r--r--src/core/hle/service/frd_u.h2
-rw-r--r--src/core/hle/service/fs/archive.cpp440
-rw-r--r--src/core/hle/service/fs/archive.h (renamed from src/core/hle/kernel/archive.h)65
-rw-r--r--src/core/hle/service/fs/fs_user.cpp (renamed from src/core/hle/service/fs_user.cpp)254
-rw-r--r--src/core/hle/service/fs/fs_user.h33
-rw-r--r--src/core/hle/service/gsp_gpu.cpp38
-rw-r--r--src/core/hle/service/gsp_gpu.h2
-rw-r--r--src/core/hle/service/hid_user.cpp4
-rw-r--r--src/core/hle/service/hid_user.h2
-rw-r--r--src/core/hle/service/ir_rst.cpp2
-rw-r--r--src/core/hle/service/ir_rst.h4
-rw-r--r--src/core/hle/service/ir_u.cpp2
-rw-r--r--src/core/hle/service/ir_u.h2
-rw-r--r--src/core/hle/service/ldr_ro.cpp28
-rw-r--r--src/core/hle/service/ldr_ro.h27
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/mic_u.h2
-rw-r--r--src/core/hle/service/ndm_u.cpp2
-rw-r--r--src/core/hle/service/ndm_u.h2
-rw-r--r--src/core/hle/service/nim_aoc.cpp31
-rw-r--r--src/core/hle/service/nim_aoc.h27
-rw-r--r--src/core/hle/service/nwm_uds.cpp2
-rw-r--r--src/core/hle/service/nwm_uds.h2
-rw-r--r--src/core/hle/service/pm_app.cpp2
-rw-r--r--src/core/hle/service/pm_app.h2
-rw-r--r--src/core/hle/service/ptm_u.cpp10
-rw-r--r--src/core/hle/service/ptm_u.h2
-rw-r--r--src/core/hle/service/service.cpp18
-rw-r--r--src/core/hle/service/service.h28
-rw-r--r--src/core/hle/service/soc_u.cpp2
-rw-r--r--src/core/hle/service/soc_u.h2
-rw-r--r--src/core/hle/service/srv.cpp8
-rw-r--r--src/core/hle/service/srv.h2
-rw-r--r--src/core/hle/service/ssl_c.cpp2
-rw-r--r--src/core/hle/service/ssl_c.h2
-rw-r--r--src/core/hle/svc.cpp20
-rw-r--r--src/core/hle/svc.h2
-rw-r--r--src/core/hw/gpu.cpp2
-rw-r--r--src/core/hw/gpu.h2
-rw-r--r--src/core/hw/hw.cpp2
-rw-r--r--src/core/hw/hw.h2
-rw-r--r--src/core/loader/3dsx.cpp8
-rw-r--r--src/core/loader/3dsx.h2
-rw-r--r--src/core/loader/elf.cpp4
-rw-r--r--src/core/loader/elf.h4
-rw-r--r--src/core/loader/loader.cpp11
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/core/loader/ncch.cpp6
-rw-r--r--src/core/loader/ncch.h8
-rw-r--r--src/core/mem_map.cpp4
-rw-r--r--src/core/mem_map.h2
-rw-r--r--src/core/mem_map_funcs.cpp2
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/system.cpp14
-rw-r--r--src/core/system.h2
-rw-r--r--src/video_core/clipper.cpp2
-rw-r--r--src/video_core/clipper.h2
-rw-r--r--src/video_core/command_processor.cpp35
-rw-r--r--src/video_core/command_processor.h2
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp255
-rw-r--r--src/video_core/debug_utils/debug_utils.h15
-rw-r--r--src/video_core/gpu_debugger.h4
-rw-r--r--src/video_core/math.h2
-rw-r--r--src/video_core/pica.h124
-rw-r--r--src/video_core/primitive_assembly.cpp25
-rw-r--r--src/video_core/primitive_assembly.h3
-rw-r--r--src/video_core/rasterizer.cpp166
-rw-r--r--src/video_core/rasterizer.h2
-rw-r--r--src/video_core/renderer_base.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shaders.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h2
-rw-r--r--src/video_core/utils.cpp2
-rw-r--r--src/video_core/utils.h2
-rw-r--r--src/video_core/vertex_shader.cpp340
-rw-r--r--src/video_core/vertex_shader.h225
-rw-r--r--src/video_core/video_core.cpp2
-rw-r--r--src/video_core/video_core.h2
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
3set -e 3set -e
4set -x
4 5
5#if OS is linux or is not set 6#if OS is linux or is not set
6if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then 7if [ "$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
3set -e 3set -e
4set -x
4 5
5#if OS is linux or is not set 6#if OS is linux or is not set
6if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then 7if [ "$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
25elif [ "$TRAVIS_OS_NAME" = osx ]; then 26elif [ "$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
28fi 31fi
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
5language: cpp 5language: cpp
6 6
7cache:
8 apt: true
9 directories:
10 - .homebrew-cache
11
7before_install: 12before_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)
15endif() 34endif()
16add_definitions(-DSINGLETHREADED) 35add_definitions(-DSINGLETHREADED)
17 36
18find_package(PNG) 37find_package(PNG QUIET)
19if (PNG_FOUND) 38if (PNG_FOUND)
20 add_definitions(-DHAVE_PNG) 39 add_definitions(-DHAVE_PNG)
21endif () 40else()
41 message(STATUS "libpng not found. Some debugging features have been disabled.")
42endif()
22 43
23find_package(Boost) 44find_package(Boost)
24if (Boost_FOUND) 45if (Boost_FOUND)
@@ -138,6 +159,8 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih")
138include_directories(${INI_PREFIX}) 159include_directories(${INI_PREFIX})
139add_subdirectory(${INI_PREFIX}) 160add_subdirectory(${INI_PREFIX})
140 161
162include_directories(externals/nihstro/include)
163
141# process subdirectories 164# process subdirectories
142if(ENABLE_QT) 165if(ENABLE_QT)
143 include_directories(externals/qhexedit) 166 include_directories(externals/qhexedit)
diff --git a/README.md b/README.md
index 554553ee8..c2741cad6 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
1citra emulator 1Citra Emulator
2============== 2==============
3[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra) 3[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra)
4 4
5An 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. 5Citra 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
7For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/). 7Citra 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
9For 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
23If 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
31We 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
49TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) 49TextureInfoDockWidget::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
127void TextureInfoDockWidget::OnAddressChanged(qint64 value) { 134void 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
152QPixmap TextureInfoDockWidget::ReloadPixmap() const { 159QPixmap 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
224void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { 231void 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
167private: 177private:
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
9namespace Common {
10
11template <typename T, typename... Args>
12std::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
1022typedef 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
1022typedef struct _adc_inst { 1031typedef 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
1278typedef struct umaal_inst {
1279 unsigned int Rn;
1280 unsigned int Rm;
1281 unsigned int RdHi;
1282 unsigned int RdLo;
1283} umaal_inst;
1284
1269typedef struct _umlal_inst { 1285typedef 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}
2376ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); } 2392ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); }
2377ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD16"); }
2378ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); } 2393ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); }
2379ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADDSUBX"); } 2394ARM_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}
2412ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index)
2413{
2414 return INTERPRETER_TRANSLATE(qadd16)(inst, index);
2415}
2380ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); } 2416ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); }
2381ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); } 2417ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); }
2382ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB"); } 2418ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB"); }
2383ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB16"); }
2384ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); } 2419ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); }
2385ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUBADDX"); } 2420ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index)
2421{
2422 return INTERPRETER_TRANSLATE(qadd16)(inst, index);
2423}
2424ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index)
2425{
2426 return INTERPRETER_TRANSLATE(qadd16)(inst, index);
2427}
2386ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) 2428ARM_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}
2465ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD16"); }
2466ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); } 2507ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); }
2467ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADDSUBX"); } 2508ARM_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}
2526ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index)
2527{
2528 return INTERPRETER_TRANSLATE(sadd16)(inst, index);
2529}
2468ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) 2530ARM_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}
2492ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SEL"); } 2554ARM_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}
2493ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); } 2572ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); }
2494ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); } 2573ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); }
2495ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); } 2574ARM_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
2630ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); } 2709ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); }
2631ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT"); } 2710ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT"); }
2632ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); } 2711ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); }
2633ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB16"); }
2634ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); } 2712ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); }
2635ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUBADDX"); } 2713ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index)
2714{
2715 return INTERPRETER_TRANSLATE(sadd16)(inst, index);
2716}
2717ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index)
2718{
2719 return INTERPRETER_TRANSLATE(sadd16)(inst, index);
2720}
2636ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) 2721ARM_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
3010ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); } 3095ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); }
3011ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); } 3096ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); }
3012ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); } 3097ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); }
3013ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); } 3098ARM_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}
3014ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) 3118ARM_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
30void switch_mode(arm_core_t *core, uint32_t mode) 30void 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
163class Archive : NonCopyable { 177class ArchiveBackend : NonCopyable {
164public: 178public:
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
23Archive_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 */
32std::unique_ptr<File> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { 32std::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
61ResultCode 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 */
81std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const Path& path) const { 87std::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 */
92size_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 */
106size_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 */
115size_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 */
122void 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 @@
17namespace FileSys { 17namespace FileSys {
18 18
19/// File system interface to the RomFS archive 19/// File system interface to the RomFS archive
20class Archive_RomFS final : public Archive { 20class Archive_RomFS final : public ArchiveBackend {
21public: 21public:
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
113private: 86private:
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
17namespace FileSys {
18
19Archive_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
24bool 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
15namespace FileSys {
16
17/// File system interface to the SaveData archive
18class Archive_SaveData final : public DiskArchive {
19public:
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
18namespace FileSys { 17namespace FileSys {
19 18
20Archive_SDMC::Archive_SDMC(const std::string& mount_point) { 19Archive_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
25Archive_SDMC::~Archive_SDMC() {
26}
27
28/**
29 * Initialize the archive.
30 * @return true if it initialized successfully
31 */
32bool Archive_SDMC::Initialize() { 23bool 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 */
52std::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 */
65bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const {
66 return FileUtil::Delete(GetMountPoint() + path.AsString());
67}
68
69bool 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 */
78bool 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 */
87bool Archive_SDMC::CreateDirectory(const Path& path) const {
88 return FileUtil::CreateDir(GetMountPoint() + path.AsString());
89}
90
91bool 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 */
100std::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 */
115size_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 */
128size_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 */
137size_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 */
145void 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 */
153std::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 @@
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the SDMC archive 17/// File system interface to the SDMC archive
18class Archive_SDMC final : public Archive { 18class Archive_SDMC final : public DiskArchive {
19public: 19public:
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
123private:
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
17namespace FileSys {
18
19static 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
25Archive_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
30bool 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
15namespace 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>
20class Archive_SystemSaveData final : public DiskArchive {
21public:
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
36static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); 36static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
37static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); 37static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
38 38
39class Directory : NonCopyable { 39class DirectoryBackend : NonCopyable {
40public: 40public:
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
15namespace FileSys { 15namespace FileSys {
16 16
17class Directory_RomFS final : public Directory { 17class Directory_RomFS final : public DirectoryBackend {
18public: 18public:
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
16namespace FileSys {
17
18Directory_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
26Directory_SDMC::~Directory_SDMC() {
27 Close();
28}
29
30bool 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 */
44u32 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 */
84bool 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
17namespace FileSys {
18
19class Directory_SDMC final : public Directory {
20public:
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
45private:
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
16namespace FileSys {
17
18std::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
26bool DiskArchive::DeleteFile(const FileSys::Path& path) const {
27 return FileUtil::Delete(GetMountPoint() + path.AsString());
28}
29
30bool 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
34bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const {
35 return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
36}
37
38ResultCode 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
59bool DiskArchive::CreateDirectory(const Path& path) const {
60 return FileUtil::CreateDir(GetMountPoint() + path.AsString());
61}
62
63bool 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
67std::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
77DiskFile::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
86bool 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
107size_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
112size_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
120size_t DiskFile::GetSize() const {
121 return static_cast<size_t>(file->GetSize());
122}
123
124bool DiskFile::SetSize(const u64 size) const {
125 file->Resize(size);
126 file->Flush();
127 return true;
128}
129
130bool DiskFile::Close() const {
131 return file->Close();
132}
133
134////////////////////////////////////////////////////////////////////////////////////////////////////
135
136DiskDirectory::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
144bool 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
152u32 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
16namespace 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 */
23class DiskArchive : public ArchiveBackend {
24public:
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
45protected:
46 std::string mount_point;
47};
48
49class DiskFile : public FileBackend {
50public:
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
69protected:
70 const DiskArchive* archive;
71 std::string path;
72 Mode mode;
73 FileUtil::IOFile* file;
74};
75
76class DiskDirectory : public DirectoryBackend {
77public:
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
92protected:
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
14namespace FileSys { 14namespace FileSys {
15 15
16class File : NonCopyable { 16class FileBackend : NonCopyable {
17public: 17public:
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
12namespace FileSys { 13namespace FileSys {
13 14
14File_RomFS::File_RomFS() {
15}
16
17File_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 */
24bool File_RomFS::Open() { 19bool 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 */
35size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { 30size_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 */
47size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { 44size_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 */
55size_t File_RomFS::GetSize() const { 53size_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 */
64bool File_RomFS::SetSize(const u64 size) const { 62bool 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
15namespace FileSys { 15namespace FileSys {
16 16
17class File_RomFS final : public File { 17class Archive_RomFS;
18
19class File_RomFS final : public FileBackend {
18public: 20public:
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
69private:
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
16namespace FileSys {
17
18File_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
26File_SDMC::~File_SDMC() {
27 Close();
28}
29
30/**
31 * Open the file
32 * @return true if the file opened correctly
33 */
34bool 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 */
62size_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 */
75size_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 */
87size_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 */
96bool 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 */
106bool 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
17namespace FileSys {
18
19class File_SDMC final : public File {
20public:
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
69private:
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
57void Init() { 59void 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
65void Shutdown() { 69void 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
19namespace Kernel {
20
21// Command to access archive file
22enum 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
37enum class DirectoryCommand : u32 {
38 Dummy1 = 0x000100C6,
39 Control = 0x040100C4,
40 Read = 0x08010042,
41 Close = 0x08020000,
42};
43
44class Archive : public Object {
45public:
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
113class File : public Object {
114public:
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
191class Directory : public Object {
192public:
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
242std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
243
244ResultVal<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
254ResultCode 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 */
269ResultCode 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
281ResultCode 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
295ResultVal<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
325ResultCode 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
335ResultCode 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
352ResultCode 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
362ResultCode 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
372ResultCode 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 */
395ResultVal<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
415void 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
431void 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
14namespace Kernel { 13namespace Kernel {
15 14
16Handle g_main_thread = 0; 15Handle g_main_thread = 0;
17ObjectPool g_object_pool; 16ObjectPool g_object_pool;
17u64 g_program_id = 0;
18 18
19ObjectPool::ObjectPool() { 19ObjectPool::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
90void Init() { 90void Init() {
91 Kernel::ThreadingInit(); 91 Kernel::ThreadingInit();
92 Kernel::ArchiveInit();
93} 92}
94 93
95/// Shutdown the kernel 94/// Shutdown the kernel
96void Shutdown() { 95void 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 */
108bool LoadExec(u32 entry_point) { 106bool 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
15namespace Kernel { 15namespace Kernel {
16 16
17// From kernel.h. Declarations duplicated here to avoid a circular header dependency.
18class Thread;
19Thread* GetCurrentThread();
20
17enum KernelHandle { 21enum KernelHandle {
18 CurrentThread = 0xFFFF8000, 22 CurrentThread = 0xFFFF8000,
19 CurrentProcess = 0xFFFF8001, 23 CurrentProcess = 0xFFFF8001,
@@ -22,7 +26,7 @@ enum KernelHandle {
22enum class HandleType : u32 { 26enum 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
39enum { 40enum {
@@ -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:
163extern ObjectPool g_object_pool; 163extern ObjectPool g_object_pool;
164extern Handle g_main_thread; 164extern 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?
170extern u64 g_program_id;
171
166/// Initialize the kernel 172/// Initialize the kernel
167void Init(); 173void 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
52ResultCode CreateSemaphore(Handle* handle, u32 initial_count, 52ResultCode 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 */
21ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown"); 21ResultCode 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
9namespace Kernel {
10
11static 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 */
18inline 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 */
44class Session : public Object {
45public:
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
14enum class MemoryPermission : u32 { 14enum 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;
83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup 83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
84static u32 next_thread_id; ///< The next available thread id 84static u32 next_thread_id; ///< The next available thread id
85 85
86/// Gets the current thread 86Thread* GetCurrentThread() {
87inline 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
152static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { 151static 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
158static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { 156static 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
161static 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
487ResultCode GetThreadId(u32* thread_id, Handle handle) { 499ResultCode 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...
79void ArbitrateAllThreads(u32 arbiter, u32 address); 78void ArbitrateAllThreads(u32 arbiter, u32 address);
80 79
80/// Gets the current thread
81Thread* GetCurrentThread();
82
81/// Gets the current thread handle 83/// Gets the current thread handle
82Handle GetCurrentThreadHandle(); 84Handle 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.
18enum class ErrorDescription : u32 { 18enum 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 */
20void GetWifiStatus(Service::Interface* self) { 20void 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
12namespace 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
20Interface::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
12namespace FS_User { 12namespace AM_APP {
13 13
14/// Interface to "fs:USER" service
15class Interface : public Service::Interface { 14class Interface : public Service::Interface {
16public: 15public:
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
42void Initialize(Service::Interface* self) { 42void 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
59void GetLockHandle(Service::Interface* self) { 59void 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
80void Enable(Service::Interface* self) { 80void 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
87void InquireNotification(Service::Interface* self) { 87void 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 */
114void ReceiveParameter(Service::Interface* self) { 114void 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 */
145void GlanceParameter(Service::Interface* self) { 145void 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 */
172void AppletUtility(Service::Interface* self) { 172void 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) {
196void GetSharedFont(Service::Interface* self) { 196void 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
12namespace 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
20Interface::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
12namespace CECD_U {
13
14class Interface : public Service::Interface {
15public:
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
11namespace Service {
12namespace CFG {
13
14const u64 CFG_SAVE_ID = 0x00010017;
15const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
16const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
17const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
18const char CONSOLE_USERNAME[0x14] = "CITRA";
19/// This will be initialized in CFGInit, and will be used when creating the block
20UsernameBlock CONSOLE_USERNAME_BLOCK;
21/// TODO(Subv): Find out what this actually is
22const u8 SOUND_OUTPUT_MODE = 2;
23const u8 UNITED_STATES_COUNTRY_ID = 49;
24/// TODO(Subv): Find what the other bytes are
25const 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 */
32const 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
37static const u32 CONFIG_SAVEFILE_SIZE = 0x8000;
38static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
39
40static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data;
41
42ResultCode 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
65ResultCode 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
99ResultCode 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
106ResultCode 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
117ResultCode 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
161void 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
197void 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
10namespace Service {
11namespace CFG {
12
13enum 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
21enum 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
36struct 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
44static 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*/
50struct 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};
56static_assert(sizeof(SaveFileConfig) == 0x455C, "The SaveFileConfig header must be exactly 0x455C bytes");
57
58struct 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};
63static_assert(sizeof(UsernameBlock) == 0x1C, "Size of UsernameBlock must be 0x1C");
64
65struct ConsoleModelInfo {
66 u8 model; ///< The console model (3DS, 2DS, etc)
67 u8 unknown[3]; ///< Unknown data
68};
69static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes");
70
71struct ConsoleCountryInfo {
72 u8 unknown[3]; ///< Unknown data
73 u8 country_code; ///< The country code of the console
74};
75static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
76
77extern const u64 CFG_SAVE_ID;
78extern const u64 CONSOLE_UNIQUE_ID;
79extern const ConsoleModelInfo CONSOLE_MODEL;
80extern const u8 CONSOLE_LANGUAGE;
81extern const char CONSOLE_USERNAME[0x14];
82/// This will be initialized in the Interface constructor, and will be used when creating the block
83extern UsernameBlock CONSOLE_USERNAME_BLOCK;
84/// TODO(Subv): Find out what this actually is
85extern const u8 SOUND_OUTPUT_MODE;
86extern const u8 UNITED_STATES_COUNTRY_ID;
87/// TODO(Subv): Find what the other bytes are
88extern const ConsoleCountryInfo COUNTRY_INFO;
89extern const std::array<float, 8> STEREO_CAMERA_SETTINGS;
90
91static_assert(sizeof(STEREO_CAMERA_SETTINGS) == 0x20, "STEREO_CAMERA_SETTINGS must be exactly 0x20 bytes");
92static_assert(sizeof(CONSOLE_UNIQUE_ID) == 8, "CONSOLE_UNIQUE_ID must be exactly 8 bytes");
93static_assert(sizeof(CONSOLE_LANGUAGE) == 1, "CONSOLE_LANGUAGE must be exactly 1 byte");
94static_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 */
106ResultCode 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 */
117ResultCode 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 */
123ResultCode 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 */
129ResultCode 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 */
135ResultCode FormatConfig();
136
137/// Initialize the config service
138void CFGInit();
139
140/// Shutdown the config service
141void 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
12namespace CFG_I { 13namespace 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 */
28static 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 */
51static 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 */
63static void FormatConfig(Service::Interface* self) {
64 u32* cmd_buffer = Kernel::GetCommandBuffer();
65 cmd_buffer[1] = Service::CFG::FormatConfig().raw;
66}
13 67
14const Interface::FunctionInfo FunctionTable[] = { 68const 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 */
54static void GetCountryCodeString(Service::Interface* self) { 58static 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 */
76static void GetCountryCodeID(Service::Interface* self) { 80static 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 */
117static 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 */
139static 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 */
157static 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
102const Interface::FunctionInfo FunctionTable[] = { 172const 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 */
27void ConvertProcessAddressFromDspDram(Service::Interface* self) { 27void 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 */
50void LoadComponent(Service::Interface* self) { 50void 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 */
67void GetSemaphoreEventHandle(Service::Interface* self) { 67void 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 */
85void RegisterInterruptEvents(Service::Interface* self) { 85void 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 */
102void WriteReg0x10(Service::Interface* self) { 102void 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 */
123void ReadPipeIfPossible(Service::Interface* self) { 123void 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
23namespace 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
36namespace Service {
37namespace FS {
38
39// Command to access archive file
40enum 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
55enum class DirectoryCommand : u32 {
56 Dummy1 = 0x000100C6,
57 Control = 0x040100C4,
58 Read = 0x08010042,
59 Close = 0x08020000,
60};
61
62class Archive {
63public:
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
74class File : public Kernel::Session {
75public:
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
159class Directory : public Kernel::Session {
160public:
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 */
214static 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 */
219static std::unordered_map<ArchiveHandle, Archive*> handle_map;
220static ArchiveHandle next_handle;
221
222static Archive* GetArchive(ArchiveHandle handle) {
223 auto itr = handle_map.find(handle);
224 return (itr == handle_map.end()) ? nullptr : itr->second;
225}
226
227ResultVal<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
254ResultCode 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
263ResultCode 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
274ResultVal<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
290ResultCode 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
301ResultCode 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
322ResultCode 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
333ResultCode 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
341ResultCode 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
352ResultCode 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 */
379ResultVal<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
395ResultCode 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
418void 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
434void 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//////////////////////////////////////////////////////////////////////////////////////////////////// 13namespace Service {
14// Kernel namespace 14namespace FS {
15 15
16namespace Kernel { 16/// Supported archive types
17enum 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
27typedef 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 */
23ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); 34ResultVal<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 */
29ResultCode CloseArchive(FileSys::Archive::IdCode id_code); 40ResultCode 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 */
36ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); 47ResultCode 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 */
45ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); 56ResultVal<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 */
53ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); 64ResultCode 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 */
63ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, 74ResultCode 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 */
72ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); 83ResultCode 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 */
92ResultCode 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 */
80ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); 100ResultCode 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 */
90ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, 110ResultCode 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 */
99ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); 119ResultVal<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 */
125ResultCode FormatSaveData();
100 126
101/// Initialize archives 127/// Initialize archives
102void ArchiveInit(); 128void ArchiveInit();
@@ -104,4 +130,5 @@ void ArchiveInit();
104/// Shutdown archives 130/// Shutdown archives
105void ArchiveShutdown(); 131void 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
17namespace FS_User { 17namespace Service {
18namespace FS {
19
20static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) {
21 return (u64)low_word | ((u64)high_word << 32);
22}
18 23
19static void Initialize(Service::Interface* self) { 24static 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 */
45static void OpenFile(Service::Interface* self) { 50static 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 */
88static void OpenFileDirectly(Service::Interface* self) { 92static 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 */
143void DeleteFile(Service::Interface* self) { 147static 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 */
177void RenameFile(Service::Interface* self) { 179static 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 */
212void DeleteDirectory(Service::Interface* self) { 212static 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 */
241static 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 */
241static void CreateDirectory(Service::Interface* self) { 268static 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 */
274void RenameDirectory(Service::Interface* self) { 299static 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 */
298static void OpenDirectory(Service::Interface* self) { 334static 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 */
334static void OpenArchive(Service::Interface* self) { 368static 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 */
406static 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*/
367static void IsSdmcDetected(Service::Interface* self) { 419static 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
376const 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 */
441static 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 */
478static 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
487const 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
468Interface::Interface() { 579FSUserInterface::FSUserInterface() {
469 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 580 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
470} 581}
471 582
472Interface::~Interface() { 583FSUserInterface::~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
12namespace Service {
13namespace FS {
14
15/// Interface to "fs:USER" service
16class FSUserInterface : public Service::Interface {
17public:
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
74static void WriteHWRegs(Service::Interface* self) { 74static 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
85static void ReadHWRegs(Service::Interface* self) { 85static 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 */
138static void SetBufferSwap(Service::Interface* self) { 138static 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 */
160static 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 */
157static void RegisterInterruptRelayQueue(Service::Interface* self) { 181static 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 */
155static void GetIPCHandles(Service::Interface* self) { 155static 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
12namespace LDR_RO {
13
14const 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
24Interface::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
12namespace LDR_RO {
13
14class Interface : public Service::Interface {
15public:
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
12namespace NIM_AOC {
13
14const 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
27Interface::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
12namespace NIM_AOC {
13
14class Interface : public Service::Interface {
15public:
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 */
36static void GetAdapterState(Service::Interface* self) { 36static 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 */
54static void GetShellState(Service::Interface* self) { 54static 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 */
70static void GetBatteryLevel(Service::Interface* self) { 70static 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 */
88static void GetBatteryChargeState(Service::Interface* self) { 88static 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
22namespace Service { 23namespace Service {
23 24
24static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) 25static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
25static 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 */
32inline static u32* GetCommandBuffer(const int offset=0) {
33 return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
34}
35 26
36class Manager; 27class Manager;
37 28
38/// Interface to a CTROS service 29/// Interface to a CTROS service
39class Interface : public Kernel::Object { 30class 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;
41public: 36public:
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;
16static void Initialize(Service::Interface* self) { 16static 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) {
24static void GetProcSemaphore(Service::Interface* self) { 24static 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
37static void GetServiceHandle(Service::Interface* self) { 37static 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
90static Result SendSyncRequest(Handle handle) { 94static 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) {
351static void SleepThread(s64 nanoseconds) { 352static 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
318u64 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
194private: 200private:
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
32void RunLoopFor(int cycles) { 32void RunLoopFor(int cycles) {
@@ -37,13 +37,13 @@ void RunLoopUntil(u64 global_cycles) {
37} 37}
38 38
39void Shutdown() { 39void 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
27using nihstro::DVLBHeader;
28using nihstro::DVLEHeader;
29using nihstro::DVLPHeader;
30
25namespace Pica { 31namespace Pica {
26 32
27void DebugContext::OnEvent(Event event, void* data) { 33void DebugContext::OnEvent(Event event, void* data) {
@@ -98,65 +104,6 @@ void GeometryDumper::Dump() {
98 } 104 }
99} 105}
100 106
101#pragma pack(1)
102struct 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};
111static_assert(sizeof(DVLBHeader) == 0x8, "Incorrect structure size");
112
113struct 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};
126static_assert(sizeof(DVLPHeader) == 0x1C, "Incorrect structure size");
127
128struct 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};
158static_assert(sizeof(DVLEHeader) == 0x40, "Incorrect structure size");
159#pragma pack()
160 107
161void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, 108void 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
359const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { 306const 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
383TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, 464TextureInfo 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);
192std::unique_ptr<PicaTrace> FinishPicaTracing(); 193std::unique_ptr<PicaTrace> FinishPicaTracing();
193 194
194struct TextureInfo { 195struct 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
205const 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 */
214const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
215 bool disable_alpha = false);
216
206void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); 217void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
207 218
208void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); 219void 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);
622ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); 675ASSERT_REG_POSITION(vs_output_attributes[0], 0x50);
623ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); 676ASSERT_REG_POSITION(vs_output_attributes[1], 0x51);
624ASSERT_REG_POSITION(viewport_corner, 0x68); 677ASSERT_REG_POSITION(viewport_corner, 0x68);
625ASSERT_REG_POSITION(texturing_enable, 0x80); 678ASSERT_REG_POSITION(texture0_enable, 0x80);
626ASSERT_REG_POSITION(texture0, 0x81); 679ASSERT_REG_POSITION(texture0, 0x81);
627ASSERT_REG_POSITION(texture0_format, 0x8e); 680ASSERT_REG_POSITION(texture0_format, 0x8e);
681ASSERT_REG_POSITION(texture1, 0x91);
682ASSERT_REG_POSITION(texture1_format, 0x96);
683ASSERT_REG_POSITION(texture2, 0x99);
684ASSERT_REG_POSITION(texture2_format, 0x9e);
628ASSERT_REG_POSITION(tev_stage0, 0xc0); 685ASSERT_REG_POSITION(tev_stage0, 0xc0);
629ASSERT_REG_POSITION(tev_stage1, 0xc8); 686ASSERT_REG_POSITION(tev_stage1, 0xc8);
630ASSERT_REG_POSITION(tev_stage2, 0xd0); 687ASSERT_REG_POSITION(tev_stage2, 0xd0);
@@ -638,6 +695,7 @@ ASSERT_REG_POSITION(num_vertices, 0x228);
638ASSERT_REG_POSITION(trigger_draw, 0x22e); 695ASSERT_REG_POSITION(trigger_draw, 0x22e);
639ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); 696ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
640ASSERT_REG_POSITION(triangle_topology, 0x25e); 697ASSERT_REG_POSITION(triangle_topology, 0x25e);
698ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0);
641ASSERT_REG_POSITION(vs_main_offset, 0x2ba); 699ASSERT_REG_POSITION(vs_main_offset, 0x2ba);
642ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); 700ASSERT_REG_POSITION(vs_input_register_map, 0x2bb);
643ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); 701ASSERT_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
722private: 788private:
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
806inline 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 {
18namespace Rasterizer { 18namespace Rasterizer {
19 19
20static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { 20static 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
28static u32 GetDepth(int x, int y) { 28static 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
35static void SetDepth(int x, int y, u16 value) { 35static 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
20using nihstro::Instruction;
21using nihstro::RegisterType;
22using nihstro::SourceRegister;
23using nihstro::SwizzlePattern;
15 24
16namespace Pica { 25namespace Pica {
17 26
@@ -19,13 +28,14 @@ namespace VertexShader {
19 28
20static struct { 29static 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.
27static u32 shader_memory[1024]; 37static std::array<u32, 1024> shader_memory;
28static u32 swizzle_data[1024]; 38static std::array<u32, 1024> swizzle_data;
29 39
30void SubmitShaderMemoryChange(u32 addr, u32 value) 40void 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
55bool& GetBoolUniform(u32 index)
56{
57 return shader_uniforms.b[index];
58}
59
60const std::array<u32, 1024>& GetShaderBinary()
61{
62 return shader_memory;
63}
64
65const std::array<u32, 1024>& GetSwizzlePatterns()
66{
67 return swizzle_data;
68}
69
45struct VertexShaderState { 70struct 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
66static void ProcessShaderCode(VertexShaderState& state) { 101static 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 {
61static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); 66static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
62static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); 67static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size");
63 68
64union 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};
201static_assert(std::is_standard_layout<Instruction>::value, "Structure is not using standard layout!");
202
203union 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
274void SubmitShaderMemoryChange(u32 addr, u32 value); 69void SubmitShaderMemoryChange(u32 addr, u32 value);
275void SubmitSwizzleDataChange(u32 addr, u32 value); 70void SubmitSwizzleDataChange(u32 addr, u32 value);
276 71
277OutputVertex RunShader(const InputVertex& input, int num_attributes); 72OutputVertex RunShader(const InputVertex& input, int num_attributes);
278 73
279Math::Vec4<float24>& GetFloatUniform(u32 index); 74Math::Vec4<float24>& GetFloatUniform(u32 index);
75bool& GetBoolUniform(u32 index);
76
77const std::array<u32, 1024>& GetShaderBinary();
78const 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