diff options
Diffstat (limited to '')
38 files changed, 642 insertions, 571 deletions
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 @@ #!/bin/sh set -e +set -x #if OS is linux or is not set if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then diff --git a/.travis-deps.sh b/.travis-deps.sh index b8e8417b2..8e22a6358 100644 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -1,13 +1,14 @@ #!/bin/sh set -e +set -x #if OS is linux or is not set if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y sudo apt-get -qq update - sudo apt-get -qq install g++-4.8 xorg-dev libglu1-mesa-dev libxcursor-dev - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 + sudo apt-get -qq install g++-4.9 xorg-dev libglu1-mesa-dev libxcursor-dev + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90 ( git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1 mkdir glfw/build && cd glfw/build diff --git a/CMakeLists.txt b/CMakeLists.txt index 61d5d524a..63738b5ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,8 @@ cmake_minimum_required(VERSION 2.8.11) project(citra) if (NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes") + # -std=c++14 is only supported on very new compilers, so use the old c++1y alias instead. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes") else() # Silence deprecation warnings add_definitions(/D_CRT_SECURE_NO_WARNINGS) @@ -1,10 +1,12 @@ -citra emulator +Citra Emulator ============== [![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra) -An experimental open-source Nintendo 3DS emulator/debugger written in C++. Citra is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. At this time, it only emulates a subset of 3DS hardware, and therefore is generally only useful for booting/debugging very simple homebrew demos. Citra is licensed under the GPLv2. Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project. +Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. At this time, it only emulates a subset of 3DS hardware, and therefore is generally only useful for booting/debugging very simple homebrew demos. However, this is changing as more and more progress is being made on running commercial titles, too. -For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/). +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. + +For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra). ### Development @@ -15,3 +17,15 @@ If you want to contribute please take a took at the [Contributor's Guide](CONTRI * __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Windows-Build) * __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Linux-Build) * __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/OS-X-Build) + + +### Support +If you like, you can [donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K899FANUJ2ZXW) - any donation received will go towards things like: +* 3DS consoles for developers to explore the hardware +* 3DS games for testing +* Any equipment required for homebrew +* Infrastructure setup +* Eventually 3D displays to get proper 3D output working +* ... etc ... + +We also more than gladly accept used 3DS consoles, preferrably ones with firmware 4.5 or lower! If you would like to give yours away, don't hesitate to join our IRC channel #citra on [Freenode](http://webchat.freenode.net/?channels=citra) and talk to neobrain or bunnei. Mind you, IRC is slow-paced, so it might be a while until people reply. If you're in a hurry you can just leave contact details in the channel or via private message and we'll get back to you. diff --git a/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) LOG_INFO(Frontend, "Citra starting...\n"); System::Init(render_window); - if (Core::Init()) { - LOG_CRITICAL(Frontend, "Core initialization failed, exiting..."); - Core::Stop(); - exit(1); - } - // Load a game or die... if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { LOG_CRITICAL(Frontend, "Failed to load ROM!"); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ab63f54de..198e4afd3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -24,7 +24,6 @@ set(SRCS file_sys/directory_romfs.cpp file_sys/directory_sdmc.cpp hle/kernel/address_arbiter.cpp - hle/kernel/archive.cpp hle/kernel/event.cpp hle/kernel/kernel.cpp hle/kernel/mutex.cpp @@ -32,21 +31,26 @@ set(SRCS hle/kernel/shared_memory.cpp hle/kernel/thread.cpp hle/service/ac_u.cpp + hle/service/am_app.cpp hle/service/am_net.cpp hle/service/apt_u.cpp hle/service/boss_u.cpp + hle/service/cecd_u.cpp hle/service/cfg_i.cpp hle/service/cfg_u.cpp hle/service/csnd_snd.cpp hle/service/dsp_dsp.cpp hle/service/err_f.cpp - hle/service/fs_user.cpp + hle/service/fs/archive.cpp + hle/service/fs/fs_user.cpp hle/service/frd_u.cpp hle/service/gsp_gpu.cpp hle/service/hid_user.cpp hle/service/ir_rst.cpp hle/service/ir_u.cpp + hle/service/ldr_ro.cpp hle/service/mic_u.cpp + hle/service/nim_aoc.cpp hle/service/ndm_u.cpp hle/service/nwm_uds.cpp hle/service/pm_app.cpp @@ -93,17 +97,16 @@ set(HEADERS arm/skyeye_common/vfp/vfp.h arm/skyeye_common/vfp/vfp_helper.h arm/arm_interface.h - file_sys/archive.h + file_sys/archive_backend.h file_sys/archive_romfs.h file_sys/archive_sdmc.h - file_sys/file.h + file_sys/file_backend.h file_sys/file_romfs.h file_sys/file_sdmc.h - file_sys/directory.h + file_sys/directory_backend.h file_sys/directory_romfs.h file_sys/directory_sdmc.h hle/kernel/address_arbiter.h - hle/kernel/archive.h hle/kernel/event.h hle/kernel/kernel.h hle/kernel/mutex.h @@ -112,21 +115,26 @@ set(HEADERS hle/kernel/shared_memory.h hle/kernel/thread.h hle/service/ac_u.h + hle/service/am_app.h hle/service/am_net.h hle/service/apt_u.h hle/service/boss_u.h + hle/service/cecd_u.h hle/service/cfg_i.h hle/service/cfg_u.h hle/service/csnd_snd.h hle/service/dsp_dsp.h hle/service/err_f.h - hle/service/fs_user.h + hle/service/fs/archive.h + hle/service/fs/fs_user.h hle/service/frd_u.h hle/service/gsp_gpu.h hle/service/hid_user.h hle/service/ir_rst.h hle/service/ir_u.h + hle/service/ldr_ro.h hle/service/mic_u.h + hle/service/nim_aoc.h hle/service/ndm_u.h hle/service/nwm_uds.h hle/service/pm_app.h diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index e46b4d15b..45714761a 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp @@ -6105,17 +6105,32 @@ L_stm_s_takeabort: return 1; } - case 0x6c: - if ((instr & 0xf03f0) == 0xf0070) { //uxtb16 - u8 rm_idx = BITS(0, 3); - u8 rd_idx = BITS(12, 15); - u32 rm_val = state->Reg[rm_idx]; - u32 rotation = BITS(10, 11) * 8; - u32 in = ((rm_val << (32 - rotation)) | (rm_val >> rotation)); - state->Reg[rd_idx] = in & 0x00FF00FF; + case 0x6c: // UXTB16 and UXTAB16 + { + const u8 rm_idx = BITS(0, 3); + const u8 rn_idx = BITS(16, 19); + const u8 rd_idx = BITS(12, 15); + const u32 rm_val = state->Reg[rm_idx]; + const u32 rn_val = state->Reg[rn_idx]; + const u32 rotation = BITS(10, 11) * 8; + const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation)); + + // UXTB16 + if ((instr & 0xf03f0) == 0xf0070) { + state->Reg[rd_idx] = rotated_rm & 0x00FF00FF; + } + else { // UXTAB16 + const u8 lo_rotated = (rotated_rm & 0xFF); + const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated; + + const u8 hi_rotated = (rotated_rm >> 16) & 0xFF; + const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated; + + state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF)); + } + return 1; - } else - printf ("Unhandled v6 insn: uxtab16\n"); + } break; case 0x6e: { ARMword Rm; @@ -6226,45 +6241,42 @@ L_stm_s_takeabort: return 1; } case 0x70: - if ((instr & 0xf0d0) == 0xf010) { //smuad //ichfly - u8 tar = BITS(16, 19); - u8 src1 = BITS(0, 3); - u8 src2 = BITS(8, 11); - u8 swap = BIT(5); - s16 a1 = (state->Reg[src1] & 0xFFFF); - s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); - s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); - s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = a1*a2 + b1*b2; - return 1; - - } else if ((instr & 0xf0d0) == 0xf050) { //smusd - u8 tar = BITS(16, 19); - u8 src1 = BITS(0, 3); - u8 src2 = BITS(8, 11); - u8 swap = BIT(5); - s16 a1 = (state->Reg[src1] & 0xFFFF); - s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); - s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); - s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = a1*a2 - b1*b2; - return 1; - } else if ((instr & 0xd0) == 0x10) { //smlad - u8 tar = BITS(16, 19); - u8 src1 = BITS(0, 3); - u8 src2 = BITS(8, 11); - u8 src3 = BITS(12, 15); - u8 swap = BIT(5); - - u32 a3 = state->Reg[src3]; - - s16 a1 = (state->Reg[src1] & 0xFFFF); - s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); - s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); - s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = a1*a2 + b1*b2 + a3; + // ichfly + // SMUAD, SMUSD, SMLAD + if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || (instr & 0xd0) == 0x10) { + const u8 rd_idx = BITS(16, 19); + const u8 rn_idx = BITS(0, 3); + const u8 rm_idx = BITS(8, 11); + const bool do_swap = (BIT(5) == 1); + + u32 rm_val = state->Reg[rm_idx]; + const u32 rn_val = state->Reg[rn_idx]; + + if (do_swap) + rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16)); + + const s16 rm_lo = (rm_val & 0xFFFF); + const s16 rm_hi = ((rm_val >> 16) & 0xFFFF); + const s16 rn_lo = (rn_val & 0xFFFF); + const s16 rn_hi = ((rn_val >> 16) & 0xFFFF); + + // SMUAD + if ((instr & 0xf0d0) == 0xf010) { + state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi); + } + // SMUSD + else if ((instr & 0xf0d0) == 0xf050) { + state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi); + } + // SMLAD + else { + const u8 ra_idx = BITS(12, 15); + state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx]; + } return 1; - } else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n"); + } else { + printf ("Unhandled v6 insn: smlsd\n"); + } break; case 0x74: printf ("Unhandled v6 insn: smlald/smlsld\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 exceptions |= FPSCR_IDC; if (tm & VFP_NAN) - vsm.sign = 0; + vsm.sign = 1; if (vsm.exponent >= 127 + 32) { 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) /* * Subtraction is addition with one sign inverted. */ - return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr); + if (m != 0x7FC00000) // Only negate if m isn't NaN. + m = vfp_single_packed_negate(m); + + return vfp_single_fadd(state, sd, sn, m, fpscr); } /* diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive_backend.h index 27ed23cd0..18c314884 100644 --- a/src/core/file_sys/archive.h +++ b/src/core/file_sys/archive_backend.h @@ -10,8 +10,8 @@ #include "common/string_util.h" #include "common/bit_field.h" -#include "core/file_sys/file.h" -#include "core/file_sys/directory.h" +#include "core/file_sys/file_backend.h" +#include "core/file_sys/directory_backend.h" #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" @@ -160,27 +160,14 @@ private: std::u16string u16str; }; -class Archive : NonCopyable { +class ArchiveBackend : NonCopyable { public: - /// Supported archive types - enum class IdCode : u32 { - RomFS = 0x00000003, - SaveData = 0x00000004, - ExtSaveData = 0x00000006, - SharedExtSaveData = 0x00000007, - SystemSaveData = 0x00000008, - SDMC = 0x00000009, - SDMCWriteOnly = 0x0000000A, - }; - - Archive() { } - virtual ~Archive() { } + virtual ~ArchiveBackend() { } /** - * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) - * @return IdCode of the archive + * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) */ - virtual IdCode GetIdCode() const = 0; + virtual std::string GetName() const = 0; /** * Open a file specified by its path, using the specified mode @@ -188,7 +175,7 @@ public: * @param mode Mode to open the file with * @return Opened file, or nullptr */ - virtual std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const = 0; + virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0; /** * Delete a file specified by its path @@ -232,37 +219,7 @@ public: * @param path Path relative to the archive * @return Opened directory, or nullptr */ - virtual std::unique_ptr<Directory> OpenDirectory(const Path& path) const = 0; - - /** - * Read data from the archive - * @param offset Offset in bytes to start reading data from - * @param length Length in bytes of data to read from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ - virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; - - /** - * Write data to the archive - * @param offset Offset in bytes to start writing data to - * @param length Length in bytes of data to write to archive - * @param buffer Buffer to write data from - * @param flush The flush parameters (0 == do not flush) - * @return Number of bytes written - */ - virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0; - - /** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ - virtual size_t GetSize() const = 0; - - /** - * Set the size of the archive in bytes - */ - virtual void SetSize(const u64 size) = 0; + virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; }; } // namespace FileSys diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index 74974c2df..0709b62a1 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include <memory> + #include "common/common_types.h" #include "core/file_sys/archive_romfs.h" @@ -20,17 +22,14 @@ Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { } } -Archive_RomFS::~Archive_RomFS() { -} - /** * Open a file specified by its path, using the specified mode * @param path Path relative to the archive * @param mode Mode to open the file with * @return Opened file, or nullptr */ -std::unique_ptr<File> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { - return std::unique_ptr<File>(new File_RomFS); +std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { + return std::make_unique<File_RomFS>(this); } /** @@ -78,49 +77,8 @@ bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys * @param path Path relative to the archive * @return Opened directory, or nullptr */ -std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const Path& path) const { - return std::unique_ptr<Directory>(new Directory_RomFS); -} - -/** - * Read data from the archive - * @param offset Offset in bytes to start reading data from - * @param length Length in bytes of data to read from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ -size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { - LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); - memcpy(buffer, &raw_data[(u32)offset], length); - return length; -} - -/** - * Write data to the archive - * @param offset Offset in bytes to start writing data to - * @param length Length in bytes of data to write to archive - * @param buffer Buffer to write data from - * @param flush The flush parameters (0 == do not flush) - * @return Number of bytes written - */ -size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { - LOG_WARNING(Service_FS, "Attempted to write to ROMFS."); - return 0; -} - -/** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ -size_t Archive_RomFS::GetSize() const { - return sizeof(u8) * raw_data.size(); -} - -/** - * Set the size of the archive in bytes - */ -void Archive_RomFS::SetSize(const u64 size) { - LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS"); +std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const { + return std::make_unique<Directory_RomFS>(); } } // namespace FileSys diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 222bdc356..5b1ee6332 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h @@ -8,7 +8,7 @@ #include "common/common_types.h" -#include "core/file_sys/archive.h" +#include "core/file_sys/archive_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17,16 +17,11 @@ namespace FileSys { /// File system interface to the RomFS archive -class Archive_RomFS final : public Archive { +class Archive_RomFS final : public ArchiveBackend { public: Archive_RomFS(const Loader::AppLoader& app_loader); - ~Archive_RomFS() override; - /** - * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) - * @return IdCode of the archive - */ - IdCode GetIdCode() const override { return IdCode::RomFS; } + std::string GetName() const override { return "RomFS"; } /** * Open a file specified by its path, using the specified mode @@ -34,7 +29,7 @@ public: * @param mode Mode to open the file with * @return Opened file, or nullptr */ - std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const override; + std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; /** * Delete a file specified by its path @@ -78,39 +73,11 @@ public: * @param path Path relative to the archive * @return Opened directory, or nullptr */ - std::unique_ptr<Directory> OpenDirectory(const Path& path) const override; - - /** - * Read data from the archive - * @param offset Offset in bytes to start reading data from - * @param length Length in bytes of data to read from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; - - /** - * Write data to the archive - * @param offset Offset in bytes to start writing data to - * @param length Length in bytes of data to write to archive - * @param buffer Buffer to write data from - * @param flush The flush parameters (0 == do not flush) - * @return Number of bytes written - */ - size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; - - /** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ - size_t GetSize() const override; - - /** - * Set the size of the archive in bytes - */ - void SetSize(const u64 size) override; + std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; private: + friend class File_RomFS; + std::vector<u8> raw_data; }; diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 9e524b60e..9d58668e0 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -49,12 +49,12 @@ bool Archive_SDMC::Initialize() { * @param mode Mode to open the file with * @return Opened file, or nullptr */ -std::unique_ptr<File> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const { +std::unique_ptr<FileBackend> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const { LOG_DEBUG(Service_FS, "called path=%s mode=%u", path.DebugStr().c_str(), mode.hex); File_SDMC* file = new File_SDMC(this, path, mode); if (!file->Open()) return nullptr; - return std::unique_ptr<File>(file); + return std::unique_ptr<FileBackend>(file); } /** @@ -97,53 +97,12 @@ bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys: * @param path Path relative to the archive * @return Opened directory, or nullptr */ -std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const { +std::unique_ptr<DirectoryBackend> Archive_SDMC::OpenDirectory(const Path& path) const { LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); Directory_SDMC* directory = new Directory_SDMC(this, path); if (!directory->Open()) return nullptr; - return std::unique_ptr<Directory>(directory); -} - -/** - * Read data from the archive - * @param offset Offset in bytes to start reading archive from - * @param length Length in bytes to read data from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ -size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { - LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); - return -1; -} - -/** - * Write data to the archive - * @param offset Offset in bytes to start writing data to - * @param length Length in bytes of data to write to archive - * @param buffer Buffer to write data from - * @param flush The flush parameters (0 == do not flush) - * @return Number of bytes written - */ -size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { - LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); - return -1; -} - -/** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ -size_t Archive_SDMC::GetSize() const { - LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); - return 0; -} - -/** - * Set the size of the archive in bytes - */ -void Archive_SDMC::SetSize(const u64 size) { - LOG_ERROR(Service_FS, "(UNIMPLEMENTED)"); + return std::unique_ptr<DirectoryBackend>(directory); } /** diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 19f563a62..059045245 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -6,7 +6,7 @@ #include "common/common_types.h" -#include "core/file_sys/archive.h" +#include "core/file_sys/archive_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,7 +15,7 @@ namespace FileSys { /// File system interface to the SDMC archive -class Archive_SDMC final : public Archive { +class Archive_SDMC final : public ArchiveBackend { public: Archive_SDMC(const std::string& mount_point); ~Archive_SDMC() override; @@ -26,11 +26,7 @@ public: */ bool Initialize(); - /** - * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) - * @return IdCode of the archive - */ - IdCode GetIdCode() const override { return IdCode::SDMC; } + std::string GetName() const override { return "SDMC"; } /** * Open a file specified by its path, using the specified mode @@ -38,7 +34,7 @@ public: * @param mode Mode to open the file with * @return Opened file, or nullptr */ - std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const override; + std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; /** * Delete a file specified by its path @@ -82,37 +78,7 @@ public: * @param path Path relative to the archive * @return Opened directory, or nullptr */ - std::unique_ptr<Directory> OpenDirectory(const Path& path) const override; - - /** - * Read data from the archive - * @param offset Offset in bytes to start reading archive from - * @param length Length in bytes to read data from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; - - /** - * Write data to the archive - * @param offset Offset in bytes to start writing data to - * @param length Length in bytes of data to write to archive - * @param buffer Buffer to write data from - * @param flush The flush parameters (0 == do not flush) - * @return Number of bytes written - */ - size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; - - /** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ - size_t GetSize() const override; - - /** - * Set the size of the archive in bytes - */ - void SetSize(const u64 size) override; + std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; /** * Getter for the path used for this Archive diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h index 1bb4101d6..188746a6f 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory_backend.h @@ -36,10 +36,10 @@ static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension i static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); -class Directory : NonCopyable { +class DirectoryBackend : NonCopyable { public: - Directory() { } - virtual ~Directory() { } + DirectoryBackend() { } + virtual ~DirectoryBackend() { } /** * Open the directory diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h index e2944099e..b775f014d 100644 --- a/src/core/file_sys/directory_romfs.h +++ b/src/core/file_sys/directory_romfs.h @@ -6,7 +6,7 @@ #include "common/common_types.h" -#include "core/file_sys/directory.h" +#include "core/file_sys/directory_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,7 +14,7 @@ namespace FileSys { -class Directory_RomFS final : public Directory { +class Directory_RomFS final : public DirectoryBackend { public: Directory_RomFS(); ~Directory_RomFS() override; diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h index 4c08b0d61..407a256ef 100644 --- a/src/core/file_sys/directory_sdmc.h +++ b/src/core/file_sys/directory_sdmc.h @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "common/file_util.h" -#include "core/file_sys/directory.h" +#include "core/file_sys/directory_backend.h" #include "core/file_sys/archive_sdmc.h" #include "core/loader/loader.h" @@ -16,7 +16,7 @@ namespace FileSys { -class Directory_SDMC final : public Directory { +class Directory_SDMC final : public DirectoryBackend { public: Directory_SDMC(); Directory_SDMC(const Archive_SDMC* archive, const Path& path); diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h index 4013b6c3e..1b81d5fe9 100644 --- a/src/core/file_sys/file.h +++ b/src/core/file_sys/file_backend.h @@ -13,10 +13,10 @@ namespace FileSys { -class File : NonCopyable { +class FileBackend : NonCopyable { public: - File() { } - virtual ~File() { } + FileBackend() { } + virtual ~FileBackend() { } /** * Open the file diff --git a/src/core/file_sys/file_romfs.cpp b/src/core/file_sys/file_romfs.cpp index b55708df4..5f38c2704 100644 --- a/src/core/file_sys/file_romfs.cpp +++ b/src/core/file_sys/file_romfs.cpp @@ -5,24 +5,19 @@ #include "common/common_types.h" #include "core/file_sys/file_romfs.h" +#include "core/file_sys/archive_romfs.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace namespace FileSys { -File_RomFS::File_RomFS() { -} - -File_RomFS::~File_RomFS() { -} - /** * Open the file * @return true if the file opened correctly */ bool File_RomFS::Open() { - return false; + return true; } /** @@ -33,7 +28,9 @@ bool File_RomFS::Open() { * @return Number of bytes read */ size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { - return -1; + LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); + memcpy(buffer, &archive->raw_data[(u32)offset], length); + return length; } /** @@ -45,7 +42,8 @@ size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { * @return Number of bytes written */ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { - return -1; + LOG_WARNING(Service_FS, "Attempted to write to ROMFS."); + return 0; } /** @@ -53,7 +51,7 @@ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, co * @return Size of the file in bytes */ size_t File_RomFS::GetSize() const { - return -1; + return sizeof(u8) * archive->raw_data.size(); } /** @@ -62,6 +60,7 @@ size_t File_RomFS::GetSize() const { * @return true if successful */ bool File_RomFS::SetSize(const u64 size) const { + LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS"); return false; } diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h index 5196701d3..09fa2e7e3 100644 --- a/src/core/file_sys/file_romfs.h +++ b/src/core/file_sys/file_romfs.h @@ -6,7 +6,7 @@ #include "common/common_types.h" -#include "core/file_sys/file.h" +#include "core/file_sys/file_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,10 +14,11 @@ namespace FileSys { -class File_RomFS final : public File { +class Archive_RomFS; + +class File_RomFS final : public FileBackend { public: - File_RomFS(); - ~File_RomFS() override; + File_RomFS(const Archive_RomFS* archive) : archive(archive) {} /** * Open the file @@ -62,6 +63,9 @@ public: * @return true if the file closed correctly */ bool Close() const override; + +private: + const Archive_RomFS* archive; }; } // namespace FileSys diff --git a/src/core/file_sys/file_sdmc.h b/src/core/file_sys/file_sdmc.h index 80b445968..e01548598 100644 --- a/src/core/file_sys/file_sdmc.h +++ b/src/core/file_sys/file_sdmc.h @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "common/file_util.h" -#include "core/file_sys/file.h" +#include "core/file_sys/file_backend.h" #include "core/file_sys/archive_sdmc.h" #include "core/loader/loader.h" @@ -16,7 +16,7 @@ namespace FileSys { -class File_SDMC final : public File { +class File_SDMC final : public FileBackend { public: File_SDMC(); File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode); diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 3f73b5538..cc3d5255a 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -8,6 +8,7 @@ #include "core/hle/hle.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" +#include "core/hle/service/fs/archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -56,6 +57,7 @@ void RegisterAllModules() { void Init() { Service::Init(); + Service::FS::ArchiveInit(); RegisterAllModules(); @@ -63,6 +65,7 @@ void Init() { } void Shutdown() { + Service::FS::ArchiveShutdown(); Service::Shutdown(); g_module_db.clear(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b38be0a49..929422b36 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -9,7 +9,6 @@ #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -#include "core/hle/kernel/archive.h" namespace Kernel { @@ -89,13 +88,11 @@ Object* ObjectPool::CreateByIDType(int type) { /// Initialize the kernel void Init() { Kernel::ThreadingInit(); - Kernel::ArchiveInit(); } /// Shutdown the kernel void Shutdown() { Kernel::ThreadingShutdown(); - Kernel::ArchiveShutdown(); g_object_pool.Clear(); // Free all kernel objects } @@ -106,8 +103,6 @@ void Shutdown() { * @return True on success, otherwise false */ bool LoadExec(u32 entry_point) { - Init(); - Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp new file mode 100644 index 000000000..b8b06418c --- /dev/null +++ b/src/core/hle/service/am_app.cpp @@ -0,0 +1,24 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/am_app.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace AM_APP + +namespace AM_APP { + +// Empty arrays are illegal -- commented out until an entry is added. +//const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/am_app.h index 005382540..86a5f5b74 100644 --- a/src/core/hle/service/fs_user.h +++ b/src/core/hle/service/am_app.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once @@ -7,24 +7,20 @@ #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace FS_User +// Namespace AM_APP -namespace FS_User { +namespace AM_APP { -/// Interface to "fs:USER" service class Interface : public Service::Interface { public: - Interface(); - ~Interface(); - /** * Gets the string port name used by CTROS for the service * @return Port name of service */ std::string GetPortName() const override { - return "fs:USER"; + return "am:app"; } }; diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp new file mode 100644 index 000000000..25d903516 --- /dev/null +++ b/src/core/hle/service/cecd_u.cpp @@ -0,0 +1,24 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/cecd_u.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CECD_U + +namespace CECD_U { + +// Empty arrays are illegal -- commented out until an entry is added. +//const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h new file mode 100644 index 000000000..969e1ed1b --- /dev/null +++ b/src/core/hle/service/cecd_u.h @@ -0,0 +1,27 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CECD_U + +namespace CECD_U { + +class Interface : public Service::Interface { +public: + Interface(); + + /** + * Gets the string port name used by CTROS for the service + * @return Port name of service + */ + std::string GetPortName() const override { + return "cecd:u"; + } +}; + +} // namespace diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/service/fs/archive.cpp index 0e3eb4564..caf82d556 100644 --- a/src/core/hle/kernel/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -2,23 +2,37 @@ // Licensed under GPLv2 // Refer to the license.txt file included. -#include <map> +#include <memory> +#include <unordered_map> #include "common/common_types.h" #include "common/file_util.h" #include "common/math_util.h" -#include "core/file_sys/archive.h" +#include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_sdmc.h" -#include "core/file_sys/directory.h" -#include "core/hle/kernel/archive.h" +#include "core/file_sys/directory_backend.h" +#include "core/hle/service/fs/archive.h" #include "core/hle/kernel/session.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace +// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. +// Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 +namespace std { + template <> + struct hash<Service::FS::ArchiveIdCode> { + typedef Service::FS::ArchiveIdCode argument_type; + typedef std::size_t result_type; + + result_type operator()(const argument_type& id_code) const { + typedef std::underlying_type<argument_type>::type Type; + return std::hash<Type>()(static_cast<Type>(id_code)); + } + }; +} -namespace Kernel { +namespace Service { +namespace FS { // Command to access archive file enum class FileCommand : u32 { @@ -43,78 +57,28 @@ enum class DirectoryCommand : u32 { Close = 0x08020000, }; -class Archive : public Kernel::Session { +class Archive { public: - std::string GetName() const override { return "Archive: " + name; } - - std::string name; ///< Name of archive (optional) - FileSys::Archive* backend; ///< Archive backend interface - - ResultVal<bool> SyncRequest() override { - u32* cmd_buff = Kernel::GetCommandBuffer(); - FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); + Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) + : backend(std::move(backend)), id_code(id_code) { + } - switch (cmd) { - // Read from archive... - case FileCommand::Read: - { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - u32 length = cmd_buff[3]; - u32 address = cmd_buff[5]; + std::string GetName() const { return "Archive: " + backend->GetName(); } - // Number of bytes read - cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); - break; - } - // Write to archive... - case FileCommand::Write: - { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - - // Number of bytes written - cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); - break; - } - case FileCommand::GetSize: - { - u64 filesize = (u64) backend->GetSize(); - cmd_buff[2] = (u32) filesize; // Lower word - cmd_buff[3] = (u32) (filesize >> 32); // Upper word - break; - } - case FileCommand::SetSize: - { - backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); - break; - } - case FileCommand::Close: - { - LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - CloseArchive(backend->GetIdCode()); - break; - } - // Unknown command... - default: - { - LOG_ERROR(Service_FS, "Unknown command=0x%08X", cmd); - cmd_buff[0] = UnimplementedFunction(ErrorModule::FS).raw; - return MakeResult<bool>(false); - } - } - cmd_buff[1] = 0; // No error - return MakeResult<bool>(false); - } + ArchiveIdCode id_code; ///< Id code of the archive + std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface }; class File : public Kernel::Session { public: + File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) + : backend(std::move(backend)), path(path) { + } + std::string GetName() const override { return "Path: " + path.DebugStr(); } FileSys::Path path; ///< Path of the file - std::unique_ptr<FileSys::File> backend; ///< File backend interface + std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface ResultVal<bool> SyncRequest() override { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -185,10 +149,14 @@ public: class Directory : public Kernel::Session { public: + Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) + : backend(std::move(backend)), path(path) { + } + std::string GetName() const override { return "Directory: " + path.DebugStr(); } FileSys::Path path; ///< Path of the directory - std::unique_ptr<FileSys::Directory> backend; ///< File backend interface + std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface ResultVal<bool> SyncRequest() override { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -230,105 +198,95 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////// -std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode +/** + * Map of registered archives, identified by id code. Once an archive is registered here, it is + * never removed until the FS service is shut down. + */ +static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map; + +/** + * Map of active archive handles. Values are pointers to the archives in `idcode_map`. + */ +static std::unordered_map<ArchiveHandle, Archive*> handle_map; +static ArchiveHandle next_handle; + +static Archive* GetArchive(ArchiveHandle handle) { + auto itr = handle_map.find(handle); + return (itr == handle_map.end()) ? nullptr : itr->second; +} + +ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) { + LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code); -ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) { - auto itr = g_archive_map.find(id_code); - if (itr == g_archive_map.end()) { + auto itr = id_code_map.find(id_code); + if (itr == id_code_map.end()) { + // TODO: Verify error against hardware return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Permanent); } - return MakeResult<Handle>(itr->second); + // This should never even happen in the first place with 64-bit handles, + while (handle_map.count(next_handle) != 0) { + ++next_handle; + } + handle_map.emplace(next_handle, itr->second.get()); + return MakeResult<ArchiveHandle>(next_handle++); } -ResultCode CloseArchive(FileSys::Archive::IdCode id_code) { - auto itr = g_archive_map.find(id_code); - if (itr == g_archive_map.end()) { - LOG_ERROR(Service_FS, "Cannot close archive %d, does not exist!", (int)id_code); +ResultCode CloseArchive(ArchiveHandle handle) { + if (handle_map.erase(handle) == 0) return InvalidHandle(ErrorModule::FS); - } - - LOG_TRACE(Service_FS, "Closed archive %d", (int) id_code); - return RESULT_SUCCESS; + else + return RESULT_SUCCESS; } -/** - * Mounts an archive - * @param archive Pointer to the archive to mount - */ -ResultCode MountArchive(Archive* archive) { - FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); - ResultVal<Handle> archive_handle = OpenArchive(id_code); - if (archive_handle.Succeeded()) { - LOG_ERROR(Service_FS, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); - return archive_handle.Code(); - } - g_archive_map[id_code] = archive->GetHandle(); - LOG_TRACE(Service_FS, "Mounted archive %s", archive->GetName().c_str()); - return RESULT_SUCCESS; -} +// TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in +// http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 +ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { + auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code)); -ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) { - Archive* archive = new Archive; - Handle handle = Kernel::g_object_pool.Create(archive); - archive->name = name; - archive->backend = backend; + bool inserted = result.second; + _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); - ResultCode result = MountArchive(archive); - if (result.IsError()) { - return result; - } - + auto& archive = result.first->second; + LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code); return RESULT_SUCCESS; } -ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { - // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create - // the archive file handles at app loading, and then keep them persistent throughout execution. - // Archives file handles are just reused and not actually freed until emulation shut down. - // Verify if real hardware works this way, or if new handles are created each time - if (path.GetType() == FileSys::Binary) - // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend - // design. While the functionally of this is OK, our implementation decision to separate - // normal files from archive file pointers is very likely wrong. - // See https://github.com/citra-emu/citra/issues/205 - return MakeResult<Handle>(archive_handle); - - File* file = new File; - Handle handle = Kernel::g_object_pool.Create(file); - - Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); - if (archive == nullptr) { +ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { + Archive* archive = GetArchive(archive_handle); + if (archive == nullptr) return InvalidHandle(ErrorModule::FS); - } - file->path = path; - file->backend = archive->backend->OpenFile(path, mode); - if (!file->backend) { + std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); + if (backend == nullptr) { return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Permanent); + ErrorSummary::NotFound, ErrorLevel::Permanent); } + auto file = std::make_unique<File>(std::move(backend), path); + Handle handle = Kernel::g_object_pool.Create(file.release()); return MakeResult<Handle>(handle); } -ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); +ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { + Archive* archive = GetArchive(archive_handle); if (archive == nullptr) return InvalidHandle(ErrorModule::FS); + if (archive->backend->DeleteFile(path)) return RESULT_SUCCESS; return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description ErrorSummary::Canceled, ErrorLevel::Status); } -ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path) { - Archive* src_archive = Kernel::g_object_pool.GetFast<Archive>(src_archive_handle); - Archive* dest_archive = Kernel::g_object_pool.GetFast<Archive>(dest_archive_handle); +ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, + ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { + Archive* src_archive = GetArchive(src_archive_handle); + Archive* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) return InvalidHandle(ErrorModule::FS); + if (src_archive == dest_archive) { if (src_archive->backend->RenameFile(src_path, dest_path)) return RESULT_SUCCESS; @@ -336,36 +294,42 @@ ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::P // TODO: Implement renaming across archives return UnimplementedFunction(ErrorModule::FS); } + + // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't + // exist or similar. Verify. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description ErrorSummary::NothingHappened, ErrorLevel::Status); } -ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); +ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { + Archive* archive = GetArchive(archive_handle); if (archive == nullptr) return InvalidHandle(ErrorModule::FS); + if (archive->backend->DeleteDirectory(path)) return RESULT_SUCCESS; return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description ErrorSummary::Canceled, ErrorLevel::Status); } -ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); +ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { + Archive* archive = GetArchive(archive_handle); if (archive == nullptr) return InvalidHandle(ErrorModule::FS); + if (archive->backend->CreateDirectory(path)) return RESULT_SUCCESS; return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description ErrorSummary::Canceled, ErrorLevel::Status); } -ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path) { - Archive* src_archive = Kernel::g_object_pool.GetFast<Archive>(src_archive_handle); - Archive* dest_archive = Kernel::g_object_pool.GetFast<Archive>(dest_archive_handle); +ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, + ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { + Archive* src_archive = GetArchive(src_archive_handle); + Archive* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) return InvalidHandle(ErrorModule::FS); + if (src_archive == dest_archive) { if (src_archive->backend->RenameDirectory(src_path, dest_path)) return RESULT_SUCCESS; @@ -373,6 +337,9 @@ ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileS // TODO: Implement renaming across archives return UnimplementedFunction(ErrorModule::FS); } + + // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't + // exist or similar. Verify. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description ErrorSummary::NothingHappened, ErrorLevel::Status); } @@ -383,44 +350,43 @@ ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileS * @param path Path to the Directory inside of the Archive * @return Opened Directory object */ -ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Directory* directory = new Directory; - Handle handle = Kernel::g_object_pool.Create(directory); - - Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); - if (archive == nullptr) { +ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { + Archive* archive = GetArchive(archive_handle); + if (archive == nullptr) return InvalidHandle(ErrorModule::FS); - } - directory->path = path; - directory->backend = archive->backend->OpenDirectory(path); - if (!directory->backend) { + std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path); + if (backend == nullptr) { return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Permanent); } + auto directory = std::make_unique<Directory>(std::move(backend), path); + Handle handle = Kernel::g_object_pool.Create(directory.release()); return MakeResult<Handle>(handle); } /// Initialize archives void ArchiveInit() { - g_archive_map.clear(); + next_handle = 1; // TODO(Link Mauve): Add the other archive types (see here for the known types: // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished // archive type is SDMC, so it is the only one getting exposed. std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); - auto archive = new FileSys::Archive_SDMC(sdmc_directory); + auto archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory); if (archive->Initialize()) - CreateArchive(archive, "SDMC"); + CreateArchive(std::move(archive), ArchiveIdCode::SDMC); else LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); } /// Shutdown archives void ArchiveShutdown() { - g_archive_map.clear(); + handle_map.clear(); + id_code_map.clear(); } -} // namespace Kernel +} // namespace FS +} // namespace Service diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/service/fs/archive.h index b50833a2b..a38de92e3 100644 --- a/src/core/hle/kernel/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -6,34 +6,45 @@ #include "common/common_types.h" -#include "core/file_sys/archive.h" +#include "core/file_sys/archive_backend.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace +namespace Service { +namespace FS { -namespace Kernel { +/// Supported archive types +enum class ArchiveIdCode : u32 { + RomFS = 0x00000003, + SaveData = 0x00000004, + ExtSaveData = 0x00000006, + SharedExtSaveData = 0x00000007, + SystemSaveData = 0x00000008, + SDMC = 0x00000009, + SDMCWriteOnly = 0x0000000A, +}; + +typedef u64 ArchiveHandle; /** * Opens an archive * @param id_code IdCode of the archive to open * @return Handle to the opened archive */ -ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); +ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code); /** * Closes an archive * @param id_code IdCode of the archive to open */ -ResultCode CloseArchive(FileSys::Archive::IdCode id_code); +ResultCode CloseArchive(ArchiveHandle handle); /** * Creates an Archive * @param backend File system backend interface to the archive - * @param name Name of Archive + * @param id_code Id code used to access this type of archive */ -ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); +ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); /** * Open a File from an Archive @@ -42,7 +53,7 @@ ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); * @param mode Mode under which to open the File * @return Handle to the opened File object */ -ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); +ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); /** * Delete a File from an Archive @@ -50,7 +61,7 @@ ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path * @param path Path to the File inside of the Archive * @return Whether deletion succeeded */ -ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); /** * Rename a File between two Archives @@ -60,8 +71,8 @@ ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& pat * @param dest_path Path to the File inside of the destination Archive * @return Whether rename succeeded */ -ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path); +ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, + ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); /** * Delete a Directory from an Archive @@ -69,7 +80,7 @@ ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::P * @param path Path to the Directory inside of the Archive * @return Whether deletion succeeded */ -ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); /** * Create a Directory from an Archive @@ -77,7 +88,7 @@ ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path * @param path Path to the Directory inside of the Archive * @return Whether creation of directory succeeded */ -ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); /** * Rename a Directory between two Archives @@ -87,8 +98,8 @@ ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path * @param dest_path Path to the Directory inside of the destination Archive * @return Whether rename succeeded */ -ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path); +ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, + ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); /** * Open a Directory from an Archive @@ -96,7 +107,7 @@ ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileS * @param path Path to the Directory inside of the Archive * @return Handle to the opened File object */ -ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); /// Initialize archives void ArchiveInit(); @@ -104,4 +115,5 @@ void ArchiveInit(); /// Shutdown archives void ArchiveShutdown(); -} // namespace FileSys +} // namespace FS +} // namespace Service diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 672ba2475..0f75d5e3a 100644 --- a/src/core/hle/service/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -3,18 +3,23 @@ // Refer to the license.txt file included. #include "common/common.h" +#include "common/scope_exit.h" #include "common/string_util.h" -#include "core/hle/kernel/archive.h" -#include "core/hle/kernel/archive.h" +#include "core/hle/service/fs/archive.h" #include "core/hle/result.h" -#include "core/hle/service/fs_user.h" +#include "core/hle/service/fs/fs_user.h" #include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace FS_User -namespace FS_User { +namespace Service { +namespace FS { + +static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) { + return (u64)low_word | ((u64)high_word << 32); +} static void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -57,11 +62,12 @@ static void OpenFile(Service::Interface* self) { LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); - ResultVal<Handle> handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode); + ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[3] = *handle; } else { + cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); } } @@ -88,7 +94,7 @@ static void OpenFile(Service::Interface* self) { static void OpenFileDirectly(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]); + auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]); auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); u32 archivename_size = cmd_buff[4]; auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); @@ -106,25 +112,25 @@ static void OpenFileDirectly(Service::Interface* self) { if (archive_path.GetType() != FileSys::Empty) { LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; + cmd_buff[3] = 0; return; } - // TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it - // TODO(yuriks): Why is there all this duplicate (and seemingly useless) code up here? - ResultVal<Handle> archive_handle = Kernel::OpenArchive(archive_id); - cmd_buff[1] = archive_handle.Code().raw; + ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id); if (archive_handle.Failed()) { LOG_ERROR(Service_FS, "failed to get a handle for archive"); + cmd_buff[1] = archive_handle.Code().raw; + cmd_buff[3] = 0; return; } - // cmd_buff[2] isn't used according to 3dmoo's implementation. - cmd_buff[3] = *archive_handle; + SCOPE_EXIT({ CloseArchive(*archive_handle); }); - ResultVal<Handle> handle = Kernel::OpenFileFromArchive(*archive_handle, file_path, mode); + ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[3] = *handle; } else { + cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); } } @@ -140,12 +146,10 @@ static void OpenFileDirectly(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void DeleteFile(Service::Interface* self) { +static void DeleteFile(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 filename_size = cmd_buff[5]; u32 filename_ptr = cmd_buff[7]; @@ -155,7 +159,7 @@ void DeleteFile(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::DeleteFileFromArchive(archive_handle, file_path).raw; + cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw; } /* @@ -174,15 +178,13 @@ void DeleteFile(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void RenameFile(Service::Interface* self) { +static void RenameFile(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2] and cmd_buff[6], aka archive handle lower word, aren't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle src_archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 src_filename_size = cmd_buff[5]; - Handle dest_archive_handle = static_cast<Handle>(cmd_buff[7]); + ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);; auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); u32 dest_filename_size = cmd_buff[9]; u32 src_filename_ptr = cmd_buff[11]; @@ -195,7 +197,7 @@ void RenameFile(Service::Interface* self) { src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(), dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; + cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; } /* @@ -209,12 +211,10 @@ void RenameFile(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void DeleteDirectory(Service::Interface* self) { +static void DeleteDirectory(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 dirname_size = cmd_buff[5]; u32 dirname_ptr = cmd_buff[7]; @@ -224,7 +224,7 @@ void DeleteDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::DeleteDirectoryFromArchive(archive_handle, dir_path).raw; + cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; } /* @@ -241,9 +241,7 @@ void DeleteDirectory(Service::Interface* self) { static void CreateDirectory(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 dirname_size = cmd_buff[5]; u32 dirname_ptr = cmd_buff[8]; @@ -252,7 +250,7 @@ static void CreateDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_path).raw; + cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw; } /* @@ -271,15 +269,13 @@ static void CreateDirectory(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void RenameDirectory(Service::Interface* self) { +static void RenameDirectory(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2] and cmd_buff[6], aka archive handle lower word, aren't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle src_archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 src_dirname_size = cmd_buff[5]; - Handle dest_archive_handle = static_cast<Handle>(cmd_buff[7]); + ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]); auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); u32 dest_dirname_size = cmd_buff[9]; u32 src_dirname_ptr = cmd_buff[11]; @@ -292,15 +288,26 @@ void RenameDirectory(Service::Interface* self) { src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(), dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; + cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; } +/** + * FS_User::OpenDirectory service function + * Inputs: + * 1 : Archive handle low word + * 2 : Archive handle high word + * 3 : Low path type + * 4 : Low path size + * 7 : (LowPathSize << 14) | 2 + * 8 : Low path data pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 3 : Directory handle + */ static void OpenDirectory(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[2]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); u32 dirname_size = cmd_buff[4]; u32 dirname_ptr = cmd_buff[6]; @@ -309,7 +316,7 @@ static void OpenDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - ResultVal<Handle> handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path); + ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[3] = *handle; @@ -334,7 +341,7 @@ static void OpenDirectory(Service::Interface* self) { static void OpenArchive(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]); + auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); u32 archivename_size = cmd_buff[3]; u32 archivename_ptr = cmd_buff[5]; @@ -348,16 +355,34 @@ static void OpenArchive(Service::Interface* self) { return; } - ResultVal<Handle> handle = Kernel::OpenArchive(archive_id); + ResultVal<ArchiveHandle> handle = OpenArchive(archive_id); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { - // cmd_buff[2] isn't used according to 3dmoo's implementation. - cmd_buff[3] = *handle; + cmd_buff[2] = *handle & 0xFFFFFFFF; + cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; } else { + cmd_buff[2] = cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for archive"); } } +/** + * FS_User::CloseArchive service function + * Inputs: + * 0 : 0x080E0080 + * 1 : Archive handle low word + * 2 : Archive handle high word + * Outputs: + * 0 : ??? TODO(yuriks): Verify return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CloseArchive(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); + cmd_buff[1] = CloseArchive(archive_handle).raw; +} + /* * FS_User::IsSdmcDetected service function * Outputs: @@ -373,7 +398,7 @@ static void IsSdmcDetected(Service::Interface* self) { LOG_DEBUG(Service_FS, "called"); } -const Interface::FunctionInfo FunctionTable[] = { +const FSUserInterface::FunctionInfo FunctionTable[] = { {0x000100C6, nullptr, "Dummy1"}, {0x040100C4, nullptr, "Control"}, {0x08010002, Initialize, "Initialize"}, @@ -389,7 +414,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080B0102, OpenDirectory, "OpenDirectory"}, {0x080C00C2, OpenArchive, "OpenArchive"}, {0x080D0144, nullptr, "ControlArchive"}, - {0x080E0080, nullptr, "CloseArchive"}, + {0x080E0080, CloseArchive, "CloseArchive"}, {0x080F0180, nullptr, "FormatThisUserSaveData"}, {0x08100200, nullptr, "CreateSystemSaveData"}, {0x08110040, nullptr, "DeleteSystemSaveData"}, @@ -465,11 +490,12 @@ const Interface::FunctionInfo FunctionTable[] = { //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class -Interface::Interface() { +FSUserInterface::FSUserInterface() { Register(FunctionTable, ARRAY_SIZE(FunctionTable)); } -Interface::~Interface() { +FSUserInterface::~FSUserInterface() { } -} // namespace +} // namespace FS +} // namespace Service diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h new file mode 100644 index 000000000..80e3804e0 --- /dev/null +++ b/src/core/hle/service/fs/fs_user.h @@ -0,0 +1,33 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace FS_User + +namespace Service { +namespace FS { + +/// Interface to "fs:USER" service +class FSUserInterface : public Service::Interface { +public: + + FSUserInterface(); + + ~FSUserInterface(); + + /** + * Gets the string port name used by CTROS for the service + * @return Port name of service + */ + std::string GetPortName() const override { + return "fs:USER"; + } +}; + +} // namespace FS +} // namespace Service diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp new file mode 100644 index 000000000..91b1a6fc5 --- /dev/null +++ b/src/core/hle/service/ldr_ro.cpp @@ -0,0 +1,28 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/ldr_ro.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +const Interface::FunctionInfo FunctionTable[] = { + {0x000100C2, nullptr, "Initialize"}, + {0x00020082, nullptr, "CRR_Load"}, + {0x00030042, nullptr, "CRR_Unload"}, + {0x000402C2, nullptr, "CRO_LoadAndFix"}, + {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"} +}; +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h new file mode 100644 index 000000000..32d7c29cf --- /dev/null +++ b/src/core/hle/service/ldr_ro.h @@ -0,0 +1,27 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace LDR_RO + +namespace LDR_RO { + +class Interface : public Service::Interface { +public: + Interface(); + + /** + * Gets the string port name used by CTROS for the service + * @return Port name of service + */ + std::string GetPortName() const override { + return "ldr:ro"; + } +}; + +} // namespace diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp new file mode 100644 index 000000000..04c1e0cf3 --- /dev/null +++ b/src/core/hle/service/nim_aoc.cpp @@ -0,0 +1,31 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/nim_aoc.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NIM_AOC + +namespace NIM_AOC { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00030042, nullptr, "SetApplicationId"}, + {0x00040042, nullptr, "SetTin"}, + {0x000902D0, nullptr, "ListContentSetsEx"}, + {0x00180000, nullptr, "GetBalance"}, + {0x001D0000, nullptr, "GetCustomerSupportCode"}, + {0x00210000, nullptr, "Initialize"}, + {0x00240282, nullptr, "CalculateContentsRequiredSize"}, + {0x00250000, nullptr, "RefreshServerTime"}, +}; +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h new file mode 100644 index 000000000..2cc673118 --- /dev/null +++ b/src/core/hle/service/nim_aoc.h @@ -0,0 +1,27 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NIM_AOC + +namespace NIM_AOC { + +class Interface : public Service::Interface { +public: + Interface(); + + /** + * Gets the string port name used by CTROS for the service + * @return Port name of service + */ + std::string GetPortName() const override { + return "nim:aoc"; + } +}; + +} // namespace diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e6973572b..2230045e3 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -7,21 +7,25 @@ #include "core/hle/service/service.h" #include "core/hle/service/ac_u.h" +#include "core/hle/service/am_app.h" #include "core/hle/service/am_net.h" #include "core/hle/service/apt_u.h" #include "core/hle/service/boss_u.h" +#include "core/hle/service/cecd_u.h" #include "core/hle/service/cfg_i.h" #include "core/hle/service/cfg_u.h" #include "core/hle/service/csnd_snd.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/err_f.h" -#include "core/hle/service/fs_user.h" +#include "core/hle/service/fs/fs_user.h" #include "core/hle/service/frd_u.h" #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/hid_user.h" #include "core/hle/service/ir_rst.h" #include "core/hle/service/ir_u.h" +#include "core/hle/service/ldr_ro.h" #include "core/hle/service/mic_u.h" +#include "core/hle/service/nim_aoc.h" #include "core/hle/service/ndm_u.h" #include "core/hle/service/nwm_uds.h" #include "core/hle/service/pm_app.h" @@ -84,21 +88,25 @@ void Init() { g_manager->AddService(new SRV::Interface); g_manager->AddService(new AC_U::Interface); + g_manager->AddService(new AM_APP::Interface); g_manager->AddService(new AM_NET::Interface); g_manager->AddService(new APT_U::Interface); g_manager->AddService(new BOSS_U::Interface); + g_manager->AddService(new CECD_U::Interface); g_manager->AddService(new CFG_I::Interface); g_manager->AddService(new CFG_U::Interface); g_manager->AddService(new CSND_SND::Interface); g_manager->AddService(new DSP_DSP::Interface); g_manager->AddService(new ERR_F::Interface); g_manager->AddService(new FRD_U::Interface); - g_manager->AddService(new FS_User::Interface); + g_manager->AddService(new FS::FSUserInterface); g_manager->AddService(new GSP_GPU::Interface); g_manager->AddService(new HID_User::Interface); g_manager->AddService(new IR_RST::Interface); g_manager->AddService(new IR_U::Interface); + g_manager->AddService(new LDR_RO::Interface); g_manager->AddService(new MIC_U::Interface); + g_manager->AddService(new NIM_AOC::Interface); g_manager->AddService(new NDM_U::Interface); g_manager->AddService(new NWM_UDS::Interface); g_manager->AddService(new PM_APP::Interface); diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index f48d13530..0437e5374 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -8,7 +8,7 @@ #include "core/file_sys/archive_romfs.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" -#include "core/hle/kernel/archive.h" +#include "core/hle/service/fs/archive.h" #include "core/mem_map.h" #include "3dsx.h" diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 3883e1307..463dacca3 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -8,7 +8,7 @@ #include "core/loader/3dsx.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" -#include "core/hle/kernel/archive.h" +#include "core/hle/service/fs/archive.h" #include "core/mem_map.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -74,7 +74,7 @@ ResultStatus LoadFile(const std::string& filename) { // Load application and RomFS if (ResultStatus::Success == app_loader.Load()) { - Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS"); + Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); return ResultStatus::Success; } break; diff --git a/src/core/system.cpp b/src/core/system.cpp index 43d0eef2c..2885ff45f 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -23,10 +23,10 @@ void Init(EmuWindow* emu_window) { Core::Init(); Memory::Init(); HW::Init(); + Kernel::Init(); HLE::Init(); CoreTiming::Init(); VideoCore::Init(emu_window); - Kernel::Init(); } void RunLoopFor(int cycles) { @@ -37,13 +37,13 @@ void RunLoopUntil(u64 global_cycles) { } void Shutdown() { - Core::Shutdown(); - Memory::Shutdown(); - HW::Shutdown(); - HLE::Shutdown(); - CoreTiming::Shutdown(); VideoCore::Shutdown(); + CoreTiming::Shutdown(); + HLE::Shutdown(); Kernel::Shutdown(); + HW::Shutdown(); + Memory::Shutdown(); + Core::Shutdown(); } } // namespace |