aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/config_mem.cpp5
-rw-r--r--src/core/hle/coprocessor.cpp2
-rw-r--r--src/core/hle/function_wrappers.h18
-rw-r--r--src/core/hle/hle.cpp19
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp21
-rw-r--r--src/core/hle/kernel/address_arbiter.h4
-rw-r--r--src/core/hle/kernel/archive.cpp436
-rw-r--r--src/core/hle/kernel/archive.h70
-rw-r--r--src/core/hle/kernel/event.cpp47
-rw-r--r--src/core/hle/kernel/event.h14
-rw-r--r--src/core/hle/kernel/kernel.cpp29
-rw-r--r--src/core/hle/kernel/kernel.h83
-rw-r--r--src/core/hle/kernel/mutex.cpp124
-rw-r--r--src/core/hle/kernel/mutex.h11
-rw-r--r--src/core/hle/kernel/semaphore.cpp94
-rw-r--r--src/core/hle/kernel/semaphore.h32
-rw-r--r--src/core/hle/kernel/session.h58
-rw-r--r--src/core/hle/kernel/shared_memory.cpp53
-rw-r--r--src/core/hle/kernel/shared_memory.h7
-rw-r--r--src/core/hle/kernel/thread.cpp215
-rw-r--r--src/core/hle/kernel/thread.h28
-rw-r--r--src/core/hle/result.h402
-rw-r--r--src/core/hle/service/ac_u.cpp26
-rw-r--r--src/core/hle/service/ac_u.h4
-rw-r--r--src/core/hle/service/am_app.cpp24
-rw-r--r--src/core/hle/service/am_app.h27
-rw-r--r--src/core/hle/service/am_net.cpp47
-rw-r--r--src/core/hle/service/am_net.h27
-rw-r--r--src/core/hle/service/apt_u.cpp203
-rw-r--r--src/core/hle/service/apt_u.h4
-rw-r--r--src/core/hle/service/boss_u.cpp28
-rw-r--r--src/core/hle/service/boss_u.h27
-rw-r--r--src/core/hle/service/cecd_u.cpp24
-rw-r--r--src/core/hle/service/cecd_u.h27
-rw-r--r--src/core/hle/service/cfg_i.cpp59
-rw-r--r--src/core/hle/service/cfg_i.h27
-rw-r--r--src/core/hle/service/cfg_u.cpp92
-rw-r--r--src/core/hle/service/cfg_u.h2
-rw-r--r--src/core/hle/service/csnd_snd.cpp39
-rw-r--r--src/core/hle/service/csnd_snd.h27
-rw-r--r--src/core/hle/service/dsp_dsp.cpp194
-rw-r--r--src/core/hle/service/dsp_dsp.h4
-rw-r--r--src/core/hle/service/err_f.cpp4
-rw-r--r--src/core/hle/service/err_f.h4
-rw-r--r--src/core/hle/service/frd_u.cpp35
-rw-r--r--src/core/hle/service/frd_u.h27
-rw-r--r--src/core/hle/service/fs/archive.cpp431
-rw-r--r--src/core/hle/service/fs/archive.h125
-rw-r--r--src/core/hle/service/fs/fs_user.cpp529
-rw-r--r--src/core/hle/service/fs/fs_user.h (renamed from src/core/hle/service/fs_user.h)12
-rw-r--r--src/core/hle/service/fs_user.cpp354
-rw-r--r--src/core/hle/service/gsp_gpu.cpp80
-rw-r--r--src/core/hle/service/hid_user.cpp17
-rw-r--r--src/core/hle/service/hid_user.h6
-rw-r--r--src/core/hle/service/ir_rst.cpp36
-rw-r--r--src/core/hle/service/ir_rst.h27
-rw-r--r--src/core/hle/service/ir_u.cpp45
-rw-r--r--src/core/hle/service/ir_u.h27
-rw-r--r--src/core/hle/service/ldr_ro.cpp28
-rw-r--r--src/core/hle/service/ldr_ro.h27
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/mic_u.h2
-rw-r--r--src/core/hle/service/nim_aoc.cpp31
-rw-r--r--src/core/hle/service/nim_aoc.h27
-rw-r--r--src/core/hle/service/nwm_uds.h2
-rw-r--r--src/core/hle/service/pm_app.cpp35
-rw-r--r--src/core/hle/service/pm_app.h27
-rw-r--r--src/core/hle/service/ptm_u.cpp98
-rw-r--r--src/core/hle/service/ptm_u.h2
-rw-r--r--src/core/hle/service/service.cpp36
-rw-r--r--src/core/hle/service/service.h74
-rw-r--r--src/core/hle/service/soc_u.h2
-rw-r--r--src/core/hle/service/srv.cpp28
-rw-r--r--src/core/hle/svc.cpp215
-rw-r--r--src/core/hle/svc.h2
76 files changed, 3583 insertions, 1500 deletions
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index a45e6142..d8ba9e6c 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -1,8 +1,9 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common_types.h"
+#include "common/log.h"
#include "core/hle/config_mem.h"
@@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
break;
default:
- ERROR_LOG(HLE, "unknown addr=0x%08X", addr);
+ LOG_ERROR(Kernel, "unknown addr=0x%08X", addr);
}
}
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp
index 1eb33eb8..e34229a5 100644
--- a/src/core/hle/coprocessor.cpp
+++ b/src/core/hle/coprocessor.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/hle/coprocessor.h"
#include "core/hle/hle.h"
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 55eaf062..b44479b2 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -50,7 +50,7 @@ template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
s32 param_1 = 0;
- s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
+ s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
(PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
Core::g_app_core->SetReg(1, (u32)param_1);
FuncReturn(retval);
@@ -103,7 +103,7 @@ template<s32 func(void*)> void Wrap() {
}
template<s32 func(s64*, u32, void*, s32)> void Wrap(){
- FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
+ FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
(s32)PARAM(3)));
}
@@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32*, s32, s32)> void Wrap() {
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
+template<s32 func(s32*, u32, s32)> void Wrap() {
+ s32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index b03894ad..cc3d5255 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <vector>
@@ -8,6 +8,7 @@
#include "core/hle/hle.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,7 +21,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n
const FunctionDef* GetSVCInfo(u32 opcode) {
u32 func_num = opcode & 0xFFFFFF; // 8 bits
if (func_num > 0xFF) {
- ERROR_LOG(HLE,"unknown svc=0x%02X", func_num);
+ LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
return nullptr;
}
return &g_module_db[0].func_table[func_num];
@@ -35,14 +36,12 @@ void CallSVC(u32 opcode) {
if (info->func) {
info->func();
} else {
- ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str());
+ LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
}
}
void Reschedule(const char *reason) {
-#ifdef _DEBUG
- _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
-#endif
+ _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
Core::g_app_core->PrepareReschedule();
g_reschedule = true;
}
@@ -58,18 +57,20 @@ void RegisterAllModules() {
void Init() {
Service::Init();
-
+ Service::FS::ArchiveInit();
+
RegisterAllModules();
- NOTICE_LOG(HLE, "initialized OK");
+ LOG_DEBUG(Kernel, "initialized OK");
}
void Shutdown() {
+ Service::FS::ArchiveShutdown();
Service::Shutdown();
g_module_db.clear();
- NOTICE_LOG(HLE, "shutdown OK");
+ LOG_DEBUG(Kernel, "shutdown OK");
}
} // namespace
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index bf4d8457..4ab258c6 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 2b21657d..9a921108 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -24,23 +24,12 @@ public:
Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; }
std::string name; ///< Name of address arbiter object (optional)
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
@@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
HLE::Reschedule(__func__);
}
break;
default:
- ERROR_LOG(KERNEL, "unknown type=%d", type);
- return -1;
+ LOG_ERROR(Kernel, "unknown type=%d", type);
+ return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Create an address arbiter
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index a483fe46..8a5fb10b 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -11,7 +11,7 @@
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
-// semphores.
+// semphores.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
};
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
/// Create an address arbiter
Handle CreateAddressArbiter(const std::string& name = "Unknown");
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
deleted file mode 100644
index 764082d7..00000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,436 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-#include "common/math_util.h"
-
-#include "core/file_sys/archive.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/file_sys/directory.h"
-#include "core/hle/service/service.h"
-#include "core/hle/kernel/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-// Command to access archive file
-enum class FileCommand : u32 {
- Dummy1 = 0x000100C6,
- Control = 0x040100C4,
- OpenSubFile = 0x08010100,
- Read = 0x080200C2,
- Write = 0x08030102,
- GetSize = 0x08040000,
- SetSize = 0x08050080,
- GetAttributes = 0x08060000,
- SetAttributes = 0x08070040,
- Close = 0x08080000,
- Flush = 0x08090000,
-};
-
-// Command to access directory
-enum class DirectoryCommand : u32 {
- Dummy1 = 0x000100C6,
- Control = 0x040100C4,
- Read = 0x08010042,
- Close = 0x08020000,
-};
-
-class Archive : public Object {
-public:
- std::string GetTypeName() const override { return "Archive"; }
- std::string GetName() const override { return name; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
-
- std::string name; ///< Name of archive (optional)
- FileSys::Archive* backend; ///< Archive backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
-
- switch (cmd) {
- // Read from archive...
- case FileCommand::Read:
- {
- u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- u32 length = cmd_buff[3];
- u32 address = cmd_buff[5];
-
- // Number of bytes read
- cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
- break;
- }
- // Write to archive...
- case FileCommand::Write:
- {
- u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- u32 length = cmd_buff[3];
- u32 flush = cmd_buff[4];
- u32 address = cmd_buff[6];
-
- // Number of bytes written
- cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
- break;
- }
- case FileCommand::GetSize:
- {
- u64 filesize = (u64) backend->GetSize();
- cmd_buff[2] = (u32) filesize; // Lower word
- cmd_buff[3] = (u32) (filesize >> 32); // Upper word
- break;
- }
- case FileCommand::SetSize:
- {
- backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
- break;
- }
- case FileCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<Archive>(GetHandle());
- CloseArchive(backend->GetIdCode());
- break;
- }
- // Unknown command...
- default:
- {
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- return -1;
- }
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class File : public Object {
-public:
- std::string GetTypeName() const override { return "File"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
- Kernel::HandleType GetHandleType() const override { return HandleType::File; }
-
- std::string path; ///< Path of the file
- std::unique_ptr<FileSys::File> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
- switch (cmd) {
-
- // Read from file...
- case FileCommand::Read:
- {
- u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
- u32 length = cmd_buff[3];
- u32 address = cmd_buff[5];
- DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
- GetTypeName().c_str(), GetName().c_str(), offset, length, address);
- cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
- break;
- }
-
- // Write to file...
- case FileCommand::Write:
- {
- u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
- u32 length = cmd_buff[3];
- u32 flush = cmd_buff[4];
- u32 address = cmd_buff[6];
- DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
- GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
- cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
- break;
- }
-
- case FileCommand::GetSize:
- {
- DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
- u64 size = backend->GetSize();
- cmd_buff[2] = (u32)size;
- cmd_buff[3] = size >> 32;
- break;
- }
-
- case FileCommand::SetSize:
- {
- u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size);
- backend->SetSize(size);
- break;
- }
-
- case FileCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<File>(GetHandle());
- break;
- }
-
- // Unknown command...
- default:
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class Directory : public Object {
-public:
- std::string GetTypeName() const override { return "Directory"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
-
- std::string path; ///< Path of the directory
- std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
- switch (cmd) {
-
- // Read from directory...
- case DirectoryCommand::Read:
- {
- u32 count = cmd_buff[1];
- u32 address = cmd_buff[3];
- FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
- DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
-
- // Number of entries actually read
- cmd_buff[2] = backend->Read(count, entries);
- break;
- }
-
- case DirectoryCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<Directory>(GetHandle());
- break;
- }
-
- // Unknown command...
- default:
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code) {
- auto itr = g_archive_map.find(id_code);
- if (itr == g_archive_map.end()) {
- return 0;
- }
- return itr->second;
-}
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code) {
- if (1 != g_archive_map.erase(id_code)) {
- ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
- return -1;
- }
-
- INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
- return 0;
-}
-
-/**
- * Mounts an archive
- * @param archive Pointer to the archive to mount
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result MountArchive(Archive* archive) {
- FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
- if (0 != OpenArchive(id_code)) {
- ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
- return -1;
- }
- g_archive_map[id_code] = archive->GetHandle();
- INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
- return 0;
-}
-
-/**
- * Creates an Archive
- * @param handle Handle to newly created archive object
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Newly created Archive object
- */
-Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
- Archive* archive = new Archive;
- handle = Kernel::g_object_pool.Create(archive);
- archive->name = name;
- archive->backend = backend;
-
- MountArchive(archive);
-
- return archive;
-}
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
- Handle handle;
- CreateArchive(handle, backend, name);
- return handle;
-}
-
-/**
- * Open a File from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the File inside of the Archive
- * @param mode Mode under which to open the File
- * @return Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
- File* file = new File;
- Handle handle = Kernel::g_object_pool.Create(file);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- file->path = path;
- file->backend = archive->backend->OpenFile(path, mode);
-
- if (!file->backend)
- return 0;
-
- return handle;
-}
-
-/**
- * Create a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- if (archive == nullptr)
- return -1;
- if (archive->backend->CreateDirectory(path))
- return 0;
- return -1;
-}
-
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Directory* directory = new Directory;
- Handle handle = Kernel::g_object_pool.Create(directory);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- directory->path = path;
- directory->backend = archive->backend->OpenDirectory(path);
-
- return handle;
-}
-
-/// Initialize archives
-void ArchiveInit() {
- g_archive_map.clear();
-
- // TODO(Link Mauve): Add the other archive types (see here for the known types:
- // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
- // archive type is SDMC, so it is the only one getting exposed.
-
- std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
- auto archive = new FileSys::Archive_SDMC(sdmc_directory);
- if (archive->Initialize())
- CreateArchive(archive, "SDMC");
- else
- ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
-}
-
-/// Shutdown archives
-void ArchiveShutdown() {
- g_archive_map.clear();
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
deleted file mode 100644
index 0230996b..00000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-#include "core/hle/kernel/kernel.h"
-#include "core/file_sys/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return true if it worked fine
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
-
-/**
- * Open a File from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the File inside of the Archive
- * @param mode Mode under which to open the File
- * @return Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode);
-
-/**
- * Create a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Whether creation of directory succeeded
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/// Initialize archives
-void ArchiveInit();
-
-/// Shutdown archives
-void ArchiveShutdown();
-
-} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 45ed79be..28808020 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <algorithm>
@@ -30,13 +30,8 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
std::string name; ///< Name of event (optional)
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- *wait = locked;
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = locked;
if (locked) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
@@ -47,7 +42,7 @@ public:
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
locked = true;
}
- return 0;
+ return MakeResult<bool>(wait);
}
};
@@ -57,12 +52,12 @@ public:
* @param permanent_locked Boolean permanent locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
* @param locked Boolean locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetEventLocked(const Handle handle, const bool locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = locked;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) {
* @param handle Handle to event to signal
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SignalEvent(const Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Resume threads waiting for event to signal
bool event_caught = false;
for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
ResumeThreadFromWait( evt->waiting_threads[i]);
- // If any thread is signalled awake by this event, assume the event was "caught" and reset
+ // If any thread is signalled awake by this event, assume the event was "caught" and reset
// the event. This will result in the next thread waiting on the event to block. Otherwise,
// the event will not be reset, and the next thread to call WaitSynchronization on it will
// not block. Not sure if this is correct behavior, but it seems to work.
@@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) {
if (!evt->permanent_locked) {
evt->locked = event_caught;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) {
* @param handle Handle to event to clear
* @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode ClearEvent(Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = true;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index c39b3318..73aec4e7 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -15,31 +15,27 @@ namespace Kernel {
* Changes whether an event is locked or not
* @param handle Handle to event to change
* @param locked Boolean locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked);
+ResultCode SetEventLocked(const Handle handle, const bool locked);
/**
* Hackish function to set an events permanent lock state, used to pass through synch blocks
* @param handle Handle to event to change
* @param permanent_locked Boolean permanent locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked);
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
/**
* Signals an event
* @param handle Handle to event to signal
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle);
+ResultCode SignalEvent(const Handle handle);
/**
* Clears an event
* @param handle Handle to event to clear
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle);
+ResultCode ClearEvent(Handle handle);
/**
* Creates an event
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 88cbc1af..6a690e91 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,18 +1,20 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
+
+#include <algorithm>
#include "common/common.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/archive.h"
namespace Kernel {
Handle g_main_thread = 0;
ObjectPool g_object_pool;
+u64 g_program_id = 0;
ObjectPool::ObjectPool() {
next_id = INITIAL_NEXT_ID;
@@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
return i + HANDLE_OFFSET;
}
}
- ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
+ LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use.");
return 0;
}
-bool ObjectPool::IsValid(Handle handle) {
+bool ObjectPool::IsValid(Handle handle) const {
int index = handle - HANDLE_OFFSET;
if (index < 0)
return false;
@@ -60,7 +62,7 @@ void ObjectPool::Clear() {
Object* &ObjectPool::operator [](Handle handle)
{
- _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
+ _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
return pool[handle - HANDLE_OFFSET];
}
@@ -68,37 +70,30 @@ void ObjectPool::List() {
for (int i = 0; i < MAX_COUNT; i++) {
if (occupied[i]) {
if (pool[i]) {
- INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
+ LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
pool[i]->GetName().c_str());
}
}
}
}
-int ObjectPool::GetCount() {
- int count = 0;
- for (int i = 0; i < MAX_COUNT; i++) {
- if (occupied[i])
- count++;
- }
- return count;
+int ObjectPool::GetCount() const {
+ return std::count(occupied.begin(), occupied.end(), true);
}
Object* ObjectPool::CreateByIDType(int type) {
- ERROR_LOG(COMMON, "Unimplemented: %d.", type);
+ LOG_ERROR(Kernel, "Unimplemented: %d.", type);
return nullptr;
}
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
- Kernel::ArchiveInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
- Kernel::ArchiveShutdown();
g_object_pool.Clear(); // Free all kernel objects
}
@@ -109,8 +104,6 @@ void Shutdown() {
* @return True on success, otherwise false
*/
bool LoadExec(u32 entry_point) {
- Init();
-
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 867d1b89..7123485b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,12 +1,13 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include "common/common.h"
+#include "core/hle/result.h"
typedef u32 Handle;
typedef s32 Result;
@@ -21,7 +22,7 @@ enum KernelHandle {
enum class HandleType : u32 {
Unknown = 0,
Port = 1,
- Service = 2,
+ Session = 2,
Event = 3,
Mutex = 4,
SharedMemory = 5,
@@ -29,12 +30,9 @@ enum class HandleType : u32 {
Thread = 7,
Process = 8,
AddressArbiter = 9,
- File = 10,
- Semaphore = 11,
- Archive = 12,
- Directory = 13,
+ Semaphore = 10,
};
-
+
enum {
DEFAULT_STACK_SIZE = 0x4000,
};
@@ -52,21 +50,13 @@ public:
virtual Kernel::HandleType GetHandleType() const = 0;
/**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
+ * Wait for kernel object to synchronize.
+ * @return True if the current thread should wait as a result of the wait
*/
- virtual Result SyncRequest(bool* wait) {
- ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
- return -1;
+ virtual ResultVal<bool> WaitSynchronization() {
+ LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
+ return UnimplementedFunction(ErrorModule::Kernel);
}
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- virtual Result WaitSynchronization(bool* wait) = 0;
};
class ObjectPool : NonCopyable {
@@ -80,38 +70,29 @@ public:
static Object* CreateByIDType(int type);
template <class T>
- u32 Destroy(Handle handle) {
- u32 error;
- if (Get<T>(handle, error)) {
+ void Destroy(Handle handle) {
+ if (Get<T>(handle)) {
occupied[handle - HANDLE_OFFSET] = false;
delete pool[handle - HANDLE_OFFSET];
}
- return error;
- };
+ }
- bool IsValid(Handle handle);
+ bool IsValid(Handle handle) const;
template <class T>
- T* Get(Handle handle, u32& outError) {
+ T* Get(Handle handle) {
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
- // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
- if (handle != 0 && (u32)handle != 0x80020001) {
- WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if (handle != 0) {
+ LOG_ERROR(Kernel, "Bad object handle %08x", handle);
}
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ return nullptr;
} else {
- // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
- // it just acted as a static case and everything worked. This means that we will never
- // see the Wrong type object error below, but we'll just have to live with that danger.
- T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
- if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
- WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ Object* t = pool[handle - HANDLE_OFFSET];
+ if (t->GetHandleType() != T::GetStaticHandleType()) {
+ LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
+ return nullptr;
}
- outError = 0;//SCE_KERNEL_ERROR_OK;
- return t;
+ return static_cast<T*>(t);
}
}
@@ -119,7 +100,7 @@ public:
template <class T>
T *GetFast(Handle handle) {
const Handle realHandle = handle - HANDLE_OFFSET;
- _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
+ _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
return static_cast<T*>(pool[realHandle]);
}
@@ -139,9 +120,9 @@ public:
}
bool GetIDType(Handle handle, HandleType* type) const {
- if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
- !occupied[handle - HANDLE_OFFSET]) {
- ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
+ !occupied[handle - HANDLE_OFFSET]) {
+ LOG_ERROR(Kernel, "Bad object handle %08X", handle);
return false;
}
Object* t = pool[handle - HANDLE_OFFSET];
@@ -152,10 +133,10 @@ public:
Object* &operator [](Handle handle);
void List();
void Clear();
- int GetCount();
+ int GetCount() const;
private:
-
+
enum {
MAX_COUNT = 0x1000,
HANDLE_OFFSET = 0x100,
@@ -170,6 +151,12 @@ private:
extern ObjectPool g_object_pool;
extern Handle g_main_thread;
+/// The ID code of the currently running game
+/// TODO(Subv): This variable should not be here,
+/// we need a way to store information about the currently loaded application
+/// for later query during runtime, maybe using the LDR service?
+extern u64 g_program_id;
+
/// Initialize the kernel
void Init();
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index fcfd061a..5a173e12 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <vector>
@@ -27,32 +27,7 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
std::string name; ///< Name of mutex (optional)
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- // TODO(bunnei): ImplementMe
- locked = true;
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- *wait = locked;
-
- if (locked) {
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
- }
-
- return 0;
- }
+ ResultVal<bool> WaitSynchronization() override;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,21 +35,46 @@ public:
typedef std::multimap<Handle, Handle> MutexMap;
static MutexMap g_mutex_held_locks;
-void MutexAcquireLock(Mutex* mutex, Handle thread) {
+/**
+ * Acquires the specified mutex for the specified thread
+ * @param mutex Mutex that is to be acquired
+ * @param thread Thread that will acquired
+ */
+void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
mutex->lock_thread = thread;
}
-void MutexAcquireLock(Mutex* mutex) {
- Handle thread = GetCurrentThreadHandle();
+bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
MutexAcquireLock(mutex, thread);
+ Kernel::ResumeThreadFromWait(thread);
+ return true;
+}
+
+/**
+ * Resumes a thread waiting for the specified mutex
+ * @param mutex The mutex that some thread is waiting on
+ */
+void ResumeWaitingThread(Mutex* mutex) {
+ // Find the next waiting thread for the mutex...
+ if (mutex->waiting_threads.empty()) {
+ // Reset mutex lock thread handle, nothing is waiting
+ mutex->locked = false;
+ mutex->lock_thread = -1;
+ }
+ else {
+ // Resume the next waiting thread and re-lock the mutex
+ std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
+ ReleaseMutexForThread(mutex, *iter);
+ mutex->waiting_threads.erase(iter);
+ }
}
void MutexEraseLock(Mutex* mutex) {
Handle handle = mutex->GetHandle();
auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- if ((*iter).second == handle) {
+ if (iter->second == handle) {
g_mutex_held_locks.erase(iter);
break;
}
@@ -82,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
mutex->lock_thread = -1;
}
+void ReleaseThreadMutexes(Handle thread) {
+ auto locked = g_mutex_held_locks.equal_range(thread);
+
+ // Release every mutex that the thread holds, and resume execution on the waiting threads
+ for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
+ Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second);
+ ResumeWaitingThread(mutex);
+ }
+
+ // Erase all the locks that this thread holds
+ g_mutex_held_locks.erase(thread);
+}
+
bool LockMutex(Mutex* mutex) {
// Mutex alread locked?
if (mutex->locked) {
@@ -91,43 +104,27 @@ bool LockMutex(Mutex* mutex) {
return true;
}
-bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
- MutexAcquireLock(mutex, thread);
- Kernel::ResumeThreadFromWait(thread);
- return true;
-}
-
bool ReleaseMutex(Mutex* mutex) {
MutexEraseLock(mutex);
- bool woke_threads = false;
-
- // Find the next waiting thread for the mutex...
- while (!woke_threads && !mutex->waiting_threads.empty()) {
- std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
- woke_threads |= ReleaseMutexForThread(mutex, *iter);
- mutex->waiting_threads.erase(iter);
- }
- // Reset mutex lock thread handle, nothing is waiting
- if (!woke_threads) {
- mutex->locked = false;
- mutex->lock_thread = -1;
- }
- return woke_threads;
+ ResumeWaitingThread(mutex);
+ return true;
}
/**
* Releases a mutex
* @param handle Handle to mutex to release
*/
-Result ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
-
- _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
+ResultCode ReleaseMutex(Handle handle) {
+ Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
+ if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
- return -1;
+ // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
+ // what error condition this is supposed to be signaling.
+ return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
+ ErrorSummary::NothingHappened, ErrorLevel::Temporary);
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -167,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
return handle;
}
+ResultVal<bool> Mutex::WaitSynchronization() {
+ bool wait = locked;
+ if (locked) {
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
+ }
+ else {
+ // Lock the mutex when the first thread accesses it
+ locked = true;
+ MutexAcquireLock(this);
+ }
+
+ return MakeResult<bool>(wait);
+}
} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 7d7b5137..7f4909a6 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -13,9 +13,8 @@ namespace Kernel {
/**
* Releases a mutex
* @param handle Handle to mutex to release
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ReleaseMutex(Handle handle);
+ResultCode ReleaseMutex(Handle handle);
/**
* Creates a mutex
@@ -25,4 +24,10 @@ Result ReleaseMutex(Handle handle);
*/
Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
+/**
+ * Releases all the mutexes held by the specified thread
+ * @param thread Thread that is holding the mutexes
+ */
+void ReleaseThreadMutexes(Handle thread);
+
} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
new file mode 100644
index 00000000..6f56da8a
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,94 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <queue>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/semaphore.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Semaphore : public Object {
+public:
+ std::string GetTypeName() const override { return "Semaphore"; }
+ std::string GetName() const override { return name; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; }
+
+ u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
+ u32 available_count; ///< Number of free slots left in the semaphore
+ std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
+ std::string name; ///< Name of semaphore (optional)
+
+ /**
+ * Tests whether a semaphore still has free slots
+ * @return Whether the semaphore is available
+ */
+ bool IsAvailable() const {
+ return available_count > 0;
+ }
+
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = !IsAvailable();
+
+ if (wait) {
+ Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
+ waiting_threads.push(GetCurrentThreadHandle());
+ } else {
+ --available_count;
+ }
+
+ return MakeResult<bool>(wait);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ResultCode CreateSemaphore(Handle* handle, u32 initial_count,
+ u32 max_count, const std::string& name) {
+
+ if (initial_count > max_count)
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+
+ Semaphore* semaphore = new Semaphore;
+ *handle = g_object_pool.Create(semaphore);
+
+ // When the semaphore is created, some slots are reserved for other threads,
+ // and the rest is reserved for the caller thread
+ semaphore->max_count = max_count;
+ semaphore->available_count = initial_count;
+ semaphore->name = name;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
+ Semaphore* semaphore = g_object_pool.Get<Semaphore>(handle);
+ if (semaphore == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ if (semaphore->max_count - semaphore->available_count < release_count)
+ return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+
+ *count = semaphore->available_count;
+ semaphore->available_count += release_count;
+
+ // Notify some of the threads that the semaphore has been released
+ // stop once the semaphore is full again or there are no more waiting threads
+ while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
+ Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
+ semaphore->waiting_threads.pop();
+ --semaphore->available_count;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
new file mode 100644
index 00000000..f0075fdb
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+/**
+ * Creates a semaphore.
+ * @param handle Pointer to the handle of the newly created object
+ * @param initial_count Number of slots reserved for other threads
+ * @param max_count Maximum number of slots the semaphore can have
+ * @param name Optional name of semaphore
+ * @return ResultCode of the error
+ */
+ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown");
+
+/**
+ * Releases a certain number of slots from a semaphore.
+ * @param count The number of free slots the semaphore had before this call
+ * @param handle The handle of the semaphore to release
+ * @param release_count The number of slots to release
+ * @return ResultCode of the error
+ */
+ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
+
+} // namespace
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 00000000..06ae4bc3
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
+
+/**
+ * Returns a pointer to the command buffer in kernel memory
+ * @param offset Optional offset into command buffer
+ * @return Pointer to command buffer
+ */
+inline static u32* GetCommandBuffer(const int offset=0) {
+ return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
+}
+
+/**
+ * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
+ * primitive for communication between different processes, and are used to implement service calls
+ * to the various system services.
+ *
+ * To make a service call, the client must write the command header and parameters to the buffer
+ * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
+ * SVC call with its Session handle. The kernel will read the command header, using it to marshall
+ * the parameters to the process at the server endpoint of the session. After the server replies to
+ * the request, the response is marshalled back to the caller's TLS buffer and control is
+ * transferred back to it.
+ *
+ * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
+ * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
+ * with the session handle, this class's SyncRequest method is called, which should read the TLS
+ * buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
+ * no parameter marshalling is done.
+ *
+ * In the long term, this should be turned into the full-fledged IPC mechanism implemented by
+ * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
+ * opposed to HLE simulations.
+ */
+class Session : public Object {
+public:
+ std::string GetTypeName() const override { return "Session"; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; }
+
+ /**
+ * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
+ * aren't supported yet.
+ */
+ virtual ResultVal<bool> SyncRequest() = 0;
+};
+
+}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 6bd5e272..3c8c502c 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common.h"
@@ -16,17 +16,6 @@ public:
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-
u32 base_address; ///< Address of shared memory block in RAM
MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
@@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
return shared_memory;
}
-/**
- * Creates a shared memory object
- * @param name Optional name of shared memory object
- * @return Handle of newly created shared memory object
- */
Handle CreateSharedMemory(const std::string& name) {
Handle handle;
CreateSharedMemory(handle, name);
@@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) {
* @param other_permissions Memory block map other permissions (specified by SVC field)
* @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions) {
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
- ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
- handle);
- return -1;
+ LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
+ handle, address);
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
- return 0;
+ return RESULT_SUCCESS;
}
-/**
- * Gets a pointer to the shared memory block
- * @param handle Shared memory block handle
- * @param offset Offset from the start of the shared memory block to get pointer
- * @return Pointer to the shared memory block from the specified offset
- */
-u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
- return Memory::GetPointer(shared_memory->base_address + offset);
+ return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
- ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
- return nullptr;
+ LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
+ // TODO(yuriks): Verify error code.
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidState, ErrorLevel::Permanent);
}
} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 6204d8a4..bb778ec2 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown");
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions);
/**
@@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
* @param offset Offset from the start of the shared memory block to get pointer
* @return Pointer to the shared memory block from the specified offset
*/
-u8* GetSharedMemoryPointer(Handle handle, u32 offset);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e15590c4..1c04701d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <algorithm>
#include <list>
@@ -11,10 +11,12 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
-#include "core/mem_map.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/result.h"
+#include "core/mem_map.h"
namespace Kernel {
@@ -33,25 +35,23 @@ public:
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- if (status != THREADSTATUS_DORMANT) {
+ ResultVal<bool> WaitSynchronization() override {
+ const bool wait = status != THREADSTATUS_DORMANT;
+ if (wait) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
waiting_threads.push_back(thread);
}
WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
- *wait = true;
}
- return 0;
+
+ return MakeResult<bool>(wait);
}
ThreadContext context;
+ u32 thread_id;
+
u32 status;
u32 entry_point;
u32 stack_top;
@@ -64,6 +64,7 @@ public:
WaitType wait_type;
Handle wait_handle;
+ VAddr wait_address;
std::vector<Handle> waiting_threads;
@@ -71,17 +72,20 @@ public:
};
// Lists all thread ids that aren't deleted/etc.
-std::vector<Handle> g_thread_queue;
+static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-Common::ThreadQueueList<Handle> g_thread_ready_queue;
+static Common::ThreadQueueList<Handle> thread_ready_queue;
+
+static Handle current_thread_handle;
+static Thread* current_thread;
-Handle g_current_thread_handle;
-Thread* g_current_thread;
+static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
+static u32 next_thread_id; ///< The next available thread id
/// Gets the current thread
inline Thread* GetCurrentThread() {
- return g_current_thread;
+ return current_thread;
}
/// Gets the current thread handle
@@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() {
/// Sets the current thread
inline void SetCurrentThread(Thread* t) {
- g_current_thread = t;
- g_current_thread_handle = t->GetHandle();
+ current_thread = t;
+ current_thread_handle = t->GetHandle();
}
/// Saves the current CPU context
@@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
t->context.pc = t->context.reg_15 = t->entry_point;
t->context.sp = t->stack_top;
t->context.cpsr = 0x1F; // Usermode
-
+
// TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a
// thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be
// agnostic of the CPU core.
@@ -124,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
}
t->wait_type = WAITTYPE_NONE;
t->wait_handle = 0;
+ t->wait_address = 0;
}
/// Change a thread to "ready" state
@@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) {
Handle handle = t->GetHandle();
if (t->IsReady()) {
if (!ready) {
- g_thread_ready_queue.remove(t->current_priority, handle);
+ thread_ready_queue.remove(t->current_priority, handle);
}
} else if (ready) {
if (t->IsRunning()) {
- g_thread_ready_queue.push_front(t->current_priority, handle);
+ thread_ready_queue.push_front(t->current_priority, handle);
} else {
- g_thread_ready_queue.push_back(t->current_priority, handle);
+ thread_ready_queue.push_back(t->current_priority, handle);
}
t->status = THREADSTATUS_READY;
}
}
/// Verify that a thread has not been released from waiting
-inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
- if (type != thread->wait_type || wait_handle != thread->wait_handle)
- return false;
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting());
+}
- return true;
+/// Verify that a thread has not been released from waiting (with wait address)
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address);
}
/// Stops the current thread
-void StopThread(Handle handle, const char* reason) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
+ResultCode StopThread(Handle handle, const char* reason) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ // Release all the mutexes that this thread holds
+ ReleaseThreadMutexes(handle);
+
ChangeReadyState(thread, false);
thread->status = THREADSTATUS_DORMANT;
- for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
- const Handle waiting_thread = thread->waiting_threads[i];
+ for (Handle waiting_handle : thread->waiting_threads) {
+ Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
- ResumeThreadFromWait(waiting_thread);
+ ResumeThreadFromWait(waiting_handle);
}
}
thread->waiting_threads.clear();
@@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) {
// Stopped threads are never waiting.
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
+
+ return RESULT_SUCCESS;
}
/// Changes a threads state
@@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
t->status = new_status;
-
+
if (new_status == THREADSTATUS_WAIT) {
if (t->wait_type == WAITTYPE_NONE) {
- ERROR_LOG(KERNEL, "Waittype none not allowed");
+ LOG_ERROR(Kernel, "Waittype none not allowed");
}
}
}
@@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
continue;
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ if (thread == nullptr)
+ continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
+
if(thread->current_priority <= priority) {
highest_priority_thread = handle;
priority = thread->current_priority;
@@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
/// Arbitrate all threads currently waiting
void ArbitrateAllThreads(u32 arbiter, u32 address) {
-
+
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
ResumeThreadFromWait(handle);
}
}
@@ -238,11 +252,11 @@ void CallThread(Thread* t) {
/// Switches CPU context to that of the specified thread
void SwitchContext(Thread* t) {
Thread* cur = GetCurrentThread();
-
+
// Save context for current thread
if (cur) {
SaveContext(cur->context);
-
+
if (cur->IsRunning()) {
ChangeReadyState(cur, true);
}
@@ -263,23 +277,18 @@ void SwitchContext(Thread* t) {
Thread* NextThread() {
Handle next;
Thread* cur = GetCurrentThread();
-
+
if (cur && cur->IsRunning()) {
- next = g_thread_ready_queue.pop_first_better(cur->current_priority);
+ next = thread_ready_queue.pop_first_better(cur->current_priority);
} else {
- next = g_thread_ready_queue.pop_first();
+ next = thread_ready_queue.pop_first();
}
if (next == 0) {
return nullptr;
}
- return Kernel::g_object_pool.GetFast<Thread>(next);
+ return Kernel::g_object_pool.Get<Thread>(next);
}
-/**
- * Puts the current thread in the wait state for the given type
- * @param wait_type Type of wait
- * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
- */
void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
Thread* thread = GetCurrentThread();
thread->wait_type = wait_type;
@@ -287,10 +296,14 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
+ WaitCurrentThread(wait_type, wait_handle);
+ GetCurrentThread()->wait_address = wait_address;
+}
+
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
- u32 error;
- Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
+ Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
if (thread) {
thread->status &= ~THREADSTATUS_WAIT;
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
@@ -305,12 +318,12 @@ void DebugThreadQueue() {
if (!thread) {
return;
}
- INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
- for (u32 i = 0; i < g_thread_queue.size(); i++) {
- Handle handle = g_thread_queue[i];
- s32 priority = g_thread_ready_queue.contains(handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
+ for (u32 i = 0; i < thread_queue.size(); i++) {
+ Handle handle = thread_queue[i];
+ s32 priority = thread_ready_queue.contains(handle);
if (priority != -1) {
- INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
}
}
}
@@ -319,16 +332,17 @@ void DebugThreadQueue() {
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
s32 processor_id, u32 stack_top, int stack_size) {
- _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
- "CreateThread priority=%d, outside of allowable range!", priority)
+ _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
+ "priority=%d, outside of allowable range!", priority)
Thread* thread = new Thread;
handle = Kernel::g_object_pool.Create(thread);
- g_thread_queue.push_back(handle);
- g_thread_ready_queue.prepare(priority);
+ thread_queue.push_back(handle);
+ thread_ready_queue.prepare(priority);
+ thread->thread_id = next_thread_id++;
thread->status = THREADSTATUS_DORMANT;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
@@ -337,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
thread->processor_id = processor_id;
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
thread->name = name;
return thread;
@@ -347,28 +362,28 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
u32 stack_top, int stack_size) {
if (name == nullptr) {
- ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
+ LOG_ERROR(Kernel_SVC, "nullptr name");
return -1;
}
if ((u32)stack_size < 0x200) {
- ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name,
+ LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
stack_size);
return -1;
}
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
+ LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
name, priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
}
if (!Memory::GetPointer(entry_point)) {
- ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point);
+ LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
return -1;
}
Handle handle;
- Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
+ Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
stack_size);
ResetThread(thread, arg, 0);
@@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
}
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
- return thread->current_priority;
+ResultVal<u32> GetThreadPriority(const Handle handle) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ return MakeResult<u32>(thread->current_priority);
}
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority) {
+ResultCode SetThreadPriority(Handle handle, s32 priority) {
Thread* thread = nullptr;
if (!handle) {
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
} else {
- thread = g_object_pool.GetFast<Thread>(handle);
+ thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel);
+ }
}
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
// If priority is invalid, clamp to valid range
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
+ LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
@@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) {
// Change thread priority
s32 old = thread->current_priority;
- g_thread_ready_queue.remove(old, handle);
+ thread_ready_queue.remove(old, handle);
thread->current_priority = priority;
- g_thread_ready_queue.prepare(thread->current_priority);
+ thread_ready_queue.prepare(thread->current_priority);
// Change thread status to "ready" and push to ready queue
if (thread->IsRunning()) {
thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
if (thread->IsReady()) {
- g_thread_ready_queue.push_back(thread->current_priority, handle);
+ thread_ready_queue.push_back(thread->current_priority, handle);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Sets up the primary application thread
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
-
+
// Initialize new "main" thread
- Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
-
+
ResetThread(thread, 0, 0);
-
+
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
if (cur && cur->IsRunning()) {
ChangeReadyState(cur, true);
}
-
+
// Run new "main" thread
SetCurrentThread(thread);
thread->status = THREADSTATUS_RUNNING;
@@ -451,13 +470,13 @@ void Reschedule() {
Thread* next = NextThread();
HLE::g_reschedule = false;
if (next > 0) {
- INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
-
+ LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+
SwitchContext(next);
// Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
// by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
- // This results in the current thread yielding on a VBLANK once, and then it will be
+ // This results in the current thread yielding on a VBLANK once, and then it will be
// immediately placed back in the queue for execution.
if (prev->wait_type == WAITTYPE_VBLANK) {
ResumeThreadFromWait(prev->GetHandle());
@@ -465,9 +484,21 @@ void Reschedule() {
}
}
+ResultCode GetThreadId(u32* thread_id, Handle handle) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr)
+ return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+
+ *thread_id = thread->thread_id;
+
+ return RESULT_SUCCESS;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
+ next_thread_id = INITIAL_THREAD_ID;
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 39fa38b7..be7adfac 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,11 +1,15 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
+
+#include "core/mem_map.h"
+
#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
enum ThreadPriority {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
@@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
void Reschedule();
/// Stops the current thread
-void StopThread(Handle thread, const char* reason);
+ResultCode StopThread(Handle thread, const char* reason);
+
+/**
+ * Retrieves the ID of the specified thread handle
+ * @param thread_id Will contain the output thread id
+ * @param handle Handle to the thread we want
+ * @return Whether the function was successful or not
+ */
+ResultCode GetThreadId(u32* thread_id, Handle handle);
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle);
@@ -76,14 +88,22 @@ Handle GetCurrentThreadHandle();
*/
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+/**
+ * Puts the current thread in the wait state for the given type
+ * @param wait_type Type of wait
+ * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
+ * @param wait_address Arbitration address used to resume from wait
+ */
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
+
/// Put current thread in a wait state - on WaitSynchronization
void WaitThread_Synchronization();
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle);
+ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority);
+ResultCode SetThreadPriority(Handle handle, s32 priority);
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
new file mode 100644
index 00000000..14d2be4a
--- /dev/null
+++ b/src/core/hle/result.h
@@ -0,0 +1,402 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+#include "common/common_types.h"
+#include "common/bit_field.h"
+
+// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
+
+/// Detailed description of the error. This listing is likely incomplete.
+enum class ErrorDescription : u32 {
+ Success = 0,
+ FS_NotFound = 100,
+ FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
+ InvalidSection = 1000,
+ TooLarge = 1001,
+ NotAuthorized = 1002,
+ AlreadyDone = 1003,
+ InvalidSize = 1004,
+ InvalidEnumValue = 1005,
+ InvalidCombination = 1006,
+ NoData = 1007,
+ Busy = 1008,
+ MisalignedAddress = 1009,
+ MisalignedSize = 1010,
+ OutOfMemory = 1011,
+ NotImplemented = 1012,
+ InvalidAddress = 1013,
+ InvalidPointer = 1014,
+ InvalidHandle = 1015,
+ NotInitialized = 1016,
+ AlreadyInitialized = 1017,
+ NotFound = 1018,
+ CancelRequested = 1019,
+ AlreadyExists = 1020,
+ OutOfRange = 1021,
+ Timeout = 1022,
+ InvalidResultValue = 1023,
+};
+
+/**
+ * Identifies the module which caused the error. Error codes can be propagated through a call
+ * chain, meaning that this doesn't always correspond to the module where the API call made is
+ * contained.
+ */
+enum class ErrorModule : u32 {
+ Common = 0,
+ Kernel = 1,
+ Util = 2,
+ FileServer = 3,
+ LoaderServer = 4,
+ TCB = 5,
+ OS = 6,
+ DBG = 7,
+ DMNT = 8,
+ PDN = 9,
+ GX = 10,
+ I2C = 11,
+ GPIO = 12,
+ DD = 13,
+ CODEC = 14,
+ SPI = 15,
+ PXI = 16,
+ FS = 17,
+ DI = 18,
+ HID = 19,
+ CAM = 20,
+ PI = 21,
+ PM = 22,
+ PM_LOW = 23,
+ FSI = 24,
+ SRV = 25,
+ NDM = 26,
+ NWM = 27,
+ SOC = 28,
+ LDR = 29,
+ ACC = 30,
+ RomFS = 31,
+ AM = 32,
+ HIO = 33,
+ Updater = 34,
+ MIC = 35,
+ FND = 36,
+ MP = 37,
+ MPWL = 38,
+ AC = 39,
+ HTTP = 40,
+ DSP = 41,
+ SND = 42,
+ DLP = 43,
+ HIO_LOW = 44,
+ CSND = 45,
+ SSL = 46,
+ AM_LOW = 47,
+ NEX = 48,
+ Friends = 49,
+ RDT = 50,
+ Applet = 51,
+ NIM = 52,
+ PTM = 53,
+ MIDI = 54,
+ MC = 55,
+ SWC = 56,
+ FatFS = 57,
+ NGC = 58,
+ CARD = 59,
+ CARDNOR = 60,
+ SDMC = 61,
+ BOSS = 62,
+ DBM = 63,
+ Config = 64,
+ PS = 65,
+ CEC = 66,
+ IR = 67,
+ UDS = 68,
+ PL = 69,
+ CUP = 70,
+ Gyroscope = 71,
+ MCU = 72,
+ NS = 73,
+ News = 74,
+ RO_1 = 75,
+ GD = 76,
+ CardSPI = 77,
+ EC = 78,
+ RO_2 = 79,
+ WebBrowser = 80,
+ Test = 81,
+ ENC = 82,
+ PIA = 83,
+
+ Application = 254,
+ InvalidResult = 255
+};
+
+/// A less specific error cause.
+enum class ErrorSummary : u32 {
+ Success = 0,
+ NothingHappened = 1,
+ WouldBlock = 2,
+ OutOfResource = 3, ///< There are no more kernel resources (memory, table slots) to
+ ///< execute the operation.
+ NotFound = 4, ///< A file or resource was not found.
+ InvalidState = 5,
+ NotSupported = 6, ///< The operation is not supported or not implemented.
+ InvalidArgument = 7, ///< Returned when a passed argument is invalid in the current runtime
+ ///< context. (Invalid handle, out-of-bounds pointer or size, etc.)
+ WrongArgument = 8, ///< Returned when a passed argument is in an incorrect format for use
+ ///< with the function. (E.g. Invalid enum value)
+ Canceled = 9,
+ StatusChanged = 10,
+ Internal = 11,
+
+ InvalidResult = 63
+};
+
+/// The severity of the error.
+enum class ErrorLevel : u32 {
+ Success = 0,
+ Info = 1,
+
+ Status = 25,
+ Temporary = 26,
+ Permanent = 27,
+ Usage = 28,
+ Reinitialize = 29,
+ Reset = 30,
+ Fatal = 31
+};
+
+/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
+union ResultCode {
+ u32 raw;
+
+ BitField<0, 10, ErrorDescription> description;
+ BitField<10, 8, ErrorModule> module;
+
+ BitField<21, 6, ErrorSummary> summary;
+ BitField<27, 5, ErrorLevel> level;
+
+ // The last bit of `level` is checked by apps and the kernel to determine if a result code is an error
+ BitField<31, 1, u32> is_error;
+
+ explicit ResultCode(u32 raw) : raw(raw) {}
+ ResultCode(ErrorDescription description_, ErrorModule module_,
+ ErrorSummary summary_, ErrorLevel level_) : raw(0) {
+ description = description_;
+ module = module_;
+ summary = summary_;
+ level = level_;
+ }
+
+ ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
+
+ bool IsSuccess() const {
+ return is_error == 0;
+ }
+
+ bool IsError() const {
+ return is_error == 1;
+ }
+};
+
+inline bool operator==(const ResultCode a, const ResultCode b) {
+ return a.raw == b.raw;
+}
+
+inline bool operator!=(const ResultCode a, const ResultCode b) {
+ return a.raw != b.raw;
+}
+
+// Convenience functions for creating some common kinds of errors:
+
+/// The default success `ResultCode`.
+const ResultCode RESULT_SUCCESS(0);
+
+/// Might be returned instead of a dummy success for unimplemented APIs.
+inline ResultCode UnimplementedFunction(ErrorModule module) {
+ return ResultCode(ErrorDescription::NotImplemented, module,
+ ErrorSummary::NotSupported, ErrorLevel::Permanent);
+}
+/// Returned when a function is passed an invalid handle.
+inline ResultCode InvalidHandle(ErrorModule module) {
+ return ResultCode(ErrorDescription::InvalidHandle, module,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+}
+
+/**
+ * This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
+ * also holds a result of type `T`. If the code is an error code then trying to access the inner
+ * value fails, thus ensuring that the ResultCode of functions is always checked properly before
+ * their return value is used. It is similar in concept to the `std::optional` type
+ * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
+ * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
+ *
+ * An example of how it could be used:
+ * \code
+ * ResultVal<int> Frobnicate(float strength) {
+ * if (strength < 0.f || strength > 1.0f) {
+ * // Can't frobnicate too weakly or too strongly
+ * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common,
+ * ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ * } else {
+ * // Frobnicated! Give caller a cookie
+ * return MakeResult<int>(42);
+ * }
+ * }
+ * \endcode
+ *
+ * \code
+ * ResultVal<int> frob_result = Frobnicate(0.75f);
+ * if (frob_result) {
+ * // Frobbed ok
+ * printf("My cookie is %d\n", *frob_result);
+ * } else {
+ * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex);
+ * }
+ * \endcode
+ */
+template <typename T>
+class ResultVal {
+public:
+ /// Constructs an empty `ResultVal` with the given error code. The code must not be a success code.
+ ResultVal(ResultCode error_code = ResultCode(-1))
+ : result_code(error_code)
+ {
+ assert(error_code.IsError());
+ UpdateDebugPtr();
+ }
+
+ /**
+ * Similar to the non-member function `MakeResult`, with the exception that you can manually
+ * specify the success code. `success_code` must not be an error code.
+ */
+ template <typename... Args>
+ static ResultVal WithCode(ResultCode success_code, Args&&... args) {
+ ResultVal<T> result;
+ result.emplace(success_code, std::forward<Args>(args)...);
+ return result;
+ }
+
+ ResultVal(const ResultVal& o)
+ : result_code(o.result_code)
+ {
+ if (!o.empty()) {
+ new (&storage) T(*o.GetPointer());
+ }
+ UpdateDebugPtr();
+ }
+
+ ResultVal(ResultVal&& o)
+ : result_code(o.result_code)
+ {
+ if (!o.empty()) {
+ new (&storage) T(std::move(*o.GetPointer()));
+ }
+ UpdateDebugPtr();
+ }
+
+ ~ResultVal() {
+ if (!empty()) {
+ GetPointer()->~T();
+ }
+ }
+
+ ResultVal& operator=(const ResultVal& o) {
+ if (*this) {
+ if (o) {
+ *GetPointer() = *o.GetPointer();
+ } else {
+ GetPointer()->~T();
+ }
+ } else {
+ if (o) {
+ new (&storage) T(*o.GetPointer());
+ }
+ }
+ result_code = o.result_code;
+ UpdateDebugPtr();
+
+ return *this;
+ }
+
+ /**
+ * Replaces the current result with a new constructed result value in-place. The code must not
+ * be an error code.
+ */
+ template <typename... Args>
+ void emplace(ResultCode success_code, Args&&... args) {
+ assert(success_code.IsSuccess());
+ if (!empty()) {
+ GetPointer()->~T();
+ }
+ new (&storage) T(std::forward<Args>(args)...);
+ result_code = success_code;
+ UpdateDebugPtr();
+ }
+
+ /// Returns true if the `ResultVal` contains an error code and no value.
+ bool empty() const { return result_code.IsError(); }
+
+ /// Returns true if the `ResultVal` contains a return value.
+ bool Succeeded() const { return result_code.IsSuccess(); }
+ /// Returns true if the `ResultVal` contains an error code and no value.
+ bool Failed() const { return empty(); }
+
+ ResultCode Code() const { return result_code; }
+
+ const T& operator* () const { return *GetPointer(); }
+ T& operator* () { return *GetPointer(); }
+ const T* operator->() const { return GetPointer(); }
+ T* operator->() { return GetPointer(); }
+
+ /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
+ template <typename U>
+ T ValueOr(U&& value) const {
+ return !empty() ? *GetPointer() : std::move(value);
+ }
+
+private:
+ typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
+
+ StorageType storage;
+ ResultCode result_code;
+#if _DEBUG
+ // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
+ // need to cast `storage` to a pointer or pay attention to `result_code`.
+ const T* debug_ptr;
+#endif
+
+ void UpdateDebugPtr() {
+#if _DEBUG
+ debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
+#endif
+ }
+
+ const T* GetPointer() const {
+ assert(!empty());
+ return static_cast<const T*>(static_cast<const void*>(&storage));
+ }
+
+ T* GetPointer() {
+ assert(!empty());
+ return static_cast<T*>(static_cast<void*>(&storage));
+ }
+};
+
+/**
+ * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct
+ * `T` with and creates a success `ResultVal` contained the constructed value.
+ */
+template <typename T, typename... Args>
+ResultVal<T> MakeResult(Args&&... args) {
+ return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...);
+}
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index b39603bd..311682ab 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -11,6 +11,24 @@
namespace AC_U {
+/**
+ * AC_U::GetWifiStatus service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
+ */
+void GetWifiStatus(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 0; // Connection type set to none
+
+ LOG_WARNING(Service_AC, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "CreateDefaultConfig"},
{0x00040006, nullptr, "ConnectAsync"},
@@ -18,16 +36,16 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080004, nullptr, "CloseAsync"},
{0x00090002, nullptr, "GetCloseResult"},
{0x000A0000, nullptr, "GetLastErrorCode"},
- {0x000D0000, nullptr, "GetWifiStatus"},
+ {0x000D0000, GetWifiStatus, "GetWifiStatus"},
{0x000E0042, nullptr, "GetCurrentAPInfo"},
{0x00100042, nullptr, "GetCurrentNZoneInfo"},
{0x00110042, nullptr, "GetNZoneApNumService"},
- {0x00240042, nullptr, "AddDenyApType "},
- {0x00270002, nullptr, "GetInfraPriority "},
+ {0x00240042, nullptr, "AddDenyApType"},
+ {0x00270002, nullptr, "GetInfraPriority"},
{0x002D0082, nullptr, "SetRequestEulaVersion"},
{0x00300004, nullptr, "RegisterDisconnectEvent"},
{0x003C0042, nullptr, "GetAPSSIDList"},
- {0x003E0042, nullptr, "IsConnected "},
+ {0x003E0042, nullptr, "IsConnected"},
{0x00400042, nullptr, "SetClientVersion"},
};
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h
index 3c5958d2..c91b2835 100644
--- a/src/core/hle/service/ac_u.h
+++ b/src/core/hle/service/ac_u.h
@@ -9,7 +9,7 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace AC_U
-// socket service "ac:u"
+// socket service "ac:u"
namespace AC_U {
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "ac:u";
}
};
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp
new file mode 100644
index 00000000..b8b06418
--- /dev/null
+++ b/src/core/hle/service/am_app.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_app.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_APP
+
+namespace AM_APP {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h
new file mode 100644
index 00000000..86a5f5b7
--- /dev/null
+++ b/src/core/hle/service/am_app.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_APP
+
+namespace AM_APP {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "am:app";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp
new file mode 100644
index 00000000..403cac35
--- /dev/null
+++ b/src/core/hle/service/am_net.cpp
@@ -0,0 +1,47 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_net.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_NET
+
+namespace AM_NET {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x08010000, nullptr, "OpenTicket"},
+ {0x08020002, nullptr, "TicketAbortInstall"},
+ {0x08030002, nullptr, "TicketFinalizeInstall"},
+ {0x08040100, nullptr, "InstallTitleBegin"},
+ {0x08050000, nullptr, "InstallTitleAbort"},
+ {0x080600C0, nullptr, "InstallTitleResume"},
+ {0x08070000, nullptr, "InstallTitleAbortTMD"},
+ {0x08080000, nullptr, "InstallTitleFinish"},
+ {0x080A0000, nullptr, "OpenTMD"},
+ {0x080B0002, nullptr, "TMDAbortInstall"},
+ {0x080C0042, nullptr, "TMDFinalizeInstall"},
+ {0x080E0040, nullptr, "OpenContentCreate"},
+ {0x080F0002, nullptr, "ContentAbortInstall"},
+ {0x08100040, nullptr, "OpenContentResume"},
+ {0x08120002, nullptr, "ContentFinalizeInstall"},
+ {0x08130000, nullptr, "GetTotalContents"},
+ {0x08140042, nullptr, "GetContentIndexes"},
+ {0x08150044, nullptr, "GetContentsInfo"},
+ {0x08190108, nullptr, "Unknown"},
+ {0x081B00C2, nullptr, "InstallTitlesFinish"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/am_net.h b/src/core/hle/service/am_net.h
new file mode 100644
index 00000000..4816e169
--- /dev/null
+++ b/src/core/hle/service/am_net.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_NET
+
+namespace AM_NET {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "am:net";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index 617b6add..ebfba4d8 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -4,10 +4,12 @@
#include "common/common.h"
+#include "common/file_util.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
#include "apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,20 @@
namespace APT_U {
+// Address used for shared font (as observed on HW)
+// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
+// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
+// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
+// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
+// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
+static const VAddr SHARED_FONT_VADDR = 0x18000000;
+
+// Handle to shared memory region designated to for shared system font
+static Handle shared_font_mem = 0;
+
+static Handle lock_handle = 0;
+static std::vector<u8> shared_font;
+
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
@@ -24,83 +40,178 @@ enum class SignalType : u32 {
};
void Initialize(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
Kernel::SetEventLocked(cmd_buff[3], true);
Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+ _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
+ Kernel::ReleaseMutex(lock_handle);
+
cmd_buff[1] = 0; // No error
- DEBUG_LOG(KERNEL, "called");
+
+ LOG_DEBUG(Service_APT, "called");
}
void GetLockHandle(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+
+ if (0 == lock_handle) {
+ // TODO(bunnei): Verify if this is created here or at application boot?
+ lock_handle = Kernel::CreateMutex(false, "APT_U:Lock");
+ Kernel::ReleaseMutex(lock_handle);
+ }
cmd_buff[1] = 0; // No error
- cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock");
- DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]);
+
+ // Not sure what these parameters are used for, but retail apps check that they are 0 after
+ // GetLockHandle has been called.
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ cmd_buff[4] = 0;
+
+ cmd_buff[5] = lock_handle;
+ LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
}
void Enable(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
cmd_buff[1] = 0; // No error
- WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk);
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
}
void InquireNotification(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id);
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
+/**
+ * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state,
+ * from the source process which set the parameters. Once finished, NS will clear a flag in the NS
+ * state so that this command will return an error if this command is used again if parameters were
+ * not set again. This is called when the second Initialize event is triggered. It returns a signal
+ * type indicating why it was triggered.
+ * Inputs:
+ * 1 : AppID
+ * 2 : Parameter buffer size, max size is 0x1000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown, for now assume AppID of the process which sent these parameters
+ * 3 : Unknown, for now assume Signal type
+ * 4 : Actual parameter buffer size, this is <= to the the input size
+ * 5 : Value
+ * 6 : Handle from the source process which set the parameters, likely used for shared memory
+ * 7 : Size
+ * 8 : Output parameter buffer ptr
+ */
void ReceiveParameter(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0x10;
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
/**
-* APT_U::GlanceParameter service function
-* Inputs:
-* 1 : AppID
-* 2 : Parameter buffer size, max size is 0x1000
-* Outputs:
-* 1 : Result of function, 0 on success, otherwise error code
-* 2 : Unknown, for now assume AppID of the process which sent these parameters
-* 3 : Unknown, for now assume Signal type
-* 4 : Actual parameter buffer size, this is <= to the the input size
-* 5 : Value
-* 6 : Handle from the source process which set the parameters, likely used for shared memory
-* 7 : Size
-* 8 : Output parameter buffer ptr
-*/
+ * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
+ * (except for the word value prior to the output handle), except this will not clear the flag
+ * (except when responseword[3]==8 || responseword[3]==9) in NS state.
+ * Inputs:
+ * 1 : AppID
+ * 2 : Parameter buffer size, max size is 0x1000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown, for now assume AppID of the process which sent these parameters
+ * 3 : Unknown, for now assume Signal type
+ * 4 : Actual parameter buffer size, this is <= to the the input size
+ * 5 : Value
+ * 6 : Handle from the source process which set the parameters, likely used for shared memory
+ * 7 : Size
+ * 8 : Output parameter buffer ptr
+ */
void GlanceParameter(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
+
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0;
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
- cmd_buff[8] = 0;
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+}
+
+/**
+ * APT_U::AppletUtility service function
+ * Inputs:
+ * 1 : Unknown, but clearly used for something
+ * 2 : Buffer 1 size (purpose is unknown)
+ * 3 : Buffer 2 size (purpose is unknown)
+ * 5 : Buffer 1 address (purpose is unknown)
+ * 65 : Buffer 2 address (purpose is unknown)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void AppletUtility(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // These are from 3dbrew - I'm not really sure what they're used for.
+ u32 unk = cmd_buff[1];
+ u32 buffer1_size = cmd_buff[2];
+ u32 buffer2_size = cmd_buff[3];
+ u32 buffer1_addr = cmd_buff[5];
+ u32 buffer2_addr = cmd_buff[65];
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
+ "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
+ buffer1_addr, buffer2_addr);
+}
+
+/**
+ * APT_U::GetSharedFont service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Virtual address of where shared font will be loaded in memory
+ * 4 : Handle to shared font memory
+ */
+void GetSharedFont(Service::Interface* self) {
+ LOG_TRACE(Kernel_SVC, "called");
+
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ if (!shared_font.empty()) {
+ // TODO(bunnei): This function shouldn't copy the shared font every time it's called.
+ // Instead, it should probably map the shared font as RO memory. We don't currently have
+ // an easy way to do this, but the copy should be sufficient for now.
+ memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
+
+ cmd_buff[0] = 0x00440082;
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = SHARED_FONT_VADDR;
+ cmd_buff[4] = shared_font_mem;
+ } else {
+ cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
+ LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
+ }
}
const Interface::FunctionInfo FunctionTable[] = {
@@ -171,14 +282,14 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
{0x00420080, nullptr, "SleepSystem"},
{0x00430040, nullptr, "NotifyToWait"},
- {0x00440000, nullptr, "GetSharedFont"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
{0x00460104, nullptr, "Wrap"},
{0x00470104, nullptr, "Unwrap"},
{0x00480100, nullptr, "GetProgramInfo"},
{0x00490180, nullptr, "Reboot"},
{0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, nullptr, "AppletUtility"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
{0x004C0000, nullptr, "SetFatalErrDispMode"},
{0x004D0080, nullptr, "GetAppletProgramInfo"},
{0x004E0000, nullptr, "HardwareResetAsync"},
@@ -190,6 +301,32 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
+ // Load the shared system font (if available).
+ // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
+ // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
+ // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
+ // "shared_font.bin" in the Citra "sysdata" directory.
+
+ shared_font.clear();
+ std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
+
+ FileUtil::CreateFullPath(filepath); // Create path if not already created
+ FileUtil::IOFile file(filepath, "rb");
+
+ if (file.IsOpen()) {
+ // Read shared font data
+ shared_font.resize(file.GetSize());
+ file.ReadBytes(shared_font.data(), file.GetSize());
+
+ // Create shared font memory object
+ shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
+ } else {
+ LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
+ shared_font_mem = 0;
+ }
+
+ lock_handle = 0;
+
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h
index 5af39e08..30673040 100644
--- a/src/core/hle/service/apt_u.h
+++ b/src/core/hle/service/apt_u.h
@@ -13,8 +13,8 @@ namespace APT_U {
// Application and title launching service. These services handle signaling for home/power button as
// well. Only one session for either APT service can be open at a time, normally processes close the
-// service handle immediately once finished using the service. The commands for APT:U and APT:S are
-// exactly the same, however certain commands are only accessible with APT:S(NS module will call
+// service handle immediately once finished using the service. The commands for APT:U and APT:S are
+// exactly the same, however certain commands are only accessible with APT:S(NS module will call
// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
/// Interface to "APT:U" service
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp
new file mode 100644
index 00000000..b2ff4a75
--- /dev/null
+++ b/src/core/hle/service/boss_u.cpp
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/boss_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_U
+
+namespace BOSS_U {
+
+ const Interface::FunctionInfo FunctionTable[] = {
+ {0x00020100, nullptr, "GetStorageInfo"},
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Interface class
+
+ Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ }
+
+ Interface::~Interface() {
+ }
+
+} // namespace
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h
new file mode 100644
index 00000000..af39b8e6
--- /dev/null
+++ b/src/core/hle/service/boss_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_U
+
+namespace BOSS_U {
+
+ class Interface : public Service::Interface {
+ public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const {
+ return "boss:U";
+ }
+ };
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp
new file mode 100644
index 00000000..25d90351
--- /dev/null
+++ b/src/core/hle/service/cecd_u.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cecd_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_U
+
+namespace CECD_U {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h
new file mode 100644
index 00000000..969e1ed1
--- /dev/null
+++ b/src/core/hle/service/cecd_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_U
+
+namespace CECD_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "cecd:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp
new file mode 100644
index 00000000..88d13d45
--- /dev/null
+++ b/src/core/hle/service/cfg_i.cpp
@@ -0,0 +1,59 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cfg_i.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_I
+
+namespace CFG_I {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x04010082, nullptr, "GetConfigInfoBlk8"},
+ {0x04020082, nullptr, "GetConfigInfoBlk4"},
+ {0x04030000, nullptr, "UpdateConfigNANDSavegame"},
+ {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x04060000, nullptr, "SecureInfoGetRegion"},
+ {0x04070000, nullptr, "SecureInfoGetByte101"},
+ {0x04080042, nullptr, "SecureInfoGetSerialNo"},
+ {0x04090000, nullptr, "UpdateConfigBlk00040003"},
+ {0x08010082, nullptr, "GetConfigInfoBlk8"},
+ {0x08020082, nullptr, "GetConfigInfoBlk4"},
+ {0x08030000, nullptr, "UpdateConfigNANDSavegame"},
+ {0x080400C2, nullptr, "CreateConfigInfoBlk"},
+ {0x08050000, nullptr, "DeleteConfigNANDSavefile"},
+ {0x08060000, nullptr, "FormatConfig"},
+ {0x08070000, nullptr, "Unknown"},
+ {0x08080000, nullptr, "UpdateConfigBlk1"},
+ {0x08090000, nullptr, "UpdateConfigBlk2"},
+ {0x080A0000, nullptr, "UpdateConfigBlk3"},
+ {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
+ {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
+ {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
+ {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
+ {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x08100000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x08110084, nullptr, "SetSecureInfo"},
+ {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
+ {0x08130000, nullptr, "VerifySigSecureInfo"},
+ {0x08140042, nullptr, "SecureInfoGetData"},
+ {0x08150042, nullptr, "SecureInfoGetSignature"},
+ {0x08160000, nullptr, "SecureInfoGetRegion"},
+ {0x08170000, nullptr, "SecureInfoGetByte101"},
+ {0x08180042, nullptr, "SecureInfoGetSerialNo"},
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg_i.h
new file mode 100644
index 00000000..fe343c96
--- /dev/null
+++ b/src/core/hle/service/cfg_i.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_I
+
+namespace CFG_I {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "cfg:i";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp
index 822b0e2b..2e9d7bf2 100644
--- a/src/core/hle/service/cfg_u.cpp
+++ b/src/core/hle/service/cfg_u.cpp
@@ -11,6 +11,94 @@
namespace CFG_U {
+// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
+#define C(code) ((code)[0] | ((code)[1] << 8))
+
+static const std::array<u16, 187> country_codes = {
+ 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
+ C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
+ C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
+ C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
+ C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
+ C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
+ C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
+ 0, 0, 0, 0, 0, 0, 0, 0, // 56-63
+ C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
+ C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
+ C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
+ C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
+ C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
+ C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
+ C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
+ C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
+ C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
+ C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
+ C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
+ C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
+ C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167
+ C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175
+ C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183
+ C("SM"), C("VA"), C("BM") // 184-186
+};
+
+#undef C
+
+/**
+ * CFG_User::GetCountryCodeString service function
+ * Inputs:
+ * 1 : Country Code ID
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Country's 2-char string
+ */
+static void GetCountryCodeString(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 country_code_id = cmd_buffer[1];
+
+ if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
+ LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
+ cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
+ return;
+ }
+
+ cmd_buffer[1] = 0;
+ cmd_buffer[2] = country_codes[country_code_id];
+}
+
+/**
+ * CFG_User::GetCountryCodeID service function
+ * Inputs:
+ * 1 : Country Code 2-char string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Country Code ID
+ */
+static void GetCountryCodeID(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u16 country_code = cmd_buffer[1];
+ u16 country_code_id = 0;
+
+ // The following algorithm will fail if the first country code isn't 0.
+ _dbg_assert_(Service_CFG, country_codes[0] == 0);
+
+ for (size_t id = 0; id < country_codes.size(); ++id) {
+ if (country_codes[id] == country_code) {
+ country_code_id = id;
+ break;
+ }
+ }
+
+ if (0 == country_code_id) {
+ LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8);
+ cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
+ cmd_buffer[2] = 0xFFFF;
+ return;
+ }
+
+ cmd_buffer[1] = 0;
+ cmd_buffer[2] = country_code_id;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010082, nullptr, "GetConfigInfoBlk2"},
{0x00020000, nullptr, "SecureInfoGetRegion"},
@@ -20,8 +108,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00060000, nullptr, "GetModelNintendo2DS"},
{0x00070040, nullptr, "unknown"},
{0x00080080, nullptr, "unknown"},
- {0x00090080, nullptr, "GetCountryCodeString"},
- {0x000A0040, nullptr, "GetCountryCodeID"},
+ {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
+ {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg_u.h
index 7525bd7c..8075d19a 100644
--- a/src/core/hle/service/cfg_u.h
+++ b/src/core/hle/service/cfg_u.h
@@ -19,7 +19,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "cfg:u";
}
};
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
new file mode 100644
index 00000000..6e59a9bf
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -0,0 +1,39 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/csnd_snd.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CSND_SND
+
+namespace CSND_SND {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010140, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030040, nullptr, "Unknown"},
+ {0x00040080, nullptr, "Unknown"},
+ {0x00050000, nullptr, "Unknown"},
+ {0x00060000, nullptr, "Unknown"},
+ {0x00070000, nullptr, "Unknown"},
+ {0x00080040, nullptr, "Unknown"},
+ {0x00090082, nullptr, "FlushDCache"},
+ {0x000A0082, nullptr, "StoreDCache"},
+ {0x000B0082, nullptr, "InvalidateDCache"},
+ {0x000C0000, nullptr, "Unknown"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
new file mode 100644
index 00000000..31cc85b0
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CSND_SND
+
+namespace CSND_SND {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "csnd:SND";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 9e84ac93..bd82063c 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -4,6 +4,7 @@
#include "common/log.h"
#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/dsp_dsp.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,38 +12,181 @@
namespace DSP_DSP {
+static u32 read_pipe_count;
+static Handle semaphore_event;
+static Handle interrupt_event;
+
+/**
+ * DSP_DSP::ConvertProcessAddressFromDspDram service function
+ * Inputs:
+ * 1 : Address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
+ */
+void ConvertProcessAddressFromDspDram(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 addr = cmd_buff[1];
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr);
+}
+
+/**
+ * DSP_DSP::LoadComponent service function
+ * Inputs:
+ * 1 : Size
+ * 2 : Unknown (observed only half word used)
+ * 3 : Unknown (observed only half word used)
+ * 4 : (size << 4) | 0xA
+ * 5 : Buffer address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Component loaded, 0 on not loaded, 1 on loaded
+ */
+void LoadComponent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
+
+ // TODO(bunnei): Implement real DSP firmware loading
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::GetSemaphoreEventHandle service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Semaphore event handle
+ */
+void GetSemaphoreEventHandle(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = semaphore_event; // Event handle
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::RegisterInterruptEvents service function
+ * Inputs:
+ * 1 : Parameter 0 (purpose unknown)
+ * 2 : Parameter 1 (purpose unknown)
+ * 4 : Interrupt event handle
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void RegisterInterruptEvents(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ interrupt_event = static_cast<Handle>(cmd_buff[4]);
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::WriteReg0x10 service function
+ * Inputs:
+ * 1 : Unknown (observed only half word used)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void WriteReg0x10(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ Kernel::SignalEvent(interrupt_event);
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::ReadPipeIfPossible service function
+ * Inputs:
+ * 1 : Unknown
+ * 2 : Unknown
+ * 3 : Size in bytes of read (observed only lower half word used)
+ * 0x41 : Virtual address to read from DSP pipe to in memory
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Number of bytes read from pipe
+ */
+void ReadPipeIfPossible(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
+ VAddr addr = cmd_buff[0x41];
+
+ // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
+ // TODO: Remove this hack :)
+ static const std::array<u16, 16> canned_read_pipe = {
+ 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
+ 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
+ };
+
+ u32 initial_size = read_pipe_count;
+
+ for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
+ if (read_pipe_count < canned_read_pipe.size()) {
+ Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
+ read_pipe_count++;
+ } else {
+ LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
+ break;
+ }
+ }
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr);
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, nullptr, "RecvData"},
- {0x00020040, nullptr, "RecvDataIsReady"},
- {0x00030080, nullptr, "SendData"},
- {0x00040040, nullptr, "SendDataIsEmpty"},
- {0x00070040, nullptr, "WriteReg0x10"},
- {0x00080000, nullptr, "GetSemaphore"},
- {0x00090040, nullptr, "ClearSemaphore"},
- {0x000B0000, nullptr, "CheckSemaphoreRequest"},
- {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"},
- {0x000D0082, nullptr, "WriteProcessPipe"},
- {0x001000C0, nullptr, "ReadPipeIfPossible"},
- {0x001100C2, nullptr, "LoadComponent"},
- {0x00120000, nullptr, "UnloadComponent"},
- {0x00130082, nullptr, "FlushDataCache"},
- {0x00140082, nullptr, "InvalidateDCache "},
- {0x00150082, nullptr, "RegisterInterruptEvents"},
- {0x00160000, nullptr, "GetSemaphoreEventHandle"},
- {0x00170040, nullptr, "SetSemaphoreMask"},
- {0x00180040, nullptr, "GetPhysicalAddress"},
- {0x00190040, nullptr, "GetVirtualAddress" },
- {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
- {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
- {0x001C0082, nullptr, "SetIirFilterEQ"},
- {0x001F0000, nullptr, "GetHeadphoneStatus"},
- {0x00210000, nullptr, "GetIsDspOccupied"},
+ {0x00010040, nullptr, "RecvData"},
+ {0x00020040, nullptr, "RecvDataIsReady"},
+ {0x00030080, nullptr, "SendData"},
+ {0x00040040, nullptr, "SendDataIsEmpty"},
+ {0x00070040, WriteReg0x10, "WriteReg0x10"},
+ {0x00080000, nullptr, "GetSemaphore"},
+ {0x00090040, nullptr, "ClearSemaphore"},
+ {0x000B0000, nullptr, "CheckSemaphoreRequest"},
+ {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
+ {0x000D0082, nullptr, "WriteProcessPipe"},
+ {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
+ {0x001100C2, LoadComponent, "LoadComponent"},
+ {0x00120000, nullptr, "UnloadComponent"},
+ {0x00130082, nullptr, "FlushDataCache"},
+ {0x00140082, nullptr, "InvalidateDCache"},
+ {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"},
+ {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
+ {0x00170040, nullptr, "SetSemaphoreMask"},
+ {0x00180040, nullptr, "GetPhysicalAddress"},
+ {0x00190040, nullptr, "GetVirtualAddress"},
+ {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
+ {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
+ {0x001C0082, nullptr, "SetIirFilterEQ"},
+ {0x001F0000, nullptr, "GetHeadphoneStatus"},
+ {0x00210000, nullptr, "GetIsDspOccupied"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
+ semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
+ interrupt_event = 0;
+ read_pipe_count = 0;
+
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index c439ed26..9431b62f 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -19,8 +19,8 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
- return "dsp:DSP";
+ std::string GetPortName() const override {
+ return "dsp::DSP";
}
};
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 917b2f8c..785c351e 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -20,8 +20,8 @@ namespace ERR_F {
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
-
+
Interface::~Interface() {
}
-
+
} // namespace
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h
index 5da66326..6d7141c1 100644
--- a/src/core/hle/service/err_f.h
+++ b/src/core/hle/service/err_f.h
@@ -19,9 +19,9 @@ namespace ERR_F {
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "err:f";
}
};
-
+
} // namespace
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp
new file mode 100644
index 00000000..58023e53
--- /dev/null
+++ b/src/core/hle/service/frd_u.cpp
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/frd_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_U
+
+namespace FRD_U {
+
+ const Interface::FunctionInfo FunctionTable[] = {
+ {0x00050000, nullptr, "GetFriendKey"},
+ {0x00080000, nullptr, "GetMyPresence"},
+ {0x00100040, nullptr, "GetPassword"},
+ {0x00190042, nullptr, "GetFriendFavoriteGame"},
+ {0x001A00C4, nullptr, "GetFriendInfo"},
+ {0x001B0080, nullptr, "IsOnFriendList"},
+ {0x001C0042, nullptr, "DecodeLocalFriendCode"},
+ {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
+ {0x00320042, nullptr, "SetClientSdkVersion"}
+ };
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Interface class
+
+ Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ }
+
+ Interface::~Interface() {
+ }
+
+} // namespace
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h
new file mode 100644
index 00000000..4020c666
--- /dev/null
+++ b/src/core/hle/service/frd_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_U
+
+namespace FRD_U {
+
+ class Interface : public Service::Interface {
+ public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "frd:u";
+ }
+ };
+
+} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
new file mode 100644
index 00000000..9c383473
--- /dev/null
+++ b/src/core/hle/service/fs/archive.cpp
@@ -0,0 +1,431 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "common/math_util.h"
+
+#include "core/file_sys/archive_savedata.h"
+#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/kernel/session.h"
+#include "core/hle/result.h"
+
+// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
+// Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
+namespace std {
+ template <>
+ struct hash<Service::FS::ArchiveIdCode> {
+ typedef Service::FS::ArchiveIdCode argument_type;
+ typedef std::size_t result_type;
+
+ result_type operator()(const argument_type& id_code) const {
+ typedef std::underlying_type<argument_type>::type Type;
+ return std::hash<Type>()(static_cast<Type>(id_code));
+ }
+ };
+}
+
+namespace Service {
+namespace FS {
+
+// Command to access archive file
+enum class FileCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ OpenSubFile = 0x08010100,
+ Read = 0x080200C2,
+ Write = 0x08030102,
+ GetSize = 0x08040000,
+ SetSize = 0x08050080,
+ GetAttributes = 0x08060000,
+ SetAttributes = 0x08070040,
+ Close = 0x08080000,
+ Flush = 0x08090000,
+};
+
+// Command to access directory
+enum class DirectoryCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ Read = 0x08010042,
+ Close = 0x08020000,
+};
+
+class Archive {
+public:
+ Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code)
+ : backend(std::move(backend)), id_code(id_code) {
+ }
+
+ std::string GetName() const { return "Archive: " + backend->GetName(); }
+
+ ArchiveIdCode id_code; ///< Id code of the archive
+ std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface
+};
+
+class File : public Kernel::Session {
+public:
+ File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
+ : backend(std::move(backend)), path(path) {
+ }
+
+ std::string GetName() const override { return "Path: " + path.DebugStr(); }
+
+ FileSys::Path path; ///< Path of the file
+ std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
+
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from file...
+ case FileCommand::Read:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 address = cmd_buff[5];
+ LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
+ GetTypeName().c_str(), GetName().c_str(), offset, length, address);
+ cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
+ break;
+ }
+
+ // Write to file...
+ case FileCommand::Write:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 flush = cmd_buff[4];
+ u32 address = cmd_buff[6];
+ LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
+ GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
+ cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
+ break;
+ }
+
+ case FileCommand::GetSize:
+ {
+ LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
+ u64 size = backend->GetSize();
+ cmd_buff[2] = (u32)size;
+ cmd_buff[3] = size >> 32;
+ break;
+ }
+
+ case FileCommand::SetSize:
+ {
+ u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
+ LOG_TRACE(Service_FS, "SetSize %s %s size=%llu",
+ GetTypeName().c_str(), GetName().c_str(), size);
+ backend->SetSize(size);
+ break;
+ }
+
+ case FileCommand::Close:
+ {
+ LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
+ Kernel::g_object_pool.Destroy<File>(GetHandle());
+ break;
+ }
+
+ case FileCommand::Flush:
+ {
+ LOG_TRACE(Service_FS, "Flush");
+ backend->Flush();
+ break;
+ }
+
+ // Unknown command...
+ default:
+ LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
+ ResultCode error = UnimplementedFunction(ErrorModule::FS);
+ cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
+ return error;
+ }
+ cmd_buff[1] = 0; // No error
+ return MakeResult<bool>(false);
+ }
+};
+
+class Directory : public Kernel::Session {
+public:
+ Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path)
+ : backend(std::move(backend)), path(path) {
+ }
+
+ std::string GetName() const override { return "Directory: " + path.DebugStr(); }
+
+ FileSys::Path path; ///< Path of the directory
+ std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
+
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from directory...
+ case DirectoryCommand::Read:
+ {
+ u32 count = cmd_buff[1];
+ u32 address = cmd_buff[3];
+ auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
+ LOG_TRACE(Service_FS, "Read %s %s: count=%d",
+ GetTypeName().c_str(), GetName().c_str(), count);
+
+ // Number of entries actually read
+ cmd_buff[2] = backend->Read(count, entries);
+ break;
+ }
+
+ case DirectoryCommand::Close:
+ {
+ LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
+ Kernel::g_object_pool.Destroy<Directory>(GetHandle());
+ break;
+ }
+
+ // Unknown command...
+ default:
+ LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
+ ResultCode error = UnimplementedFunction(ErrorModule::FS);
+ cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
+ return MakeResult<bool>(false);
+ }
+ cmd_buff[1] = 0; // No error
+ return MakeResult<bool>(false);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Map of registered archives, identified by id code. Once an archive is registered here, it is
+ * never removed until the FS service is shut down.
+ */
+static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map;
+
+/**
+ * Map of active archive handles. Values are pointers to the archives in `idcode_map`.
+ */
+static std::unordered_map<ArchiveHandle, Archive*> handle_map;
+static ArchiveHandle next_handle;
+
+static Archive* GetArchive(ArchiveHandle handle) {
+ auto itr = handle_map.find(handle);
+ return (itr == handle_map.end()) ? nullptr : itr->second;
+}
+
+ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
+ LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code);
+
+ auto itr = id_code_map.find(id_code);
+ if (itr == id_code_map.end()) {
+ if (id_code == ArchiveIdCode::SaveData) {
+ // When a SaveData archive is created for the first time, it is not yet formatted
+ // and the save file/directory structure expected by the game has not yet been initialized.
+ // Returning the NotFormatted error code will signal the game to provision the SaveData archive
+ // with the files and folders that it expects.
+ // The FormatSaveData service call will create the SaveData archive when it is called.
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+ // TODO: Verify error against hardware
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Permanent);
+ }
+
+ // This should never even happen in the first place with 64-bit handles,
+ while (handle_map.count(next_handle) != 0) {
+ ++next_handle;
+ }
+ handle_map.emplace(next_handle, itr->second.get());
+ return MakeResult<ArchiveHandle>(next_handle++);
+}
+
+ResultCode CloseArchive(ArchiveHandle handle) {
+ if (handle_map.erase(handle) == 0)
+ return InvalidHandle(ErrorModule::FS);
+ else
+ return RESULT_SUCCESS;
+}
+
+// TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in
+// http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22
+ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) {
+ auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code));
+
+ bool inserted = result.second;
+ _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code");
+
+ auto& archive = result.first->second;
+ LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code);
+ return RESULT_SUCCESS;
+}
+
+ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
+ if (backend == nullptr) {
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+ }
+
+ auto file = std::make_unique<File>(std::move(backend), path);
+ Handle handle = Kernel::g_object_pool.Create(file.release());
+ return MakeResult<Handle>(handle);
+}
+
+ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->DeleteFile(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
+ Archive* src_archive = GetArchive(src_archive_handle);
+ Archive* dest_archive = GetArchive(dest_archive_handle);
+ if (src_archive == nullptr || dest_archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (src_archive == dest_archive) {
+ if (src_archive->backend->RenameFile(src_path, dest_path))
+ return RESULT_SUCCESS;
+ } else {
+ // TODO: Implement renaming across archives
+ return UnimplementedFunction(ErrorModule::FS);
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->DeleteDirectory(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->CreateDirectory(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
+ Archive* src_archive = GetArchive(src_archive_handle);
+ Archive* dest_archive = GetArchive(dest_archive_handle);
+ if (src_archive == nullptr || dest_archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (src_archive == dest_archive) {
+ if (src_archive->backend->RenameDirectory(src_path, dest_path))
+ return RESULT_SUCCESS;
+ } else {
+ // TODO: Implement renaming across archives
+ return UnimplementedFunction(ErrorModule::FS);
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+/**
+ * Open a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Opened Directory object
+ */
+ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
+ if (backend == nullptr) {
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Permanent);
+ }
+
+ auto directory = std::make_unique<Directory>(std::move(backend), path);
+ Handle handle = Kernel::g_object_pool.Create(directory.release());
+ return MakeResult<Handle>(handle);
+}
+
+ResultCode FormatSaveData() {
+ // TODO(Subv): Actually wipe the savedata folder after creating or opening it
+
+ // Do not create the archive again if it already exists
+ if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
+
+ // Create the SaveData archive
+ std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
+ auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
+ Kernel::g_program_id);
+
+ if (savedata_archive->Initialize()) {
+ CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
+ return RESULT_SUCCESS;
+ } else {
+ LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
+ savedata_archive->GetMountPoint().c_str());
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
+ }
+}
+
+/// Initialize archives
+void ArchiveInit() {
+ next_handle = 1;
+
+ // TODO(Link Mauve): Add the other archive types (see here for the known types:
+ // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
+ // archive type is SDMC, so it is the only one getting exposed.
+
+ std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
+ auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
+ if (sdmc_archive->Initialize())
+ CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
+ else
+ LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
+}
+
+/// Shutdown archives
+void ArchiveShutdown() {
+ handle_map.clear();
+ id_code_map.clear();
+}
+
+} // namespace FS
+} // 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 00000000..a128276b
--- /dev/null
+++ b/src/core/hle/service/fs/archive.h
@@ -0,0 +1,125 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive_backend.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
+
+namespace Service {
+namespace FS {
+
+/// Supported archive types
+enum class ArchiveIdCode : u32 {
+ RomFS = 0x00000003,
+ SaveData = 0x00000004,
+ ExtSaveData = 0x00000006,
+ SharedExtSaveData = 0x00000007,
+ SystemSaveData = 0x00000008,
+ SDMC = 0x00000009,
+ SDMCWriteOnly = 0x0000000A,
+};
+
+typedef u64 ArchiveHandle;
+
+/**
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to the opened archive
+ */
+ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code);
+
+/**
+ * Closes an archive
+ * @param id_code IdCode of the archive to open
+ */
+ResultCode CloseArchive(ArchiveHandle handle);
+
+/**
+ * Creates an Archive
+ * @param backend File system backend interface to the archive
+ * @param id_code Id code used to access this type of archive
+ */
+ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code);
+
+/**
+ * Open a File from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the File inside of the Archive
+ * @param mode Mode under which to open the File
+ * @return Handle to the opened File object
+ */
+ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
+
+/**
+ * Delete a File from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the File inside of the Archive
+ * @return Whether deletion succeeded
+ */
+ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Rename a File between two Archives
+ * @param src_archive_handle Handle to the source Archive object
+ * @param src_path Path to the File inside of the source Archive
+ * @param dest_archive_handle Handle to the destination Archive object
+ * @param dest_path Path to the File inside of the destination Archive
+ * @return Whether rename succeeded
+ */
+ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
+
+/**
+ * Delete a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Whether deletion succeeded
+ */
+ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Create a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Whether creation of directory succeeded
+ */
+ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Rename a Directory between two Archives
+ * @param src_archive_handle Handle to the source Archive object
+ * @param src_path Path to the Directory inside of the source Archive
+ * @param dest_archive_handle Handle to the destination Archive object
+ * @param dest_path Path to the Directory inside of the destination Archive
+ * @return Whether rename succeeded
+ */
+ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
+
+/**
+ * Open a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Handle to the opened File object
+ */
+ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Creates a blank SaveData archive.
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode FormatSaveData();
+
+/// Initialize archives
+void ArchiveInit();
+
+/// Shutdown archives
+void ArchiveShutdown();
+
+} // namespace FS
+} // 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 00000000..f99d84b2
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -0,0 +1,529 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common.h"
+#include "common/file_util.h"
+#include "common/scope_exit.h"
+#include "common/string_util.h"
+#include "core/hle/result.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/fs/fs_user.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FS_User
+
+namespace Service {
+namespace FS {
+
+static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) {
+ return (u64)low_word | ((u64)high_word << 32);
+}
+
+static void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
+ // http://3dbrew.org/wiki/FS:Initialize#Request
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_DEBUG(Service_FS, "called");
+}
+
+/**
+ * FS_User::OpenFile service function
+ * Inputs:
+ * 1 : Transaction
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Low path type
+ * 5 : Low path size
+ * 6 : Open flags
+ * 7 : Attributes
+ * 8 : (LowPathSize << 14) | 2
+ * 9 : Low path data pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : File handle
+ */
+static void OpenFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 filename_size = cmd_buff[5];
+ FileSys::Mode mode; mode.hex = cmd_buff[6];
+ u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
+ u32 filename_ptr = cmd_buff[9];
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes);
+
+ ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
+ }
+}
+
+/**
+ * FS_User::OpenFileDirectly service function
+ * Inputs:
+ * 1 : Transaction
+ * 2 : Archive ID
+ * 3 : Archive low path type
+ * 4 : Archive low path size
+ * 5 : File low path type
+ * 6 : File low path size
+ * 7 : Flags
+ * 8 : Attributes
+ * 9 : (ArchiveLowPathSize << 14) | 0x802
+ * 10 : Archive low path
+ * 11 : (FileLowPathSize << 14) | 2
+ * 12 : File low path
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : File handle
+ */
+static void OpenFileDirectly(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
+ u32 archivename_size = cmd_buff[4];
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]);
+ u32 filename_size = cmd_buff[6];
+ FileSys::Mode mode; mode.hex = cmd_buff[7];
+ u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
+ u32 archivename_ptr = cmd_buff[10];
+ u32 filename_ptr = cmd_buff[12];
+ FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d",
+ archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
+
+ if (archive_path.GetType() != FileSys::Empty) {
+ LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
+ cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ cmd_buff[3] = 0;
+ return;
+ }
+
+ ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id);
+ if (archive_handle.Failed()) {
+ LOG_ERROR(Service_FS, "failed to get a handle for archive");
+ cmd_buff[1] = archive_handle.Code().raw;
+ cmd_buff[3] = 0;
+ return;
+ }
+ SCOPE_EXIT({ CloseArchive(*archive_handle); });
+
+ ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
+ }
+}
+
+/*
+ * FS_User::DeleteFile service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : File path string type
+ * 5 : File path string size
+ * 7 : File path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 filename_size = cmd_buff[5];
+ u32 filename_ptr = cmd_buff[7];
+
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
+ filename_type, filename_size, file_path.DebugStr().c_str());
+
+ cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw;
+}
+
+/*
+ * FS_User::RenameFile service function
+ * Inputs:
+ * 2 : Source archive handle lower word
+ * 3 : Source archive handle upper word
+ * 4 : Source file path type
+ * 5 : Source file path size
+ * 6 : Dest archive handle lower word
+ * 7 : Dest archive handle upper word
+ * 8 : Dest file path type
+ * 9 : Dest file path size
+ * 11: Source file path string data
+ * 13: Dest file path string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void RenameFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 src_filename_size = cmd_buff[5];
+ ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);;
+ auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
+ u32 dest_filename_size = cmd_buff[9];
+ u32 src_filename_ptr = cmd_buff[11];
+ u32 dest_filename_ptr = cmd_buff[13];
+
+ FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr);
+ FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr);
+
+ LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
+ src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(),
+ dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str());
+
+ cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw;
+}
+
+/*
+ * FS_User::DeleteDirectory service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Directory path string type
+ * 5 : Directory path string size
+ * 7 : Directory path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 dirname_size = cmd_buff[5];
+ u32 dirname_ptr = cmd_buff[7];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
+ dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw;
+}
+
+/*
+ * FS_User::CreateDirectory service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Directory path string type
+ * 5 : Directory path string size
+ * 8 : Directory path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CreateDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 dirname_size = cmd_buff[5];
+ u32 dirname_ptr = cmd_buff[8];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw;
+}
+
+/*
+ * FS_User::RenameDirectory service function
+ * Inputs:
+ * 2 : Source archive handle lower word
+ * 3 : Source archive handle upper word
+ * 4 : Source dir path type
+ * 5 : Source dir path size
+ * 6 : Dest archive handle lower word
+ * 7 : Dest archive handle upper word
+ * 8 : Dest dir path type
+ * 9 : Dest dir path size
+ * 11: Source dir path string data
+ * 13: Dest dir path string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void RenameDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 src_dirname_size = cmd_buff[5];
+ ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);
+ auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
+ u32 dest_dirname_size = cmd_buff[9];
+ u32 src_dirname_ptr = cmd_buff[11];
+ u32 dest_dirname_ptr = cmd_buff[13];
+
+ FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr);
+ FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
+ src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(),
+ dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw;
+}
+
+/**
+ * FS_User::OpenDirectory service function
+ * Inputs:
+ * 1 : Archive handle low word
+ * 2 : Archive handle high word
+ * 3 : Low path type
+ * 4 : Low path size
+ * 7 : (LowPathSize << 14) | 2
+ * 8 : Low path data pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Directory handle
+ */
+static void OpenDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
+ u32 dirname_size = cmd_buff[4];
+ u32 dirname_ptr = cmd_buff[6];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ LOG_ERROR(Service_FS, "failed to get a handle for directory");
+ }
+}
+
+/**
+ * FS_User::OpenArchive service function
+ * Inputs:
+ * 1 : Archive ID
+ * 2 : Archive low path type
+ * 3 : Archive low path size
+ * 4 : (LowPathSize << 14) | 2
+ * 5 : Archive low path
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Archive handle lower word (unused)
+ * 3 : Archive handle upper word (same as file handle)
+ */
+static void OpenArchive(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
+ u32 archivename_size = cmd_buff[3];
+ u32 archivename_ptr = cmd_buff[5];
+ FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
+
+ LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
+
+ if (archive_path.GetType() != FileSys::Empty) {
+ LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
+ cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ return;
+ }
+
+ ResultVal<ArchiveHandle> handle = OpenArchive(archive_id);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[2] = *handle & 0xFFFFFFFF;
+ cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF;
+ } else {
+ cmd_buff[2] = cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for archive");
+ }
+}
+
+/**
+ * FS_User::CloseArchive service function
+ * Inputs:
+ * 0 : 0x080E0080
+ * 1 : Archive handle low word
+ * 2 : Archive handle high word
+ * Outputs:
+ * 0 : ??? TODO(yuriks): Verify return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CloseArchive(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ cmd_buff[1] = CloseArchive(archive_handle).raw;
+}
+
+/*
+* FS_User::IsSdmcDetected service function
+* Outputs:
+* 1 : Result of function, 0 on success, otherwise error code
+* 2 : Whether the Sdmc could be detected
+*/
+static void IsSdmcDetected(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
+
+ LOG_DEBUG(Service_FS, "called");
+}
+
+/**
+ * FS_User::FormatSaveData service function
+ * Inputs:
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void FormatSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ LOG_DEBUG(Service_FS, "(STUBBED)");
+
+ // TODO(Subv): Find out what the inputs and outputs of this function are
+
+ cmd_buff[1] = FormatSaveData().raw;
+}
+
+/**
+ * FS_User::FormatThisUserSaveData service function
+ * Inputs:
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void FormatThisUserSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ LOG_DEBUG(Service_FS, "(STUBBED)");
+
+ // TODO(Subv): Find out what the inputs and outputs of this function are
+
+ cmd_buff[1] = FormatSaveData().raw;
+}
+
+const FSUserInterface::FunctionInfo FunctionTable[] = {
+ {0x000100C6, nullptr, "Dummy1"},
+ {0x040100C4, nullptr, "Control"},
+ {0x08010002, Initialize, "Initialize"},
+ {0x080201C2, OpenFile, "OpenFile"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
+ {0x08040142, DeleteFile, "DeleteFile"},
+ {0x08050244, RenameFile, "RenameFile"},
+ {0x08060142, DeleteDirectory, "DeleteDirectory"},
+ {0x08070142, nullptr, "DeleteDirectoryRecursively"},
+ {0x08080202, nullptr, "CreateFile"},
+ {0x08090182, CreateDirectory, "CreateDirectory"},
+ {0x080A0244, RenameDirectory, "RenameDirectory"},
+ {0x080B0102, OpenDirectory, "OpenDirectory"},
+ {0x080C00C2, OpenArchive, "OpenArchive"},
+ {0x080D0144, nullptr, "ControlArchive"},
+ {0x080E0080, CloseArchive, "CloseArchive"},
+ {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
+ {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08110040, nullptr, "DeleteSystemSaveData"},
+ {0x08120080, nullptr, "GetFreeBytes"},
+ {0x08130000, nullptr, "GetCardType"},
+ {0x08140000, nullptr, "GetSdmcArchiveResource"},
+ {0x08150000, nullptr, "GetNandArchiveResource"},
+ {0x08160000, nullptr, "GetSdmcFatfsErro"},
+ {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
+ {0x08180000, nullptr, "IsSdmcWritable"},
+ {0x08190042, nullptr, "GetSdmcCid"},
+ {0x081A0042, nullptr, "GetNandCid"},
+ {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
+ {0x081C0000, nullptr, "GetNandSpeedInfo"},
+ {0x081D0042, nullptr, "GetSdmcLog"},
+ {0x081E0042, nullptr, "GetNandLog"},
+ {0x081F0000, nullptr, "ClearSdmcLog"},
+ {0x08200000, nullptr, "ClearNandLog"},
+ {0x08210000, nullptr, "CardSlotIsInserted"},
+ {0x08220000, nullptr, "CardSlotPowerOn"},
+ {0x08230000, nullptr, "CardSlotPowerOff"},
+ {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
+ {0x08250040, nullptr, "CardNorDirectCommand"},
+ {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
+ {0x08270082, nullptr, "CardNorDirectRead"},
+ {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
+ {0x08290082, nullptr, "CardNorDirectWrite"},
+ {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
+ {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
+ {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
+ {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
+ {0x082E0040, nullptr, "GetProductInfo"},
+ {0x082F0040, nullptr, "GetProgramLaunchInfo"},
+ {0x08300182, nullptr, "CreateExtSaveData"},
+ {0x08310180, nullptr, "CreateSharedExtSaveData"},
+ {0x08320102, nullptr, "ReadExtSaveDataIcon"},
+ {0x08330082, nullptr, "EnumerateExtSaveData"},
+ {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
+ {0x08350080, nullptr, "DeleteExtSaveData"},
+ {0x08360080, nullptr, "DeleteSharedExtSaveData"},
+ {0x08370040, nullptr, "SetCardSpiBaudRate"},
+ {0x08380040, nullptr, "SetCardSpiBusMode"},
+ {0x08390000, nullptr, "SendInitializeInfoTo9"},
+ {0x083A0100, nullptr, "GetSpecialContentIndex"},
+ {0x083B00C2, nullptr, "GetLegacyRomHeader"},
+ {0x083C00C2, nullptr, "GetLegacyBannerData"},
+ {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
+ {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
+ {0x083F00C0, nullptr, "GetExtDataBlockSize"},
+ {0x08400040, nullptr, "AbnegateAccessRight"},
+ {0x08410000, nullptr, "DeleteSdmcRoot"},
+ {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
+ {0x08430000, nullptr, "InitializeCtrFileSystem"},
+ {0x08440000, nullptr, "CreateSeed"},
+ {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x08460102, nullptr, "GetLegacyRomHeader2"},
+ {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
+ {0x08480042, nullptr, "GetSdmcCtrRootPath"},
+ {0x08490040, nullptr, "GetArchiveResource"},
+ {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
+ {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
+ {0x084C0242, FormatSaveData, "FormatSaveData"},
+ {0x084D0102, nullptr, "GetLegacySubBannerData"},
+ {0x084E0342, nullptr, "UpdateSha256Context"},
+ {0x084F0102, nullptr, "ReadSpecialFile"},
+ {0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
+ {0x08610042, nullptr, "InitializeWithSdkVersion"},
+ {0x08620040, nullptr, "SetPriority"},
+ {0x08630000, nullptr, "GetPriority"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+FSUserInterface::FSUserInterface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+FSUserInterface::~FSUserInterface() {
+}
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/fs/fs_user.h
index 00538254..80e3804e 100644
--- a/src/core/hle/service/fs_user.h
+++ b/src/core/hle/service/fs/fs_user.h
@@ -9,15 +9,16 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace FS_User
-namespace FS_User {
+namespace Service {
+namespace FS {
/// Interface to "fs:USER" service
-class Interface : public Service::Interface {
+class FSUserInterface : public Service::Interface {
public:
- Interface();
+ FSUserInterface();
- ~Interface();
+ ~FSUserInterface();
/**
* Gets the string port name used by CTROS for the service
@@ -28,4 +29,5 @@ public:
}
};
-} // namespace
+} // namespace FS
+} // 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 48d806e2..00000000
--- a/src/core/hle/service/fs_user.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common.h"
-
-#include "fs_user.h"
-#include "core/settings.h"
-#include "core/hle/kernel/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace FS_User
-
-namespace FS_User {
-
-// Command to access archive file
-enum class LowPathType : u32 {
- Invalid = 0,
- Empty = 1,
- Binary = 2,
- Char = 3,
- Wchar = 4
-};
-
-std::string GetStringFromCmdBuff(const u32 pointer, const u32 size) {
- auto data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
- return std::string(data, size - 1);
-}
-
-// We currently return 0 for success and -1 for failure in cmd_buff[1]. -1 was chosen because it
-// puts all the sections of the http://3dbrew.org/wiki/Error_codes to something non-zero, to make
-// sure we don't mislead the application into thinking something worked.
-
-void Initialize(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
- // http://3dbrew.org/wiki/FS:Initialize#Request
- cmd_buff[1] = 0;
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenFile(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- u32 transaction = cmd_buff[1];
- // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
- u32 size = cmd_buff[5];
- FileSys::Mode mode; mode.hex = cmd_buff[6];
- u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
- u32 pointer = cmd_buff[9];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "file LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string file_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d mode=%d attrs=%d data=%s", type, size, mode, attributes, file_name.c_str());
-
- Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenFileDirectly(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- u32 transaction = cmd_buff[1];
- auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
- LowPathType archive_type = static_cast<LowPathType>(cmd_buff[3]);
- u32 archive_size = cmd_buff[4];
- LowPathType file_type = static_cast<LowPathType>(cmd_buff[5]);
- u32 size = cmd_buff[6];
- FileSys::Mode mode; mode.hex = cmd_buff[7];
- u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
- u32 archive_pointer = cmd_buff[10];
- u32 pointer = cmd_buff[12];
-
- if (archive_type != LowPathType::Empty) {
- ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string archive_name = GetStringFromCmdBuff(archive_pointer, archive_size);
- std::string file_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "archive_type=%d archive_size=%d archive_data=%s "
- "file_type=%d file_size=%d file_mode=%d file_attrs=%d file_data=%s",
- archive_type, archive_size, archive_name.c_str(),
- file_type, size, mode, attributes, file_name.c_str());
-
- // TODO(Link Mauve): check if we should even get a handle for the archive, and don't leak it.
- Handle archive_handle = Kernel::OpenArchive(archive_id);
- if (archive_handle) {
- cmd_buff[1] = 0;
- // cmd_buff[2] isn't used according to 3dmoo's implementation.
- cmd_buff[3] = archive_handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- return;
- }
-
- if (file_type != LowPathType::Char) {
- WARN_LOG(KERNEL, "file LowPath type other than char is currently unsupported; returning archive handle instead");
- return;
- }
-
- Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-/*
- * FS_User::CreateDirectory service function
- * Inputs:
- * 2 : Archive handle lower word
- * 3 : Archive handle upper word
- * 4 : Directory path string type
- * 5 : Directory path string size
- * 8 : Directory path string data
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- */
-void CreateDirectory(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
- u32 name_size = cmd_buff[5];
- u32 name_offset = cmd_buff[8];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string dir_name = GetStringFromCmdBuff(name_offset, name_size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, name_size, dir_name.c_str());
-
- cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_name);
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenDirectory(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[2]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[3]);
- u32 size = cmd_buff[4];
- u32 pointer = cmd_buff[6];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string dir_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, dir_name.c_str());
-
- Handle handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_name);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for directory %s", dir_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenArchive(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- auto arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[2]);
- u32 size = cmd_buff[3];
- u32 pointer = cmd_buff[5];
-
- if (type != LowPathType::Empty) {
- ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string archive_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, archive_name.c_str());
-
- Handle handle = Kernel::OpenArchive(arch_id);
- if (handle) {
- cmd_buff[1] = 0;
- // cmd_buff[2] isn't used according to 3dmoo's implementation.
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-/*
-* FS_User::IsSdmcDetected service function
-* Outputs:
-* 1 : Result of function, 0 on success, otherwise error code
-* 2 : Whether the Sdmc could be detected
-*/
-void IsSdmcDetected(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- cmd_buff[1] = 0;
- cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C6, nullptr, "Dummy1"},
- {0x040100C4, nullptr, "Control"},
- {0x08010002, Initialize, "Initialize"},
- {0x080201C2, OpenFile, "OpenFile"},
- {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
- {0x08040142, nullptr, "DeleteFile"},
- {0x08050244, nullptr, "RenameFile"},
- {0x08060142, nullptr, "DeleteDirectory"},
- {0x08070142, nullptr, "DeleteDirectoryRecursively"},
- {0x08080202, nullptr, "CreateFile"},
- {0x08090182, CreateDirectory, "CreateDirectory"},
- {0x080A0244, nullptr, "RenameDirectory"},
- {0x080B0102, OpenDirectory, "OpenDirectory"},
- {0x080C00C2, OpenArchive, "OpenArchive"},
- {0x080D0144, nullptr, "ControlArchive"},
- {0x080E0080, nullptr, "CloseArchive"},
- {0x080F0180, nullptr, "FormatThisUserSaveData"},
- {0x08100200, nullptr, "CreateSystemSaveData"},
- {0x08110040, nullptr, "DeleteSystemSaveData"},
- {0x08120080, nullptr, "GetFreeBytes"},
- {0x08130000, nullptr, "GetCardType"},
- {0x08140000, nullptr, "GetSdmcArchiveResource"},
- {0x08150000, nullptr, "GetNandArchiveResource"},
- {0x08160000, nullptr, "GetSdmcFatfsErro"},
- {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
- {0x08180000, nullptr, "IsSdmcWritable"},
- {0x08190042, nullptr, "GetSdmcCid"},
- {0x081A0042, nullptr, "GetNandCid"},
- {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
- {0x081C0000, nullptr, "GetNandSpeedInfo"},
- {0x081D0042, nullptr, "GetSdmcLog"},
- {0x081E0042, nullptr, "GetNandLog"},
- {0x081F0000, nullptr, "ClearSdmcLog"},
- {0x08200000, nullptr, "ClearNandLog"},
- {0x08210000, nullptr, "CardSlotIsInserted"},
- {0x08220000, nullptr, "CardSlotPowerOn"},
- {0x08230000, nullptr, "CardSlotPowerOff"},
- {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
- {0x08250040, nullptr, "CardNorDirectCommand"},
- {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
- {0x08270082, nullptr, "CardNorDirectRead"},
- {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
- {0x08290082, nullptr, "CardNorDirectWrite"},
- {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
- {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
- {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
- {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
- {0x082E0040, nullptr, "GetProductInfo"},
- {0x082F0040, nullptr, "GetProgramLaunchInfo"},
- {0x08300182, nullptr, "CreateExtSaveData"},
- {0x08310180, nullptr, "CreateSharedExtSaveData"},
- {0x08320102, nullptr, "ReadExtSaveDataIcon"},
- {0x08330082, nullptr, "EnumerateExtSaveData"},
- {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
- {0x08350080, nullptr, "DeleteExtSaveData"},
- {0x08360080, nullptr, "DeleteSharedExtSaveData"},
- {0x08370040, nullptr, "SetCardSpiBaudRate"},
- {0x08380040, nullptr, "SetCardSpiBusMode"},
- {0x08390000, nullptr, "SendInitializeInfoTo9"},
- {0x083A0100, nullptr, "GetSpecialContentIndex"},
- {0x083B00C2, nullptr, "GetLegacyRomHeader"},
- {0x083C00C2, nullptr, "GetLegacyBannerData"},
- {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
- {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
- {0x083F00C0, nullptr, "GetExtDataBlockSize"},
- {0x08400040, nullptr, "AbnegateAccessRight"},
- {0x08410000, nullptr, "DeleteSdmcRoot"},
- {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
- {0x08430000, nullptr, "InitializeCtrFileSystem"},
- {0x08440000, nullptr, "CreateSeed"},
- {0x084500C2, nullptr, "GetFormatInfo"},
- {0x08460102, nullptr, "GetLegacyRomHeader2"},
- {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
- {0x08480042, nullptr, "GetSdmcCtrRootPath"},
- {0x08490040, nullptr, "GetArchiveResource"},
- {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
- {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
- {0x084C0242, nullptr, "FormatSaveData"},
- {0x084D0102, nullptr, "GetLegacySubBannerData"},
- {0x084E0342, nullptr, "UpdateSha256Context"},
- {0x084F0102, nullptr, "ReadSpecialFile"},
- {0x08500040, nullptr, "GetSpecialFileSize"},
- {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
- {0x08610042, nullptr, "InitializeWithSdkVersion"},
- {0x08620040, nullptr, "SetPriority"},
- {0x08630000, nullptr, "GetPriority"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
-}
-
-Interface::~Interface() {
-}
-
-} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 6119e630..db802714 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -28,41 +28,36 @@ u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(u32 thread_id) {
- if (0 == g_shared_memory)
- return nullptr;
-
- return Kernel::GetSharedMemoryPointer(g_shared_memory,
- 0x800 + (thread_id * sizeof(CommandBuffer)));
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer)));
+ return ptr.ValueOr(nullptr);
}
static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
- if (0 == g_shared_memory)
- return nullptr;
-
- _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
+ _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index");
// For each thread there are two FrameBufferUpdate fields
u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
- return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr));
}
/// Gets a pointer to the interrupt relay queue for a given thread index
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
- return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory,
- sizeof(InterruptRelayQueue) * thread_id);
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id);
+ return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr));
}
-void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
+static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
// TODO: Return proper error codes
if (base_address + size_in_bytes >= 0x420000) {
- ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)",
+ LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)",
base_address, size_in_bytes);
return;
}
// size should be word-aligned
if ((size_in_bytes % 4) != 0) {
- ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes);
+ LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes);
return;
}
@@ -76,8 +71,8 @@ void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
}
/// Write a GSP GPU hardware register
-void WriteHWRegs(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void WriteHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
@@ -87,20 +82,20 @@ void WriteHWRegs(Service::Interface* self) {
}
/// Read a GSP GPU hardware register
-void ReadHWRegs(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void ReadHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
// TODO: Return proper error codes
if (reg_addr + size >= 0x420000) {
- ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
+ LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
return;
}
// size should be word-aligned
if ((size % 4) != 0) {
- ERROR_LOG(GPU, "Invalid size 0x%08x", size);
+ LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size);
return;
}
@@ -115,7 +110,7 @@ void ReadHWRegs(Service::Interface* self) {
}
}
-void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
+static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
u32 base_address = 0x400000;
if (info.active_fb == 0) {
WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left);
@@ -140,8 +135,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
* Outputs:
* 1: Result code
*/
-void SetBufferSwap(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void SetBufferSwap(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 screen_id = cmd_buff[1];
FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
SetBufferSwap(screen_id, *fb_info);
@@ -159,15 +154,16 @@ void SetBufferSwap(Service::Interface* self) {
* 2 : Thread index into GSP command buffer
* 4 : Handle to GSP shared memory
*/
-void RegisterInterruptRelayQueue(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void RegisterInterruptRelayQueue(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1];
g_interrupt_event = cmd_buff[3];
g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
_assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
- cmd_buff[2] = g_thread_id++; // ThreadID
+ cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init
+ cmd_buff[2] = g_thread_id++; // Thread ID
cmd_buff[4] = g_shared_memory; // GSP shared memory
Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
@@ -177,14 +173,15 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
* Signals that the specified interrupt type has occurred to userland code
* @param interrupt_id ID of interrupt that is being signalled
* @todo This should probably take a thread_id parameter and only signal this thread?
+ * @todo This probably does not belong in the GSP module, instead move to video_core
*/
void SignalInterrupt(InterruptId interrupt_id) {
if (0 == g_interrupt_event) {
- WARN_LOG(GSP, "cannot synchronize until GSP event has been created!");
+ LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
if (0 == g_shared_memory) {
- WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!");
+ LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
return;
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -202,7 +199,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
}
/// Executes the next GSP command
-void ExecuteCommand(const Command& command, u32 thread_id) {
+static void ExecuteCommand(const Command& command, u32 thread_id) {
// Utility function to convert register ID to address
auto WriteGPURegister = [](u32 id, u32 data) {
GPU::Write<u32>(0x1EF00000 + 4 * id, data);
@@ -215,6 +212,7 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
memcpy(Memory::GetPointer(command.dma_request.dest_address),
Memory::GetPointer(command.dma_request.source_address),
command.dma_request.size);
+ SignalInterrupt(InterruptId::DMA);
break;
// ctrulib homebrew sends all relevant command list data with this command,
@@ -223,13 +221,13 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
case CommandId::SET_COMMAND_LIST_LAST:
{
auto& params = command.set_command_list_last;
+
WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3);
- WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3);
+ WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size);
// TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
- SignalInterrupt(InterruptId::P3D);
break;
}
@@ -247,6 +245,8 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
+
+ SignalInterrupt(InterruptId::PSC0);
break;
}
@@ -260,14 +260,9 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
- // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to
- // work well enough for running demos. Need to figure out how these all work and trigger
- // them correctly.
- SignalInterrupt(InterruptId::PSC0);
+ // TODO(bunnei): Determine if these interrupts should be signalled here.
SignalInterrupt(InterruptId::PSC1);
SignalInterrupt(InterruptId::PPF);
- SignalInterrupt(InterruptId::P3D);
- SignalInterrupt(InterruptId::DMA);
// Update framebuffer information if requested
for (int screen_id = 0; screen_id < 2; ++screen_id) {
@@ -303,12 +298,14 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
}
default:
- ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value());
+ LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value());
}
}
/// This triggers handling of the GX command written to the command buffer in shared memory.
-void TriggerCmdReqQueue(Service::Interface* self) {
+static void TriggerCmdReqQueue(Service::Interface* self) {
+
+ LOG_TRACE(Service_GSP, "called");
// Iterate through each thread's command queue...
for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -325,6 +322,9 @@ void TriggerCmdReqQueue(Service::Interface* self) {
command_buffer->number_commands = command_buffer->number_commands - 1;
}
}
+
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = 0; // No error
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index 0eb32ba4..eb2d3596 100644
--- a/src/core/hle/service/hid_user.cpp
+++ b/src/core/hle/service/hid_user.cpp
@@ -34,10 +34,7 @@ static s16 next_circle_y = 0;
* Gets a pointer to the PadData structure inside HID shared memory
*/
static inline PadData* GetPadData() {
- if (0 == shared_mem)
- return nullptr;
-
- return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0));
+ return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr));
}
/**
@@ -47,7 +44,7 @@ static inline PadData* GetPadData() {
*
* Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
*/
-void UpdateNextCirclePadState() {
+static void UpdateNextCirclePadState() {
static const s16 max_value = 0x9C;
next_circle_x = next_state.circle_left ? -max_value : 0x0;
next_circle_x += next_state.circle_right ? max_value : 0x0;
@@ -58,7 +55,7 @@ void UpdateNextCirclePadState() {
/**
* Sets a Pad state (button or button combo) as pressed
*/
-void PadButtonPress(PadState pad_state) {
+void PadButtonPress(const PadState& pad_state) {
next_state.hex |= pad_state.hex;
UpdateNextCirclePadState();
}
@@ -66,7 +63,7 @@ void PadButtonPress(PadState pad_state) {
/**
* Sets a Pad state (button or button combo) as released
*/
-void PadButtonRelease(PadState pad_state) {
+void PadButtonRelease(const PadState& pad_state) {
next_state.hex &= ~pad_state.hex;
UpdateNextCirclePadState();
}
@@ -155,8 +152,8 @@ void PadUpdateComplete() {
* 7 : Gyroscope event
* 8 : Event signaled by HID_User
*/
-void GetIPCHandles(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void GetIPCHandles(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
cmd_buff[3] = shared_mem;
@@ -166,7 +163,7 @@ void GetIPCHandles(Service::Interface* self) {
cmd_buff[7] = event_gyroscope;
cmd_buff[8] = event_debug_pad;
- DEBUG_LOG(KERNEL, "called");
+ LOG_TRACE(Service_HID, "called");
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h
index 9f6c4d5e..8f53befd 100644
--- a/src/core/hle/service/hid_user.h
+++ b/src/core/hle/service/hid_user.h
@@ -15,7 +15,7 @@
namespace HID_User {
-/**
+/**
* Structure of a Pad controller state.
*/
struct PadState {
@@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}};
const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
// Methods for updating the HID module's state
-void PadButtonPress(PadState pad_state);
-void PadButtonRelease(PadState pad_state);
+void PadButtonPress(const PadState& pad_state);
+void PadButtonRelease(const PadState& pad_state);
void PadUpdateComplete();
/**
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp
new file mode 100644
index 00000000..be15db23
--- /dev/null
+++ b/src/core/hle/service/ir_rst.cpp
@@ -0,0 +1,36 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ir_rst.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_RST
+
+namespace IR_RST {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010000, nullptr, "GetHandles"},
+ {0x00020080, nullptr, "Initialize"},
+ {0x00030000, nullptr, "Shutdown"},
+ {0x00040000, nullptr, "Unknown"},
+ {0x00050000, nullptr, "Unknown"},
+ {0x00060000, nullptr, "Unknown"},
+ {0x00070080, nullptr, "Unknown"},
+ {0x00080000, nullptr, "Unknown"},
+ {0x00090000, nullptr, "Unknown"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h
new file mode 100644
index 00000000..73effd7e
--- /dev/null
+++ b/src/core/hle/service/ir_rst.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_RST
+
+namespace IR_RST {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ir:rst";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp
new file mode 100644
index 00000000..aa9db6f6
--- /dev/null
+++ b/src/core/hle/service/ir_u.cpp
@@ -0,0 +1,45 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ir_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_U
+
+namespace IR_U {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010000, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030042, nullptr, "StartSendTransfer"},
+ {0x00040000, nullptr, "WaitSendTransfer"},
+ {0x000500C2, nullptr, "StartRecvTransfer"},
+ {0x00060000, nullptr, "WaitRecvTransfer"},
+ {0x00070080, nullptr, "GetRecvTransferCount"},
+ {0x00080000, nullptr, "GetSendState"},
+ {0x00090040, nullptr, "SetBitRate"},
+ {0x000A0000, nullptr, "GetBitRate"},
+ {0x000B0040, nullptr, "SetIRLEDState"},
+ {0x000C0000, nullptr, "GetIRLEDRecvState"},
+ {0x000D0000, nullptr, "GetSendFinishedEvent"},
+ {0x000E0000, nullptr, "GetRecvFinishedEvent"},
+ {0x000F0000, nullptr, "GetTransferState"},
+ {0x00100000, nullptr, "GetErrorStatus"},
+ {0x00110040, nullptr, "SetSleepModeActive"},
+ {0x00120040, nullptr, "SetSleepModeState"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h
new file mode 100644
index 00000000..86d98d07
--- /dev/null
+++ b/src/core/hle/service/ir_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_U
+
+namespace IR_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ir:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
new file mode 100644
index 00000000..91b1a6fc
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ldr_ro.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C2, nullptr, "Initialize"},
+ {0x00020082, nullptr, "CRR_Load"},
+ {0x00030042, nullptr, "CRR_Unload"},
+ {0x000402C2, nullptr, "CRO_LoadAndFix"},
+ {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"}
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h
new file mode 100644
index 00000000..32d7c29c
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ldr:ro";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 58051f13..d6f30e9a 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000D0040, nullptr, "SetClamp"},
{0x000E0000, nullptr, "GetClamp"},
{0x000F0040, nullptr, "unknown_input1"},
- {0x00100040, nullptr, "unknown_input2"},
+ {0x00100040, nullptr, "unknown_input2"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index 72ba048e..2a495f3a 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "mic:u";
}
};
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp
new file mode 100644
index 00000000..04c1e0cf
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.cpp
@@ -0,0 +1,31 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/nim_aoc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NIM_AOC
+
+namespace NIM_AOC {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00030042, nullptr, "SetApplicationId"},
+ {0x00040042, nullptr, "SetTin"},
+ {0x000902D0, nullptr, "ListContentSetsEx"},
+ {0x00180000, nullptr, "GetBalance"},
+ {0x001D0000, nullptr, "GetCustomerSupportCode"},
+ {0x00210000, nullptr, "Initialize"},
+ {0x00240282, nullptr, "CalculateContentsRequiredSize"},
+ {0x00250000, nullptr, "RefreshServerTime"},
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h
new file mode 100644
index 00000000..2cc67311
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NIM_AOC
+
+namespace NIM_AOC {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "nim:aoc";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h
index a956ca81..69d2c200 100644
--- a/src/core/hle/service/nwm_uds.h
+++ b/src/core/hle/service/nwm_uds.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "nwm:UDS";
}
};
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
new file mode 100644
index 00000000..90e9b1bf
--- /dev/null
+++ b/src/core/hle/service/pm_app.cpp
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/pm_app.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PM_APP
+
+namespace PM_APP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010140, nullptr, "LaunchTitle"},
+ {0x00020082, nullptr, "LaunchFIRMSetParams"},
+ {0x00030080, nullptr, "TerminateProcesse"},
+ {0x00040100, nullptr, "TerminateProcessTID"},
+ {0x000500C0, nullptr, "TerminateProcessTID_unknown"},
+ {0x00070042, nullptr, "GetFIRMLaunchParams"},
+ {0x00080100, nullptr, "GetTitleExheaderFlags"},
+ {0x00090042, nullptr, "SetFIRMLaunchParams"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h
new file mode 100644
index 00000000..28c38f58
--- /dev/null
+++ b/src/core/hle/service/pm_app.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PM_APP
+
+namespace PM_APP {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "pm:app";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index f6a14d50..b8c0f6da 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -11,19 +11,105 @@
namespace PTM_U {
+/// Charge levels used by PTM functions
+enum class ChargeLevels : u32 {
+ CriticalBattery = 1,
+ LowBattery = 2,
+ HalfFull = 3,
+ MostlyFull = 4,
+ CompletelyFull = 5,
+};
+
+static bool shell_open = true;
+
+static bool battery_is_charging = true;
+
+/**
+ * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
+ * it is likely to just be a duplicate function of GetBatteryChargeState
+ * that controls another part of the HW.
+ * PTM_U::GetAdapterState service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, 0 = not charging, 1 = charging.
+ */
+static void GetAdapterState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = battery_is_charging ? 1 : 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+/*
+ * PTM_User::GetShellState service function.
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
+ */
+static void GetShellState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = shell_open ? 1 : 0;
+
+ LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
+}
+
+/**
+ * PTM_U::GetBatteryLevel service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
+ * 3 = half full battery, 2 = low battery, 1 = critical battery.
+ */
+static void GetBatteryLevel(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+/**
+ * PTM_U::GetBatteryChargeState service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, 0 = not charging, 1 = charging.
+ */
+static void GetBatteryChargeState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = battery_is_charging ? 1 : 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010002, nullptr, "RegisterAlarmClient"},
{0x00020080, nullptr, "SetRtcAlarm"},
{0x00030000, nullptr, "GetRtcAlarm"},
{0x00040000, nullptr, "CancelRtcAlarm"},
- {0x00050000, nullptr, "GetAdapterState"},
- {0x00060000, nullptr, "GetShellState "},
- {0x00070000, nullptr, "GetBatteryLevel"},
- {0x00080000, nullptr, "GetBatteryChargeState"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
{0x00090000, nullptr, "GetPedometerState"},
{0x000A0042, nullptr, "GetStepHistoryEntry"},
- {0x000B00C2, nullptr, "GetStepHistory "},
- {0x000C0000, nullptr, "GetTotalStepCount "},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, nullptr, "GetTotalStepCount"},
{0x000D0040, nullptr, "SetPedometerRecordingMode"},
{0x000E0000, nullptr, "GetPedometerRecordingMode"},
{0x000F0084, nullptr, "GetStepHistoryAll"},
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h
index 82749fa3..f8d9f57b 100644
--- a/src/core/hle/service/ptm_u.h
+++ b/src/core/hle/service/ptm_u.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "ptm:u";
}
};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b144a77d..2230045e 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,16 +7,28 @@
#include "core/hle/service/service.h"
#include "core/hle/service/ac_u.h"
+#include "core/hle/service/am_app.h"
+#include "core/hle/service/am_net.h"
#include "core/hle/service/apt_u.h"
+#include "core/hle/service/boss_u.h"
+#include "core/hle/service/cecd_u.h"
+#include "core/hle/service/cfg_i.h"
#include "core/hle/service/cfg_u.h"
+#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
-#include "core/hle/service/fs_user.h"
+#include "core/hle/service/fs/fs_user.h"
+#include "core/hle/service/frd_u.h"
#include "core/hle/service/gsp_gpu.h"
#include "core/hle/service/hid_user.h"
+#include "core/hle/service/ir_rst.h"
+#include "core/hle/service/ir_u.h"
+#include "core/hle/service/ldr_ro.h"
#include "core/hle/service/mic_u.h"
+#include "core/hle/service/nim_aoc.h"
#include "core/hle/service/ndm_u.h"
#include "core/hle/service/nwm_uds.h"
+#include "core/hle/service/pm_app.h"
#include "core/hle/service/ptm_u.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
@@ -54,7 +66,7 @@ void Manager::DeleteService(const std::string& port_name) {
/// Get a Service Interface from its Handle
Interface* Manager::FetchFromHandle(Handle handle) {
- return Kernel::g_object_pool.GetFast<Interface>(handle);
+ return Kernel::g_object_pool.Get<Interface>(handle);
}
/// Get a Service Interface from its port
@@ -73,30 +85,42 @@ Interface* Manager::FetchFromPortName(const std::string& port_name) {
/// Initialize ServiceManager
void Init() {
g_manager = new Manager;
-
+
g_manager->AddService(new SRV::Interface);
g_manager->AddService(new AC_U::Interface);
+ g_manager->AddService(new AM_APP::Interface);
+ g_manager->AddService(new AM_NET::Interface);
g_manager->AddService(new APT_U::Interface);
+ g_manager->AddService(new BOSS_U::Interface);
+ g_manager->AddService(new CECD_U::Interface);
+ g_manager->AddService(new CFG_I::Interface);
g_manager->AddService(new CFG_U::Interface);
+ g_manager->AddService(new CSND_SND::Interface);
g_manager->AddService(new DSP_DSP::Interface);
g_manager->AddService(new ERR_F::Interface);
- g_manager->AddService(new FS_User::Interface);
+ g_manager->AddService(new FRD_U::Interface);
+ g_manager->AddService(new FS::FSUserInterface);
g_manager->AddService(new GSP_GPU::Interface);
g_manager->AddService(new HID_User::Interface);
+ g_manager->AddService(new IR_RST::Interface);
+ g_manager->AddService(new IR_U::Interface);
+ g_manager->AddService(new LDR_RO::Interface);
g_manager->AddService(new MIC_U::Interface);
+ g_manager->AddService(new NIM_AOC::Interface);
g_manager->AddService(new NDM_U::Interface);
g_manager->AddService(new NWM_UDS::Interface);
+ g_manager->AddService(new PM_APP::Interface);
g_manager->AddService(new PTM_U::Interface);
g_manager->AddService(new SOC_U::Interface);
g_manager->AddService(new SSL_C::Interface);
- NOTICE_LOG(HLE, "initialized OK");
+ LOG_DEBUG(Service, "initialized OK");
}
/// Shutdown ServiceManager
void Shutdown() {
delete g_manager;
- NOTICE_LOG(HLE, "shutdown OK");
+ LOG_DEBUG(Service, "shutdown OK");
}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2f5a866c..9cd90615 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -10,9 +10,11 @@
#include <string>
#include "common/common.h"
+#include "common/string_util.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/session.h"
#include "core/hle/svc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,30 +22,19 @@
namespace Service {
-static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters)
-static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
-
-/**
- * Returns a pointer to the command buffer in kernel memory
- * @param offset Optional offset into command buffer
- * @return Pointer to command buffer
- */
-inline static u32* GetCommandBuffer(const int offset=0) {
- return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
-}
+static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
class Manager;
/// Interface to a CTROS service
-class Interface : public Kernel::Object {
+class Interface : public Kernel::Session {
+ // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
+ // just something that encapsulates a session and acts as a helper to implement service
+ // processes.
+
friend class Manager;
public:
-
std::string GetName() const override { return GetPortName(); }
- std::string GetTypeName() const override { return GetPortName(); }
-
- static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
- Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; }
typedef void (*Function)(Interface*);
@@ -75,48 +66,31 @@ public:
m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
}
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = GetCommandBuffer();
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
auto itr = m_functions.find(cmd_buff[0]);
- if (itr == m_functions.end()) {
- ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X",
- GetPortName().c_str(), cmd_buff[0]);
+ if (itr == m_functions.end() || itr->second.func == nullptr) {
+ // Number of params == bits 0-5 + bits 6-11
+ int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
- // TODO(bunnei): Hack - ignore error
- u32* cmd_buff = Service::GetCommandBuffer();
- cmd_buff[1] = 0;
- return 0;
- }
- if (itr->second.func == nullptr) {
- ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
- GetPortName().c_str(), itr->second.name.c_str());
+ std::string error = "unknown/unimplemented function '%s': port=%s";
+ for (int i = 1; i <= num_params; ++i) {
+ error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
+ }
+
+ std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
+
+ LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str());
// TODO(bunnei): Hack - ignore error
- u32* cmd_buff = Service::GetCommandBuffer();
cmd_buff[1] = 0;
- return 0;
- }
+ return MakeResult<bool>(false);
+ }
itr->second.func(this);
- return 0; // TODO: Implement return from actual function
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "unimplemented function");
- return 0;
+ return MakeResult<bool>(false); // TODO: Implement return from actual function
}
protected:
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index e27a2b1f..d5590a68 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -19,7 +19,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "soc:U";
}
};
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 6c02a43d..165fd7aa 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -11,20 +11,20 @@
namespace SRV {
-Handle g_event_handle = 0;
+static Handle g_event_handle = 0;
-void Initialize(Service::Interface* self) {
- DEBUG_LOG(OSHLE, "called");
+static void Initialize(Service::Interface* self) {
+ LOG_DEBUG(Service_SRV, "called");
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
}
-void GetProcSemaphore(Service::Interface* self) {
- DEBUG_LOG(OSHLE, "called");
+static void GetProcSemaphore(Service::Interface* self) {
+ LOG_TRACE(Service_SRV, "called");
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(bunnei): Change to a semaphore once these have been implemented
g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event");
@@ -34,21 +34,21 @@ void GetProcSemaphore(Service::Interface* self) {
cmd_buff[3] = g_event_handle;
}
-void GetServiceHandle(Service::Interface* self) {
- Result res = 0;
- u32* cmd_buff = Service::GetCommandBuffer();
+static void GetServiceHandle(Service::Interface* self) {
+ ResultCode res = RESULT_SUCCESS;
+ u32* cmd_buff = Kernel::GetCommandBuffer();
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
if (nullptr != service) {
cmd_buff[3] = service->GetHandle();
- DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
+ LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
} else {
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
- res = -1;
+ LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
+ res = UnimplementedFunction(ErrorModule::SRV);
}
- cmd_buff[1] = res;
+ cmd_buff[1] = res.raw;
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2aa1303f..47e9bf77 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
@@ -12,10 +12,12 @@
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/function_wrappers.h"
+#include "core/hle/result.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,8 +31,8 @@ enum ControlMemoryOperation {
};
/// Map application or GSP heap memory
-Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
- DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
+static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+ LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
operation, addr0, addr1, size, permissions);
switch (operation) {
@@ -42,19 +44,19 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz
// Map GSP heap memory
case MEMORY_OPERATION_GSP_HEAP:
- *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions);
+ *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions);
break;
// Unknown ControlMemory operation
default:
- ERROR_LOG(SVC, "unknown operation=0x%08X", operation);
+ LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
}
return 0;
}
/// Maps a memory block to specified address
-Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
- DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
+static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
+ LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
@@ -67,20 +69,20 @@ Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permis
case Kernel::MemoryPermission::WriteExecute:
case Kernel::MemoryPermission::ReadWriteExecute:
case Kernel::MemoryPermission::DontCare:
- Kernel::MapSharedMemory(handle, addr, permissions_type,
+ Kernel::MapSharedMemory(handle, addr, permissions_type,
static_cast<Kernel::MemoryPermission>(other_permissions));
break;
default:
- ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions);
+ LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
}
return 0;
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-Result ConnectToPort(Handle* out, const char* port_name) {
+static Result ConnectToPort(Handle* out, const char* port_name) {
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
- DEBUG_LOG(SVC, "called port_name=%s", port_name);
+ LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
_assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
*out = service->GetHandle();
@@ -89,78 +91,80 @@ Result ConnectToPort(Handle* out, const char* port_name) {
}
/// Synchronize to an OS service
-Result SendSyncRequest(Handle handle) {
- Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+static Result SendSyncRequest(Handle handle) {
+ Kernel::Session* session = Kernel::g_object_pool.Get<Kernel::Session>(handle);
+ if (session == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
- _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
- DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
- bool wait = false;
- Result res = object->SyncRequest(&wait);
- if (wait) {
+ ResultVal<bool> wait = session->SyncRequest();
+ if (wait.Succeeded() && *wait) {
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
}
- return res;
+ return wait.Code().raw;
}
/// Close a handle
-Result CloseHandle(Handle handle) {
+static Result CloseHandle(Handle handle) {
// ImplementMe
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
return 0;
}
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
+static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool wait = false;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
+ if (!Kernel::g_object_pool.IsValid(handle)) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+ _dbg_assert_(Kernel, object != nullptr);
- DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
object->GetName().c_str(), nano_seconds);
- _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
-
- Result res = object->WaitSynchronization(&wait);
+ ResultVal<bool> wait = object->WaitSynchronization();
// Check for next thread to schedule
- if (wait) {
+ if (wait.Succeeded() && *wait) {
HLE::Reschedule(__func__);
- return 0;
}
- return res;
+ return wait.Code().raw;
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
+static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
s64 nano_seconds) {
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
bool unlock_all = true;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
- DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
+ LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
handle_count, (wait_all ? "true" : "false"), nano_seconds);
// Iterate through each handle, synchronize kernel object
for (s32 i = 0; i < handle_count; i++) {
- bool wait = false;
+ if (!Kernel::g_object_pool.IsValid(handles[i])) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
- _assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object "
- "is nullptr!", handles[i]);
-
- DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
+ LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
object->GetName().c_str());
- Result res = object->WaitSynchronization(&wait);
+ // TODO(yuriks): Verify how the real function behaves when an error happens here
+ ResultVal<bool> wait_result = object->WaitSynchronization();
+ bool wait = wait_result.Succeeded() && *wait_result;
if (!wait && !wait_all) {
*out = i;
- return 0;
+ return RESULT_SUCCESS.raw;
} else {
unlock_all = false;
}
@@ -168,55 +172,57 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa
if (wait_all && unlock_all) {
*out = handle_count;
- return 0;
+ return RESULT_SUCCESS.raw;
}
// Check for next thread to schedule
HLE::Reschedule(__func__);
- return 0;
+ return RESULT_SUCCESS.raw;
}
/// Create an address arbiter (to allocate access to shared resources)
-Result CreateAddressArbiter(u32* arbiter) {
- DEBUG_LOG(SVC, "called");
+static Result CreateAddressArbiter(u32* arbiter) {
+ LOG_TRACE(Kernel_SVC, "called");
Handle handle = Kernel::CreateAddressArbiter();
*arbiter = handle;
return 0;
}
/// Arbitrate address
-Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
- return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address,
- value);
+static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
+ address, type, value);
+ return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
+ address, value).raw;
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-void OutputDebugString(const char* string) {
- OS_LOG(SVC, "%s", string);
+static void OutputDebugString(const char* string) {
+ LOG_DEBUG(Debug_Emulated, "%s", string);
}
/// Get resource limit
-Result GetResourceLimit(Handle* resource_limit, Handle process) {
+static Result GetResourceLimit(Handle* resource_limit, Handle process) {
// With regards to proceess values:
- // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
+ // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
// the current KThread.
*resource_limit = 0xDEADBEEF;
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
return 0;
}
/// Get resource limit current values
-Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
+static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
s32 name_count) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
resource_limit, names, name_count);
Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
return 0;
}
/// Creates a new thread
-Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
std::string name;
if (Symbols::HasSymbol(entry_point)) {
TSymbol symbol = Symbols::GetSymbol(entry_point);
@@ -230,18 +236,18 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
Core::g_app_core->SetReg(1, thread);
- DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
- "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
+ LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
+ "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
name.c_str(), arg, stack_top, priority, processor_id, thread);
-
+
return 0;
}
/// Called when a thread exits
-u32 ExitThread() {
+static u32 ExitThread() {
Handle thread = Kernel::GetCurrentThreadHandle();
- DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
+ LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
Kernel::StopThread(thread, __func__);
HLE::Reschedule(__func__);
@@ -249,55 +255,73 @@ u32 ExitThread() {
}
/// Gets the priority for the specified thread
-Result GetThreadPriority(s32* priority, Handle handle) {
- *priority = Kernel::GetThreadPriority(handle);
- return 0;
+static Result GetThreadPriority(s32* priority, Handle handle) {
+ ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle);
+ if (priority_result.Succeeded()) {
+ *priority = *priority_result;
+ }
+ return priority_result.Code().raw;
}
/// Sets the priority for the specified thread
-Result SetThreadPriority(Handle handle, s32 priority) {
- return Kernel::SetThreadPriority(handle, priority);
+static Result SetThreadPriority(Handle handle, s32 priority) {
+ return Kernel::SetThreadPriority(handle, priority).raw;
}
/// Create a mutex
-Result CreateMutex(Handle* mutex, u32 initial_locked) {
+static Result CreateMutex(Handle* mutex, u32 initial_locked) {
*mutex = Kernel::CreateMutex((initial_locked != 0));
- DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X",
+ LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
initial_locked ? "true" : "false", *mutex);
return 0;
}
/// Release a mutex
-Result ReleaseMutex(Handle handle) {
- DEBUG_LOG(SVC, "called handle=0x%08X", handle);
- _assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!");
- Kernel::ReleaseMutex(handle);
- return 0;
+static Result ReleaseMutex(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
+ ResultCode res = Kernel::ReleaseMutex(handle);
+ return res.raw;
}
-/// Get current thread ID
-Result GetThreadId(u32* thread_id, Handle thread) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread);
- return 0;
+/// Get the ID for the specified thread.
+static Result GetThreadId(u32* thread_id, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
+ ResultCode result = Kernel::GetThreadId(thread_id, handle);
+ return result.raw;
+}
+
+/// Creates a semaphore
+static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) {
+ ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count);
+ LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
+ initial_count, max_count, *semaphore);
+ return res.raw;
+}
+
+/// Releases a certain number of slots in a semaphore
+static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) {
+ LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore);
+ ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count);
+ return res.raw;
}
/// Query memory
-Result QueryMemory(void* info, void* out, u32 addr) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
+static Result QueryMemory(void* info, void* out, u32 addr) {
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
return 0;
}
/// Create an event
-Result CreateEvent(Handle* evt, u32 reset_type) {
+static Result CreateEvent(Handle* evt, u32 reset_type) {
*evt = Kernel::CreateEvent((ResetType)reset_type);
- DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
reset_type, *evt);
return 0;
}
/// Duplicates a kernel handle
-Result DuplicateHandle(Handle* out, Handle handle) {
- DEBUG_LOG(SVC, "called handle=0x%08X", handle);
+static Result DuplicateHandle(Handle* out, Handle handle) {
+ LOG_WARNING(Kernel_SVC, "(STUBBED) called handle=0x%08X", handle);
// Translate kernel handles -> real handles
if (handle == Kernel::CurrentThread) {
@@ -305,7 +329,7 @@ Result DuplicateHandle(Handle* out, Handle handle) {
}
_assert_msg_(KERNEL, (handle != Kernel::CurrentProcess),
"(UNIMPLEMENTED) process handle duplication!");
-
+
// TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
*out = handle;
@@ -313,26 +337,27 @@ Result DuplicateHandle(Handle* out, Handle handle) {
}
/// Signals an event
-Result SignalEvent(Handle evt) {
- Result res = Kernel::SignalEvent(evt);
- DEBUG_LOG(SVC, "called event=0x%08X", evt);
- return res;
+static Result SignalEvent(Handle evt) {
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
+ return Kernel::SignalEvent(evt).raw;
}
/// Clears an event
-Result ClearEvent(Handle evt) {
- Result res = Kernel::ClearEvent(evt);
- DEBUG_LOG(SVC, "called event=0x%08X", evt);
- return res;
+static Result ClearEvent(Handle evt) {
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
+ return Kernel::ClearEvent(evt).raw;
}
/// Sleep the current thread
-void SleepThread(s64 nanoseconds) {
- DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds);
+static void SleepThread(s64 nanoseconds) {
+ LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
+
+ // Check for next thread to schedule
+ HLE::Reschedule(__func__);
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
-s64 GetSystemTick() {
+static s64 GetSystemTick() {
return (s64)Core::g_app_core->GetTicks();
}
@@ -358,8 +383,8 @@ const HLE::FunctionDef SVC_Table[] = {
{0x12, nullptr, "Run"},
{0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
{0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
- {0x15, nullptr, "CreateSemaphore"},
- {0x16, nullptr, "ReleaseSemaphore"},
+ {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"},
+ {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"},
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 1d125faf..6be393d0 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once