diff options
| author | 2014-12-29 19:47:41 -0800 | |
|---|---|---|
| committer | 2014-12-29 19:47:41 -0800 | |
| commit | 8ba9ac0f74abb0408a26207a76a0c1808bad8de0 (patch) | |
| tree | f1c7c3393fa726435b5b90bf335567c93e528ef1 /src/core/hle | |
| parent | Add comment regarding __WIN32__ in SkyEye code (diff) | |
| parent | Merge pull request #367 from bunnei/usat_ssat (diff) | |
| download | yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.gz yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.xz yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.zip | |
Fix merge conflicts
Diffstat (limited to 'src/core/hle')
97 files changed, 3610 insertions, 1834 deletions
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index c7cf5b1d3..721a600b5 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 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 | #include "common/log.h" | ||
| 6 | 7 | ||
| 7 | #include "core/hle/config_mem.h" | 8 | #include "core/hle/config_mem.h" |
| 8 | 9 | ||
| @@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) { | |||
| 54 | break; | 55 | break; |
| 55 | 56 | ||
| 56 | default: | 57 | default: |
| 57 | ERROR_LOG(HLE, "unknown addr=0x%08X", addr); | 58 | LOG_ERROR(Kernel, "unknown addr=0x%08X", addr); |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
diff --git a/src/core/hle/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 3dbe25037..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 |
| @@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() { | |||
| 114 | FuncReturn(retval); | 114 | FuncReturn(retval); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | template<s32 func(u32*, s32, s32)> void Wrap() { | ||
| 118 | u32 param_1 = 0; | ||
| 119 | u32 retval = func(¶m_1, PARAM(1), PARAM(2)); | ||
| 120 | Core::g_app_core->SetReg(1, param_1); | ||
| 121 | FuncReturn(retval); | ||
| 122 | } | ||
| 123 | |||
| 124 | template<s32 func(s32*, u32, s32)> void Wrap() { | ||
| 125 | s32 param_1 = 0; | ||
| 126 | u32 retval = func(¶m_1, PARAM(1), PARAM(2)); | ||
| 127 | Core::g_app_core->SetReg(1, param_1); | ||
| 128 | FuncReturn(retval); | ||
| 129 | } | ||
| 130 | |||
| 117 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 131 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 118 | // Function wrappers that return type u32 | 132 | // Function wrappers that return type u32 |
| 119 | 133 | ||
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index b8ac186f6..33ac12507 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 | ||
| @@ -20,7 +22,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n | |||
| 20 | const FunctionDef* GetSVCInfo(u32 opcode) { | 22 | const FunctionDef* GetSVCInfo(u32 opcode) { |
| 21 | u32 func_num = opcode & 0xFFFFFF; // 8 bits | 23 | u32 func_num = opcode & 0xFFFFFF; // 8 bits |
| 22 | if (func_num > 0xFF) { | 24 | if (func_num > 0xFF) { |
| 23 | ERROR_LOG(HLE,"unknown svc=0x%02X", func_num); | 25 | LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num); |
| 24 | return nullptr; | 26 | return nullptr; |
| 25 | } | 27 | } |
| 26 | return &g_module_db[0].func_table[func_num]; | 28 | return &g_module_db[0].func_table[func_num]; |
| @@ -35,15 +37,21 @@ void CallSVC(u32 opcode) { | |||
| 35 | if (info->func) { | 37 | if (info->func) { |
| 36 | info->func(); | 38 | info->func(); |
| 37 | } else { | 39 | } else { |
| 38 | ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str()); | 40 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str()); |
| 39 | } | 41 | } |
| 40 | } | 42 | } |
| 41 | 43 | ||
| 42 | void Reschedule(const char *reason) { | 44 | void Reschedule(const char *reason) { |
| 43 | #ifdef _DEBUG | 45 | _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); |
| 44 | _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); | 46 | |
| 45 | #endif | 47 | // TODO(bunnei): It seems that games depend on some CPU execution time elapsing during HLE |
| 48 | // routines. This simulates that time by artificially advancing the number of CPU "ticks". | ||
| 49 | // The value was chosen empirically, it seems to work well enough for everything tested, but | ||
| 50 | // is likely not ideal. We should find a more accurate way to simulate timing with HLE. | ||
| 51 | Core::g_app_core->AddTicks(4000); | ||
| 52 | |||
| 46 | Core::g_app_core->PrepareReschedule(); | 53 | Core::g_app_core->PrepareReschedule(); |
| 54 | |||
| 47 | g_reschedule = true; | 55 | g_reschedule = true; |
| 48 | } | 56 | } |
| 49 | 57 | ||
| @@ -58,18 +66,22 @@ void RegisterAllModules() { | |||
| 58 | 66 | ||
| 59 | void Init() { | 67 | void Init() { |
| 60 | Service::Init(); | 68 | Service::Init(); |
| 69 | Service::FS::ArchiveInit(); | ||
| 70 | Service::CFG::CFGInit(); | ||
| 61 | 71 | ||
| 62 | RegisterAllModules(); | 72 | RegisterAllModules(); |
| 63 | 73 | ||
| 64 | NOTICE_LOG(HLE, "initialized OK"); | 74 | LOG_DEBUG(Kernel, "initialized OK"); |
| 65 | } | 75 | } |
| 66 | 76 | ||
| 67 | void Shutdown() { | 77 | void Shutdown() { |
| 78 | Service::CFG::CFGShutdown(); | ||
| 79 | Service::FS::ArchiveShutdown(); | ||
| 68 | Service::Shutdown(); | 80 | Service::Shutdown(); |
| 69 | 81 | ||
| 70 | g_module_db.clear(); | 82 | g_module_db.clear(); |
| 71 | 83 | ||
| 72 | NOTICE_LOG(HLE, "shutdown OK"); | 84 | LOG_DEBUG(Kernel, "shutdown OK"); |
| 73 | } | 85 | } |
| 74 | 86 | ||
| 75 | } // namespace | 87 | } // namespace |
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 db571b895..38705e3cd 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" |
| @@ -20,16 +20,10 @@ public: | |||
| 20 | std::string GetTypeName() const override { return "Arbiter"; } | 20 | std::string GetTypeName() const override { return "Arbiter"; } |
| 21 | std::string GetName() const override { return name; } | 21 | std::string GetName() const override { return name; } |
| 22 | 22 | ||
| 23 | static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; } | 23 | static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; |
| 24 | Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } | 24 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 25 | 25 | ||
| 26 | std::string name; ///< Name of address arbiter object (optional) | 26 | std::string name; ///< Name of address arbiter object (optional) |
| 27 | |||
| 28 | ResultVal<bool> WaitSynchronization() override { | ||
| 29 | // TODO(bunnei): ImplementMe | ||
| 30 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 31 | return UnimplementedFunction(ErrorModule::OS); | ||
| 32 | } | ||
| 33 | }; | 27 | }; |
| 34 | 28 | ||
| 35 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 29 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -53,13 +47,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | |||
| 53 | // Wait current thread (acquire the arbiter)... | 47 | // Wait current thread (acquire the arbiter)... |
| 54 | case ArbitrationType::WaitIfLessThan: | 48 | case ArbitrationType::WaitIfLessThan: |
| 55 | if ((s32)Memory::Read32(address) <= value) { | 49 | if ((s32)Memory::Read32(address) <= value) { |
| 56 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); | 50 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); |
| 57 | HLE::Reschedule(__func__); | 51 | HLE::Reschedule(__func__); |
| 58 | } | 52 | } |
| 59 | break; | 53 | break; |
| 60 | 54 | ||
| 61 | default: | 55 | default: |
| 62 | ERROR_LOG(KERNEL, "unknown type=%d", type); | 56 | LOG_ERROR(Kernel, "unknown type=%d", type); |
| 63 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); | 57 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); |
| 64 | } | 58 | } |
| 65 | return RESULT_SUCCESS; | 59 | return RESULT_SUCCESS; |
| @@ -68,7 +62,8 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | |||
| 68 | /// Create an address arbiter | 62 | /// Create an address arbiter |
| 69 | AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { | 63 | AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { |
| 70 | AddressArbiter* address_arbiter = new AddressArbiter; | 64 | AddressArbiter* address_arbiter = new AddressArbiter; |
| 71 | handle = Kernel::g_object_pool.Create(address_arbiter); | 65 | // TOOD(yuriks): Fix error reporting |
| 66 | handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE); | ||
| 72 | address_arbiter->name = name; | 67 | address_arbiter->name = name; |
| 73 | return address_arbiter; | 68 | return address_arbiter; |
| 74 | } | 69 | } |
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 e273444c9..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null | |||
| @@ -1,429 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/math_util.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive.h" | ||
| 10 | #include "core/file_sys/archive_sdmc.h" | ||
| 11 | #include "core/file_sys/directory.h" | ||
| 12 | #include "core/hle/kernel/archive.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/service.h" | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // Kernel namespace | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | // Command to access archive file | ||
| 22 | enum class FileCommand : u32 { | ||
| 23 | Dummy1 = 0x000100C6, | ||
| 24 | Control = 0x040100C4, | ||
| 25 | OpenSubFile = 0x08010100, | ||
| 26 | Read = 0x080200C2, | ||
| 27 | Write = 0x08030102, | ||
| 28 | GetSize = 0x08040000, | ||
| 29 | SetSize = 0x08050080, | ||
| 30 | GetAttributes = 0x08060000, | ||
| 31 | SetAttributes = 0x08070040, | ||
| 32 | Close = 0x08080000, | ||
| 33 | Flush = 0x08090000, | ||
| 34 | }; | ||
| 35 | |||
| 36 | // Command to access directory | ||
| 37 | enum class DirectoryCommand : u32 { | ||
| 38 | Dummy1 = 0x000100C6, | ||
| 39 | Control = 0x040100C4, | ||
| 40 | Read = 0x08010042, | ||
| 41 | Close = 0x08020000, | ||
| 42 | }; | ||
| 43 | |||
| 44 | class Archive : public Object { | ||
| 45 | public: | ||
| 46 | std::string GetTypeName() const override { return "Archive"; } | ||
| 47 | std::string GetName() const override { return name; } | ||
| 48 | |||
| 49 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; } | ||
| 50 | Kernel::HandleType GetHandleType() const override { return HandleType::Archive; } | ||
| 51 | |||
| 52 | std::string name; ///< Name of archive (optional) | ||
| 53 | FileSys::Archive* backend; ///< Archive backend interface | ||
| 54 | |||
| 55 | ResultVal<bool> SyncRequest() override { | ||
| 56 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 57 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 58 | |||
| 59 | switch (cmd) { | ||
| 60 | // Read from archive... | ||
| 61 | case FileCommand::Read: | ||
| 62 | { | ||
| 63 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 64 | u32 length = cmd_buff[3]; | ||
| 65 | u32 address = cmd_buff[5]; | ||
| 66 | |||
| 67 | // Number of bytes read | ||
| 68 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | // Write to archive... | ||
| 72 | case FileCommand::Write: | ||
| 73 | { | ||
| 74 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 75 | u32 length = cmd_buff[3]; | ||
| 76 | u32 flush = cmd_buff[4]; | ||
| 77 | u32 address = cmd_buff[6]; | ||
| 78 | |||
| 79 | // Number of bytes written | ||
| 80 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | case FileCommand::GetSize: | ||
| 84 | { | ||
| 85 | u64 filesize = (u64) backend->GetSize(); | ||
| 86 | cmd_buff[2] = (u32) filesize; // Lower word | ||
| 87 | cmd_buff[3] = (u32) (filesize >> 32); // Upper word | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | case FileCommand::SetSize: | ||
| 91 | { | ||
| 92 | backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | case FileCommand::Close: | ||
| 96 | { | ||
| 97 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 98 | CloseArchive(backend->GetIdCode()); | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | // Unknown command... | ||
| 102 | default: | ||
| 103 | { | ||
| 104 | ERROR_LOG(KERNEL, "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 | ResultVal<bool> WaitSynchronization() override { | ||
| 113 | // TODO(bunnei): ImplementMe | ||
| 114 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 115 | return UnimplementedFunction(ErrorModule::FS); | ||
| 116 | } | ||
| 117 | }; | ||
| 118 | |||
| 119 | class File : public Object { | ||
| 120 | public: | ||
| 121 | std::string GetTypeName() const override { return "File"; } | ||
| 122 | std::string GetName() const override { return path.DebugStr(); } | ||
| 123 | |||
| 124 | static Kernel::HandleType GetStaticHandleType() { return HandleType::File; } | ||
| 125 | Kernel::HandleType GetHandleType() const override { return HandleType::File; } | ||
| 126 | |||
| 127 | FileSys::Path path; ///< Path of the file | ||
| 128 | std::unique_ptr<FileSys::File> backend; ///< File backend interface | ||
| 129 | |||
| 130 | ResultVal<bool> SyncRequest() override { | ||
| 131 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 132 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 133 | switch (cmd) { | ||
| 134 | |||
| 135 | // Read from file... | ||
| 136 | case FileCommand::Read: | ||
| 137 | { | ||
| 138 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 139 | u32 length = cmd_buff[3]; | ||
| 140 | u32 address = cmd_buff[5]; | ||
| 141 | DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 142 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 143 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | |||
| 147 | // Write to file... | ||
| 148 | case FileCommand::Write: | ||
| 149 | { | ||
| 150 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 151 | u32 length = cmd_buff[3]; | ||
| 152 | u32 flush = cmd_buff[4]; | ||
| 153 | u32 address = cmd_buff[6]; | ||
| 154 | DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 155 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 156 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 157 | break; | ||
| 158 | } | ||
| 159 | |||
| 160 | case FileCommand::GetSize: | ||
| 161 | { | ||
| 162 | DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 163 | u64 size = backend->GetSize(); | ||
| 164 | cmd_buff[2] = (u32)size; | ||
| 165 | cmd_buff[3] = size >> 32; | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | |||
| 169 | case FileCommand::SetSize: | ||
| 170 | { | ||
| 171 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 172 | DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", | ||
| 173 | GetTypeName().c_str(), GetName().c_str(), size); | ||
| 174 | backend->SetSize(size); | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | |||
| 178 | case FileCommand::Close: | ||
| 179 | { | ||
| 180 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 181 | Kernel::g_object_pool.Destroy<File>(GetHandle()); | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | |||
| 185 | // Unknown command... | ||
| 186 | default: | ||
| 187 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 188 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 189 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 190 | return error; | ||
| 191 | } | ||
| 192 | cmd_buff[1] = 0; // No error | ||
| 193 | return MakeResult<bool>(false); | ||
| 194 | } | ||
| 195 | |||
| 196 | ResultVal<bool> WaitSynchronization() override { | ||
| 197 | // TODO(bunnei): ImplementMe | ||
| 198 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 199 | return UnimplementedFunction(ErrorModule::FS); | ||
| 200 | } | ||
| 201 | }; | ||
| 202 | |||
| 203 | class Directory : public Object { | ||
| 204 | public: | ||
| 205 | std::string GetTypeName() const override { return "Directory"; } | ||
| 206 | std::string GetName() const override { return path.DebugStr(); } | ||
| 207 | |||
| 208 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; } | ||
| 209 | Kernel::HandleType GetHandleType() const override { return HandleType::Directory; } | ||
| 210 | |||
| 211 | FileSys::Path path; ///< Path of the directory | ||
| 212 | std::unique_ptr<FileSys::Directory> backend; ///< File backend interface | ||
| 213 | |||
| 214 | ResultVal<bool> SyncRequest() override { | ||
| 215 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 216 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 217 | switch (cmd) { | ||
| 218 | |||
| 219 | // Read from directory... | ||
| 220 | case DirectoryCommand::Read: | ||
| 221 | { | ||
| 222 | u32 count = cmd_buff[1]; | ||
| 223 | u32 address = cmd_buff[3]; | ||
| 224 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 225 | DEBUG_LOG(KERNEL, "Read %s %s: count=%d", | ||
| 226 | GetTypeName().c_str(), GetName().c_str(), count); | ||
| 227 | |||
| 228 | // Number of entries actually read | ||
| 229 | cmd_buff[2] = backend->Read(count, entries); | ||
| 230 | break; | ||
| 231 | } | ||
| 232 | |||
| 233 | case DirectoryCommand::Close: | ||
| 234 | { | ||
| 235 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 236 | Kernel::g_object_pool.Destroy<Directory>(GetHandle()); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | |||
| 240 | // Unknown command... | ||
| 241 | default: | ||
| 242 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 243 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 244 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 245 | return error; | ||
| 246 | } | ||
| 247 | cmd_buff[1] = 0; // No error | ||
| 248 | return MakeResult<bool>(false); | ||
| 249 | } | ||
| 250 | |||
| 251 | ResultVal<bool> WaitSynchronization() override { | ||
| 252 | // TODO(bunnei): ImplementMe | ||
| 253 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 254 | return UnimplementedFunction(ErrorModule::FS); | ||
| 255 | } | ||
| 256 | }; | ||
| 257 | |||
| 258 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 259 | |||
| 260 | std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode | ||
| 261 | |||
| 262 | ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) { | ||
| 263 | auto itr = g_archive_map.find(id_code); | ||
| 264 | if (itr == g_archive_map.end()) { | ||
| 265 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 266 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 267 | } | ||
| 268 | |||
| 269 | return MakeResult<Handle>(itr->second); | ||
| 270 | } | ||
| 271 | |||
| 272 | ResultCode CloseArchive(FileSys::Archive::IdCode id_code) { | ||
| 273 | auto itr = g_archive_map.find(id_code); | ||
| 274 | if (itr == g_archive_map.end()) { | ||
| 275 | ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code); | ||
| 276 | return InvalidHandle(ErrorModule::FS); | ||
| 277 | } | ||
| 278 | |||
| 279 | INFO_LOG(KERNEL, "Closed archive %d", (int) id_code); | ||
| 280 | return RESULT_SUCCESS; | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * Mounts an archive | ||
| 285 | * @param archive Pointer to the archive to mount | ||
| 286 | */ | ||
| 287 | ResultCode MountArchive(Archive* archive) { | ||
| 288 | FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); | ||
| 289 | ResultVal<Handle> archive_handle = OpenArchive(id_code); | ||
| 290 | if (archive_handle.Succeeded()) { | ||
| 291 | ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); | ||
| 292 | return archive_handle.Code(); | ||
| 293 | } | ||
| 294 | g_archive_map[id_code] = archive->GetHandle(); | ||
| 295 | INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str()); | ||
| 296 | return RESULT_SUCCESS; | ||
| 297 | } | ||
| 298 | |||
| 299 | ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) { | ||
| 300 | Archive* archive = new Archive; | ||
| 301 | Handle handle = Kernel::g_object_pool.Create(archive); | ||
| 302 | archive->name = name; | ||
| 303 | archive->backend = backend; | ||
| 304 | |||
| 305 | ResultCode result = MountArchive(archive); | ||
| 306 | if (result.IsError()) { | ||
| 307 | return result; | ||
| 308 | } | ||
| 309 | |||
| 310 | return RESULT_SUCCESS; | ||
| 311 | } | ||
| 312 | |||
| 313 | ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { | ||
| 314 | // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create | ||
| 315 | // the archive file handles at app loading, and then keep them persistent throughout execution. | ||
| 316 | // Archives file handles are just reused and not actually freed until emulation shut down. | ||
| 317 | // Verify if real hardware works this way, or if new handles are created each time | ||
| 318 | if (path.GetType() == FileSys::Binary) | ||
| 319 | // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend | ||
| 320 | // design. While the functionally of this is OK, our implementation decision to separate | ||
| 321 | // normal files from archive file pointers is very likely wrong. | ||
| 322 | // See https://github.com/citra-emu/citra/issues/205 | ||
| 323 | return MakeResult<Handle>(archive_handle); | ||
| 324 | |||
| 325 | File* file = new File; | ||
| 326 | Handle handle = Kernel::g_object_pool.Create(file); | ||
| 327 | |||
| 328 | Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); | ||
| 329 | if (archive == nullptr) { | ||
| 330 | return InvalidHandle(ErrorModule::FS); | ||
| 331 | } | ||
| 332 | file->path = path; | ||
| 333 | file->backend = archive->backend->OpenFile(path, mode); | ||
| 334 | |||
| 335 | if (!file->backend) { | ||
| 336 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 337 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 338 | } | ||
| 339 | |||
| 340 | return MakeResult<Handle>(handle); | ||
| 341 | } | ||
| 342 | |||
| 343 | /** | ||
| 344 | * Delete a File from an Archive | ||
| 345 | * @param archive_handle Handle to an open Archive object | ||
| 346 | * @param path Path to the File inside of the Archive | ||
| 347 | * @return Whether deletion succeeded | ||
| 348 | */ | ||
| 349 | Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 350 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 351 | if (archive == nullptr) | ||
| 352 | return -1; | ||
| 353 | if (archive->backend->DeleteFile(path)) | ||
| 354 | return 0; | ||
| 355 | return -1; | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * Delete a Directory from an Archive | ||
| 360 | * @param archive_handle Handle to an open Archive object | ||
| 361 | * @param path Path to the Directory inside of the Archive | ||
| 362 | * @return Whether deletion succeeded | ||
| 363 | */ | ||
| 364 | Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 365 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 366 | if (archive == nullptr) | ||
| 367 | return -1; | ||
| 368 | if (archive->backend->DeleteDirectory(path)) | ||
| 369 | return 0; | ||
| 370 | return -1; | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Create 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 Whether creation succeeded | ||
| 378 | */ | ||
| 379 | Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 380 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 381 | if (archive == nullptr) | ||
| 382 | return -1; | ||
| 383 | if (archive->backend->CreateDirectory(path)) | ||
| 384 | return 0; | ||
| 385 | return -1; | ||
| 386 | } | ||
| 387 | |||
| 388 | /** | ||
| 389 | * Open a Directory from an Archive | ||
| 390 | * @param archive_handle Handle to an open Archive object | ||
| 391 | * @param path Path to the Directory inside of the Archive | ||
| 392 | * @return Opened Directory object | ||
| 393 | */ | ||
| 394 | ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 395 | Directory* directory = new Directory; | ||
| 396 | Handle handle = Kernel::g_object_pool.Create(directory); | ||
| 397 | |||
| 398 | Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); | ||
| 399 | if (archive == nullptr) { | ||
| 400 | return InvalidHandle(ErrorModule::FS); | ||
| 401 | } | ||
| 402 | directory->path = path; | ||
| 403 | directory->backend = archive->backend->OpenDirectory(path); | ||
| 404 | |||
| 405 | return MakeResult<Handle>(handle); | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Initialize archives | ||
| 409 | void ArchiveInit() { | ||
| 410 | g_archive_map.clear(); | ||
| 411 | |||
| 412 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 413 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 414 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 415 | |||
| 416 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 417 | auto archive = new FileSys::Archive_SDMC(sdmc_directory); | ||
| 418 | if (archive->Initialize()) | ||
| 419 | CreateArchive(archive, "SDMC"); | ||
| 420 | else | ||
| 421 | ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Shutdown archives | ||
| 425 | void ArchiveShutdown() { | ||
| 426 | g_archive_map.clear(); | ||
| 427 | } | ||
| 428 | |||
| 429 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h deleted file mode 100644 index 6fc4f0f25..000000000 --- a/src/core/hle/kernel/archive.h +++ /dev/null | |||
| @@ -1,85 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // Kernel namespace | ||
| 15 | |||
| 16 | namespace Kernel { | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Opens an archive | ||
| 20 | * @param id_code IdCode of the archive to open | ||
| 21 | * @return Handle to the opened archive | ||
| 22 | */ | ||
| 23 | ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Closes an archive | ||
| 27 | * @param id_code IdCode of the archive to open | ||
| 28 | */ | ||
| 29 | ResultCode CloseArchive(FileSys::Archive::IdCode id_code); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Creates an Archive | ||
| 33 | * @param backend File system backend interface to the archive | ||
| 34 | * @param name Name of Archive | ||
| 35 | */ | ||
| 36 | ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Open a File from an Archive | ||
| 40 | * @param archive_handle Handle to an open Archive object | ||
| 41 | * @param path Path to the File inside of the Archive | ||
| 42 | * @param mode Mode under which to open the File | ||
| 43 | * @return Handle to the opened File object | ||
| 44 | */ | ||
| 45 | ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Delete a File from an Archive | ||
| 49 | * @param archive_handle Handle to an open Archive object | ||
| 50 | * @param path Path to the File inside of the Archive | ||
| 51 | * @return Whether deletion succeeded | ||
| 52 | */ | ||
| 53 | Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Delete a Directory from an Archive | ||
| 57 | * @param archive_handle Handle to an open Archive object | ||
| 58 | * @param path Path to the Directory inside of the Archive | ||
| 59 | * @return Whether deletion succeeded | ||
| 60 | */ | ||
| 61 | Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Create a Directory from an Archive | ||
| 65 | * @param archive_handle Handle to an open Archive object | ||
| 66 | * @param path Path to the Directory inside of the Archive | ||
| 67 | * @return Whether creation of directory succeeded | ||
| 68 | */ | ||
| 69 | Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Open a Directory from an Archive | ||
| 73 | * @param archive_handle Handle to an open Archive object | ||
| 74 | * @param path Path to the Directory inside of the Archive | ||
| 75 | * @return Handle to the opened File object | ||
| 76 | */ | ||
| 77 | ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 78 | |||
| 79 | /// Initialize archives | ||
| 80 | void ArchiveInit(); | ||
| 81 | |||
| 82 | /// Shutdown archives | ||
| 83 | void ArchiveShutdown(); | ||
| 84 | |||
| 85 | } // namespace FileSys | ||
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 288080209..e43c3ee4e 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> |
| @@ -19,8 +19,8 @@ public: | |||
| 19 | std::string GetTypeName() const override { return "Event"; } | 19 | std::string GetTypeName() const override { return "Event"; } |
| 20 | std::string GetName() const override { return name; } | 20 | std::string GetName() const override { return name; } |
| 21 | 21 | ||
| 22 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } | 22 | static const HandleType HANDLE_TYPE = HandleType::Event; |
| 23 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Event; } | 23 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 24 | 24 | ||
| 25 | ResetType intitial_reset_type; ///< ResetType specified at Event initialization | 25 | ResetType intitial_reset_type; ///< ResetType specified at Event initialization |
| 26 | ResetType reset_type; ///< Current ResetType | 26 | ResetType reset_type; ///< Current ResetType |
| @@ -53,7 +53,7 @@ public: | |||
| 53 | * @return Result of operation, 0 on success, otherwise error code | 53 | * @return Result of operation, 0 on success, otherwise error code |
| 54 | */ | 54 | */ |
| 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { | 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { |
| 56 | Event* evt = g_object_pool.Get<Event>(handle); | 56 | Event* evt = g_handle_table.Get<Event>(handle); |
| 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 58 | 58 | ||
| 59 | evt->permanent_locked = permanent_locked; | 59 | evt->permanent_locked = permanent_locked; |
| @@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { | |||
| 67 | * @return Result of operation, 0 on success, otherwise error code | 67 | * @return Result of operation, 0 on success, otherwise error code |
| 68 | */ | 68 | */ |
| 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { | 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { |
| 70 | Event* evt = g_object_pool.Get<Event>(handle); | 70 | Event* evt = g_handle_table.Get<Event>(handle); |
| 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 72 | 72 | ||
| 73 | if (!evt->permanent_locked) { | 73 | if (!evt->permanent_locked) { |
| @@ -82,7 +82,7 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) { | |||
| 82 | * @return Result of operation, 0 on success, otherwise error code | 82 | * @return Result of operation, 0 on success, otherwise error code |
| 83 | */ | 83 | */ |
| 84 | ResultCode SignalEvent(const Handle handle) { | 84 | ResultCode SignalEvent(const Handle handle) { |
| 85 | Event* evt = g_object_pool.Get<Event>(handle); | 85 | Event* evt = g_handle_table.Get<Event>(handle); |
| 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 87 | 87 | ||
| 88 | // Resume threads waiting for event to signal | 88 | // Resume threads waiting for event to signal |
| @@ -110,7 +110,7 @@ ResultCode SignalEvent(const Handle handle) { | |||
| 110 | * @return Result of operation, 0 on success, otherwise error code | 110 | * @return Result of operation, 0 on success, otherwise error code |
| 111 | */ | 111 | */ |
| 112 | ResultCode ClearEvent(Handle handle) { | 112 | ResultCode ClearEvent(Handle handle) { |
| 113 | Event* evt = g_object_pool.Get<Event>(handle); | 113 | Event* evt = g_handle_table.Get<Event>(handle); |
| 114 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 114 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 115 | 115 | ||
| 116 | if (!evt->permanent_locked) { | 116 | if (!evt->permanent_locked) { |
| @@ -129,7 +129,8 @@ ResultCode ClearEvent(Handle handle) { | |||
| 129 | Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { | 129 | Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { |
| 130 | Event* evt = new Event; | 130 | Event* evt = new Event; |
| 131 | 131 | ||
| 132 | handle = Kernel::g_object_pool.Create(evt); | 132 | // TOOD(yuriks): Fix error reporting |
| 133 | handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); | ||
| 133 | 134 | ||
| 134 | evt->locked = true; | 135 | evt->locked = true; |
| 135 | evt->permanent_locked = false; | 136 | evt->permanent_locked = false; |
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 018000abd..e59ed1b57 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -1,106 +1,117 @@ | |||
| 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> | ||
| 6 | |||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | 8 | ||
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/thread.h" | 11 | #include "core/hle/kernel/thread.h" |
| 10 | #include "core/hle/kernel/archive.h" | ||
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| 14 | Handle g_main_thread = 0; | 15 | Handle g_main_thread = 0; |
| 15 | ObjectPool g_object_pool; | 16 | HandleTable g_handle_table; |
| 17 | u64 g_program_id = 0; | ||
| 16 | 18 | ||
| 17 | ObjectPool::ObjectPool() { | 19 | HandleTable::HandleTable() { |
| 18 | next_id = INITIAL_NEXT_ID; | 20 | next_generation = 1; |
| 21 | Clear(); | ||
| 19 | } | 22 | } |
| 20 | 23 | ||
| 21 | Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { | 24 | ResultVal<Handle> HandleTable::Create(Object* obj) { |
| 22 | if (range_top > MAX_COUNT) { | 25 | _dbg_assert_(Kernel, obj != nullptr); |
| 23 | range_top = MAX_COUNT; | 26 | |
| 24 | } | 27 | u16 slot = next_free_slot; |
| 25 | if (next_id >= range_bottom && next_id < range_top) { | 28 | if (slot >= generations.size()) { |
| 26 | range_bottom = next_id++; | 29 | LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); |
| 27 | } | 30 | return ERR_OUT_OF_HANDLES; |
| 28 | for (int i = range_bottom; i < range_top; i++) { | ||
| 29 | if (!occupied[i]) { | ||
| 30 | occupied[i] = true; | ||
| 31 | pool[i] = obj; | ||
| 32 | pool[i]->handle = i + HANDLE_OFFSET; | ||
| 33 | return i + HANDLE_OFFSET; | ||
| 34 | } | ||
| 35 | } | 31 | } |
| 36 | ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); | 32 | next_free_slot = generations[slot]; |
| 37 | return 0; | 33 | |
| 38 | } | 34 | u16 generation = next_generation++; |
| 39 | 35 | ||
| 40 | bool ObjectPool::IsValid(Handle handle) { | 36 | // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. |
| 41 | int index = handle - HANDLE_OFFSET; | 37 | // CTR-OS doesn't use generation 0, so skip straight to 1. |
| 42 | if (index < 0) | 38 | if (next_generation >= (1 << 15)) next_generation = 1; |
| 43 | return false; | ||
| 44 | if (index >= MAX_COUNT) | ||
| 45 | return false; | ||
| 46 | 39 | ||
| 47 | return occupied[index]; | 40 | generations[slot] = generation; |
| 41 | intrusive_ptr_add_ref(obj); | ||
| 42 | objects[slot] = obj; | ||
| 43 | |||
| 44 | Handle handle = generation | (slot << 15); | ||
| 45 | obj->handle = handle; | ||
| 46 | return MakeResult<Handle>(handle); | ||
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | void ObjectPool::Clear() { | 49 | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { |
| 51 | for (int i = 0; i < MAX_COUNT; i++) { | 50 | Object* object = GetGeneric(handle); |
| 52 | //brutally clear everything, no validation | 51 | if (object == nullptr) { |
| 53 | if (occupied[i]) | 52 | LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); |
| 54 | delete pool[i]; | 53 | return ERR_INVALID_HANDLE; |
| 55 | occupied[i] = false; | ||
| 56 | } | 54 | } |
| 57 | pool.fill(nullptr); | 55 | return Create(object); |
| 58 | next_id = INITIAL_NEXT_ID; | 56 | } |
| 57 | |||
| 58 | ResultCode HandleTable::Close(Handle handle) { | ||
| 59 | if (!IsValid(handle)) | ||
| 60 | return ERR_INVALID_HANDLE; | ||
| 61 | |||
| 62 | size_t slot = GetSlot(handle); | ||
| 63 | u16 generation = GetGeneration(handle); | ||
| 64 | |||
| 65 | intrusive_ptr_release(objects[slot]); | ||
| 66 | objects[slot] = nullptr; | ||
| 67 | |||
| 68 | generations[generation] = next_free_slot; | ||
| 69 | next_free_slot = slot; | ||
| 70 | return RESULT_SUCCESS; | ||
| 59 | } | 71 | } |
| 60 | 72 | ||
| 61 | Object* &ObjectPool::operator [](Handle handle) | 73 | bool HandleTable::IsValid(Handle handle) const { |
| 62 | { | 74 | size_t slot = GetSlot(handle); |
| 63 | _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); | 75 | u16 generation = GetGeneration(handle); |
| 64 | return pool[handle - HANDLE_OFFSET]; | 76 | |
| 77 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | ||
| 65 | } | 78 | } |
| 66 | 79 | ||
| 67 | void ObjectPool::List() { | 80 | Object* HandleTable::GetGeneric(Handle handle) const { |
| 68 | for (int i = 0; i < MAX_COUNT; i++) { | 81 | if (handle == CurrentThread) { |
| 69 | if (occupied[i]) { | 82 | // TODO(yuriks) Directly return the pointer once this is possible. |
| 70 | if (pool[i]) { | 83 | handle = GetCurrentThreadHandle(); |
| 71 | INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), | 84 | } else if (handle == CurrentProcess) { |
| 72 | pool[i]->GetName().c_str()); | 85 | LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); |
| 73 | } | 86 | return nullptr; |
| 74 | } | ||
| 75 | } | 87 | } |
| 76 | } | ||
| 77 | 88 | ||
| 78 | int ObjectPool::GetCount() { | 89 | if (!IsValid(handle)) { |
| 79 | int count = 0; | 90 | return nullptr; |
| 80 | for (int i = 0; i < MAX_COUNT; i++) { | ||
| 81 | if (occupied[i]) | ||
| 82 | count++; | ||
| 83 | } | 91 | } |
| 84 | return count; | 92 | return objects[GetSlot(handle)]; |
| 85 | } | 93 | } |
| 86 | 94 | ||
| 87 | Object* ObjectPool::CreateByIDType(int type) { | 95 | void HandleTable::Clear() { |
| 88 | ERROR_LOG(COMMON, "Unimplemented: %d.", type); | 96 | for (size_t i = 0; i < MAX_COUNT; ++i) { |
| 89 | return nullptr; | 97 | generations[i] = i + 1; |
| 98 | if (objects[i] != nullptr) | ||
| 99 | intrusive_ptr_release(objects[i]); | ||
| 100 | objects[i] = nullptr; | ||
| 101 | } | ||
| 102 | next_free_slot = 0; | ||
| 90 | } | 103 | } |
| 91 | 104 | ||
| 92 | /// Initialize the kernel | 105 | /// Initialize the kernel |
| 93 | void Init() { | 106 | void Init() { |
| 94 | Kernel::ThreadingInit(); | 107 | Kernel::ThreadingInit(); |
| 95 | Kernel::ArchiveInit(); | ||
| 96 | } | 108 | } |
| 97 | 109 | ||
| 98 | /// Shutdown the kernel | 110 | /// Shutdown the kernel |
| 99 | void Shutdown() { | 111 | void Shutdown() { |
| 100 | Kernel::ThreadingShutdown(); | 112 | Kernel::ThreadingShutdown(); |
| 101 | Kernel::ArchiveShutdown(); | ||
| 102 | 113 | ||
| 103 | g_object_pool.Clear(); // Free all kernel objects | 114 | g_handle_table.Clear(); // Free all kernel objects |
| 104 | } | 115 | } |
| 105 | 116 | ||
| 106 | /** | 117 | /** |
| @@ -109,8 +120,6 @@ void Shutdown() { | |||
| 109 | * @return True on success, otherwise false | 120 | * @return True on success, otherwise false |
| 110 | */ | 121 | */ |
| 111 | bool LoadExec(u32 entry_point) { | 122 | bool LoadExec(u32 entry_point) { |
| 112 | Init(); | ||
| 113 | |||
| 114 | Core::g_app_core->SetPC(entry_point); | 123 | Core::g_app_core->SetPC(entry_point); |
| 115 | 124 | ||
| 116 | // 0x30 is the typical main thread priority I've seen used so far | 125 | // 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 8d3937ce8..7f86fd07d 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 |
| @@ -12,9 +12,17 @@ | |||
| 12 | typedef u32 Handle; | 12 | typedef u32 Handle; |
| 13 | typedef s32 Result; | 13 | typedef s32 Result; |
| 14 | 14 | ||
| 15 | const Handle INVALID_HANDLE = 0; | ||
| 16 | |||
| 15 | namespace Kernel { | 17 | namespace Kernel { |
| 16 | 18 | ||
| 17 | enum KernelHandle { | 19 | // TODO: Verify code |
| 20 | const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, | ||
| 21 | ErrorSummary::OutOfResource, ErrorLevel::Temporary); | ||
| 22 | // TOOD: Verify code | ||
| 23 | const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel); | ||
| 24 | |||
| 25 | enum KernelHandle : Handle { | ||
| 18 | CurrentThread = 0xFFFF8000, | 26 | CurrentThread = 0xFFFF8000, |
| 19 | CurrentProcess = 0xFFFF8001, | 27 | CurrentProcess = 0xFFFF8001, |
| 20 | }; | 28 | }; |
| @@ -22,7 +30,7 @@ enum KernelHandle { | |||
| 22 | enum class HandleType : u32 { | 30 | enum class HandleType : u32 { |
| 23 | Unknown = 0, | 31 | Unknown = 0, |
| 24 | Port = 1, | 32 | Port = 1, |
| 25 | Service = 2, | 33 | Session = 2, |
| 26 | Event = 3, | 34 | Event = 3, |
| 27 | Mutex = 4, | 35 | Mutex = 4, |
| 28 | SharedMemory = 5, | 36 | SharedMemory = 5, |
| @@ -30,20 +38,17 @@ enum class HandleType : u32 { | |||
| 30 | Thread = 7, | 38 | Thread = 7, |
| 31 | Process = 8, | 39 | Process = 8, |
| 32 | AddressArbiter = 9, | 40 | AddressArbiter = 9, |
| 33 | File = 10, | 41 | Semaphore = 10, |
| 34 | Semaphore = 11, | ||
| 35 | Archive = 12, | ||
| 36 | Directory = 13, | ||
| 37 | }; | 42 | }; |
| 38 | 43 | ||
| 39 | enum { | 44 | enum { |
| 40 | DEFAULT_STACK_SIZE = 0x4000, | 45 | DEFAULT_STACK_SIZE = 0x4000, |
| 41 | }; | 46 | }; |
| 42 | 47 | ||
| 43 | class ObjectPool; | 48 | class HandleTable; |
| 44 | 49 | ||
| 45 | class Object : NonCopyable { | 50 | class Object : NonCopyable { |
| 46 | friend class ObjectPool; | 51 | friend class HandleTable; |
| 47 | u32 handle; | 52 | u32 handle; |
| 48 | public: | 53 | public: |
| 49 | virtual ~Object() {} | 54 | virtual ~Object() {} |
| @@ -53,113 +58,145 @@ public: | |||
| 53 | virtual Kernel::HandleType GetHandleType() const = 0; | 58 | virtual Kernel::HandleType GetHandleType() const = 0; |
| 54 | 59 | ||
| 55 | /** | 60 | /** |
| 56 | * Synchronize kernel object. | 61 | * Wait for kernel object to synchronize. |
| 57 | * @return True if the current thread should wait as a result of the sync | 62 | * @return True if the current thread should wait as a result of the wait |
| 58 | */ | 63 | */ |
| 59 | virtual ResultVal<bool> SyncRequest() { | 64 | virtual ResultVal<bool> WaitSynchronization() { |
| 60 | ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); | 65 | LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); |
| 61 | return UnimplementedFunction(ErrorModule::Kernel); | 66 | return UnimplementedFunction(ErrorModule::Kernel); |
| 62 | } | 67 | } |
| 63 | 68 | ||
| 64 | /** | 69 | private: |
| 65 | * Wait for kernel object to synchronize. | 70 | friend void intrusive_ptr_add_ref(Object*); |
| 66 | * @return True if the current thread should wait as a result of the wait | 71 | friend void intrusive_ptr_release(Object*); |
| 67 | */ | 72 | |
| 68 | virtual ResultVal<bool> WaitSynchronization() = 0; | 73 | unsigned int ref_count = 0; |
| 69 | }; | 74 | }; |
| 70 | 75 | ||
| 71 | class ObjectPool : NonCopyable { | 76 | // Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting |
| 72 | public: | 77 | inline void intrusive_ptr_add_ref(Object* object) { |
| 73 | ObjectPool(); | 78 | ++object->ref_count; |
| 74 | ~ObjectPool() {} | 79 | } |
| 75 | 80 | ||
| 76 | // Allocates a handle within the range and inserts the object into the map. | 81 | inline void intrusive_ptr_release(Object* object) { |
| 77 | Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); | 82 | if (--object->ref_count == 0) { |
| 83 | delete object; | ||
| 84 | } | ||
| 85 | } | ||
| 78 | 86 | ||
| 79 | static Object* CreateByIDType(int type); | 87 | /** |
| 88 | * This class allows the creation of Handles, which are references to objects that can be tested | ||
| 89 | * for validity and looked up. Here they are used to pass references to kernel objects to/from the | ||
| 90 | * emulated process. it has been designed so that it follows the same handle format and has | ||
| 91 | * approximately the same restrictions as the handle manager in the CTR-OS. | ||
| 92 | * | ||
| 93 | * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). | ||
| 94 | * The slot index is used to index into the arrays in this class to access the data corresponding | ||
| 95 | * to the Handle. | ||
| 96 | * | ||
| 97 | * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter | ||
| 98 | * is kept and incremented every time a Handle is created. This is the Handle's "generation". The | ||
| 99 | * value of the counter is stored into the Handle as well as in the handle table (in the | ||
| 100 | * "generations" array). When looking up a handle, the Handle's generation must match with the | ||
| 101 | * value stored on the class, otherwise the Handle is considered invalid. | ||
| 102 | * | ||
| 103 | * To find free slots when allocating a Handle without needing to scan the entire object array, the | ||
| 104 | * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. | ||
| 105 | * When a Handle is created, an index is popped off the list and used for the new Handle. When it | ||
| 106 | * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is | ||
| 107 | * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been | ||
| 108 | * verified and isn't likely to cause any problems. | ||
| 109 | */ | ||
| 110 | class HandleTable final : NonCopyable { | ||
| 111 | public: | ||
| 112 | HandleTable(); | ||
| 80 | 113 | ||
| 81 | template <class T> | 114 | /** |
| 82 | void Destroy(Handle handle) { | 115 | * Allocates a handle for the given object. |
| 83 | if (Get<T>(handle)) { | 116 | * @return The created Handle or one of the following errors: |
| 84 | occupied[handle - HANDLE_OFFSET] = false; | 117 | * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. |
| 85 | delete pool[handle - HANDLE_OFFSET]; | 118 | */ |
| 86 | } | 119 | ResultVal<Handle> Create(Object* obj); |
| 87 | } | ||
| 88 | 120 | ||
| 89 | bool IsValid(Handle handle); | 121 | /** |
| 122 | * Returns a new handle that points to the same object as the passed in handle. | ||
| 123 | * @return The duplicated Handle or one of the following errors: | ||
| 124 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||
| 125 | * - Any errors returned by `Create()`. | ||
| 126 | */ | ||
| 127 | ResultVal<Handle> Duplicate(Handle handle); | ||
| 90 | 128 | ||
| 91 | template <class T> | 129 | /** |
| 92 | T* Get(Handle handle) { | 130 | * Closes a handle, removing it from the table and decreasing the object's ref-count. |
| 93 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { | 131 | * @return `RESULT_SUCCESS` or one of the following errors: |
| 94 | if (handle != 0) { | 132 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. |
| 95 | WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | 133 | */ |
| 96 | } | 134 | ResultCode Close(Handle handle); |
| 97 | return nullptr; | ||
| 98 | } else { | ||
| 99 | Object* t = pool[handle - HANDLE_OFFSET]; | ||
| 100 | if (t->GetHandleType() != T::GetStaticHandleType()) { | ||
| 101 | WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); | ||
| 102 | return nullptr; | ||
| 103 | } | ||
| 104 | return static_cast<T*>(t); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | 135 | ||
| 108 | // ONLY use this when you know the handle is valid. | 136 | /// Checks if a handle is valid and points to an existing object. |
| 109 | template <class T> | 137 | bool IsValid(Handle handle) const; |
| 110 | T *GetFast(Handle handle) { | ||
| 111 | const Handle realHandle = handle - HANDLE_OFFSET; | ||
| 112 | _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); | ||
| 113 | return static_cast<T*>(pool[realHandle]); | ||
| 114 | } | ||
| 115 | 138 | ||
| 116 | template <class T, typename ArgT> | 139 | /** |
| 117 | void Iterate(bool func(T*, ArgT), ArgT arg) { | 140 | * Looks up a handle. |
| 118 | int type = T::GetStaticIDType(); | 141 | * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. |
| 119 | for (int i = 0; i < MAX_COUNT; i++) | 142 | */ |
| 120 | { | 143 | Object* GetGeneric(Handle handle) const; |
| 121 | if (!occupied[i]) | ||
| 122 | continue; | ||
| 123 | T* t = static_cast<T*>(pool[i]); | ||
| 124 | if (t->GetIDType() == type) { | ||
| 125 | if (!func(t, arg)) | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | 144 | ||
| 131 | bool GetIDType(Handle handle, HandleType* type) const { | 145 | /** |
| 132 | if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || | 146 | * Looks up a handle while verifying its type. |
| 133 | !occupied[handle - HANDLE_OFFSET]) { | 147 | * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its |
| 134 | ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | 148 | * type differs from the handle type `T::HANDLE_TYPE`. |
| 135 | return false; | 149 | */ |
| 150 | template <class T> | ||
| 151 | T* Get(Handle handle) const { | ||
| 152 | Object* object = GetGeneric(handle); | ||
| 153 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | ||
| 154 | return static_cast<T*>(object); | ||
| 136 | } | 155 | } |
| 137 | Object* t = pool[handle - HANDLE_OFFSET]; | 156 | return nullptr; |
| 138 | *type = t->GetHandleType(); | ||
| 139 | return true; | ||
| 140 | } | 157 | } |
| 141 | 158 | ||
| 142 | Object* &operator [](Handle handle); | 159 | /// Closes all handles held in this table. |
| 143 | void List(); | ||
| 144 | void Clear(); | 160 | void Clear(); |
| 145 | int GetCount(); | ||
| 146 | 161 | ||
| 147 | private: | 162 | private: |
| 163 | /** | ||
| 164 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | ||
| 165 | * reduced by ExHeader values, but this is not emulated here. | ||
| 166 | */ | ||
| 167 | static const size_t MAX_COUNT = 4096; | ||
| 148 | 168 | ||
| 149 | enum { | 169 | static size_t GetSlot(Handle handle) { return handle >> 15; } |
| 150 | MAX_COUNT = 0x1000, | 170 | static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; } |
| 151 | HANDLE_OFFSET = 0x100, | ||
| 152 | INITIAL_NEXT_ID = 0x10, | ||
| 153 | }; | ||
| 154 | 171 | ||
| 155 | std::array<Object*, MAX_COUNT> pool; | 172 | /// Stores the Object referenced by the handle or null if the slot is empty. |
| 156 | std::array<bool, MAX_COUNT> occupied; | 173 | std::array<Object*, MAX_COUNT> objects; |
| 157 | int next_id; | 174 | |
| 175 | /** | ||
| 176 | * The value of `next_generation` when the handle was created, used to check for validity. For | ||
| 177 | * empty slots, contains the index of the next free slot in the list. | ||
| 178 | */ | ||
| 179 | std::array<u16, MAX_COUNT> generations; | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Global counter of the number of created handles. Stored in `generations` when a handle is | ||
| 183 | * created, and wraps around to 1 when it hits 0x8000. | ||
| 184 | */ | ||
| 185 | u16 next_generation; | ||
| 186 | |||
| 187 | /// Head of the free slots linked list. | ||
| 188 | u16 next_free_slot; | ||
| 158 | }; | 189 | }; |
| 159 | 190 | ||
| 160 | extern ObjectPool g_object_pool; | 191 | extern HandleTable g_handle_table; |
| 161 | extern Handle g_main_thread; | 192 | extern Handle g_main_thread; |
| 162 | 193 | ||
| 194 | /// The ID code of the currently running game | ||
| 195 | /// TODO(Subv): This variable should not be here, | ||
| 196 | /// we need a way to store information about the currently loaded application | ||
| 197 | /// for later query during runtime, maybe using the LDR service? | ||
| 198 | extern u64 g_program_id; | ||
| 199 | |||
| 163 | /// Initialize the kernel | 200 | /// Initialize the kernel |
| 164 | void Init(); | 201 | void Init(); |
| 165 | 202 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index b303ba128..558068c79 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> |
| @@ -18,8 +18,8 @@ public: | |||
| 18 | std::string GetTypeName() const override { return "Mutex"; } | 18 | std::string GetTypeName() const override { return "Mutex"; } |
| 19 | std::string GetName() const override { return name; } | 19 | std::string GetName() const override { return name; } |
| 20 | 20 | ||
| 21 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } | 21 | static const HandleType HANDLE_TYPE = HandleType::Mutex; |
| 22 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; } | 22 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 23 | 23 | ||
| 24 | bool initial_locked; ///< Initial lock state when mutex was created | 24 | bool initial_locked; ///< Initial lock state when mutex was created |
| 25 | bool locked; ///< Current locked state | 25 | bool locked; ///< Current locked state |
| @@ -27,21 +27,7 @@ public: | |||
| 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex | 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex |
| 28 | std::string name; ///< Name of mutex (optional) | 28 | std::string name; ///< Name of mutex (optional) |
| 29 | 29 | ||
| 30 | ResultVal<bool> SyncRequest() override { | 30 | ResultVal<bool> WaitSynchronization() override; |
| 31 | // TODO(bunnei): ImplementMe | ||
| 32 | locked = true; | ||
| 33 | return MakeResult<bool>(false); | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultVal<bool> WaitSynchronization() override { | ||
| 37 | // TODO(bunnei): ImplementMe | ||
| 38 | bool wait = locked; | ||
| 39 | if (locked) { | ||
| 40 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | ||
| 41 | } | ||
| 42 | |||
| 43 | return MakeResult<bool>(wait); | ||
| 44 | } | ||
| 45 | }; | 31 | }; |
| 46 | 32 | ||
| 47 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 33 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -49,21 +35,46 @@ public: | |||
| 49 | typedef std::multimap<Handle, Handle> MutexMap; | 35 | typedef std::multimap<Handle, Handle> MutexMap; |
| 50 | static MutexMap g_mutex_held_locks; | 36 | static MutexMap g_mutex_held_locks; |
| 51 | 37 | ||
| 52 | void MutexAcquireLock(Mutex* mutex, Handle thread) { | 38 | /** |
| 39 | * Acquires the specified mutex for the specified thread | ||
| 40 | * @param mutex Mutex that is to be acquired | ||
| 41 | * @param thread Thread that will acquired | ||
| 42 | */ | ||
| 43 | void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { | ||
| 53 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); | 44 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); |
| 54 | mutex->lock_thread = thread; | 45 | mutex->lock_thread = thread; |
| 55 | } | 46 | } |
| 56 | 47 | ||
| 57 | void MutexAcquireLock(Mutex* mutex) { | 48 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { |
| 58 | Handle thread = GetCurrentThreadHandle(); | ||
| 59 | MutexAcquireLock(mutex, thread); | 49 | MutexAcquireLock(mutex, thread); |
| 50 | Kernel::ResumeThreadFromWait(thread); | ||
| 51 | return true; | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Resumes a thread waiting for the specified mutex | ||
| 56 | * @param mutex The mutex that some thread is waiting on | ||
| 57 | */ | ||
| 58 | void ResumeWaitingThread(Mutex* mutex) { | ||
| 59 | // Find the next waiting thread for the mutex... | ||
| 60 | if (mutex->waiting_threads.empty()) { | ||
| 61 | // Reset mutex lock thread handle, nothing is waiting | ||
| 62 | mutex->locked = false; | ||
| 63 | mutex->lock_thread = -1; | ||
| 64 | } | ||
| 65 | else { | ||
| 66 | // Resume the next waiting thread and re-lock the mutex | ||
| 67 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 68 | ReleaseMutexForThread(mutex, *iter); | ||
| 69 | mutex->waiting_threads.erase(iter); | ||
| 70 | } | ||
| 60 | } | 71 | } |
| 61 | 72 | ||
| 62 | void MutexEraseLock(Mutex* mutex) { | 73 | void MutexEraseLock(Mutex* mutex) { |
| 63 | Handle handle = mutex->GetHandle(); | 74 | Handle handle = mutex->GetHandle(); |
| 64 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); | 75 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); |
| 65 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | 76 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { |
| 66 | if ((*iter).second == handle) { | 77 | if (iter->second == handle) { |
| 67 | g_mutex_held_locks.erase(iter); | 78 | g_mutex_held_locks.erase(iter); |
| 68 | break; | 79 | break; |
| 69 | } | 80 | } |
| @@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) { | |||
| 71 | mutex->lock_thread = -1; | 82 | mutex->lock_thread = -1; |
| 72 | } | 83 | } |
| 73 | 84 | ||
| 85 | void ReleaseThreadMutexes(Handle thread) { | ||
| 86 | auto locked = g_mutex_held_locks.equal_range(thread); | ||
| 87 | |||
| 88 | // Release every mutex that the thread holds, and resume execution on the waiting threads | ||
| 89 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | ||
| 90 | Mutex* mutex = g_handle_table.Get<Mutex>(iter->second); | ||
| 91 | ResumeWaitingThread(mutex); | ||
| 92 | } | ||
| 93 | |||
| 94 | // Erase all the locks that this thread holds | ||
| 95 | g_mutex_held_locks.erase(thread); | ||
| 96 | } | ||
| 97 | |||
| 74 | bool LockMutex(Mutex* mutex) { | 98 | bool LockMutex(Mutex* mutex) { |
| 75 | // Mutex alread locked? | 99 | // Mutex alread locked? |
| 76 | if (mutex->locked) { | 100 | if (mutex->locked) { |
| @@ -80,28 +104,10 @@ bool LockMutex(Mutex* mutex) { | |||
| 80 | return true; | 104 | return true; |
| 81 | } | 105 | } |
| 82 | 106 | ||
| 83 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | ||
| 84 | MutexAcquireLock(mutex, thread); | ||
| 85 | Kernel::ResumeThreadFromWait(thread); | ||
| 86 | return true; | ||
| 87 | } | ||
| 88 | |||
| 89 | bool ReleaseMutex(Mutex* mutex) { | 107 | bool ReleaseMutex(Mutex* mutex) { |
| 90 | MutexEraseLock(mutex); | 108 | MutexEraseLock(mutex); |
| 91 | bool woke_threads = false; | 109 | ResumeWaitingThread(mutex); |
| 92 | 110 | return true; | |
| 93 | // Find the next waiting thread for the mutex... | ||
| 94 | while (!woke_threads && !mutex->waiting_threads.empty()) { | ||
| 95 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 96 | woke_threads |= ReleaseMutexForThread(mutex, *iter); | ||
| 97 | mutex->waiting_threads.erase(iter); | ||
| 98 | } | ||
| 99 | // Reset mutex lock thread handle, nothing is waiting | ||
| 100 | if (!woke_threads) { | ||
| 101 | mutex->locked = false; | ||
| 102 | mutex->lock_thread = -1; | ||
| 103 | } | ||
| 104 | return woke_threads; | ||
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | /** | 113 | /** |
| @@ -109,7 +115,7 @@ bool ReleaseMutex(Mutex* mutex) { | |||
| 109 | * @param handle Handle to mutex to release | 115 | * @param handle Handle to mutex to release |
| 110 | */ | 116 | */ |
| 111 | ResultCode ReleaseMutex(Handle handle) { | 117 | ResultCode ReleaseMutex(Handle handle) { |
| 112 | Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); | 118 | Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle); |
| 113 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); | 119 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 114 | 120 | ||
| 115 | if (!ReleaseMutex(mutex)) { | 121 | if (!ReleaseMutex(mutex)) { |
| @@ -130,7 +136,8 @@ ResultCode ReleaseMutex(Handle handle) { | |||
| 130 | */ | 136 | */ |
| 131 | Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { | 137 | Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { |
| 132 | Mutex* mutex = new Mutex; | 138 | Mutex* mutex = new Mutex; |
| 133 | handle = Kernel::g_object_pool.Create(mutex); | 139 | // TODO(yuriks): Fix error reporting |
| 140 | handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE); | ||
| 134 | 141 | ||
| 135 | mutex->locked = mutex->initial_locked = initial_locked; | 142 | mutex->locked = mutex->initial_locked = initial_locked; |
| 136 | mutex->name = name; | 143 | mutex->name = name; |
| @@ -158,4 +165,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | |||
| 158 | return handle; | 165 | return handle; |
| 159 | } | 166 | } |
| 160 | 167 | ||
| 168 | ResultVal<bool> Mutex::WaitSynchronization() { | ||
| 169 | bool wait = locked; | ||
| 170 | if (locked) { | ||
| 171 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | ||
| 172 | } | ||
| 173 | else { | ||
| 174 | // Lock the mutex when the first thread accesses it | ||
| 175 | locked = true; | ||
| 176 | MutexAcquireLock(this); | ||
| 177 | } | ||
| 178 | |||
| 179 | return MakeResult<bool>(wait); | ||
| 180 | } | ||
| 161 | } // namespace | 181 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 155449f95..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 |
| @@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle); | |||
| 24 | */ | 24 | */ |
| 25 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); | 25 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); |
| 26 | 26 | ||
| 27 | /** | ||
| 28 | * Releases all the mutexes held by the specified thread | ||
| 29 | * @param thread Thread that is holding the mutexes | ||
| 30 | */ | ||
| 31 | void ReleaseThreadMutexes(Handle thread); | ||
| 32 | |||
| 27 | } // namespace | 33 | } // namespace |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp new file mode 100644 index 000000000..6bc8066a6 --- /dev/null +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -0,0 +1,95 @@ | |||
| 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 <queue> | ||
| 6 | |||
| 7 | #include "common/common.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/hle/kernel/semaphore.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | class Semaphore : public Object { | ||
| 16 | public: | ||
| 17 | std::string GetTypeName() const override { return "Semaphore"; } | ||
| 18 | std::string GetName() const override { return name; } | ||
| 19 | |||
| 20 | static const HandleType HANDLE_TYPE = HandleType::Semaphore; | ||
| 21 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 22 | |||
| 23 | s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have | ||
| 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 | ||
| 26 | std::string name; ///< Name of semaphore (optional) | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Tests whether a semaphore still has free slots | ||
| 30 | * @return Whether the semaphore is available | ||
| 31 | */ | ||
| 32 | bool IsAvailable() const { | ||
| 33 | return available_count > 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultVal<bool> WaitSynchronization() override { | ||
| 37 | bool wait = !IsAvailable(); | ||
| 38 | |||
| 39 | if (wait) { | ||
| 40 | Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); | ||
| 41 | waiting_threads.push(GetCurrentThreadHandle()); | ||
| 42 | } else { | ||
| 43 | --available_count; | ||
| 44 | } | ||
| 45 | |||
| 46 | return MakeResult<bool>(wait); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 51 | |||
| 52 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, | ||
| 53 | s32 max_count, const std::string& name) { | ||
| 54 | |||
| 55 | if (initial_count > max_count) | ||
| 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, | ||
| 57 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 58 | |||
| 59 | Semaphore* semaphore = new Semaphore; | ||
| 60 | // TOOD(yuriks): Fix error reporting | ||
| 61 | *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE); | ||
| 62 | |||
| 63 | // When the semaphore is created, some slots are reserved for other threads, | ||
| 64 | // and the rest is reserved for the caller thread | ||
| 65 | semaphore->max_count = max_count; | ||
| 66 | semaphore->available_count = initial_count; | ||
| 67 | semaphore->name = name; | ||
| 68 | |||
| 69 | return RESULT_SUCCESS; | ||
| 70 | } | ||
| 71 | |||
| 72 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { | ||
| 73 | Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle); | ||
| 74 | if (semaphore == nullptr) | ||
| 75 | return InvalidHandle(ErrorModule::Kernel); | ||
| 76 | |||
| 77 | if (semaphore->max_count - semaphore->available_count < release_count) | ||
| 78 | return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, | ||
| 79 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 80 | |||
| 81 | *count = semaphore->available_count; | ||
| 82 | semaphore->available_count += release_count; | ||
| 83 | |||
| 84 | // Notify some of the threads that the semaphore has been released | ||
| 85 | // stop once the semaphore is full again or there are no more waiting threads | ||
| 86 | while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { | ||
| 87 | Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); | ||
| 88 | semaphore->waiting_threads.pop(); | ||
| 89 | --semaphore->available_count; | ||
| 90 | } | ||
| 91 | |||
| 92 | return RESULT_SUCCESS; | ||
| 93 | } | ||
| 94 | |||
| 95 | } // namespace | ||
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h new file mode 100644 index 000000000..8644ecf0c --- /dev/null +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -0,0 +1,32 @@ | |||
| 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/hle/kernel/kernel.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Creates a semaphore. | ||
| 15 | * @param handle Pointer to the handle of the newly created object | ||
| 16 | * @param initial_count Number of slots reserved for other threads | ||
| 17 | * @param max_count Maximum number of slots the semaphore can have | ||
| 18 | * @param name Optional name of semaphore | ||
| 19 | * @return ResultCode of the error | ||
| 20 | */ | ||
| 21 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Releases a certain number of slots from a semaphore. | ||
| 25 | * @param count The number of free slots the semaphore had before this call | ||
| 26 | * @param handle The handle of the semaphore to release | ||
| 27 | * @param release_count The number of slots to release | ||
| 28 | * @return ResultCode of the error | ||
| 29 | */ | ||
| 30 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); | ||
| 31 | |||
| 32 | } // namespace | ||
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h new file mode 100644 index 000000000..91f3ffc2c --- /dev/null +++ b/src/core/hle/kernel/session.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Returns a pointer to the command buffer in kernel memory | ||
| 15 | * @param offset Optional offset into command buffer | ||
| 16 | * @return Pointer to command buffer | ||
| 17 | */ | ||
| 18 | inline static u32* GetCommandBuffer(const int offset=0) { | ||
| 19 | return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||
| 20 | } | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS | ||
| 24 | * primitive for communication between different processes, and are used to implement service calls | ||
| 25 | * to the various system services. | ||
| 26 | * | ||
| 27 | * To make a service call, the client must write the command header and parameters to the buffer | ||
| 28 | * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest | ||
| 29 | * SVC call with its Session handle. The kernel will read the command header, using it to marshall | ||
| 30 | * the parameters to the process at the server endpoint of the session. After the server replies to | ||
| 31 | * the request, the response is marshalled back to the caller's TLS buffer and control is | ||
| 32 | * transferred back to it. | ||
| 33 | * | ||
| 34 | * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC | ||
| 35 | * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called | ||
| 36 | * with the session handle, this class's SyncRequest method is called, which should read the TLS | ||
| 37 | * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, | ||
| 38 | * no parameter marshalling is done. | ||
| 39 | * | ||
| 40 | * In the long term, this should be turned into the full-fledged IPC mechanism implemented by | ||
| 41 | * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as | ||
| 42 | * opposed to HLE simulations. | ||
| 43 | */ | ||
| 44 | class Session : public Object { | ||
| 45 | public: | ||
| 46 | std::string GetTypeName() const override { return "Session"; } | ||
| 47 | |||
| 48 | static const HandleType HANDLE_TYPE = HandleType::Session; | ||
| 49 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 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 cfcc0e0b7..cea1f6fa1 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" |
| @@ -13,14 +13,8 @@ class SharedMemory : public Object { | |||
| 13 | public: | 13 | public: |
| 14 | std::string GetTypeName() const override { return "SharedMemory"; } | 14 | std::string GetTypeName() const override { return "SharedMemory"; } |
| 15 | 15 | ||
| 16 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } | 16 | static const HandleType HANDLE_TYPE = HandleType::SharedMemory; |
| 17 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } | 17 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 18 | |||
| 19 | ResultVal<bool> WaitSynchronization() override { | ||
| 20 | // TODO(bunnei): ImplementMe | ||
| 21 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 22 | return UnimplementedFunction(ErrorModule::OS); | ||
| 23 | } | ||
| 24 | 18 | ||
| 25 | u32 base_address; ///< Address of shared memory block in RAM | 19 | u32 base_address; ///< Address of shared memory block in RAM |
| 26 | MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) | 20 | MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) |
| @@ -38,7 +32,8 @@ public: | |||
| 38 | */ | 32 | */ |
| 39 | SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { | 33 | SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { |
| 40 | SharedMemory* shared_memory = new SharedMemory; | 34 | SharedMemory* shared_memory = new SharedMemory; |
| 41 | handle = Kernel::g_object_pool.Create(shared_memory); | 35 | // TOOD(yuriks): Fix error reporting |
| 36 | handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE); | ||
| 42 | shared_memory->name = name; | 37 | shared_memory->name = name; |
| 43 | return shared_memory; | 38 | return shared_memory; |
| 44 | } | 39 | } |
| @@ -61,12 +56,12 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions | |||
| 61 | MemoryPermission other_permissions) { | 56 | MemoryPermission other_permissions) { |
| 62 | 57 | ||
| 63 | if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { | 58 | if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { |
| 64 | ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", | 59 | LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", |
| 65 | handle, address); | 60 | handle, address); |
| 66 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 61 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| 67 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | 62 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); |
| 68 | } | 63 | } |
| 69 | SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); | 64 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); |
| 70 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); | 65 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 71 | 66 | ||
| 72 | shared_memory->base_address = address; | 67 | shared_memory->base_address = address; |
| @@ -77,13 +72,13 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions | |||
| 77 | } | 72 | } |
| 78 | 73 | ||
| 79 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { | 74 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { |
| 80 | SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); | 75 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); |
| 81 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); | 76 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 82 | 77 | ||
| 83 | if (0 != shared_memory->base_address) | 78 | if (0 != shared_memory->base_address) |
| 84 | return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); | 79 | return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); |
| 85 | 80 | ||
| 86 | ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); | 81 | LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); |
| 87 | // TODO(yuriks): Verify error code. | 82 | // TODO(yuriks): Verify error code. |
| 88 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 83 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| 89 | ErrorSummary::InvalidState, ErrorLevel::Permanent); | 84 | ErrorSummary::InvalidState, ErrorLevel::Permanent); |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 304cf5b67..bb65c7ccd 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -12,11 +12,15 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | /// Permissions for mapped shared memory blocks | 13 | /// Permissions for mapped shared memory blocks |
| 14 | enum class MemoryPermission : u32 { | 14 | enum class MemoryPermission : u32 { |
| 15 | None = 0, | 15 | None = 0, |
| 16 | Read = (1u << 0), | 16 | Read = (1u << 0), |
| 17 | Write = (1u << 1), | 17 | Write = (1u << 1), |
| 18 | ReadWrite = (Read | Write), | 18 | ReadWrite = (Read | Write), |
| 19 | DontCare = (1u << 28) | 19 | Execute = (1u << 2), |
| 20 | ReadExecute = (Read | Execute), | ||
| 21 | WriteExecute = (Write | Execute), | ||
| 22 | ReadWriteExecute = (Read | Write | Execute), | ||
| 23 | DontCare = (1u << 28) | ||
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | /** | 26 | /** |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f3f54a4e9..872df2d14 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> |
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/hle/hle.h" | 14 | #include "core/hle/hle.h" |
| 15 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 16 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 17 | #include "core/hle/kernel/mutex.h" | ||
| 17 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 18 | #include "core/mem_map.h" | 19 | #include "core/mem_map.h" |
| 19 | 20 | ||
| @@ -25,8 +26,8 @@ public: | |||
| 25 | std::string GetName() const override { return name; } | 26 | std::string GetName() const override { return name; } |
| 26 | std::string GetTypeName() const override { return "Thread"; } | 27 | std::string GetTypeName() const override { return "Thread"; } |
| 27 | 28 | ||
| 28 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } | 29 | static const HandleType HANDLE_TYPE = HandleType::Thread; |
| 29 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; } | 30 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 30 | 31 | ||
| 31 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | 32 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } |
| 32 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | 33 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } |
| @@ -49,6 +50,8 @@ public: | |||
| 49 | 50 | ||
| 50 | ThreadContext context; | 51 | ThreadContext context; |
| 51 | 52 | ||
| 53 | u32 thread_id; | ||
| 54 | |||
| 52 | u32 status; | 55 | u32 status; |
| 53 | u32 entry_point; | 56 | u32 entry_point; |
| 54 | u32 stack_top; | 57 | u32 stack_top; |
| @@ -61,6 +64,7 @@ public: | |||
| 61 | 64 | ||
| 62 | WaitType wait_type; | 65 | WaitType wait_type; |
| 63 | Handle wait_handle; | 66 | Handle wait_handle; |
| 67 | VAddr wait_address; | ||
| 64 | 68 | ||
| 65 | std::vector<Handle> waiting_threads; | 69 | std::vector<Handle> waiting_threads; |
| 66 | 70 | ||
| @@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue; | |||
| 76 | static Handle current_thread_handle; | 80 | static Handle current_thread_handle; |
| 77 | static Thread* current_thread; | 81 | static Thread* current_thread; |
| 78 | 82 | ||
| 79 | /// Gets the current thread | 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| 80 | inline Thread* GetCurrentThread() { | 84 | static u32 next_thread_id; ///< The next available thread id |
| 85 | |||
| 86 | Thread* GetCurrentThread() { | ||
| 81 | return current_thread; | 87 | return current_thread; |
| 82 | } | 88 | } |
| 83 | 89 | ||
| @@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 121 | } | 127 | } |
| 122 | t->wait_type = WAITTYPE_NONE; | 128 | t->wait_type = WAITTYPE_NONE; |
| 123 | t->wait_handle = 0; | 129 | t->wait_handle = 0; |
| 130 | t->wait_address = 0; | ||
| 124 | } | 131 | } |
| 125 | 132 | ||
| 126 | /// Change a thread to "ready" state | 133 | /// Change a thread to "ready" state |
| @@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) { | |||
| 140 | } | 147 | } |
| 141 | } | 148 | } |
| 142 | 149 | ||
| 143 | /// Verify that a thread has not been released from waiting | 150 | /// Check if a thread is blocking on a specified wait type |
| 144 | inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { | 151 | static bool CheckWaitType(const Thread* thread, WaitType type) { |
| 145 | _dbg_assert_(KERNEL, thread != nullptr); | 152 | return (type == thread->wait_type) && (thread->IsWaiting()); |
| 146 | return type == thread->wait_type && wait_handle == thread->wait_handle; | 153 | } |
| 154 | |||
| 155 | /// Check if a thread is blocking on a specified wait type with a specified handle | ||
| 156 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { | ||
| 157 | return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | ||
| 161 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | ||
| 162 | return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); | ||
| 147 | } | 163 | } |
| 148 | 164 | ||
| 149 | /// Stops the current thread | 165 | /// Stops the current thread |
| 150 | ResultCode StopThread(Handle handle, const char* reason) { | 166 | ResultCode StopThread(Handle handle, const char* reason) { |
| 151 | Thread* thread = g_object_pool.Get<Thread>(handle); | 167 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 152 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | 168 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 153 | 169 | ||
| 170 | // Release all the mutexes that this thread holds | ||
| 171 | ReleaseThreadMutexes(handle); | ||
| 172 | |||
| 154 | ChangeReadyState(thread, false); | 173 | ChangeReadyState(thread, false); |
| 155 | thread->status = THREADSTATUS_DORMANT; | 174 | thread->status = THREADSTATUS_DORMANT; |
| 156 | for (Handle waiting_handle : thread->waiting_threads) { | 175 | for (Handle waiting_handle : thread->waiting_threads) { |
| 157 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); | 176 | Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle); |
| 158 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { | 177 | |
| 178 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) | ||
| 159 | ResumeThreadFromWait(waiting_handle); | 179 | ResumeThreadFromWait(waiting_handle); |
| 160 | } | ||
| 161 | } | 180 | } |
| 162 | thread->waiting_threads.clear(); | 181 | thread->waiting_threads.clear(); |
| 163 | 182 | ||
| 164 | // Stopped threads are never waiting. | 183 | // Stopped threads are never waiting. |
| 165 | thread->wait_type = WAITTYPE_NONE; | 184 | thread->wait_type = WAITTYPE_NONE; |
| 166 | thread->wait_handle = 0; | 185 | thread->wait_handle = 0; |
| 186 | thread->wait_address = 0; | ||
| 167 | 187 | ||
| 168 | return RESULT_SUCCESS; | 188 | return RESULT_SUCCESS; |
| 169 | } | 189 | } |
| @@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 178 | 198 | ||
| 179 | if (new_status == THREADSTATUS_WAIT) { | 199 | if (new_status == THREADSTATUS_WAIT) { |
| 180 | if (t->wait_type == WAITTYPE_NONE) { | 200 | if (t->wait_type == WAITTYPE_NONE) { |
| 181 | ERROR_LOG(KERNEL, "Waittype none not allowed"); | 201 | LOG_ERROR(Kernel, "Waittype none not allowed"); |
| 182 | } | 202 | } |
| 183 | } | 203 | } |
| 184 | } | 204 | } |
| @@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 190 | 210 | ||
| 191 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 211 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 192 | for (Handle handle : thread_queue) { | 212 | for (Handle handle : thread_queue) { |
| 193 | Thread* thread = g_object_pool.Get<Thread>(handle); | 213 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 194 | 214 | ||
| 195 | // TODO(bunnei): Verify arbiter address... | 215 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 196 | if (!VerifyWait(thread, WAITTYPE_ARB, arbiter)) | ||
| 197 | continue; | 216 | continue; |
| 198 | 217 | ||
| 199 | if (thread == nullptr) | 218 | if (thread == nullptr) |
| 200 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. | 219 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. |
| 220 | |||
| 201 | if(thread->current_priority <= priority) { | 221 | if(thread->current_priority <= priority) { |
| 202 | highest_priority_thread = handle; | 222 | highest_priority_thread = handle; |
| 203 | priority = thread->current_priority; | 223 | priority = thread->current_priority; |
| @@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) { | |||
| 215 | 235 | ||
| 216 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 236 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 217 | for (Handle handle : thread_queue) { | 237 | for (Handle handle : thread_queue) { |
| 218 | Thread* thread = g_object_pool.Get<Thread>(handle); | 238 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 219 | 239 | ||
| 220 | // TODO(bunnei): Verify arbiter address... | 240 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 221 | if (VerifyWait(thread, WAITTYPE_ARB, arbiter)) | ||
| 222 | ResumeThreadFromWait(handle); | 241 | ResumeThreadFromWait(handle); |
| 223 | } | 242 | } |
| 224 | } | 243 | } |
| @@ -269,14 +288,9 @@ Thread* NextThread() { | |||
| 269 | if (next == 0) { | 288 | if (next == 0) { |
| 270 | return nullptr; | 289 | return nullptr; |
| 271 | } | 290 | } |
| 272 | return Kernel::g_object_pool.Get<Thread>(next); | 291 | return Kernel::g_handle_table.Get<Thread>(next); |
| 273 | } | 292 | } |
| 274 | 293 | ||
| 275 | /** | ||
| 276 | * Puts the current thread in the wait state for the given type | ||
| 277 | * @param wait_type Type of wait | ||
| 278 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | ||
| 279 | */ | ||
| 280 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | 294 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { |
| 281 | Thread* thread = GetCurrentThread(); | 295 | Thread* thread = GetCurrentThread(); |
| 282 | thread->wait_type = wait_type; | 296 | thread->wait_type = wait_type; |
| @@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | |||
| 284 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 298 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |
| 285 | } | 299 | } |
| 286 | 300 | ||
| 301 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { | ||
| 302 | WaitCurrentThread(wait_type, wait_handle); | ||
| 303 | GetCurrentThread()->wait_address = wait_address; | ||
| 304 | } | ||
| 305 | |||
| 287 | /// Resumes a thread from waiting by marking it as "ready" | 306 | /// Resumes a thread from waiting by marking it as "ready" |
| 288 | void ResumeThreadFromWait(Handle handle) { | 307 | void ResumeThreadFromWait(Handle handle) { |
| 289 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); | 308 | Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); |
| 290 | if (thread) { | 309 | if (thread) { |
| 291 | thread->status &= ~THREADSTATUS_WAIT; | 310 | thread->status &= ~THREADSTATUS_WAIT; |
| 311 | thread->wait_handle = 0; | ||
| 312 | thread->wait_type = WAITTYPE_NONE; | ||
| 292 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 313 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 293 | ChangeReadyState(thread, true); | 314 | ChangeReadyState(thread, true); |
| 294 | } | 315 | } |
| @@ -301,12 +322,12 @@ void DebugThreadQueue() { | |||
| 301 | if (!thread) { | 322 | if (!thread) { |
| 302 | return; | 323 | return; |
| 303 | } | 324 | } |
| 304 | INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); | 325 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); |
| 305 | for (u32 i = 0; i < thread_queue.size(); i++) { | 326 | for (u32 i = 0; i < thread_queue.size(); i++) { |
| 306 | Handle handle = thread_queue[i]; | 327 | Handle handle = thread_queue[i]; |
| 307 | s32 priority = thread_ready_queue.contains(handle); | 328 | s32 priority = thread_ready_queue.contains(handle); |
| 308 | if (priority != -1) { | 329 | if (priority != -1) { |
| 309 | INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); | 330 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); |
| 310 | } | 331 | } |
| 311 | } | 332 | } |
| 312 | } | 333 | } |
| @@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 316 | s32 processor_id, u32 stack_top, int stack_size) { | 337 | s32 processor_id, u32 stack_top, int stack_size) { |
| 317 | 338 | ||
| 318 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | 339 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), |
| 319 | "CreateThread priority=%d, outside of allowable range!", priority) | 340 | "priority=%d, outside of allowable range!", priority) |
| 320 | 341 | ||
| 321 | Thread* thread = new Thread; | 342 | Thread* thread = new Thread; |
| 322 | 343 | ||
| 323 | handle = Kernel::g_object_pool.Create(thread); | 344 | // TOOD(yuriks): Fix error reporting |
| 345 | handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); | ||
| 324 | 346 | ||
| 325 | thread_queue.push_back(handle); | 347 | thread_queue.push_back(handle); |
| 326 | thread_ready_queue.prepare(priority); | 348 | thread_ready_queue.prepare(priority); |
| 327 | 349 | ||
| 350 | thread->thread_id = next_thread_id++; | ||
| 328 | thread->status = THREADSTATUS_DORMANT; | 351 | thread->status = THREADSTATUS_DORMANT; |
| 329 | thread->entry_point = entry_point; | 352 | thread->entry_point = entry_point; |
| 330 | thread->stack_top = stack_top; | 353 | thread->stack_top = stack_top; |
| @@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 333 | thread->processor_id = processor_id; | 356 | thread->processor_id = processor_id; |
| 334 | thread->wait_type = WAITTYPE_NONE; | 357 | thread->wait_type = WAITTYPE_NONE; |
| 335 | thread->wait_handle = 0; | 358 | thread->wait_handle = 0; |
| 359 | thread->wait_address = 0; | ||
| 336 | thread->name = name; | 360 | thread->name = name; |
| 337 | 361 | ||
| 338 | return thread; | 362 | return thread; |
| @@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 343 | u32 stack_top, int stack_size) { | 367 | u32 stack_top, int stack_size) { |
| 344 | 368 | ||
| 345 | if (name == nullptr) { | 369 | if (name == nullptr) { |
| 346 | ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); | 370 | LOG_ERROR(Kernel_SVC, "nullptr name"); |
| 347 | return -1; | 371 | return -1; |
| 348 | } | 372 | } |
| 349 | if ((u32)stack_size < 0x200) { | 373 | if ((u32)stack_size < 0x200) { |
| 350 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, | 374 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, |
| 351 | stack_size); | 375 | stack_size); |
| 352 | return -1; | 376 | return -1; |
| 353 | } | 377 | } |
| 354 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 378 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 355 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 379 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 356 | WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", | 380 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", |
| 357 | name, priority, new_priority); | 381 | name, priority, new_priority); |
| 358 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 382 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 359 | // validity of this | 383 | // validity of this |
| 360 | priority = new_priority; | 384 | priority = new_priority; |
| 361 | } | 385 | } |
| 362 | if (!Memory::GetPointer(entry_point)) { | 386 | if (!Memory::GetPointer(entry_point)) { |
| 363 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); | 387 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); |
| 364 | return -1; | 388 | return -1; |
| 365 | } | 389 | } |
| 366 | Handle handle; | 390 | Handle handle; |
| @@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 375 | 399 | ||
| 376 | /// Get the priority of the thread specified by handle | 400 | /// Get the priority of the thread specified by handle |
| 377 | ResultVal<u32> GetThreadPriority(const Handle handle) { | 401 | ResultVal<u32> GetThreadPriority(const Handle handle) { |
| 378 | Thread* thread = g_object_pool.Get<Thread>(handle); | 402 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 379 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | 403 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 380 | 404 | ||
| 381 | return MakeResult<u32>(thread->current_priority); | 405 | return MakeResult<u32>(thread->current_priority); |
| @@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { | |||
| 387 | if (!handle) { | 411 | if (!handle) { |
| 388 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? | 412 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? |
| 389 | } else { | 413 | } else { |
| 390 | thread = g_object_pool.Get<Thread>(handle); | 414 | thread = g_handle_table.Get<Thread>(handle); |
| 391 | if (thread == nullptr) { | 415 | if (thread == nullptr) { |
| 392 | return InvalidHandle(ErrorModule::Kernel); | 416 | return InvalidHandle(ErrorModule::Kernel); |
| 393 | } | 417 | } |
| @@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { | |||
| 397 | // If priority is invalid, clamp to valid range | 421 | // If priority is invalid, clamp to valid range |
| 398 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 422 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 399 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 423 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 400 | WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); | 424 | LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); |
| 401 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 425 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 402 | // validity of this | 426 | // validity of this |
| 403 | priority = new_priority; | 427 | priority = new_priority; |
| @@ -450,24 +474,44 @@ void Reschedule() { | |||
| 450 | Thread* prev = GetCurrentThread(); | 474 | Thread* prev = GetCurrentThread(); |
| 451 | Thread* next = NextThread(); | 475 | Thread* next = NextThread(); |
| 452 | HLE::g_reschedule = false; | 476 | HLE::g_reschedule = false; |
| 453 | if (next > 0) { | ||
| 454 | INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 455 | 477 | ||
| 478 | if (next != nullptr) { | ||
| 479 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 456 | SwitchContext(next); | 480 | SwitchContext(next); |
| 481 | } else { | ||
| 482 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | ||
| 457 | 483 | ||
| 458 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep | 484 | for (Handle handle : thread_queue) { |
| 459 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. | 485 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 460 | // This results in the current thread yielding on a VBLANK once, and then it will be | 486 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", |
| 461 | // immediately placed back in the queue for execution. | 487 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); |
| 462 | if (prev->wait_type == WAITTYPE_VBLANK) { | ||
| 463 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 464 | } | 488 | } |
| 465 | } | 489 | } |
| 490 | |||
| 491 | // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put | ||
| 492 | // to sleep. So, we'll just immediately set it to "ready" again after an attempted context | ||
| 493 | // switch has occurred. This results in the current thread yielding on a sleep once, and then it | ||
| 494 | // will immediately be placed back in the queue for execution. | ||
| 495 | |||
| 496 | if (CheckWaitType(prev, WAITTYPE_SLEEP)) | ||
| 497 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 498 | } | ||
| 499 | |||
| 500 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | ||
| 501 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 502 | if (thread == nullptr) | ||
| 503 | return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, | ||
| 504 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 505 | |||
| 506 | *thread_id = thread->thread_id; | ||
| 507 | |||
| 508 | return RESULT_SUCCESS; | ||
| 466 | } | 509 | } |
| 467 | 510 | ||
| 468 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 511 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 469 | 512 | ||
| 470 | void ThreadingInit() { | 513 | void ThreadingInit() { |
| 514 | next_thread_id = INITIAL_THREAD_ID; | ||
| 471 | } | 515 | } |
| 472 | 516 | ||
| 473 | void ThreadingShutdown() { | 517 | void ThreadingShutdown() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index ce63a70d3..0e1397cd9 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 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 | |||
| 9 | #include "core/mem_map.h" | ||
| 10 | |||
| 8 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 10 | 13 | ||
| @@ -37,7 +40,6 @@ enum WaitType { | |||
| 37 | WAITTYPE_SEMA, | 40 | WAITTYPE_SEMA, |
| 38 | WAITTYPE_EVENT, | 41 | WAITTYPE_EVENT, |
| 39 | WAITTYPE_THREADEND, | 42 | WAITTYPE_THREADEND, |
| 40 | WAITTYPE_VBLANK, | ||
| 41 | WAITTYPE_MUTEX, | 43 | WAITTYPE_MUTEX, |
| 42 | WAITTYPE_SYNCH, | 44 | WAITTYPE_SYNCH, |
| 43 | WAITTYPE_ARB, | 45 | WAITTYPE_ARB, |
| @@ -58,6 +60,14 @@ void Reschedule(); | |||
| 58 | /// Stops the current thread | 60 | /// Stops the current thread |
| 59 | ResultCode StopThread(Handle thread, const char* reason); | 61 | ResultCode StopThread(Handle thread, const char* reason); |
| 60 | 62 | ||
| 63 | /** | ||
| 64 | * Retrieves the ID of the specified thread handle | ||
| 65 | * @param thread_id Will contain the output thread id | ||
| 66 | * @param handle Handle to the thread we want | ||
| 67 | * @return Whether the function was successful or not | ||
| 68 | */ | ||
| 69 | ResultCode GetThreadId(u32* thread_id, Handle handle); | ||
| 70 | |||
| 61 | /// Resumes a thread from waiting by marking it as "ready" | 71 | /// Resumes a thread from waiting by marking it as "ready" |
| 62 | void ResumeThreadFromWait(Handle handle); | 72 | void ResumeThreadFromWait(Handle handle); |
| 63 | 73 | ||
| @@ -77,6 +87,14 @@ Handle GetCurrentThreadHandle(); | |||
| 77 | */ | 87 | */ |
| 78 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); | 88 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); |
| 79 | 89 | ||
| 90 | /** | ||
| 91 | * Puts the current thread in the wait state for the given type | ||
| 92 | * @param wait_type Type of wait | ||
| 93 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | ||
| 94 | * @param wait_address Arbitration address used to resume from wait | ||
| 95 | */ | ||
| 96 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); | ||
| 97 | |||
| 80 | /// Put current thread in a wait state - on WaitSynchronization | 98 | /// Put current thread in a wait state - on WaitSynchronization |
| 81 | void WaitThread_Synchronization(); | 99 | void WaitThread_Synchronization(); |
| 82 | 100 | ||
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 15c4a2677..0e9c213e0 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| @@ -17,6 +17,8 @@ | |||
| 17 | /// Detailed description of the error. This listing is likely incomplete. | 17 | /// Detailed description of the error. This listing is likely incomplete. |
| 18 | enum class ErrorDescription : u32 { | 18 | enum class ErrorDescription : u32 { |
| 19 | Success = 0, | 19 | Success = 0, |
| 20 | FS_NotFound = 100, | ||
| 21 | FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive | ||
| 20 | InvalidSection = 1000, | 22 | InvalidSection = 1000, |
| 21 | TooLarge = 1001, | 23 | TooLarge = 1001, |
| 22 | NotAuthorized = 1002, | 24 | NotAuthorized = 1002, |
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index 9af96f6b8..20a3fa2e5 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" |
| @@ -11,6 +11,24 @@ | |||
| 11 | 11 | ||
| 12 | namespace AC_U { | 12 | namespace AC_U { |
| 13 | 13 | ||
| 14 | /** | ||
| 15 | * AC_U::GetWifiStatus service function | ||
| 16 | * Outputs: | ||
| 17 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 18 | * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. | ||
| 19 | */ | ||
| 20 | void GetWifiStatus(Service::Interface* self) { | ||
| 21 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 22 | |||
| 23 | // TODO(purpasmart96): This function is only a stub, | ||
| 24 | // it returns a valid result without implementing full functionality. | ||
| 25 | |||
| 26 | cmd_buff[1] = 0; // No error | ||
| 27 | cmd_buff[2] = 0; // Connection type set to none | ||
| 28 | |||
| 29 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 30 | } | ||
| 31 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 32 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010000, nullptr, "CreateDefaultConfig"}, | 33 | {0x00010000, nullptr, "CreateDefaultConfig"}, |
| 16 | {0x00040006, nullptr, "ConnectAsync"}, | 34 | {0x00040006, nullptr, "ConnectAsync"}, |
| @@ -18,7 +36,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 18 | {0x00080004, nullptr, "CloseAsync"}, | 36 | {0x00080004, nullptr, "CloseAsync"}, |
| 19 | {0x00090002, nullptr, "GetCloseResult"}, | 37 | {0x00090002, nullptr, "GetCloseResult"}, |
| 20 | {0x000A0000, nullptr, "GetLastErrorCode"}, | 38 | {0x000A0000, nullptr, "GetLastErrorCode"}, |
| 21 | {0x000D0000, nullptr, "GetWifiStatus"}, | 39 | {0x000D0000, GetWifiStatus, "GetWifiStatus"}, |
| 22 | {0x000E0042, nullptr, "GetCurrentAPInfo"}, | 40 | {0x000E0042, nullptr, "GetCurrentAPInfo"}, |
| 23 | {0x00100042, nullptr, "GetCurrentNZoneInfo"}, | 41 | {0x00100042, nullptr, "GetCurrentNZoneInfo"}, |
| 24 | {0x00110042, nullptr, "GetNZoneApNumService"}, | 42 | {0x00110042, nullptr, "GetNZoneApNumService"}, |
| @@ -38,7 +56,4 @@ Interface::Interface() { | |||
| 38 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 56 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 39 | } | 57 | } |
| 40 | 58 | ||
| 41 | Interface::~Interface() { | ||
| 42 | } | ||
| 43 | |||
| 44 | } // namespace | 59 | } // namespace |
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h index c91b28353..f1d26ebe8 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 |
| @@ -16,11 +16,7 @@ namespace AC_U { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface(); | 19 | |
| 20 | /** | ||
| 21 | * Gets the string port name used by CTROS for the service | ||
| 22 | * @return Port name of service | ||
| 23 | */ | ||
| 24 | std::string GetPortName() const override { | 20 | std::string GetPortName() const override { |
| 25 | return "ac:u"; | 21 | return "ac:u"; |
| 26 | } | 22 | } |
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp new file mode 100644 index 000000000..10870f14b --- /dev/null +++ b/src/core/hle/service/act_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/act_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace ACT_U | ||
| 11 | |||
| 12 | namespace ACT_U { | ||
| 13 | |||
| 14 | // Empty arrays are illegal -- commented out until an entry is added. | ||
| 15 | //const Interface::FunctionInfo FunctionTable[] = { }; | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // Interface class | ||
| 19 | |||
| 20 | Interface::Interface() { | ||
| 21 | //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 22 | } | ||
| 23 | |||
| 24 | } // namespace | ||
diff --git a/src/core/hle/service/act_u.h b/src/core/hle/service/act_u.h new file mode 100644 index 000000000..be41454a4 --- /dev/null +++ b/src/core/hle/service/act_u.h | |||
| @@ -0,0 +1,23 @@ | |||
| 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 ACT_U | ||
| 11 | |||
| 12 | namespace ACT_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "act:u"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp new file mode 100644 index 000000000..0b396b6d3 --- /dev/null +++ b/src/core/hle/service/am_app.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/am_app.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace AM_APP | ||
| 11 | |||
| 12 | namespace AM_APP { | ||
| 13 | |||
| 14 | // Empty arrays are illegal -- commented out until an entry is added. | ||
| 15 | //const Interface::FunctionInfo FunctionTable[] = { }; | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // Interface class | ||
| 19 | |||
| 20 | Interface::Interface() { | ||
| 21 | //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 22 | } | ||
| 23 | |||
| 24 | } // namespace | ||
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h new file mode 100644 index 000000000..50dc2f5a2 --- /dev/null +++ b/src/core/hle/service/am_app.h | |||
| @@ -0,0 +1,23 @@ | |||
| 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 AM_APP | ||
| 11 | |||
| 12 | namespace AM_APP { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "am:app"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp index 403cac353..112844e5b 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" |
| @@ -41,7 +41,4 @@ Interface::Interface() { | |||
| 41 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 41 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | Interface::~Interface() { | ||
| 45 | } | ||
| 46 | |||
| 47 | } // namespace | 44 | } // namespace |
diff --git a/src/core/hle/service/am_net.h b/src/core/hle/service/am_net.h index 4816e1697..616c33ee8 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 |
| @@ -14,11 +14,7 @@ namespace AM_NET { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "am:net"; | 19 | return "am:net"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp new file mode 100644 index 000000000..dcf5ec4fe --- /dev/null +++ b/src/core/hle/service/apt_a.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 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/apt_a.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace APT_A | ||
| 11 | |||
| 12 | namespace APT_A { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010040, nullptr, "GetLockHandle?"}, | ||
| 16 | {0x00020080, nullptr, "Initialize?"}, | ||
| 17 | {0x00030040, nullptr, "Enable?"}, | ||
| 18 | {0x00040040, nullptr, "Finalize?"}, | ||
| 19 | {0x00050040, nullptr, "GetAppletManInfo?"}, | ||
| 20 | {0x00060040, nullptr, "GetAppletInfo?"}, | ||
| 21 | {0x003B0040, nullptr, "CancelLibraryApplet?"}, | ||
| 22 | {0x00430040, nullptr, "NotifyToWait?"}, | ||
| 23 | {0x004B00C2, nullptr, "AppletUtility?"}, | ||
| 24 | {0x00550040, nullptr, "WriteInputToNsState?"}, | ||
| 25 | }; | ||
| 26 | |||
| 27 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 28 | // Interface class | ||
| 29 | |||
| 30 | Interface::Interface() { | ||
| 31 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace | ||
diff --git a/src/core/hle/service/apt_a.h b/src/core/hle/service/apt_a.h new file mode 100644 index 000000000..6cbf1288f --- /dev/null +++ b/src/core/hle/service/apt_a.h | |||
| @@ -0,0 +1,23 @@ | |||
| 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 APT_A | ||
| 11 | |||
| 12 | namespace APT_A { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "APT:A"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index 4bb05ce40..d8b261ba7 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp | |||
| @@ -1,13 +1,15 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under 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 | ||
| 6 | #include "common/common.h" | 6 | #include "common/common.h" |
| 7 | #include "common/file_util.h" | ||
| 7 | 8 | ||
| 8 | #include "core/hle/hle.h" | 9 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/event.h" |
| 10 | #include "core/hle/kernel/mutex.h" | 11 | #include "core/hle/kernel/mutex.h" |
| 12 | #include "core/hle/kernel/shared_memory.h" | ||
| 11 | #include "apt_u.h" | 13 | #include "apt_u.h" |
| 12 | 14 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -15,7 +17,19 @@ | |||
| 15 | 17 | ||
| 16 | namespace APT_U { | 18 | namespace APT_U { |
| 17 | 19 | ||
| 20 | // Address used for shared font (as observed on HW) | ||
| 21 | // TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via | ||
| 22 | // https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any | ||
| 23 | // address other than 0x18000000 due to internal pointers in the shared font dump that would need to | ||
| 24 | // be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then | ||
| 25 | // correctly mapping it in Citra, however we still do not understand how the mapping is determined. | ||
| 26 | static const VAddr SHARED_FONT_VADDR = 0x18000000; | ||
| 27 | |||
| 28 | // Handle to shared memory region designated to for shared system font | ||
| 29 | static Handle shared_font_mem = 0; | ||
| 30 | |||
| 18 | static Handle lock_handle = 0; | 31 | static Handle lock_handle = 0; |
| 32 | static std::vector<u8> shared_font; | ||
| 19 | 33 | ||
| 20 | /// Signals used by APT functions | 34 | /// Signals used by APT functions |
| 21 | enum class SignalType : u32 { | 35 | enum class SignalType : u32 { |
| @@ -26,7 +40,7 @@ enum class SignalType : u32 { | |||
| 26 | }; | 40 | }; |
| 27 | 41 | ||
| 28 | void Initialize(Service::Interface* self) { | 42 | void Initialize(Service::Interface* self) { |
| 29 | u32* cmd_buff = Service::GetCommandBuffer(); | 43 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 30 | 44 | ||
| 31 | 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 |
| 32 | 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 |
| @@ -39,11 +53,11 @@ void Initialize(Service::Interface* self) { | |||
| 39 | 53 | ||
| 40 | cmd_buff[1] = 0; // No error | 54 | cmd_buff[1] = 0; // No error |
| 41 | 55 | ||
| 42 | DEBUG_LOG(KERNEL, "called"); | 56 | LOG_DEBUG(Service_APT, "called"); |
| 43 | } | 57 | } |
| 44 | 58 | ||
| 45 | void GetLockHandle(Service::Interface* self) { | 59 | void GetLockHandle(Service::Interface* self) { |
| 46 | u32* cmd_buff = Service::GetCommandBuffer(); | 60 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 47 | 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 |
| 48 | 62 | ||
| 49 | if (0 == lock_handle) { | 63 | if (0 == lock_handle) { |
| @@ -60,22 +74,22 @@ void GetLockHandle(Service::Interface* self) { | |||
| 60 | cmd_buff[4] = 0; | 74 | cmd_buff[4] = 0; |
| 61 | 75 | ||
| 62 | cmd_buff[5] = lock_handle; | 76 | cmd_buff[5] = lock_handle; |
| 63 | DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]); | 77 | LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]); |
| 64 | } | 78 | } |
| 65 | 79 | ||
| 66 | void Enable(Service::Interface* self) { | 80 | void Enable(Service::Interface* self) { |
| 67 | u32* cmd_buff = Service::GetCommandBuffer(); | 81 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 68 | 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? |
| 69 | cmd_buff[1] = 0; // No error | 83 | cmd_buff[1] = 0; // No error |
| 70 | WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk); | 84 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); |
| 71 | } | 85 | } |
| 72 | 86 | ||
| 73 | void InquireNotification(Service::Interface* self) { | 87 | void InquireNotification(Service::Interface* self) { |
| 74 | u32* cmd_buff = Service::GetCommandBuffer(); | 88 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 75 | u32 app_id = cmd_buff[2]; | 89 | u32 app_id = cmd_buff[2]; |
| 76 | cmd_buff[1] = 0; // No error | 90 | cmd_buff[1] = 0; // No error |
| 77 | cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type | 91 | cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type |
| 78 | WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id); | 92 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); |
| 79 | } | 93 | } |
| 80 | 94 | ||
| 81 | /** | 95 | /** |
| @@ -84,21 +98,21 @@ void InquireNotification(Service::Interface* self) { | |||
| 84 | * state so that this command will return an error if this command is used again if parameters were | 98 | * state so that this command will return an error if this command is used again if parameters were |
| 85 | * not set again. This is called when the second Initialize event is triggered. It returns a signal | 99 | * not set again. This is called when the second Initialize event is triggered. It returns a signal |
| 86 | * type indicating why it was triggered. | 100 | * type indicating why it was triggered. |
| 87 | * Inputs: | 101 | * Inputs: |
| 88 | * 1 : AppID | 102 | * 1 : AppID |
| 89 | * 2 : Parameter buffer size, max size is 0x1000 | 103 | * 2 : Parameter buffer size, max size is 0x1000 |
| 90 | * Outputs: | 104 | * Outputs: |
| 91 | * 1 : Result of function, 0 on success, otherwise error code | 105 | * 1 : Result of function, 0 on success, otherwise error code |
| 92 | * 2 : Unknown, for now assume AppID of the process which sent these parameters | 106 | * 2 : Unknown, for now assume AppID of the process which sent these parameters |
| 93 | * 3 : Unknown, for now assume Signal type | 107 | * 3 : Unknown, for now assume Signal type |
| 94 | * 4 : Actual parameter buffer size, this is <= to the the input size | 108 | * 4 : Actual parameter buffer size, this is <= to the the input size |
| 95 | * 5 : Value | 109 | * 5 : Value |
| 96 | * 6 : Handle from the source process which set the parameters, likely used for shared memory | 110 | * 6 : Handle from the source process which set the parameters, likely used for shared memory |
| 97 | * 7 : Size | 111 | * 7 : Size |
| 98 | * 8 : Output parameter buffer ptr | 112 | * 8 : Output parameter buffer ptr |
| 99 | */ | 113 | */ |
| 100 | void ReceiveParameter(Service::Interface* self) { | 114 | void ReceiveParameter(Service::Interface* self) { |
| 101 | u32* cmd_buff = Service::GetCommandBuffer(); | 115 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 102 | u32 app_id = cmd_buff[1]; | 116 | u32 app_id = cmd_buff[1]; |
| 103 | u32 buffer_size = cmd_buff[2]; | 117 | u32 buffer_size = cmd_buff[2]; |
| 104 | cmd_buff[1] = 0; // No error | 118 | cmd_buff[1] = 0; // No error |
| @@ -108,28 +122,28 @@ void ReceiveParameter(Service::Interface* self) { | |||
| 108 | cmd_buff[5] = 0; | 122 | cmd_buff[5] = 0; |
| 109 | cmd_buff[6] = 0; | 123 | cmd_buff[6] = 0; |
| 110 | cmd_buff[7] = 0; | 124 | cmd_buff[7] = 0; |
| 111 | WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | 125 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); |
| 112 | } | 126 | } |
| 113 | 127 | ||
| 114 | /** | 128 | /** |
| 115 | * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter | 129 | * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter |
| 116 | * (except for the word value prior to the output handle), except this will not clear the flag | 130 | * (except for the word value prior to the output handle), except this will not clear the flag |
| 117 | * (except when responseword[3]==8 || responseword[3]==9) in NS state. | 131 | * (except when responseword[3]==8 || responseword[3]==9) in NS state. |
| 118 | * Inputs: | 132 | * Inputs: |
| 119 | * 1 : AppID | 133 | * 1 : AppID |
| 120 | * 2 : Parameter buffer size, max size is 0x1000 | 134 | * 2 : Parameter buffer size, max size is 0x1000 |
| 121 | * Outputs: | 135 | * Outputs: |
| 122 | * 1 : Result of function, 0 on success, otherwise error code | 136 | * 1 : Result of function, 0 on success, otherwise error code |
| 123 | * 2 : Unknown, for now assume AppID of the process which sent these parameters | 137 | * 2 : Unknown, for now assume AppID of the process which sent these parameters |
| 124 | * 3 : Unknown, for now assume Signal type | 138 | * 3 : Unknown, for now assume Signal type |
| 125 | * 4 : Actual parameter buffer size, this is <= to the the input size | 139 | * 4 : Actual parameter buffer size, this is <= to the the input size |
| 126 | * 5 : Value | 140 | * 5 : Value |
| 127 | * 6 : Handle from the source process which set the parameters, likely used for shared memory | 141 | * 6 : Handle from the source process which set the parameters, likely used for shared memory |
| 128 | * 7 : Size | 142 | * 7 : Size |
| 129 | * 8 : Output parameter buffer ptr | 143 | * 8 : Output parameter buffer ptr |
| 130 | */ | 144 | */ |
| 131 | void GlanceParameter(Service::Interface* self) { | 145 | void GlanceParameter(Service::Interface* self) { |
| 132 | u32* cmd_buff = Service::GetCommandBuffer(); | 146 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 133 | u32 app_id = cmd_buff[1]; | 147 | u32 app_id = cmd_buff[1]; |
| 134 | u32 buffer_size = cmd_buff[2]; | 148 | u32 buffer_size = cmd_buff[2]; |
| 135 | 149 | ||
| @@ -141,22 +155,22 @@ void GlanceParameter(Service::Interface* self) { | |||
| 141 | cmd_buff[6] = 0; | 155 | cmd_buff[6] = 0; |
| 142 | cmd_buff[7] = 0; | 156 | cmd_buff[7] = 0; |
| 143 | 157 | ||
| 144 | WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); | 158 | LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); |
| 145 | } | 159 | } |
| 146 | 160 | ||
| 147 | /** | 161 | /** |
| 148 | * APT_U::AppletUtility service function | 162 | * APT_U::AppletUtility service function |
| 149 | * Inputs: | 163 | * Inputs: |
| 150 | * 1 : Unknown, but clearly used for something | 164 | * 1 : Unknown, but clearly used for something |
| 151 | * 2 : Buffer 1 size (purpose is unknown) | 165 | * 2 : Buffer 1 size (purpose is unknown) |
| 152 | * 3 : Buffer 2 size (purpose is unknown) | 166 | * 3 : Buffer 2 size (purpose is unknown) |
| 153 | * 5 : Buffer 1 address (purpose is unknown) | 167 | * 5 : Buffer 1 address (purpose is unknown) |
| 154 | * 65 : Buffer 2 address (purpose is unknown) | 168 | * 65 : Buffer 2 address (purpose is unknown) |
| 155 | * Outputs: | 169 | * Outputs: |
| 156 | * 1 : Result of function, 0 on success, otherwise error code | 170 | * 1 : Result of function, 0 on success, otherwise error code |
| 157 | */ | 171 | */ |
| 158 | void AppletUtility(Service::Interface* self) { | 172 | void AppletUtility(Service::Interface* self) { |
| 159 | u32* cmd_buff = Service::GetCommandBuffer(); | 173 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 160 | 174 | ||
| 161 | // 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. |
| 162 | u32 unk = cmd_buff[1]; | 176 | u32 unk = cmd_buff[1]; |
| @@ -167,11 +181,39 @@ void AppletUtility(Service::Interface* self) { | |||
| 167 | 181 | ||
| 168 | cmd_buff[1] = 0; // No error | 182 | cmd_buff[1] = 0; // No error |
| 169 | 183 | ||
| 170 | WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, " | 184 | LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, " |
| 171 | "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size, | 185 | "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size, |
| 172 | buffer1_addr, buffer2_addr); | 186 | buffer1_addr, buffer2_addr); |
| 173 | } | 187 | } |
| 174 | 188 | ||
| 189 | /** | ||
| 190 | * APT_U::GetSharedFont service function | ||
| 191 | * Outputs: | ||
| 192 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 193 | * 2 : Virtual address of where shared font will be loaded in memory | ||
| 194 | * 4 : Handle to shared font memory | ||
| 195 | */ | ||
| 196 | void GetSharedFont(Service::Interface* self) { | ||
| 197 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 198 | |||
| 199 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 200 | |||
| 201 | if (!shared_font.empty()) { | ||
| 202 | // TODO(bunnei): This function shouldn't copy the shared font every time it's called. | ||
| 203 | // Instead, it should probably map the shared font as RO memory. We don't currently have | ||
| 204 | // an easy way to do this, but the copy should be sufficient for now. | ||
| 205 | memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); | ||
| 206 | |||
| 207 | cmd_buff[0] = 0x00440082; | ||
| 208 | cmd_buff[1] = 0; // No error | ||
| 209 | cmd_buff[2] = SHARED_FONT_VADDR; | ||
| 210 | cmd_buff[4] = shared_font_mem; | ||
| 211 | } else { | ||
| 212 | cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware) | ||
| 213 | LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 175 | const Interface::FunctionInfo FunctionTable[] = { | 217 | const Interface::FunctionInfo FunctionTable[] = { |
| 176 | {0x00010040, GetLockHandle, "GetLockHandle"}, | 218 | {0x00010040, GetLockHandle, "GetLockHandle"}, |
| 177 | {0x00020080, Initialize, "Initialize"}, | 219 | {0x00020080, Initialize, "Initialize"}, |
| @@ -240,7 +282,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 240 | {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, | 282 | {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, |
| 241 | {0x00420080, nullptr, "SleepSystem"}, | 283 | {0x00420080, nullptr, "SleepSystem"}, |
| 242 | {0x00430040, nullptr, "NotifyToWait"}, | 284 | {0x00430040, nullptr, "NotifyToWait"}, |
| 243 | {0x00440000, nullptr, "GetSharedFont"}, | 285 | {0x00440000, GetSharedFont, "GetSharedFont"}, |
| 244 | {0x00450040, nullptr, "GetWirelessRebootInfo"}, | 286 | {0x00450040, nullptr, "GetWirelessRebootInfo"}, |
| 245 | {0x00460104, nullptr, "Wrap"}, | 287 | {0x00460104, nullptr, "Wrap"}, |
| 246 | {0x00470104, nullptr, "Unwrap"}, | 288 | {0x00470104, nullptr, "Unwrap"}, |
| @@ -259,12 +301,33 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 259 | // Interface class | 301 | // Interface class |
| 260 | 302 | ||
| 261 | Interface::Interface() { | 303 | Interface::Interface() { |
| 262 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 304 | // Load the shared system font (if available). |
| 305 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | ||
| 306 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | ||
| 307 | // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||
| 308 | // "shared_font.bin" in the Citra "sysdata" directory. | ||
| 309 | |||
| 310 | shared_font.clear(); | ||
| 311 | std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||
| 312 | |||
| 313 | FileUtil::CreateFullPath(filepath); // Create path if not already created | ||
| 314 | FileUtil::IOFile file(filepath, "rb"); | ||
| 315 | |||
| 316 | if (file.IsOpen()) { | ||
| 317 | // Read shared font data | ||
| 318 | shared_font.resize((size_t)file.GetSize()); | ||
| 319 | file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); | ||
| 320 | |||
| 321 | // Create shared font memory object | ||
| 322 | shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem"); | ||
| 323 | } else { | ||
| 324 | LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); | ||
| 325 | shared_font_mem = 0; | ||
| 326 | } | ||
| 263 | 327 | ||
| 264 | lock_handle = 0; | 328 | lock_handle = 0; |
| 265 | } | ||
| 266 | 329 | ||
| 267 | Interface::~Interface() { | 330 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 268 | } | 331 | } |
| 269 | 332 | ||
| 270 | } // namespace | 333 | } // namespace |
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h index 306730400..aad918cfc 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 |
| @@ -20,15 +20,8 @@ namespace APT_U { | |||
| 20 | /// Interface to "APT:U" service | 20 | /// Interface to "APT:U" service |
| 21 | class Interface : public Service::Interface { | 21 | class Interface : public Service::Interface { |
| 22 | public: | 22 | public: |
| 23 | |||
| 24 | Interface(); | 23 | Interface(); |
| 25 | 24 | ||
| 26 | ~Interface(); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Gets the string port name used by CTROS for the service | ||
| 30 | * @return Port name of service | ||
| 31 | */ | ||
| 32 | std::string GetPortName() const override { | 25 | std::string GetPortName() const override { |
| 33 | return "APT:U"; | 26 | return "APT:U"; |
| 34 | } | 27 | } |
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp index b2ff4a756..1820ea7ad 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" |
| @@ -11,18 +11,15 @@ | |||
| 11 | 11 | ||
| 12 | namespace BOSS_U { | 12 | namespace BOSS_U { |
| 13 | 13 | ||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 14 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00020100, nullptr, "GetStorageInfo"}, | 15 | {0x00020100, nullptr, "GetStorageInfo"}, |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 19 | // Interface class | 19 | // Interface class |
| 20 | |||
| 21 | Interface::Interface() { | ||
| 22 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 23 | } | ||
| 24 | 20 | ||
| 25 | Interface::~Interface() { | 21 | Interface::Interface() { |
| 26 | } | 22 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 23 | } | ||
| 27 | 24 | ||
| 28 | } // namespace | 25 | } // namespace |
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h index af39b8e65..2668f2dfd 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 |
| @@ -11,17 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | namespace BOSS_U { | 12 | namespace BOSS_U { |
| 13 | 13 | ||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~Interface(); | 17 | |
| 18 | /** | 18 | std::string GetPortName() const override { |
| 19 | * Gets the string port name used by CTROS for the service | 19 | return "boss:U"; |
| 20 | * @return Port name of service | 20 | } |
| 21 | */ | 21 | }; |
| 22 | std::string GetPortName() const { | ||
| 23 | return "boss:U"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | 22 | ||
| 27 | } // namespace | 23 | } // namespace |
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp new file mode 100644 index 000000000..b7655ef0b --- /dev/null +++ b/src/core/hle/service/cecd_u.cpp | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/cecd_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CECD_U | ||
| 11 | |||
| 12 | namespace CECD_U { | ||
| 13 | |||
| 14 | // Empty arrays are illegal -- commented out until an entry is added. | ||
| 15 | //const Interface::FunctionInfo FunctionTable[] = { }; | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // Interface class | ||
| 19 | |||
| 20 | Interface::Interface() { | ||
| 21 | //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 22 | } | ||
| 23 | |||
| 24 | } // namespace | ||
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h new file mode 100644 index 000000000..e67564135 --- /dev/null +++ b/src/core/hle/service/cecd_u.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CECD_U | ||
| 11 | |||
| 12 | namespace CECD_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "cecd:u"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp new file mode 100644 index 000000000..161aa8531 --- /dev/null +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include "common/log.h" | ||
| 7 | #include "common/make_unique.h" | ||
| 8 | #include "core/file_sys/archive_systemsavedata.h" | ||
| 9 | #include "core/hle/service/cfg/cfg.h" | ||
| 10 | |||
| 11 | namespace Service { | ||
| 12 | namespace CFG { | ||
| 13 | |||
| 14 | const u64 CFG_SAVE_ID = 0x00010017; | ||
| 15 | const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; | ||
| 16 | const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } }; | ||
| 17 | const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; | ||
| 18 | const char CONSOLE_USERNAME[0x14] = "CITRA"; | ||
| 19 | /// This will be initialized in CFGInit, and will be used when creating the block | ||
| 20 | UsernameBlock CONSOLE_USERNAME_BLOCK; | ||
| 21 | /// TODO(Subv): Find out what this actually is | ||
| 22 | const u8 SOUND_OUTPUT_MODE = 2; | ||
| 23 | const u8 UNITED_STATES_COUNTRY_ID = 49; | ||
| 24 | /// TODO(Subv): Find what the other bytes are | ||
| 25 | const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games, | ||
| 29 | * for example Nintendo Zone | ||
| 30 | * Thanks Normmatt for providing this information | ||
| 31 | */ | ||
| 32 | const std::array<float, 8> STEREO_CAMERA_SETTINGS = { | ||
| 33 | 62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f, | ||
| 34 | 10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f | ||
| 35 | }; | ||
| 36 | |||
| 37 | static const u32 CONFIG_SAVEFILE_SIZE = 0x8000; | ||
| 38 | static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer; | ||
| 39 | |||
| 40 | static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data; | ||
| 41 | |||
| 42 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | ||
| 43 | // Read the header | ||
| 44 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 45 | |||
| 46 | auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), | ||
| 47 | [&](const SaveConfigBlockEntry& entry) { | ||
| 48 | return entry.block_id == block_id && entry.size == size && (entry.flags & flag); | ||
| 49 | }); | ||
| 50 | |||
| 51 | if (itr == std::end(config->block_entries)) { | ||
| 52 | LOG_ERROR(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag); | ||
| 53 | return ResultCode(-1); // TODO(Subv): Find the correct error code | ||
| 54 | } | ||
| 55 | |||
| 56 | // The data is located in the block header itself if the size is less than 4 bytes | ||
| 57 | if (itr->size <= 4) | ||
| 58 | memcpy(output, &itr->offset_or_data, itr->size); | ||
| 59 | else | ||
| 60 | memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size); | ||
| 61 | |||
| 62 | return RESULT_SUCCESS; | ||
| 63 | } | ||
| 64 | |||
| 65 | ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data) { | ||
| 66 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 67 | if (config->total_entries >= CONFIG_FILE_MAX_BLOCK_ENTRIES) | ||
| 68 | return ResultCode(-1); // TODO(Subv): Find the right error code | ||
| 69 | |||
| 70 | // Insert the block header with offset 0 for now | ||
| 71 | config->block_entries[config->total_entries] = { block_id, 0, size, flags }; | ||
| 72 | if (size > 4) { | ||
| 73 | u32 offset = config->data_entries_offset; | ||
| 74 | // Perform a search to locate the next offset for the new data | ||
| 75 | // use the offset and size of the previous block to determine the new position | ||
| 76 | for (int i = config->total_entries - 1; i >= 0; --i) { | ||
| 77 | // Ignore the blocks that don't have a separate data offset | ||
| 78 | if (config->block_entries[i].size > 4) { | ||
| 79 | offset = config->block_entries[i].offset_or_data + | ||
| 80 | config->block_entries[i].size; | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | config->block_entries[config->total_entries].offset_or_data = offset; | ||
| 86 | |||
| 87 | // Write the data at the new offset | ||
| 88 | memcpy(&cfg_config_file_buffer[offset], data, size); | ||
| 89 | } | ||
| 90 | else { | ||
| 91 | // The offset_or_data field in the header contains the data itself if it's 4 bytes or less | ||
| 92 | memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size); | ||
| 93 | } | ||
| 94 | |||
| 95 | ++config->total_entries; | ||
| 96 | return RESULT_SUCCESS; | ||
| 97 | } | ||
| 98 | |||
| 99 | ResultCode DeleteConfigNANDSaveFile() { | ||
| 100 | FileSys::Path path("config"); | ||
| 101 | if (cfg_system_save_data->DeleteFile(path)) | ||
| 102 | return RESULT_SUCCESS; | ||
| 103 | return ResultCode(-1); // TODO(Subv): Find the right error code | ||
| 104 | } | ||
| 105 | |||
| 106 | ResultCode UpdateConfigNANDSavegame() { | ||
| 107 | FileSys::Mode mode = {}; | ||
| 108 | mode.write_flag = 1; | ||
| 109 | mode.create_flag = 1; | ||
| 110 | FileSys::Path path("config"); | ||
| 111 | auto file = cfg_system_save_data->OpenFile(path, mode); | ||
| 112 | _assert_msg_(Service_CFG, file != nullptr, "could not open file"); | ||
| 113 | file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); | ||
| 114 | return RESULT_SUCCESS; | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultCode FormatConfig() { | ||
| 118 | ResultCode res = DeleteConfigNANDSaveFile(); | ||
| 119 | if (!res.IsSuccess()) | ||
| 120 | return res; | ||
| 121 | // Delete the old data | ||
| 122 | cfg_config_file_buffer.fill(0); | ||
| 123 | // Create the header | ||
| 124 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 125 | // This value is hardcoded, taken from 3dbrew, verified by hardware, it's always the same value | ||
| 126 | config->data_entries_offset = 0x455C; | ||
| 127 | // Insert the default blocks | ||
| 128 | res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, | ||
| 129 | reinterpret_cast<const u8*>(STEREO_CAMERA_SETTINGS.data())); | ||
| 130 | if (!res.IsSuccess()) | ||
| 131 | return res; | ||
| 132 | res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, | ||
| 133 | reinterpret_cast<const u8*>(&CONSOLE_UNIQUE_ID)); | ||
| 134 | if (!res.IsSuccess()) | ||
| 135 | return res; | ||
| 136 | res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0x8, | ||
| 137 | reinterpret_cast<const u8*>(&CONSOLE_MODEL)); | ||
| 138 | if (!res.IsSuccess()) | ||
| 139 | return res; | ||
| 140 | res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xA, &CONSOLE_LANGUAGE); | ||
| 141 | if (!res.IsSuccess()) | ||
| 142 | return res; | ||
| 143 | res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); | ||
| 144 | if (!res.IsSuccess()) | ||
| 145 | return res; | ||
| 146 | res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, | ||
| 147 | reinterpret_cast<const u8*>(&COUNTRY_INFO)); | ||
| 148 | if (!res.IsSuccess()) | ||
| 149 | return res; | ||
| 150 | res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, | ||
| 151 | reinterpret_cast<const u8*>(&CONSOLE_USERNAME_BLOCK)); | ||
| 152 | if (!res.IsSuccess()) | ||
| 153 | return res; | ||
| 154 | // Save the buffer to the file | ||
| 155 | res = UpdateConfigNANDSavegame(); | ||
| 156 | if (!res.IsSuccess()) | ||
| 157 | return res; | ||
| 158 | return RESULT_SUCCESS; | ||
| 159 | } | ||
| 160 | |||
| 161 | void CFGInit() { | ||
| 162 | // TODO(Subv): In the future we should use the FS service to query this archive, | ||
| 163 | // currently it is not possible because you can only have one open archive of the same type at any time | ||
| 164 | std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX); | ||
| 165 | cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>( | ||
| 166 | syssavedata_directory, CFG_SAVE_ID); | ||
| 167 | if (!cfg_system_save_data->Initialize()) { | ||
| 168 | LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service"); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | |||
| 172 | // TODO(Subv): All this code should be moved to cfg:i, | ||
| 173 | // it's only here because we do not currently emulate the lower level code that uses that service | ||
| 174 | // Try to open the file in read-only mode to check its existence | ||
| 175 | FileSys::Mode mode = {}; | ||
| 176 | mode.read_flag = 1; | ||
| 177 | FileSys::Path path("config"); | ||
| 178 | auto file = cfg_system_save_data->OpenFile(path, mode); | ||
| 179 | |||
| 180 | // Load the config if it already exists | ||
| 181 | if (file != nullptr) { | ||
| 182 | file->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | |||
| 186 | // Initialize the Username block | ||
| 187 | // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals | ||
| 188 | CONSOLE_USERNAME_BLOCK.ng_word = 0; | ||
| 189 | CONSOLE_USERNAME_BLOCK.zero = 0; | ||
| 190 | // Copy string to buffer and pad with zeros at the end | ||
| 191 | auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14); | ||
| 192 | std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size, | ||
| 193 | std::end(CONSOLE_USERNAME_BLOCK.username), 0); | ||
| 194 | FormatConfig(); | ||
| 195 | } | ||
| 196 | |||
| 197 | void CFGShutdown() { | ||
| 198 | |||
| 199 | } | ||
| 200 | |||
| 201 | } // namespace CFG | ||
| 202 | } // namespace Service | ||
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h new file mode 100644 index 000000000..c74527ca4 --- /dev/null +++ b/src/core/hle/service/cfg/cfg.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include "core/hle/result.h" | ||
| 9 | |||
| 10 | namespace Service { | ||
| 11 | namespace CFG { | ||
| 12 | |||
| 13 | enum SystemModel { | ||
| 14 | NINTENDO_3DS = 0, | ||
| 15 | NINTENDO_3DS_XL = 1, | ||
| 16 | NEW_NINTENDO_3DS = 2, | ||
| 17 | NINTENDO_2DS = 3, | ||
| 18 | NEW_NINTENDO_3DS_XL = 4 | ||
| 19 | }; | ||
| 20 | |||
| 21 | enum SystemLanguage { | ||
| 22 | LANGUAGE_JP = 0, | ||
| 23 | LANGUAGE_EN = 1, | ||
| 24 | LANGUAGE_FR = 2, | ||
| 25 | LANGUAGE_DE = 3, | ||
| 26 | LANGUAGE_IT = 4, | ||
| 27 | LANGUAGE_ES = 5, | ||
| 28 | LANGUAGE_ZH = 6, | ||
| 29 | LANGUAGE_KO = 7, | ||
| 30 | LANGUAGE_NL = 8, | ||
| 31 | LANGUAGE_PT = 9, | ||
| 32 | LANGUAGE_RU = 10 | ||
| 33 | }; | ||
| 34 | |||
| 35 | /// Block header in the config savedata file | ||
| 36 | struct SaveConfigBlockEntry { | ||
| 37 | u32 block_id; ///< The id of the current block | ||
| 38 | u32 offset_or_data; ///< This is the absolute offset to the block data if the size is greater than 4 bytes, otherwise it contains the data itself | ||
| 39 | u16 size; ///< The size of the block | ||
| 40 | u16 flags; ///< The flags of the block, possibly used for access control | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// The maximum number of block entries that can exist in the config file | ||
| 44 | static const u32 CONFIG_FILE_MAX_BLOCK_ENTRIES = 1479; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * The header of the config savedata file, | ||
| 48 | * contains information about the blocks in the file | ||
| 49 | */ | ||
| 50 | struct SaveFileConfig { | ||
| 51 | u16 total_entries; ///< The total number of set entries in the config file | ||
| 52 | u16 data_entries_offset; ///< The offset where the data for the blocks start, this is hardcoded to 0x455C as per hardware | ||
| 53 | SaveConfigBlockEntry block_entries[CONFIG_FILE_MAX_BLOCK_ENTRIES]; ///< The block headers, the maximum possible value is 1479 as per hardware | ||
| 54 | u32 unknown; ///< This field is unknown, possibly padding, 0 has been observed in hardware | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(SaveFileConfig) == 0x455C, "The SaveFileConfig header must be exactly 0x455C bytes"); | ||
| 57 | |||
| 58 | struct UsernameBlock { | ||
| 59 | char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary | ||
| 60 | u32 zero; | ||
| 61 | u32 ng_word; | ||
| 62 | }; | ||
| 63 | static_assert(sizeof(UsernameBlock) == 0x1C, "Size of UsernameBlock must be 0x1C"); | ||
| 64 | |||
| 65 | struct ConsoleModelInfo { | ||
| 66 | u8 model; ///< The console model (3DS, 2DS, etc) | ||
| 67 | u8 unknown[3]; ///< Unknown data | ||
| 68 | }; | ||
| 69 | static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes"); | ||
| 70 | |||
| 71 | struct ConsoleCountryInfo { | ||
| 72 | u8 unknown[3]; ///< Unknown data | ||
| 73 | u8 country_code; ///< The country code of the console | ||
| 74 | }; | ||
| 75 | static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes"); | ||
| 76 | |||
| 77 | extern const u64 CFG_SAVE_ID; | ||
| 78 | extern const u64 CONSOLE_UNIQUE_ID; | ||
| 79 | extern const ConsoleModelInfo CONSOLE_MODEL; | ||
| 80 | extern const u8 CONSOLE_LANGUAGE; | ||
| 81 | extern const char CONSOLE_USERNAME[0x14]; | ||
| 82 | /// This will be initialized in the Interface constructor, and will be used when creating the block | ||
| 83 | extern UsernameBlock CONSOLE_USERNAME_BLOCK; | ||
| 84 | /// TODO(Subv): Find out what this actually is | ||
| 85 | extern const u8 SOUND_OUTPUT_MODE; | ||
| 86 | extern const u8 UNITED_STATES_COUNTRY_ID; | ||
| 87 | /// TODO(Subv): Find what the other bytes are | ||
| 88 | extern const ConsoleCountryInfo COUNTRY_INFO; | ||
| 89 | extern const std::array<float, 8> STEREO_CAMERA_SETTINGS; | ||
| 90 | |||
| 91 | static_assert(sizeof(STEREO_CAMERA_SETTINGS) == 0x20, "STEREO_CAMERA_SETTINGS must be exactly 0x20 bytes"); | ||
| 92 | static_assert(sizeof(CONSOLE_UNIQUE_ID) == 8, "CONSOLE_UNIQUE_ID must be exactly 8 bytes"); | ||
| 93 | static_assert(sizeof(CONSOLE_LANGUAGE) == 1, "CONSOLE_LANGUAGE must be exactly 1 byte"); | ||
| 94 | static_assert(sizeof(SOUND_OUTPUT_MODE) == 1, "SOUND_OUTPUT_MODE must be exactly 1 byte"); | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Reads a block with the specified id and flag from the Config savegame buffer | ||
| 98 | * and writes the output to output. | ||
| 99 | * The input size must match exactly the size of the requested block | ||
| 100 | * @param block_id The id of the block we want to read | ||
| 101 | * @param size The size of the block we want to read | ||
| 102 | * @param flag The requested block must have this flag set | ||
| 103 | * @param output A pointer where we will write the read data | ||
| 104 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 105 | */ | ||
| 106 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. | ||
| 110 | * The config savegame file in the filesystem is not updated. | ||
| 111 | * @param block_id The id of the block we want to create | ||
| 112 | * @param size The size of the block we want to create | ||
| 113 | * @param flag The flags of the new block | ||
| 114 | * @param data A pointer containing the data we will write to the new block | ||
| 115 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 116 | */ | ||
| 117 | ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data); | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Deletes the config savegame file from the filesystem, the buffer in memory is not affected | ||
| 121 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 122 | */ | ||
| 123 | ResultCode DeleteConfigNANDSaveFile(); | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Writes the config savegame memory buffer to the config savegame file in the filesystem | ||
| 127 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 128 | */ | ||
| 129 | ResultCode UpdateConfigNANDSavegame(); | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Re-creates the config savegame file in memory and the filesystem with the default blocks | ||
| 133 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 134 | */ | ||
| 135 | ResultCode FormatConfig(); | ||
| 136 | |||
| 137 | /// Initialize the config service | ||
| 138 | void CFGInit(); | ||
| 139 | |||
| 140 | /// Shutdown the config service | ||
| 141 | void CFGShutdown(); | ||
| 142 | |||
| 143 | } // namespace CFG | ||
| 144 | } // namespace Service | ||
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp new file mode 100644 index 000000000..7c1ee8ac3 --- /dev/null +++ b/src/core/hle/service/cfg/cfg_i.cpp | |||
| @@ -0,0 +1,110 @@ | |||
| 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/cfg/cfg.h" | ||
| 8 | #include "core/hle/service/cfg/cfg_i.h" | ||
| 9 | |||
| 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 11 | // Namespace CFG_I | ||
| 12 | |||
| 13 | namespace CFG_I { | ||
| 14 | |||
| 15 | /** | ||
| 16 | * CFG_I::GetConfigInfoBlk8 service function | ||
| 17 | * This function is called by two command headers, | ||
| 18 | * there appears to be no difference between them according to 3dbrew | ||
| 19 | * Inputs: | ||
| 20 | * 0 : 0x04010082 / 0x08010082 | ||
| 21 | * 1 : Size | ||
| 22 | * 2 : Block ID | ||
| 23 | * 3 : Descriptor for the output buffer | ||
| 24 | * 4 : Output buffer pointer | ||
| 25 | * Outputs: | ||
| 26 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 27 | */ | ||
| 28 | static void GetConfigInfoBlk8(Service::Interface* self) { | ||
| 29 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 30 | u32 size = cmd_buffer[1]; | ||
| 31 | u32 block_id = cmd_buffer[2]; | ||
| 32 | u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); | ||
| 33 | |||
| 34 | if (data_pointer == nullptr) { | ||
| 35 | cmd_buffer[1] = -1; // TODO(Subv): Find the right error code | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | |||
| 39 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw; | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * CFG_I::UpdateConfigNANDSavegame service function | ||
| 44 | * This function is called by two command headers, | ||
| 45 | * there appears to be no difference between them according to 3dbrew | ||
| 46 | * Inputs: | ||
| 47 | * 0 : 0x04030000 / 0x08030000 | ||
| 48 | * Outputs: | ||
| 49 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 50 | */ | ||
| 51 | static void UpdateConfigNANDSavegame(Service::Interface* self) { | ||
| 52 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 53 | cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * CFG_I::FormatConfig service function | ||
| 58 | * Inputs: | ||
| 59 | * 0 : 0x08060000 | ||
| 60 | * Outputs: | ||
| 61 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 62 | */ | ||
| 63 | static void FormatConfig(Service::Interface* self) { | ||
| 64 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 65 | cmd_buffer[1] = Service::CFG::FormatConfig().raw; | ||
| 66 | } | ||
| 67 | |||
| 68 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 69 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, | ||
| 70 | {0x04020082, nullptr, "SetConfigInfoBlk4"}, | ||
| 71 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | ||
| 72 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, | ||
| 73 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, | ||
| 74 | {0x04060000, nullptr, "SecureInfoGetRegion"}, | ||
| 75 | {0x04070000, nullptr, "SecureInfoGetByte101"}, | ||
| 76 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, | ||
| 77 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, | ||
| 78 | {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, | ||
| 79 | {0x08020082, nullptr, "SetConfigInfoBlk4"}, | ||
| 80 | {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | ||
| 81 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, | ||
| 82 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, | ||
| 83 | {0x08060000, FormatConfig, "FormatConfig"}, | ||
| 84 | {0x08080000, nullptr, "UpdateConfigBlk1"}, | ||
| 85 | {0x08090000, nullptr, "UpdateConfigBlk2"}, | ||
| 86 | {0x080A0000, nullptr, "UpdateConfigBlk3"}, | ||
| 87 | {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"}, | ||
| 88 | {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"}, | ||
| 89 | {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"}, | ||
| 90 | {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"}, | ||
| 91 | {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"}, | ||
| 92 | {0x08100000, nullptr, "GetLocalFriendCodeSeed"}, | ||
| 93 | {0x08110084, nullptr, "SetSecureInfo"}, | ||
| 94 | {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"}, | ||
| 95 | {0x08130000, nullptr, "VerifySigSecureInfo"}, | ||
| 96 | {0x08140042, nullptr, "SecureInfoGetData"}, | ||
| 97 | {0x08150042, nullptr, "SecureInfoGetSignature"}, | ||
| 98 | {0x08160000, nullptr, "SecureInfoGetRegion"}, | ||
| 99 | {0x08170000, nullptr, "SecureInfoGetByte101"}, | ||
| 100 | {0x08180042, nullptr, "SecureInfoGetSerialNo"}, | ||
| 101 | }; | ||
| 102 | |||
| 103 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 104 | // Interface class | ||
| 105 | |||
| 106 | Interface::Interface() { | ||
| 107 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 108 | } | ||
| 109 | |||
| 110 | } // namespace | ||
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h index fe343c968..a498dd589 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 |
| @@ -14,11 +14,7 @@ namespace CFG_I { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "cfg:i"; | 19 | return "cfg:i"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp new file mode 100644 index 000000000..03c01cf90 --- /dev/null +++ b/src/core/hle/service/cfg/cfg_u.cpp | |||
| @@ -0,0 +1,192 @@ | |||
| 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/file_util.h" | ||
| 6 | #include "common/log.h" | ||
| 7 | #include "common/string_util.h" | ||
| 8 | #include "core/file_sys/archive_systemsavedata.h" | ||
| 9 | #include "core/hle/hle.h" | ||
| 10 | #include "core/hle/service/cfg/cfg.h" | ||
| 11 | #include "core/hle/service/cfg/cfg_u.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // Namespace CFG_U | ||
| 15 | |||
| 16 | namespace CFG_U { | ||
| 17 | |||
| 18 | // TODO(Link Mauve): use a constexpr once MSVC starts supporting it. | ||
| 19 | #define C(code) ((code)[0] | ((code)[1] << 8)) | ||
| 20 | |||
| 21 | static const std::array<u16, 187> country_codes = { | ||
| 22 | 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7 | ||
| 23 | C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15 | ||
| 24 | C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23 | ||
| 25 | C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31 | ||
| 26 | C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39 | ||
| 27 | C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47 | ||
| 28 | C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55 | ||
| 29 | 0, 0, 0, 0, 0, 0, 0, 0, // 56-63 | ||
| 30 | C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71 | ||
| 31 | C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79 | ||
| 32 | C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87 | ||
| 33 | C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95 | ||
| 34 | C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103 | ||
| 35 | C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111 | ||
| 36 | C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119 | ||
| 37 | C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127 | ||
| 38 | C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135 | ||
| 39 | C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143 | ||
| 40 | C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151 | ||
| 41 | C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159 | ||
| 42 | C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167 | ||
| 43 | C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175 | ||
| 44 | C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183 | ||
| 45 | C("SM"), C("VA"), C("BM") // 184-186 | ||
| 46 | }; | ||
| 47 | |||
| 48 | #undef C | ||
| 49 | |||
| 50 | /** | ||
| 51 | * CFG_User::GetCountryCodeString service function | ||
| 52 | * Inputs: | ||
| 53 | * 1 : Country Code ID | ||
| 54 | * Outputs: | ||
| 55 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 56 | * 2 : Country's 2-char string | ||
| 57 | */ | ||
| 58 | static void GetCountryCodeString(Service::Interface* self) { | ||
| 59 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 60 | u32 country_code_id = cmd_buffer[1]; | ||
| 61 | |||
| 62 | if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) { | ||
| 63 | LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id); | ||
| 64 | cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | cmd_buffer[1] = 0; | ||
| 69 | cmd_buffer[2] = country_codes[country_code_id]; | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * CFG_User::GetCountryCodeID service function | ||
| 74 | * Inputs: | ||
| 75 | * 1 : Country Code 2-char string | ||
| 76 | * Outputs: | ||
| 77 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 78 | * 2 : Country Code ID | ||
| 79 | */ | ||
| 80 | static void GetCountryCodeID(Service::Interface* self) { | ||
| 81 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 82 | u16 country_code = cmd_buffer[1]; | ||
| 83 | u16 country_code_id = 0; | ||
| 84 | |||
| 85 | // The following algorithm will fail if the first country code isn't 0. | ||
| 86 | _dbg_assert_(Service_CFG, country_codes[0] == 0); | ||
| 87 | |||
| 88 | for (size_t id = 0; id < country_codes.size(); ++id) { | ||
| 89 | if (country_codes[id] == country_code) { | ||
| 90 | country_code_id = id; | ||
| 91 | break; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | if (0 == country_code_id) { | ||
| 96 | LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8); | ||
| 97 | cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; | ||
| 98 | cmd_buffer[2] = 0xFFFF; | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | |||
| 102 | cmd_buffer[1] = 0; | ||
| 103 | cmd_buffer[2] = country_code_id; | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * CFG_User::GetConfigInfoBlk2 service function | ||
| 108 | * Inputs: | ||
| 109 | * 0 : 0x00010082 | ||
| 110 | * 1 : Size | ||
| 111 | * 2 : Block ID | ||
| 112 | * 3 : Descriptor for the output buffer | ||
| 113 | * 4 : Output buffer pointer | ||
| 114 | * Outputs: | ||
| 115 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 116 | */ | ||
| 117 | static void GetConfigInfoBlk2(Service::Interface* self) { | ||
| 118 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 119 | u32 size = cmd_buffer[1]; | ||
| 120 | u32 block_id = cmd_buffer[2]; | ||
| 121 | u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); | ||
| 122 | |||
| 123 | if (data_pointer == nullptr) { | ||
| 124 | cmd_buffer[1] = -1; // TODO(Subv): Find the right error code | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw; | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * CFG_User::GetSystemModel service function | ||
| 133 | * Inputs: | ||
| 134 | * 0 : 0x00050000 | ||
| 135 | * Outputs: | ||
| 136 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 137 | * 2 : Model of the console | ||
| 138 | */ | ||
| 139 | static void GetSystemModel(Service::Interface* self) { | ||
| 140 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 141 | u32 data; | ||
| 142 | |||
| 143 | // TODO(Subv): Find out the correct error codes | ||
| 144 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8, | ||
| 145 | reinterpret_cast<u8*>(&data)).raw; | ||
| 146 | cmd_buffer[2] = data & 0xFF; | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * CFG_User::GetModelNintendo2DS service function | ||
| 151 | * Inputs: | ||
| 152 | * 0 : 0x00060000 | ||
| 153 | * Outputs: | ||
| 154 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 155 | * 2 : 0 if the system is a Nintendo 2DS, 1 otherwise | ||
| 156 | */ | ||
| 157 | static void GetModelNintendo2DS(Service::Interface* self) { | ||
| 158 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 159 | u32 data; | ||
| 160 | |||
| 161 | // TODO(Subv): Find out the correct error codes | ||
| 162 | cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8, | ||
| 163 | reinterpret_cast<u8*>(&data)).raw; | ||
| 164 | |||
| 165 | u8 model = data & 0xFF; | ||
| 166 | if (model == Service::CFG::NINTENDO_2DS) | ||
| 167 | cmd_buffer[2] = 0; | ||
| 168 | else | ||
| 169 | cmd_buffer[2] = 1; | ||
| 170 | } | ||
| 171 | |||
| 172 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 173 | {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, | ||
| 174 | {0x00020000, nullptr, "SecureInfoGetRegion"}, | ||
| 175 | {0x00030000, nullptr, "GenHashConsoleUnique"}, | ||
| 176 | {0x00040000, nullptr, "GetRegionCanadaUSA"}, | ||
| 177 | {0x00050000, GetSystemModel, "GetSystemModel"}, | ||
| 178 | {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, | ||
| 179 | {0x00070040, nullptr, "unknown"}, | ||
| 180 | {0x00080080, nullptr, "unknown"}, | ||
| 181 | {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, | ||
| 182 | {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, | ||
| 183 | }; | ||
| 184 | |||
| 185 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 186 | // Interface class | ||
| 187 | |||
| 188 | Interface::Interface() { | ||
| 189 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 190 | } | ||
| 191 | |||
| 192 | } // namespace | ||
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h index 8075d19a8..9ad73f355 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 |
| @@ -14,11 +14,7 @@ namespace CFG_U { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "cfg:u"; | 19 | return "cfg:u"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp deleted file mode 100644 index 88d13d459..000000000 --- a/src/core/hle/service/cfg_i.cpp +++ /dev/null | |||
| @@ -1,59 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/cfg_i.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CFG_I | ||
| 11 | |||
| 12 | namespace CFG_I { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x04010082, nullptr, "GetConfigInfoBlk8"}, | ||
| 16 | {0x04020082, nullptr, "GetConfigInfoBlk4"}, | ||
| 17 | {0x04030000, nullptr, "UpdateConfigNANDSavegame"}, | ||
| 18 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, | ||
| 19 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, | ||
| 20 | {0x04060000, nullptr, "SecureInfoGetRegion"}, | ||
| 21 | {0x04070000, nullptr, "SecureInfoGetByte101"}, | ||
| 22 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, | ||
| 23 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, | ||
| 24 | {0x08010082, nullptr, "GetConfigInfoBlk8"}, | ||
| 25 | {0x08020082, nullptr, "GetConfigInfoBlk4"}, | ||
| 26 | {0x08030000, nullptr, "UpdateConfigNANDSavegame"}, | ||
| 27 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, | ||
| 28 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, | ||
| 29 | {0x08060000, nullptr, "FormatConfig"}, | ||
| 30 | {0x08070000, nullptr, "Unknown"}, | ||
| 31 | {0x08080000, nullptr, "UpdateConfigBlk1"}, | ||
| 32 | {0x08090000, nullptr, "UpdateConfigBlk2"}, | ||
| 33 | {0x080A0000, nullptr, "UpdateConfigBlk3"}, | ||
| 34 | {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"}, | ||
| 35 | {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"}, | ||
| 36 | {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"}, | ||
| 37 | {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"}, | ||
| 38 | {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"}, | ||
| 39 | {0x08100000, nullptr, "GetLocalFriendCodeSeed"}, | ||
| 40 | {0x08110084, nullptr, "SetSecureInfo"}, | ||
| 41 | {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"}, | ||
| 42 | {0x08130000, nullptr, "VerifySigSecureInfo"}, | ||
| 43 | {0x08140042, nullptr, "SecureInfoGetData"}, | ||
| 44 | {0x08150042, nullptr, "SecureInfoGetSignature"}, | ||
| 45 | {0x08160000, nullptr, "SecureInfoGetRegion"}, | ||
| 46 | {0x08170000, nullptr, "SecureInfoGetByte101"}, | ||
| 47 | {0x08180042, nullptr, "SecureInfoGetSerialNo"}, | ||
| 48 | }; | ||
| 49 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 50 | // Interface class | ||
| 51 | |||
| 52 | Interface::Interface() { | ||
| 53 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 54 | } | ||
| 55 | |||
| 56 | Interface::~Interface() { | ||
| 57 | } | ||
| 58 | |||
| 59 | } // namespace | ||
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp deleted file mode 100644 index 822b0e2b8..000000000 --- a/src/core/hle/service/cfg_u.cpp +++ /dev/null | |||
| @@ -1,36 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/cfg_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace CFG_U | ||
| 11 | |||
| 12 | namespace CFG_U { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010082, nullptr, "GetConfigInfoBlk2"}, | ||
| 16 | {0x00020000, nullptr, "SecureInfoGetRegion"}, | ||
| 17 | {0x00030000, nullptr, "GenHashConsoleUnique"}, | ||
| 18 | {0x00040000, nullptr, "GetRegionCanadaUSA"}, | ||
| 19 | {0x00050000, nullptr, "GetSystemModel"}, | ||
| 20 | {0x00060000, nullptr, "GetModelNintendo2DS"}, | ||
| 21 | {0x00070040, nullptr, "unknown"}, | ||
| 22 | {0x00080080, nullptr, "unknown"}, | ||
| 23 | {0x00090080, nullptr, "GetCountryCodeString"}, | ||
| 24 | {0x000A0040, nullptr, "GetCountryCodeID"}, | ||
| 25 | }; | ||
| 26 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 27 | // Interface class | ||
| 28 | |||
| 29 | Interface::Interface() { | ||
| 30 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 31 | } | ||
| 32 | |||
| 33 | Interface::~Interface() { | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace | ||
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 6e59a9bf3..aef8cfbca 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" |
| @@ -33,7 +33,4 @@ Interface::Interface() { | |||
| 33 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 33 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | Interface::~Interface() { | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace | 36 | } // namespace |
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h index 31cc85b07..a84752473 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 |
| @@ -14,11 +14,7 @@ namespace CSND_SND { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "csnd:SND"; | 19 | return "csnd:SND"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index bbcf26f61..2cf4d118f 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -1,9 +1,10 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 2 | // Licensed under 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/kernel/event.h" | ||
| 7 | #include "core/hle/service/dsp_dsp.h" | 8 | #include "core/hle/service/dsp_dsp.h" |
| 8 | 9 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -11,42 +12,182 @@ | |||
| 11 | 12 | ||
| 12 | namespace DSP_DSP { | 13 | namespace DSP_DSP { |
| 13 | 14 | ||
| 15 | static u32 read_pipe_count; | ||
| 16 | static Handle semaphore_event; | ||
| 17 | static Handle interrupt_event; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * DSP_DSP::ConvertProcessAddressFromDspDram service function | ||
| 21 | * Inputs: | ||
| 22 | * 1 : Address | ||
| 23 | * Outputs: | ||
| 24 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 25 | * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address) | ||
| 26 | */ | ||
| 27 | void ConvertProcessAddressFromDspDram(Service::Interface* self) { | ||
| 28 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 29 | |||
| 30 | u32 addr = cmd_buff[1]; | ||
| 31 | |||
| 32 | cmd_buff[1] = 0; // No error | ||
| 33 | cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000); | ||
| 34 | |||
| 35 | LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr); | ||
| 36 | } | ||
| 37 | |||
| 38 | /** | ||
| 39 | * DSP_DSP::LoadComponent service function | ||
| 40 | * Inputs: | ||
| 41 | * 1 : Size | ||
| 42 | * 2 : Unknown (observed only half word used) | ||
| 43 | * 3 : Unknown (observed only half word used) | ||
| 44 | * 4 : (size << 4) | 0xA | ||
| 45 | * 5 : Buffer address | ||
| 46 | * Outputs: | ||
| 47 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 48 | * 2 : Component loaded, 0 on not loaded, 1 on loaded | ||
| 49 | */ | ||
| 50 | void LoadComponent(Service::Interface* self) { | ||
| 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 52 | |||
| 53 | cmd_buff[1] = 0; // No error | ||
| 54 | cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware | ||
| 55 | |||
| 56 | // TODO(bunnei): Implement real DSP firmware loading | ||
| 57 | |||
| 58 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * DSP_DSP::GetSemaphoreEventHandle service function | ||
| 63 | * Outputs: | ||
| 64 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 65 | * 3 : Semaphore event handle | ||
| 66 | */ | ||
| 67 | void GetSemaphoreEventHandle(Service::Interface* self) { | ||
| 68 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 69 | |||
| 70 | cmd_buff[1] = 0; // No error | ||
| 71 | cmd_buff[3] = semaphore_event; // Event handle | ||
| 72 | |||
| 73 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * DSP_DSP::RegisterInterruptEvents service function | ||
| 78 | * Inputs: | ||
| 79 | * 1 : Parameter 0 (purpose unknown) | ||
| 80 | * 2 : Parameter 1 (purpose unknown) | ||
| 81 | * 4 : Interrupt event handle | ||
| 82 | * Outputs: | ||
| 83 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 84 | */ | ||
| 85 | void RegisterInterruptEvents(Service::Interface* self) { | ||
| 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 87 | |||
| 88 | interrupt_event = static_cast<Handle>(cmd_buff[4]); | ||
| 89 | |||
| 90 | cmd_buff[1] = 0; // No error | ||
| 91 | |||
| 92 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * DSP_DSP::WriteReg0x10 service function | ||
| 97 | * Inputs: | ||
| 98 | * 1 : Unknown (observed only half word used) | ||
| 99 | * Outputs: | ||
| 100 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 101 | */ | ||
| 102 | void WriteReg0x10(Service::Interface* self) { | ||
| 103 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 104 | |||
| 105 | Kernel::SignalEvent(interrupt_event); | ||
| 106 | |||
| 107 | cmd_buff[1] = 0; // No error | ||
| 108 | |||
| 109 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * DSP_DSP::ReadPipeIfPossible service function | ||
| 114 | * Inputs: | ||
| 115 | * 1 : Unknown | ||
| 116 | * 2 : Unknown | ||
| 117 | * 3 : Size in bytes of read (observed only lower half word used) | ||
| 118 | * 0x41 : Virtual address to read from DSP pipe to in memory | ||
| 119 | * Outputs: | ||
| 120 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 121 | * 2 : Number of bytes read from pipe | ||
| 122 | */ | ||
| 123 | void ReadPipeIfPossible(Service::Interface* self) { | ||
| 124 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 125 | |||
| 126 | u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size | ||
| 127 | VAddr addr = cmd_buff[0x41]; | ||
| 128 | |||
| 129 | // Canned DSP responses that games expect. These were taken from HW by 3dmoo team. | ||
| 130 | // TODO: Remove this hack :) | ||
| 131 | static const std::array<u16, 16> canned_read_pipe = { | ||
| 132 | 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540, | ||
| 133 | 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58 | ||
| 134 | }; | ||
| 135 | |||
| 136 | u32 initial_size = read_pipe_count; | ||
| 137 | |||
| 138 | for (unsigned offset = 0; offset < size; offset += sizeof(u16)) { | ||
| 139 | if (read_pipe_count < canned_read_pipe.size()) { | ||
| 140 | Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]); | ||
| 141 | read_pipe_count++; | ||
| 142 | } else { | ||
| 143 | LOG_ERROR(Service_DSP, "canned read pipe log exceeded!"); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | cmd_buff[1] = 0; // No error | ||
| 149 | cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); | ||
| 150 | |||
| 151 | LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); | ||
| 152 | } | ||
| 153 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 154 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010040, nullptr, "RecvData"}, | 155 | {0x00010040, nullptr, "RecvData"}, |
| 16 | {0x00020040, nullptr, "RecvDataIsReady"}, | 156 | {0x00020040, nullptr, "RecvDataIsReady"}, |
| 17 | {0x00030080, nullptr, "SendData"}, | 157 | {0x00030080, nullptr, "SendData"}, |
| 18 | {0x00040040, nullptr, "SendDataIsEmpty"}, | 158 | {0x00040040, nullptr, "SendDataIsEmpty"}, |
| 19 | {0x00070040, nullptr, "WriteReg0x10"}, | 159 | {0x00070040, WriteReg0x10, "WriteReg0x10"}, |
| 20 | {0x00080000, nullptr, "GetSemaphore"}, | 160 | {0x00080000, nullptr, "GetSemaphore"}, |
| 21 | {0x00090040, nullptr, "ClearSemaphore"}, | 161 | {0x00090040, nullptr, "ClearSemaphore"}, |
| 22 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, | 162 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, |
| 23 | {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"}, | 163 | {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, |
| 24 | {0x000D0082, nullptr, "WriteProcessPipe"}, | 164 | {0x000D0082, nullptr, "WriteProcessPipe"}, |
| 25 | {0x001000C0, nullptr, "ReadPipeIfPossible"}, | 165 | {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, |
| 26 | {0x001100C2, nullptr, "LoadComponent"}, | 166 | {0x001100C2, LoadComponent, "LoadComponent"}, |
| 27 | {0x00120000, nullptr, "UnloadComponent"}, | 167 | {0x00120000, nullptr, "UnloadComponent"}, |
| 28 | {0x00130082, nullptr, "FlushDataCache"}, | 168 | {0x00130082, nullptr, "FlushDataCache"}, |
| 29 | {0x00140082, nullptr, "InvalidateDCache"}, | 169 | {0x00140082, nullptr, "InvalidateDCache"}, |
| 30 | {0x00150082, nullptr, "RegisterInterruptEvents"}, | 170 | {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"}, |
| 31 | {0x00160000, nullptr, "GetSemaphoreEventHandle"}, | 171 | {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"}, |
| 32 | {0x00170040, nullptr, "SetSemaphoreMask"}, | 172 | {0x00170040, nullptr, "SetSemaphoreMask"}, |
| 33 | {0x00180040, nullptr, "GetPhysicalAddress"}, | 173 | {0x00180040, nullptr, "GetPhysicalAddress"}, |
| 34 | {0x00190040, nullptr, "GetVirtualAddress"}, | 174 | {0x00190040, nullptr, "GetVirtualAddress"}, |
| 35 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, | 175 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, |
| 36 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, | 176 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, |
| 37 | {0x001C0082, nullptr, "SetIirFilterEQ"}, | 177 | {0x001C0082, nullptr, "SetIirFilterEQ"}, |
| 38 | {0x001F0000, nullptr, "GetHeadphoneStatus"}, | 178 | {0x001F0000, nullptr, "GetHeadphoneStatus"}, |
| 39 | {0x00210000, nullptr, "GetIsDspOccupied"}, | 179 | {0x00210000, nullptr, "GetIsDspOccupied"}, |
| 40 | }; | 180 | }; |
| 41 | 181 | ||
| 42 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 182 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 43 | // Interface class | 183 | // Interface class |
| 44 | 184 | ||
| 45 | Interface::Interface() { | 185 | Interface::Interface() { |
| 46 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 186 | semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event"); |
| 47 | } | 187 | interrupt_event = 0; |
| 188 | read_pipe_count = 0; | ||
| 48 | 189 | ||
| 49 | Interface::~Interface() { | 190 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 50 | } | 191 | } |
| 51 | 192 | ||
| 52 | } // namespace | 193 | } // namespace |
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index c4ce44245..0b8b64600 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 |
| @@ -14,13 +14,9 @@ namespace DSP_DSP { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "dsp:DSP"; | 19 | return "dsp::DSP"; |
| 24 | } | 20 | } |
| 25 | }; | 21 | }; |
| 26 | 22 | ||
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 785c351e9..8c900eabc 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" |
| @@ -11,17 +11,15 @@ | |||
| 11 | 11 | ||
| 12 | namespace ERR_F { | 12 | namespace ERR_F { |
| 13 | 13 | ||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 14 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010800, nullptr, "ThrowFatalError"} | 15 | {0x00010800, nullptr, "ThrowFatalError"} |
| 16 | }; | 16 | }; |
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // Interface class | ||
| 19 | 17 | ||
| 20 | Interface::Interface() { | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 21 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 19 | // Interface class |
| 22 | } | ||
| 23 | 20 | ||
| 24 | Interface::~Interface() { | 21 | Interface::Interface() { |
| 25 | } | 22 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 23 | } | ||
| 26 | 24 | ||
| 27 | } // namespace | 25 | } // namespace |
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h index 6d7141c1b..892d8af9b 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 |
| @@ -11,17 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | namespace ERR_F { | 12 | namespace ERR_F { |
| 13 | 13 | ||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~Interface(); | 17 | |
| 18 | /** | 18 | std::string GetPortName() const override { |
| 19 | * Gets the string port name used by CTROS for the service | 19 | return "err:f"; |
| 20 | * @return Port name of service | 20 | } |
| 21 | */ | 21 | }; |
| 22 | std::string GetPortName() const override { | ||
| 23 | return "err:f"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | 22 | ||
| 27 | } // namespace | 23 | } // namespace |
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp index 58023e536..021186e57 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" |
| @@ -11,25 +11,23 @@ | |||
| 11 | 11 | ||
| 12 | namespace FRD_U { | 12 | namespace FRD_U { |
| 13 | 13 | ||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 14 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00050000, nullptr, "GetFriendKey"}, | 15 | {0x00050000, nullptr, "GetFriendKey"}, |
| 16 | {0x00080000, nullptr, "GetMyPresence"}, | 16 | {0x00080000, nullptr, "GetMyPresence"}, |
| 17 | {0x00100040, nullptr, "GetPassword"}, | 17 | {0x00100040, nullptr, "GetPassword"}, |
| 18 | {0x00190042, nullptr, "GetFriendFavoriteGame"}, | 18 | {0x00190042, nullptr, "GetFriendFavoriteGame"}, |
| 19 | {0x001A00C4, nullptr, "GetFriendInfo"}, | 19 | {0x001A00C4, nullptr, "GetFriendInfo"}, |
| 20 | {0x001B0080, nullptr, "IsOnFriendList"}, | 20 | {0x001B0080, nullptr, "IsOnFriendList"}, |
| 21 | {0x001C0042, nullptr, "DecodeLocalFriendCode"}, | 21 | {0x001C0042, nullptr, "DecodeLocalFriendCode"}, |
| 22 | {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, | 22 | {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, |
| 23 | {0x00320042, nullptr, "SetClientSdkVersion"} | 23 | {0x00320042, nullptr, "SetClientSdkVersion"} |
| 24 | }; | 24 | }; |
| 25 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 26 | // Interface class | ||
| 27 | 25 | ||
| 28 | Interface::Interface() { | 26 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 27 | // Interface class |
| 30 | } | ||
| 31 | 28 | ||
| 32 | Interface::~Interface() { | 29 | Interface::Interface() { |
| 33 | } | 30 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 31 | } | ||
| 34 | 32 | ||
| 35 | } // namespace | 33 | } // namespace |
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h index 4020c6664..ab8897d5b 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 |
| @@ -11,17 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | namespace FRD_U { | 12 | namespace FRD_U { |
| 13 | 13 | ||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~Interface(); | 17 | |
| 18 | /** | 18 | std::string GetPortName() const override { |
| 19 | * Gets the string port name used by CTROS for the service | 19 | return "frd:u"; |
| 20 | * @return Port name of service | 20 | } |
| 21 | */ | 21 | }; |
| 22 | std::string GetPortName() const override { | ||
| 23 | return "frd:u"; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | 22 | ||
| 27 | } // namespace | 23 | } // namespace |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp new file mode 100644 index 000000000..487bf3aa7 --- /dev/null +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -0,0 +1,442 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <unordered_map> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/make_unique.h" | ||
| 11 | #include "common/math_util.h" | ||
| 12 | |||
| 13 | #include "core/file_sys/archive_savedata.h" | ||
| 14 | #include "core/file_sys/archive_backend.h" | ||
| 15 | #include "core/file_sys/archive_sdmc.h" | ||
| 16 | #include "core/file_sys/directory_backend.h" | ||
| 17 | #include "core/hle/service/fs/archive.h" | ||
| 18 | #include "core/hle/kernel/session.h" | ||
| 19 | #include "core/hle/result.h" | ||
| 20 | |||
| 21 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. | ||
| 22 | // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 | ||
| 23 | namespace std { | ||
| 24 | template <> | ||
| 25 | struct hash<Service::FS::ArchiveIdCode> { | ||
| 26 | typedef Service::FS::ArchiveIdCode argument_type; | ||
| 27 | typedef std::size_t result_type; | ||
| 28 | |||
| 29 | result_type operator()(const argument_type& id_code) const { | ||
| 30 | typedef std::underlying_type<argument_type>::type Type; | ||
| 31 | return std::hash<Type>()(static_cast<Type>(id_code)); | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | namespace Service { | ||
| 37 | namespace FS { | ||
| 38 | |||
| 39 | // Command to access archive file | ||
| 40 | enum class FileCommand : u32 { | ||
| 41 | Dummy1 = 0x000100C6, | ||
| 42 | Control = 0x040100C4, | ||
| 43 | OpenSubFile = 0x08010100, | ||
| 44 | Read = 0x080200C2, | ||
| 45 | Write = 0x08030102, | ||
| 46 | GetSize = 0x08040000, | ||
| 47 | SetSize = 0x08050080, | ||
| 48 | GetAttributes = 0x08060000, | ||
| 49 | SetAttributes = 0x08070040, | ||
| 50 | Close = 0x08080000, | ||
| 51 | Flush = 0x08090000, | ||
| 52 | }; | ||
| 53 | |||
| 54 | // Command to access directory | ||
| 55 | enum class DirectoryCommand : u32 { | ||
| 56 | Dummy1 = 0x000100C6, | ||
| 57 | Control = 0x040100C4, | ||
| 58 | Read = 0x08010042, | ||
| 59 | Close = 0x08020000, | ||
| 60 | }; | ||
| 61 | |||
| 62 | class Archive { | ||
| 63 | public: | ||
| 64 | Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) | ||
| 65 | : backend(std::move(backend)), id_code(id_code) { | ||
| 66 | } | ||
| 67 | |||
| 68 | std::string GetName() const { return "Archive: " + backend->GetName(); } | ||
| 69 | |||
| 70 | ArchiveIdCode id_code; ///< Id code of the archive | ||
| 71 | std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface | ||
| 72 | }; | ||
| 73 | |||
| 74 | class File : public Kernel::Session { | ||
| 75 | public: | ||
| 76 | File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) | ||
| 77 | : backend(std::move(backend)), path(path) { | ||
| 78 | } | ||
| 79 | |||
| 80 | std::string GetName() const override { return "Path: " + path.DebugStr(); } | ||
| 81 | |||
| 82 | FileSys::Path path; ///< Path of the file | ||
| 83 | std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface | ||
| 84 | |||
| 85 | ResultVal<bool> SyncRequest() override { | ||
| 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 87 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 88 | switch (cmd) { | ||
| 89 | |||
| 90 | // Read from file... | ||
| 91 | case FileCommand::Read: | ||
| 92 | { | ||
| 93 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 94 | u32 length = cmd_buff[3]; | ||
| 95 | u32 address = cmd_buff[5]; | ||
| 96 | LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 97 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 98 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | |||
| 102 | // Write to file... | ||
| 103 | case FileCommand::Write: | ||
| 104 | { | ||
| 105 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 106 | u32 length = cmd_buff[3]; | ||
| 107 | u32 flush = cmd_buff[4]; | ||
| 108 | u32 address = cmd_buff[6]; | ||
| 109 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 110 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 111 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | |||
| 115 | case FileCommand::GetSize: | ||
| 116 | { | ||
| 117 | LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 118 | u64 size = backend->GetSize(); | ||
| 119 | cmd_buff[2] = (u32)size; | ||
| 120 | cmd_buff[3] = size >> 32; | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | |||
| 124 | case FileCommand::SetSize: | ||
| 125 | { | ||
| 126 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 127 | LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", | ||
| 128 | GetTypeName().c_str(), GetName().c_str(), size); | ||
| 129 | backend->SetSize(size); | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | |||
| 133 | case FileCommand::Close: | ||
| 134 | { | ||
| 135 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 136 | backend->Close(); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | |||
| 140 | case FileCommand::Flush: | ||
| 141 | { | ||
| 142 | LOG_TRACE(Service_FS, "Flush"); | ||
| 143 | backend->Flush(); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | |||
| 147 | // Unknown command... | ||
| 148 | default: | ||
| 149 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 150 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 151 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 152 | return error; | ||
| 153 | } | ||
| 154 | cmd_buff[1] = 0; // No error | ||
| 155 | return MakeResult<bool>(false); | ||
| 156 | } | ||
| 157 | }; | ||
| 158 | |||
| 159 | class Directory : public Kernel::Session { | ||
| 160 | public: | ||
| 161 | Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) | ||
| 162 | : backend(std::move(backend)), path(path) { | ||
| 163 | } | ||
| 164 | |||
| 165 | std::string GetName() const override { return "Directory: " + path.DebugStr(); } | ||
| 166 | |||
| 167 | FileSys::Path path; ///< Path of the directory | ||
| 168 | std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface | ||
| 169 | |||
| 170 | ResultVal<bool> SyncRequest() override { | ||
| 171 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 172 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 173 | switch (cmd) { | ||
| 174 | |||
| 175 | // Read from directory... | ||
| 176 | case DirectoryCommand::Read: | ||
| 177 | { | ||
| 178 | u32 count = cmd_buff[1]; | ||
| 179 | u32 address = cmd_buff[3]; | ||
| 180 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 181 | LOG_TRACE(Service_FS, "Read %s %s: count=%d", | ||
| 182 | GetTypeName().c_str(), GetName().c_str(), count); | ||
| 183 | |||
| 184 | // Number of entries actually read | ||
| 185 | cmd_buff[2] = backend->Read(count, entries); | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | |||
| 189 | case DirectoryCommand::Close: | ||
| 190 | { | ||
| 191 | LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 192 | backend->Close(); | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | |||
| 196 | // Unknown command... | ||
| 197 | default: | ||
| 198 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||
| 199 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 200 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 201 | return MakeResult<bool>(false); | ||
| 202 | } | ||
| 203 | cmd_buff[1] = 0; // No error | ||
| 204 | return MakeResult<bool>(false); | ||
| 205 | } | ||
| 206 | }; | ||
| 207 | |||
| 208 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Map of registered archives, identified by id code. Once an archive is registered here, it is | ||
| 212 | * never removed until the FS service is shut down. | ||
| 213 | */ | ||
| 214 | static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map; | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Map of active archive handles. Values are pointers to the archives in `idcode_map`. | ||
| 218 | */ | ||
| 219 | static std::unordered_map<ArchiveHandle, Archive*> handle_map; | ||
| 220 | static ArchiveHandle next_handle; | ||
| 221 | |||
| 222 | static Archive* GetArchive(ArchiveHandle handle) { | ||
| 223 | auto itr = handle_map.find(handle); | ||
| 224 | return (itr == handle_map.end()) ? nullptr : itr->second; | ||
| 225 | } | ||
| 226 | |||
| 227 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) { | ||
| 228 | LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code); | ||
| 229 | |||
| 230 | auto itr = id_code_map.find(id_code); | ||
| 231 | if (itr == id_code_map.end()) { | ||
| 232 | if (id_code == ArchiveIdCode::SaveData) { | ||
| 233 | // When a SaveData archive is created for the first time, it is not yet formatted | ||
| 234 | // and the save file/directory structure expected by the game has not yet been initialized. | ||
| 235 | // Returning the NotFormatted error code will signal the game to provision the SaveData archive | ||
| 236 | // with the files and folders that it expects. | ||
| 237 | // The FormatSaveData service call will create the SaveData archive when it is called. | ||
| 238 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||
| 239 | ErrorSummary::InvalidState, ErrorLevel::Status); | ||
| 240 | } | ||
| 241 | // TODO: Verify error against hardware | ||
| 242 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 243 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 244 | } | ||
| 245 | |||
| 246 | // This should never even happen in the first place with 64-bit handles, | ||
| 247 | while (handle_map.count(next_handle) != 0) { | ||
| 248 | ++next_handle; | ||
| 249 | } | ||
| 250 | handle_map.emplace(next_handle, itr->second.get()); | ||
| 251 | return MakeResult<ArchiveHandle>(next_handle++); | ||
| 252 | } | ||
| 253 | |||
| 254 | ResultCode CloseArchive(ArchiveHandle handle) { | ||
| 255 | if (handle_map.erase(handle) == 0) | ||
| 256 | return InvalidHandle(ErrorModule::FS); | ||
| 257 | else | ||
| 258 | return RESULT_SUCCESS; | ||
| 259 | } | ||
| 260 | |||
| 261 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in | ||
| 262 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 | ||
| 263 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { | ||
| 264 | auto result = id_code_map.emplace(id_code, Common::make_unique<Archive>(std::move(backend), id_code)); | ||
| 265 | |||
| 266 | bool inserted = result.second; | ||
| 267 | _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); | ||
| 268 | |||
| 269 | auto& archive = result.first->second; | ||
| 270 | LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code); | ||
| 271 | return RESULT_SUCCESS; | ||
| 272 | } | ||
| 273 | |||
| 274 | ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { | ||
| 275 | Archive* archive = GetArchive(archive_handle); | ||
| 276 | if (archive == nullptr) | ||
| 277 | return InvalidHandle(ErrorModule::FS); | ||
| 278 | |||
| 279 | std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); | ||
| 280 | if (backend == nullptr) { | ||
| 281 | return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||
| 282 | ErrorSummary::NotFound, ErrorLevel::Status); | ||
| 283 | } | ||
| 284 | |||
| 285 | auto file = Common::make_unique<File>(std::move(backend), path); | ||
| 286 | // TOOD(yuriks): Fix error reporting | ||
| 287 | Handle handle = Kernel::g_handle_table.Create(file.release()).ValueOr(INVALID_HANDLE); | ||
| 288 | return MakeResult<Handle>(handle); | ||
| 289 | } | ||
| 290 | |||
| 291 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 292 | Archive* archive = GetArchive(archive_handle); | ||
| 293 | if (archive == nullptr) | ||
| 294 | return InvalidHandle(ErrorModule::FS); | ||
| 295 | |||
| 296 | if (archive->backend->DeleteFile(path)) | ||
| 297 | return RESULT_SUCCESS; | ||
| 298 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 299 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 300 | } | ||
| 301 | |||
| 302 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 303 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 304 | Archive* src_archive = GetArchive(src_archive_handle); | ||
| 305 | Archive* dest_archive = GetArchive(dest_archive_handle); | ||
| 306 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 307 | return InvalidHandle(ErrorModule::FS); | ||
| 308 | |||
| 309 | if (src_archive == dest_archive) { | ||
| 310 | if (src_archive->backend->RenameFile(src_path, dest_path)) | ||
| 311 | return RESULT_SUCCESS; | ||
| 312 | } else { | ||
| 313 | // TODO: Implement renaming across archives | ||
| 314 | return UnimplementedFunction(ErrorModule::FS); | ||
| 315 | } | ||
| 316 | |||
| 317 | // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't | ||
| 318 | // exist or similar. Verify. | ||
| 319 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 320 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 321 | } | ||
| 322 | |||
| 323 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 324 | Archive* archive = GetArchive(archive_handle); | ||
| 325 | if (archive == nullptr) | ||
| 326 | return InvalidHandle(ErrorModule::FS); | ||
| 327 | |||
| 328 | if (archive->backend->DeleteDirectory(path)) | ||
| 329 | return RESULT_SUCCESS; | ||
| 330 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 331 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 332 | } | ||
| 333 | |||
| 334 | ResultCode CreateFileInArchive(Handle archive_handle, const FileSys::Path& path, u32 file_size) { | ||
| 335 | Archive* archive = GetArchive(archive_handle); | ||
| 336 | if (archive == nullptr) | ||
| 337 | return InvalidHandle(ErrorModule::FS); | ||
| 338 | |||
| 339 | return archive->backend->CreateFile(path, file_size); | ||
| 340 | } | ||
| 341 | |||
| 342 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 343 | Archive* archive = GetArchive(archive_handle); | ||
| 344 | if (archive == nullptr) | ||
| 345 | return InvalidHandle(ErrorModule::FS); | ||
| 346 | |||
| 347 | if (archive->backend->CreateDirectory(path)) | ||
| 348 | return RESULT_SUCCESS; | ||
| 349 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 350 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 351 | } | ||
| 352 | |||
| 353 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 354 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | ||
| 355 | Archive* src_archive = GetArchive(src_archive_handle); | ||
| 356 | Archive* dest_archive = GetArchive(dest_archive_handle); | ||
| 357 | if (src_archive == nullptr || dest_archive == nullptr) | ||
| 358 | return InvalidHandle(ErrorModule::FS); | ||
| 359 | |||
| 360 | if (src_archive == dest_archive) { | ||
| 361 | if (src_archive->backend->RenameDirectory(src_path, dest_path)) | ||
| 362 | return RESULT_SUCCESS; | ||
| 363 | } else { | ||
| 364 | // TODO: Implement renaming across archives | ||
| 365 | return UnimplementedFunction(ErrorModule::FS); | ||
| 366 | } | ||
| 367 | |||
| 368 | // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't | ||
| 369 | // exist or similar. Verify. | ||
| 370 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | ||
| 371 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 372 | } | ||
| 373 | |||
| 374 | /** | ||
| 375 | * Open a Directory from an Archive | ||
| 376 | * @param archive_handle Handle to an open Archive object | ||
| 377 | * @param path Path to the Directory inside of the Archive | ||
| 378 | * @return Opened Directory object | ||
| 379 | */ | ||
| 380 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||
| 381 | Archive* archive = GetArchive(archive_handle); | ||
| 382 | if (archive == nullptr) | ||
| 383 | return InvalidHandle(ErrorModule::FS); | ||
| 384 | |||
| 385 | std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path); | ||
| 386 | if (backend == nullptr) { | ||
| 387 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 388 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 389 | } | ||
| 390 | |||
| 391 | auto directory = Common::make_unique<Directory>(std::move(backend), path); | ||
| 392 | // TOOD(yuriks): Fix error reporting | ||
| 393 | Handle handle = Kernel::g_handle_table.Create(directory.release()).ValueOr(INVALID_HANDLE); | ||
| 394 | return MakeResult<Handle>(handle); | ||
| 395 | } | ||
| 396 | |||
| 397 | ResultCode FormatSaveData() { | ||
| 398 | // TODO(Subv): Actually wipe the savedata folder after creating or opening it | ||
| 399 | |||
| 400 | // Do not create the archive again if it already exists | ||
| 401 | if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end()) | ||
| 402 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code | ||
| 403 | |||
| 404 | // Create the SaveData archive | ||
| 405 | std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX); | ||
| 406 | auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory, | ||
| 407 | Kernel::g_program_id); | ||
| 408 | |||
| 409 | if (savedata_archive->Initialize()) { | ||
| 410 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | ||
| 411 | return RESULT_SUCCESS; | ||
| 412 | } else { | ||
| 413 | LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s", | ||
| 414 | savedata_archive->GetMountPoint().c_str()); | ||
| 415 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Initialize archives | ||
| 420 | void ArchiveInit() { | ||
| 421 | next_handle = 1; | ||
| 422 | |||
| 423 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 424 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 425 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 426 | |||
| 427 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 428 | auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | ||
| 429 | if (sdmc_archive->Initialize()) | ||
| 430 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | ||
| 431 | else | ||
| 432 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 433 | } | ||
| 434 | |||
| 435 | /// Shutdown archives | ||
| 436 | void ArchiveShutdown() { | ||
| 437 | handle_map.clear(); | ||
| 438 | id_code_map.clear(); | ||
| 439 | } | ||
| 440 | |||
| 441 | } // namespace FS | ||
| 442 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h new file mode 100644 index 000000000..b39bc41b6 --- /dev/null +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -0,0 +1,134 @@ | |||
| 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/archive_backend.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace Service { | ||
| 14 | namespace FS { | ||
| 15 | |||
| 16 | /// Supported archive types | ||
| 17 | enum class ArchiveIdCode : u32 { | ||
| 18 | RomFS = 0x00000003, | ||
| 19 | SaveData = 0x00000004, | ||
| 20 | ExtSaveData = 0x00000006, | ||
| 21 | SharedExtSaveData = 0x00000007, | ||
| 22 | SystemSaveData = 0x00000008, | ||
| 23 | SDMC = 0x00000009, | ||
| 24 | SDMCWriteOnly = 0x0000000A, | ||
| 25 | }; | ||
| 26 | |||
| 27 | typedef u64 ArchiveHandle; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Opens an archive | ||
| 31 | * @param id_code IdCode of the archive to open | ||
| 32 | * @return Handle to the opened archive | ||
| 33 | */ | ||
| 34 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code); | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Closes an archive | ||
| 38 | * @param id_code IdCode of the archive to open | ||
| 39 | */ | ||
| 40 | ResultCode CloseArchive(ArchiveHandle handle); | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Creates an Archive | ||
| 44 | * @param backend File system backend interface to the archive | ||
| 45 | * @param id_code Id code used to access this type of archive | ||
| 46 | */ | ||
| 47 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Open a File from an Archive | ||
| 51 | * @param archive_handle Handle to an open Archive object | ||
| 52 | * @param path Path to the File inside of the Archive | ||
| 53 | * @param mode Mode under which to open the File | ||
| 54 | * @return Handle to the opened File object | ||
| 55 | */ | ||
| 56 | ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Delete a File from an Archive | ||
| 60 | * @param archive_handle Handle to an open Archive object | ||
| 61 | * @param path Path to the File inside of the Archive | ||
| 62 | * @return Whether deletion succeeded | ||
| 63 | */ | ||
| 64 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Rename a File between two Archives | ||
| 68 | * @param src_archive_handle Handle to the source Archive object | ||
| 69 | * @param src_path Path to the File inside of the source Archive | ||
| 70 | * @param dest_archive_handle Handle to the destination Archive object | ||
| 71 | * @param dest_path Path to the File inside of the destination Archive | ||
| 72 | * @return Whether rename succeeded | ||
| 73 | */ | ||
| 74 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 75 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Delete a Directory from an Archive | ||
| 79 | * @param archive_handle Handle to an open Archive object | ||
| 80 | * @param path Path to the Directory inside of the Archive | ||
| 81 | * @return Whether deletion succeeded | ||
| 82 | */ | ||
| 83 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Create a File in an Archive | ||
| 87 | * @param archive_handle Handle to an open Archive object | ||
| 88 | * @param path Path to the File inside of the Archive | ||
| 89 | * @param file_size The size of the new file, filled with zeroes | ||
| 90 | * @return File creation result code | ||
| 91 | */ | ||
| 92 | ResultCode CreateFileInArchive(Handle archive_handle, const FileSys::Path& path, u32 file_size); | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Create a Directory from an Archive | ||
| 96 | * @param archive_handle Handle to an open Archive object | ||
| 97 | * @param path Path to the Directory inside of the Archive | ||
| 98 | * @return Whether creation of directory succeeded | ||
| 99 | */ | ||
| 100 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Rename a Directory between two Archives | ||
| 104 | * @param src_archive_handle Handle to the source Archive object | ||
| 105 | * @param src_path Path to the Directory inside of the source Archive | ||
| 106 | * @param dest_archive_handle Handle to the destination Archive object | ||
| 107 | * @param dest_path Path to the Directory inside of the destination Archive | ||
| 108 | * @return Whether rename succeeded | ||
| 109 | */ | ||
| 110 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||
| 111 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Open a Directory from an Archive | ||
| 115 | * @param archive_handle Handle to an open Archive object | ||
| 116 | * @param path Path to the Directory inside of the Archive | ||
| 117 | * @return Handle to the opened File object | ||
| 118 | */ | ||
| 119 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Creates a blank SaveData archive. | ||
| 123 | * @return ResultCode 0 on success or the corresponding code on error | ||
| 124 | */ | ||
| 125 | ResultCode FormatSaveData(); | ||
| 126 | |||
| 127 | /// Initialize archives | ||
| 128 | void ArchiveInit(); | ||
| 129 | |||
| 130 | /// Shutdown archives | ||
| 131 | void ArchiveShutdown(); | ||
| 132 | |||
| 133 | } // namespace FS | ||
| 134 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp new file mode 100644 index 000000000..b1a465274 --- /dev/null +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -0,0 +1,601 @@ | |||
| 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/common.h" | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/scope_exit.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/fs/archive.h" | ||
| 11 | #include "core/hle/service/fs/fs_user.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // Namespace FS_User | ||
| 16 | |||
| 17 | namespace Service { | ||
| 18 | namespace FS { | ||
| 19 | |||
| 20 | static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) { | ||
| 21 | return (u64)low_word | ((u64)high_word << 32); | ||
| 22 | } | ||
| 23 | |||
| 24 | static void Initialize(Service::Interface* self) { | ||
| 25 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 26 | |||
| 27 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per | ||
| 28 | // http://3dbrew.org/wiki/FS:Initialize#Request | ||
| 29 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 30 | |||
| 31 | LOG_DEBUG(Service_FS, "called"); | ||
| 32 | } | ||
| 33 | |||
| 34 | /** | ||
| 35 | * FS_User::OpenFile service function | ||
| 36 | * Inputs: | ||
| 37 | * 1 : Transaction | ||
| 38 | * 2 : Archive handle lower word | ||
| 39 | * 3 : Archive handle upper word | ||
| 40 | * 4 : Low path type | ||
| 41 | * 5 : Low path size | ||
| 42 | * 6 : Open flags | ||
| 43 | * 7 : Attributes | ||
| 44 | * 8 : (LowPathSize << 14) | 2 | ||
| 45 | * 9 : Low path data pointer | ||
| 46 | * Outputs: | ||
| 47 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 48 | * 3 : File handle | ||
| 49 | */ | ||
| 50 | static void OpenFile(Service::Interface* self) { | ||
| 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 52 | |||
| 53 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 54 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 55 | u32 filename_size = cmd_buff[5]; | ||
| 56 | FileSys::Mode mode; mode.hex = cmd_buff[6]; | ||
| 57 | u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes. | ||
| 58 | u32 filename_ptr = cmd_buff[9]; | ||
| 59 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 60 | |||
| 61 | LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); | ||
| 62 | |||
| 63 | ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode); | ||
| 64 | cmd_buff[1] = handle.Code().raw; | ||
| 65 | if (handle.Succeeded()) { | ||
| 66 | cmd_buff[3] = *handle; | ||
| 67 | } else { | ||
| 68 | cmd_buff[3] = 0; | ||
| 69 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * FS_User::OpenFileDirectly service function | ||
| 75 | * Inputs: | ||
| 76 | * 1 : Transaction | ||
| 77 | * 2 : Archive ID | ||
| 78 | * 3 : Archive low path type | ||
| 79 | * 4 : Archive low path size | ||
| 80 | * 5 : File low path type | ||
| 81 | * 6 : File low path size | ||
| 82 | * 7 : Flags | ||
| 83 | * 8 : Attributes | ||
| 84 | * 9 : (ArchiveLowPathSize << 14) | 0x802 | ||
| 85 | * 10 : Archive low path | ||
| 86 | * 11 : (FileLowPathSize << 14) | 2 | ||
| 87 | * 12 : File low path | ||
| 88 | * Outputs: | ||
| 89 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 90 | * 3 : File handle | ||
| 91 | */ | ||
| 92 | static void OpenFileDirectly(Service::Interface* self) { | ||
| 93 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 94 | |||
| 95 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]); | ||
| 96 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||
| 97 | u32 archivename_size = cmd_buff[4]; | ||
| 98 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); | ||
| 99 | u32 filename_size = cmd_buff[6]; | ||
| 100 | FileSys::Mode mode; mode.hex = cmd_buff[7]; | ||
| 101 | u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes. | ||
| 102 | u32 archivename_ptr = cmd_buff[10]; | ||
| 103 | u32 filename_ptr = cmd_buff[12]; | ||
| 104 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 105 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 106 | |||
| 107 | LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d", | ||
| 108 | archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes); | ||
| 109 | |||
| 110 | if (archive_path.GetType() != FileSys::Empty) { | ||
| 111 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 112 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 113 | cmd_buff[3] = 0; | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id); | ||
| 118 | if (archive_handle.Failed()) { | ||
| 119 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | ||
| 120 | cmd_buff[1] = archive_handle.Code().raw; | ||
| 121 | cmd_buff[3] = 0; | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | SCOPE_EXIT({ CloseArchive(*archive_handle); }); | ||
| 125 | |||
| 126 | ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode); | ||
| 127 | cmd_buff[1] = handle.Code().raw; | ||
| 128 | if (handle.Succeeded()) { | ||
| 129 | cmd_buff[3] = *handle; | ||
| 130 | } else { | ||
| 131 | cmd_buff[3] = 0; | ||
| 132 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | /* | ||
| 137 | * FS_User::DeleteFile service function | ||
| 138 | * Inputs: | ||
| 139 | * 2 : Archive handle lower word | ||
| 140 | * 3 : Archive handle upper word | ||
| 141 | * 4 : File path string type | ||
| 142 | * 5 : File path string size | ||
| 143 | * 7 : File path string data | ||
| 144 | * Outputs: | ||
| 145 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 146 | */ | ||
| 147 | static void DeleteFile(Service::Interface* self) { | ||
| 148 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 149 | |||
| 150 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 151 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 152 | u32 filename_size = cmd_buff[5]; | ||
| 153 | u32 filename_ptr = cmd_buff[7]; | ||
| 154 | |||
| 155 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 156 | |||
| 157 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", | ||
| 158 | filename_type, filename_size, file_path.DebugStr().c_str()); | ||
| 159 | |||
| 160 | cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw; | ||
| 161 | } | ||
| 162 | |||
| 163 | /* | ||
| 164 | * FS_User::RenameFile service function | ||
| 165 | * Inputs: | ||
| 166 | * 2 : Source archive handle lower word | ||
| 167 | * 3 : Source archive handle upper word | ||
| 168 | * 4 : Source file path type | ||
| 169 | * 5 : Source file path size | ||
| 170 | * 6 : Dest archive handle lower word | ||
| 171 | * 7 : Dest archive handle upper word | ||
| 172 | * 8 : Dest file path type | ||
| 173 | * 9 : Dest file path size | ||
| 174 | * 11: Source file path string data | ||
| 175 | * 13: Dest file path string | ||
| 176 | * Outputs: | ||
| 177 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 178 | */ | ||
| 179 | static void RenameFile(Service::Interface* self) { | ||
| 180 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 181 | |||
| 182 | ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 183 | auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 184 | u32 src_filename_size = cmd_buff[5]; | ||
| 185 | ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);; | ||
| 186 | auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | ||
| 187 | u32 dest_filename_size = cmd_buff[9]; | ||
| 188 | u32 src_filename_ptr = cmd_buff[11]; | ||
| 189 | u32 dest_filename_ptr = cmd_buff[13]; | ||
| 190 | |||
| 191 | FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr); | ||
| 192 | FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr); | ||
| 193 | |||
| 194 | LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s", | ||
| 195 | src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(), | ||
| 196 | dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str()); | ||
| 197 | |||
| 198 | cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* | ||
| 202 | * FS_User::DeleteDirectory service function | ||
| 203 | * Inputs: | ||
| 204 | * 2 : Archive handle lower word | ||
| 205 | * 3 : Archive handle upper word | ||
| 206 | * 4 : Directory path string type | ||
| 207 | * 5 : Directory path string size | ||
| 208 | * 7 : Directory path string data | ||
| 209 | * Outputs: | ||
| 210 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 211 | */ | ||
| 212 | static void DeleteDirectory(Service::Interface* self) { | ||
| 213 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 214 | |||
| 215 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 216 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 217 | u32 dirname_size = cmd_buff[5]; | ||
| 218 | u32 dirname_ptr = cmd_buff[7]; | ||
| 219 | |||
| 220 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 221 | |||
| 222 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", | ||
| 223 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 224 | |||
| 225 | cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* | ||
| 229 | * FS_User::CreateFile service function | ||
| 230 | * Inputs: | ||
| 231 | * 0 : Command header 0x08080202 | ||
| 232 | * 2 : Archive handle lower word | ||
| 233 | * 3 : Archive handle upper word | ||
| 234 | * 4 : File path string type | ||
| 235 | * 5 : File path string size | ||
| 236 | * 7 : File size (filled with zeroes) | ||
| 237 | * 10: File path string data | ||
| 238 | * Outputs: | ||
| 239 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 240 | */ | ||
| 241 | static void CreateFile(Service::Interface* self) { | ||
| 242 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 243 | |||
| 244 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 245 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 246 | u32 filename_size = cmd_buff[5]; | ||
| 247 | u32 file_size = cmd_buff[7]; | ||
| 248 | u32 filename_ptr = cmd_buff[10]; | ||
| 249 | |||
| 250 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 251 | |||
| 252 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); | ||
| 253 | |||
| 254 | cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* | ||
| 258 | * FS_User::CreateDirectory service function | ||
| 259 | * Inputs: | ||
| 260 | * 2 : Archive handle lower word | ||
| 261 | * 3 : Archive handle upper word | ||
| 262 | * 4 : Directory path string type | ||
| 263 | * 5 : Directory path string size | ||
| 264 | * 8 : Directory path string data | ||
| 265 | * Outputs: | ||
| 266 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 267 | */ | ||
| 268 | static void CreateDirectory(Service::Interface* self) { | ||
| 269 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 270 | |||
| 271 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 272 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 273 | u32 dirname_size = cmd_buff[5]; | ||
| 274 | u32 dirname_ptr = cmd_buff[8]; | ||
| 275 | |||
| 276 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 277 | |||
| 278 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 279 | |||
| 280 | cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* | ||
| 284 | * FS_User::RenameDirectory service function | ||
| 285 | * Inputs: | ||
| 286 | * 2 : Source archive handle lower word | ||
| 287 | * 3 : Source archive handle upper word | ||
| 288 | * 4 : Source dir path type | ||
| 289 | * 5 : Source dir path size | ||
| 290 | * 6 : Dest archive handle lower word | ||
| 291 | * 7 : Dest archive handle upper word | ||
| 292 | * 8 : Dest dir path type | ||
| 293 | * 9 : Dest dir path size | ||
| 294 | * 11: Source dir path string data | ||
| 295 | * 13: Dest dir path string | ||
| 296 | * Outputs: | ||
| 297 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 298 | */ | ||
| 299 | static void RenameDirectory(Service::Interface* self) { | ||
| 300 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 301 | |||
| 302 | ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||
| 303 | auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 304 | u32 src_dirname_size = cmd_buff[5]; | ||
| 305 | ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]); | ||
| 306 | auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); | ||
| 307 | u32 dest_dirname_size = cmd_buff[9]; | ||
| 308 | u32 src_dirname_ptr = cmd_buff[11]; | ||
| 309 | u32 dest_dirname_ptr = cmd_buff[13]; | ||
| 310 | |||
| 311 | FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr); | ||
| 312 | FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr); | ||
| 313 | |||
| 314 | LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s", | ||
| 315 | src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(), | ||
| 316 | dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str()); | ||
| 317 | |||
| 318 | cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; | ||
| 319 | } | ||
| 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 | */ | ||
| 334 | static void OpenDirectory(Service::Interface* self) { | ||
| 335 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 336 | |||
| 337 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); | ||
| 338 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||
| 339 | u32 dirname_size = cmd_buff[4]; | ||
| 340 | u32 dirname_ptr = cmd_buff[6]; | ||
| 341 | |||
| 342 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 343 | |||
| 344 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 345 | |||
| 346 | ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path); | ||
| 347 | cmd_buff[1] = handle.Code().raw; | ||
| 348 | if (handle.Succeeded()) { | ||
| 349 | cmd_buff[3] = *handle; | ||
| 350 | } else { | ||
| 351 | LOG_ERROR(Service_FS, "failed to get a handle for directory"); | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * FS_User::OpenArchive service function | ||
| 357 | * Inputs: | ||
| 358 | * 1 : Archive ID | ||
| 359 | * 2 : Archive low path type | ||
| 360 | * 3 : Archive low path size | ||
| 361 | * 4 : (LowPathSize << 14) | 2 | ||
| 362 | * 5 : Archive low path | ||
| 363 | * Outputs: | ||
| 364 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 365 | * 2 : Archive handle lower word (unused) | ||
| 366 | * 3 : Archive handle upper word (same as file handle) | ||
| 367 | */ | ||
| 368 | static void OpenArchive(Service::Interface* self) { | ||
| 369 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 370 | |||
| 371 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||
| 372 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||
| 373 | u32 archivename_size = cmd_buff[3]; | ||
| 374 | u32 archivename_ptr = cmd_buff[5]; | ||
| 375 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 376 | |||
| 377 | LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||
| 378 | |||
| 379 | if (archive_path.GetType() != FileSys::Empty) { | ||
| 380 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 381 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 382 | return; | ||
| 383 | } | ||
| 384 | |||
| 385 | ResultVal<ArchiveHandle> handle = OpenArchive(archive_id); | ||
| 386 | cmd_buff[1] = handle.Code().raw; | ||
| 387 | if (handle.Succeeded()) { | ||
| 388 | cmd_buff[2] = *handle & 0xFFFFFFFF; | ||
| 389 | cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; | ||
| 390 | } else { | ||
| 391 | cmd_buff[2] = cmd_buff[3] = 0; | ||
| 392 | LOG_ERROR(Service_FS, "failed to get a handle for archive"); | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | /** | ||
| 397 | * FS_User::CloseArchive service function | ||
| 398 | * Inputs: | ||
| 399 | * 0 : 0x080E0080 | ||
| 400 | * 1 : Archive handle low word | ||
| 401 | * 2 : Archive handle high word | ||
| 402 | * Outputs: | ||
| 403 | * 0 : ??? TODO(yuriks): Verify return header | ||
| 404 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 405 | */ | ||
| 406 | static void CloseArchive(Service::Interface* self) { | ||
| 407 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 408 | |||
| 409 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); | ||
| 410 | cmd_buff[1] = CloseArchive(archive_handle).raw; | ||
| 411 | } | ||
| 412 | |||
| 413 | /* | ||
| 414 | * FS_User::IsSdmcDetected service function | ||
| 415 | * Outputs: | ||
| 416 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 417 | * 2 : Whether the Sdmc could be detected | ||
| 418 | */ | ||
| 419 | static void IsSdmcDetected(Service::Interface* self) { | ||
| 420 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 421 | |||
| 422 | cmd_buff[1] = 0; | ||
| 423 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | ||
| 424 | |||
| 425 | LOG_DEBUG(Service_FS, "called"); | ||
| 426 | } | ||
| 427 | |||
| 428 | /** | ||
| 429 | * FS_User::IsSdmcWriteable service function | ||
| 430 | * Outputs: | ||
| 431 | * 0 : Command header 0x08180000 | ||
| 432 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 433 | * 2 : Whether the Sdmc is currently writeable | ||
| 434 | */ | ||
| 435 | static void IsSdmcWriteable(Service::Interface* self) { | ||
| 436 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 437 | |||
| 438 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 439 | // If the SD isn't enabled, it can't be writeable...else, stubbed true | ||
| 440 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | ||
| 441 | |||
| 442 | LOG_DEBUG(Service_FS, " (STUBBED)"); | ||
| 443 | } | ||
| 444 | |||
| 445 | /** | ||
| 446 | * FS_User::FormatSaveData service function, | ||
| 447 | * formats the SaveData specified by the input path. | ||
| 448 | * Inputs: | ||
| 449 | * 0 : 0x084C0242 | ||
| 450 | * 1 : Archive ID | ||
| 451 | * 2 : Archive low path type | ||
| 452 | * 3 : Archive low path size | ||
| 453 | * 10 : (LowPathSize << 14) | 2 | ||
| 454 | * 11 : Archive low path | ||
| 455 | * Outputs: | ||
| 456 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 457 | */ | ||
| 458 | static void FormatSaveData(Service::Interface* self) { | ||
| 459 | // TODO(Subv): Find out what the other inputs and outputs of this function are | ||
| 460 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 461 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 462 | |||
| 463 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||
| 464 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||
| 465 | u32 archivename_size = cmd_buff[3]; | ||
| 466 | u32 archivename_ptr = cmd_buff[11]; | ||
| 467 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 468 | |||
| 469 | LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||
| 470 | |||
| 471 | if (archive_id != FS::ArchiveIdCode::SaveData) { | ||
| 472 | // TODO(Subv): What should happen if somebody attempts to format a different archive? | ||
| 473 | LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]); | ||
| 474 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 475 | return; | ||
| 476 | } | ||
| 477 | |||
| 478 | if (archive_path.GetType() != FileSys::LowPathType::Empty) { | ||
| 479 | // TODO(Subv): Implement formatting the SaveData of other games | ||
| 480 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 481 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 482 | return; | ||
| 483 | } | ||
| 484 | |||
| 485 | cmd_buff[1] = FormatSaveData().raw; | ||
| 486 | } | ||
| 487 | |||
| 488 | /** | ||
| 489 | * FS_User::FormatThisUserSaveData service function | ||
| 490 | * Inputs: | ||
| 491 | * 0: 0x080F0180 | ||
| 492 | * Outputs: | ||
| 493 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 494 | */ | ||
| 495 | static void FormatThisUserSaveData(Service::Interface* self) { | ||
| 496 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 497 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 498 | |||
| 499 | // TODO(Subv): Find out what the inputs and outputs of this function are | ||
| 500 | |||
| 501 | cmd_buff[1] = FormatSaveData().raw; | ||
| 502 | } | ||
| 503 | |||
| 504 | const FSUserInterface::FunctionInfo FunctionTable[] = { | ||
| 505 | {0x000100C6, nullptr, "Dummy1"}, | ||
| 506 | {0x040100C4, nullptr, "Control"}, | ||
| 507 | {0x08010002, Initialize, "Initialize"}, | ||
| 508 | {0x080201C2, OpenFile, "OpenFile"}, | ||
| 509 | {0x08030204, OpenFileDirectly, "OpenFileDirectly"}, | ||
| 510 | {0x08040142, DeleteFile, "DeleteFile"}, | ||
| 511 | {0x08050244, RenameFile, "RenameFile"}, | ||
| 512 | {0x08060142, DeleteDirectory, "DeleteDirectory"}, | ||
| 513 | {0x08070142, nullptr, "DeleteDirectoryRecursively"}, | ||
| 514 | {0x08080202, CreateFile, "CreateFile"}, | ||
| 515 | {0x08090182, CreateDirectory, "CreateDirectory"}, | ||
| 516 | {0x080A0244, RenameDirectory, "RenameDirectory"}, | ||
| 517 | {0x080B0102, OpenDirectory, "OpenDirectory"}, | ||
| 518 | {0x080C00C2, OpenArchive, "OpenArchive"}, | ||
| 519 | {0x080D0144, nullptr, "ControlArchive"}, | ||
| 520 | {0x080E0080, CloseArchive, "CloseArchive"}, | ||
| 521 | {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, | ||
| 522 | {0x08100200, nullptr, "CreateSystemSaveData"}, | ||
| 523 | {0x08110040, nullptr, "DeleteSystemSaveData"}, | ||
| 524 | {0x08120080, nullptr, "GetFreeBytes"}, | ||
| 525 | {0x08130000, nullptr, "GetCardType"}, | ||
| 526 | {0x08140000, nullptr, "GetSdmcArchiveResource"}, | ||
| 527 | {0x08150000, nullptr, "GetNandArchiveResource"}, | ||
| 528 | {0x08160000, nullptr, "GetSdmcFatfsErro"}, | ||
| 529 | {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, | ||
| 530 | {0x08180000, IsSdmcWriteable, "IsSdmcWritable"}, | ||
| 531 | {0x08190042, nullptr, "GetSdmcCid"}, | ||
| 532 | {0x081A0042, nullptr, "GetNandCid"}, | ||
| 533 | {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, | ||
| 534 | {0x081C0000, nullptr, "GetNandSpeedInfo"}, | ||
| 535 | {0x081D0042, nullptr, "GetSdmcLog"}, | ||
| 536 | {0x081E0042, nullptr, "GetNandLog"}, | ||
| 537 | {0x081F0000, nullptr, "ClearSdmcLog"}, | ||
| 538 | {0x08200000, nullptr, "ClearNandLog"}, | ||
| 539 | {0x08210000, nullptr, "CardSlotIsInserted"}, | ||
| 540 | {0x08220000, nullptr, "CardSlotPowerOn"}, | ||
| 541 | {0x08230000, nullptr, "CardSlotPowerOff"}, | ||
| 542 | {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, | ||
| 543 | {0x08250040, nullptr, "CardNorDirectCommand"}, | ||
| 544 | {0x08260080, nullptr, "CardNorDirectCommandWithAddress"}, | ||
| 545 | {0x08270082, nullptr, "CardNorDirectRead"}, | ||
| 546 | {0x082800C2, nullptr, "CardNorDirectReadWithAddress"}, | ||
| 547 | {0x08290082, nullptr, "CardNorDirectWrite"}, | ||
| 548 | {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"}, | ||
| 549 | {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"}, | ||
| 550 | {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, | ||
| 551 | {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, | ||
| 552 | {0x082E0040, nullptr, "GetProductInfo"}, | ||
| 553 | {0x082F0040, nullptr, "GetProgramLaunchInfo"}, | ||
| 554 | {0x08300182, nullptr, "CreateExtSaveData"}, | ||
| 555 | {0x08310180, nullptr, "CreateSharedExtSaveData"}, | ||
| 556 | {0x08320102, nullptr, "ReadExtSaveDataIcon"}, | ||
| 557 | {0x08330082, nullptr, "EnumerateExtSaveData"}, | ||
| 558 | {0x08340082, nullptr, "EnumerateSharedExtSaveData"}, | ||
| 559 | {0x08350080, nullptr, "DeleteExtSaveData"}, | ||
| 560 | {0x08360080, nullptr, "DeleteSharedExtSaveData"}, | ||
| 561 | {0x08370040, nullptr, "SetCardSpiBaudRate"}, | ||
| 562 | {0x08380040, nullptr, "SetCardSpiBusMode"}, | ||
| 563 | {0x08390000, nullptr, "SendInitializeInfoTo9"}, | ||
| 564 | {0x083A0100, nullptr, "GetSpecialContentIndex"}, | ||
| 565 | {0x083B00C2, nullptr, "GetLegacyRomHeader"}, | ||
| 566 | {0x083C00C2, nullptr, "GetLegacyBannerData"}, | ||
| 567 | {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"}, | ||
| 568 | {0x083E00C2, nullptr, "QueryTotalQuotaSize"}, | ||
| 569 | {0x083F00C0, nullptr, "GetExtDataBlockSize"}, | ||
| 570 | {0x08400040, nullptr, "AbnegateAccessRight"}, | ||
| 571 | {0x08410000, nullptr, "DeleteSdmcRoot"}, | ||
| 572 | {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, | ||
| 573 | {0x08430000, nullptr, "InitializeCtrFileSystem"}, | ||
| 574 | {0x08440000, nullptr, "CreateSeed"}, | ||
| 575 | {0x084500C2, nullptr, "GetFormatInfo"}, | ||
| 576 | {0x08460102, nullptr, "GetLegacyRomHeader2"}, | ||
| 577 | {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, | ||
| 578 | {0x08480042, nullptr, "GetSdmcCtrRootPath"}, | ||
| 579 | {0x08490040, nullptr, "GetArchiveResource"}, | ||
| 580 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, | ||
| 581 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, | ||
| 582 | {0x084C0242, FormatSaveData, "FormatSaveData"}, | ||
| 583 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, | ||
| 584 | {0x084E0342, nullptr, "UpdateSha256Context"}, | ||
| 585 | {0x084F0102, nullptr, "ReadSpecialFile"}, | ||
| 586 | {0x08500040, nullptr, "GetSpecialFileSize"}, | ||
| 587 | {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, | ||
| 588 | {0x08610042, nullptr, "InitializeWithSdkVersion"}, | ||
| 589 | {0x08620040, nullptr, "SetPriority"}, | ||
| 590 | {0x08630000, nullptr, "GetPriority"}, | ||
| 591 | }; | ||
| 592 | |||
| 593 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 594 | // Interface class | ||
| 595 | |||
| 596 | FSUserInterface::FSUserInterface() { | ||
| 597 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 598 | } | ||
| 599 | |||
| 600 | } // namespace FS | ||
| 601 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/fs/fs_user.h index 005382540..2d896dd5f 100644 --- a/src/core/hle/service/fs_user.h +++ b/src/core/hle/service/fs/fs_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 |
| @@ -9,23 +9,18 @@ | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // Namespace FS_User | 10 | // Namespace FS_User |
| 11 | 11 | ||
| 12 | namespace FS_User { | 12 | namespace Service { |
| 13 | namespace FS { | ||
| 13 | 14 | ||
| 14 | /// Interface to "fs:USER" service | 15 | /// Interface to "fs:USER" service |
| 15 | class Interface : public Service::Interface { | 16 | class FSUserInterface : public Service::Interface { |
| 16 | public: | 17 | public: |
| 18 | FSUserInterface(); | ||
| 17 | 19 | ||
| 18 | Interface(); | ||
| 19 | |||
| 20 | ~Interface(); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Gets the string port name used by CTROS for the service | ||
| 24 | * @return Port name of service | ||
| 25 | */ | ||
| 26 | std::string GetPortName() const override { | 20 | std::string GetPortName() const override { |
| 27 | return "fs:USER"; | 21 | return "fs:USER"; |
| 28 | } | 22 | } |
| 29 | }; | 23 | }; |
| 30 | 24 | ||
| 31 | } // namespace | 25 | } // namespace FS |
| 26 | } // namespace Service | ||
diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs_user.cpp deleted file mode 100644 index 435be5b5d..000000000 --- a/src/core/hle/service/fs_user.cpp +++ /dev/null | |||
| @@ -1,409 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common.h" | ||
| 6 | |||
| 7 | #include "common/string_util.h" | ||
| 8 | #include "core/hle/kernel/archive.h" | ||
| 9 | #include "core/hle/kernel/archive.h" | ||
| 10 | #include "core/hle/result.h" | ||
| 11 | #include "core/hle/service/fs_user.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // Namespace FS_User | ||
| 16 | |||
| 17 | namespace FS_User { | ||
| 18 | |||
| 19 | static void Initialize(Service::Interface* self) { | ||
| 20 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 21 | |||
| 22 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per | ||
| 23 | // http://3dbrew.org/wiki/FS:Initialize#Request | ||
| 24 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 25 | |||
| 26 | DEBUG_LOG(KERNEL, "called"); | ||
| 27 | } | ||
| 28 | |||
| 29 | /** | ||
| 30 | * FS_User::OpenFile service function | ||
| 31 | * Inputs: | ||
| 32 | * 1 : Transaction | ||
| 33 | * 2 : Archive handle lower word | ||
| 34 | * 3 : Archive handle upper word | ||
| 35 | * 4 : Low path type | ||
| 36 | * 5 : Low path size | ||
| 37 | * 6 : Open flags | ||
| 38 | * 7 : Attributes | ||
| 39 | * 8 : (LowPathSize << 14) | 2 | ||
| 40 | * 9 : Low path data pointer | ||
| 41 | * Outputs: | ||
| 42 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 43 | * 3 : File handle | ||
| 44 | */ | ||
| 45 | static void OpenFile(Service::Interface* self) { | ||
| 46 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 47 | |||
| 48 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 49 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 50 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 51 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 52 | u32 filename_size = cmd_buff[5]; | ||
| 53 | FileSys::Mode mode; mode.hex = cmd_buff[6]; | ||
| 54 | u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes. | ||
| 55 | u32 filename_ptr = cmd_buff[9]; | ||
| 56 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 57 | |||
| 58 | DEBUG_LOG(KERNEL, "path=%s, mode=%d attrs=%d", file_path.DebugStr().c_str(), mode, attributes); | ||
| 59 | |||
| 60 | ResultVal<Handle> handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode); | ||
| 61 | cmd_buff[1] = handle.Code().raw; | ||
| 62 | if (handle.Succeeded()) { | ||
| 63 | cmd_buff[3] = *handle; | ||
| 64 | } else { | ||
| 65 | ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||
| 66 | } | ||
| 67 | |||
| 68 | DEBUG_LOG(KERNEL, "called"); | ||
| 69 | } | ||
| 70 | |||
| 71 | /** | ||
| 72 | * FS_User::OpenFileDirectly service function | ||
| 73 | * Inputs: | ||
| 74 | * 1 : Transaction | ||
| 75 | * 2 : Archive ID | ||
| 76 | * 3 : Archive low path type | ||
| 77 | * 4 : Archive low path size | ||
| 78 | * 5 : File low path type | ||
| 79 | * 6 : File low path size | ||
| 80 | * 7 : Flags | ||
| 81 | * 8 : Attributes | ||
| 82 | * 9 : (ArchiveLowPathSize << 14) | 0x802 | ||
| 83 | * 10 : Archive low path | ||
| 84 | * 11 : (FileLowPathSize << 14) | 2 | ||
| 85 | * 12 : File low path | ||
| 86 | * Outputs: | ||
| 87 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 88 | * 3 : File handle | ||
| 89 | */ | ||
| 90 | static void OpenFileDirectly(Service::Interface* self) { | ||
| 91 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 92 | |||
| 93 | auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]); | ||
| 94 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||
| 95 | u32 archivename_size = cmd_buff[4]; | ||
| 96 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); | ||
| 97 | u32 filename_size = cmd_buff[6]; | ||
| 98 | FileSys::Mode mode; mode.hex = cmd_buff[7]; | ||
| 99 | u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes. | ||
| 100 | u32 archivename_ptr = cmd_buff[10]; | ||
| 101 | u32 filename_ptr = cmd_buff[12]; | ||
| 102 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 103 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 104 | |||
| 105 | DEBUG_LOG(KERNEL, "archive_path=%s file_path=%s, mode=%d attributes=%d", | ||
| 106 | archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode, attributes); | ||
| 107 | |||
| 108 | if (archive_path.GetType() != FileSys::Empty) { | ||
| 109 | ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported"); | ||
| 110 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | // TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it | ||
| 115 | // TODO(yuriks): Why is there all this duplicate (and seemingly useless) code up here? | ||
| 116 | ResultVal<Handle> archive_handle = Kernel::OpenArchive(archive_id); | ||
| 117 | cmd_buff[1] = archive_handle.Code().raw; | ||
| 118 | if (archive_handle.Failed()) { | ||
| 119 | ERROR_LOG(KERNEL, "failed to get a handle for archive"); | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | // cmd_buff[2] isn't used according to 3dmoo's implementation. | ||
| 123 | cmd_buff[3] = *archive_handle; | ||
| 124 | |||
| 125 | ResultVal<Handle> handle = Kernel::OpenFileFromArchive(*archive_handle, file_path, mode); | ||
| 126 | cmd_buff[1] = handle.Code().raw; | ||
| 127 | if (handle.Succeeded()) { | ||
| 128 | cmd_buff[3] = *handle; | ||
| 129 | } else { | ||
| 130 | ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||
| 131 | } | ||
| 132 | |||
| 133 | DEBUG_LOG(KERNEL, "called"); | ||
| 134 | } | ||
| 135 | |||
| 136 | /* | ||
| 137 | * FS_User::DeleteFile service function | ||
| 138 | * Inputs: | ||
| 139 | * 2 : Archive handle lower word | ||
| 140 | * 3 : Archive handle upper word | ||
| 141 | * 4 : File path string type | ||
| 142 | * 5 : File path string size | ||
| 143 | * 7 : File path string data | ||
| 144 | * Outputs: | ||
| 145 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 146 | */ | ||
| 147 | void DeleteFile(Service::Interface* self) { | ||
| 148 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 149 | |||
| 150 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 151 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 152 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 153 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 154 | u32 filename_size = cmd_buff[5]; | ||
| 155 | u32 filename_ptr = cmd_buff[7]; | ||
| 156 | |||
| 157 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||
| 158 | |||
| 159 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", | ||
| 160 | filename_type, filename_size, file_path.DebugStr().c_str()); | ||
| 161 | |||
| 162 | cmd_buff[1] = Kernel::DeleteFileFromArchive(archive_handle, file_path); | ||
| 163 | |||
| 164 | DEBUG_LOG(KERNEL, "called"); | ||
| 165 | } | ||
| 166 | |||
| 167 | /* | ||
| 168 | * FS_User::DeleteDirectory service function | ||
| 169 | * Inputs: | ||
| 170 | * 2 : Archive handle lower word | ||
| 171 | * 3 : Archive handle upper word | ||
| 172 | * 4 : Directory path string type | ||
| 173 | * 5 : Directory path string size | ||
| 174 | * 7 : Directory path string data | ||
| 175 | * Outputs: | ||
| 176 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 177 | */ | ||
| 178 | void DeleteDirectory(Service::Interface* self) { | ||
| 179 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 180 | |||
| 181 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 182 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 183 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 184 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 185 | u32 dirname_size = cmd_buff[5]; | ||
| 186 | u32 dirname_ptr = cmd_buff[7]; | ||
| 187 | |||
| 188 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 189 | |||
| 190 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", | ||
| 191 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 192 | |||
| 193 | cmd_buff[1] = Kernel::DeleteDirectoryFromArchive(archive_handle, dir_path); | ||
| 194 | |||
| 195 | DEBUG_LOG(KERNEL, "called"); | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | ||
| 199 | * FS_User::CreateDirectory service function | ||
| 200 | * Inputs: | ||
| 201 | * 2 : Archive handle lower word | ||
| 202 | * 3 : Archive handle upper word | ||
| 203 | * 4 : Directory path string type | ||
| 204 | * 5 : Directory path string size | ||
| 205 | * 8 : Directory path string data | ||
| 206 | * Outputs: | ||
| 207 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 208 | */ | ||
| 209 | static void CreateDirectory(Service::Interface* self) { | ||
| 210 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 211 | |||
| 212 | // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 213 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 214 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 215 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||
| 216 | u32 dirname_size = cmd_buff[5]; | ||
| 217 | u32 dirname_ptr = cmd_buff[8]; | ||
| 218 | |||
| 219 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 220 | |||
| 221 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 222 | |||
| 223 | cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_path); | ||
| 224 | |||
| 225 | DEBUG_LOG(KERNEL, "called"); | ||
| 226 | } | ||
| 227 | |||
| 228 | static void OpenDirectory(Service::Interface* self) { | ||
| 229 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 230 | |||
| 231 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | ||
| 232 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 233 | Handle archive_handle = static_cast<Handle>(cmd_buff[2]); | ||
| 234 | auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); | ||
| 235 | u32 dirname_size = cmd_buff[4]; | ||
| 236 | u32 dirname_ptr = cmd_buff[6]; | ||
| 237 | |||
| 238 | FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr); | ||
| 239 | |||
| 240 | DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||
| 241 | |||
| 242 | ResultVal<Handle> handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path); | ||
| 243 | cmd_buff[1] = handle.Code().raw; | ||
| 244 | if (handle.Succeeded()) { | ||
| 245 | cmd_buff[3] = *handle; | ||
| 246 | } else { | ||
| 247 | ERROR_LOG(KERNEL, "failed to get a handle for directory"); | ||
| 248 | } | ||
| 249 | |||
| 250 | DEBUG_LOG(KERNEL, "called"); | ||
| 251 | } | ||
| 252 | |||
| 253 | /** | ||
| 254 | * FS_User::OpenArchive service function | ||
| 255 | * Inputs: | ||
| 256 | * 1 : Archive ID | ||
| 257 | * 2 : Archive low path type | ||
| 258 | * 3 : Archive low path size | ||
| 259 | * 4 : (LowPathSize << 14) | 2 | ||
| 260 | * 5 : Archive low path | ||
| 261 | * Outputs: | ||
| 262 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 263 | * 2 : Archive handle lower word (unused) | ||
| 264 | * 3 : Archive handle upper word (same as file handle) | ||
| 265 | */ | ||
| 266 | static void OpenArchive(Service::Interface* self) { | ||
| 267 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 268 | |||
| 269 | auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]); | ||
| 270 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||
| 271 | u32 archivename_size = cmd_buff[3]; | ||
| 272 | u32 archivename_ptr = cmd_buff[5]; | ||
| 273 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 274 | |||
| 275 | DEBUG_LOG(KERNEL, "archive_path=%s", archive_path.DebugStr().c_str()); | ||
| 276 | |||
| 277 | if (archive_path.GetType() != FileSys::Empty) { | ||
| 278 | ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported"); | ||
| 279 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 280 | return; | ||
| 281 | } | ||
| 282 | |||
| 283 | ResultVal<Handle> handle = Kernel::OpenArchive(archive_id); | ||
| 284 | cmd_buff[1] = handle.Code().raw; | ||
| 285 | if (handle.Succeeded()) { | ||
| 286 | // cmd_buff[2] isn't used according to 3dmoo's implementation. | ||
| 287 | cmd_buff[3] = *handle; | ||
| 288 | } else { | ||
| 289 | ERROR_LOG(KERNEL, "failed to get a handle for archive"); | ||
| 290 | } | ||
| 291 | |||
| 292 | DEBUG_LOG(KERNEL, "called"); | ||
| 293 | } | ||
| 294 | |||
| 295 | /* | ||
| 296 | * FS_User::IsSdmcDetected service function | ||
| 297 | * Outputs: | ||
| 298 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 299 | * 2 : Whether the Sdmc could be detected | ||
| 300 | */ | ||
| 301 | static void IsSdmcDetected(Service::Interface* self) { | ||
| 302 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 303 | |||
| 304 | cmd_buff[1] = 0; | ||
| 305 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | ||
| 306 | |||
| 307 | DEBUG_LOG(KERNEL, "called"); | ||
| 308 | } | ||
| 309 | |||
| 310 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 311 | {0x000100C6, nullptr, "Dummy1"}, | ||
| 312 | {0x040100C4, nullptr, "Control"}, | ||
| 313 | {0x08010002, Initialize, "Initialize"}, | ||
| 314 | {0x080201C2, OpenFile, "OpenFile"}, | ||
| 315 | {0x08030204, OpenFileDirectly, "OpenFileDirectly"}, | ||
| 316 | {0x08040142, DeleteFile, "DeleteFile"}, | ||
| 317 | {0x08050244, nullptr, "RenameFile"}, | ||
| 318 | {0x08060142, DeleteDirectory, "DeleteDirectory"}, | ||
| 319 | {0x08070142, nullptr, "DeleteDirectoryRecursively"}, | ||
| 320 | {0x08080202, nullptr, "CreateFile"}, | ||
| 321 | {0x08090182, CreateDirectory, "CreateDirectory"}, | ||
| 322 | {0x080A0244, nullptr, "RenameDirectory"}, | ||
| 323 | {0x080B0102, OpenDirectory, "OpenDirectory"}, | ||
| 324 | {0x080C00C2, OpenArchive, "OpenArchive"}, | ||
| 325 | {0x080D0144, nullptr, "ControlArchive"}, | ||
| 326 | {0x080E0080, nullptr, "CloseArchive"}, | ||
| 327 | {0x080F0180, nullptr, "FormatThisUserSaveData"}, | ||
| 328 | {0x08100200, nullptr, "CreateSystemSaveData"}, | ||
| 329 | {0x08110040, nullptr, "DeleteSystemSaveData"}, | ||
| 330 | {0x08120080, nullptr, "GetFreeBytes"}, | ||
| 331 | {0x08130000, nullptr, "GetCardType"}, | ||
| 332 | {0x08140000, nullptr, "GetSdmcArchiveResource"}, | ||
| 333 | {0x08150000, nullptr, "GetNandArchiveResource"}, | ||
| 334 | {0x08160000, nullptr, "GetSdmcFatfsErro"}, | ||
| 335 | {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, | ||
| 336 | {0x08180000, nullptr, "IsSdmcWritable"}, | ||
| 337 | {0x08190042, nullptr, "GetSdmcCid"}, | ||
| 338 | {0x081A0042, nullptr, "GetNandCid"}, | ||
| 339 | {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, | ||
| 340 | {0x081C0000, nullptr, "GetNandSpeedInfo"}, | ||
| 341 | {0x081D0042, nullptr, "GetSdmcLog"}, | ||
| 342 | {0x081E0042, nullptr, "GetNandLog"}, | ||
| 343 | {0x081F0000, nullptr, "ClearSdmcLog"}, | ||
| 344 | {0x08200000, nullptr, "ClearNandLog"}, | ||
| 345 | {0x08210000, nullptr, "CardSlotIsInserted"}, | ||
| 346 | {0x08220000, nullptr, "CardSlotPowerOn"}, | ||
| 347 | {0x08230000, nullptr, "CardSlotPowerOff"}, | ||
| 348 | {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, | ||
| 349 | {0x08250040, nullptr, "CardNorDirectCommand"}, | ||
| 350 | {0x08260080, nullptr, "CardNorDirectCommandWithAddress"}, | ||
| 351 | {0x08270082, nullptr, "CardNorDirectRead"}, | ||
| 352 | {0x082800C2, nullptr, "CardNorDirectReadWithAddress"}, | ||
| 353 | {0x08290082, nullptr, "CardNorDirectWrite"}, | ||
| 354 | {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"}, | ||
| 355 | {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"}, | ||
| 356 | {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, | ||
| 357 | {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, | ||
| 358 | {0x082E0040, nullptr, "GetProductInfo"}, | ||
| 359 | {0x082F0040, nullptr, "GetProgramLaunchInfo"}, | ||
| 360 | {0x08300182, nullptr, "CreateExtSaveData"}, | ||
| 361 | {0x08310180, nullptr, "CreateSharedExtSaveData"}, | ||
| 362 | {0x08320102, nullptr, "ReadExtSaveDataIcon"}, | ||
| 363 | {0x08330082, nullptr, "EnumerateExtSaveData"}, | ||
| 364 | {0x08340082, nullptr, "EnumerateSharedExtSaveData"}, | ||
| 365 | {0x08350080, nullptr, "DeleteExtSaveData"}, | ||
| 366 | {0x08360080, nullptr, "DeleteSharedExtSaveData"}, | ||
| 367 | {0x08370040, nullptr, "SetCardSpiBaudRate"}, | ||
| 368 | {0x08380040, nullptr, "SetCardSpiBusMode"}, | ||
| 369 | {0x08390000, nullptr, "SendInitializeInfoTo9"}, | ||
| 370 | {0x083A0100, nullptr, "GetSpecialContentIndex"}, | ||
| 371 | {0x083B00C2, nullptr, "GetLegacyRomHeader"}, | ||
| 372 | {0x083C00C2, nullptr, "GetLegacyBannerData"}, | ||
| 373 | {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"}, | ||
| 374 | {0x083E00C2, nullptr, "QueryTotalQuotaSize"}, | ||
| 375 | {0x083F00C0, nullptr, "GetExtDataBlockSize"}, | ||
| 376 | {0x08400040, nullptr, "AbnegateAccessRight"}, | ||
| 377 | {0x08410000, nullptr, "DeleteSdmcRoot"}, | ||
| 378 | {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, | ||
| 379 | {0x08430000, nullptr, "InitializeCtrFileSystem"}, | ||
| 380 | {0x08440000, nullptr, "CreateSeed"}, | ||
| 381 | {0x084500C2, nullptr, "GetFormatInfo"}, | ||
| 382 | {0x08460102, nullptr, "GetLegacyRomHeader2"}, | ||
| 383 | {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, | ||
| 384 | {0x08480042, nullptr, "GetSdmcCtrRootPath"}, | ||
| 385 | {0x08490040, nullptr, "GetArchiveResource"}, | ||
| 386 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, | ||
| 387 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, | ||
| 388 | {0x084C0242, nullptr, "FormatSaveData"}, | ||
| 389 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, | ||
| 390 | {0x084E0342, nullptr, "UpdateSha256Context"}, | ||
| 391 | {0x084F0102, nullptr, "ReadSpecialFile"}, | ||
| 392 | {0x08500040, nullptr, "GetSpecialFileSize"}, | ||
| 393 | {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, | ||
| 394 | {0x08610042, nullptr, "InitializeWithSdkVersion"}, | ||
| 395 | {0x08620040, nullptr, "SetPriority"}, | ||
| 396 | {0x08630000, nullptr, "GetPriority"}, | ||
| 397 | }; | ||
| 398 | |||
| 399 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 400 | // Interface class | ||
| 401 | |||
| 402 | Interface::Interface() { | ||
| 403 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 404 | } | ||
| 405 | |||
| 406 | Interface::~Interface() { | ||
| 407 | } | ||
| 408 | |||
| 409 | } // namespace | ||
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index de1bd3f61..0127d4ee5 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 | ||
| @@ -33,7 +33,7 @@ static inline u8* GetCommandBuffer(u32 thread_id) { | |||
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { | 35 | static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { |
| 36 | _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index"); | 36 | _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index"); |
| 37 | 37 | ||
| 38 | // For each thread there are two FrameBufferUpdate fields | 38 | // For each thread there are two FrameBufferUpdate fields |
| 39 | u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); | 39 | u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); |
| @@ -50,14 +50,14 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { | |||
| 50 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | 50 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { |
| 51 | // TODO: Return proper error codes | 51 | // TODO: Return proper error codes |
| 52 | if (base_address + size_in_bytes >= 0x420000) { | 52 | if (base_address + size_in_bytes >= 0x420000) { |
| 53 | ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", | 53 | LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", |
| 54 | base_address, size_in_bytes); | 54 | base_address, size_in_bytes); |
| 55 | return; | 55 | return; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | // size should be word-aligned | 58 | // size should be word-aligned |
| 59 | if ((size_in_bytes % 4) != 0) { | 59 | if ((size_in_bytes % 4) != 0) { |
| 60 | ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes); | 60 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); |
| 61 | return; | 61 | return; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| @@ -72,7 +72,7 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | |||
| 72 | 72 | ||
| 73 | /// Write a GSP GPU hardware register | 73 | /// Write a GSP GPU hardware register |
| 74 | static void WriteHWRegs(Service::Interface* self) { | 74 | static void WriteHWRegs(Service::Interface* self) { |
| 75 | u32* cmd_buff = Service::GetCommandBuffer(); | 75 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 76 | u32 reg_addr = cmd_buff[1]; | 76 | u32 reg_addr = cmd_buff[1]; |
| 77 | u32 size = cmd_buff[2]; | 77 | u32 size = cmd_buff[2]; |
| 78 | 78 | ||
| @@ -83,19 +83,19 @@ static void WriteHWRegs(Service::Interface* self) { | |||
| 83 | 83 | ||
| 84 | /// Read a GSP GPU hardware register | 84 | /// Read a GSP GPU hardware register |
| 85 | static void ReadHWRegs(Service::Interface* self) { | 85 | static void ReadHWRegs(Service::Interface* self) { |
| 86 | u32* cmd_buff = Service::GetCommandBuffer(); | 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 87 | u32 reg_addr = cmd_buff[1]; | 87 | u32 reg_addr = cmd_buff[1]; |
| 88 | u32 size = cmd_buff[2]; | 88 | u32 size = cmd_buff[2]; |
| 89 | 89 | ||
| 90 | // TODO: Return proper error codes | 90 | // TODO: Return proper error codes |
| 91 | if (reg_addr + size >= 0x420000) { | 91 | if (reg_addr + size >= 0x420000) { |
| 92 | ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); | 92 | LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); |
| 93 | return; | 93 | return; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | // size should be word-aligned | 96 | // size should be word-aligned |
| 97 | if ((size % 4) != 0) { | 97 | if ((size % 4) != 0) { |
| 98 | ERROR_LOG(GPU, "Invalid size 0x%08x", size); | 98 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size); |
| 99 | return; | 99 | return; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| @@ -136,7 +136,7 @@ static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | |||
| 136 | * 1: Result code | 136 | * 1: Result code |
| 137 | */ | 137 | */ |
| 138 | static void SetBufferSwap(Service::Interface* self) { | 138 | static void SetBufferSwap(Service::Interface* self) { |
| 139 | u32* cmd_buff = Service::GetCommandBuffer(); | 139 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 140 | u32 screen_id = cmd_buff[1]; | 140 | u32 screen_id = cmd_buff[1]; |
| 141 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; | 141 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; |
| 142 | SetBufferSwap(screen_id, *fb_info); | 142 | SetBufferSwap(screen_id, *fb_info); |
| @@ -145,6 +145,30 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | /** | 147 | /** |
| 148 | * GSP_GPU::FlushDataCache service function | ||
| 149 | * | ||
| 150 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 151 | * | ||
| 152 | * Inputs: | ||
| 153 | * 1 : Address | ||
| 154 | * 2 : Size | ||
| 155 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 156 | * 4 : KProcess handle | ||
| 157 | * Outputs: | ||
| 158 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 159 | */ | ||
| 160 | static void FlushDataCache(Service::Interface* self) { | ||
| 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 162 | u32 address = cmd_buff[1]; | ||
| 163 | u32 size = cmd_buff[2]; | ||
| 164 | u32 process = cmd_buff[4]; | ||
| 165 | |||
| 166 | // TODO(purpasmart96): Verify return header on HW | ||
| 167 | |||
| 168 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 148 | * GSP_GPU::RegisterInterruptRelayQueue service function | 172 | * GSP_GPU::RegisterInterruptRelayQueue service function |
| 149 | * Inputs: | 173 | * Inputs: |
| 150 | * 1 : "Flags" field, purpose is unknown | 174 | * 1 : "Flags" field, purpose is unknown |
| @@ -155,14 +179,15 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 155 | * 4 : Handle to GSP shared memory | 179 | * 4 : Handle to GSP shared memory |
| 156 | */ | 180 | */ |
| 157 | static void RegisterInterruptRelayQueue(Service::Interface* self) { | 181 | static void RegisterInterruptRelayQueue(Service::Interface* self) { |
| 158 | u32* cmd_buff = Service::GetCommandBuffer(); | 182 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 159 | u32 flags = cmd_buff[1]; | 183 | u32 flags = cmd_buff[1]; |
| 160 | g_interrupt_event = cmd_buff[3]; | 184 | g_interrupt_event = cmd_buff[3]; |
| 161 | g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); | 185 | g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); |
| 162 | 186 | ||
| 163 | _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); | 187 | _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); |
| 164 | 188 | ||
| 165 | cmd_buff[2] = g_thread_id++; // ThreadID | 189 | cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init |
| 190 | cmd_buff[2] = g_thread_id++; // Thread ID | ||
| 166 | cmd_buff[4] = g_shared_memory; // GSP shared memory | 191 | cmd_buff[4] = g_shared_memory; // GSP shared memory |
| 167 | 192 | ||
| 168 | Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? | 193 | Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? |
| @@ -172,14 +197,15 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { | |||
| 172 | * Signals that the specified interrupt type has occurred to userland code | 197 | * Signals that the specified interrupt type has occurred to userland code |
| 173 | * @param interrupt_id ID of interrupt that is being signalled | 198 | * @param interrupt_id ID of interrupt that is being signalled |
| 174 | * @todo This should probably take a thread_id parameter and only signal this thread? | 199 | * @todo This should probably take a thread_id parameter and only signal this thread? |
| 200 | * @todo This probably does not belong in the GSP module, instead move to video_core | ||
| 175 | */ | 201 | */ |
| 176 | void SignalInterrupt(InterruptId interrupt_id) { | 202 | void SignalInterrupt(InterruptId interrupt_id) { |
| 177 | if (0 == g_interrupt_event) { | 203 | if (0 == g_interrupt_event) { |
| 178 | WARN_LOG(GSP, "cannot synchronize until GSP event has been created!"); | 204 | LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); |
| 179 | return; | 205 | return; |
| 180 | } | 206 | } |
| 181 | if (0 == g_shared_memory) { | 207 | if (0 == g_shared_memory) { |
| 182 | WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!"); | 208 | LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); |
| 183 | return; | 209 | return; |
| 184 | } | 210 | } |
| 185 | for (int thread_id = 0; thread_id < 0x4; ++thread_id) { | 211 | for (int thread_id = 0; thread_id < 0x4; ++thread_id) { |
| @@ -210,6 +236,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 210 | memcpy(Memory::GetPointer(command.dma_request.dest_address), | 236 | memcpy(Memory::GetPointer(command.dma_request.dest_address), |
| 211 | Memory::GetPointer(command.dma_request.source_address), | 237 | Memory::GetPointer(command.dma_request.source_address), |
| 212 | command.dma_request.size); | 238 | command.dma_request.size); |
| 239 | SignalInterrupt(InterruptId::DMA); | ||
| 213 | break; | 240 | break; |
| 214 | 241 | ||
| 215 | // ctrulib homebrew sends all relevant command list data with this command, | 242 | // ctrulib homebrew sends all relevant command list data with this command, |
| @@ -218,13 +245,13 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 218 | case CommandId::SET_COMMAND_LIST_LAST: | 245 | case CommandId::SET_COMMAND_LIST_LAST: |
| 219 | { | 246 | { |
| 220 | auto& params = command.set_command_list_last; | 247 | auto& params = command.set_command_list_last; |
| 248 | |||
| 221 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); | 249 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); |
| 222 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); | 250 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size); |
| 223 | 251 | ||
| 224 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though | 252 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though |
| 225 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); | 253 | WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); |
| 226 | 254 | ||
| 227 | SignalInterrupt(InterruptId::P3D); | ||
| 228 | break; | 255 | break; |
| 229 | } | 256 | } |
| 230 | 257 | ||
| @@ -242,6 +269,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 242 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); | 269 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); |
| 243 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); | 270 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); |
| 244 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); | 271 | WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); |
| 272 | |||
| 273 | SignalInterrupt(InterruptId::PSC0); | ||
| 245 | break; | 274 | break; |
| 246 | } | 275 | } |
| 247 | 276 | ||
| @@ -255,14 +284,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 255 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); | 284 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); |
| 256 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); | 285 | WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); |
| 257 | 286 | ||
| 258 | // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to | 287 | // TODO(bunnei): Determine if these interrupts should be signalled here. |
| 259 | // work well enough for running demos. Need to figure out how these all work and trigger | ||
| 260 | // them correctly. | ||
| 261 | SignalInterrupt(InterruptId::PSC0); | ||
| 262 | SignalInterrupt(InterruptId::PSC1); | 288 | SignalInterrupt(InterruptId::PSC1); |
| 263 | SignalInterrupt(InterruptId::PPF); | 289 | SignalInterrupt(InterruptId::PPF); |
| 264 | SignalInterrupt(InterruptId::P3D); | ||
| 265 | SignalInterrupt(InterruptId::DMA); | ||
| 266 | 290 | ||
| 267 | // Update framebuffer information if requested | 291 | // Update framebuffer information if requested |
| 268 | for (int screen_id = 0; screen_id < 2; ++screen_id) { | 292 | for (int screen_id = 0; screen_id < 2; ++screen_id) { |
| @@ -298,13 +322,15 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 298 | } | 322 | } |
| 299 | 323 | ||
| 300 | default: | 324 | default: |
| 301 | ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value()); | 325 | LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); |
| 302 | } | 326 | } |
| 303 | } | 327 | } |
| 304 | 328 | ||
| 305 | /// This triggers handling of the GX command written to the command buffer in shared memory. | 329 | /// This triggers handling of the GX command written to the command buffer in shared memory. |
| 306 | static void TriggerCmdReqQueue(Service::Interface* self) { | 330 | static void TriggerCmdReqQueue(Service::Interface* self) { |
| 307 | 331 | ||
| 332 | LOG_TRACE(Service_GSP, "called"); | ||
| 333 | |||
| 308 | // Iterate through each thread's command queue... | 334 | // Iterate through each thread's command queue... |
| 309 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { | 335 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { |
| 310 | CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); | 336 | CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); |
| @@ -320,6 +346,9 @@ static void TriggerCmdReqQueue(Service::Interface* self) { | |||
| 320 | command_buffer->number_commands = command_buffer->number_commands - 1; | 346 | command_buffer->number_commands = command_buffer->number_commands - 1; |
| 321 | } | 347 | } |
| 322 | } | 348 | } |
| 349 | |||
| 350 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 351 | cmd_buff[1] = 0; // No error | ||
| 323 | } | 352 | } |
| 324 | 353 | ||
| 325 | const Interface::FunctionInfo FunctionTable[] = { | 354 | const Interface::FunctionInfo FunctionTable[] = { |
| @@ -330,7 +359,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 330 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, | 359 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, |
| 331 | {0x00060082, nullptr, "SetCommandList"}, | 360 | {0x00060082, nullptr, "SetCommandList"}, |
| 332 | {0x000700C2, nullptr, "RequestDma"}, | 361 | {0x000700C2, nullptr, "RequestDma"}, |
| 333 | {0x00080082, nullptr, "FlushDataCache"}, | 362 | {0x00080082, FlushDataCache, "FlushDataCache"}, |
| 334 | {0x00090082, nullptr, "InvalidateDataCache"}, | 363 | {0x00090082, nullptr, "InvalidateDataCache"}, |
| 335 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, | 364 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, |
| 336 | {0x000B0040, nullptr, "SetLcdForceBlack"}, | 365 | {0x000B0040, nullptr, "SetLcdForceBlack"}, |
| @@ -367,7 +396,4 @@ Interface::Interface() { | |||
| 367 | g_thread_id = 1; | 396 | g_thread_id = 1; |
| 368 | } | 397 | } |
| 369 | 398 | ||
| 370 | Interface::~Interface() { | ||
| 371 | } | ||
| 372 | |||
| 373 | } // namespace | 399 | } // namespace |
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 177ce8da6..932b6170f 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 |
| @@ -158,19 +158,11 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec | |||
| 158 | /// Interface to "srv:" service | 158 | /// Interface to "srv:" service |
| 159 | class Interface : public Service::Interface { | 159 | class Interface : public Service::Interface { |
| 160 | public: | 160 | public: |
| 161 | |||
| 162 | Interface(); | 161 | Interface(); |
| 163 | 162 | ||
| 164 | ~Interface(); | ||
| 165 | |||
| 166 | /** | ||
| 167 | * Gets the string port name used by CTROS for the service | ||
| 168 | * @return Port name of service | ||
| 169 | */ | ||
| 170 | std::string GetPortName() const override { | 163 | std::string GetPortName() const override { |
| 171 | return "gsp::Gpu"; | 164 | return "gsp::Gpu"; |
| 172 | } | 165 | } |
| 173 | |||
| 174 | }; | 166 | }; |
| 175 | 167 | ||
| 176 | /** | 168 | /** |
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp index d29de1a52..99b0ea5a0 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" |
| @@ -55,7 +55,7 @@ static void UpdateNextCirclePadState() { | |||
| 55 | /** | 55 | /** |
| 56 | * Sets a Pad state (button or button combo) as pressed | 56 | * Sets a Pad state (button or button combo) as pressed |
| 57 | */ | 57 | */ |
| 58 | void PadButtonPress(PadState pad_state) { | 58 | void PadButtonPress(const PadState& pad_state) { |
| 59 | next_state.hex |= pad_state.hex; | 59 | next_state.hex |= pad_state.hex; |
| 60 | UpdateNextCirclePadState(); | 60 | UpdateNextCirclePadState(); |
| 61 | } | 61 | } |
| @@ -63,7 +63,7 @@ void PadButtonPress(PadState pad_state) { | |||
| 63 | /** | 63 | /** |
| 64 | * Sets a Pad state (button or button combo) as released | 64 | * Sets a Pad state (button or button combo) as released |
| 65 | */ | 65 | */ |
| 66 | void PadButtonRelease(PadState pad_state) { | 66 | void PadButtonRelease(const PadState& pad_state) { |
| 67 | next_state.hex &= ~pad_state.hex; | 67 | next_state.hex &= ~pad_state.hex; |
| 68 | UpdateNextCirclePadState(); | 68 | UpdateNextCirclePadState(); |
| 69 | } | 69 | } |
| @@ -153,7 +153,7 @@ void PadUpdateComplete() { | |||
| 153 | * 8 : Event signaled by HID_User | 153 | * 8 : Event signaled by HID_User |
| 154 | */ | 154 | */ |
| 155 | static void GetIPCHandles(Service::Interface* self) { | 155 | static void GetIPCHandles(Service::Interface* self) { |
| 156 | u32* cmd_buff = Service::GetCommandBuffer(); | 156 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 157 | 157 | ||
| 158 | cmd_buff[1] = 0; // No error | 158 | cmd_buff[1] = 0; // No error |
| 159 | cmd_buff[3] = shared_mem; | 159 | cmd_buff[3] = shared_mem; |
| @@ -163,7 +163,7 @@ static void GetIPCHandles(Service::Interface* self) { | |||
| 163 | cmd_buff[7] = event_gyroscope; | 163 | cmd_buff[7] = event_gyroscope; |
| 164 | cmd_buff[8] = event_debug_pad; | 164 | cmd_buff[8] = event_debug_pad; |
| 165 | 165 | ||
| 166 | DEBUG_LOG(KERNEL, "called"); | 166 | LOG_TRACE(Service_HID, "called"); |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | const Interface::FunctionInfo FunctionTable[] = { | 169 | const Interface::FunctionInfo FunctionTable[] = { |
| @@ -179,7 +179,6 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 179 | {0x00170000, nullptr, "GetSoundVolume"}, | 179 | {0x00170000, nullptr, "GetSoundVolume"}, |
| 180 | }; | 180 | }; |
| 181 | 181 | ||
| 182 | |||
| 183 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 182 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 184 | // Interface class | 183 | // Interface class |
| 185 | 184 | ||
| @@ -196,7 +195,4 @@ Interface::Interface() { | |||
| 196 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 195 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 197 | } | 196 | } |
| 198 | 197 | ||
| 199 | Interface::~Interface() { | ||
| 200 | } | ||
| 201 | |||
| 202 | } // namespace | 198 | } // namespace |
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h index 5ed97085d..5b96dda60 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 |
| @@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}}; | |||
| 93 | const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; | 93 | const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; |
| 94 | 94 | ||
| 95 | // Methods for updating the HID module's state | 95 | // Methods for updating the HID module's state |
| 96 | void PadButtonPress(PadState pad_state); | 96 | void PadButtonPress(const PadState& pad_state); |
| 97 | void PadButtonRelease(PadState pad_state); | 97 | void PadButtonRelease(const PadState& pad_state); |
| 98 | void PadUpdateComplete(); | 98 | void PadUpdateComplete(); |
| 99 | 99 | ||
| 100 | /** | 100 | /** |
| @@ -102,19 +102,11 @@ void PadUpdateComplete(); | |||
| 102 | */ | 102 | */ |
| 103 | class Interface : public Service::Interface { | 103 | class Interface : public Service::Interface { |
| 104 | public: | 104 | public: |
| 105 | |||
| 106 | Interface(); | 105 | Interface(); |
| 107 | 106 | ||
| 108 | ~Interface(); | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Gets the string port name used by CTROS for the service | ||
| 112 | * @return Port name of service | ||
| 113 | */ | ||
| 114 | std::string GetPortName() const override { | 107 | std::string GetPortName() const override { |
| 115 | return "hid:USER"; | 108 | return "hid:USER"; |
| 116 | } | 109 | } |
| 117 | |||
| 118 | }; | 110 | }; |
| 119 | 111 | ||
| 120 | } // namespace | 112 | } // namespace |
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp new file mode 100644 index 000000000..d0bff552f --- /dev/null +++ b/src/core/hle/service/http_c.cpp | |||
| @@ -0,0 +1,64 @@ | |||
| 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/http_c.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace HTTP_C | ||
| 11 | |||
| 12 | namespace HTTP_C { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00010044, nullptr, "Initialize"}, | ||
| 16 | {0x00020082, nullptr, "CreateContext"}, | ||
| 17 | {0x00030040, nullptr, "CloseContext"}, | ||
| 18 | {0x00040040, nullptr, "CancelConnection"}, | ||
| 19 | {0x00050040, nullptr, "GetRequestState"}, | ||
| 20 | {0x00060040, nullptr, "GetDownloadSizeState"}, | ||
| 21 | {0x00070040, nullptr, "GetRequestError"}, | ||
| 22 | {0x00080042, nullptr, "InitializeConnectionSession"}, | ||
| 23 | {0x00090040, nullptr, "BeginRequest"}, | ||
| 24 | {0x000A0040, nullptr, "BeginRequestAsync"}, | ||
| 25 | {0x000B0082, nullptr, "ReceiveData"}, | ||
| 26 | {0x000C0102, nullptr, "ReceiveDataTimeout"}, | ||
| 27 | {0x000D0146, nullptr, "SetProxy"}, | ||
| 28 | {0x000E0040, nullptr, "SetProxyDefault"}, | ||
| 29 | {0x000F00C4, nullptr, "SetBasicAuthorization"}, | ||
| 30 | {0x00100080, nullptr, "SetSocketBufferSize"}, | ||
| 31 | {0x001100C4, nullptr, "AddRequestHeader"}, | ||
| 32 | {0x001200C4, nullptr, "AddPostDataAscii"}, | ||
| 33 | {0x001300C4, nullptr, "AddPostDataBinary"}, | ||
| 34 | {0x00140082, nullptr, "AddPostDataRaw"}, | ||
| 35 | {0x00150080, nullptr, "SetPostDataType"}, | ||
| 36 | {0x001600C4, nullptr, "SendPostDataAscii"}, | ||
| 37 | {0x00170144, nullptr, "SendPostDataAsciiTimeout"}, | ||
| 38 | {0x001800C4, nullptr, "SendPostDataBinary"}, | ||
| 39 | {0x00190144, nullptr, "SendPostDataBinaryTimeout"}, | ||
| 40 | {0x001A0082, nullptr, "SendPostDataRaw"}, | ||
| 41 | {0x001B0102, nullptr, "SendPOSTDataRawTimeout"}, | ||
| 42 | {0x001C0080, nullptr, "SetPostDataEncoding"}, | ||
| 43 | {0x001D0040, nullptr, "NotifyFinishSendPostData"}, | ||
| 44 | {0x001E00C4, nullptr, "GetResponseHeader"}, | ||
| 45 | {0x001F0144, nullptr, "GetResponseHeaderTimeout"}, | ||
| 46 | {0x00200082, nullptr, "GetResponseData"}, | ||
| 47 | {0x00210102, nullptr, "GetResponseDataTimeout"}, | ||
| 48 | {0x00220040, nullptr, "GetResponseStatusCode"}, | ||
| 49 | {0x002300C0, nullptr, "GetResponseStatusCodeTimeout"}, | ||
| 50 | {0x00240082, nullptr, "AddTrustedRootCA"}, | ||
| 51 | {0x00350186, nullptr, "SetDefaultProxy"}, | ||
| 52 | {0x00360000, nullptr, "ClearDNSCache"}, | ||
| 53 | {0x00370080, nullptr, "SetKeepAlive"}, | ||
| 54 | {0x003800C0, nullptr, "Finalize"}, | ||
| 55 | }; | ||
| 56 | |||
| 57 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 58 | // Interface class | ||
| 59 | |||
| 60 | Interface::Interface() { | ||
| 61 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 62 | } | ||
| 63 | |||
| 64 | } // namespace | ||
diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h new file mode 100644 index 000000000..5ea3d1df3 --- /dev/null +++ b/src/core/hle/service/http_c.h | |||
| @@ -0,0 +1,23 @@ | |||
| 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 HTTP_C | ||
| 11 | |||
| 12 | namespace HTTP_C { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "http:C"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp index be15db231..b388afb15 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" |
| @@ -30,7 +30,4 @@ Interface::Interface() { | |||
| 30 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 30 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | Interface::~Interface() { | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace | 33 | } // namespace |
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h index 73effd7e3..deef701c5 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 | ||
| @@ -14,11 +14,7 @@ namespace IR_RST { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "ir:rst"; | 19 | return "ir:rst"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp index aa9db6f6d..da6f38e41 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" |
| @@ -39,7 +39,4 @@ Interface::Interface() { | |||
| 39 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 39 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | Interface::~Interface() { | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace | 42 | } // namespace |
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h index 86d98d079..ec47a1524 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 |
| @@ -14,11 +14,7 @@ namespace IR_U { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "ir:u"; | 19 | return "ir:u"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp new file mode 100644 index 000000000..9c9e90a40 --- /dev/null +++ b/src/core/hle/service/ldr_ro.cpp | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/ldr_ro.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace LDR_RO | ||
| 11 | |||
| 12 | namespace LDR_RO { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x000100C2, nullptr, "Initialize"}, | ||
| 16 | {0x00020082, nullptr, "CRR_Load"}, | ||
| 17 | {0x00030042, nullptr, "CRR_Unload"}, | ||
| 18 | {0x000402C2, nullptr, "CRO_LoadAndFix"}, | ||
| 19 | {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"} | ||
| 20 | }; | ||
| 21 | |||
| 22 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 23 | // Interface class | ||
| 24 | |||
| 25 | Interface::Interface() { | ||
| 26 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 27 | } | ||
| 28 | |||
| 29 | } // 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..331637cde --- /dev/null +++ b/src/core/hle/service/ldr_ro.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace LDR_RO | ||
| 11 | |||
| 12 | namespace LDR_RO { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "ldr:ro"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index d6f30e9ae..82bce9180 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" |
| @@ -37,7 +37,4 @@ Interface::Interface() { | |||
| 37 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 37 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | Interface::~Interface() { | ||
| 41 | } | ||
| 42 | |||
| 43 | } // namespace | 40 | } // namespace |
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 2a495f3a9..dc795d14c 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 |
| @@ -16,11 +16,7 @@ namespace MIC_U { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface(); | 19 | |
| 20 | /** | ||
| 21 | * Gets the string port name used by CTROS for the service | ||
| 22 | * @return Port name of service | ||
| 23 | */ | ||
| 24 | std::string GetPortName() const override { | 20 | std::string GetPortName() const override { |
| 25 | return "mic:u"; | 21 | return "mic:u"; |
| 26 | } | 22 | } |
diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp index 37c0661bf..233b14f6d 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" |
| @@ -24,7 +24,4 @@ Interface::Interface() { | |||
| 24 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 24 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | Interface::~Interface() { | ||
| 28 | } | ||
| 29 | |||
| 30 | } // namespace | 27 | } // namespace |
diff --git a/src/core/hle/service/ndm_u.h b/src/core/hle/service/ndm_u.h index 2ca9fcf22..51c4b3902 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 |
| @@ -15,19 +15,11 @@ namespace NDM_U { | |||
| 15 | 15 | ||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | |||
| 19 | Interface(); | 18 | Interface(); |
| 20 | 19 | ||
| 21 | ~Interface(); | ||
| 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 { | 20 | std::string GetPortName() const override { |
| 28 | return "ndm:u"; | 21 | return "ndm:u"; |
| 29 | } | 22 | } |
| 30 | |||
| 31 | }; | 23 | }; |
| 32 | 24 | ||
| 33 | } // namespace | 25 | } // namespace |
diff --git a/src/core/hle/service/news_u.cpp b/src/core/hle/service/news_u.cpp new file mode 100644 index 000000000..b5adad4c6 --- /dev/null +++ b/src/core/hle/service/news_u.cpp | |||
| @@ -0,0 +1,25 @@ | |||
| 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/news_u.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace NEWS_U | ||
| 11 | |||
| 12 | namespace NEWS_U { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x000100C8, nullptr, "AddNotification"}, | ||
| 16 | }; | ||
| 17 | |||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 19 | // Interface class | ||
| 20 | |||
| 21 | Interface::Interface() { | ||
| 22 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 23 | } | ||
| 24 | |||
| 25 | } // namespace | ||
diff --git a/src/core/hle/service/news_u.h b/src/core/hle/service/news_u.h new file mode 100644 index 000000000..0473cd19c --- /dev/null +++ b/src/core/hle/service/news_u.h | |||
| @@ -0,0 +1,23 @@ | |||
| 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 NEWS_U | ||
| 11 | |||
| 12 | namespace NEWS_U { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "news:u"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // 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..17d1c4ff5 --- /dev/null +++ b/src/core/hle/service/nim_aoc.cpp | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/log.h" | ||
| 6 | #include "core/hle/hle.h" | ||
| 7 | #include "core/hle/service/nim_aoc.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace NIM_AOC | ||
| 11 | |||
| 12 | namespace NIM_AOC { | ||
| 13 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 15 | {0x00030042, nullptr, "SetApplicationId"}, | ||
| 16 | {0x00040042, nullptr, "SetTin"}, | ||
| 17 | {0x000902D0, nullptr, "ListContentSetsEx"}, | ||
| 18 | {0x00180000, nullptr, "GetBalance"}, | ||
| 19 | {0x001D0000, nullptr, "GetCustomerSupportCode"}, | ||
| 20 | {0x00210000, nullptr, "Initialize"}, | ||
| 21 | {0x00240282, nullptr, "CalculateContentsRequiredSize"}, | ||
| 22 | {0x00250000, nullptr, "RefreshServerTime"}, | ||
| 23 | }; | ||
| 24 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 25 | // Interface class | ||
| 26 | |||
| 27 | Interface::Interface() { | ||
| 28 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||
| 29 | } | ||
| 30 | |||
| 31 | } // namespace | ||
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h new file mode 100644 index 000000000..aeb71eed2 --- /dev/null +++ b/src/core/hle/service/nim_aoc.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace NIM_AOC | ||
| 11 | |||
| 12 | namespace NIM_AOC { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "nim:aoc"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index 14df86d85..ce456a966 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" |
| @@ -29,7 +29,4 @@ Interface::Interface() { | |||
| 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | Interface::~Interface() { | ||
| 33 | } | ||
| 34 | |||
| 35 | } // namespace | 32 | } // namespace |
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index 69d2c2002..9043f5aa7 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 |
| @@ -16,11 +16,7 @@ namespace NWM_UDS { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface(); | 19 | |
| 20 | /** | ||
| 21 | * Gets the string port name used by CTROS for the service | ||
| 22 | * @return Port name of service | ||
| 23 | */ | ||
| 24 | std::string GetPortName() const override { | 20 | std::string GetPortName() const override { |
| 25 | return "nwm:UDS"; | 21 | return "nwm:UDS"; |
| 26 | } | 22 | } |
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp index 90e9b1bfa..529dccafb 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" |
| @@ -29,7 +29,4 @@ Interface::Interface() { | |||
| 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 29 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | Interface::~Interface() { | ||
| 33 | } | ||
| 34 | |||
| 35 | } // namespace | 32 | } // namespace |
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h index 28c38f582..c1fb1f9da 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 |
| @@ -14,11 +14,7 @@ namespace PM_APP { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "pm:app"; | 19 | return "pm:app"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index d9122dbbc..d1498f05c 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" |
| @@ -11,15 +11,101 @@ | |||
| 11 | 11 | ||
| 12 | namespace PTM_U { | 12 | namespace PTM_U { |
| 13 | 13 | ||
| 14 | /// Charge levels used by PTM functions | ||
| 15 | enum class ChargeLevels : u32 { | ||
| 16 | CriticalBattery = 1, | ||
| 17 | LowBattery = 2, | ||
| 18 | HalfFull = 3, | ||
| 19 | MostlyFull = 4, | ||
| 20 | CompletelyFull = 5, | ||
| 21 | }; | ||
| 22 | |||
| 23 | static bool shell_open = true; | ||
| 24 | |||
| 25 | static bool battery_is_charging = true; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * It is unknown if GetAdapterState is the same as GetBatteryChargeState, | ||
| 29 | * it is likely to just be a duplicate function of GetBatteryChargeState | ||
| 30 | * that controls another part of the HW. | ||
| 31 | * PTM_U::GetAdapterState service function | ||
| 32 | * Outputs: | ||
| 33 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 34 | * 2 : Output of function, 0 = not charging, 1 = charging. | ||
| 35 | */ | ||
| 36 | static void GetAdapterState(Service::Interface* self) { | ||
| 37 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 38 | |||
| 39 | // TODO(purpasmart96): This function is only a stub, | ||
| 40 | // it returns a valid result without implementing full functionality. | ||
| 41 | |||
| 42 | cmd_buff[1] = 0; // No error | ||
| 43 | cmd_buff[2] = battery_is_charging ? 1 : 0; | ||
| 44 | |||
| 45 | LOG_WARNING(Service_PTM, "(STUBBED) called"); | ||
| 46 | } | ||
| 47 | |||
| 48 | /* | ||
| 49 | * PTM_User::GetShellState service function. | ||
| 50 | * Outputs: | ||
| 51 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 52 | * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) | ||
| 53 | */ | ||
| 54 | static void GetShellState(Service::Interface* self) { | ||
| 55 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 56 | |||
| 57 | cmd_buff[1] = 0; | ||
| 58 | cmd_buff[2] = shell_open ? 1 : 0; | ||
| 59 | |||
| 60 | LOG_TRACE(Service_PTM, "PTM_U::GetShellState called"); | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * PTM_U::GetBatteryLevel service function | ||
| 65 | * Outputs: | ||
| 66 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 67 | * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery, | ||
| 68 | * 3 = half full battery, 2 = low battery, 1 = critical battery. | ||
| 69 | */ | ||
| 70 | static void GetBatteryLevel(Service::Interface* self) { | ||
| 71 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 72 | |||
| 73 | // TODO(purpasmart96): This function is only a stub, | ||
| 74 | // it returns a valid result without implementing full functionality. | ||
| 75 | |||
| 76 | cmd_buff[1] = 0; // No error | ||
| 77 | cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery | ||
| 78 | |||
| 79 | LOG_WARNING(Service_PTM, "(STUBBED) called"); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * PTM_U::GetBatteryChargeState service function | ||
| 84 | * Outputs: | ||
| 85 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 86 | * 2 : Output of function, 0 = not charging, 1 = charging. | ||
| 87 | */ | ||
| 88 | static void GetBatteryChargeState(Service::Interface* self) { | ||
| 89 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 90 | |||
| 91 | // TODO(purpasmart96): This function is only a stub, | ||
| 92 | // it returns a valid result without implementing full functionality. | ||
| 93 | |||
| 94 | cmd_buff[1] = 0; // No error | ||
| 95 | cmd_buff[2] = battery_is_charging ? 1 : 0; | ||
| 96 | |||
| 97 | LOG_WARNING(Service_PTM, "(STUBBED) called"); | ||
| 98 | } | ||
| 99 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 100 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010002, nullptr, "RegisterAlarmClient"}, | 101 | {0x00010002, nullptr, "RegisterAlarmClient"}, |
| 16 | {0x00020080, nullptr, "SetRtcAlarm"}, | 102 | {0x00020080, nullptr, "SetRtcAlarm"}, |
| 17 | {0x00030000, nullptr, "GetRtcAlarm"}, | 103 | {0x00030000, nullptr, "GetRtcAlarm"}, |
| 18 | {0x00040000, nullptr, "CancelRtcAlarm"}, | 104 | {0x00040000, nullptr, "CancelRtcAlarm"}, |
| 19 | {0x00050000, nullptr, "GetAdapterState"}, | 105 | {0x00050000, GetAdapterState, "GetAdapterState"}, |
| 20 | {0x00060000, nullptr, "GetShellState"}, | 106 | {0x00060000, GetShellState, "GetShellState"}, |
| 21 | {0x00070000, nullptr, "GetBatteryLevel"}, | 107 | {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, |
| 22 | {0x00080000, nullptr, "GetBatteryChargeState"}, | 108 | {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, |
| 23 | {0x00090000, nullptr, "GetPedometerState"}, | 109 | {0x00090000, nullptr, "GetPedometerState"}, |
| 24 | {0x000A0042, nullptr, "GetStepHistoryEntry"}, | 110 | {0x000A0042, nullptr, "GetStepHistoryEntry"}, |
| 25 | {0x000B00C2, nullptr, "GetStepHistory"}, | 111 | {0x000B00C2, nullptr, "GetStepHistory"}, |
| @@ -36,7 +122,4 @@ Interface::Interface() { | |||
| 36 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 122 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 37 | } | 123 | } |
| 38 | 124 | ||
| 39 | Interface::~Interface() { | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace | 125 | } // namespace |
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h index f8d9f57be..a44624fd5 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 |
| @@ -16,11 +16,7 @@ namespace PTM_U { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface(); | 19 | |
| 20 | /** | ||
| 21 | * Gets the string port name used by CTROS for the service | ||
| 22 | * @return Port name of service | ||
| 23 | */ | ||
| 24 | std::string GetPortName() const override { | 20 | std::string GetPortName() const override { |
| 25 | return "ptm:u"; | 21 | return "ptm:u"; |
| 26 | } | 22 | } |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index fed2268a0..0f3cc2aa8 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,22 +7,30 @@ | |||
| 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/act_u.h" | ||
| 11 | #include "core/hle/service/am_app.h" | ||
| 10 | #include "core/hle/service/am_net.h" | 12 | #include "core/hle/service/am_net.h" |
| 13 | #include "core/hle/service/apt_a.h" | ||
| 11 | #include "core/hle/service/apt_u.h" | 14 | #include "core/hle/service/apt_u.h" |
| 12 | #include "core/hle/service/boss_u.h" | 15 | #include "core/hle/service/boss_u.h" |
| 13 | #include "core/hle/service/cfg_i.h" | 16 | #include "core/hle/service/cecd_u.h" |
| 14 | #include "core/hle/service/cfg_u.h" | 17 | #include "core/hle/service/cfg/cfg_i.h" |
| 18 | #include "core/hle/service/cfg/cfg_u.h" | ||
| 15 | #include "core/hle/service/csnd_snd.h" | 19 | #include "core/hle/service/csnd_snd.h" |
| 16 | #include "core/hle/service/dsp_dsp.h" | 20 | #include "core/hle/service/dsp_dsp.h" |
| 17 | #include "core/hle/service/err_f.h" | 21 | #include "core/hle/service/err_f.h" |
| 18 | #include "core/hle/service/fs_user.h" | 22 | #include "core/hle/service/fs/fs_user.h" |
| 19 | #include "core/hle/service/frd_u.h" | 23 | #include "core/hle/service/frd_u.h" |
| 20 | #include "core/hle/service/gsp_gpu.h" | 24 | #include "core/hle/service/gsp_gpu.h" |
| 21 | #include "core/hle/service/hid_user.h" | 25 | #include "core/hle/service/hid_user.h" |
| 26 | #include "core/hle/service/http_c.h" | ||
| 22 | #include "core/hle/service/ir_rst.h" | 27 | #include "core/hle/service/ir_rst.h" |
| 23 | #include "core/hle/service/ir_u.h" | 28 | #include "core/hle/service/ir_u.h" |
| 29 | #include "core/hle/service/ldr_ro.h" | ||
| 24 | #include "core/hle/service/mic_u.h" | 30 | #include "core/hle/service/mic_u.h" |
| 25 | #include "core/hle/service/ndm_u.h" | 31 | #include "core/hle/service/ndm_u.h" |
| 32 | #include "core/hle/service/news_u.h" | ||
| 33 | #include "core/hle/service/nim_aoc.h" | ||
| 26 | #include "core/hle/service/nwm_uds.h" | 34 | #include "core/hle/service/nwm_uds.h" |
| 27 | #include "core/hle/service/pm_app.h" | 35 | #include "core/hle/service/pm_app.h" |
| 28 | #include "core/hle/service/ptm_u.h" | 36 | #include "core/hle/service/ptm_u.h" |
| @@ -48,7 +56,8 @@ Manager::~Manager() { | |||
| 48 | 56 | ||
| 49 | /// Add a service to the manager (does not create it though) | 57 | /// Add a service to the manager (does not create it though) |
| 50 | void Manager::AddService(Interface* service) { | 58 | void Manager::AddService(Interface* service) { |
| 51 | m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service); | 59 | // TOOD(yuriks): Fix error reporting |
| 60 | m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE); | ||
| 52 | m_services.push_back(service); | 61 | m_services.push_back(service); |
| 53 | } | 62 | } |
| 54 | 63 | ||
| @@ -62,7 +71,7 @@ void Manager::DeleteService(const std::string& port_name) { | |||
| 62 | 71 | ||
| 63 | /// Get a Service Interface from its Handle | 72 | /// Get a Service Interface from its Handle |
| 64 | Interface* Manager::FetchFromHandle(Handle handle) { | 73 | Interface* Manager::FetchFromHandle(Handle handle) { |
| 65 | return Kernel::g_object_pool.Get<Interface>(handle); | 74 | return Kernel::g_handle_table.Get<Interface>(handle); |
| 66 | } | 75 | } |
| 67 | 76 | ||
| 68 | /// Get a Service Interface from its port | 77 | /// Get a Service Interface from its port |
| @@ -84,35 +93,43 @@ void Init() { | |||
| 84 | 93 | ||
| 85 | g_manager->AddService(new SRV::Interface); | 94 | g_manager->AddService(new SRV::Interface); |
| 86 | g_manager->AddService(new AC_U::Interface); | 95 | g_manager->AddService(new AC_U::Interface); |
| 96 | g_manager->AddService(new ACT_U::Interface); | ||
| 97 | g_manager->AddService(new AM_APP::Interface); | ||
| 87 | g_manager->AddService(new AM_NET::Interface); | 98 | g_manager->AddService(new AM_NET::Interface); |
| 99 | g_manager->AddService(new APT_A::Interface); | ||
| 88 | g_manager->AddService(new APT_U::Interface); | 100 | g_manager->AddService(new APT_U::Interface); |
| 89 | g_manager->AddService(new BOSS_U::Interface); | 101 | g_manager->AddService(new BOSS_U::Interface); |
| 102 | g_manager->AddService(new CECD_U::Interface); | ||
| 90 | g_manager->AddService(new CFG_I::Interface); | 103 | g_manager->AddService(new CFG_I::Interface); |
| 91 | g_manager->AddService(new CFG_U::Interface); | 104 | g_manager->AddService(new CFG_U::Interface); |
| 92 | g_manager->AddService(new CSND_SND::Interface); | 105 | g_manager->AddService(new CSND_SND::Interface); |
| 93 | g_manager->AddService(new DSP_DSP::Interface); | 106 | g_manager->AddService(new DSP_DSP::Interface); |
| 94 | g_manager->AddService(new ERR_F::Interface); | 107 | g_manager->AddService(new ERR_F::Interface); |
| 95 | g_manager->AddService(new FRD_U::Interface); | 108 | g_manager->AddService(new FRD_U::Interface); |
| 96 | g_manager->AddService(new FS_User::Interface); | 109 | g_manager->AddService(new FS::FSUserInterface); |
| 97 | g_manager->AddService(new GSP_GPU::Interface); | 110 | g_manager->AddService(new GSP_GPU::Interface); |
| 98 | g_manager->AddService(new HID_User::Interface); | 111 | g_manager->AddService(new HID_User::Interface); |
| 112 | g_manager->AddService(new HTTP_C::Interface); | ||
| 99 | g_manager->AddService(new IR_RST::Interface); | 113 | g_manager->AddService(new IR_RST::Interface); |
| 100 | g_manager->AddService(new IR_U::Interface); | 114 | g_manager->AddService(new IR_U::Interface); |
| 115 | g_manager->AddService(new LDR_RO::Interface); | ||
| 101 | g_manager->AddService(new MIC_U::Interface); | 116 | g_manager->AddService(new MIC_U::Interface); |
| 102 | g_manager->AddService(new NDM_U::Interface); | 117 | g_manager->AddService(new NDM_U::Interface); |
| 118 | g_manager->AddService(new NEWS_U::Interface); | ||
| 119 | g_manager->AddService(new NIM_AOC::Interface); | ||
| 103 | g_manager->AddService(new NWM_UDS::Interface); | 120 | g_manager->AddService(new NWM_UDS::Interface); |
| 104 | g_manager->AddService(new PM_APP::Interface); | 121 | g_manager->AddService(new PM_APP::Interface); |
| 105 | g_manager->AddService(new PTM_U::Interface); | 122 | g_manager->AddService(new PTM_U::Interface); |
| 106 | g_manager->AddService(new SOC_U::Interface); | 123 | g_manager->AddService(new SOC_U::Interface); |
| 107 | g_manager->AddService(new SSL_C::Interface); | 124 | g_manager->AddService(new SSL_C::Interface); |
| 108 | 125 | ||
| 109 | NOTICE_LOG(HLE, "initialized OK"); | 126 | LOG_DEBUG(Service, "initialized OK"); |
| 110 | } | 127 | } |
| 111 | 128 | ||
| 112 | /// Shutdown ServiceManager | 129 | /// Shutdown ServiceManager |
| 113 | void Shutdown() { | 130 | void Shutdown() { |
| 114 | delete g_manager; | 131 | delete g_manager; |
| 115 | NOTICE_LOG(HLE, "shutdown OK"); | 132 | LOG_DEBUG(Service, "shutdown OK"); |
| 116 | } | 133 | } |
| 117 | 134 | ||
| 118 | 135 | ||
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 20e7fb4d3..28b4ccd17 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 |
| @@ -10,9 +10,11 @@ | |||
| 10 | #include <string> | 10 | #include <string> |
| 11 | 11 | ||
| 12 | #include "common/common.h" | 12 | #include "common/common.h" |
| 13 | #include "common/string_util.h" | ||
| 13 | #include "core/mem_map.h" | 14 | #include "core/mem_map.h" |
| 14 | 15 | ||
| 15 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| 17 | #include "core/hle/kernel/session.h" | ||
| 16 | #include "core/hle/svc.h" | 18 | #include "core/hle/svc.h" |
| 17 | 19 | ||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -20,30 +22,19 @@ | |||
| 20 | 22 | ||
| 21 | namespace Service { | 23 | namespace Service { |
| 22 | 24 | ||
| 23 | static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) | 25 | static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) |
| 24 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Returns a pointer to the command buffer in kernel memory | ||
| 28 | * @param offset Optional offset into command buffer | ||
| 29 | * @return Pointer to command buffer | ||
| 30 | */ | ||
| 31 | inline static u32* GetCommandBuffer(const int offset=0) { | ||
| 32 | return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||
| 33 | } | ||
| 34 | 26 | ||
| 35 | class Manager; | 27 | class Manager; |
| 36 | 28 | ||
| 37 | /// Interface to a CTROS service | 29 | /// Interface to a CTROS service |
| 38 | class Interface : public Kernel::Object { | 30 | class Interface : public Kernel::Session { |
| 31 | // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be | ||
| 32 | // just something that encapsulates a session and acts as a helper to implement service | ||
| 33 | // processes. | ||
| 34 | |||
| 39 | friend class Manager; | 35 | friend class Manager; |
| 40 | public: | 36 | public: |
| 41 | |||
| 42 | std::string GetName() const override { return GetPortName(); } | 37 | std::string GetName() const override { return GetPortName(); } |
| 43 | std::string GetTypeName() const override { return GetPortName(); } | ||
| 44 | |||
| 45 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } | ||
| 46 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; } | ||
| 47 | 38 | ||
| 48 | typedef void (*Function)(Interface*); | 39 | typedef void (*Function)(Interface*); |
| 49 | 40 | ||
| @@ -63,7 +54,8 @@ public: | |||
| 63 | 54 | ||
| 64 | /// Allocates a new handle for the service | 55 | /// Allocates a new handle for the service |
| 65 | Handle CreateHandle(Kernel::Object *obj) { | 56 | Handle CreateHandle(Kernel::Object *obj) { |
| 66 | Handle handle = Kernel::g_object_pool.Create(obj); | 57 | // TODO(yuriks): Fix error reporting |
| 58 | Handle handle = Kernel::g_handle_table.Create(obj).ValueOr(INVALID_HANDLE); | ||
| 67 | m_handles.push_back(handle); | 59 | m_handles.push_back(handle); |
| 68 | return handle; | 60 | return handle; |
| 69 | } | 61 | } |
| @@ -71,29 +63,28 @@ public: | |||
| 71 | /// Frees a handle from the service | 63 | /// Frees a handle from the service |
| 72 | template <class T> | 64 | template <class T> |
| 73 | void DeleteHandle(const Handle handle) { | 65 | void DeleteHandle(const Handle handle) { |
| 74 | Kernel::g_object_pool.Destroy<T>(handle); | 66 | Kernel::g_handle_table.Close(handle); |
| 75 | m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); | 67 | m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); |
| 76 | } | 68 | } |
| 77 | 69 | ||
| 78 | ResultVal<bool> SyncRequest() override { | 70 | ResultVal<bool> SyncRequest() override { |
| 79 | u32* cmd_buff = GetCommandBuffer(); | 71 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 80 | auto itr = m_functions.find(cmd_buff[0]); | 72 | auto itr = m_functions.find(cmd_buff[0]); |
| 81 | 73 | ||
| 82 | if (itr == m_functions.end()) { | 74 | if (itr == m_functions.end() || itr->second.func == nullptr) { |
| 83 | ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X", | 75 | // Number of params == bits 0-5 + bits 6-11 |
| 84 | GetPortName().c_str(), cmd_buff[0]); | 76 | int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); |
| 85 | 77 | ||
| 86 | // TODO(bunnei): Hack - ignore error | 78 | std::string error = "unknown/unimplemented function '%s': port=%s"; |
| 87 | u32* cmd_buff = Service::GetCommandBuffer(); | 79 | for (int i = 1; i <= num_params; ++i) { |
| 88 | cmd_buff[1] = 0; | 80 | error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); |
| 89 | return MakeResult<bool>(false); | 81 | } |
| 90 | } | 82 | |
| 91 | if (itr->second.func == nullptr) { | 83 | std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; |
| 92 | ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s", | 84 | |
| 93 | GetPortName().c_str(), itr->second.name.c_str()); | 85 | LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str()); |
| 94 | 86 | ||
| 95 | // TODO(bunnei): Hack - ignore error | 87 | // TODO(bunnei): Hack - ignore error |
| 96 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 97 | cmd_buff[1] = 0; | 88 | cmd_buff[1] = 0; |
| 98 | return MakeResult<bool>(false); | 89 | return MakeResult<bool>(false); |
| 99 | } | 90 | } |
| @@ -103,12 +94,6 @@ public: | |||
| 103 | return MakeResult<bool>(false); // TODO: Implement return from actual function | 94 | return MakeResult<bool>(false); // TODO: Implement return from actual function |
| 104 | } | 95 | } |
| 105 | 96 | ||
| 106 | ResultVal<bool> WaitSynchronization() override { | ||
| 107 | // TODO(bunnei): ImplementMe | ||
| 108 | ERROR_LOG(OSHLE, "unimplemented function"); | ||
| 109 | return UnimplementedFunction(ErrorModule::OS); | ||
| 110 | } | ||
| 111 | |||
| 112 | protected: | 97 | protected: |
| 113 | 98 | ||
| 114 | /** | 99 | /** |
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 2f8910468..ef4f9829d 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" |
| @@ -52,7 +52,4 @@ Interface::Interface() { | |||
| 52 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 52 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | Interface::~Interface() { | ||
| 56 | } | ||
| 57 | |||
| 58 | } // namespace | 55 | } // namespace |
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index d5590a683..2edf3b482 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 |
| @@ -14,11 +14,7 @@ namespace SOC_U { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~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 { | 18 | std::string GetPortName() const override { |
| 23 | return "soc:U"; | 19 | return "soc:U"; |
| 24 | } | 20 | } |
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 0e7fa9e3b..25fab1a4f 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" |
| @@ -14,17 +14,17 @@ namespace SRV { | |||
| 14 | static Handle g_event_handle = 0; | 14 | static Handle g_event_handle = 0; |
| 15 | 15 | ||
| 16 | static void Initialize(Service::Interface* self) { | 16 | static void Initialize(Service::Interface* self) { |
| 17 | DEBUG_LOG(OSHLE, "called"); | 17 | LOG_DEBUG(Service_SRV, "called"); |
| 18 | 18 | ||
| 19 | u32* cmd_buff = Service::GetCommandBuffer(); | 19 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 20 | 20 | ||
| 21 | cmd_buff[1] = 0; // No error | 21 | cmd_buff[1] = 0; // No error |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | static void GetProcSemaphore(Service::Interface* self) { | 24 | static void GetProcSemaphore(Service::Interface* self) { |
| 25 | DEBUG_LOG(OSHLE, "called"); | 25 | LOG_TRACE(Service_SRV, "called"); |
| 26 | 26 | ||
| 27 | u32* cmd_buff = Service::GetCommandBuffer(); | 27 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 28 | 28 | ||
| 29 | // TODO(bunnei): Change to a semaphore once these have been implemented | 29 | // TODO(bunnei): Change to a semaphore once these have been implemented |
| 30 | g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); | 30 | g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); |
| @@ -36,16 +36,16 @@ static void GetProcSemaphore(Service::Interface* self) { | |||
| 36 | 36 | ||
| 37 | static void GetServiceHandle(Service::Interface* self) { | 37 | static void GetServiceHandle(Service::Interface* self) { |
| 38 | ResultCode res = RESULT_SUCCESS; | 38 | ResultCode res = RESULT_SUCCESS; |
| 39 | u32* cmd_buff = Service::GetCommandBuffer(); | 39 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 40 | 40 | ||
| 41 | std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); | 41 | std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); |
| 42 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | 42 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |
| 43 | 43 | ||
| 44 | if (nullptr != service) { | 44 | if (nullptr != service) { |
| 45 | cmd_buff[3] = service->GetHandle(); | 45 | cmd_buff[3] = service->GetHandle(); |
| 46 | DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); | 46 | LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); |
| 47 | } else { | 47 | } else { |
| 48 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); | 48 | LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); |
| 49 | res = UnimplementedFunction(ErrorModule::SRV); | 49 | res = UnimplementedFunction(ErrorModule::SRV); |
| 50 | } | 50 | } |
| 51 | cmd_buff[1] = res.raw; | 51 | cmd_buff[1] = res.raw; |
| @@ -68,7 +68,4 @@ Interface::Interface() { | |||
| 68 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 68 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | Interface::~Interface() { | ||
| 72 | } | ||
| 73 | |||
| 74 | } // namespace | 71 | } // namespace |
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 6d5fe5048..653aba5cb 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" |
| @@ -11,21 +11,12 @@ namespace SRV { | |||
| 11 | 11 | ||
| 12 | /// Interface to "srv:" service | 12 | /// Interface to "srv:" service |
| 13 | class Interface : public Service::Interface { | 13 | class Interface : public Service::Interface { |
| 14 | |||
| 15 | public: | 14 | public: |
| 16 | |||
| 17 | Interface(); | 15 | Interface(); |
| 18 | 16 | ||
| 19 | ~Interface(); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Gets the string name used by CTROS for the service | ||
| 23 | * @return Port name of service | ||
| 24 | */ | ||
| 25 | std::string GetPortName() const override { | 17 | std::string GetPortName() const override { |
| 26 | return "srv:"; | 18 | return "srv:"; |
| 27 | } | 19 | } |
| 28 | |||
| 29 | }; | 20 | }; |
| 30 | 21 | ||
| 31 | } // namespace | 22 | } // namespace |
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index 4aa660ecc..360516cdf 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" |
| @@ -25,7 +25,4 @@ Interface::Interface() { | |||
| 25 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 25 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | Interface::~Interface() { | ||
| 29 | } | ||
| 30 | |||
| 31 | } // namespace | 28 | } // namespace |
diff --git a/src/core/hle/service/ssl_c.h b/src/core/hle/service/ssl_c.h index 7b4e7fd8a..58e87c1cb 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 |
| @@ -14,12 +14,8 @@ namespace SSL_C { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~Interface(); | 17 | |
| 18 | /** | 18 | std::string GetPortName() const override { |
| 19 | * Gets the string port name used by CTROS for the service | ||
| 20 | * @return Port name of service | ||
| 21 | */ | ||
| 22 | std::string GetPortName() const { | ||
| 23 | return "ssl:C"; | 19 | return "ssl:C"; |
| 24 | } | 20 | } |
| 25 | }; | 21 | }; |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 87d768856..25944fc68 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> |
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hle/kernel/address_arbiter.h" | 12 | #include "core/hle/kernel/address_arbiter.h" |
| 13 | #include "core/hle/kernel/event.h" | 13 | #include "core/hle/kernel/event.h" |
| 14 | #include "core/hle/kernel/mutex.h" | 14 | #include "core/hle/kernel/mutex.h" |
| 15 | #include "core/hle/kernel/semaphore.h" | ||
| 15 | #include "core/hle/kernel/shared_memory.h" | 16 | #include "core/hle/kernel/shared_memory.h" |
| 16 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 17 | 18 | ||
| @@ -31,7 +32,7 @@ enum ControlMemoryOperation { | |||
| 31 | 32 | ||
| 32 | /// Map application or GSP heap memory | 33 | /// Map application or GSP heap memory |
| 33 | static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { | 34 | static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { |
| 34 | DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", | 35 | LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", |
| 35 | operation, addr0, addr1, size, permissions); | 36 | operation, addr0, addr1, size, permissions); |
| 36 | 37 | ||
| 37 | switch (operation) { | 38 | switch (operation) { |
| @@ -43,19 +44,19 @@ static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, | |||
| 43 | 44 | ||
| 44 | // Map GSP heap memory | 45 | // Map GSP heap memory |
| 45 | case MEMORY_OPERATION_GSP_HEAP: | 46 | case MEMORY_OPERATION_GSP_HEAP: |
| 46 | *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions); | 47 | *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions); |
| 47 | break; | 48 | break; |
| 48 | 49 | ||
| 49 | // Unknown ControlMemory operation | 50 | // Unknown ControlMemory operation |
| 50 | default: | 51 | default: |
| 51 | ERROR_LOG(SVC, "unknown operation=0x%08X", operation); | 52 | LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); |
| 52 | } | 53 | } |
| 53 | return 0; | 54 | return 0; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | /// Maps a memory block to specified address | 57 | /// Maps a memory block to specified address |
| 57 | static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { | 58 | static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { |
| 58 | DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", | 59 | LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", |
| 59 | handle, addr, permissions, other_permissions); | 60 | handle, addr, permissions, other_permissions); |
| 60 | 61 | ||
| 61 | Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); | 62 | Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); |
| @@ -63,12 +64,16 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other | |||
| 63 | case Kernel::MemoryPermission::Read: | 64 | case Kernel::MemoryPermission::Read: |
| 64 | case Kernel::MemoryPermission::Write: | 65 | case Kernel::MemoryPermission::Write: |
| 65 | 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: | ||
| 66 | case Kernel::MemoryPermission::DontCare: | 71 | case Kernel::MemoryPermission::DontCare: |
| 67 | Kernel::MapSharedMemory(handle, addr, permissions_type, | 72 | Kernel::MapSharedMemory(handle, addr, permissions_type, |
| 68 | static_cast<Kernel::MemoryPermission>(other_permissions)); | 73 | static_cast<Kernel::MemoryPermission>(other_permissions)); |
| 69 | break; | 74 | break; |
| 70 | default: | 75 | default: |
| 71 | ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions); | 76 | LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); |
| 72 | } | 77 | } |
| 73 | return 0; | 78 | return 0; |
| 74 | } | 79 | } |
| @@ -77,7 +82,7 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other | |||
| 77 | static Result ConnectToPort(Handle* out, const char* port_name) { | 82 | static Result ConnectToPort(Handle* out, const char* port_name) { |
| 78 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | 83 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |
| 79 | 84 | ||
| 80 | DEBUG_LOG(SVC, "called port_name=%s", port_name); | 85 | LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); |
| 81 | _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); | 86 | _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); |
| 82 | 87 | ||
| 83 | *out = service->GetHandle(); | 88 | *out = service->GetHandle(); |
| @@ -87,17 +92,14 @@ static Result ConnectToPort(Handle* out, const char* port_name) { | |||
| 87 | 92 | ||
| 88 | /// Synchronize to an OS service | 93 | /// Synchronize to an OS service |
| 89 | static Result SendSyncRequest(Handle handle) { | 94 | static Result SendSyncRequest(Handle handle) { |
| 90 | // 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_handle_table.Get<Kernel::Session>(handle); |
| 91 | // so we are forced to use GetFast and manually verify the handle. | 96 | if (session == nullptr) { |
| 92 | if (!Kernel::g_object_pool.IsValid(handle)) { | ||
| 93 | return InvalidHandle(ErrorModule::Kernel).raw; | 97 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 94 | } | 98 | } |
| 95 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); | ||
| 96 | 99 | ||
| 97 | _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()); |
| 98 | DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str()); | ||
| 99 | 101 | ||
| 100 | ResultVal<bool> wait = object->SyncRequest(); | 102 | ResultVal<bool> wait = session->SyncRequest(); |
| 101 | if (wait.Succeeded() && *wait) { | 103 | if (wait.Succeeded() && *wait) { |
| 102 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? | 104 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? |
| 103 | } | 105 | } |
| @@ -108,7 +110,7 @@ static Result SendSyncRequest(Handle handle) { | |||
| 108 | /// Close a handle | 110 | /// Close a handle |
| 109 | static Result CloseHandle(Handle handle) { | 111 | static Result CloseHandle(Handle handle) { |
| 110 | // ImplementMe | 112 | // ImplementMe |
| 111 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); | 113 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); |
| 112 | return 0; | 114 | return 0; |
| 113 | } | 115 | } |
| 114 | 116 | ||
| @@ -117,13 +119,11 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 117 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this | 119 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this |
| 118 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated | 120 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated |
| 119 | 121 | ||
| 120 | if (!Kernel::g_object_pool.IsValid(handle)) { | 122 | Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handle); |
| 123 | if (object == nullptr) | ||
| 121 | return InvalidHandle(ErrorModule::Kernel).raw; | 124 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 122 | } | ||
| 123 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); | ||
| 124 | _dbg_assert_(KERNEL, object != nullptr); | ||
| 125 | 125 | ||
| 126 | DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), | 126 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), |
| 127 | object->GetName().c_str(), nano_seconds); | 127 | object->GetName().c_str(), nano_seconds); |
| 128 | 128 | ||
| 129 | ResultVal<bool> wait = object->WaitSynchronization(); | 129 | ResultVal<bool> wait = object->WaitSynchronization(); |
| @@ -143,17 +143,16 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, | |||
| 143 | bool unlock_all = true; | 143 | bool unlock_all = true; |
| 144 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated | 144 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated |
| 145 | 145 | ||
| 146 | DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", | 146 | LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", |
| 147 | handle_count, (wait_all ? "true" : "false"), nano_seconds); | 147 | handle_count, (wait_all ? "true" : "false"), nano_seconds); |
| 148 | 148 | ||
| 149 | // Iterate through each handle, synchronize kernel object | 149 | // Iterate through each handle, synchronize kernel object |
| 150 | for (s32 i = 0; i < handle_count; i++) { | 150 | for (s32 i = 0; i < handle_count; i++) { |
| 151 | if (!Kernel::g_object_pool.IsValid(handles[i])) { | 151 | Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handles[i]); |
| 152 | if (object == nullptr) | ||
| 152 | return InvalidHandle(ErrorModule::Kernel).raw; | 153 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 153 | } | ||
| 154 | Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]); | ||
| 155 | 154 | ||
| 156 | DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), | 155 | LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), |
| 157 | object->GetName().c_str()); | 156 | object->GetName().c_str()); |
| 158 | 157 | ||
| 159 | // TODO(yuriks): Verify how the real function behaves when an error happens here | 158 | // TODO(yuriks): Verify how the real function behaves when an error happens here |
| @@ -181,7 +180,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, | |||
| 181 | 180 | ||
| 182 | /// Create an address arbiter (to allocate access to shared resources) | 181 | /// Create an address arbiter (to allocate access to shared resources) |
| 183 | static Result CreateAddressArbiter(u32* arbiter) { | 182 | static Result CreateAddressArbiter(u32* arbiter) { |
| 184 | DEBUG_LOG(SVC, "called"); | 183 | LOG_TRACE(Kernel_SVC, "called"); |
| 185 | Handle handle = Kernel::CreateAddressArbiter(); | 184 | Handle handle = Kernel::CreateAddressArbiter(); |
| 186 | *arbiter = handle; | 185 | *arbiter = handle; |
| 187 | return 0; | 186 | return 0; |
| @@ -189,13 +188,15 @@ static Result CreateAddressArbiter(u32* arbiter) { | |||
| 189 | 188 | ||
| 190 | /// Arbitrate address | 189 | /// Arbitrate address |
| 191 | static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { | 190 | static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { |
| 191 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter, | ||
| 192 | address, type, value); | ||
| 192 | return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), | 193 | return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), |
| 193 | address, value).raw; | 194 | address, value).raw; |
| 194 | } | 195 | } |
| 195 | 196 | ||
| 196 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 197 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 197 | static void OutputDebugString(const char* string) { | 198 | static void OutputDebugString(const char* string) { |
| 198 | OS_LOG(SVC, "%s", string); | 199 | LOG_DEBUG(Debug_Emulated, "%s", string); |
| 199 | } | 200 | } |
| 200 | 201 | ||
| 201 | /// Get resource limit | 202 | /// Get resource limit |
| @@ -204,14 +205,14 @@ static Result GetResourceLimit(Handle* resource_limit, Handle process) { | |||
| 204 | // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for | 205 | // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for |
| 205 | // the current KThread. | 206 | // the current KThread. |
| 206 | *resource_limit = 0xDEADBEEF; | 207 | *resource_limit = 0xDEADBEEF; |
| 207 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process); | 208 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process); |
| 208 | return 0; | 209 | return 0; |
| 209 | } | 210 | } |
| 210 | 211 | ||
| 211 | /// Get resource limit current values | 212 | /// Get resource limit current values |
| 212 | static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, | 213 | static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, |
| 213 | s32 name_count) { | 214 | s32 name_count) { |
| 214 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", | 215 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", |
| 215 | resource_limit, names, name_count); | 216 | resource_limit, names, name_count); |
| 216 | Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now | 217 | Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now |
| 217 | return 0; | 218 | return 0; |
| @@ -232,7 +233,7 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top | |||
| 232 | 233 | ||
| 233 | Core::g_app_core->SetReg(1, thread); | 234 | Core::g_app_core->SetReg(1, thread); |
| 234 | 235 | ||
| 235 | DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 236 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| 236 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, | 237 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, |
| 237 | name.c_str(), arg, stack_top, priority, processor_id, thread); | 238 | name.c_str(), arg, stack_top, priority, processor_id, thread); |
| 238 | 239 | ||
| @@ -243,7 +244,7 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top | |||
| 243 | static u32 ExitThread() { | 244 | static u32 ExitThread() { |
| 244 | Handle thread = Kernel::GetCurrentThreadHandle(); | 245 | Handle thread = Kernel::GetCurrentThreadHandle(); |
| 245 | 246 | ||
| 246 | DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C | 247 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C |
| 247 | 248 | ||
| 248 | Kernel::StopThread(thread, __func__); | 249 | Kernel::StopThread(thread, __func__); |
| 249 | HLE::Reschedule(__func__); | 250 | HLE::Reschedule(__func__); |
| @@ -267,70 +268,83 @@ static Result SetThreadPriority(Handle handle, s32 priority) { | |||
| 267 | /// Create a mutex | 268 | /// Create a mutex |
| 268 | static Result CreateMutex(Handle* mutex, u32 initial_locked) { | 269 | static Result CreateMutex(Handle* mutex, u32 initial_locked) { |
| 269 | *mutex = Kernel::CreateMutex((initial_locked != 0)); | 270 | *mutex = Kernel::CreateMutex((initial_locked != 0)); |
| 270 | DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X", | 271 | LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", |
| 271 | initial_locked ? "true" : "false", *mutex); | 272 | initial_locked ? "true" : "false", *mutex); |
| 272 | return 0; | 273 | return 0; |
| 273 | } | 274 | } |
| 274 | 275 | ||
| 275 | /// Release a mutex | 276 | /// Release a mutex |
| 276 | static Result ReleaseMutex(Handle handle) { | 277 | static Result ReleaseMutex(Handle handle) { |
| 277 | DEBUG_LOG(SVC, "called handle=0x%08X", handle); | 278 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle); |
| 278 | ResultCode res = Kernel::ReleaseMutex(handle); | 279 | ResultCode res = Kernel::ReleaseMutex(handle); |
| 279 | return res.raw; | 280 | return res.raw; |
| 280 | } | 281 | } |
| 281 | 282 | ||
| 282 | /// Get current thread ID | 283 | /// Get the ID for the specified thread. |
| 283 | static Result GetThreadId(u32* thread_id, Handle thread) { | 284 | static Result GetThreadId(u32* thread_id, Handle handle) { |
| 284 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread); | 285 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); |
| 285 | return 0; | 286 | ResultCode result = Kernel::GetThreadId(thread_id, handle); |
| 287 | return result.raw; | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Creates a semaphore | ||
| 291 | static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) { | ||
| 292 | ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count); | ||
| 293 | LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X", | ||
| 294 | initial_count, max_count, *semaphore); | ||
| 295 | return res.raw; | ||
| 296 | } | ||
| 297 | |||
| 298 | /// Releases a certain number of slots in a semaphore | ||
| 299 | static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) { | ||
| 300 | LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore); | ||
| 301 | ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count); | ||
| 302 | return res.raw; | ||
| 286 | } | 303 | } |
| 287 | 304 | ||
| 288 | /// Query memory | 305 | /// Query memory |
| 289 | static Result QueryMemory(void* info, void* out, u32 addr) { | 306 | static Result QueryMemory(void* info, void* out, u32 addr) { |
| 290 | ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); | 307 | LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); |
| 291 | return 0; | 308 | return 0; |
| 292 | } | 309 | } |
| 293 | 310 | ||
| 294 | /// Create an event | 311 | /// Create an event |
| 295 | static Result CreateEvent(Handle* evt, u32 reset_type) { | 312 | static Result CreateEvent(Handle* evt, u32 reset_type) { |
| 296 | *evt = Kernel::CreateEvent((ResetType)reset_type); | 313 | *evt = Kernel::CreateEvent((ResetType)reset_type); |
| 297 | DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X", | 314 | LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", |
| 298 | reset_type, *evt); | 315 | reset_type, *evt); |
| 299 | return 0; | 316 | return 0; |
| 300 | } | 317 | } |
| 301 | 318 | ||
| 302 | /// Duplicates a kernel handle | 319 | /// Duplicates a kernel handle |
| 303 | static Result DuplicateHandle(Handle* out, Handle handle) { | 320 | static Result DuplicateHandle(Handle* out, Handle handle) { |
| 304 | DEBUG_LOG(SVC, "called handle=0x%08X", handle); | 321 | ResultVal<Handle> out_h = Kernel::g_handle_table.Duplicate(handle); |
| 305 | 322 | if (out_h.Succeeded()) { | |
| 306 | // Translate kernel handles -> real handles | 323 | *out = *out_h; |
| 307 | if (handle == Kernel::CurrentThread) { | 324 | LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out); |
| 308 | handle = Kernel::GetCurrentThreadHandle(); | ||
| 309 | } | 325 | } |
| 310 | _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess), | 326 | return out_h.Code().raw; |
| 311 | "(UNIMPLEMENTED) process handle duplication!"); | ||
| 312 | |||
| 313 | // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate. | ||
| 314 | *out = handle; | ||
| 315 | |||
| 316 | return 0; | ||
| 317 | } | 327 | } |
| 318 | 328 | ||
| 319 | /// Signals an event | 329 | /// Signals an event |
| 320 | static Result SignalEvent(Handle evt) { | 330 | static Result SignalEvent(Handle evt) { |
| 321 | DEBUG_LOG(SVC, "called event=0x%08X", evt); | 331 | LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); |
| 322 | return Kernel::SignalEvent(evt).raw; | 332 | return Kernel::SignalEvent(evt).raw; |
| 323 | } | 333 | } |
| 324 | 334 | ||
| 325 | /// Clears an event | 335 | /// Clears an event |
| 326 | static Result ClearEvent(Handle evt) { | 336 | static Result ClearEvent(Handle evt) { |
| 327 | DEBUG_LOG(SVC, "called event=0x%08X", evt); | 337 | LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); |
| 328 | return Kernel::ClearEvent(evt).raw; | 338 | return Kernel::ClearEvent(evt).raw; |
| 329 | } | 339 | } |
| 330 | 340 | ||
| 331 | /// Sleep the current thread | 341 | /// Sleep the current thread |
| 332 | static void SleepThread(s64 nanoseconds) { | 342 | static void SleepThread(s64 nanoseconds) { |
| 333 | DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds); | 343 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| 344 | |||
| 345 | // Sleep current thread and check for next thread to schedule | ||
| 346 | Kernel::WaitCurrentThread(WAITTYPE_SLEEP); | ||
| 347 | HLE::Reschedule(__func__); | ||
| 334 | } | 348 | } |
| 335 | 349 | ||
| 336 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 350 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| @@ -360,8 +374,8 @@ const HLE::FunctionDef SVC_Table[] = { | |||
| 360 | {0x12, nullptr, "Run"}, | 374 | {0x12, nullptr, "Run"}, |
| 361 | {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, | 375 | {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, |
| 362 | {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, | 376 | {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, |
| 363 | {0x15, nullptr, "CreateSemaphore"}, | 377 | {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"}, |
| 364 | {0x16, nullptr, "ReleaseSemaphore"}, | 378 | {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"}, |
| 365 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, | 379 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, |
| 366 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, | 380 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, |
| 367 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, | 381 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, |
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 |